]> git.notmuchmail.org Git - sup/blob - bin/sup-sync-back
d61f219a09e428ee27d90283ab4133f15e8bdd4b
[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   sources = index.usual_sources.select { |s| s.is_a? Redwood::MBox::Loader } if sources.empty?
64
65   any_modified = false
66   
67   sources.each do |source|
68     $stderr.puts "Scanning #{source}..."
69     source.reset!
70     num_deleted = num_moved = num_scanned = 0
71     
72     out_fp = Tempfile.new "sup-sync-back-#{source.id}"
73     Redwood::PollManager.add_messages_from source do |m, offset, entry|
74       num_scanned += 1
75
76       if entry
77         labels = entry[:label].split.map { |x| x.intern }.to_boolean_h
78
79         if labels.member? :deleted
80           if opts[:delete_deleted]
81             puts "Dropping deleted message #{source}##{offset}" if opts[:verbose]
82             num_deleted += 1
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]
86             num_moved += 1
87           end
88
89         elsif labels.member? :spam
90           if opts[:delete_spam]
91             puts "Deleting spam message #{source}##{offset}" if opts[:verbose]
92             num_deleted += 1
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]
96             num_moved += 1
97           end
98         else
99           save m, out_fp unless opts[:dry_run]
100         end
101       else
102         save m, out_fp unless opts[:dry_run]
103       end
104
105       nil # don't actually add anything!
106     end
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]
110
111     unless opts[:dry_run] || !any_modified
112       deleted_fp.flush if deleted_fp
113       spam_fp.flush if spam_fp
114       out_fp.close
115       $stderr.puts "Moving #{out_fp.path} to #{source.file_path}"
116       FileUtils.mv out_fp.path, source.file_path
117     end
118   end
119
120   unless opts[:dry_run]
121     deleted_fp.close if deleted_fp
122     spam_fp.close if spam_fp
123   end
124
125   $stderr.puts "Done."
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 }
129   raise
130 ensure
131   index.save
132   Redwood::finish
133   index.unlock
134 end