lib/sup/maildir.rb in sup-0.8.1 vs lib/sup/maildir.rb in sup-0.9
- old
+ new
@@ -7,11 +7,13 @@
## requires to be really useful. So we must maintain, in memory, a
## mapping between Sup "ids" (timestamps, essentially) and the
## pathnames on disk.
class Maildir < Source
+ include SerializeLabelsNicely
SCAN_INTERVAL = 30 # seconds
+ MYHOSTNAME = Socket.gethostname
## remind me never to use inheritance again.
yaml_properties :uri, :cur_offset, :usual, :archived, :id, :labels, :mtimes
def initialize uri, last_date=nil, usual=true, archived=false, id=nil, labels=[], mtimes={}
super uri, last_date, usual, archived, id
@@ -20,11 +22,11 @@
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
+ @labels = Set.new(labels || [])
@ids = []
@ids_to_fns = {}
@last_scan = nil
@mutex = Mutex.new
#the mtime from the subdirs in the maildir with the unix epoch as default.
@@ -42,11 +44,39 @@
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
-
+
+ def store_message date, from_email, &block
+ stored = false
+ new_fn = new_maildir_basefn + ':2,S'
+ Dir.chdir(@dir) do |d|
+ tmp_path = File.join(@dir, 'tmp', new_fn)
+ new_path = File.join(@dir, 'new', new_fn)
+ begin
+ sleep 2 if File.stat(tmp_path)
+
+ File.stat(tmp_path)
+ rescue Errno::ENOENT #this is what we want.
+ begin
+ File.open(tmp_path, 'w') do |f|
+ yield f #provide a writable interface for the caller
+ f.fsync
+ end
+
+ File.link tmp_path, new_path
+ stored = true
+ ensure
+ File.unlink tmp_path if File.exists? tmp_path
+ end
+ end #rescue Errno...
+ end #Dir.chdir
+
+ stored
+ end
+
def each_raw_message_line id
scan_mailbox
with_file_for(id) do |f|
until f.eof?
yield f.gets
@@ -84,11 +114,11 @@
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..."
+ debug "scanning maildir #@dir..."
begin
@mtimes.each_key do |d|
subdir = File.join(@dir, d)
raise FatalSourceError, "#{subdir} not a directory" unless File.directory? subdir
@@ -103,19 +133,19 @@
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."
+ debug "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.message}."
end
- Redwood::log "done scanning maildir"
+ debug "done scanning maildir"
@last_scan = Time.now
end
synchronized :scan_mailbox
def each
@@ -165,10 +195,15 @@
stat = File.stat(fn)
# use 7 digits for the size. why 7? seems nice.
sprintf("%d%07d", stat.mtime, stat.size % 10000000).to_i
end
+ def new_maildir_basefn
+ Kernel::srand()
+ "#{Time.now.to_i.to_s}.#{$$}#{Kernel.rand(1000000)}.#{MYHOSTNAME}"
+ end
+
def with_file_for id
fn = @ids_to_fns[id] or raise OutOfSyncSourceError, "No such id: #{id.inspect}."
begin
File.open(fn) { |f| yield f }
rescue SystemCallError, IOError => e
@@ -176,10 +211,10 @@
end
end
def maildir_data msg
fn = File.basename @ids_to_fns[msg]
- fn =~ %r{^([^:,]+):([12]),([DFPRST]*)$}
+ fn =~ %r{^([^:]+):([12]),([DFPRST]*)$}
[($1 || fn), ($2 || "2"), ($3 || "")]
end
## not thread-safe on msg
def maildir_mark_file msg, flag