]> git.cworth.org Git - sup/blob - bin/sup-sync-back
95819b6e685739f1448c626b38a0a7e2d6aaf4f0
[sup] / bin / sup-sync-back
1 #!/usr/bin/env ruby
2
3 require 'rubygems'
4 require 'uri'
5 require 'tempfile'
6 require 'trollop'
7 require "sup"
8
9 ## save a message 'm' to an open file pointer 'fp'
10 def save m, fp
11   m.source.each_raw_full_message_line(m.source_info) { |l| fp.print l }
12 end
13
14 opts = Trollop::options do
15   version "sup-sync-back (sup #{Redwood::VERSION})"
16   banner <<EOS
17 Drop or move messages from Sup sources that are marked as deleted or
18 spam in the Sup index.
19
20 Currently only works with mbox sources.
21
22 Usage:
23   sup-sync-back [options] <source>*
24
25 where <source>* is zero or more source URIs. If no sources are given,
26 sync back all usual sources.
27
28 You almost certainly want to run sup-sync --changed after this command.
29 Running this does not change the index.
30
31 Options include:
32 EOS
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
40
41   conflicts :drop_deleted, :move_deleted
42   conflicts :drop_spam, :move_spam
43 end
44
45 unless opts[:drop_deleted] || opts[:move_deleted] || opts[:drop_spam] || opts[:move_spam]
46   puts "Nothing to do."
47   exit
48 end
49
50 Redwood::start
51 index = Redwood::Index.new
52 index.lock_or_die
53
54 deleted_fp, spam_fp = nil
55 unless opts[:dry_run]
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]
58 end
59
60 begin
61   index.load
62
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."
66     s
67   end
68
69   if sources.empty?
70     sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader } 
71   end
72
73   modified_sources = []
74   sources.each do |source|
75     $stderr.puts "Scanning #{source}..."
76
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"
79       next
80     end
81
82     source.reset!
83     num_dropped = num_moved = num_scanned = 0
84     
85     out_fp = Tempfile.new "sup-sync-back-#{source.id}"
86     Redwood::PollManager.add_messages_from source do |m, offset, entry|
87       num_scanned += 1
88
89       if entry
90         labels = entry[:label].split.map { |x| x.intern }.to_boolean_h
91
92         if labels.member? :deleted
93           if opts[:drop_deleted]
94             puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
95             num_dropped += 1
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]
99             num_moved += 1
100           end
101
102         elsif labels.member? :spam
103           if opts[:drop_spam]
104             puts "Dropping spam message #{source}##{offset}" if opts[:verbose]
105             num_dropped += 1
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]
109             num_moved += 1
110           end
111         else
112           save m, out_fp unless opts[:dry_run]
113         end
114       else
115         save m, out_fp unless opts[:dry_run]
116       end
117
118       nil # don't actually add anything!
119     end
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]
123
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
129     end
130   end
131
132   unless opts[:dry_run]
133     deleted_fp.close if deleted_fp
134     spam_fp.close if spam_fp
135   end
136
137   $stderr.puts "Done."
138   unless modified_sources.empty?
139     $stderr.puts "You should now run: sup-sync --changed #{modified_sources.join(' ')}"
140   end
141 rescue Exception => e
142   File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
143   raise
144 ensure
145   index.save
146   Redwood::finish
147   index.unlock
148 end