sup 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +12 -0
- data/CONTRIBUTORS +84 -0
- data/Gemfile +3 -0
- data/HACKING +42 -0
- data/History.txt +361 -0
- data/LICENSE +280 -0
- data/README.md +70 -0
- data/Rakefile +12 -0
- data/ReleaseNotes +231 -0
- data/bin/sup +434 -0
- data/bin/sup-add +118 -0
- data/bin/sup-config +243 -0
- data/bin/sup-dump +43 -0
- data/bin/sup-import-dump +101 -0
- data/bin/sup-psych-ify-config-files +21 -0
- data/bin/sup-recover-sources +87 -0
- data/bin/sup-sync +210 -0
- data/bin/sup-sync-back-maildir +127 -0
- data/bin/sup-tweak-labels +140 -0
- data/contrib/colorpicker.rb +100 -0
- data/contrib/completion/_sup.zsh +114 -0
- data/devel/console.sh +3 -0
- data/devel/count-loc.sh +3 -0
- data/devel/load-index.rb +9 -0
- data/devel/profile.rb +12 -0
- data/devel/start-console.rb +5 -0
- data/doc/FAQ.txt +119 -0
- data/doc/Hooks.txt +79 -0
- data/doc/Philosophy.txt +69 -0
- data/lib/sup.rb +467 -0
- data/lib/sup/account.rb +90 -0
- data/lib/sup/buffer.rb +768 -0
- data/lib/sup/colormap.rb +239 -0
- data/lib/sup/contact.rb +67 -0
- data/lib/sup/crypto.rb +461 -0
- data/lib/sup/draft.rb +119 -0
- data/lib/sup/hook.rb +159 -0
- data/lib/sup/horizontal_selector.rb +59 -0
- data/lib/sup/idle.rb +42 -0
- data/lib/sup/index.rb +882 -0
- data/lib/sup/interactive_lock.rb +89 -0
- data/lib/sup/keymap.rb +140 -0
- data/lib/sup/label.rb +87 -0
- data/lib/sup/logger.rb +77 -0
- data/lib/sup/logger/singleton.rb +10 -0
- data/lib/sup/maildir.rb +257 -0
- data/lib/sup/mbox.rb +187 -0
- data/lib/sup/message.rb +803 -0
- data/lib/sup/message_chunks.rb +328 -0
- data/lib/sup/mode.rb +140 -0
- data/lib/sup/modes/buffer_list_mode.rb +50 -0
- data/lib/sup/modes/completion_mode.rb +55 -0
- data/lib/sup/modes/compose_mode.rb +38 -0
- data/lib/sup/modes/console_mode.rb +125 -0
- data/lib/sup/modes/contact_list_mode.rb +148 -0
- data/lib/sup/modes/edit_message_async_mode.rb +110 -0
- data/lib/sup/modes/edit_message_mode.rb +728 -0
- data/lib/sup/modes/file_browser_mode.rb +109 -0
- data/lib/sup/modes/forward_mode.rb +82 -0
- data/lib/sup/modes/help_mode.rb +19 -0
- data/lib/sup/modes/inbox_mode.rb +85 -0
- data/lib/sup/modes/label_list_mode.rb +138 -0
- data/lib/sup/modes/label_search_results_mode.rb +38 -0
- data/lib/sup/modes/line_cursor_mode.rb +203 -0
- data/lib/sup/modes/log_mode.rb +57 -0
- data/lib/sup/modes/person_search_results_mode.rb +12 -0
- data/lib/sup/modes/poll_mode.rb +19 -0
- data/lib/sup/modes/reply_mode.rb +228 -0
- data/lib/sup/modes/resume_mode.rb +52 -0
- data/lib/sup/modes/scroll_mode.rb +252 -0
- data/lib/sup/modes/search_list_mode.rb +204 -0
- data/lib/sup/modes/search_results_mode.rb +59 -0
- data/lib/sup/modes/text_mode.rb +76 -0
- data/lib/sup/modes/thread_index_mode.rb +1033 -0
- data/lib/sup/modes/thread_view_mode.rb +941 -0
- data/lib/sup/person.rb +134 -0
- data/lib/sup/poll.rb +272 -0
- data/lib/sup/rfc2047.rb +56 -0
- data/lib/sup/search.rb +110 -0
- data/lib/sup/sent.rb +58 -0
- data/lib/sup/service/label_service.rb +45 -0
- data/lib/sup/source.rb +244 -0
- data/lib/sup/tagger.rb +50 -0
- data/lib/sup/textfield.rb +253 -0
- data/lib/sup/thread.rb +452 -0
- data/lib/sup/time.rb +93 -0
- data/lib/sup/undo.rb +38 -0
- data/lib/sup/update.rb +30 -0
- data/lib/sup/util.rb +747 -0
- data/lib/sup/util/ncurses.rb +274 -0
- data/lib/sup/util/path.rb +9 -0
- data/lib/sup/util/query.rb +17 -0
- data/lib/sup/util/uri.rb +15 -0
- data/lib/sup/version.rb +3 -0
- data/sup.gemspec +53 -0
- data/test/dummy_source.rb +61 -0
- data/test/gnupg_test_home/gpg.conf +1 -0
- data/test/gnupg_test_home/pubring.gpg +0 -0
- data/test/gnupg_test_home/receiver_pubring.gpg +0 -0
- data/test/gnupg_test_home/receiver_secring.gpg +0 -0
- data/test/gnupg_test_home/receiver_trustdb.gpg +0 -0
- data/test/gnupg_test_home/secring.gpg +0 -0
- data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -0
- data/test/gnupg_test_home/trustdb.gpg +0 -0
- data/test/integration/test_label_service.rb +18 -0
- data/test/messages/bad-content-transfer-encoding-1.eml +8 -0
- data/test/messages/binary-content-transfer-encoding-2.eml +21 -0
- data/test/messages/missing-line.eml +9 -0
- data/test/test_crypto.rb +109 -0
- data/test/test_header_parsing.rb +168 -0
- data/test/test_helper.rb +7 -0
- data/test/test_message.rb +532 -0
- data/test/test_messages_dir.rb +147 -0
- data/test/test_yaml_migration.rb +85 -0
- data/test/test_yaml_regressions.rb +17 -0
- data/test/unit/service/test_label_service.rb +19 -0
- data/test/unit/test_horizontal_selector.rb +40 -0
- data/test/unit/util/test_query.rb +46 -0
- data/test/unit/util/test_string.rb +57 -0
- data/test/unit/util/test_uri.rb +19 -0
- metadata +423 -0
data/bin/sup-config
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'highline/import'
|
7
|
+
require 'trollop'
|
8
|
+
require "sup"
|
9
|
+
|
10
|
+
$opts = Trollop::options do
|
11
|
+
version "sup-config (sup #{Redwood::VERSION})"
|
12
|
+
banner <<EOS
|
13
|
+
Interactive configuration tool for Sup. Won't destroy existing
|
14
|
+
configuration.
|
15
|
+
|
16
|
+
Usage:
|
17
|
+
sup-config
|
18
|
+
|
19
|
+
No options.
|
20
|
+
EOS
|
21
|
+
end
|
22
|
+
|
23
|
+
def axe q, default=nil
|
24
|
+
question = if default && !default.empty?
|
25
|
+
"#{q} (enter for \"#{default}\"): "
|
26
|
+
else
|
27
|
+
"#{q}: "
|
28
|
+
end
|
29
|
+
ans = ask question
|
30
|
+
ans.empty? ? default : ans.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def axe_yes q, default="n"
|
34
|
+
axe(q, default) =~ /^y|yes$/i
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_cmd cmd
|
38
|
+
(ENV["RUBY_INVOCATION"] ? ENV["RUBY_INVOCATION"] + " " : "") + File.join(File.dirname($0), cmd)
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_source
|
42
|
+
require "sup/util/uri"
|
43
|
+
|
44
|
+
type = nil
|
45
|
+
|
46
|
+
say "Ok, adding a new source."
|
47
|
+
choose do |menu|
|
48
|
+
menu.prompt = "What type of mail source is it? "
|
49
|
+
menu.choice("mbox file") { type = :mbox }
|
50
|
+
menu.choice("maildir directory") { type = :maildir }
|
51
|
+
menu.choice("Get me out of here!") { return }
|
52
|
+
end
|
53
|
+
|
54
|
+
while true do
|
55
|
+
say "Ok, now for the details."
|
56
|
+
|
57
|
+
default_labels, components = case type
|
58
|
+
when :mbox
|
59
|
+
$last_fn ||= ENV["MAIL"]
|
60
|
+
fn = axe "What's the full path to the mbox file?", $last_fn
|
61
|
+
return if fn.nil? || fn.empty?
|
62
|
+
|
63
|
+
$last_fn = fn
|
64
|
+
[Redwood::MBox.suggest_labels_for(fn),
|
65
|
+
{ :scheme => "mbox", :path => fn }]
|
66
|
+
when :maildir
|
67
|
+
$last_fn ||= ENV["MAIL"]
|
68
|
+
fn = axe "What's the full path to the maildir directory?", $last_fn
|
69
|
+
return if fn.nil? || fn.empty?
|
70
|
+
|
71
|
+
$last_fn = fn
|
72
|
+
[Redwood::Maildir.suggest_labels_for(fn),
|
73
|
+
{ :scheme => "maildir", :path => fn }]
|
74
|
+
end
|
75
|
+
|
76
|
+
uri = begin
|
77
|
+
Redwood::Util::Uri.build components
|
78
|
+
rescue URI::Error => e
|
79
|
+
say "Whoopsie! I couldn't build a URI from that: #{e.message}"
|
80
|
+
if axe_yes("Try again?") then next else return end
|
81
|
+
end
|
82
|
+
|
83
|
+
say "I'm going to add this source: #{uri}"
|
84
|
+
unless axe("Does that look right?", "y") =~ /^y|yes$/i
|
85
|
+
if axe_yes("Try again?") then next else return end
|
86
|
+
end
|
87
|
+
|
88
|
+
usual = axe_yes "Does this source ever receive new messages?", "y"
|
89
|
+
archive = usual ? axe_yes("Should new messages be automatically archived? (I.e. not appear in your inbox, though still be accessible via search.)") : false
|
90
|
+
|
91
|
+
sync_back = (type == :maildir) ? axe_yes("Should the original Maildir messages be modified to reflect changes like read status, starred messages, etc.?", "y") : false
|
92
|
+
|
93
|
+
labels_str = axe("Enter any labels to be automatically added to all messages from this source, separated by spaces (or 'none')", default_labels.join(","))
|
94
|
+
|
95
|
+
labels = if labels_str =~ /^\s*none\s*$/i
|
96
|
+
nil
|
97
|
+
else
|
98
|
+
labels_str.split(/\s+/)
|
99
|
+
end
|
100
|
+
|
101
|
+
cmd = build_cmd "sup-add"
|
102
|
+
cmd += " --unusual" unless usual
|
103
|
+
cmd += " --archive" if archive
|
104
|
+
cmd += " --no-sync-back" unless sync_back
|
105
|
+
cmd += " --labels=#{labels.join(',')}" if labels && !labels.empty?
|
106
|
+
cmd += " #{uri}"
|
107
|
+
|
108
|
+
puts "Ok, trying to run \"#{cmd}\"..."
|
109
|
+
|
110
|
+
system cmd
|
111
|
+
if $?.success?
|
112
|
+
say "Great! Added!"
|
113
|
+
break
|
114
|
+
else
|
115
|
+
say "Rats, that failed. You may have to do it manually."
|
116
|
+
if axe_yes("Try again?") then next else return end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
$terminal.wrap_at = :auto
|
122
|
+
Redwood::start
|
123
|
+
index = Redwood::Index.init
|
124
|
+
Redwood::SourceManager.load_sources
|
125
|
+
|
126
|
+
say <<EOS
|
127
|
+
Howdy neighbor! This here's sup-config, ready to help you jack in to
|
128
|
+
the next generation of digital cyberspace: the text-based email
|
129
|
+
program. Get ready to be the envy of everyone in your internets
|
130
|
+
with your amazing keyboarding skills! Jump from email to email with
|
131
|
+
nary a click of the mouse!
|
132
|
+
|
133
|
+
Just answer these simple questions and you'll be on your way.
|
134
|
+
|
135
|
+
EOS
|
136
|
+
|
137
|
+
account = $config[:accounts][:default]
|
138
|
+
|
139
|
+
name = axe "What's your name?", account[:name]
|
140
|
+
email = axe "What's your (primary) email address?", account[:email]
|
141
|
+
|
142
|
+
say "Ok, your from header will look like this:"
|
143
|
+
say " From: #{name} <#{email}>"
|
144
|
+
|
145
|
+
say "\nDo you have any alternate email addresses that also receive email?"
|
146
|
+
say "If so, enter them now, separated by spaces."
|
147
|
+
alts = axe("Alternate email addresses", account[:alternates].join(" ")).split(/\s+/)
|
148
|
+
|
149
|
+
sigfn = axe "What file contains your signature?", account[:signature]
|
150
|
+
editor = axe "What editor would you like to use?", $config[:editor]
|
151
|
+
|
152
|
+
time_mode = axe "Would like to display time in 12h (type 12h) or in 24h (type 24h)?", $config[:time_mode]
|
153
|
+
|
154
|
+
$config[:accounts][:default][:name] = name
|
155
|
+
$config[:accounts][:default][:email] = email
|
156
|
+
$config[:accounts][:default][:alternates] = alts
|
157
|
+
$config[:accounts][:default][:signature] = sigfn
|
158
|
+
$config[:editor] = editor
|
159
|
+
$config[:time_mode] = time_mode
|
160
|
+
|
161
|
+
done = false
|
162
|
+
until done
|
163
|
+
say "\nNow, we'll tell Sup where to find all your email."
|
164
|
+
Redwood::SourceManager.load_sources
|
165
|
+
say "Current sources:"
|
166
|
+
if Redwood::SourceManager.sources.empty?
|
167
|
+
say " No sources!"
|
168
|
+
else
|
169
|
+
Redwood::SourceManager.sources.each { |s| puts "* #{s}" }
|
170
|
+
end
|
171
|
+
|
172
|
+
say "\n"
|
173
|
+
choose do |menu|
|
174
|
+
menu.prompt = "Your wish? "
|
175
|
+
menu.choice("Add a new source.") { add_source }
|
176
|
+
menu.choice("Done adding sources!") { done = true }
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
say "\nSup needs to know where to store your sent messages."
|
181
|
+
say "Only sources capable of storing mail will be listed.\n\n"
|
182
|
+
|
183
|
+
Redwood::SourceManager.load_sources
|
184
|
+
if Redwood::SourceManager.sources.empty?
|
185
|
+
say "\nUsing the default sup://sent, since you haven't configured other sources yet."
|
186
|
+
$config[:sent_source] = 'sup://sent'
|
187
|
+
else
|
188
|
+
# this handles the event that source.yaml already contains the SentLoader
|
189
|
+
# source.
|
190
|
+
have_sup_sent = false
|
191
|
+
|
192
|
+
choose do |menu|
|
193
|
+
menu.prompt = "Store my sent mail in? "
|
194
|
+
|
195
|
+
menu.choice('Default (an mbox in ~/.sup, aka sup://sent)') { $config[:sent_source] = 'sup://sent'} unless have_sup_sent
|
196
|
+
|
197
|
+
valid_sents = Redwood::SourceManager.sources.each do |s|
|
198
|
+
have_sup_sent = true if s.to_s.eql?('sup://sent')
|
199
|
+
menu.choice(s.to_s) { $config[:sent_source] = s.to_s } if s.respond_to? :store_message
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
Redwood::save_yaml_obj $config, Redwood::CONFIG_FN, false, true
|
205
|
+
|
206
|
+
say "Ok, I've saved you up a nice lil' #{Redwood::CONFIG_FN}."
|
207
|
+
|
208
|
+
say <<EOS
|
209
|
+
|
210
|
+
The final step is to import all your messages into the Sup index.
|
211
|
+
Depending on how many messages are in the sources, this could take
|
212
|
+
quite a while.
|
213
|
+
|
214
|
+
EOS
|
215
|
+
|
216
|
+
if axe_yes "Run sup-sync to import all messages now?"
|
217
|
+
while true
|
218
|
+
cmd = build_cmd("sup-sync") + " --all-sources"
|
219
|
+
puts "Ok, trying to run \"#{cmd}\"..."
|
220
|
+
system cmd
|
221
|
+
if $?.success?
|
222
|
+
say "Great! It worked!"
|
223
|
+
break
|
224
|
+
else
|
225
|
+
say "Rats, that failed. You may have to do it manually."
|
226
|
+
if axe_yes("Try again?") then next else break end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
index.load
|
232
|
+
|
233
|
+
say <<EOS
|
234
|
+
|
235
|
+
Okee doke, you've got yourself an index of #{index.size} messages. Looks
|
236
|
+
like you're ready to jack in to cyberspace there, cowboy.
|
237
|
+
|
238
|
+
Just one last command:
|
239
|
+
|
240
|
+
#{build_cmd "sup"}
|
241
|
+
|
242
|
+
Have fun!
|
243
|
+
EOS
|
data/bin/sup-dump
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'xapian'
|
7
|
+
require 'trollop'
|
8
|
+
require 'set'
|
9
|
+
|
10
|
+
BASE_DIR = ENV["SUP_BASE"] || File.join(ENV["HOME"], ".sup")
|
11
|
+
|
12
|
+
$opts = Trollop::options do
|
13
|
+
version "sup-dump"
|
14
|
+
banner <<EOS
|
15
|
+
Dumps all message state from the sup index to standard out. You can
|
16
|
+
later use sup-sync --restored --restore <filename> to recover the index.
|
17
|
+
|
18
|
+
This tool is primarily useful in the event that a Sup upgrade breaks index
|
19
|
+
format compatibility.
|
20
|
+
|
21
|
+
Usage:
|
22
|
+
sup-dump > <filename>
|
23
|
+
sup-dump | bzip2 > <filename> # even better
|
24
|
+
EOS
|
25
|
+
end
|
26
|
+
|
27
|
+
xapian = Xapian::Database.new File.join(BASE_DIR, 'xapian')
|
28
|
+
version = xapian.get_metadata 'rescue-version'
|
29
|
+
version = '0' if version.empty?
|
30
|
+
|
31
|
+
case version
|
32
|
+
when '0'
|
33
|
+
xapian.postlist('Kmail').each do |x|
|
34
|
+
begin
|
35
|
+
entry = Marshal.load(xapian.document(x.docid).data)
|
36
|
+
puts "#{entry[:message_id]} (#{entry[:labels].sort_by { |l| l.to_s } * ' '})"
|
37
|
+
rescue
|
38
|
+
$stderr.puts "failed to dump document #{x.docid}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
else
|
42
|
+
abort "this sup-dump version doesn't understand your index"
|
43
|
+
end
|
data/bin/sup-import-dump
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'uri'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'trollop'
|
8
|
+
require "sup"
|
9
|
+
|
10
|
+
PROGRESS_UPDATE_INTERVAL = 15 # seconds
|
11
|
+
|
12
|
+
class AbortExecution < SystemExit
|
13
|
+
end
|
14
|
+
|
15
|
+
opts = Trollop::options do
|
16
|
+
version "sup-import-dump (sup #{Redwood::VERSION})"
|
17
|
+
banner <<EOS
|
18
|
+
Imports message state previously exported by sup-dump into the index.
|
19
|
+
sup-import-dump operates on the index only, so the messages must have already
|
20
|
+
been added using sup-sync. If you need to recreate the index, see sup-sync
|
21
|
+
--restore <filename> instead.
|
22
|
+
|
23
|
+
Messages not mentioned in the dump file will not be modified.
|
24
|
+
|
25
|
+
Usage:
|
26
|
+
sup-import-dump [options] <dump file>
|
27
|
+
|
28
|
+
Options:
|
29
|
+
EOS
|
30
|
+
opt :verbose, "Print message ids as they're processed."
|
31
|
+
opt :ignore_missing, "Silently skip over messages that are not in the index."
|
32
|
+
opt :warn_missing, "Warn about messages that are not in the index, but continue."
|
33
|
+
opt :abort_missing, "Abort on encountering messages that are not in the index. (default)"
|
34
|
+
opt :atomic, "Use transaction to apply all changes atomically."
|
35
|
+
opt :dry_run, "Don't actually modify the index. Probably only useful with --verbose.", :short => "-n"
|
36
|
+
opt :version, "Show version information", :short => :none
|
37
|
+
|
38
|
+
conflicts :ignore_missing, :warn_missing, :abort_missing
|
39
|
+
end
|
40
|
+
Trollop::die "No dump file given" if ARGV.empty?
|
41
|
+
Trollop::die "Extra arguments given" if ARGV.length > 1
|
42
|
+
dump_name = ARGV.shift
|
43
|
+
missing_action = [:ignore_missing, :warn_missing, :abort_missing].find { |x| opts[x] } || :abort_missing
|
44
|
+
|
45
|
+
Redwood::start
|
46
|
+
index = Redwood::Index.init
|
47
|
+
|
48
|
+
index.lock_interactively or exit
|
49
|
+
begin
|
50
|
+
num_read = 0
|
51
|
+
num_changed = 0
|
52
|
+
index.load
|
53
|
+
index.begin_transaction if opts[:atomic]
|
54
|
+
|
55
|
+
IO.foreach dump_name do |l|
|
56
|
+
l =~ /^(\S+) \((.*?)\)$/ or raise "Can't read dump line: #{l.inspect}"
|
57
|
+
mid, labels = $1, $2
|
58
|
+
num_read += 1
|
59
|
+
|
60
|
+
unless index.contains_id? mid
|
61
|
+
if missing_action == :abort_missing
|
62
|
+
$stderr.puts "Message #{mid} not found in index, aborting."
|
63
|
+
raise AbortExecution, 10
|
64
|
+
elsif missing_action == :warn_missing
|
65
|
+
$stderr.puts "Message #{mid} not found in index, skipping."
|
66
|
+
end
|
67
|
+
|
68
|
+
next
|
69
|
+
end
|
70
|
+
|
71
|
+
m = index.build_message mid
|
72
|
+
new_labels = labels.to_set_of_symbols
|
73
|
+
|
74
|
+
if m.labels == new_labels
|
75
|
+
puts "#{mid} unchanged" if opts[:verbose]
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
79
|
+
puts "Changing flags for #{mid} from '#{m.labels.to_a * ' '}' to '#{new_labels.to_a * ' '}'" if opts[:verbose]
|
80
|
+
num_changed += 1
|
81
|
+
|
82
|
+
next if opts[:dry_run]
|
83
|
+
|
84
|
+
m.labels = new_labels
|
85
|
+
index.update_message_state m
|
86
|
+
end
|
87
|
+
|
88
|
+
index.commit_transaction if opts[:atomic]
|
89
|
+
puts "Updated #{num_changed} of #{num_read} messages."
|
90
|
+
rescue AbortExecution
|
91
|
+
index.cancel_transaction if opts[:atomic]
|
92
|
+
raise
|
93
|
+
rescue Exception => e
|
94
|
+
index.cancel_transaction if opts[:atomic]
|
95
|
+
File.open("sup-exception-log.txt", "w") { |f| f.puts e.backtrace }
|
96
|
+
raise
|
97
|
+
ensure
|
98
|
+
index.save_index unless opts[:atomic]
|
99
|
+
Redwood::finish
|
100
|
+
index.unlock
|
101
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require "sup"
|
6
|
+
require "fileutils"
|
7
|
+
|
8
|
+
if RUBY_VERSION >= "2.1"
|
9
|
+
puts "YAML migration is deprecated by Ruby 2.1 and newer."
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
Redwood.start
|
14
|
+
|
15
|
+
fn = Redwood::SOURCE_FN
|
16
|
+
FileUtils.cp fn, "#{fn}.syck_bak"
|
17
|
+
|
18
|
+
Redwood::SourceManager.load_sources fn
|
19
|
+
Redwood::SourceManager.save_sources fn, true
|
20
|
+
|
21
|
+
Redwood.finish
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
$opts = {
|
8
|
+
:unusual => false,
|
9
|
+
:archive => false,
|
10
|
+
:scan_num => 10,
|
11
|
+
}
|
12
|
+
|
13
|
+
|
14
|
+
OPTIONPARSERSUCKS = "\n" + " " * 38
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = <<EOS
|
17
|
+
Usage: sup-recover-sources [options] <source>+
|
18
|
+
|
19
|
+
Rebuilds a lost sources.yaml file by reading messages from a list of
|
20
|
+
sources and determining, for each source, the most prevalent
|
21
|
+
'source_id' field of messages from that source in the index.
|
22
|
+
|
23
|
+
The only non-deterministic component to this is that if the same
|
24
|
+
message appears in multiple sources, those sources may be
|
25
|
+
mis-diagnosed by this program.
|
26
|
+
|
27
|
+
If the first N messages (--scan-num below) all have the same source_id
|
28
|
+
in the index, the source will be added to sources.yaml. Otherwise, the
|
29
|
+
distribution will be printed, and you will have to add it by hand.
|
30
|
+
|
31
|
+
The offset pointer into the sources will be set to the end of the source,
|
32
|
+
so you will have to run sup-import --rebuild for each new source after
|
33
|
+
doing this.
|
34
|
+
|
35
|
+
Options include:
|
36
|
+
EOS
|
37
|
+
|
38
|
+
opts.on("--unusual", "Mark sources as 'unusual'. Only usual#{OPTIONPARSERSUCKS}sources will be polled by hand. Default:#{OPTIONPARSERSUCKS}#{$opts[:unusual]}.") { $opts[:unusual] = true }
|
39
|
+
|
40
|
+
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 }
|
41
|
+
|
42
|
+
opts.on("--scan-num N", Integer, "Number of messages to scan per source.#{OPTIONPARSERSUCKS}Default: #{$opts[:scan_num]}.") do |n|
|
43
|
+
$opts[:scan_num] = n
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
47
|
+
puts opts
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
end.parse(ARGV)
|
51
|
+
|
52
|
+
require "sup"
|
53
|
+
Redwood::start
|
54
|
+
puts "loading index..."
|
55
|
+
index = Redwood::Index.init
|
56
|
+
index.load
|
57
|
+
puts "loaded index of #{index.size} messages"
|
58
|
+
|
59
|
+
ARGV.each do |fn|
|
60
|
+
next if Redwood::SourceManager.source_for fn
|
61
|
+
|
62
|
+
## TODO: merge this code with the same snippet in import
|
63
|
+
source = Redwood::MBox.new(fn, nil, !$opts[:unusual], $opts[:archive])
|
64
|
+
|
65
|
+
source_ids = Hash.new 0
|
66
|
+
count = 0
|
67
|
+
source.each do |offset, labels|
|
68
|
+
m = Redwood::Message.new :source => source, :source_info => offset
|
69
|
+
m.load_from_source!
|
70
|
+
source_id = Redwood::SourceManager.source_for_id m.id
|
71
|
+
next unless source_id
|
72
|
+
source_ids[source_id] += 1
|
73
|
+
count += 1
|
74
|
+
break if count == $opts[:scan_num]
|
75
|
+
end
|
76
|
+
|
77
|
+
if source_ids.size == 1
|
78
|
+
id = source_ids.keys.first.to_i
|
79
|
+
puts "assigned #{source} to #{source_ids.keys.first}"
|
80
|
+
source.id = id
|
81
|
+
Redwood::SourceManager.add_source source
|
82
|
+
else
|
83
|
+
puts ">> unable to determine #{source}: #{source_ids.inspect}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
index.save
|