net-imap 0.3.4 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/COPYING +56 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +3 -22
  6. data/README.md +25 -8
  7. data/Rakefile +0 -7
  8. data/docs/styles.css +72 -23
  9. data/lib/net/imap/authenticators.rb +26 -57
  10. data/lib/net/imap/command_data.rb +74 -54
  11. data/lib/net/imap/config/attr_accessors.rb +75 -0
  12. data/lib/net/imap/config/attr_inheritance.rb +90 -0
  13. data/lib/net/imap/config/attr_type_coercion.rb +61 -0
  14. data/lib/net/imap/config.rb +470 -0
  15. data/lib/net/imap/data_encoding.rb +21 -9
  16. data/lib/net/imap/data_lite.rb +226 -0
  17. data/lib/net/imap/deprecated_client_options.rb +142 -0
  18. data/lib/net/imap/errors.rb +27 -1
  19. data/lib/net/imap/esearch_result.rb +180 -0
  20. data/lib/net/imap/fetch_data.rb +597 -0
  21. data/lib/net/imap/flags.rb +1 -1
  22. data/lib/net/imap/response_data.rb +250 -440
  23. data/lib/net/imap/response_parser/parser_utils.rb +245 -0
  24. data/lib/net/imap/response_parser.rb +1867 -1184
  25. data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
  26. data/lib/net/imap/sasl/authentication_exchange.rb +139 -0
  27. data/lib/net/imap/sasl/authenticators.rb +122 -0
  28. data/lib/net/imap/sasl/client_adapter.rb +123 -0
  29. data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +24 -14
  30. data/lib/net/imap/sasl/digest_md5_authenticator.rb +342 -0
  31. data/lib/net/imap/sasl/external_authenticator.rb +83 -0
  32. data/lib/net/imap/sasl/gs2_header.rb +80 -0
  33. data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +28 -18
  34. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
  35. data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
  36. data/lib/net/imap/sasl/protocol_adapters.rb +101 -0
  37. data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
  38. data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
  39. data/lib/net/imap/sasl/stringprep.rb +6 -66
  40. data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
  41. data/lib/net/imap/sasl.rb +148 -44
  42. data/lib/net/imap/sasl_adapter.rb +20 -0
  43. data/lib/net/imap/search_result.rb +146 -0
  44. data/lib/net/imap/sequence_set.rb +1565 -0
  45. data/lib/net/imap/stringprep/nameprep.rb +70 -0
  46. data/lib/net/imap/stringprep/saslprep.rb +69 -0
  47. data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
  48. data/lib/net/imap/stringprep/tables.rb +146 -0
  49. data/lib/net/imap/stringprep/trace.rb +85 -0
  50. data/lib/net/imap/stringprep.rb +159 -0
  51. data/lib/net/imap/uidplus_data.rb +244 -0
  52. data/lib/net/imap/vanished_data.rb +56 -0
  53. data/lib/net/imap.rb +2090 -823
  54. data/net-imap.gemspec +7 -8
  55. data/rakelib/benchmarks.rake +91 -0
  56. data/rakelib/rfcs.rake +2 -0
  57. data/rakelib/saslprep.rake +4 -4
  58. data/rakelib/string_prep_tables_generator.rb +84 -60
  59. data/sample/net-imap.rb +167 -0
  60. metadata +45 -49
  61. data/.github/dependabot.yml +0 -6
  62. data/.github/workflows/test.yml +0 -31
  63. data/.gitignore +0 -10
  64. data/benchmarks/stringprep.yml +0 -65
  65. data/benchmarks/table-regexps.yml +0 -39
  66. data/lib/net/imap/authenticators/digest_md5.rb +0 -115
  67. data/lib/net/imap/authenticators/plain.rb +0 -41
  68. data/lib/net/imap/authenticators/xoauth2.rb +0 -20
  69. data/lib/net/imap/sasl/saslprep.rb +0 -55
  70. data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
  71. data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
@@ -0,0 +1,597 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP < Protocol
5
+
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
+ #
10
+ # === Fetch attributes
11
+ #
12
+ # See {[IMAP4rev1 §7.4.2]}[https://www.rfc-editor.org/rfc/rfc3501.html#section-7.4.2]
13
+ # and {[IMAP4rev2 §7.5.2]}[https://www.rfc-editor.org/rfc/rfc9051.html#section-7.5.2]
14
+ # for a full description of the standard fetch response data items, and
15
+ # Net::IMAP@Message+envelope+and+body+structure for other relevant RFCs.
16
+ #
17
+ # ==== Static fetch data items
18
+ #
19
+ # Most message attributes are static, and must never change for a given
20
+ # <tt>(server, account, mailbox, UIDVALIDITY, UID)</tt> tuple.
21
+ #
22
+ # The static fetch data items defined by both
23
+ # IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501.html] and
24
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html] are:
25
+ #
26
+ # * <b><tt>"UID"</tt></b> --- See #uid.
27
+ # * <b><tt>"BODY"</tt></b> --- See #body.
28
+ # * <b><tt>"BODY[#{section_spec}]"</tt></b>,
29
+ # <b><tt>"BODY[#{section_spec}]<#{offset}>"</tt></b> --- See #message,
30
+ # #part, #header, #header_fields, #header_fields_not, #mime, and #text.
31
+ # * <b><tt>"BODYSTRUCTURE"</tt></b> --- See #bodystructure.
32
+ # * <b><tt>"ENVELOPE"</tt></b> --- See #envelope.
33
+ # * <b><tt>"INTERNALDATE"</tt></b> --- See #internaldate.
34
+ # * <b><tt>"RFC822.SIZE"</tt></b> --- See #rfc822_size.
35
+ #
36
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html] adds the
37
+ # additional fetch items from the +BINARY+ extension
38
+ # {[RFC3516]}[https://www.rfc-editor.org/rfc/rfc3516.html]:
39
+ #
40
+ # * <b><tt>"BINARY[#{part}]"</tt></b>,
41
+ # <b><tt>"BINARY[#{part}]<#{offset}>"</tt></b> -- See #binary.
42
+ # * <b><tt>"BINARY.SIZE[#{part}]"</tt></b> -- See #binary_size.
43
+ #
44
+ # Several static message attributes in
45
+ # IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501.html] are obsolete and
46
+ # been removed from
47
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html]:
48
+ #
49
+ # * <b><tt>"RFC822"</tt></b> --- See #rfc822 or replace with
50
+ # <tt>"BODY[]"</tt> and #message.
51
+ # * <b><tt>"RFC822.HEADER"</tt></b> --- See #rfc822_header or replace with
52
+ # <tt>"BODY[HEADER]"</tt> and #header.
53
+ # * <b><tt>"RFC822.TEXT"</tt></b> --- See #rfc822_text or replace with
54
+ # <tt>"BODY[TEXT]"</tt> and #text.
55
+ #
56
+ # Net::IMAP supports static attributes defined by the following extensions:
57
+ # * +OBJECTID+ {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html]
58
+ # * <b><tt>"EMAILID"</tt></b> --- See #emailid.
59
+ # * <b><tt>"THREADID"</tt></b> --- See #threadid.
60
+ #
61
+ # * +X-GM-EXT-1+ {[non-standard Gmail
62
+ # extension]}[https://developers.google.com/gmail/imap/imap-extensions]
63
+ # * <b><tt>"X-GM-MSGID"</tt></b> --- unique message ID. Access via #attr.
64
+ # * <b><tt>"X-GM-THRID"</tt></b> --- Thread ID. Access via #attr.
65
+ #
66
+ # [NOTE:]
67
+ # Additional static fields are defined in other \IMAP extensions, but
68
+ # Net::IMAP can't parse them yet.
69
+ #
70
+ # ==== Dynamic message attributes
71
+ #
72
+ # Some message attributes can be dynamically changed, for example using the
73
+ # {STORE command}[rdoc-ref:Net::IMAP#store].
74
+ #
75
+ # The only dynamic message attribute defined by
76
+ # IMAP4rev1[https://www.rfc-editor.org/rfc/rfc3501.html] and
77
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html] is:
78
+ #
79
+ # * <b><tt>"FLAGS"</tt></b> --- See #flags.
80
+ #
81
+ # Net::IMAP supports dynamic attributes defined by the following extensions:
82
+ #
83
+ # * +CONDSTORE+ {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html]:
84
+ # * <b><tt>"MODSEQ"</tt></b> --- See #modseq.
85
+ # * +X-GM-EXT-1+ {[non-standard Gmail
86
+ # extension]}[https://developers.google.com/gmail/imap/imap-extensions]
87
+ # * <b><tt>"X-GM-LABELS"</tt></b> --- Gmail labels. Access via #attr.
88
+ #
89
+ # [NOTE:]
90
+ # Additional dynamic fields are defined in other \IMAP extensions, but
91
+ # Net::IMAP can't parse them yet.
92
+ #
93
+ # === Implicitly setting <tt>\Seen</tt> and using +PEEK+
94
+ #
95
+ # Unless the mailbox has been opened as read-only, fetching
96
+ # <tt>BODY[#{section}]</tt> or <tt>BINARY[#{section}]</tt>
97
+ # will implicitly set the <tt>\Seen</tt> flag. To avoid this, fetch using
98
+ # <tt>BODY.PEEK[#{section}]</tt> or <tt>BINARY.PEEK[#{section}]</tt>
99
+ # instead.
100
+ #
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>.
104
+ #
105
+ class FetchStruct < Struct
106
+ ##
107
+ # method: attr
108
+ # :call-seq: attr -> hash
109
+ #
110
+ # Each key specifies a message attribute, and the value is the
111
+ # corresponding data item. Standard data items have corresponding
112
+ # accessor methods. The definitions of each attribute type is documented
113
+ # on its accessor.
114
+
115
+ # :call-seq: attr_upcase -> hash
116
+ #
117
+ # A transformation of #attr, with all the keys converted to upper case.
118
+ #
119
+ # Header field names are case-preserved but not case-sensitive, so this is
120
+ # used by #header_fields and #header_fields_not.
121
+ def attr_upcase; attr.transform_keys(&:upcase) end
122
+
123
+ # :call-seq:
124
+ # body -> body structure or nil
125
+ #
126
+ # Returns an alternate form of #bodystructure, without any extension data.
127
+ #
128
+ # This is the same as getting the value for <tt>"BODY"</tt> from #attr.
129
+ #
130
+ # [NOTE:]
131
+ # Use #message, #part, #header, #header_fields, #header_fields_not,
132
+ # #text, or #mime to retrieve <tt>BODY[#{section_spec}]</tt> attributes.
133
+ def body; attr["BODY"] end
134
+
135
+ # :call-seq:
136
+ # message(offset: bytes) -> string or nil
137
+ #
138
+ # The RFC5322[https://www.rfc-editor.org/rfc/rfc5322.html]
139
+ # expression of the entire message, as a string.
140
+ #
141
+ # See #part for a description of +offset+.
142
+ #
143
+ # <em>RFC5322 messages can be parsed using the "mail" gem.</em>
144
+ #
145
+ # This is the same as getting the value for <tt>"BODY[]"</tt> or
146
+ # <tt>"BODY[]<#{offset}>"</tt> from #attr.
147
+ #
148
+ # See also: #header, #text, and #mime.
149
+ def message(offset: nil) attr[body_section_attr(offset: offset)] end
150
+
151
+ # :call-seq:
152
+ # part(*part_nums, offset: bytes) -> string or nil
153
+ #
154
+ # The string representation of a particular MIME part.
155
+ #
156
+ # +part_nums+ forms a path of MIME part numbers, counting up from +1+,
157
+ # which may specify an arbitrarily nested part, similarly to Array#dig.
158
+ # Messages that don't use MIME, or MIME messages that are not multipart
159
+ # and don't hold an encapsulated message, only have part +1+.
160
+ #
161
+ # If a zero-based +offset+ is given, the returned string is a substring of
162
+ # the entire contents, starting at that origin octet. This means that
163
+ # <tt>BODY[]<0></tt> MAY be truncated, but <tt>BODY[]</tt> is never
164
+ # truncated.
165
+ #
166
+ # This is the same as getting the value of
167
+ # <tt>"BODY[#{part_nums.join(".")}]"</tt> or
168
+ # <tt>"BODY[#{part_nums.join(".")}]<#{offset}>"</tt> from #attr.
169
+ #
170
+ # See also: #message, #header, #text, and #mime.
171
+ def part(index, *subparts, offset: nil)
172
+ attr[body_section_attr([index, *subparts], offset: offset)]
173
+ end
174
+
175
+ # :call-seq:
176
+ # header(*part_nums, offset: nil) -> string or nil
177
+ # header(*part_nums, fields: names, offset: nil) -> string or nil
178
+ # header(*part_nums, except: names, offset: nil) -> string or nil
179
+ #
180
+ # The {[RFC5322]}[https://www.rfc-editor.org/rfc/rfc5322.html] header of a
181
+ # message or of an encapsulated
182
+ # {[MIME-IMT]}[https://www.rfc-editor.org/rfc/rfc2046.html]
183
+ # MESSAGE/RFC822 or MESSAGE/GLOBAL message.
184
+ #
185
+ # <em>Headers can be parsed using the "mail" gem.</em>
186
+ #
187
+ # See #part for a description of +part_nums+ and +offset+.
188
+ #
189
+ # ==== Without +fields+ or +except+
190
+ # This is the same as getting the value from #attr for one of:
191
+ # * <tt>BODY[HEADER]</tt>
192
+ # * <tt>BODY[HEADER]<#{offset}></tt>
193
+ # * <tt>BODY[#{part_nums.join "."}.HEADER]"</tt>
194
+ # * <tt>BODY[#{part_nums.join "."}.HEADER]<#{offset}>"</tt>
195
+ #
196
+ # ==== With +fields+
197
+ # When +fields+ is sent, returns a subset of the header which contains
198
+ # only the header fields that match one of the names in the list.
199
+ #
200
+ # This is the same as getting the value from #attr_upcase for one of:
201
+ # * <tt>BODY[HEADER.FIELDS (#{names.join " "})]</tt>
202
+ # * <tt>BODY[HEADER.FIELDS (#{names.join " "})]<#{offset}></tt>
203
+ # * <tt>BODY[#{part_nums.join "."}.HEADER.FIELDS (#{names.join " "})]</tt>
204
+ # * <tt>BODY[#{part_nums.join "."}.HEADER.FIELDS (#{names.join " "})]<#{offset}></tt>
205
+ #
206
+ # See also: #header_fields
207
+ #
208
+ # ==== With +except+
209
+ # When +except+ is sent, returns a subset of the header which contains
210
+ # only the header fields that do _not_ match one of the names in the list.
211
+ #
212
+ # This is the same as getting the value from #attr_upcase for one of:
213
+ # * <tt>BODY[HEADER.FIELDS.NOT (#{names.join " "})]</tt>
214
+ # * <tt>BODY[HEADER.FIELDS.NOT (#{names.join " "})]<#{offset}></tt>
215
+ # * <tt>BODY[#{part_nums.join "."}.HEADER.FIELDS.NOT (#{names.join " "})]</tt>
216
+ # * <tt>BODY[#{part_nums.join "."}.HEADER.FIELDS.NOT (#{names.join " "})]<#{offset}></tt>
217
+ #
218
+ # See also: #header_fields_not
219
+ def header(*part_nums, fields: nil, except: nil, offset: nil)
220
+ fields && except and
221
+ raise ArgumentError, "conflicting 'fields' and 'except' arguments"
222
+ if fields
223
+ text = "HEADER.FIELDS (%s)" % [fields.join(" ").upcase]
224
+ attr_upcase[body_section_attr(part_nums, text, offset: offset)]
225
+ elsif except
226
+ text = "HEADER.FIELDS.NOT (%s)" % [except.join(" ").upcase]
227
+ attr_upcase[body_section_attr(part_nums, text, offset: offset)]
228
+ else
229
+ attr[body_section_attr(part_nums, "HEADER", offset: offset)]
230
+ end
231
+ end
232
+
233
+ # :call-seq:
234
+ # header_fields(*names, part: [], offset: nil) -> string or nil
235
+ #
236
+ # The result from #header when called with <tt>fields: names</tt>.
237
+ def header_fields(first, *rest, part: [], offset: nil)
238
+ header(*part, fields: [first, *rest], offset: offset)
239
+ end
240
+
241
+ # :call-seq:
242
+ # header_fields_not(*names, part: [], offset: nil) -> string or nil
243
+ #
244
+ # The result from #header when called with <tt>except: names</tt>.
245
+ def header_fields_not(first, *rest, part: [], offset: nil)
246
+ header(*part, except: [first, *rest], offset: offset)
247
+ end
248
+
249
+ # :call-seq:
250
+ # mime(*part_nums) -> string or nil
251
+ # mime(*part_nums, offset: bytes) -> string or nil
252
+ #
253
+ # The {[MIME-IMB]}[https://www.rfc-editor.org/rfc/rfc2045.html] header for
254
+ # a message part, if it was fetched.
255
+ #
256
+ # See #part for a description of +part_nums+ and +offset+.
257
+ #
258
+ # This is the same as getting the value for
259
+ # <tt>"BODY[#{part_nums}.MIME]"</tt> or
260
+ # <tt>"BODY[#{part_nums}.MIME]<#{offset}>"</tt> from #attr.
261
+ #
262
+ # See also: #message, #header, and #text.
263
+ def mime(part, *subparts, offset: nil)
264
+ attr[body_section_attr([part, *subparts], "MIME", offset: offset)]
265
+ end
266
+
267
+ # :call-seq:
268
+ # text(*part_nums) -> string or nil
269
+ # text(*part_nums, offset: bytes) -> string or nil
270
+ #
271
+ # The text body of a message or a message part, if it was fetched,
272
+ # omitting the {[RFC5322]}[https://www.rfc-editor.org/rfc/rfc5322.html]
273
+ # header.
274
+ #
275
+ # See #part for a description of +part_nums+ and +offset+.
276
+ #
277
+ # This is the same as getting the value from #attr for one of:
278
+ # * <tt>"BODY[TEXT]"</tt>,
279
+ # * <tt>"BODY[TEXT]<#{offset}>"</tt>,
280
+ # * <tt>"BODY[#{section}.TEXT]"</tt>, or
281
+ # * <tt>"BODY[#{section}.TEXT]<#{offset}>"</tt>.
282
+ #
283
+ # See also: #message, #header, and #mime.
284
+ def text(*part, offset: nil)
285
+ attr[body_section_attr(part, "TEXT", offset: offset)]
286
+ end
287
+
288
+ # :call-seq:
289
+ # bodystructure -> BodyStructure struct or nil
290
+ #
291
+ # A BodyStructure object that describes the message, if it was fetched.
292
+ #
293
+ # This is the same as getting the value for <tt>"BODYSTRUCTURE"</tt> from
294
+ # #attr.
295
+ def bodystructure; attr["BODYSTRUCTURE"] end
296
+
297
+ alias body_structure bodystructure
298
+
299
+ # :call-seq: envelope -> Envelope or nil
300
+ #
301
+ # An Envelope object that describes the envelope structure of a message.
302
+ # See the documentation for Envelope for a description of the envelope
303
+ # structure attributes.
304
+ #
305
+ # This is the same as getting the value for <tt>"ENVELOPE"</tt> from
306
+ # #attr.
307
+ def envelope; attr["ENVELOPE"] end
308
+
309
+ # :call-seq: flags -> array of Symbols and Strings, or nil
310
+ #
311
+ # A array of flags that are set for this message. System flags are
312
+ # symbols that have been capitalized by String#capitalize. Keyword flags
313
+ # are strings and their case is not changed.
314
+ #
315
+ # This is the same as getting the value for <tt>"FLAGS"</tt> from #attr.
316
+ #
317
+ # [NOTE:]
318
+ # The +FLAGS+ field is dynamic, and can change for a uniquely identified
319
+ # message.
320
+ def flags; attr["FLAGS"] end
321
+
322
+ # :call-seq: internaldate -> Time or nil
323
+ #
324
+ # The internal date and time of the message on the server. This is not
325
+ # the date and time in the [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]
326
+ # header, but rather a date and time which reflects when the message was
327
+ # received.
328
+ #
329
+ # This is similar to getting the value for <tt>"INTERNALDATE"</tt> from
330
+ # #attr.
331
+ #
332
+ # [NOTE:]
333
+ # <tt>attr["INTERNALDATE"]</tt> returns a string, and this method
334
+ # returns a Time object.
335
+ def internaldate
336
+ attr["INTERNALDATE"]&.then { IMAP.decode_time _1 }
337
+ end
338
+
339
+ alias internal_date internaldate
340
+
341
+ # :call-seq: rfc822 -> String or nil
342
+ #
343
+ # Semantically equivalent to #message with no arguments.
344
+ #
345
+ # This is the same as getting the value for <tt>"RFC822"</tt> from #attr.
346
+ #
347
+ # [NOTE:]
348
+ # +IMAP4rev2+ deprecates <tt>RFC822</tt>.
349
+ def rfc822; attr["RFC822"] end
350
+
351
+ # :call-seq: rfc822_size -> Integer or nil
352
+ #
353
+ # A number expressing the [RFC5322[https://www.rfc-editor.org/rfc/rfc5322]]
354
+ # size of the message.
355
+ #
356
+ # This is the same as getting the value for <tt>"RFC822.SIZE"</tt> from
357
+ # #attr.
358
+ #
359
+ # [NOTE:]
360
+ # \IMAP was originally developed for the older
361
+ # RFC822[https://www.rfc-editor.org/rfc/rfc822.html] standard, and as a
362
+ # consequence several fetch items in \IMAP incorporate "RFC822" in their
363
+ # name. With the exception of +RFC822.SIZE+, there are more modern
364
+ # replacements; for example, the modern version of +RFC822.HEADER+ is
365
+ # <tt>BODY.PEEK[HEADER]</tt>. In all cases, "RFC822" should be
366
+ # interpreted as a reference to the updated
367
+ # RFC5322[https://www.rfc-editor.org/rfc/rfc5322.html] standard.
368
+ def rfc822_size; attr["RFC822.SIZE"] end
369
+
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
379
+ #
380
+ # Semantically equivalent to #header, with no arguments.
381
+ #
382
+ # This is the same as getting the value for <tt>"RFC822.HEADER"</tt> from #attr.
383
+ #
384
+ # [NOTE:]
385
+ # +IMAP4rev2+ deprecates <tt>RFC822.HEADER</tt>.
386
+ def rfc822_header; attr["RFC822.HEADER"] end
387
+
388
+ # :call-seq: rfc822_text -> String or nil
389
+ #
390
+ # Semantically equivalent to #text, with no arguments.
391
+ #
392
+ # This is the same as getting the value for <tt>"RFC822.TEXT"</tt> from
393
+ # #attr.
394
+ #
395
+ # [NOTE:]
396
+ # +IMAP4rev2+ deprecates <tt>RFC822.TEXT</tt>.
397
+ def rfc822_text; attr["RFC822.TEXT"] end
398
+
399
+ # :call-seq: uid -> Integer or nil
400
+ #
401
+ # A number expressing the unique identifier of the message.
402
+ #
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.
408
+ def uid; attr["UID"] end
409
+
410
+ # :call-seq:
411
+ # binary(*part_nums, offset: nil) -> string or nil
412
+ #
413
+ # Returns the binary representation of a particular MIME part, which has
414
+ # already been decoded according to its Content-Transfer-Encoding.
415
+ #
416
+ # See #part for a description of +part_nums+ and +offset+.
417
+ #
418
+ # This is the same as getting the value of
419
+ # <tt>"BINARY[#{part_nums.join(".")}]"</tt> or
420
+ # <tt>"BINARY[#{part_nums.join(".")}]<#{offset}>"</tt> from #attr.
421
+ #
422
+ # The server must support either
423
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html]
424
+ # or the +BINARY+ extension
425
+ # {[RFC3516]}[https://www.rfc-editor.org/rfc/rfc3516.html].
426
+ #
427
+ # See also: #binary_size, #mime
428
+ def binary(*part_nums, offset: nil)
429
+ attr[section_attr("BINARY", part_nums, offset: offset)]
430
+ end
431
+
432
+ # :call-seq:
433
+ # binary_size(*part_nums) -> integer or nil
434
+ #
435
+ # Returns the decoded size of a particular MIME part (the size to expect
436
+ # in response to a <tt>BINARY</tt> fetch request).
437
+ #
438
+ # See #part for a description of +part_nums+.
439
+ #
440
+ # This is the same as getting the value of
441
+ # <tt>"BINARY.SIZE[#{part_nums.join(".")}]"</tt> from #attr.
442
+ #
443
+ # The server must support either
444
+ # IMAP4rev2[https://www.rfc-editor.org/rfc/rfc9051.html]
445
+ # or the +BINARY+ extension
446
+ # {[RFC3516]}[https://www.rfc-editor.org/rfc/rfc3516.html].
447
+ #
448
+ # See also: #binary, #mime
449
+ def binary_size(*part_nums)
450
+ attr[section_attr("BINARY.SIZE", part_nums)]
451
+ end
452
+
453
+ # :call-seq: modseq -> Integer or nil
454
+ #
455
+ # The modification sequence number associated with this IMAP message.
456
+ #
457
+ # This is the same as getting the value for <tt>"MODSEQ"</tt> from #attr.
458
+ #
459
+ # The server must support the +CONDSTORE+ extension
460
+ # {[RFC7162]}[https://www.rfc-editor.org/rfc/rfc7162.html].
461
+ #
462
+ # [NOTE:]
463
+ # The +MODSEQ+ field is dynamic, and can change for a uniquely
464
+ # identified message.
465
+ def modseq; attr["MODSEQ"] end
466
+
467
+ # :call-seq: emailid -> string or nil
468
+ #
469
+ # An ObjectID that uniquely identifies the immutable content of a single
470
+ # message.
471
+ #
472
+ # The server must return the same +EMAILID+ for both the source and
473
+ # destination messages after a COPY or MOVE command. However, it is
474
+ # possible for different messages with the same EMAILID to have different
475
+ # mutable attributes, such as flags.
476
+ #
477
+ # This is the same as getting the value for <tt>"EMAILID"</tt> from
478
+ # #attr.
479
+ #
480
+ # The server must support the +OBJECTID+ extension
481
+ # {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html].
482
+ def emailid; attr["EMAILID"] end
483
+
484
+ # :call-seq: threadid -> string or nil
485
+ #
486
+ # An ObjectID that uniquely identifies a set of messages that the server
487
+ # believes should be grouped together.
488
+ #
489
+ # It is generally based on some combination of References, In-Reply-To,
490
+ # and Subject, but the exact implementation is left up to the server
491
+ # implementation. The server should return the same thread identifier for
492
+ # related messages, even if they are in different mailboxes.
493
+ #
494
+ # This is the same as getting the value for <tt>"THREADID"</tt> from
495
+ # #attr.
496
+ #
497
+ # The server must support the +OBJECTID+ extension
498
+ # {[RFC8474]}[https://www.rfc-editor.org/rfc/rfc8474.html].
499
+ def threadid; attr["THREADID"] end
500
+
501
+ private
502
+
503
+ def body_section_attr(...) section_attr("BODY", ...) end
504
+
505
+ def section_attr(attr, part = [], text = nil, offset: nil)
506
+ spec = Array(part).flatten.map { Integer(_1) }
507
+ spec << text if text
508
+ spec = spec.join(".")
509
+ if offset then "%s[%s]<%d>" % [attr, spec, Integer(offset)] else "%s[%s]" % [attr, spec] end
510
+ end
511
+ end
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
595
+ end
596
+ end
597
+ end
@@ -71,7 +71,7 @@ module Net
71
71
  # Mailbox name attributes are not case-sensitive. <em>The current
72
72
  # implementation</em> normalizes mailbox attribute case using
73
73
  # String#capitalize, such as +:Noselect+ (not +:NoSelect+). The constants
74
- # (such as NO_SELECT) can also be used for comparison. The contants have
74
+ # (such as NO_SELECT) can also be used for comparison. The constants have
75
75
  # been defined both with and without underscores between words.
76
76
  #
77
77
  # <em>The descriptions here were copied from</em> {[RFC-9051 §