Skip to content

Commit

Permalink
Fix IMAP search issues
Browse files Browse the repository at this point in the history
The code had several issues with existing IMAP UID search:
* net-imap <= v0.3 returns `nil` when the server returns nothing.
  (>= v0.4 returns an empty array when the server returns nothing.)
* net-imap v0.4.8 and v0.4.9 return a _frozen_ `SearchResult`.
  `SearchResult` inherits from `Array`, but because it's frozen,
  mutating it with `reverse!` results in an exception.
  See ruby/net-imap#262.
  (v0.4.10 and above doesn't freeze SearchResult.)
* newer net-imap (>= 0.5) will support servers that send `ESEARCH`
  results, but those will be returned as an `ESearchResult` struct
  (probably frozen).  `IMAP4rev1` servers shouldn't return `ESEARCH`
  unless the client has activated that extension, but `IMAP4rev2`
  servers will _always_ return `ESEARCH`.
* RFC3501 says nothing about sorting the UIDs that come back from
  `UID SEARCH`.  Almost all servers do return sorted UIDs, most of the
  time.  But it isn't 100% reliable.

The fix is simple:
* use `#to_a` to convert both `nil` and `ESearchResult` into an array.
* use `#sort` to ensure the UIDs are sorted.  This also returns a new
  array, without mutating the original (which may be frozen).
  • Loading branch information
nevans committed May 21, 2024
1 parent 3dd53e0 commit c779866
Showing 1 changed file with 3 additions and 0 deletions.
3 changes: 3 additions & 0 deletions lib/mail/network/retriever_methods/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ def find(options=nil, &block)

start do |imap|
options[:read_only] ? imap.examine(options[:mailbox]) : imap.select(options[:mailbox])

uids = imap.uid_search(options[:keys], options[:search_charset])
.to_a # older net-imap may return nil, newer may return ESearchResult struct
.sort # RFC3501 does _not_ require UIDs to be returned in order
uids.reverse! if options[:what].to_sym == :last
uids = uids.first(options[:count]) if options[:count].is_a?(Integer)
uids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
Expand Down

0 comments on commit c779866

Please sign in to comment.