]> git.cworth.org Git - sup/blob - bin/sup-sync-back
make sup-sync-back short-circuit work properly
[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 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.
20
21 Currently only works with mbox sources.
22
23 Usage:
24   sup-sync-back [options] <source>*
25
26 where <source>* is zero or more source URIs. If no sources are given,
27 sync back all usual sources.
28
29 You probably want to run sup-sync --changed after this command.
30
31 Options include:
32 EOS
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
40
41   conflicts :delete_deleted, :move_deleted
42   conflicts :delete_spam, :move_spam
43 end
44
45 Redwood::start
46 index = Redwood::Index.new
47 index.lock_or_die
48
49 deleted_fp, spam_fp = nil
50 unless opts[:dry_run]
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]
53 end
54
55 begin
56   index.load
57
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."
61     s
62   end
63
64   using_usual_sources =
65     if sources.empty?
66       sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader } 
67       true
68     else
69       false
70     end
71
72   any_modified = false
73   
74   sources.each do |source|
75     $stderr.puts "Scanning #{source}..."
76
77     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))
78       $stderr.puts "Nothing to do from this source; skipping"
79       next
80     end
81
82     source.reset!
83     num_deleted = 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[:delete_deleted]
94             puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
95             num_deleted += 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[:delete_spam]
104             puts "Deleting spam message #{source}##{offset}" if opts[:verbose]
105             num_deleted += 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}, deleted #{num_deleted}, moved #{num_moved} messages from #{source}."
121     any_modified = true if num_deleted > 0 || num_moved > 0
122     out_fp.close unless opts[:dry_run]
123
124     unless opts[:dry_run] || !any_modified
125       deleted_fp.flush if deleted_fp
126       spam_fp.flush if spam_fp
127       out_fp.close
128       $stderr.puts "Moving #{out_fp.path} to #{source.file_path}"
129       FileUtils.mv out_fp.path, source.file_path
130     end
131   end
132
133   unless opts[:dry_run]
134     deleted_fp.close if deleted_fp
135     spam_fp.close if spam_fp
136   end
137
138   $stderr.puts "Done."
139   if any_modified
140     if using_usual_sources
141       $stderr.puts "You should now run: sup-sync --changed"
142     else
143       $stderr.puts "You should now run: sup-sync --changed #{sources.join(' ')}"
144     end
145   end
146 rescue Exception => e
147   File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
148   raise
149 ensure
150   index.save
151   Redwood::finish
152   index.unlock
153 end