Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2020,7 +2020,7 @@ def status(mailbox, attr)
#
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
# supported and the destination supports persistent UIDs, the server's
# response should include an +APPENDUID+ response code with UIDPlusData.
# response should include an +APPENDUID+ response code with AppendUIDData.
# This will report the UIDVALIDITY of the destination mailbox and the
# assigned UID of the appended message.
#
Expand Down Expand Up @@ -2777,7 +2777,7 @@ def uid_store(set, attr, flags, unchangedsince: nil)
#
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
# supported, the server's response should include a +COPYUID+ response code
# with UIDPlusData. This will report the UIDVALIDITY of the destination
# with CopyUIDData. This will report the UIDVALIDITY of the destination
# mailbox, the UID set of the source messages, and the assigned UID set of
# the moved messages.
#
Expand Down Expand Up @@ -2818,7 +2818,7 @@ def uid_copy(set, mailbox)
#
# If +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315.html]] is
# supported, the server's response should include a +COPYUID+ response code
# with UIDPlusData. This will report the UIDVALIDITY of the destination
# with CopyUIDData. This will report the UIDVALIDITY of the destination
# mailbox, the UID set of the source messages, and the assigned UID set of
# the moved messages.
#
Expand Down
57 changes: 25 additions & 32 deletions lib/net/imap/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -347,38 +347,31 @@ def self.[](config)
#
# Alias for responses_without_block

# Whether ResponseParser should use the deprecated UIDPlusData or
# **NOTE:** <em>+UIDPlusData+ has been removed since +v0.6.0+, and this
# config option is completely ignored. The config option is kept
# only for compatibility and will be removed by +v0.7.0+.</em>
#
# Whether ResponseParser would use the deprecated UIDPlusData or
# CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
# AppendUIDData for +APPENDUID+ response codes.
#
# UIDPlusData stores its data in arrays of numbers, which is vulnerable to
# a memory exhaustion denial of service attack from an untrusted or
# compromised server. Set this option to +false+ to completely block this
# vulnerability. Otherwise, parser_max_deprecated_uidplus_data_size
# mitigates this vulnerability.
#
# AppendUIDData and CopyUIDData are _mostly_ backward-compatible with
# UIDPlusData. Most applications should be able to upgrade with little
# or no changes.
#
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
# Parser support for +UIDPLUS+ added in +v0.3.2+.
#
# <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
# Config option added in +v0.4.19+ and +v0.5.6+.
#
# <em>UIDPlusData will be removed in +v0.6+ and this config setting will
# be ignored.</em>
# <em>UIDPlusData was removed in +v0.6.0+.</em>
#
# ==== Valid options
# ==== Options
#
# [+true+ <em>(original default)</em>]
# ResponseParser only uses UIDPlusData.
# <em>IGNORED since v0.6+.</em>
# Prints a warning when UIDPLUS data is parsed.
#
# [+:up_to_max_size+ <em>(default since +v0.5.6+)</em>]
# ResponseParser uses UIDPlusData when the +uid-set+ size is below
# parser_max_deprecated_uidplus_data_size. Above that size,
# ResponseParser uses AppendUIDData or CopyUIDData.
# <em>IGNORED since v0.6+.</em>
# Prints a warning when UIDPLUS data is parsed.
#
# [+false+ <em>(planned default for +v0.6+)</em>]
# [+false+ <em>(only valid option since +v0.6.0+)</em>]
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
true, :up_to_max_size, false
Expand All @@ -388,22 +381,22 @@ def self.[](config)
0.6r => false,
}

# The maximum +uid-set+ size that ResponseParser will parse into
# deprecated UIDPlusData. This limit only applies when
# parser_use_deprecated_uidplus_data is not +false+.
# **NOTE:** <em>+UIDPlusData+ has been removed since +v0.6.0+, and this
# config option is ignored. The config option is kept only for
# compatibility and will be removed by +v0.7.0+.</em>
#
# <em>(Parser support for +UIDPLUS+ added in +v0.3.2+.)</em>
# The maximum +uid-set+ size that ResponseParser would parse into
# deprecated UIDPlusData. This limit would only apply when
# parser_use_deprecated_uidplus_data was not +false+.
#
# <em>Support for limiting UIDPlusData to a maximum size was added in
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.</em>
# Parser support for +UIDPLUS+ added in +v0.3.2+.
#
# <em>UIDPlusData will be removed in +v0.6+.</em>
# Support for limiting UIDPlusData to a maximum size was added in
# +v0.3.8+, +v0.4.19+, and +v0.5.6+.
#
# ==== Versioned Defaults
# <em>UIDPlusData was removed in +v0.6.0+.</em>
#
# Because this limit guards against a remote server causing catastrophic
# memory exhaustion, the versioned default (used by #load_defaults) also
# applies to versions without the feature.
# ==== Versioned Defaults
#
# * +0.3+ and prior: <tt>10,000</tt>
# * +0.4+: <tt>1,000</tt>
Expand Down
5 changes: 2 additions & 3 deletions lib/net/imap/response_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ class IMAP < Protocol
autoload :FetchData, "#{__dir__}/fetch_data"
autoload :UIDFetchData, "#{__dir__}/fetch_data"
autoload :SearchResult, "#{__dir__}/search_result"
autoload :UIDPlusData, "#{__dir__}/uidplus_data"
autoload :AppendUIDData, "#{__dir__}/uidplus_data"
autoload :CopyUIDData, "#{__dir__}/uidplus_data"
autoload :VanishedData, "#{__dir__}/vanished_data"
Expand Down Expand Up @@ -260,8 +259,8 @@ class ResponseText < Struct.new(:code, :text)
#
# === +UIDPLUS+ extension
# See {[RFC4315 §3]}[https://www.rfc-editor.org/rfc/rfc4315#section-3].
# * +APPENDUID+, #data is UIDPlusData. See IMAP#append.
# * +COPYUID+, #data is UIDPlusData. See IMAP#copy.
# * +APPENDUID+, #data is AppendUIDData. See IMAP#append.
# * +COPYUID+, #data is CopyUIDData. See IMAP#copy.
# * +UIDNOTSTICKY+, #data is +nil+. See IMAP#select.
#
# === +SEARCHRES+ extension
Expand Down
19 changes: 2 additions & 17 deletions lib/net/imap/response_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2017,23 +2017,8 @@ def resp_code_copy__data
CopyUID(validity, src_uids, dst_uids)
end

def AppendUID(...) DeprecatedUIDPlus(...) || AppendUIDData.new(...) end
def CopyUID(...) DeprecatedUIDPlus(...) || CopyUIDData.new(...) end

# TODO: remove this code in the v0.6.0 release
def DeprecatedUIDPlus(validity, src_uids = nil, dst_uids)
return unless config.parser_use_deprecated_uidplus_data
compact_uid_sets = [src_uids, dst_uids].compact
count = compact_uid_sets.map { _1.count_with_duplicates }.max
max = config.parser_max_deprecated_uidplus_data_size
if count <= max
src_uids &&= src_uids.each_ordered_number.to_a
dst_uids = dst_uids.each_ordered_number.to_a
UIDPlusData.new(validity, src_uids, dst_uids)
elsif config.parser_use_deprecated_uidplus_data != :up_to_max_size
parse_error("uid-set is too large: %d > %d", count, max)
end
end
def AppendUID(...) AppendUIDData.new(...) end
def CopyUID(...) CopyUIDData.new(...) end

ADDRESS_REGEXP = /\G
\( (?: NIL | #{Patterns::QUOTED_rev2} ) # 1: NAME
Expand Down
65 changes: 2 additions & 63 deletions lib/net/imap/uidplus_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,8 @@
module Net
class IMAP < Protocol

# *NOTE:* <em>UIDPlusData is deprecated and will be removed in the +0.6.0+
# release.</em> To use AppendUIDData and CopyUIDData before +0.6.0+, set
# Config#parser_use_deprecated_uidplus_data to +false+.
#
# UIDPlusData represents the ResponseCode#data that accompanies the
# +APPENDUID+ and +COPYUID+ {response codes}[rdoc-ref:ResponseCode].
#
# A server that supports +UIDPLUS+ should send UIDPlusData in response to
# the append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy],
# move[rdoc-ref:Net::IMAP#move], {uid copy}[rdoc-ref:Net::IMAP#uid_copy],
# and {uid move}[rdoc-ref:Net::IMAP#uid_move] commands---unless the
# destination mailbox reports +UIDNOTSTICKY+.
#
# Note that append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy]
# and {uid_copy}[rdoc-ref:Net::IMAP#uid_copy] return UIDPlusData in their
# TaggedResponse. But move[rdoc-ref:Net::IMAP#copy] and
# {uid_move}[rdoc-ref:Net::IMAP#uid_move] _should_ send UIDPlusData in an
# UntaggedResponse response before sending their TaggedResponse. However
# some servers do send UIDPlusData in the TaggedResponse for +MOVE+
# commands---this complies with the older +UIDPLUS+ specification but is
# discouraged by the +MOVE+ extension and disallowed by +IMAP4rev2+.
#
# == Required capability
# Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
# or +IMAP4rev2+ capability.
#
class UIDPlusData < Struct.new(:uidvalidity, :source_uids, :assigned_uids)
##
# method: uidvalidity
# :call-seq: uidvalidity -> nonzero uint32
#
# The UIDVALIDITY of the destination mailbox.

##
# method: source_uids
# :call-seq: source_uids -> nil or an array of nonzero uint32
#
# The UIDs of the copied or moved messages.
#
# Note:: Returns +nil+ for Net::IMAP#append.

##
# method: assigned_uids
# :call-seq: assigned_uids -> an array of nonzero uint32
#
# The newly assigned UIDs of the copied, moved, or appended messages.
#
# Note:: This always returns an array, even when it contains only one UID.

##
# :call-seq: uid_mapping -> nil or a hash
#
# Returns a hash mapping each source UID to the newly assigned destination
# UID.
#
# Note:: Returns +nil+ for Net::IMAP#append.
def uid_mapping
source_uids&.zip(assigned_uids)&.to_h
end
end

# >>>
# *NOTE:* <em>AppendUIDData will replace UIDPlusData for +APPENDUID+ in the
# *NOTE:* <em>AppendUIDData replaced UIDPlusData for +APPENDUID+ in the
# +0.6.0+ release.</em> To use AppendUIDData before +0.6.0+, set
# Config#parser_use_deprecated_uidplus_data to +false+.
#
Expand Down Expand Up @@ -109,7 +48,7 @@ def size
end

# >>>
# *NOTE:* <em>CopyUIDData will replace UIDPlusData for +COPYUID+ in the
# *NOTE:* <em>CopyUIDData replaced UIDPlusData for +COPYUID+ in the
# +0.6.0+ release.</em> To use CopyUIDData before +0.6.0+, set
# Config#parser_use_deprecated_uidplus_data to +false+.
#
Expand Down
94 changes: 0 additions & 94 deletions test/net/imap/test_response_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -201,51 +201,6 @@ def test_fetch_binary_and_binary_size
end
end

test "APPENDUID with parser_use_deprecated_uidplus_data = true" do
parser = Net::IMAP::ResponseParser.new(config: {
parser_use_deprecated_uidplus_data: true,
parser_max_deprecated_uidplus_data_size: 10_000,
})
assert_raise_with_message Net::IMAP::ResponseParseError, /uid-set is too large/ do
parser.parse(
"A004 OK [APPENDUID 1 10000:20000,1] Done\r\n"
)
end
response = parser.parse("A004 OK [APPENDUID 1 100:200] Done\r\n")
uidplus = response.data.code.data
assert_equal 101, uidplus.assigned_uids.size
parser.config.parser_max_deprecated_uidplus_data_size = 100
assert_raise_with_message Net::IMAP::ResponseParseError, /uid-set is too large/ do
parser.parse(
"A004 OK [APPENDUID 1 100:200] Done\r\n"
)
end
response = parser.parse("A004 OK [APPENDUID 1 101:200] Done\r\n")
uidplus = response.data.code.data
assert_instance_of Net::IMAP::UIDPlusData, uidplus
assert_equal 100, uidplus.assigned_uids.size
end

test "APPENDUID with parser_use_deprecated_uidplus_data = :up_to_max_size" do
parser = Net::IMAP::ResponseParser.new(config: {
parser_use_deprecated_uidplus_data: :up_to_max_size,
parser_max_deprecated_uidplus_data_size: 100
})
response = parser.parse("A004 OK [APPENDUID 1 101:200] Done\r\n")
assert_instance_of Net::IMAP::UIDPlusData, response.data.code.data
response = parser.parse("A004 OK [APPENDUID 1 100:200] Done\r\n")
assert_instance_of Net::IMAP::AppendUIDData, response.data.code.data
end

test "APPENDUID with parser_use_deprecated_uidplus_data = false" do
parser = Net::IMAP::ResponseParser.new(config: {
parser_use_deprecated_uidplus_data: false,
parser_max_deprecated_uidplus_data_size: 10_000_000,
})
response = parser.parse("A004 OK [APPENDUID 1 10] Done\r\n")
assert_instance_of Net::IMAP::AppendUIDData, response.data.code.data
end

test "COPYUID with backwards ranges" do
parser = Net::IMAP::ResponseParser.new
response = parser.parse(
Expand Down Expand Up @@ -276,53 +231,4 @@ def test_fetch_binary_and_binary_size
end
end

test "COPYUID with parser_use_deprecated_uidplus_data = true" do
parser = Net::IMAP::ResponseParser.new(config: {
parser_use_deprecated_uidplus_data: true,
parser_max_deprecated_uidplus_data_size: 10_000,
})
assert_raise_with_message Net::IMAP::ResponseParseError, /uid-set is too large/ do
parser.parse(
"A004 OK [copyUID 1 10000:20000,1 1:10001] Done\r\n"
)
end
response = parser.parse("A004 OK [copyUID 1 100:200 1:101] Done\r\n")
uidplus = response.data.code.data
assert_equal 101, uidplus.assigned_uids.size
assert_equal 101, uidplus.source_uids.size
parser.config.parser_max_deprecated_uidplus_data_size = 100
assert_raise_with_message Net::IMAP::ResponseParseError, /uid-set is too large/ do
parser.parse(
"A004 OK [copyUID 1 100:200 1:101] Done\r\n"
)
end
response = parser.parse("A004 OK [copyUID 1 101:200 1:100] Done\r\n")
uidplus = response.data.code.data
assert_instance_of Net::IMAP::UIDPlusData, uidplus
assert_equal 100, uidplus.assigned_uids.size
assert_equal 100, uidplus.source_uids.size
end

test "COPYUID with parser_use_deprecated_uidplus_data = :up_to_max_size" do
parser = Net::IMAP::ResponseParser.new(config: {
parser_use_deprecated_uidplus_data: :up_to_max_size,
parser_max_deprecated_uidplus_data_size: 100
})
response = parser.parse("A004 OK [COPYUID 1 101:200 1:100] Done\r\n")
copyuid = response.data.code.data
assert_instance_of Net::IMAP::UIDPlusData, copyuid
response = parser.parse("A004 OK [COPYUID 1 100:200 1:101] Done\r\n")
copyuid = response.data.code.data
assert_instance_of Net::IMAP::CopyUIDData, copyuid
end

test "COPYUID with parser_use_deprecated_uidplus_data = false" do
parser = Net::IMAP::ResponseParser.new(config: {
parser_use_deprecated_uidplus_data: false,
parser_max_deprecated_uidplus_data_size: 10_000_000,
})
response = parser.parse("A004 OK [COPYUID 1 101 1] Done\r\n")
assert_instance_of Net::IMAP::CopyUIDData, response.data.code.data
end

end
42 changes: 0 additions & 42 deletions test/net/imap/test_uidplus_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,6 @@
require "net/imap"
require "test/unit"

class TestUIDPlusData < Net::IMAP::TestCase

test "#uid_mapping with sorted source_uids" do
uidplus = Net::IMAP::UIDPlusData.new(
1, [19, 20, *(495..500)], [*(92..97), 100, 101],
)
assert_equal(
{
19 => 92,
20 => 93,
495 => 94,
496 => 95,
497 => 96,
498 => 97,
499 => 100,
500 => 101,
},
uidplus.uid_mapping
)
end

test "#uid_mapping for with source_uids in unsorted order" do
uidplus = Net::IMAP::UIDPlusData.new(
1, [*(495..500), 19, 20], [*(92..97), 100, 101],
)
assert_equal(
{
495 => 92,
496 => 93,
497 => 94,
498 => 95,
499 => 96,
500 => 97,
19 => 100,
20 => 101,
},
uidplus.uid_mapping
)
end

end

class TestAppendUIDData < Net::IMAP::TestCase
# alias for convenience
AppendUIDData = Net::IMAP::AppendUIDData
Expand Down