]> git.cworth.org Git - sup/blobdiff - bin/sup-sync-back
Merge branch 'master' into next
[sup] / bin / sup-sync-back
index c0d6e79b030381cbca6f8391e4a1c0310480f497..4216cf9e46f5db3c595b72edb8462f15bb0fe966 100644 (file)
@@ -8,14 +8,17 @@ require "sup"
 
 ## save a message 'm' to an open file pointer 'fp'
 def save m, fp
 
 ## save a message 'm' to an open file pointer 'fp'
 def save m, fp
-  m.source.each_raw_full_message_line(m.source_info) { |l| fp.print l }
+  m.source.each_raw_message_line(m.source_info) { |l| fp.print l }
+end
+def die msg
+  $stderr.puts "Error: #{msg}"
+  exit(-1)
 end
 
 opts = Trollop::options do
   version "sup-sync-back (sup #{Redwood::VERSION})"
   banner <<EOS
 end
 
 opts = Trollop::options do
   version "sup-sync-back (sup #{Redwood::VERSION})"
   banner <<EOS
-Partially synchronizes a message source with the Sup index by deleting
-or moving any messages from the source that are marked as deleted or
+Drop or move messages from Sup sources that are marked as deleted or
 spam in the Sup index.
 
 Currently only works with mbox sources.
 spam in the Sup index.
 
 Currently only works with mbox sources.
@@ -26,20 +29,34 @@ Usage:
 where <source>* is zero or more source URIs. If no sources are given,
 sync back all usual sources.
 
 where <source>* is zero or more source URIs. If no sources are given,
 sync back all usual sources.
 
-You probably want to run sup-sync --changed after this command.
+You almost certainly want to run sup-sync --changed after this command.
+Running this does not change the index.
 
 Options include:
 EOS
 
 Options include:
 EOS
-  opt :delete_deleted, "Delete deleted messages.", :default => false, :short => "d"
+  opt :drop_deleted, "Drop deleted messages.", :default => false, :short => "d"
   opt :move_deleted, "Move deleted messages to a local mbox file.", :type => String, :short => :none
   opt :move_deleted, "Move deleted messages to a local mbox file.", :type => String, :short => :none
-  opt :delete_spam, "Delete spam messages.", :default => false, :short => "s"
+  opt :drop_spam, "Drop spam messages.", :default => false, :short => "s"
   opt :move_spam, "Move spam messages to a local mbox file.", :type => String, :short => :none
   opt :move_spam, "Move spam messages to a local mbox file.", :type => String, :short => :none
+
+  opt :with_dotlockfile, "Specific dotlockfile location (mbox files only).", :default => "/usr/bin/dotlockfile", :short => :none
+  opt :dont_use_dotlockfile, "Don't use dotlockfile to lock mbox files. Dangerous if other processes modify them concurrently.", :default => false, :short => :none
+
   opt :verbose, "Print message ids as they're processed."
   opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
   opt :version, "Show version information", :short => :none
 
   opt :verbose, "Print message ids as they're processed."
   opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
   opt :version, "Show version information", :short => :none
 
-  conflicts :delete_deleted, :move_deleted
-  conflicts :delete_spam, :move_spam
+  conflicts :drop_deleted, :move_deleted
+  conflicts :drop_spam, :move_spam
+end
+
+unless opts[:drop_deleted] || opts[:move_deleted] || opts[:drop_spam] || opts[:move_spam]
+  puts <<EOS
+Nothing to do. Please specify at least one of --drop-deleted, --move-deleted,
+--drop-spam, or --move-spam.
+EOS
+
+  exit
 end
 
 Redwood::start
 end
 
 Redwood::start
@@ -52,22 +69,40 @@ unless opts[:dry_run]
   spam_fp = File.open(opts[:move_spam], "a") if opts[:move_spam]
 end
 
   spam_fp = File.open(opts[:move_spam], "a") if opts[:move_spam]
 end
 
+dotlockfile = opts[:with_dotlockfile] || "/usr/bin/dotlockfile"
+
 begin
   index.load
 
   sources = ARGV.map do |uri|
 begin
   index.load
 
   sources = ARGV.map do |uri|
-    s = index.source_for(uri) or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?"
-    s.is_a?(Redwood::MBox::Loader) or Trollop::die "#{uri} is not an mbox source."
+    s = index.source_for(uri) or die "unknown source: #{uri}. Did you add it with sup-add first?"
+    s.is_a?(Redwood::MBox::Loader) or die "#{uri} is not an mbox source."
     s
   end
     s
   end
-  sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader } if sources.empty?
 
 
-  any_modified = false
-  
+  if sources.empty?
+    sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader } 
+  end
+
+  unless sources.all? { |s| s.file_path.nil? } || File.executable?(dotlockfile) || opts[:dont_use_dotlockfile]
+    die <<EOS
+can't execute dotlockfile binary: #{dotlockfile}. Specify --with-dotlockfile
+if it's in a nonstandard location, or, if you want to live dangerously, try
+--dont-use-dotlockfile
+EOS
+  end
+
+  modified_sources = []
   sources.each do |source|
     $stderr.puts "Scanning #{source}..."
   sources.each do |source|
     $stderr.puts "Scanning #{source}..."
+
+    unless ((opts[:drop_deleted] || opts[:move_deleted]) && index.has_any_from_source_with_label?(source, :deleted)) || ((opts[:drop_spam] || opts[:move_spam]) && index.has_any_from_source_with_label?(source, :spam))
+      $stderr.puts "Nothing to do from this source; skipping"
+      next
+    end
+
     source.reset!
     source.reset!
-    num_deleted = num_moved = num_scanned = 0
+    num_dropped = num_moved = num_scanned = 0
     
     out_fp = Tempfile.new "sup-sync-back-#{source.id}"
     Redwood::PollManager.add_messages_from source do |m, offset, entry|
     
     out_fp = Tempfile.new "sup-sync-back-#{source.id}"
     Redwood::PollManager.add_messages_from source do |m, offset, entry|
@@ -77,9 +112,9 @@ begin
         labels = entry[:label].split.map { |x| x.intern }.to_boolean_h
 
         if labels.member? :deleted
         labels = entry[:label].split.map { |x| x.intern }.to_boolean_h
 
         if labels.member? :deleted
-          if opts[:delete_deleted]
+          if opts[:drop_deleted]
             puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
             puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
-            num_deleted += 1
+            num_dropped += 1
           elsif opts[:move_deleted] && labels.member?(:deleted)
             puts "Moving deleted message #{source}##{offset}" if opts[:verbose]
             save m, deleted_fp unless opts[:dry_run]
           elsif opts[:move_deleted] && labels.member?(:deleted)
             puts "Moving deleted message #{source}##{offset}" if opts[:verbose]
             save m, deleted_fp unless opts[:dry_run]
@@ -87,9 +122,9 @@ begin
           end
 
         elsif labels.member? :spam
           end
 
         elsif labels.member? :spam
-          if opts[:delete_spam]
-            puts "Deleting spam message #{source}##{offset}" if opts[:verbose]
-            num_deleted += 1
+          if opts[:drop_spam]
+            puts "Dropping spam message #{source}##{offset}" if opts[:verbose]
+            num_dropped += 1
           elsif opts[:move_spam] && labels.member?(:spam)
             puts "Moving spam message #{source}##{offset}" if opts[:verbose]
             save m, spam_fp unless opts[:dry_run]
           elsif opts[:move_spam] && labels.member?(:spam)
             puts "Moving spam message #{source}##{offset}" if opts[:verbose]
             save m, spam_fp unless opts[:dry_run]
@@ -104,15 +139,21 @@ begin
 
       nil # don't actually add anything!
     end
 
       nil # don't actually add anything!
     end
-    $stderr.puts "Scanned #{num_scanned}, deleted #{num_deleted}, moved #{num_moved} messages from #{source}."
-    any_modified = true if num_deleted > 0 || num_moved > 0
+    $stderr.puts "Scanned #{num_scanned}, dropped #{num_dropped}, moved #{num_moved} messages from #{source}."
+    modified_sources << source if num_dropped > 0 || num_moved > 0
     out_fp.close unless opts[:dry_run]
 
     out_fp.close unless opts[:dry_run]
 
-    unless opts[:dry_run] || !any_modified
+    unless opts[:dry_run] || (num_dropped == 0 && num_moved == 0)
       deleted_fp.flush if deleted_fp
       spam_fp.flush if spam_fp
       deleted_fp.flush if deleted_fp
       spam_fp.flush if spam_fp
-      out_fp.close
-      FileUtils.mv out_fp.path, source.file_path
+      unless opts[:dont_use_dotlockfile]
+        puts "Locking #{source.file_path}..."
+        system "#{opts[:dotlockfile]} -l #{source.file_path}"
+        puts "Writing #{source.file_path}..."
+        FileUtils.cp out_fp.path, source.file_path
+        puts "Unlocking #{source.file_path}..."
+        system "#{opts[:dotlockfile]} -u #{source.file_path}"
+      end
     end
   end
 
     end
   end
 
@@ -122,12 +163,13 @@ begin
   end
 
   $stderr.puts "Done."
   end
 
   $stderr.puts "Done."
-  $stderr.puts "You should now run: sup-sync --changed #{sources.join(' ')}" if any_modified
+  unless modified_sources.empty?
+    $stderr.puts "You should now run: sup-sync --changed #{modified_sources.join(' ')}"
+  end
 rescue Exception => e
   File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
   raise
 ensure
 rescue Exception => e
   File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
   raise
 ensure
-  index.save
   Redwood::finish
   index.unlock
 end
   Redwood::finish
   index.unlock
 end