9 ## save a message 'm' to an open file pointer 'fp'
11 m.source.each_raw_full_message_line(m.source_info) { |l| fp.print l }
14 opts = Trollop::options do
15 version "sup-sync-back (sup #{Redwood::VERSION})"
17 Partially synchronizes a message source with the Sup index by deleting
18 or moving any messages from the source that are marked as deleted or
19 spam in the Sup index.
21 Currently only works with mbox sources.
24 sup-sync-back [options] <source>*
26 where <source>* is zero or more source URIs. If no sources are given,
27 sync back all usual sources.
29 You probably want to run sup-sync --changed after this command.
33 opt :delete_deleted, "Delete deleted messages.", :default => false, :short => "d"
34 opt :move_deleted, "Move deleted messages to a local mbox file.", :type => String, :short => :none
35 opt :delete_spam, "Delete spam messages.", :default => false, :short => "s"
36 opt :move_spam, "Move spam messages to a local mbox file.", :type => String, :short => :none
37 opt :verbose, "Print message ids as they're processed."
38 opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
39 opt :version, "Show version information", :short => :none
41 conflicts :delete_deleted, :move_deleted
42 conflicts :delete_spam, :move_spam
46 index = Redwood::Index.new
49 deleted_fp, spam_fp = nil
51 deleted_fp = File.open(opts[:move_deleted], "a") if opts[:move_deleted]
52 spam_fp = File.open(opts[:move_spam], "a") if opts[:move_spam]
58 sources = ARGV.map do |uri|
59 s = index.source_for(uri) or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?"
60 s.is_a?(Redwood::MBox::Loader) or Trollop::die "#{uri} is not an mbox source."
63 sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader } if sources.empty?
67 sources.each do |source|
68 $stderr.puts "Scanning #{source}..."
70 num_deleted = num_moved = num_scanned = 0
72 out_fp = Tempfile.new "sup-sync-back-#{source.id}"
73 Redwood::PollManager.add_messages_from source do |m, offset, entry|
77 labels = entry[:label].split.map { |x| x.intern }.to_boolean_h
79 if labels.member? :deleted
80 if opts[:delete_deleted]
81 puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
83 elsif opts[:move_deleted] && labels.member?(:deleted)
84 puts "Moving deleted message #{source}##{offset}" if opts[:verbose]
85 save m, deleted_fp unless opts[:dry_run]
89 elsif labels.member? :spam
91 puts "Deleting spam message #{source}##{offset}" if opts[:verbose]
93 elsif opts[:move_spam] && labels.member?(:spam)
94 puts "Moving spam message #{source}##{offset}" if opts[:verbose]
95 save m, spam_fp unless opts[:dry_run]
99 save m, out_fp unless opts[:dry_run]
102 save m, out_fp unless opts[:dry_run]
105 nil # don't actually add anything!
107 $stderr.puts "Scanned #{num_scanned}, deleted #{num_deleted}, moved #{num_moved} messages from #{source}."
108 any_modified = true if num_deleted > 0 || num_moved > 0
109 out_fp.close unless opts[:dry_run]
111 unless opts[:dry_run] || !any_modified
112 deleted_fp.flush if deleted_fp
113 spam_fp.flush if spam_fp
115 $stderr.puts "Moving #{out_fp.path} to #{source.file_path}"
116 FileUtils.mv out_fp.path, source.file_path
120 unless opts[:dry_run]
121 deleted_fp.close if deleted_fp
122 spam_fp.close if spam_fp
126 $stderr.puts "You should now run: sup-sync --changed #{sources.join(' ')}" if any_modified
127 rescue Exception => e
128 File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }