net-imap 0.5.5 → 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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/lib/net/imap/config.rb +68 -0
- data/lib/net/imap/response_data.rb +3 -49
- data/lib/net/imap/response_parser.rb +28 -13
- data/lib/net/imap/sequence_set.rb +193 -58
- data/lib/net/imap/uidplus_data.rb +244 -0
- data/lib/net/imap.rb +10 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cdbdda0ed73da899ec338f66022a16104562d3701c568b0a6d4897270a608ac5
|
4
|
+
data.tar.gz: b6a7ec70776b32f8eb57d01a0869503eb5d76f719ac091eb92e0206608e936e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 381bf2428719ed8decb5d241fda0e19f28031dd4a77980b3717bb29c37bed1c927f00e5b57862e209ecf24b2e9b38c01088d6e1a90fc4b4cc026cdd9e6611100
|
7
|
+
data.tar.gz: 513c6a77d46b6d2cf67aea4511023acc76c69940e3b1a0d0eae7223b53ff63bc8e6e009f51fef826b09f76f6ad1d92e84243e8457f59d3912db7e74bf69d3d1b
|
data/Gemfile
CHANGED
data/lib/net/imap/config.rb
CHANGED
@@ -287,6 +287,67 @@ module Net
|
|
287
287
|
#
|
288
288
|
# Alias for responses_without_block
|
289
289
|
|
290
|
+
# Whether ResponseParser should use the deprecated UIDPlusData or
|
291
|
+
# CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
|
292
|
+
# AppendUIDData for +APPENDUID+ response codes.
|
293
|
+
#
|
294
|
+
# UIDPlusData stores its data in arrays of numbers, which is vulnerable to
|
295
|
+
# a memory exhaustion denial of service attack from an untrusted or
|
296
|
+
# compromised server. Set this option to +false+ to completely block this
|
297
|
+
# vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
|
298
|
+
# mitigates this vulnerability.
|
299
|
+
#
|
300
|
+
# AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
|
301
|
+
# UIDPlusData. Most applications should be able to upgrade with little
|
302
|
+
# or no changes.
|
303
|
+
#
|
304
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
305
|
+
#
|
306
|
+
# <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
|
307
|
+
#
|
308
|
+
# <em>UIDPlusData will be removed in +v0.6+ and this config setting will
|
309
|
+
# be ignored.</em>
|
310
|
+
#
|
311
|
+
# ==== Valid options
|
312
|
+
#
|
313
|
+
# [+true+ <em>(original default)</em>]
|
314
|
+
# ResponseParser only uses UIDPlusData.
|
315
|
+
#
|
316
|
+
# [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
|
317
|
+
# ResponseParser uses UIDPlusData when the +uid-set+ size is below
|
318
|
+
# parser_max_deprecated_uidplus_data_size. Above that size,
|
319
|
+
# ResponseParser uses AppendUIDData or CopyUIDData.
|
320
|
+
#
|
321
|
+
# [+false+ <em>(planned default for +v0.6+)</em>]
|
322
|
+
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
|
323
|
+
attr_accessor :parser_use_deprecated_uidplus_data, type: [
|
324
|
+
true, :up_to_max_size, false
|
325
|
+
]
|
326
|
+
|
327
|
+
# The maximum +uid-set+ size that ResponseParser will parse into
|
328
|
+
# deprecated UIDPlusData. This limit only applies when
|
329
|
+
# parser_use_deprecated_uidplus_data is not +false+.
|
330
|
+
#
|
331
|
+
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
|
332
|
+
#
|
333
|
+
# <em>Support for limiting UIDPlusData to a maximum size was added in
|
334
|
+
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
|
335
|
+
#
|
336
|
+
# <em>UIDPlusData will be removed in +v0.6+.</em>
|
337
|
+
#
|
338
|
+
# ==== Versioned Defaults
|
339
|
+
#
|
340
|
+
# Because this limit guards against a remote server causing catastrophic
|
341
|
+
# memory exhaustion, the versioned default (used by #load_defaults) also
|
342
|
+
# applies to versions without the feature.
|
343
|
+
#
|
344
|
+
# * +0.3+ and prior: <tt>10,000</tt>
|
345
|
+
# * +0.4+: <tt>1,000</tt>
|
346
|
+
# * +0.5+: <tt>100</tt>
|
347
|
+
# * +0.6+: <tt>0</tt>
|
348
|
+
#
|
349
|
+
attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
|
350
|
+
|
290
351
|
# Creates a new config object and initialize its attribute with +attrs+.
|
291
352
|
#
|
292
353
|
# If +parent+ is not given, the global config is used by default.
|
@@ -367,6 +428,8 @@ module Net
|
|
367
428
|
sasl_ir: true,
|
368
429
|
enforce_logindisabled: true,
|
369
430
|
responses_without_block: :warn,
|
431
|
+
parser_use_deprecated_uidplus_data: :up_to_max_size,
|
432
|
+
parser_max_deprecated_uidplus_data_size: 100,
|
370
433
|
).freeze
|
371
434
|
|
372
435
|
@global = default.new
|
@@ -378,6 +441,8 @@ module Net
|
|
378
441
|
sasl_ir: false,
|
379
442
|
responses_without_block: :silence_deprecation_warning,
|
380
443
|
enforce_logindisabled: false,
|
444
|
+
parser_use_deprecated_uidplus_data: true,
|
445
|
+
parser_max_deprecated_uidplus_data_size: 10_000,
|
381
446
|
).freeze
|
382
447
|
version_defaults[0.0] = Config[0]
|
383
448
|
version_defaults[0.1] = Config[0]
|
@@ -386,12 +451,15 @@ module Net
|
|
386
451
|
|
387
452
|
version_defaults[0.4] = Config[0.3].dup.update(
|
388
453
|
sasl_ir: true,
|
454
|
+
parser_max_deprecated_uidplus_data_size: 1000,
|
389
455
|
).freeze
|
390
456
|
|
391
457
|
version_defaults[0.5] = Config[:current]
|
392
458
|
|
393
459
|
version_defaults[0.6] = Config[0.5].dup.update(
|
394
460
|
responses_without_block: :frozen_dup,
|
461
|
+
parser_use_deprecated_uidplus_data: false,
|
462
|
+
parser_max_deprecated_uidplus_data_size: 0,
|
395
463
|
).freeze
|
396
464
|
version_defaults[:next] = Config[0.6]
|
397
465
|
version_defaults[:future] = Config[:next]
|
@@ -7,6 +7,9 @@ module Net
|
|
7
7
|
autoload :UIDFetchData, "#{__dir__}/fetch_data"
|
8
8
|
autoload :SearchResult, "#{__dir__}/search_result"
|
9
9
|
autoload :SequenceSet, "#{__dir__}/sequence_set"
|
10
|
+
autoload :UIDPlusData, "#{__dir__}/uidplus_data"
|
11
|
+
autoload :AppendUIDData, "#{__dir__}/uidplus_data"
|
12
|
+
autoload :CopyUIDData, "#{__dir__}/uidplus_data"
|
10
13
|
autoload :VanishedData, "#{__dir__}/vanished_data"
|
11
14
|
|
12
15
|
# Net::IMAP::ContinuationRequest represents command continuation requests.
|
@@ -344,55 +347,6 @@ module Net
|
|
344
347
|
# code data can take.
|
345
348
|
end
|
346
349
|
|
347
|
-
# UIDPlusData represents the ResponseCode#data that accompanies the
|
348
|
-
# +APPENDUID+ and +COPYUID+ {response codes}[rdoc-ref:ResponseCode].
|
349
|
-
#
|
350
|
-
# A server that supports +UIDPLUS+ should send a UIDPlusData object inside
|
351
|
-
# every TaggedResponse returned by the append[rdoc-ref:Net::IMAP#append],
|
352
|
-
# copy[rdoc-ref:Net::IMAP#copy], move[rdoc-ref:Net::IMAP#move], {uid
|
353
|
-
# copy}[rdoc-ref:Net::IMAP#uid_copy], and {uid
|
354
|
-
# move}[rdoc-ref:Net::IMAP#uid_move] commands---unless the destination
|
355
|
-
# mailbox reports +UIDNOTSTICKY+.
|
356
|
-
#
|
357
|
-
# == Required capability
|
358
|
-
# Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
|
359
|
-
# or +IMAP4rev2+ capability.
|
360
|
-
#
|
361
|
-
class UIDPlusData < Struct.new(:uidvalidity, :source_uids, :assigned_uids)
|
362
|
-
##
|
363
|
-
# method: uidvalidity
|
364
|
-
# :call-seq: uidvalidity -> nonzero uint32
|
365
|
-
#
|
366
|
-
# The UIDVALIDITY of the destination mailbox.
|
367
|
-
|
368
|
-
##
|
369
|
-
# method: source_uids
|
370
|
-
# :call-seq: source_uids -> nil or an array of nonzero uint32
|
371
|
-
#
|
372
|
-
# The UIDs of the copied or moved messages.
|
373
|
-
#
|
374
|
-
# Note:: Returns +nil+ for Net::IMAP#append.
|
375
|
-
|
376
|
-
##
|
377
|
-
# method: assigned_uids
|
378
|
-
# :call-seq: assigned_uids -> an array of nonzero uint32
|
379
|
-
#
|
380
|
-
# The newly assigned UIDs of the copied, moved, or appended messages.
|
381
|
-
#
|
382
|
-
# Note:: This always returns an array, even when it contains only one UID.
|
383
|
-
|
384
|
-
##
|
385
|
-
# :call-seq: uid_mapping -> nil or a hash
|
386
|
-
#
|
387
|
-
# Returns a hash mapping each source UID to the newly assigned destination
|
388
|
-
# UID.
|
389
|
-
#
|
390
|
-
# Note:: Returns +nil+ for Net::IMAP#append.
|
391
|
-
def uid_mapping
|
392
|
-
source_uids&.zip(assigned_uids)&.to_h
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
350
|
# MailboxList represents the data of an untagged +LIST+ response, for a
|
397
351
|
# _single_ mailbox path. IMAP#list returns an array of MailboxList objects.
|
398
352
|
#
|
@@ -13,13 +13,17 @@ module Net
|
|
13
13
|
|
14
14
|
attr_reader :config
|
15
15
|
|
16
|
-
#
|
16
|
+
# Creates a new ResponseParser.
|
17
|
+
#
|
18
|
+
# When +config+ is frozen or global, the parser #config inherits from it.
|
19
|
+
# Otherwise, +config+ will be used directly.
|
17
20
|
def initialize(config: Config.global)
|
18
21
|
@str = nil
|
19
22
|
@pos = nil
|
20
23
|
@lex_state = nil
|
21
24
|
@token = nil
|
22
25
|
@config = Config[config]
|
26
|
+
@config = @config.new if @config == Config.global || @config.frozen?
|
23
27
|
end
|
24
28
|
|
25
29
|
# :call-seq:
|
@@ -1997,11 +2001,10 @@ module Net
|
|
1997
2001
|
#
|
1998
2002
|
# n.b, uniqueid ⊂ uid-set. To avoid inconsistent return types, we always
|
1999
2003
|
# match uid_set even if that returns a single-member array.
|
2000
|
-
#
|
2001
2004
|
def resp_code_apnd__data
|
2002
2005
|
validity = number; SP!
|
2003
2006
|
dst_uids = uid_set # uniqueid ⊂ uid-set
|
2004
|
-
|
2007
|
+
AppendUID(validity, dst_uids)
|
2005
2008
|
end
|
2006
2009
|
|
2007
2010
|
# already matched: "COPYUID"
|
@@ -2011,7 +2014,25 @@ module Net
|
|
2011
2014
|
validity = number; SP!
|
2012
2015
|
src_uids = uid_set; SP!
|
2013
2016
|
dst_uids = uid_set
|
2014
|
-
|
2017
|
+
CopyUID(validity, src_uids, dst_uids)
|
2018
|
+
end
|
2019
|
+
|
2020
|
+
def AppendUID(...) DeprecatedUIDPlus(...) || AppendUIDData.new(...) end
|
2021
|
+
def CopyUID(...) DeprecatedUIDPlus(...) || CopyUIDData.new(...) end
|
2022
|
+
|
2023
|
+
# TODO: remove this code in the v0.6.0 release
|
2024
|
+
def DeprecatedUIDPlus(validity, src_uids = nil, dst_uids)
|
2025
|
+
return unless config.parser_use_deprecated_uidplus_data
|
2026
|
+
compact_uid_sets = [src_uids, dst_uids].compact
|
2027
|
+
count = compact_uid_sets.map { _1.count_with_duplicates }.max
|
2028
|
+
max = config.parser_max_deprecated_uidplus_data_size
|
2029
|
+
if count <= max
|
2030
|
+
src_uids &&= src_uids.each_ordered_number.to_a
|
2031
|
+
dst_uids = dst_uids.each_ordered_number.to_a
|
2032
|
+
UIDPlusData.new(validity, src_uids, dst_uids)
|
2033
|
+
elsif config.parser_use_deprecated_uidplus_data != :up_to_max_size
|
2034
|
+
parse_error("uid-set is too large: %d > %d", count, max)
|
2035
|
+
end
|
2015
2036
|
end
|
2016
2037
|
|
2017
2038
|
ADDRESS_REGEXP = /\G
|
@@ -2137,15 +2158,9 @@ module Net
|
|
2137
2158
|
# uniqueid = nz-number
|
2138
2159
|
# ; Strictly ascending
|
2139
2160
|
def uid_set
|
2140
|
-
|
2141
|
-
|
2142
|
-
|
2143
|
-
when T_ATOM
|
2144
|
-
token.value.split(",").flat_map {|range|
|
2145
|
-
range = range.split(":").map {|uniqueid| Integer(uniqueid) }
|
2146
|
-
range.size == 1 ? range : Range.new(range.min, range.max).to_a
|
2147
|
-
}
|
2148
|
-
end
|
2161
|
+
set = sequence_set
|
2162
|
+
parse_error("uid-set cannot contain '*'") if set.include_star?
|
2163
|
+
set
|
2149
2164
|
end
|
2150
2165
|
|
2151
2166
|
def nil_atom
|
@@ -56,18 +56,20 @@ module Net
|
|
56
56
|
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
|
57
57
|
# set.valid_string #=> "1:10,55,1024:2048"
|
58
58
|
#
|
59
|
-
# == Normalized
|
59
|
+
# == Ordered and Normalized sets
|
60
60
|
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
# methods use this normalized representation. Most modification methods
|
66
|
-
# will convert #string to its normalized form.
|
61
|
+
# Sometimes the order of the set's members is significant, such as with the
|
62
|
+
# +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
|
63
|
+
# sequence set is created by the parser or with a single string value, that
|
64
|
+
# #string representation is preserved.
|
67
65
|
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
66
|
+
# Internally, SequenceSet stores a normalized representation which sorts all
|
67
|
+
# entries, de-duplicates numbers, and coalesces adjacent or overlapping
|
68
|
+
# ranges. Most methods use this normalized representation to achieve
|
69
|
+
# <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
|
70
|
+
# the set in its original order.
|
71
|
+
#
|
72
|
+
# Most modification methods convert #string to its normalized form. To
|
71
73
|
# preserve #string order while modifying a set, use #append, #string=, or
|
72
74
|
# #replace.
|
73
75
|
#
|
@@ -160,7 +162,7 @@ module Net
|
|
160
162
|
# - #===:
|
161
163
|
# Returns whether a given object is fully contained within +self+, or
|
162
164
|
# +nil+ if the object cannot be converted to a compatible type.
|
163
|
-
# - #cover
|
165
|
+
# - #cover?:
|
164
166
|
# Returns whether a given object is fully contained within +self+.
|
165
167
|
# - #intersect? (aliased as #overlap?):
|
166
168
|
# Returns whether +self+ and a given object have any common elements.
|
@@ -181,30 +183,41 @@ module Net
|
|
181
183
|
# - #max: Returns the maximum number in the set.
|
182
184
|
# - #minmax: Returns the minimum and maximum numbers in the set.
|
183
185
|
#
|
184
|
-
# <i>Accessing value by offset:</i>
|
186
|
+
# <i>Accessing value by offset in sorted set:</i>
|
185
187
|
# - #[] (aliased as #slice): Returns the number or consecutive subset at a
|
186
|
-
# given offset or range of offsets.
|
187
|
-
# - #at: Returns the number at a given offset.
|
188
|
-
# - #find_index: Returns the given number's offset in the set
|
188
|
+
# given offset or range of offsets in the sorted set.
|
189
|
+
# - #at: Returns the number at a given offset in the sorted set.
|
190
|
+
# - #find_index: Returns the given number's offset in the sorted set.
|
191
|
+
#
|
192
|
+
# <i>Accessing value by offset in ordered entries</i>
|
193
|
+
# - #ordered_at: Returns the number at a given offset in the ordered entries.
|
194
|
+
# - #find_ordered_index: Returns the index of the given number's first
|
195
|
+
# occurrence in entries.
|
189
196
|
#
|
190
197
|
# <i>Set cardinality:</i>
|
191
198
|
# - #count (aliased as #size): Returns the count of numbers in the set.
|
199
|
+
# Duplicated numbers are not counted.
|
192
200
|
# - #empty?: Returns whether the set has no members. \IMAP syntax does not
|
193
201
|
# allow empty sequence sets.
|
194
202
|
# - #valid?: Returns whether the set has any members.
|
195
203
|
# - #full?: Returns whether the set contains every possible value, including
|
196
204
|
# <tt>*</tt>.
|
197
205
|
#
|
206
|
+
# <i>Denormalized properties:</i>
|
207
|
+
# - #has_duplicates?: Returns whether the ordered entries repeat any
|
208
|
+
# numbers.
|
209
|
+
# - #count_duplicates: Returns the count of repeated numbers in the ordered
|
210
|
+
# entries.
|
211
|
+
# - #count_with_duplicates: Returns the count of numbers in the ordered
|
212
|
+
# entries, including any repeated numbers.
|
213
|
+
#
|
198
214
|
# === Methods for Iterating
|
199
215
|
#
|
216
|
+
# <i>Normalized (sorted and coalesced):</i>
|
200
217
|
# - #each_element: Yields each number and range in the set, sorted and
|
201
218
|
# coalesced, and returns +self+.
|
202
219
|
# - #elements (aliased as #to_a): Returns an Array of every number and range
|
203
220
|
# in the set, sorted and coalesced.
|
204
|
-
# - #each_entry: Yields each number and range in the set, unsorted and
|
205
|
-
# without deduplicating numbers or coalescing ranges, and returns +self+.
|
206
|
-
# - #entries: Returns an Array of every number and range in the set,
|
207
|
-
# unsorted and without deduplicating numbers or coalescing ranges.
|
208
221
|
# - #each_range:
|
209
222
|
# Yields each element in the set as a Range and returns +self+.
|
210
223
|
# - #ranges: Returns an Array of every element in the set, converting
|
@@ -214,6 +227,14 @@ module Net
|
|
214
227
|
# ranges into all of their contained numbers.
|
215
228
|
# - #to_set: Returns a Set containing all of the #numbers in the set.
|
216
229
|
#
|
230
|
+
# <i>Order preserving:</i>
|
231
|
+
# - #each_entry: Yields each number and range in the set, unsorted and
|
232
|
+
# without deduplicating numbers or coalescing ranges, and returns +self+.
|
233
|
+
# - #entries: Returns an Array of every number and range in the set,
|
234
|
+
# unsorted and without deduplicating numbers or coalescing ranges.
|
235
|
+
# - #each_ordered_number: Yields each number in the ordered entries and
|
236
|
+
# returns +self+.
|
237
|
+
#
|
217
238
|
# === Methods for \Set Operations
|
218
239
|
# These methods do not modify +self+.
|
219
240
|
#
|
@@ -233,19 +254,29 @@ module Net
|
|
233
254
|
# === Methods for Assigning
|
234
255
|
# These methods add or replace elements in +self+.
|
235
256
|
#
|
257
|
+
# <i>Normalized (sorted and coalesced):</i>
|
258
|
+
#
|
259
|
+
# These methods always update #string to be fully sorted and coalesced.
|
260
|
+
#
|
236
261
|
# - #add (aliased as #<<): Adds a given object to the set; returns +self+.
|
237
262
|
# - #add?: If the given object is not an element in the set, adds it and
|
238
263
|
# returns +self+; otherwise, returns +nil+.
|
239
264
|
# - #merge: Merges multiple elements into the set; returns +self+.
|
265
|
+
# - #complement!: Replaces the contents of the set with its own #complement.
|
266
|
+
#
|
267
|
+
# <i>Order preserving:</i>
|
268
|
+
#
|
269
|
+
# These methods _may_ cause #string to not be sorted or coalesced.
|
270
|
+
#
|
240
271
|
# - #append: Adds a given object to the set, appending it to the existing
|
241
272
|
# string, and returns +self+.
|
242
273
|
# - #string=: Assigns a new #string value and replaces #elements to match.
|
243
274
|
# - #replace: Replaces the contents of the set with the contents
|
244
275
|
# of a given object.
|
245
|
-
# - #complement!: Replaces the contents of the set with its own #complement.
|
246
276
|
#
|
247
277
|
# === Methods for Deleting
|
248
|
-
# These methods remove elements from +self
|
278
|
+
# These methods remove elements from +self+, and update #string to be fully
|
279
|
+
# sorted and coalesced.
|
249
280
|
#
|
250
281
|
# - #clear: Removes all elements in the set; returns +self+.
|
251
282
|
# - #delete: Removes a given object from the set; returns +self+.
|
@@ -681,8 +712,9 @@ module Net
|
|
681
712
|
modifying!
|
682
713
|
tuple = input_to_tuple object
|
683
714
|
entry = tuple_to_str tuple
|
715
|
+
string unless empty? # write @string before tuple_add
|
684
716
|
tuple_add tuple
|
685
|
-
@string = -(string ? "#{@string},#{entry}" : entry)
|
717
|
+
@string = -(@string ? "#{@string},#{entry}" : entry)
|
686
718
|
self
|
687
719
|
end
|
688
720
|
|
@@ -838,8 +870,8 @@ module Net
|
|
838
870
|
# <tt>*</tt> translates to an endless range. Use #limit to translate both
|
839
871
|
# cases to a maximum value.
|
840
872
|
#
|
841
|
-
#
|
842
|
-
#
|
873
|
+
# The returned elements will be sorted and coalesced, even when the input
|
874
|
+
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
843
875
|
#
|
844
876
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
|
845
877
|
# #=> [2, 5..9, 11..12, :*]
|
@@ -857,7 +889,7 @@ module Net
|
|
857
889
|
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
|
858
890
|
# value.
|
859
891
|
#
|
860
|
-
# The returned ranges will be
|
892
|
+
# The returned ranges will be sorted and coalesced, even when the input
|
861
893
|
# #string is not. <tt>*</tt> will sort last. See #normalize.
|
862
894
|
#
|
863
895
|
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
|
@@ -906,9 +938,7 @@ module Net
|
|
906
938
|
# Related: #entries, #each_element
|
907
939
|
def each_entry(&block) # :yields: integer or range or :*
|
908
940
|
return to_enum(__method__) unless block_given?
|
909
|
-
|
910
|
-
@string.split(",").each do yield tuple_to_entry str_to_tuple _1 end
|
911
|
-
self
|
941
|
+
each_entry_tuple do yield tuple_to_entry _1 end
|
912
942
|
end
|
913
943
|
|
914
944
|
# Yields each number or range (or <tt>:*</tt>) in #elements to the block
|
@@ -926,6 +956,16 @@ module Net
|
|
926
956
|
|
927
957
|
private
|
928
958
|
|
959
|
+
def each_entry_tuple(&block)
|
960
|
+
return to_enum(__method__) unless block_given?
|
961
|
+
if @string
|
962
|
+
@string.split(",") do block.call str_to_tuple _1 end
|
963
|
+
else
|
964
|
+
@tuples.each(&block)
|
965
|
+
end
|
966
|
+
self
|
967
|
+
end
|
968
|
+
|
929
969
|
def tuple_to_entry((min, max))
|
930
970
|
if min == STAR_INT then :*
|
931
971
|
elsif max == STAR_INT then min..
|
@@ -957,19 +997,36 @@ module Net
|
|
957
997
|
# Returns an enumerator when called without a block (even if the set
|
958
998
|
# contains <tt>*</tt>).
|
959
999
|
#
|
960
|
-
# Related: #numbers
|
1000
|
+
# Related: #numbers, #each_ordered_number
|
961
1001
|
def each_number(&block) # :yields: integer
|
962
1002
|
return to_enum(__method__) unless block_given?
|
963
1003
|
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
964
|
-
|
965
|
-
case elem
|
966
|
-
when Range then elem.each(&block)
|
967
|
-
when Integer then block.(elem)
|
968
|
-
end
|
969
|
-
end
|
1004
|
+
@tuples.each do each_number_in_tuple _1, _2, &block end
|
970
1005
|
self
|
971
1006
|
end
|
972
1007
|
|
1008
|
+
# Yields each number in #entries to the block and returns self.
|
1009
|
+
# If the set contains a <tt>*</tt>, RangeError will be raised.
|
1010
|
+
#
|
1011
|
+
# Returns an enumerator when called without a block (even if the set
|
1012
|
+
# contains <tt>*</tt>).
|
1013
|
+
#
|
1014
|
+
# Related: #entries, #each_number
|
1015
|
+
def each_ordered_number(&block)
|
1016
|
+
return to_enum(__method__) unless block_given?
|
1017
|
+
raise RangeError, '%s contains "*"' % [self.class] if include_star?
|
1018
|
+
each_entry_tuple do each_number_in_tuple _1, _2, &block end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
private def each_number_in_tuple(min, max, &block)
|
1022
|
+
if min == STAR_INT then yield :*
|
1023
|
+
elsif min == max then yield min
|
1024
|
+
elsif max != STAR_INT then (min..max).each(&block)
|
1025
|
+
else
|
1026
|
+
raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
|
1027
|
+
end
|
1028
|
+
end
|
1029
|
+
|
973
1030
|
# Returns a Set with all of the #numbers in the sequence set.
|
974
1031
|
#
|
975
1032
|
# If the set contains a <tt>*</tt>, RangeError will be raised.
|
@@ -981,8 +1038,10 @@ module Net
|
|
981
1038
|
|
982
1039
|
# Returns the count of #numbers in the set.
|
983
1040
|
#
|
984
|
-
#
|
985
|
-
# integer value)
|
1041
|
+
# <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
|
1042
|
+
# unsigned integer value).
|
1043
|
+
#
|
1044
|
+
# Related: #count_with_duplicates
|
986
1045
|
def count
|
987
1046
|
@tuples.sum(@tuples.count) { _2 - _1 } +
|
988
1047
|
(include_star? && include?(UINT32_MAX) ? -1 : 0)
|
@@ -990,33 +1049,87 @@ module Net
|
|
990
1049
|
|
991
1050
|
alias size count
|
992
1051
|
|
993
|
-
# Returns the
|
994
|
-
#
|
1052
|
+
# Returns the count of numbers in the ordered #entries, including any
|
1053
|
+
# repeated numbers.
|
1054
|
+
#
|
1055
|
+
# <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
|
1056
|
+
# unsigned integer value).
|
1057
|
+
#
|
1058
|
+
# When #string is normalized, this behaves the same as #count.
|
995
1059
|
#
|
996
|
-
# Related: #
|
1060
|
+
# Related: #entries, #count_duplicates, #has_duplicates?
|
1061
|
+
def count_with_duplicates
|
1062
|
+
return count unless @string
|
1063
|
+
each_entry_tuple.sum {|min, max|
|
1064
|
+
max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
|
1065
|
+
}
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
# Returns the count of repeated numbers in the ordered #entries, the
|
1069
|
+
# difference between #count_with_duplicates and #count.
|
1070
|
+
#
|
1071
|
+
# When #string is normalized, this is zero.
|
1072
|
+
#
|
1073
|
+
# Related: #entries, #count_with_duplicates, #has_duplicates?
|
1074
|
+
def count_duplicates
|
1075
|
+
return 0 unless @string
|
1076
|
+
count_with_duplicates - count
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
# :call-seq: has_duplicates? -> true | false
|
1080
|
+
#
|
1081
|
+
# Returns whether or not the ordered #entries repeat any numbers.
|
1082
|
+
#
|
1083
|
+
# Always returns +false+ when #string is normalized.
|
1084
|
+
#
|
1085
|
+
# Related: #entries, #count_with_duplicates, #count_duplicates?
|
1086
|
+
def has_duplicates?
|
1087
|
+
return false unless @string
|
1088
|
+
count_with_duplicates != count
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
# Returns the (sorted and deduplicated) index of +number+ in the set, or
|
1092
|
+
# +nil+ if +number+ isn't in the set.
|
1093
|
+
#
|
1094
|
+
# Related: #[], #at, #find_ordered_index
|
997
1095
|
def find_index(number)
|
998
1096
|
number = to_tuple_int number
|
999
|
-
each_tuple_with_index do |min, max, idx_min|
|
1097
|
+
each_tuple_with_index(@tuples) do |min, max, idx_min|
|
1000
1098
|
number < min and return nil
|
1001
1099
|
number <= max and return from_tuple_int(idx_min + (number - min))
|
1002
1100
|
end
|
1003
1101
|
nil
|
1004
1102
|
end
|
1005
1103
|
|
1104
|
+
# Returns the first index of +number+ in the ordered #entries, or
|
1105
|
+
# +nil+ if +number+ isn't in the set.
|
1106
|
+
#
|
1107
|
+
# Related: #find_index
|
1108
|
+
def find_ordered_index(number)
|
1109
|
+
number = to_tuple_int number
|
1110
|
+
each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
|
1111
|
+
if min <= number && number <= max
|
1112
|
+
return from_tuple_int(idx_min + (number - min))
|
1113
|
+
end
|
1114
|
+
end
|
1115
|
+
nil
|
1116
|
+
end
|
1117
|
+
|
1006
1118
|
private
|
1007
1119
|
|
1008
|
-
def each_tuple_with_index
|
1120
|
+
def each_tuple_with_index(tuples)
|
1009
1121
|
idx_min = 0
|
1010
|
-
|
1011
|
-
|
1122
|
+
tuples.each do |min, max|
|
1123
|
+
idx_max = idx_min + (max - min)
|
1124
|
+
yield min, max, idx_min, idx_max
|
1012
1125
|
idx_min = idx_max + 1
|
1013
1126
|
end
|
1014
1127
|
idx_min
|
1015
1128
|
end
|
1016
1129
|
|
1017
|
-
def reverse_each_tuple_with_index
|
1130
|
+
def reverse_each_tuple_with_index(tuples)
|
1018
1131
|
idx_max = -1
|
1019
|
-
|
1132
|
+
tuples.reverse_each do |min, max|
|
1020
1133
|
yield min, max, (idx_min = idx_max - (max - min)), idx_max
|
1021
1134
|
idx_max = idx_min - 1
|
1022
1135
|
end
|
@@ -1027,18 +1140,38 @@ module Net
|
|
1027
1140
|
|
1028
1141
|
# :call-seq: at(index) -> integer or nil
|
1029
1142
|
#
|
1030
|
-
# Returns
|
1031
|
-
#
|
1143
|
+
# Returns the number at the given +index+ in the sorted set, without
|
1144
|
+
# modifying the set.
|
1032
1145
|
#
|
1033
|
-
#
|
1146
|
+
# +index+ is interpreted the same as in #[], except that #at only allows a
|
1147
|
+
# single integer argument.
|
1148
|
+
#
|
1149
|
+
# Related: #[], #slice, #ordered_at
|
1034
1150
|
def at(index)
|
1151
|
+
lookup_number_by_tuple_index(tuples, index)
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
# :call-seq: ordered_at(index) -> integer or nil
|
1155
|
+
#
|
1156
|
+
# Returns the number at the given +index+ in the ordered #entries, without
|
1157
|
+
# modifying the set.
|
1158
|
+
#
|
1159
|
+
# +index+ is interpreted the same as in #at (and #[]), except that
|
1160
|
+
# #ordered_at applies to the ordered #entries, not the sorted set.
|
1161
|
+
#
|
1162
|
+
# Related: #[], #slice, #ordered_at
|
1163
|
+
def ordered_at(index)
|
1164
|
+
lookup_number_by_tuple_index(each_entry_tuple, index)
|
1165
|
+
end
|
1166
|
+
|
1167
|
+
private def lookup_number_by_tuple_index(tuples, index)
|
1035
1168
|
index = Integer(index.to_int)
|
1036
1169
|
if index.negative?
|
1037
|
-
reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
|
1170
|
+
reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
|
1038
1171
|
idx_min <= index and return from_tuple_int(min + (index - idx_min))
|
1039
1172
|
end
|
1040
1173
|
else
|
1041
|
-
each_tuple_with_index do |min, _, idx_min, idx_max|
|
1174
|
+
each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
|
1042
1175
|
index <= idx_max and return from_tuple_int(min + (index - idx_min))
|
1043
1176
|
end
|
1044
1177
|
end
|
@@ -1053,17 +1186,18 @@ module Net
|
|
1053
1186
|
# seqset[range] -> sequence set or nil
|
1054
1187
|
# slice(range) -> sequence set or nil
|
1055
1188
|
#
|
1056
|
-
# Returns a number or a subset from
|
1189
|
+
# Returns a number or a subset from the _sorted_ set, without modifying
|
1190
|
+
# the set.
|
1057
1191
|
#
|
1058
1192
|
# When an Integer argument +index+ is given, the number at offset +index+
|
1059
|
-
# is returned:
|
1193
|
+
# in the sorted set is returned:
|
1060
1194
|
#
|
1061
1195
|
# set = Net::IMAP::SequenceSet["10:15,20:23,26"]
|
1062
1196
|
# set[0] #=> 10
|
1063
1197
|
# set[5] #=> 15
|
1064
1198
|
# set[10] #=> 26
|
1065
1199
|
#
|
1066
|
-
# If +index+ is negative, it counts relative to the end of
|
1200
|
+
# If +index+ is negative, it counts relative to the end of the sorted set:
|
1067
1201
|
# set = Net::IMAP::SequenceSet["10:15,20:23,26"]
|
1068
1202
|
# set[-1] #=> 26
|
1069
1203
|
# set[-3] #=> 22
|
@@ -1075,13 +1209,14 @@ module Net
|
|
1075
1209
|
# set[11] #=> nil
|
1076
1210
|
# set[-12] #=> nil
|
1077
1211
|
#
|
1078
|
-
# The result is based on the
|
1079
|
-
#
|
1212
|
+
# The result is based on the sorted and de-duplicated set, not on the
|
1213
|
+
# ordered #entries in #string.
|
1080
1214
|
#
|
1081
1215
|
# set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
|
1082
1216
|
# set[0] #=> 11
|
1083
1217
|
# set[-1] #=> 23
|
1084
1218
|
#
|
1219
|
+
# Related: #at
|
1085
1220
|
def [](index, length = nil)
|
1086
1221
|
if length then slice_length(index, length)
|
1087
1222
|
elsif index.is_a?(Range) then slice_range(index)
|
@@ -1337,8 +1472,8 @@ module Net
|
|
1337
1472
|
modifying!
|
1338
1473
|
min, max = tuple
|
1339
1474
|
lower, lower_idx = tuple_gte_with_index(min - 1)
|
1340
|
-
if lower.nil? then tuples <<
|
1341
|
-
elsif (max + 1) < lower.first then tuples.insert(lower_idx,
|
1475
|
+
if lower.nil? then tuples << [min, max]
|
1476
|
+
elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max])
|
1342
1477
|
else tuple_coalesce(lower, lower_idx, min, max)
|
1343
1478
|
end
|
1344
1479
|
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP < Protocol
|
5
|
+
|
6
|
+
# *NOTE:* <em>UIDPlusData is deprecated and will be removed in the +0.6.0+
|
7
|
+
# release.</em> To use AppendUIDData and CopyUIDData before +0.6.0+, set
|
8
|
+
# Config#parser_use_deprecated_uidplus_data to +false+.
|
9
|
+
#
|
10
|
+
# UIDPlusData represents the ResponseCode#data that accompanies the
|
11
|
+
# +APPENDUID+ and +COPYUID+ {response codes}[rdoc-ref:ResponseCode].
|
12
|
+
#
|
13
|
+
# A server that supports +UIDPLUS+ should send UIDPlusData in response to
|
14
|
+
# the append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy],
|
15
|
+
# move[rdoc-ref:Net::IMAP#move], {uid copy}[rdoc-ref:Net::IMAP#uid_copy],
|
16
|
+
# and {uid move}[rdoc-ref:Net::IMAP#uid_move] commands---unless the
|
17
|
+
# destination mailbox reports +UIDNOTSTICKY+.
|
18
|
+
#
|
19
|
+
# Note that append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy]
|
20
|
+
# and {uid_copy}[rdoc-ref:Net::IMAP#uid_copy] return UIDPlusData in their
|
21
|
+
# TaggedResponse. But move[rdoc-ref:Net::IMAP#copy] and
|
22
|
+
# {uid_move}[rdoc-ref:Net::IMAP#uid_move] _should_ send UIDPlusData in an
|
23
|
+
# UntaggedResponse response before sending their TaggedResponse. However
|
24
|
+
# some servers do send UIDPlusData in the TaggedResponse for +MOVE+
|
25
|
+
# commands---this complies with the older +UIDPLUS+ specification but is
|
26
|
+
# discouraged by the +MOVE+ extension and disallowed by +IMAP4rev2+.
|
27
|
+
#
|
28
|
+
# == Required capability
|
29
|
+
# Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
|
30
|
+
# or +IMAP4rev2+ capability.
|
31
|
+
#
|
32
|
+
class UIDPlusData < Struct.new(:uidvalidity, :source_uids, :assigned_uids)
|
33
|
+
##
|
34
|
+
# method: uidvalidity
|
35
|
+
# :call-seq: uidvalidity -> nonzero uint32
|
36
|
+
#
|
37
|
+
# The UIDVALIDITY of the destination mailbox.
|
38
|
+
|
39
|
+
##
|
40
|
+
# method: source_uids
|
41
|
+
# :call-seq: source_uids -> nil or an array of nonzero uint32
|
42
|
+
#
|
43
|
+
# The UIDs of the copied or moved messages.
|
44
|
+
#
|
45
|
+
# Note:: Returns +nil+ for Net::IMAP#append.
|
46
|
+
|
47
|
+
##
|
48
|
+
# method: assigned_uids
|
49
|
+
# :call-seq: assigned_uids -> an array of nonzero uint32
|
50
|
+
#
|
51
|
+
# The newly assigned UIDs of the copied, moved, or appended messages.
|
52
|
+
#
|
53
|
+
# Note:: This always returns an array, even when it contains only one UID.
|
54
|
+
|
55
|
+
##
|
56
|
+
# :call-seq: uid_mapping -> nil or a hash
|
57
|
+
#
|
58
|
+
# Returns a hash mapping each source UID to the newly assigned destination
|
59
|
+
# UID.
|
60
|
+
#
|
61
|
+
# Note:: Returns +nil+ for Net::IMAP#append.
|
62
|
+
def uid_mapping
|
63
|
+
source_uids&.zip(assigned_uids)&.to_h
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# >>>
|
68
|
+
# *NOTE:* <em>AppendUIDData will replace UIDPlusData for +APPENDUID+ in the
|
69
|
+
# +0.6.0+ release.</em> To use AppendUIDData before +0.6.0+, set
|
70
|
+
# Config#parser_use_deprecated_uidplus_data to +false+.
|
71
|
+
#
|
72
|
+
# AppendUIDData represents the ResponseCode#data that accompanies the
|
73
|
+
# +APPENDUID+ {response code}[rdoc-ref:ResponseCode].
|
74
|
+
#
|
75
|
+
# A server that supports +UIDPLUS+ (or +IMAP4rev2+) should send
|
76
|
+
# AppendUIDData inside every TaggedResponse returned by the
|
77
|
+
# append[rdoc-ref:Net::IMAP#append] command---unless the target mailbox
|
78
|
+
# reports +UIDNOTSTICKY+.
|
79
|
+
#
|
80
|
+
# == Required capability
|
81
|
+
# Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
|
82
|
+
# or +IMAP4rev2+ capability.
|
83
|
+
class AppendUIDData < Data.define(:uidvalidity, :assigned_uids)
|
84
|
+
def initialize(uidvalidity:, assigned_uids:)
|
85
|
+
uidvalidity = Integer(uidvalidity)
|
86
|
+
assigned_uids = SequenceSet[assigned_uids]
|
87
|
+
NumValidator.ensure_nz_number(uidvalidity)
|
88
|
+
if assigned_uids.include_star?
|
89
|
+
raise DataFormatError, "uid-set cannot contain '*'"
|
90
|
+
end
|
91
|
+
super
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# attr_reader: uidvalidity
|
96
|
+
# :call-seq: uidvalidity -> nonzero uint32
|
97
|
+
#
|
98
|
+
# The UIDVALIDITY of the destination mailbox.
|
99
|
+
|
100
|
+
##
|
101
|
+
# attr_reader: assigned_uids
|
102
|
+
#
|
103
|
+
# A SequenceSet with the newly assigned UIDs of the appended messages.
|
104
|
+
|
105
|
+
# Returns the number of messages that have been appended.
|
106
|
+
def size
|
107
|
+
assigned_uids.count_with_duplicates
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# >>>
|
112
|
+
# *NOTE:* <em>CopyUIDData will replace UIDPlusData for +COPYUID+ in the
|
113
|
+
# +0.6.0+ release.</em> To use CopyUIDData before +0.6.0+, set
|
114
|
+
# Config#parser_use_deprecated_uidplus_data to +false+.
|
115
|
+
#
|
116
|
+
# CopyUIDData represents the ResponseCode#data that accompanies the
|
117
|
+
# +COPYUID+ {response code}[rdoc-ref:ResponseCode].
|
118
|
+
#
|
119
|
+
# A server that supports +UIDPLUS+ (or +IMAP4rev2+) should send CopyUIDData
|
120
|
+
# in response to
|
121
|
+
# copy[rdoc-ref:Net::IMAP#copy], {uid_copy}[rdoc-ref:Net::IMAP#uid_copy],
|
122
|
+
# move[rdoc-ref:Net::IMAP#copy], and {uid_move}[rdoc-ref:Net::IMAP#uid_move]
|
123
|
+
# commands---unless the destination mailbox reports +UIDNOTSTICKY+.
|
124
|
+
#
|
125
|
+
# Note that copy[rdoc-ref:Net::IMAP#copy] and
|
126
|
+
# {uid_copy}[rdoc-ref:Net::IMAP#uid_copy] return CopyUIDData in their
|
127
|
+
# TaggedResponse. But move[rdoc-ref:Net::IMAP#copy] and
|
128
|
+
# {uid_move}[rdoc-ref:Net::IMAP#uid_move] _should_ send CopyUIDData in an
|
129
|
+
# UntaggedResponse response before sending their TaggedResponse. However
|
130
|
+
# some servers do send CopyUIDData in the TaggedResponse for +MOVE+
|
131
|
+
# commands---this complies with the older +UIDPLUS+ specification but is
|
132
|
+
# discouraged by the +MOVE+ extension and disallowed by +IMAP4rev2+.
|
133
|
+
#
|
134
|
+
# == Required capability
|
135
|
+
# Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
|
136
|
+
# or +IMAP4rev2+ capability.
|
137
|
+
class CopyUIDData < Data.define(:uidvalidity, :source_uids, :assigned_uids)
|
138
|
+
def initialize(uidvalidity:, source_uids:, assigned_uids:)
|
139
|
+
uidvalidity = Integer(uidvalidity)
|
140
|
+
source_uids = SequenceSet[source_uids]
|
141
|
+
assigned_uids = SequenceSet[assigned_uids]
|
142
|
+
NumValidator.ensure_nz_number(uidvalidity)
|
143
|
+
if source_uids.include_star? || assigned_uids.include_star?
|
144
|
+
raise DataFormatError, "uid-set cannot contain '*'"
|
145
|
+
elsif source_uids.count_with_duplicates != assigned_uids.count_with_duplicates
|
146
|
+
raise DataFormatError, "mismatched uid-set sizes for %s and %s" % [
|
147
|
+
source_uids, assigned_uids
|
148
|
+
]
|
149
|
+
end
|
150
|
+
super
|
151
|
+
end
|
152
|
+
|
153
|
+
##
|
154
|
+
# attr_reader: uidvalidity
|
155
|
+
#
|
156
|
+
# The +UIDVALIDITY+ of the destination mailbox (a nonzero unsigned 32 bit
|
157
|
+
# integer).
|
158
|
+
|
159
|
+
##
|
160
|
+
# attr_reader: source_uids
|
161
|
+
#
|
162
|
+
# A SequenceSet with the original UIDs of the copied or moved messages.
|
163
|
+
|
164
|
+
##
|
165
|
+
# attr_reader: assigned_uids
|
166
|
+
#
|
167
|
+
# A SequenceSet with the newly assigned UIDs of the copied or moved
|
168
|
+
# messages.
|
169
|
+
|
170
|
+
# Returns the number of messages that have been copied or moved.
|
171
|
+
# source_uids and the assigned_uids will both the same number of UIDs.
|
172
|
+
def size
|
173
|
+
assigned_uids.count_with_duplicates
|
174
|
+
end
|
175
|
+
|
176
|
+
# :call-seq:
|
177
|
+
# assigned_uid_for(source_uid) -> uid
|
178
|
+
# self[source_uid] -> uid
|
179
|
+
#
|
180
|
+
# Returns the UID in the destination mailbox for the message that was
|
181
|
+
# copied from +source_uid+ in the source mailbox.
|
182
|
+
#
|
183
|
+
# This is the reverse of #source_uid_for.
|
184
|
+
#
|
185
|
+
# Related: source_uid_for, each_uid_pair, uid_mapping
|
186
|
+
def assigned_uid_for(source_uid)
|
187
|
+
idx = source_uids.find_ordered_index(source_uid) and
|
188
|
+
assigned_uids.ordered_at(idx)
|
189
|
+
end
|
190
|
+
alias :[] :assigned_uid_for
|
191
|
+
|
192
|
+
# :call-seq:
|
193
|
+
# source_uid_for(assigned_uid) -> uid
|
194
|
+
#
|
195
|
+
# Returns the UID in the source mailbox for the message that was copied to
|
196
|
+
# +assigned_uid+ in the source mailbox.
|
197
|
+
#
|
198
|
+
# This is the reverse of #assigned_uid_for.
|
199
|
+
#
|
200
|
+
# Related: assigned_uid_for, each_uid_pair, uid_mapping
|
201
|
+
def source_uid_for(assigned_uid)
|
202
|
+
idx = assigned_uids.find_ordered_index(assigned_uid) and
|
203
|
+
source_uids.ordered_at(idx)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Yields a pair of UIDs for each copied message. The first is the
|
207
|
+
# message's UID in the source mailbox and the second is the UID in the
|
208
|
+
# destination mailbox.
|
209
|
+
#
|
210
|
+
# Returns an enumerator when no block is given.
|
211
|
+
#
|
212
|
+
# Please note the warning on uid_mapping before calling methods like
|
213
|
+
# +to_h+ or +to_a+ on the returned enumerator.
|
214
|
+
#
|
215
|
+
# Related: uid_mapping, assigned_uid_for, source_uid_for
|
216
|
+
def each_uid_pair
|
217
|
+
return enum_for(__method__) unless block_given?
|
218
|
+
source_uids.each_ordered_number.lazy
|
219
|
+
.zip(assigned_uids.each_ordered_number.lazy) do
|
220
|
+
|source_uid, assigned_uid|
|
221
|
+
yield source_uid, assigned_uid
|
222
|
+
end
|
223
|
+
end
|
224
|
+
alias each_pair each_uid_pair
|
225
|
+
alias each each_uid_pair
|
226
|
+
|
227
|
+
# :call-seq: uid_mapping -> hash
|
228
|
+
#
|
229
|
+
# Returns a hash mapping each source UID to the newly assigned destination
|
230
|
+
# UID.
|
231
|
+
#
|
232
|
+
# <em>*Warning:*</em> The hash that is created may consume _much_ more
|
233
|
+
# memory than the data used to create it. When handling responses from an
|
234
|
+
# untrusted server, check #size before calling this method.
|
235
|
+
#
|
236
|
+
# Related: each_uid_pair, assigned_uid_for, source_uid_for
|
237
|
+
def uid_mapping
|
238
|
+
each_uid_pair.to_h
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
data/lib/net/imap.rb
CHANGED
@@ -744,7 +744,7 @@ module Net
|
|
744
744
|
# * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
|
745
745
|
#
|
746
746
|
class IMAP < Protocol
|
747
|
-
VERSION = "0.5.
|
747
|
+
VERSION = "0.5.6"
|
748
748
|
|
749
749
|
# Aliases for supported capabilities, to be used with the #enable command.
|
750
750
|
ENABLE_ALIASES = {
|
@@ -1239,13 +1239,21 @@ module Net
|
|
1239
1239
|
#
|
1240
1240
|
def starttls(**options)
|
1241
1241
|
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
|
1242
|
-
|
1242
|
+
error = nil
|
1243
|
+
ok = send_command("STARTTLS") do |resp|
|
1243
1244
|
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
|
1244
1245
|
clear_cached_capabilities
|
1245
1246
|
clear_responses
|
1246
1247
|
start_tls_session
|
1247
1248
|
end
|
1249
|
+
rescue Exception => error
|
1250
|
+
raise # note that the error backtrace is in the receiver_thread
|
1248
1251
|
end
|
1252
|
+
if error
|
1253
|
+
disconnect
|
1254
|
+
raise error
|
1255
|
+
end
|
1256
|
+
ok
|
1249
1257
|
end
|
1250
1258
|
|
1251
1259
|
# :call-seq:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-imap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shugo Maeda
|
8
8
|
- nicholas a. evans
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-protocol
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- lib/net/imap/stringprep/saslprep_tables.rb
|
97
97
|
- lib/net/imap/stringprep/tables.rb
|
98
98
|
- lib/net/imap/stringprep/trace.rb
|
99
|
+
- lib/net/imap/uidplus_data.rb
|
99
100
|
- lib/net/imap/vanished_data.rb
|
100
101
|
- net-imap.gemspec
|
101
102
|
- rakelib/benchmarks.rake
|