]> git.cworth.org Git - sup/blobdiff - lib/sup/maildir.rb
Merge branch 'hook-local-vars'
[sup] / lib / sup / maildir.rb
index 74a3e02554c7eed68ce29de41c71e1417b669fed..2c33e3bdc06b08f68b1d4b3caf564021eb6d71b8 100644 (file)
@@ -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