7 Thread.abort_on_exception = true # make debugging possible
10 def to_s; sprintf '%.2f', self; end
16 sprintf "%d:%02d:%02d", i / 3600, (i / 60) % 60, i % 60
28 Loads messages into the Sup index, adding sources as needed to the
32 sup-import [options] <source>*
33 where <source>* is zero or more source descriptions (e.g., mbox
34 filenames on disk, or imap/imaps URIs).
36 If the sources listed are not already in the Sup source list,
37 they will be added to it, as parameterized by the following options:
38 --archive: messages from these sources will not appear in the inbox
39 --unusual: these sources will not be polled when the flag --the-usual
42 Regardless of whether the sources are new or not, they will be polled,
43 and any new messages will be added to the index, as parameterized by
44 the following options:
45 --force-archive: regardless of the source "archive" flag, any new
46 messages found will not appear in the inbox.
47 --force-read: any messages found will not be marked as new.
49 The following options can also be specified:
50 --the-usual: import new messages from all usual sources
51 --rebuild: rebuild the index for the specified sources rather than
52 just adding new messages. Useful if the sources
53 have changed in any way *other* than new messages
55 --force-rebuild: force a rebuild of all messages in the inbox, not just
56 ones that have changed. You probably won't need this
57 unless William changes the index format.
58 --optimize: optimize the index after adding any new messages.
59 --help: don't do anything, just show this message.
64 educate_user if ARGV.member? '--help'
66 archive = ARGV.delete "--archive"
67 unusual = ARGV.delete "--unusual"
68 force_archive = ARGV.delete "--force-archive"
69 force_read = ARGV.delete "--force-read"
70 the_usual = ARGV.delete "--the-usual"
71 rebuild = ARGV.delete "--rebuild"
72 force_rebuild = ARGV.delete "--force-rebuild"
73 optimize = ARGV.delete "--optimize"
74 start_at = # ok really need to use optparse or something now
75 if(i = ARGV.index("--start-at"))
76 raise "start-at requires a numeric argument: #{ARGV[i + 1].inspect}" unless ARGV.length > (i + 1) && ARGV[i + 1] =~ /\d/
78 ARGV.delete_at(i).to_i # whoa!
81 if(o = ARGV.find { |x| x =~ /^--/ })
82 $stderr.puts "error: unknown option #{o}"
88 index = Redwood::Index.new
93 sources = ARGV.map do |fn|
94 fn = "mbox://#{fn}" unless fn =~ %r!://!
95 source = index.source_for fn
99 when %r!^mbox\+ssh://!
100 username = h.ask("Username for #{fn}: ");
101 password = h.ask("Password for #{fn}: ") { |q| q.echo = false }
103 Redwood::MBox::SSHLoader.new(fn, username, password, nil, !unusual, !!archive)
105 username = h.ask("Username for #{fn}: ");
106 password = h.ask("Password for #{fn}: ") { |q| q.echo = false }
108 Redwood::IMAP.new(fn, username, password, nil, !unusual, !!archive)
110 Redwood::MBox::Loader.new(fn, nil, !unusual, !!archive)
112 index.add_source source
117 sources = (sources + index.usual_sources).uniq if the_usual
118 if rebuild || force_rebuild
120 sources.each { |s| s.seek_to! start_at }
122 sources.each { |s| s.reset! }
129 sources.each do |source|
131 puts "error loading messages from #{source}: #{source.broken_msg}"
135 puts "loading from #{source}... "
138 source.each do |offset, labels|
139 start_offset ||= offset
140 labels -= [:inbox] if force_archive
141 labels -= [:unread] if force_read
143 m = Redwood::Message.new :source => source, :source_info => offset, :labels => labels
145 puts "skipping duplicate message #{m.id}"
151 m.remove_label :unread if m.status == "RO" unless force_read
152 puts "# message at #{offset}, labels: #{labels * ', '}"
153 if (rebuild || force_rebuild) &&
154 (docid, entry = index.load_entry_for_id(m.id)) && entry
155 if force_rebuild || entry[:source_info].to_i != offset
156 puts "replacing message #{m.id} labels #{entry[:label].inspect} (offset #{entry[:source_info]} => #{offset})"
157 m.labels = entry[:label].split.map { |l| l.intern }
158 num += 1 if index.update_message m, source, offset
161 num += 1 if index.add_message m
163 rescue Redwood::MessageFormatError, Redwood::SourceError => e
164 $stderr.puts "ignoring erroneous message at #{source}##{offset}: #{e.message}"
166 if num % 1000 == 0 && num > 0
167 elapsed = Time.now - start
168 pctdone = (offset.to_f - start_offset) / (source.total.to_f - start_offset)
169 remaining = (source.total.to_f - offset.to_f) * (elapsed.to_f / (offset.to_f - start_offset))
170 puts "## #{num} (#{(pctdone * 100.0)}% done) read; #{elapsed.to_time_s} elapsed; est. #{remaining.to_time_s} remaining"
173 puts "loaded #{num} messages" unless num == 0
176 $stderr.puts "saving index and sources..."
181 if rebuild || force_rebuild
182 puts "deleting missing messages from the index..."
184 sources.each do |source|
185 raise "no source id for #{source}" unless source.id
186 q = "+source_id:#{source.id}"
187 q += " +source_info: >= #{start_at}" if start_at
189 num += index.index.search_each(q, :limit => :all) do |docid, score|
190 mid = index.index[docid][:message_id]
192 puts "deleting #{mid}"
193 index.index.delete docid
198 puts "deleted #{numdel} / #{num} messages"
202 puts "optimizing index..."
203 optt = time { index.index.optimize }
204 puts "optimized index of size #{index.size} in #{optt}s."