6 ## Maildir doesn't provide an ordered unique id, which is what Sup
7 ## requires to be really useful. So we must maintain, in memory, a
8 ## mapping between Sup "ids" (timestamps, essentially) and the
11 class Maildir < Source
12 SCAN_INTERVAL = 30 # seconds
14 def initialize uri, last_date=nil, usual=true, archived=false, id=nil
18 raise ArgumentError, "not a maildir URI" unless uri.scheme == "maildir"
19 raise ArgumentError, "maildir URI cannot have a host: #{uri.host}" if uri.host
30 start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email
35 with_file_for(id) { |f| MBox::read_header f }
40 with_file_for(id) { |f| RMail::Parser.read f }
46 with_file_for(id) do |f|
47 until f.eof? || (l = f.gets) =~ /^$/
54 def raw_full_message id
56 with_file_for(id) { |f| f.readlines.join }
60 return if @last_scan && (Time.now - @last_scan) < SCAN_INTERVAL
62 cdir = File.join(@dir, 'cur')
63 ndir = File.join(@dir, 'new')
65 raise FatalSourceError, "#{cdir} not a directory" unless File.directory? cdir
66 raise FatalSourceError, "#{ndir} not a directory" unless File.directory? ndir
69 @ids, @ids_to_fns = @mutex.synchronize do
70 ids, ids_to_fns = [], {}
71 (Dir[File.join(cdir, "*")] + Dir[File.join(ndir, "*")]).map do |fn|
76 [ids.sort, ids_to_fns]
78 rescue SystemCallError, IOError => e
79 raise FatalSourceError, "Problem scanning Maildir directories: #{e.message}."
87 start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email
89 start.upto(@ids.length - 1) do |i|
92 yield id, (@ids_to_fns[id] =~ /,.*R.*$/ ? [] : [:unread])
106 def pct_done; 100.0 * (@ids.index(cur_offset) || 0).to_f / (@ids.length - 1).to_f; end
111 # use 7 digits for the size. why 7? seems nice.
112 sprintf("%d%07d", File.mtime(fn), File.size(fn)).to_i
116 fn = @ids_to_fns[id] or raise OutOfSyncSourceError, "No such id: #{id.inspect}."
118 File.open(fn) { |f| yield f }
119 rescue SystemCallError, IOError => e
120 raise FatalSourceError, "Problem reading file for id #{id.inspect}: #{fn.inspect}: #{e.message}."
125 Redwood::register_yaml(Maildir, %w(uri cur_offset usual archived id))