X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=lib%2Fsup%2Fmaildir.rb;h=2c33e3bdc06b08f68b1d4b3caf564021eb6d71b8;hb=46f8e5116f38c8248fdc8553db18f8d2132a1f46;hp=74a3e02554c7eed68ce29de41c71e1417b669fed;hpb=89ae317d6316170d05af99d5862a86cfb2cdff62;p=sup diff --git a/lib/sup/maildir.rb b/lib/sup/maildir.rb index 74a3e02..2c33e3b 100644 --- a/lib/sup/maildir.rb +++ b/lib/sup/maildir.rb @@ -9,7 +9,9 @@ module Redwood ## 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 @@ -22,7 +24,7 @@ class Maildir < Source 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 @@ -44,7 +46,35 @@ class Maildir < Source 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| @@ -56,7 +86,7 @@ class Maildir < Source def load_header id scan_mailbox - with_file_for(id) { |f| MBox::read_header f } + with_file_for(id) { |f| parse_raw_email_header f } end def load_message id @@ -86,7 +116,7 @@ class Maildir < Source initial_poll = @ids.empty? - Redwood::log "scanning maildir #@dir..." + debug "scanning maildir #@dir..." begin @mtimes.each_key do |d| subdir = File.join(@dir, d) @@ -105,18 +135,15 @@ class Maildir < Source @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! - #remove old id to fn mappings...hopefully this doesn't actually change - #anything...normally, we'll add to this list but never remove mail. - @ids_to_fns.delete_if { |k, v| !@ids.include?(k) } 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 @@ -170,6 +197,11 @@ private 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 @@ -181,7 +213,7 @@ private 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