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 Drop or move messages from Sup sources that are marked as deleted or
18 spam in the Sup index.
20 Currently only works with mbox sources.
23 sup-sync-back [options] <source>*
25 where <source>* is zero or more source URIs. If no sources are given,
26 sync back all usual sources.
28 You almost certainly want to run sup-sync --changed after this command.
29 Running this does not change the index.
33 opt :drop_deleted, "Drop deleted messages.", :default => false, :short => "d"
34 opt :move_deleted, "Move deleted messages to a local mbox file.", :type => String, :short => :none
35 opt :drop_spam, "Drop 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 :drop_deleted, :move_deleted
42 conflicts :drop_spam, :move_spam
45 unless opts[:drop_deleted] || opts[:move_deleted] || opts[:drop_spam] || opts[:move_spam]
51 index = Redwood::Index.new
54 deleted_fp, spam_fp = nil
56 deleted_fp = File.open(opts[:move_deleted], "a") if opts[:move_deleted]
57 spam_fp = File.open(opts[:move_spam], "a") if opts[:move_spam]
63 sources = ARGV.map do |uri|
64 s = index.source_for(uri) or Trollop::die "Unknown source: #{uri}. Did you add it with sup-add first?"
65 s.is_a?(Redwood::MBox::Loader) or Trollop::die "#{uri} is not an mbox source."
70 sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader }
74 sources.each do |source|
75 $stderr.puts "Scanning #{source}..."
77 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))
78 $stderr.puts "Nothing to do from this source; skipping"
83 num_dropped = num_moved = num_scanned = 0
85 out_fp = Tempfile.new "sup-sync-back-#{source.id}"
86 Redwood::PollManager.add_messages_from source do |m, offset, entry|
90 labels = entry[:label].split.map { |x| x.intern }.to_boolean_h
92 if labels.member? :deleted
93 if opts[:drop_deleted]
94 puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
96 elsif opts[:move_deleted] && labels.member?(:deleted)
97 puts "Moving deleted message #{source}##{offset}" if opts[:verbose]
98 save m, deleted_fp unless opts[:dry_run]
102 elsif labels.member? :spam
104 puts "Dropping spam message #{source}##{offset}" if opts[:verbose]
106 elsif opts[:move_spam] && labels.member?(:spam)
107 puts "Moving spam message #{source}##{offset}" if opts[:verbose]
108 save m, spam_fp unless opts[:dry_run]
112 save m, out_fp unless opts[:dry_run]
115 save m, out_fp unless opts[:dry_run]
118 nil # don't actually add anything!
120 $stderr.puts "Scanned #{num_scanned}, dropped #{num_dropped}, moved #{num_moved} messages from #{source}."
121 modified_sources << source if num_dropped > 0 || num_moved > 0
122 out_fp.close unless opts[:dry_run]
124 unless opts[:dry_run] || (num_dropped == 0 && num_moved == 0)
125 deleted_fp.flush if deleted_fp
126 spam_fp.flush if spam_fp
127 $stderr.puts "Moving #{out_fp.path} to #{source.file_path}"
128 FileUtils.mv out_fp.path, source.file_path
132 unless opts[:dry_run]
133 deleted_fp.close if deleted_fp
134 spam_fp.close if spam_fp
138 unless modified_sources.empty?
139 $stderr.puts "You should now run: sup-sync --changed #{modified_sources.join(' ')}"
141 rescue Exception => e
142 File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }