net-imap 0.5.1 → 0.5.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.

@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ # An "extended search" response (+ESEARCH+). ESearchResult should be
6
+ # returned (instead of SearchResult) by IMAP#search, IMAP#uid_search,
7
+ # IMAP#sort, and IMAP#uid_sort under any of the following conditions:
8
+ #
9
+ # * Return options were specified for IMAP#search or IMAP#uid_search.
10
+ # The server must support a search extension which allows
11
+ # RFC4466[https://www.rfc-editor.org/rfc/rfc4466.html] +return+ options,
12
+ # such as +ESEARCH+, +PARTIAL+, or +IMAP4rev2+.
13
+ # * Return options were specified for IMAP#sort or IMAP#uid_sort.
14
+ # The server must support the +ESORT+ extension
15
+ # {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html#section-3].
16
+ #
17
+ # *NOTE:* IMAP#search and IMAP#uid_search do not support +ESORT+ yet.
18
+ # * The server supports +IMAP4rev2+ but _not_ +IMAP4rev1+, or +IMAP4rev2+
19
+ # has been enabled. +IMAP4rev2+ requires +ESEARCH+ results.
20
+ #
21
+ # Note that some servers may claim to support a search extension which
22
+ # requires an +ESEARCH+ result, such as +PARTIAL+, but still only return a
23
+ # +SEARCH+ result when +return+ options are specified.
24
+ #
25
+ # Some search extensions may result in the server sending ESearchResult
26
+ # responses after the initiating command has completed. Use
27
+ # IMAP#add_response_handler to handle these responses.
28
+ class ESearchResult < Data.define(:tag, :uid, :data)
29
+ def initialize(tag: nil, uid: nil, data: nil)
30
+ tag => String | nil; tag = -tag if tag
31
+ uid => true | false | nil; uid = !!uid
32
+ data => Array | nil; data ||= []; data.freeze
33
+ super
34
+ end
35
+
36
+ # :call-seq: to_a -> Array of integers
37
+ #
38
+ # When either #all or #partial contains a SequenceSet of message sequence
39
+ # numbers or UIDs, +to_a+ returns that set as an array of integers.
40
+ #
41
+ # When both #all and #partial are +nil+, either because the server
42
+ # returned no results or because +ALL+ and +PARTIAL+ were not included in
43
+ # the IMAP#search +RETURN+ options, #to_a returns an empty array.
44
+ #
45
+ # Note that SearchResult also implements +to_a+, so it can be used without
46
+ # checking if the server returned +SEARCH+ or +ESEARCH+ data.
47
+ def to_a; all&.numbers || partial&.to_a || [] end
48
+
49
+ ##
50
+ # attr_reader: tag
51
+ #
52
+ # The tag string for the command that caused this response to be returned.
53
+ #
54
+ # When +nil+, this response was not caused by a particular command.
55
+
56
+ ##
57
+ # attr_reader: uid
58
+ #
59
+ # Indicates whether #data in this response refers to UIDs (when +true+) or
60
+ # to message sequence numbers (when +false+).
61
+
62
+ ##
63
+ alias uid? uid
64
+
65
+ ##
66
+ # attr_reader: data
67
+ #
68
+ # Search return data, as an array of <tt>[name, value]</tt> pairs. Most
69
+ # return data corresponds to a search +return+ option with the same name.
70
+ #
71
+ # Note that some return data names may be used more than once per result.
72
+ #
73
+ # This data can be more simply retrieved by #min, #max, #all, #count,
74
+ # #modseq, and other methods.
75
+
76
+ # :call-seq: min -> integer or nil
77
+ #
78
+ # The lowest message number/UID that satisfies the SEARCH criteria.
79
+ #
80
+ # Returns +nil+ when the associated search command has no results, or when
81
+ # the +MIN+ return option wasn't specified.
82
+ #
83
+ # Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
84
+ # +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
85
+ def min; data.assoc("MIN")&.last end
86
+
87
+ # :call-seq: max -> integer or nil
88
+ #
89
+ # The highest message number/UID that satisfies the SEARCH criteria.
90
+ #
91
+ # Returns +nil+ when the associated search command has no results, or when
92
+ # the +MAX+ return option wasn't specified.
93
+ #
94
+ # Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
95
+ # +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
96
+ def max; data.assoc("MAX")&.last end
97
+
98
+ # :call-seq: all -> sequence set or nil
99
+ #
100
+ # A SequenceSet containing all message sequence numbers or UIDs that
101
+ # satisfy the SEARCH criteria.
102
+ #
103
+ # Returns +nil+ when the associated search command has no results, or when
104
+ # the +ALL+ return option was not specified but other return options were.
105
+ #
106
+ # Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
107
+ # +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
108
+ #
109
+ # See also: #to_a
110
+ def all; data.assoc("ALL")&.last end
111
+
112
+ # :call-seq: count -> integer or nil
113
+ #
114
+ # Returns the number of messages that satisfy the SEARCH criteria.
115
+ #
116
+ # Returns +nil+ when the associated search command has no results, or when
117
+ # the +COUNT+ return option wasn't specified.
118
+ #
119
+ # Requires +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.1] or
120
+ # +IMAP4rev2+ {[RFC9051]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.3.4].
121
+ def count; data.assoc("COUNT")&.last end
122
+
123
+ # :call-seq: modseq -> integer or nil
124
+ #
125
+ # The highest +mod-sequence+ of all messages being returned.
126
+ #
127
+ # Returns +nil+ when the associated search command has no results, or when
128
+ # the +MODSEQ+ search criterion wasn't specified.
129
+ #
130
+ # Note that there is no search +return+ option for +MODSEQ+. It will be
131
+ # returned whenever the +CONDSTORE+ extension has been enabled. Using the
132
+ # +MODSEQ+ search criteria will implicitly enable +CONDSTORE+.
133
+ #
134
+ # Requires +CONDSTORE+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html]
135
+ # and +ESEARCH+ {[RFC4731]}[https://www.rfc-editor.org/rfc/rfc4731.html#section-3.2].
136
+ def modseq; data.assoc("MODSEQ")&.last end
137
+
138
+ # Returned by ESearchResult#partial.
139
+ #
140
+ # Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
141
+ # or <tt>CONTEXT=SEARCH</tt>/<tt>CONTEXT=SORT</tt>
142
+ # {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
143
+ #
144
+ # See also: #to_a
145
+ class PartialResult < Data.define(:range, :results)
146
+ def initialize(range:, results:)
147
+ range => Range
148
+ results = SequenceSet[results] unless results.nil?
149
+ super
150
+ end
151
+
152
+ ##
153
+ # method: range
154
+ # :call-seq: range -> range
155
+
156
+ ##
157
+ # method: results
158
+ # :call-seq: results -> sequence set or nil
159
+
160
+ # Converts #results to an array of integers.
161
+ #
162
+ # See also: ESearchResult#to_a.
163
+ def to_a; results&.numbers || [] end
164
+ end
165
+
166
+ # :call-seq: partial -> PartialResult or nil
167
+ #
168
+ # A PartialResult containing a subset of the message sequence numbers or
169
+ # UIDs that satisfy the SEARCH criteria.
170
+ #
171
+ # Requires +PARTIAL+ {[RFC9394]}[https://www.rfc-editor.org/rfc/rfc9394.html]
172
+ # or <tt>CONTEXT=SEARCH</tt>/<tt>CONTEXT=SORT</tt>
173
+ # {[RFC5267]}[https://www.rfc-editor.org/rfc/rfc5267.html]
174
+ #
175
+ # See also: #to_a
176
+ def partial; data.assoc("PARTIAL")&.last end
177
+
178
+ end
179
+ end
180
+ end
@@ -3,9 +3,9 @@
3
3
  module Net
4
4
  class IMAP < Protocol
5
5
 
6
- # Net::IMAP::FetchData represents the contents of a FETCH response.
7
- # Net::IMAP#fetch and Net::IMAP#uid_fetch both return an array of
8
- # FetchData objects.
6
+ # Net::IMAP::FetchStruct is the superclass for FetchData and UIDFetchData.
7
+ # Net::IMAP#fetch, Net::IMAP#uid_fetch, Net::IMAP#store, and
8
+ # Net::IMAP#uid_store all return arrays of FetchStruct objects.
9
9
  #
10
10
  # === Fetch attributes
11
11
  #
@@ -63,8 +63,7 @@ module Net
63
63
  # * <b><tt>"X-GM-MSGID"</tt></b> --- unique message ID. Access via #attr.
64
64
  # * <b><tt>"X-GM-THRID"</tt></b> --- Thread ID. Access via #attr.
65
65
  #
66
- # [Note:]
67
- # >>>
66
+ # [NOTE:]
68
67
  # Additional static fields are defined in other \IMAP extensions, but
69
68
  # Net::IMAP can't parse them yet.
70
69
  #
@@ -87,34 +86,23 @@ module Net
87
86
  # extension]}[https://developers.google.com/gmail/imap/imap-extensions]
88
87
  # * <b><tt>"X-GM-LABELS"</tt></b> --- Gmail labels. Access via #attr.
89
88
  #
90
- # [Note:]
91
- # >>>
89
+ # [NOTE:]
92
90
  # Additional dynamic fields are defined in other \IMAP extensions, but
93
91
  # Net::IMAP can't parse them yet.
94
92
  #
95
93
  # === Implicitly setting <tt>\Seen</tt> and using +PEEK+
96
94
  #
97
- # Unless the mailbox is has been opened as read-only, fetching
95
+ # Unless the mailbox has been opened as read-only, fetching
98
96
  # <tt>BODY[#{section}]</tt> or <tt>BINARY[#{section}]</tt>
99
97
  # will implicitly set the <tt>\Seen</tt> flag. To avoid this, fetch using
100
98
  # <tt>BODY.PEEK[#{section}]</tt> or <tt>BINARY.PEEK[#{section}]</tt>
101
99
  # instead.
102
100
  #
103
- # Note that the data will always be _returned_ without <tt>".PEEK"</tt>, in
104
- # <tt>BODY[#{specifier}]</tt> or <tt>BINARY[#{section}]</tt>.
101
+ # [NOTE:]
102
+ # The data will always be _returned_ without the <tt>".PEEK"</tt> suffix,
103
+ # as <tt>BODY[#{specifier}]</tt> or <tt>BINARY[#{section}]</tt>.
105
104
  #
106
- class FetchData < Struct.new(:seqno, :attr)
107
- ##
108
- # method: seqno
109
- # :call-seq: seqno -> Integer
110
- #
111
- # The message sequence number.
112
- #
113
- # [Note]
114
- # This is never the unique identifier (UID), not even for the
115
- # Net::IMAP#uid_fetch result. The UID is available from #uid, if it was
116
- # returned.
117
-
105
+ class FetchStruct < Struct
118
106
  ##
119
107
  # method: attr
120
108
  # :call-seq: attr -> hash
@@ -123,9 +111,6 @@ module Net
123
111
  # corresponding data item. Standard data items have corresponding
124
112
  # accessor methods. The definitions of each attribute type is documented
125
113
  # on its accessor.
126
- #
127
- # >>>
128
- # *Note:* #seqno is not a message attribute.
129
114
 
130
115
  # :call-seq: attr_upcase -> hash
131
116
  #
@@ -142,7 +127,7 @@ module Net
142
127
  #
143
128
  # This is the same as getting the value for <tt>"BODY"</tt> from #attr.
144
129
  #
145
- # [Note]
130
+ # [NOTE:]
146
131
  # Use #message, #part, #header, #header_fields, #header_fields_not,
147
132
  # #text, or #mime to retrieve <tt>BODY[#{section_spec}]</tt> attributes.
148
133
  def body; attr["BODY"] end
@@ -235,7 +220,7 @@ module Net
235
220
  fields && except and
236
221
  raise ArgumentError, "conflicting 'fields' and 'except' arguments"
237
222
  if fields
238
- text = "HEADER.FIELDS (%s)" % [fields.join(" ").upcase]
223
+ text = "HEADER.FIELDS (%s)" % [fields.join(" ").upcase]
239
224
  attr_upcase[body_section_attr(part_nums, text, offset: offset)]
240
225
  elsif except
241
226
  text = "HEADER.FIELDS.NOT (%s)" % [except.join(" ").upcase]
@@ -308,6 +293,7 @@ module Net
308
293
  # This is the same as getting the value for <tt>"BODYSTRUCTURE"</tt> from
309
294
  # #attr.
310
295
  def bodystructure; attr["BODYSTRUCTURE"] end
296
+
311
297
  alias body_structure bodystructure
312
298
 
313
299
  # :call-seq: envelope -> Envelope or nil
@@ -320,7 +306,7 @@ module Net
320
306
  # #attr.
321
307
  def envelope; attr["ENVELOPE"] end
322
308
 
323
- # :call-seq: flags -> array of Symbols and Strings
309
+ # :call-seq: flags -> array of Symbols and Strings, or nil
324
310
  #
325
311
  # A array of flags that are set for this message. System flags are
326
312
  # symbols that have been capitalized by String#capitalize. Keyword flags
@@ -328,7 +314,7 @@ module Net
328
314
  #
329
315
  # This is the same as getting the value for <tt>"FLAGS"</tt> from #attr.
330
316
  #
331
- # [Note]
317
+ # [NOTE:]
332
318
  # The +FLAGS+ field is dynamic, and can change for a uniquely identified
333
319
  # message.
334
320
  def flags; attr["FLAGS"] end
@@ -336,40 +322,41 @@ module Net
336
322
  # :call-seq: internaldate -> Time or nil
337
323
  #
338
324
  # The internal date and time of the message on the server. This is not
339
- # the date and time in the [RFC5322[https://tools.ietf.org/html/rfc5322]]
325
+ # the date and time in the [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]
340
326
  # header, but rather a date and time which reflects when the message was
341
327
  # received.
342
328
  #
343
329
  # This is similar to getting the value for <tt>"INTERNALDATE"</tt> from
344
330
  # #attr.
345
331
  #
346
- # [Note]
332
+ # [NOTE:]
347
333
  # <tt>attr["INTERNALDATE"]</tt> returns a string, and this method
348
334
  # returns a Time object.
349
335
  def internaldate
350
336
  attr["INTERNALDATE"]&.then { IMAP.decode_time _1 }
351
337
  end
338
+
352
339
  alias internal_date internaldate
353
340
 
354
- # :call-seq: rfc822 -> String
341
+ # :call-seq: rfc822 -> String or nil
355
342
  #
356
343
  # Semantically equivalent to #message with no arguments.
357
344
  #
358
345
  # This is the same as getting the value for <tt>"RFC822"</tt> from #attr.
359
346
  #
360
- # [Note]
347
+ # [NOTE:]
361
348
  # +IMAP4rev2+ deprecates <tt>RFC822</tt>.
362
349
  def rfc822; attr["RFC822"] end
363
350
 
364
- # :call-seq: rfc822_size -> Integer
351
+ # :call-seq: rfc822_size -> Integer or nil
365
352
  #
366
- # A number expressing the [RFC5322[https://tools.ietf.org/html/rfc5322]]
353
+ # A number expressing the [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]
367
354
  # size of the message.
368
355
  #
369
356
  # This is the same as getting the value for <tt>"RFC822.SIZE"</tt> from
370
357
  # #attr.
371
358
  #
372
- # [Note]
359
+ # [NOTE:]
373
360
  # \IMAP was originally developed for the older
374
361
  # RFC822[https://www.rfc-editor.org/rfc/rfc822.html] standard, and as a
375
362
  # consequence several fetch items in \IMAP incorporate "RFC822" in their
@@ -379,34 +366,45 @@ module Net
379
366
  # interpreted as a reference to the updated
380
367
  # RFC5322[https://www.rfc-editor.org/rfc/rfc5322.html] standard.
381
368
  def rfc822_size; attr["RFC822.SIZE"] end
382
- alias size rfc822_size
383
369
 
384
- # :call-seq: rfc822_header -> String
370
+ # NOTE: a bug in rdoc 6.7 prevents us from adding a call-seq to
371
+ # rfc822_size _and_ aliasing size => rfc822_size. Is it because this
372
+ # class inherits from Struct?
373
+
374
+ # Alias for: rfc822_size
375
+ def size; rfc822_size end
376
+
377
+
378
+ # :call-seq: rfc822_header -> String or nil
385
379
  #
386
380
  # Semantically equivalent to #header, with no arguments.
387
381
  #
388
382
  # This is the same as getting the value for <tt>"RFC822.HEADER"</tt> from #attr.
389
383
  #
390
- # [Note]
384
+ # [NOTE:]
391
385
  # +IMAP4rev2+ deprecates <tt>RFC822.HEADER</tt>.
392
386
  def rfc822_header; attr["RFC822.HEADER"] end
393
387
 
394
- # :call-seq: rfc822_text -> String
388
+ # :call-seq: rfc822_text -> String or nil
395
389
  #
396
390
  # Semantically equivalent to #text, with no arguments.
397
391
  #
398
392
  # This is the same as getting the value for <tt>"RFC822.TEXT"</tt> from
399
393
  # #attr.
400
394
  #
401
- # [Note]
395
+ # [NOTE:]
402
396
  # +IMAP4rev2+ deprecates <tt>RFC822.TEXT</tt>.
403
397
  def rfc822_text; attr["RFC822.TEXT"] end
404
398
 
405
- # :call-seq: uid -> Integer
399
+ # :call-seq: uid -> Integer or nil
406
400
  #
407
401
  # A number expressing the unique identifier of the message.
408
402
  #
409
403
  # This is the same as getting the value for <tt>"UID"</tt> from #attr.
404
+ #
405
+ # [NOTE:]
406
+ # For UIDFetchData, this returns the uniqueid at the beginning of the
407
+ # +UIDFETCH+ response, _not_ the value from #attr.
410
408
  def uid; attr["UID"] end
411
409
 
412
410
  # :call-seq:
@@ -452,7 +450,7 @@ module Net
452
450
  attr[section_attr("BINARY.SIZE", part_nums)]
453
451
  end
454
452
 
455
- # :call-seq: modseq -> Integer
453
+ # :call-seq: modseq -> Integer or nil
456
454
  #
457
455
  # The modification sequence number associated with this IMAP message.
458
456
  #
@@ -461,7 +459,7 @@ module Net
461
459
  # The server must support the +CONDSTORE+ extension
462
460
  # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
463
461
  #
464
- # [Note]
462
+ # [NOTE:]
465
463
  # The +MODSEQ+ field is dynamic, and can change for a uniquely
466
464
  # identified message.
467
465
  def modseq; attr["MODSEQ"] end
@@ -508,11 +506,92 @@ module Net
508
506
  spec = Array(part).flatten.map { Integer(_1) }
509
507
  spec << text if text
510
508
  spec = spec.join(".")
511
- if offset then "%s[%s]<%d>" % [attr, spec, Integer(offset)]
512
- else "%s[%s]" % [attr, spec]
513
- end
509
+ if offset then "%s[%s]<%d>" % [attr, spec, Integer(offset)] else "%s[%s]" % [attr, spec] end
514
510
  end
511
+ end
515
512
 
513
+ # Net::IMAP::FetchData represents the contents of a +FETCH+ response.
514
+ # Net::IMAP#fetch, Net::IMAP#uid_fetch, Net::IMAP#store, and
515
+ # Net::IMAP#uid_store all return arrays of FetchData objects, except when
516
+ # the +UIDONLY+ extension is enabled.
517
+ #
518
+ # See FetchStruct documentation for a list of standard message attributes.
519
+ class FetchData < FetchStruct.new(:seqno, :attr)
520
+ ##
521
+ # method: seqno
522
+ # :call-seq: seqno -> Integer
523
+ #
524
+ # The message sequence number.
525
+ #
526
+ # [NOTE:]
527
+ # This is not the same as the unique identifier (UID), not even for the
528
+ # Net::IMAP#uid_fetch result. The UID is available from #uid, if it was
529
+ # returned.
530
+ #
531
+ # [NOTE:]
532
+ # UIDFetchData will raise a NoMethodError.
533
+
534
+ ##
535
+ # method: attr
536
+ # :call-seq: attr -> hash
537
+ #
538
+ # Each key specifies a message attribute, and the value is the
539
+ # corresponding data item. Standard data items have corresponding
540
+ # accessor methods. The definitions of each attribute type is documented
541
+ # on its accessor.
542
+ #
543
+ # See FetchStruct documentation for message attribute accessors.
544
+ #
545
+ # [NOTE:]
546
+ # #seqno is not a message attribute.
547
+ end
548
+
549
+ # Net::IMAP::UIDFetchData represents the contents of a +UIDFETCH+ response,
550
+ # When the +UIDONLY+ extension has been enabled, Net::IMAP#uid_fetch and
551
+ # Net::IMAP#uid_store will both return an array of UIDFetchData objects.
552
+ #
553
+ # UIDFetchData contains the same message attributes as FetchData. However,
554
+ # +UIDFETCH+ responses return the UID at the beginning of the response,
555
+ # replacing FetchData#seqno. UIDFetchData never contains a message sequence
556
+ # number.
557
+ #
558
+ # See FetchStruct documentation for a list of standard message attributes.
559
+ class UIDFetchData < FetchStruct.new(:uid, :attr)
560
+ ##
561
+ # method: uid
562
+ # call-seq: uid -> Integer
563
+ #
564
+ # A number expressing the unique identifier of the message.
565
+ #
566
+ # [NOTE:]
567
+ # Although #attr may _also_ have a redundant +UID+ attribute, #uid
568
+ # returns the uniqueid at the beginning of the +UIDFETCH+ response.
569
+
570
+ ##
571
+ # method: attr
572
+ # call-seq: attr -> hash
573
+ #
574
+ # Each key specifies a message attribute, and the value is the
575
+ # corresponding data item. Standard data items have corresponding
576
+ # accessor methods. The definitions of each attribute type is documented
577
+ # on its accessor.
578
+ #
579
+ # See FetchStruct documentation for message attribute accessors.
580
+ #
581
+ # [NOTE:]
582
+ # #uid is not a message attribute. Although the server may return a
583
+ # +UID+ message attribute, it is not required to. #uid is taken from
584
+ # its corresponding +UIDFETCH+ field.
585
+
586
+ # UIDFetchData will print a warning if <tt>#attr["UID"]</tt> is present
587
+ # but not identical to #uid.
588
+ def initialize(...)
589
+ super
590
+ attr and
591
+ attr_uid = attr["UID"] and
592
+ attr_uid != uid and
593
+ warn "#{self.class} UIDs do not match (#{attr_uid} != #{uid})"
594
+ end
516
595
  end
517
596
  end
518
597
  end