Class: Redwood::Maildir
Overview
Maildir doesn't provide an ordered unique id, which is what Sup requires to be really useful. So we must maintain, in memory, a mapping between Sup "ids" (timestamps, essentially) and the pathnames on disk.
Constant Summary
- SCAN_INTERVAL =
seconds
30
Instance Attribute Summary
Attributes inherited from Source
Class Method Summary (collapse)
Instance Method Summary (collapse)
- - (Object) check
- - (Boolean) draft?(msg)
- - (Object) each
- - (Object) each_raw_message_line(id)
- - (Object) end_offset
- - (Object) file_path
- - (Boolean) flagged?(msg)
-
- (Maildir) initialize(uri, last_date = nil, usual = true, archived = false, id = nil, labels = [], mtimes = {})
constructor
A new instance of Maildir.
- - (Boolean) is_source_for?(uri)
- - (Object) load_header(id)
- - (Object) load_message(id)
- - (Object) mark_draft(msg)
- - (Object) mark_flagged(msg)
- - (Object) mark_passed(msg)
- - (Object) mark_replied(msg)
- - (Object) mark_seen(msg)
- - (Object) mark_trashed(msg)
- - (Boolean) passed?(msg)
- - (Object) pct_done
- - (Object) raw_header(id)
- - (Object) raw_message(id)
- - (Boolean) replied?(msg)
- - (Object) scan_mailbox(opts = {})
- - (Boolean) seen?(msg)
- - (Object) start_offset
- - (Boolean) trashed?(msg)
Methods inherited from Source
#==, #done?, #reset!, #seek_to!, #to_s
Constructor Details
- (Maildir) initialize(uri, last_date = nil, usual = true, archived = false, id = nil, labels = [], mtimes = {})
A new instance of Maildir
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/sup/maildir.rb', line 16 def initialize uri, last_date=nil, usual=true, archived=false, id=nil, labels=[], mtimes={} super uri, last_date, usual, archived, id uri = URI(Source.(uri)) raise ArgumentError, "not a maildir URI" unless uri.scheme == "maildir" raise ArgumentError, "maildir URI cannot have a host: #{uri.host}" if uri.host raise ArgumentError, "maildir URI must have a path component" unless uri.path @dir = uri.path @labels = (labels || []).freeze @ids = [] @ids_to_fns = {} @last_scan = nil @mutex = Mutex.new #the mtime from the subdirs in the maildir with the unix epoch as default. #these are used to determine whether scanning the directory for new mail #is a worthwhile effort @mtimes = { 'cur' => Time.at(0), 'new' => Time.at(0) }.merge(mtimes || {}) @dir_ids = { 'cur' => [], 'new' => [] } end |
Class Method Details
+ (Object) suggest_labels_for(path)
38 |
# File 'lib/sup/maildir.rb', line 38 def self.suggest_labels_for path; [] end |
Instance Method Details
- (Object) check
41 42 43 44 45 46 |
# File 'lib/sup/maildir.rb', line 41 def check scan_mailbox return unless start_offset start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email end |
- (Boolean) draft?(msg)
146 |
# File 'lib/sup/maildir.rb', line 146 def draft? msg; maildir_data(msg)[2].include? "D"; end |
- (Object) each
121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/sup/maildir.rb', line 121 def each scan_mailbox return unless start_offset start = @ids.index(cur_offset || start_offset) or raise OutOfSyncSourceError, "Unknown message id #{cur_offset || start_offset}." # couldn't find the most recent email start.upto(@ids.length - 1) do |i| id = @ids[i] self.cur_offset = id yield id, @labels + (seen?(id) ? [] : [:unread]) + (trashed?(id) ? [:deleted] : []) + (flagged?(id) ? [:starred] : []) end end |
- (Object) each_raw_message_line(id)
48 49 50 51 52 53 54 55 |
# File 'lib/sup/maildir.rb', line 48 def id scan_mailbox with_file_for(id) do |f| until f.eof? yield f.gets end end end |
- (Object) end_offset
139 140 141 142 |
# File 'lib/sup/maildir.rb', line 139 def end_offset scan_mailbox :rescan => true @ids.last + 1 end |
- (Object) file_path
37 |
# File 'lib/sup/maildir.rb', line 37 def file_path; @dir end |
- (Boolean) flagged?(msg)
147 |
# File 'lib/sup/maildir.rb', line 147 def flagged? msg; maildir_data(msg)[2].include? "F"; end |
- (Boolean) is_source_for?(uri)
39 |
# File 'lib/sup/maildir.rb', line 39 def is_source_for? uri; super || (URI(Source.(uri)) == URI(self.uri)); end |
- (Object) load_header(id)
57 58 59 60 |
# File 'lib/sup/maildir.rb', line 57 def load_header id scan_mailbox with_file_for(id) { |f| MBox::read_header f } end |
- (Object) load_message(id)
62 63 64 65 |
# File 'lib/sup/maildir.rb', line 62 def id scan_mailbox with_file_for(id) { |f| RMail::Parser.read f } end |
- (Object) mark_draft(msg)
153 |
# File 'lib/sup/maildir.rb', line 153 def mark_draft msg; maildir_mark_file msg, "D" unless draft? msg; end |
- (Object) mark_flagged(msg)
154 |
# File 'lib/sup/maildir.rb', line 154 def mark_flagged msg; maildir_mark_file msg, "F" unless flagged? msg; end |
- (Object) mark_passed(msg)
155 |
# File 'lib/sup/maildir.rb', line 155 def mark_passed msg; maildir_mark_file msg, "P" unless passed? msg; end |
- (Object) mark_replied(msg)
156 |
# File 'lib/sup/maildir.rb', line 156 def mark_replied msg; maildir_mark_file msg, "R" unless replied? msg; end |
- (Object) mark_seen(msg)
157 |
# File 'lib/sup/maildir.rb', line 157 def mark_seen msg; maildir_mark_file msg, "S" unless seen? msg; end |
- (Object) mark_trashed(msg)
158 |
# File 'lib/sup/maildir.rb', line 158 def mark_trashed msg; maildir_mark_file msg, "T" unless trashed? msg; end |
- (Boolean) passed?(msg)
148 |
# File 'lib/sup/maildir.rb', line 148 def passed? msg; maildir_data(msg)[2].include? "P"; end |
- (Object) pct_done
144 |
# File 'lib/sup/maildir.rb', line 144 def pct_done; 100.0 * (@ids.index(cur_offset) || 0).to_f / (@ids.length - 1).to_f; end |
- (Object) raw_header(id)
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/sup/maildir.rb', line 67 def raw_header id scan_mailbox ret = "" with_file_for(id) do |f| until f.eof? || (l = f.gets) =~ /^$/ ret += l end end ret end |
- (Object) raw_message(id)
78 79 80 81 |
# File 'lib/sup/maildir.rb', line 78 def id scan_mailbox with_file_for(id) { |f| f.read } end |
- (Boolean) replied?(msg)
149 |
# File 'lib/sup/maildir.rb', line 149 def replied? msg; maildir_data(msg)[2].include? "R"; end |
- (Object) scan_mailbox(opts = {})
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/sup/maildir.rb', line 83 def scan_mailbox opts={} return unless @ids.empty? || opts[:rescan] return if @last_scan && (Time.now - @last_scan) < SCAN_INTERVAL initial_poll = @ids.empty? Redwood::log "scanning maildir #@dir..." begin @mtimes.each_key do |d| subdir = File.join(@dir, d) raise FatalSourceError, "#{subdir} not a directory" unless File.directory? subdir mtime = File.mtime subdir #only scan the dir if the mtime is more recent (or we haven't polled #since startup) if @mtimes[d] < mtime || initial_poll @mtimes[d] = mtime @dir_ids[d] = [] Dir[File.join(subdir, '*')].map do |fn| id = make_id fn @dir_ids[d] << id @ids_to_fns[id] = fn end else Redwood::log "no poll on #{d}. mtime on indicates no new messages." end end @ids = @dir_ids.values.flatten.uniq.sort! rescue SystemCallError, IOError => e raise FatalSourceError, "Problem scanning Maildir directories: #{e.}." end Redwood::log "done scanning maildir" @last_scan = Time.now end |
- (Boolean) seen?(msg)
150 |
# File 'lib/sup/maildir.rb', line 150 def seen? msg; maildir_data(msg)[2].include? "S"; end |
- (Object) start_offset
134 135 136 137 |
# File 'lib/sup/maildir.rb', line 134 def start_offset scan_mailbox @ids.first end |
- (Boolean) trashed?(msg)
151 |
# File 'lib/sup/maildir.rb', line 151 def trashed? msg; maildir_data(msg)[2].include? "T"; end |