Skip to content

Commit

Permalink
πŸ”€ Merge pull request #404 from ruby/backport-0.4-uidplus-deprecation
Browse files Browse the repository at this point in the history
✨ Backport UIDPlusData, AppendUIDData, CopyUIDData to v0.4
  • Loading branch information
nevans authored Feb 7, 2025
2 parents 3023888 + d32320a commit e4d57b1
Show file tree
Hide file tree
Showing 8 changed files with 708 additions and 123 deletions.
29 changes: 29 additions & 0 deletions lib/net/imap/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,32 @@ def self.[](config)
#
# Alias for responses_without_block

# Whether ResponseParser should use the deprecated UIDPlusData or
# CopyUIDData for +COPYUID+ response codes, and UIDPlusData or
# AppendUIDData for +APPENDUID+ response codes.
#
# 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>
#
# <em>(Config option added in +v0.4.19+ and +v0.5.6+.)</em>
#
# <em>UIDPlusData will be removed in +v0.6+ and this config setting will
# be ignored.</em>
#
# ==== Valid options
#
# [+true+ <em>(original default)</em>]
# ResponseParser only uses UIDPlusData.
#
# [+false+ <em>(planned default for +v0.6+)</em>]
# ResponseParser _only_ uses AppendUIDData and CopyUIDData.
attr_accessor :parser_use_deprecated_uidplus_data, type: [
true, false
]

# Creates a new config object and initialize its attribute with +attrs+.
#
# If +parent+ is not given, the global config is used by default.
Expand Down Expand Up @@ -341,6 +367,7 @@ def defaults_hash
idle_response_timeout: 5,
sasl_ir: true,
responses_without_block: :silence_deprecation_warning,
parser_use_deprecated_uidplus_data: true,
).freeze

@global = default.new
Expand All @@ -349,6 +376,7 @@ def defaults_hash

version_defaults[0] = Config[0.4].dup.update(
sasl_ir: false,
parser_use_deprecated_uidplus_data: true,
).freeze
version_defaults[0.0] = Config[0]
version_defaults[0.1] = Config[0]
Expand All @@ -365,6 +393,7 @@ def defaults_hash

version_defaults[0.6] = Config[0.5].dup.update(
responses_without_block: :frozen_dup,
parser_use_deprecated_uidplus_data: false,
).freeze
version_defaults[:future] = Config[0.6]

Expand Down
57 changes: 3 additions & 54 deletions lib/net/imap/response_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ class IMAP < Protocol
autoload :FetchData, "#{__dir__}/fetch_data"
autoload :SearchResult, "#{__dir__}/search_result"
autoload :SequenceSet, "#{__dir__}/sequence_set"
autoload :UIDPlusData, "#{__dir__}/uidplus_data"
autoload :AppendUIDData, "#{__dir__}/uidplus_data"
autoload :CopyUIDData, "#{__dir__}/uidplus_data"

# Net::IMAP::ContinuationRequest represents command continuation requests.
#
Expand Down Expand Up @@ -324,60 +327,6 @@ class ResponseCode < Struct.new(:name, :data)
# code data can take.
end

# Net::IMAP::UIDPlusData represents the ResponseCode#data that accompanies
# the +APPENDUID+ and +COPYUID+ response codes.
#
# See [[UIDPLUS[https://www.rfc-editor.org/rfc/rfc4315.html]].
#
# ==== Capability requirement
#
# The +UIDPLUS+ capability[rdoc-ref:Net::IMAP#capability] must be supported.
# A server that supports +UIDPLUS+ should send a UIDPlusData object inside
# every TaggedResponse returned by 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+.
#
#--
# TODO: support MULTIAPPEND
#++
#
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

# Net::IMAP::MailboxList represents contents of the LIST response,
# representing a single mailbox path.
#
Expand Down
26 changes: 15 additions & 11 deletions lib/net/imap/response_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1867,11 +1867,10 @@ def charset__list
#
# n.b, uniqueid βŠ‚ uid-set. To avoid inconsistent return types, we always
# match uid_set even if that returns a single-member array.
#
def resp_code_apnd__data
validity = number; SP!
dst_uids = uid_set # uniqueid βŠ‚ uid-set
UIDPlusData.new(validity, nil, dst_uids)
AppendUID(validity, dst_uids)
end

# already matched: "COPYUID"
Expand All @@ -1881,6 +1880,17 @@ def resp_code_copy__data
validity = number; SP!
src_uids = uid_set; SP!
dst_uids = uid_set
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
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)
end

Expand Down Expand Up @@ -2007,15 +2017,9 @@ def nparens__objectid; NIL? ? nil : parens__objectid end
# uniqueid = nz-number
# ; Strictly ascending
def uid_set
token = match(T_NUMBER, T_ATOM)
case token.symbol
when T_NUMBER then [Integer(token.value)]
when T_ATOM
token.value.split(",").flat_map {|range|
range = range.split(":").map {|uniqueid| Integer(uniqueid) }
range.size == 1 ? range : Range.new(range.min, range.max).to_a
}
end
set = sequence_set
parse_error("uid-set cannot contain '*'") if set.include_star?
set
end

def nil_atom
Expand Down
Loading

0 comments on commit e4d57b1

Please sign in to comment.