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}" }