lib/sup/imap.rb in sup-0.1 vs lib/sup/imap.rb in sup-0.2

- old
+ new

@@ -1,10 +1,11 @@ require 'uri' require 'net/imap' require 'stringio' require 'time' require 'rmail' +require 'cgi' ## fucking imap fucking sucks. what the FUCK kind of committee of ## dunces designed this shit. ## ## imap talks about 'unique ids' for messages, to be used for @@ -60,14 +61,14 @@ @parsed_uri = URI(uri) @username = username @password = password @imap = nil - @imap_ids = {} + @imap_state = {} @ids = [] @last_scan = nil - @labels = (labels || []).freeze + @labels = ((labels || []) - LabelManager::RESERVED_LABELS).uniq.freeze @say_id = nil @mutex = Mutex.new end def self.suggest_labels_for path @@ -80,15 +81,17 @@ def host; @parsed_uri.host; end def port; @parsed_uri.port || (ssl? ? 993 : 143); end def mailbox x = @parsed_uri.path[1..-1] - x.nil? || x.empty? ? 'INBOX' : x + (x.nil? || x.empty?) ? 'INBOX' : CGI.unescape(x) end def ssl?; @parsed_uri.scheme == 'imaps' end def check + return unless start_offset + ids = @mutex.synchronize do unsynchronized_scan_mailbox @ids end @@ -102,27 +105,25 @@ def load_header id MBox::read_header StringIO.new(raw_header(id)) end def load_message id - RMail::Parser.read raw_full_message(id) + RMail::Parser.read raw_message(id) end def raw_header id unsynchronized_scan_mailbox - header, flags = get_imap_fields id, 'RFC822.HEADER', 'FLAGS' - ## very bad. this is very very bad. very bad bad bad. - header = header + "Status: RO\n" if flags.include? :Seen # fake an mbox-style read header # TODO: improve source-marked-as-read reporting system + header, flags = get_imap_fields id, 'RFC822.HEADER' header.gsub(/\r\n/, "\n") end synchronized :raw_header - def raw_full_message id + def raw_message id unsynchronized_scan_mailbox get_imap_fields(id, 'RFC822').first.gsub(/\r\n/, "\n") end - synchronized :raw_full_message + synchronized :raw_message def connect return if @imap safely { } # do nothing! end @@ -138,31 +139,41 @@ return if last_id == @ids.length range = (@ids.length + 1) .. last_id Redwood::log "fetching IMAP headers #{range}" - fetch(range, ['RFC822.SIZE', 'INTERNALDATE']).each do |v| + fetch(range, ['RFC822.SIZE', 'INTERNALDATE', 'FLAGS']).each do |v| id = make_id v @ids << id - @imap_ids[id] = v.seqno + @imap_state[id] = { :id => v.seqno, :flags => v.attr["FLAGS"] } end end synchronized :scan_mailbox def each + return unless start_offset + ids = @mutex.synchronize do unsynchronized_scan_mailbox @ids end start = ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." - start.upto(ids.length - 1) do |i| + start.upto(ids.length - 1) do |i| id = ids[i] - self.cur_offset = id - yield id, @labels + state = @mutex.synchronize { @imap_state[id] } or next + self.cur_offset = id + labels = { :Seen => :unread, + :Flagged => :starred, + :Deleted => :deleted + }.inject(@labels) do |cur, (imap, sup)| + cur + (state[:flags].include?(imap) ? [sup] : []) + end + + yield id, labels end end def start_offset unsynchronized_scan_mailbox @@ -257,12 +268,12 @@ msize, mdate = imap_stuff.attr['RFC822.SIZE'] % 10000000, Time.parse(imap_stuff.attr["INTERNALDATE"]) sprintf("%d%07d", mdate.to_i, msize).to_i end def get_imap_fields id, *fields - imap_id = @imap_ids[id] or raise OutOfSyncSourceError, "Unknown message id #{id}" + raise OutOfSyncSourceError, "Unknown message id #{id}" unless @imap_state[id] - retried = false + imap_id = @imap_state[id][:id] result = fetch(imap_id, (fields + ['RFC822.SIZE', 'INTERNALDATE']).uniq).first got_id = make_id result raise OutOfSyncSourceError, "IMAP message mismatch: requested #{id}, got #{got_id}." unless got_id == id fields.map { |f| result.attr[f] or raise FatalSourceError, "empty response from IMAP server: #{f}" }