net-imap 0.3.7 → 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.
- checksums.yaml +4 -4
- data/BSDL +22 -0
- data/COPYING +56 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +3 -22
- data/README.md +25 -8
- data/Rakefile +0 -7
- data/docs/styles.css +72 -23
- data/lib/net/imap/authenticators.rb +26 -57
- data/lib/net/imap/command_data.rb +74 -54
- data/lib/net/imap/config/attr_accessors.rb +75 -0
- data/lib/net/imap/config/attr_inheritance.rb +90 -0
- data/lib/net/imap/config/attr_type_coercion.rb +61 -0
- data/lib/net/imap/config.rb +470 -0
- data/lib/net/imap/data_encoding.rb +18 -6
- data/lib/net/imap/data_lite.rb +226 -0
- data/lib/net/imap/deprecated_client_options.rb +142 -0
- data/lib/net/imap/errors.rb +27 -1
- data/lib/net/imap/esearch_result.rb +180 -0
- data/lib/net/imap/fetch_data.rb +597 -0
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +250 -440
- data/lib/net/imap/response_parser/parser_utils.rb +245 -0
- data/lib/net/imap/response_parser.rb +1867 -1184
- data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
- data/lib/net/imap/sasl/authentication_exchange.rb +139 -0
- data/lib/net/imap/sasl/authenticators.rb +122 -0
- data/lib/net/imap/sasl/client_adapter.rb +123 -0
- data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +24 -14
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +342 -0
- data/lib/net/imap/sasl/external_authenticator.rb +83 -0
- data/lib/net/imap/sasl/gs2_header.rb +80 -0
- data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +28 -18
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
- data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
- data/lib/net/imap/sasl/protocol_adapters.rb +101 -0
- data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
- data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
- data/lib/net/imap/sasl/stringprep.rb +6 -66
- data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
- data/lib/net/imap/sasl.rb +148 -44
- data/lib/net/imap/sasl_adapter.rb +20 -0
- data/lib/net/imap/search_result.rb +146 -0
- data/lib/net/imap/sequence_set.rb +1565 -0
- data/lib/net/imap/stringprep/nameprep.rb +70 -0
- data/lib/net/imap/stringprep/saslprep.rb +69 -0
- data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
- data/lib/net/imap/stringprep/tables.rb +146 -0
- data/lib/net/imap/stringprep/trace.rb +85 -0
- data/lib/net/imap/stringprep.rb +159 -0
- data/lib/net/imap/uidplus_data.rb +244 -0
- data/lib/net/imap/vanished_data.rb +56 -0
- data/lib/net/imap.rb +2090 -823
- data/net-imap.gemspec +7 -8
- data/rakelib/benchmarks.rake +91 -0
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/saslprep.rake +4 -4
- data/rakelib/string_prep_tables_generator.rb +84 -60
- data/sample/net-imap.rb +167 -0
- metadata +45 -49
- data/.github/dependabot.yml +0 -6
- data/.github/workflows/test.yml +0 -38
- data/.gitignore +0 -10
- data/benchmarks/stringprep.yml +0 -65
- data/benchmarks/table-regexps.yml +0 -39
- data/lib/net/imap/authenticators/digest_md5.rb +0 -115
- data/lib/net/imap/authenticators/plain.rb +0 -41
- data/lib/net/imap/authenticators/xoauth2.rb +0 -20
- data/lib/net/imap/sasl/saslprep.rb +0 -55
- data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
- data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP < Protocol
|
5
|
+
|
6
|
+
# Regexps and utility methods for implementing stringprep profiles. The
|
7
|
+
# \StringPrep algorithm is defined by
|
8
|
+
# {RFC-3454}[https://www.rfc-editor.org/rfc/rfc3454.html]. Each
|
9
|
+
# codepoint table defined in the RFC-3454 appendices is matched by a Regexp
|
10
|
+
# defined in this module.
|
11
|
+
module StringPrep
|
12
|
+
autoload :NamePrep, File.expand_path("stringprep/nameprep", __dir__)
|
13
|
+
autoload :SASLprep, File.expand_path("stringprep/saslprep", __dir__)
|
14
|
+
autoload :Tables, File.expand_path("stringprep/tables", __dir__)
|
15
|
+
autoload :Trace, File.expand_path("stringprep/trace", __dir__)
|
16
|
+
|
17
|
+
# ArgumentError raised when +string+ is invalid for the stringprep
|
18
|
+
# +profile+.
|
19
|
+
class StringPrepError < ArgumentError
|
20
|
+
attr_reader :string, :profile
|
21
|
+
|
22
|
+
def initialize(*args, string: nil, profile: nil)
|
23
|
+
@string = -string.to_str unless string.nil?
|
24
|
+
@profile = -profile.to_str unless profile.nil?
|
25
|
+
super(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# StringPrepError raised when +string+ contains a codepoint prohibited by
|
30
|
+
# +table+.
|
31
|
+
class ProhibitedCodepoint < StringPrepError
|
32
|
+
attr_reader :table
|
33
|
+
|
34
|
+
def initialize(table, *args, **kwargs)
|
35
|
+
@table = table
|
36
|
+
details = (title = Tables::TITLES[table]) ?
|
37
|
+
"%s [%s]" % [title, table] : table
|
38
|
+
message = "String contains a prohibited codepoint: %s" % [details]
|
39
|
+
super(message, *args, **kwargs)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# StringPrepError raised when +string+ contains bidirectional characters
|
44
|
+
# which violate the StringPrep requirements.
|
45
|
+
class BidiStringError < StringPrepError
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns a Regexp matching the given +table+ name.
|
49
|
+
def self.[](table)
|
50
|
+
Tables::REGEXPS.fetch(table)
|
51
|
+
end
|
52
|
+
|
53
|
+
module_function
|
54
|
+
|
55
|
+
# >>>
|
56
|
+
# 1. Map -- For each character in the input, check if it has a mapping
|
57
|
+
# and, if so, replace it with its mapping. This is described in
|
58
|
+
# section 3.
|
59
|
+
#
|
60
|
+
# 2. Normalize -- Possibly normalize the result of step 1 using Unicode
|
61
|
+
# normalization. This is described in section 4.
|
62
|
+
#
|
63
|
+
# 3. Prohibit -- Check for any characters that are not allowed in the
|
64
|
+
# output. If any are found, return an error. This is described in
|
65
|
+
# section 5.
|
66
|
+
#
|
67
|
+
# 4. Check bidi -- Possibly check for right-to-left characters, and if
|
68
|
+
# any are found, make sure that the whole string satisfies the
|
69
|
+
# requirements for bidirectional strings. If the string does not
|
70
|
+
# satisfy the requirements for bidirectional strings, return an
|
71
|
+
# error. This is described in section 6.
|
72
|
+
#
|
73
|
+
# The above steps MUST be performed in the order given to comply with
|
74
|
+
# this specification.
|
75
|
+
#
|
76
|
+
def stringprep(string,
|
77
|
+
maps:,
|
78
|
+
normalization:,
|
79
|
+
prohibited:,
|
80
|
+
**opts)
|
81
|
+
string = string.encode("UTF-8") # also dups (and raises invalid encoding)
|
82
|
+
map_tables!(string, *maps) if maps
|
83
|
+
string.unicode_normalize!(normalization) if normalization
|
84
|
+
check_prohibited!(string, *prohibited, **opts) if prohibited
|
85
|
+
string
|
86
|
+
end
|
87
|
+
|
88
|
+
def map_tables!(string, *tables)
|
89
|
+
tables.each do |table|
|
90
|
+
regexp, replacements = Tables::MAPPINGS.fetch(table)
|
91
|
+
string.gsub!(regexp, replacements)
|
92
|
+
end
|
93
|
+
string
|
94
|
+
end
|
95
|
+
|
96
|
+
# Checks +string+ for any codepoint in +tables+. Raises a
|
97
|
+
# ProhibitedCodepoint describing the first matching table.
|
98
|
+
#
|
99
|
+
# Also checks bidirectional characters, when <tt>bidi: true</tt>, which may
|
100
|
+
# raise a BidiStringError.
|
101
|
+
#
|
102
|
+
# +profile+ is an optional string which will be added to any exception that
|
103
|
+
# is raised (it does not affect behavior).
|
104
|
+
def check_prohibited!(string,
|
105
|
+
*tables,
|
106
|
+
bidi: false,
|
107
|
+
unassigned: "A.1",
|
108
|
+
stored: false,
|
109
|
+
profile: nil)
|
110
|
+
tables = Tables::TITLES.keys.grep(/^C/) if tables.empty?
|
111
|
+
tables |= [unassigned] if stored
|
112
|
+
tables |= %w[C.8] if bidi
|
113
|
+
table = tables.find {|t|
|
114
|
+
case t
|
115
|
+
when String then Tables::REGEXPS.fetch(t).match?(string)
|
116
|
+
when Regexp then t.match?(string)
|
117
|
+
else raise ArgumentError, "only table names and regexps can be checked"
|
118
|
+
end
|
119
|
+
}
|
120
|
+
if table
|
121
|
+
raise ProhibitedCodepoint.new(
|
122
|
+
table, string: string, profile: profile
|
123
|
+
)
|
124
|
+
end
|
125
|
+
check_bidi!(string, profile: profile) if bidi
|
126
|
+
end
|
127
|
+
|
128
|
+
# Checks that +string+ obeys all of the "Bidirectional Characters"
|
129
|
+
# requirements in RFC-3454, §6:
|
130
|
+
#
|
131
|
+
# * The characters in \StringPrep\[\"C.8\"] MUST be prohibited
|
132
|
+
# * If a string contains any RandALCat character, the string MUST NOT
|
133
|
+
# contain any LCat character.
|
134
|
+
# * If a string contains any RandALCat character, a RandALCat
|
135
|
+
# character MUST be the first character of the string, and a
|
136
|
+
# RandALCat character MUST be the last character of the string.
|
137
|
+
#
|
138
|
+
# This is usually combined with #check_prohibited!, so table "C.8" is only
|
139
|
+
# checked when <tt>c_8: true</tt>.
|
140
|
+
#
|
141
|
+
# Raises either ProhibitedCodepoint or BidiStringError unless all
|
142
|
+
# requirements are met. +profile+ is an optional string which will be
|
143
|
+
# added to any exception that is raised (it does not affect behavior).
|
144
|
+
def check_bidi!(string, c_8: false, profile: nil)
|
145
|
+
check_prohibited!(string, "C.8", profile: profile) if c_8
|
146
|
+
if Tables::BIDI_FAILS_REQ2.match?(string)
|
147
|
+
raise BidiStringError.new(
|
148
|
+
Tables::BIDI_DESC_REQ2, string: string, profile: profile,
|
149
|
+
)
|
150
|
+
elsif Tables::BIDI_FAILS_REQ3.match?(string)
|
151
|
+
raise BidiStringError.new(
|
152
|
+
Tables::BIDI_DESC_REQ3, string: string, profile: profile,
|
153
|
+
)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
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
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class IMAP < Protocol
|
5
|
+
|
6
|
+
# Net::IMAP::VanishedData represents the contents of a +VANISHED+ response,
|
7
|
+
# which is described by the
|
8
|
+
# {QRESYNC}[https://www.rfc-editor.org/rfc/rfc7162.html] extension.
|
9
|
+
# [{RFC7162 §3.2.10}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.2.10]].
|
10
|
+
#
|
11
|
+
# +VANISHED+ responses replace +EXPUNGE+ responses when either the
|
12
|
+
# {QRESYNC}[https://www.rfc-editor.org/rfc/rfc7162.html] or the
|
13
|
+
# {UIDONLY}[https://www.rfc-editor.org/rfc/rfc9586.html] extension has been
|
14
|
+
# enabled.
|
15
|
+
class VanishedData < Data.define(:uids, :earlier)
|
16
|
+
|
17
|
+
# Returns a new VanishedData object.
|
18
|
+
#
|
19
|
+
# * +uids+ will be converted by SequenceSet.[].
|
20
|
+
# * +earlier+ will be converted to +true+ or +false+
|
21
|
+
def initialize(uids:, earlier:)
|
22
|
+
uids = SequenceSet[uids]
|
23
|
+
earlier = !!earlier
|
24
|
+
super
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# :attr_reader: uids
|
29
|
+
#
|
30
|
+
# SequenceSet of UIDs that have been permanently removed from the mailbox.
|
31
|
+
|
32
|
+
##
|
33
|
+
# :attr_reader: earlier
|
34
|
+
#
|
35
|
+
# +true+ when the response was caused by Net::IMAP#uid_fetch with
|
36
|
+
# <tt>vanished: true</tt> or Net::IMAP#select/Net::IMAP#examine with
|
37
|
+
# <tt>qresync: true</tt>.
|
38
|
+
#
|
39
|
+
# +false+ when the response is used to announce message removals within an
|
40
|
+
# already selected mailbox.
|
41
|
+
|
42
|
+
# rdoc doesn't handle attr aliases nicely. :(
|
43
|
+
alias earlier? earlier # :nodoc:
|
44
|
+
##
|
45
|
+
# :attr_reader: earlier?
|
46
|
+
#
|
47
|
+
# Alias for #earlier.
|
48
|
+
|
49
|
+
# Returns an Array of all of the UIDs in #uids.
|
50
|
+
#
|
51
|
+
# See SequenceSet#numbers.
|
52
|
+
def to_a; uids.numbers end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|