]> git.cworth.org Git - sup/blob - bin/sup-recover-sources
chmod a+x bin/*
[sup] / bin / sup-recover-sources
1 #!/usr/bin/env ruby
2
3 require 'optparse'
4
5 $opts = {
6   :unusual => false,
7   :archive => false,
8   :scan_num => 10,
9 }
10
11
12 OPTIONPARSERSUCKS = "\n" + " " * 38
13 OptionParser.new do |opts|
14   opts.banner = <<EOS
15 Usage: sup-recover-sources [options] <source>+
16
17 Rebuilds a lost sources.yaml file by reading messages from a list of
18 sources and determining, for each source, the most prevalent
19 'source_id' field of messages from that source in the index.
20
21 The only non-deterministic component to this is that if the same
22 message appears in multiple sources, those sources may be
23 mis-diagnosed by this program.
24
25 If the first N messages (--scan-num below) all have the same source_id
26 in the index, the source will be added to sources.yaml. Otherwise, the
27 distribution will be printed, and you will have to add it by hand.
28
29 The offset pointer into the sources will be set to the end of the source,
30 so you will have to run sup-import --rebuild for each new source after
31 doing this.
32
33 Options include:
34 EOS
35
36   opts.on("--unusual", "Mark sources as 'unusual'. Only usual#{OPTIONPARSERSUCKS}sources will be polled by hand. Default:#{OPTIONPARSERSUCKS}#{$opts[:unusual]}.") { $opts[:unusual] = true }
37
38   opts.on("--archive", "Mark sources as 'archive'. New messages#{OPTIONPARSERSUCKS}from these sources will not appear in#{OPTIONPARSERSUCKS}the inbox. Default: #{$opts[:archive]}.") { $opts[:archive] = true }
39
40   opts.on("--scan-num N", Integer, "Number of messages to scan per source.#{OPTIONPARSERSUCKS}Default: #{$opts[:scan_num]}.") do |n|
41     $opts[:scan_num] = n
42   end
43
44   opts.on_tail("-h", "--help", "Show this message") do
45     puts opts
46     exit
47   end
48 end.parse(ARGV)
49
50 require "sup"
51 puts "loading index..."
52 index = Redwood::Index.new
53 index.load
54 puts "loaded index of #{index.size} messages"
55
56 ARGV.each do |fn|
57   next if index.source_for fn
58
59   ## TODO: merge this code with the same snippet in import
60   source = 
61     case fn
62     when %r!^imaps?://!
63       print "Username for #{fn}: "
64       username = $stdin.gets.chomp
65       print "Password for #{fn} (warning: cleartext): "
66       password = $stdin.gets.chomp
67       Redwood::IMAP.new(fn, username, password, nil, !$opts[:unusual], $opts[:archive])
68     else
69       Redwood::MBox::Loader.new(fn, nil, !$opts[:unusual], $opts[:archive])
70     end
71
72   source_ids = {}
73   count = 0
74   source.each do |offset, labels|
75     begin
76       m = Redwood::Message.new :source => source, :source_info => offset
77       docid, entry = index.load_entry_for_id m.id
78       next unless entry
79       #puts "# #{source} #{offset} #{entry[:source_id]}"
80
81       source_ids[entry[:source_id]] = (source_ids[entry[:source_id]] || 0) + 1
82       count += 1
83       break if count == $opts[:scan_num]
84     rescue Redwood::MessageFormatError => e
85       puts "# #{e.message}"
86     end
87   end
88
89   if source_ids.size == 1
90     id = source_ids.keys.first.to_i
91     puts "assigned #{source} to #{source_ids.keys.first}"
92     source.id = id
93     source.seek_to! source.total
94     index.add_source source
95   else
96     puts ">> unable to determine #{source}: #{source_ids.inspect}"
97   end
98 end
99
100 index.save