#!/usr/bin/env ruby require 'rubygems' require 'uri' require 'tempfile' require 'trollop' require "sup" ## 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 } 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 spam in the Sup index. Currently only works with mbox sources. Usage: sup-sync-back [options] <source>* 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. Options include: EOS opt :delete_deleted, "Delete deleted messages.", :default => false, :short => "d" 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 :move_spam, "Move spam messages to a local mbox file.", :type => String, :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 end Redwood::start index = Redwood::Index.new index.lock_or_die deleted_fp, spam_fp = nil unless opts[:dry_run] deleted_fp = File.open(opts[:move_deleted], "a") if opts[:move_deleted] spam_fp = File.open(opts[:move_spam], "a") if opts[:move_spam] end 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 end if sources.empty? sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader } end modified_sources = [] sources.each do |source| $stderr.puts "Scanning #{source}..." unless ((opts[:delete_deleted] || opts[:move_deleted]) && index.has_any_from_source_with_label?(source, :deleted)) || ((opts[:delete_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! num_deleted = num_moved = num_scanned = 0 out_fp = Tempfile.new "sup-sync-back-#{source.id}" Redwood::PollManager.add_messages_from source do |m, offset, entry| num_scanned += 1 if entry labels = entry[:label].split.map { |x| x.intern }.to_boolean_h if labels.member? :deleted if opts[:delete_deleted] puts "Dropping deleted message #{source}##{offset}" if opts[:verbose] num_deleted += 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] num_moved += 1 end elsif labels.member? :spam if opts[:delete_spam] puts "Deleting spam message #{source}##{offset}" if opts[:verbose] num_deleted += 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] num_moved += 1 end else save m, out_fp unless opts[:dry_run] end else save m, out_fp unless opts[:dry_run] end nil # don't actually add anything! end $stderr.puts "Scanned #{num_scanned}, deleted #{num_deleted}, moved #{num_moved} messages from #{source}." modified_sources << source if num_deleted > 0 || num_moved > 0 out_fp.close unless opts[:dry_run] unless opts[:dry_run] || (num_deleted == 0 && num_moved == 0) deleted_fp.flush if deleted_fp spam_fp.flush if spam_fp $stderr.puts "Moving #{out_fp.path} to #{source.file_path}" FileUtils.mv out_fp.path, source.file_path end end unless opts[:dry_run] deleted_fp.close if deleted_fp spam_fp.close if spam_fp end $stderr.puts "Done." 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 index.save Redwood::finish index.unlock end