sup 0.20.0 → 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +2 -1
- data/.travis.yml +11 -6
- data/CONTRIBUTORS +27 -15
- data/Gemfile +2 -1
- data/History.txt +84 -0
- data/README.md +26 -5
- data/Rakefile +0 -1
- data/ReleaseNotes +7 -0
- data/bin/sup +17 -30
- data/bin/sup-add +15 -16
- data/bin/sup-config +30 -45
- data/bin/sup-dump +2 -3
- data/bin/sup-import-dump +5 -6
- data/bin/sup-sync +3 -4
- data/bin/sup-sync-back-maildir +3 -4
- data/bin/sup-tweak-labels +6 -7
- data/contrib/colorpicker.rb +0 -2
- data/contrib/completion/_sup.bash +102 -0
- data/devel/profile.rb +0 -1
- data/ext/mkrf_conf_xapian.rb +1 -1
- data/lib/sup.rb +8 -8
- data/lib/sup/colormap.rb +5 -2
- data/lib/sup/contact.rb +4 -2
- data/lib/sup/crypto.rb +58 -16
- data/lib/sup/draft.rb +8 -8
- data/lib/sup/hook.rb +9 -9
- data/lib/sup/index.rb +20 -7
- data/lib/sup/label.rb +1 -1
- data/lib/sup/logger.rb +1 -1
- data/lib/sup/maildir.rb +2 -2
- data/lib/sup/mbox.rb +2 -2
- data/lib/sup/message.rb +26 -10
- data/lib/sup/message_chunks.rb +7 -4
- data/lib/sup/mode.rb +34 -28
- data/lib/sup/modes/contact_list_mode.rb +1 -0
- data/lib/sup/modes/edit_message_mode.rb +1 -1
- data/lib/sup/modes/forward_mode.rb +22 -3
- data/lib/sup/modes/line_cursor_mode.rb +1 -1
- data/lib/sup/modes/reply_mode.rb +3 -1
- data/lib/sup/modes/text_mode.rb +6 -1
- data/lib/sup/modes/thread_index_mode.rb +6 -2
- data/lib/sup/modes/thread_view_mode.rb +63 -18
- data/lib/sup/person.rb +68 -61
- data/lib/sup/search.rb +1 -1
- data/lib/sup/sent.rb +1 -1
- data/lib/sup/source.rb +1 -1
- data/lib/sup/util.rb +15 -94
- data/lib/sup/util/axe.rb +17 -0
- data/lib/sup/util/locale_fiddler.rb +24 -0
- data/lib/sup/util/ncurses.rb +3 -3
- data/lib/sup/version.rb +10 -1
- data/sup.gemspec +12 -10
- data/test/{messages → fixtures}/bad-content-transfer-encoding-1.eml +0 -0
- data/test/{messages → fixtures}/binary-content-transfer-encoding-2.eml +0 -0
- data/test/fixtures/blank-header-fields.eml +71 -0
- data/test/fixtures/contacts.txt +1 -0
- data/test/fixtures/mailing-list-header.eml +80 -0
- data/test/fixtures/malicious-attachment-names.eml +55 -0
- data/test/fixtures/missing-from-to.eml +18 -0
- data/test/{messages → fixtures}/missing-line.eml +0 -0
- data/test/fixtures/multi-part-2.eml +72 -0
- data/test/fixtures/multi-part.eml +61 -0
- data/test/fixtures/no-body.eml +18 -0
- data/test/fixtures/simple-message.eml +29 -0
- data/test/fixtures/text-attachments-with-charset.eml +46 -0
- data/test/fixtures/zimbra-quote-with-bottom-post.eml +27 -0
- data/test/gnupg_test_home/gpg.conf +2 -1
- data/test/gnupg_test_home/private-keys-v1.d/306D2EE90FF0014B5B9FD07E265C751791674140.key +0 -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/regen_keys.sh +70 -16
- data/test/gnupg_test_home/secring.gpg +0 -0
- data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -22
- data/test/integration/test_maildir.rb +1 -1
- data/test/integration/test_mbox.rb +1 -1
- data/test/test_crypto.rb +14 -2
- data/test/test_header_parsing.rb +1 -1
- data/test/test_helper.rb +6 -3
- data/test/test_message.rb +115 -341
- data/test/test_messages_dir.rb +4 -28
- data/test/test_yaml_regressions.rb +1 -1
- data/test/unit/test_contact.rb +33 -0
- data/test/unit/test_locale_fiddler.rb +15 -0
- data/test/unit/test_person.rb +37 -0
- data/test/unit/util/test_query.rb +10 -4
- data/test/unit/util/test_string.rb +6 -0
- metadata +107 -43
- data/test/gnupg_test_home/key1.gen +0 -15
- data/test/gnupg_test_home/key2.gen +0 -15
data/devel/profile.rb
CHANGED
data/ext/mkrf_conf_xapian.rb
CHANGED
data/lib/sup.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'rubygems'
|
4
3
|
require 'yaml'
|
5
4
|
require 'zlib'
|
6
5
|
require 'thread'
|
@@ -105,7 +104,7 @@ module Redwood
|
|
105
104
|
o
|
106
105
|
end
|
107
106
|
|
108
|
-
mode = if File.
|
107
|
+
mode = if File.exist? fn
|
109
108
|
File.stat(fn).mode
|
110
109
|
else
|
111
110
|
0600
|
@@ -113,7 +112,7 @@ module Redwood
|
|
113
112
|
|
114
113
|
if backup
|
115
114
|
backup_fn = fn + '.bak'
|
116
|
-
if File.
|
115
|
+
if File.exist?(fn) && File.size(fn) > 0
|
117
116
|
File.open(backup_fn, "w", mode) do |f|
|
118
117
|
File.open(fn, "r") { |old_f| FileUtils.copy_stream old_f, f }
|
119
118
|
f.fsync
|
@@ -139,7 +138,7 @@ module Redwood
|
|
139
138
|
end
|
140
139
|
|
141
140
|
def load_yaml_obj fn, compress=false
|
142
|
-
o = if File.
|
141
|
+
o = if File.exist? fn
|
143
142
|
if compress
|
144
143
|
Zlib::GzipReader.open(fn) { |f| YAML::load f }
|
145
144
|
else
|
@@ -180,7 +179,7 @@ module Redwood
|
|
180
179
|
return if bypass_sync_check
|
181
180
|
|
182
181
|
if $config[:sync_back_to_maildir]
|
183
|
-
if not File.
|
182
|
+
if not File.exist? Redwood::SYNC_OK_FN
|
184
183
|
Redwood.warn_syncback <<EOS
|
185
184
|
It appears that the "sync_back_to_maildir" option has been changed
|
186
185
|
from false to true since the last execution of sup.
|
@@ -191,14 +190,14 @@ Should I complain about this again? (Y/n)
|
|
191
190
|
EOS
|
192
191
|
File.open(Redwood::SYNC_OK_FN, 'w') {|f| f.write(Redwood::MAILDIR_SYNC_CHECK_SKIPPED) } if STDIN.gets.chomp.downcase == 'n'
|
193
192
|
end
|
194
|
-
elsif not $config[:sync_back_to_maildir] and File.
|
193
|
+
elsif not $config[:sync_back_to_maildir] and File.exist? Redwood::SYNC_OK_FN
|
195
194
|
File.delete(Redwood::SYNC_OK_FN)
|
196
195
|
end
|
197
196
|
end
|
198
197
|
|
199
198
|
def check_syncback_settings
|
200
199
|
# don't check if syncback was never performed
|
201
|
-
return unless File.
|
200
|
+
return unless File.exist? Redwood::SYNC_OK_FN
|
202
201
|
active_sync_sources = File.readlines(Redwood::SYNC_OK_FN).collect { |e| e.strip }.find_all { |e| not e.empty? }
|
203
202
|
return if active_sync_sources.length == 1 and active_sync_sources[0] == Redwood::MAILDIR_SYNC_CHECK_SKIPPED
|
204
203
|
sources = SourceManager.sources
|
@@ -332,13 +331,14 @@ EOM
|
|
332
331
|
:poll_interval => 300,
|
333
332
|
:wrap_width => 0,
|
334
333
|
:slip_rows => 0,
|
334
|
+
:indent_spaces => 2,
|
335
335
|
:col_jump => 2,
|
336
336
|
:stem_language => "english",
|
337
337
|
:sync_back_to_maildir => false,
|
338
338
|
:continuous_scroll => false,
|
339
339
|
:always_edit_async => false,
|
340
340
|
}
|
341
|
-
if File.
|
341
|
+
if File.exist? filename
|
342
342
|
config = Redwood::load_yaml_obj filename
|
343
343
|
abort "#{filename} is not a valid configuration file (it's a #{config.class}, not a hash)" unless config.is_a?(Hash)
|
344
344
|
default_config.merge config
|
data/lib/sup/colormap.rb
CHANGED
@@ -17,6 +17,9 @@ module Ncurses
|
|
17
17
|
|
18
18
|
## xterm 24-shade grayscale
|
19
19
|
24.times { |x| color! "g#{x}", (16+6*6*6) + x }
|
20
|
+
elsif Ncurses::NUM_COLORS == -1
|
21
|
+
## Terminal emulator doesn't appear to support colors
|
22
|
+
fail "sup must be run in a terminal with color support, please check your TERM variable."
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
@@ -186,13 +189,13 @@ class Colormap
|
|
186
189
|
## Try to use the user defined colors, in case of an error fall back
|
187
190
|
## to the default ones.
|
188
191
|
def populate_colormap
|
189
|
-
user_colors = if File.
|
192
|
+
user_colors = if File.exist? Redwood::COLOR_FN
|
190
193
|
debug "loading user colors from #{Redwood::COLOR_FN}"
|
191
194
|
Redwood::load_yaml_obj Redwood::COLOR_FN
|
192
195
|
end
|
193
196
|
|
194
197
|
## Set attachment sybmol to sane default for existing colorschemes
|
195
|
-
if user_colors and user_colors.has_key? :to_me
|
198
|
+
if user_colors and user_colors.has_key? :to_me
|
196
199
|
user_colors[:with_attachment] = user_colors[:to_me] unless user_colors.has_key? :with_attachment
|
197
200
|
end
|
198
201
|
|
data/lib/sup/contact.rb
CHANGED
@@ -16,7 +16,7 @@ class ContactManager
|
|
16
16
|
@a2p = {} # alias to person
|
17
17
|
@e2p = {} # email to person
|
18
18
|
|
19
|
-
if File.
|
19
|
+
if File.exist? fn
|
20
20
|
IO.foreach(fn) do |l|
|
21
21
|
l =~ /^([^:]*): (.*)$/ or raise "can't parse #{fn} line #{l.inspect}"
|
22
22
|
aalias, addr = $1, $2
|
@@ -29,11 +29,13 @@ class ContactManager
|
|
29
29
|
def contacts_with_aliases; @a2p.values.uniq end
|
30
30
|
|
31
31
|
def update_alias person, aalias=nil
|
32
|
+
## Deleting old data if it exists
|
32
33
|
old_aalias = @p2a[person]
|
33
|
-
if
|
34
|
+
if old_aalias
|
34
35
|
@a2p.delete old_aalias
|
35
36
|
@e2p.delete person.email
|
36
37
|
end
|
38
|
+
## Update with new data
|
37
39
|
@p2a[person] = aalias
|
38
40
|
unless aalias.nil? || aalias.empty?
|
39
41
|
@a2p[aalias] = person
|
data/lib/sup/crypto.rb
CHANGED
@@ -10,11 +10,14 @@ class CryptoManager
|
|
10
10
|
|
11
11
|
class Error < StandardError; end
|
12
12
|
|
13
|
-
OUTGOING_MESSAGE_OPERATIONS =
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
OUTGOING_MESSAGE_OPERATIONS = {
|
14
|
+
sign: "Sign",
|
15
|
+
sign_and_encrypt: "Sign and encrypt",
|
16
|
+
encrypt: "Encrypt only"
|
17
|
+
}
|
18
|
+
|
19
|
+
KEY_PATTERN = /(-----BEGIN PGP PUBLIC KEY BLOCK.*-----END PGP PUBLIC KEY BLOCK)/m
|
20
|
+
KEYSERVER_URL = "http://pool.sks-keyservers.net:11371/pks/lookup"
|
18
21
|
|
19
22
|
HookManager.register "gpg-options", <<EOS
|
20
23
|
Runs before gpg is called, allowing you to modify the options (most
|
@@ -124,18 +127,27 @@ EOS
|
|
124
127
|
def sign from, to, payload
|
125
128
|
return unknown_status(@not_working_reason) unless @not_working_reason.nil?
|
126
129
|
|
130
|
+
# We grab this from the GPG::Ctx below after signing, so that we can set
|
131
|
+
# micalg in Content-Type to match the hash algorithm GPG decided to use.
|
132
|
+
hash_algo = nil
|
133
|
+
|
127
134
|
gpg_opts = {:protocol => GPGME::PROTOCOL_OpenPGP, :armor => true, :textmode => true}
|
128
135
|
gpg_opts.merge!(gen_sign_user_opts(from))
|
129
136
|
gpg_opts = HookManager.run("gpg-options",
|
130
137
|
{:operation => "sign", :options => gpg_opts}) || gpg_opts
|
131
138
|
begin
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
139
|
+
input = GPGME::Data.new(format_payload(payload))
|
140
|
+
output = GPGME::Data.new()
|
141
|
+
GPGME::Ctx.new(gpg_opts) do |ctx|
|
142
|
+
if gpg_opts[:signer]
|
143
|
+
signers = GPGME::Key.find(:secret, gpg_opts[:signer], :sign)
|
144
|
+
ctx.add_signer(*signers)
|
145
|
+
end
|
146
|
+
ctx.sign(input, output, GPGME::SIG_MODE_DETACH)
|
147
|
+
hash_algo = GPGME::hash_algo_name(ctx.sign_result.signatures[0].hash_algo)
|
138
148
|
end
|
149
|
+
output.seek(0)
|
150
|
+
sig = output.read
|
139
151
|
rescue GPGME::Error => exc
|
140
152
|
raise Error, gpgme_exc_msg(exc.message)
|
141
153
|
end
|
@@ -147,7 +159,7 @@ EOS
|
|
147
159
|
end
|
148
160
|
|
149
161
|
envelope = RMail::Message.new
|
150
|
-
envelope.header["Content-Type"] =
|
162
|
+
envelope.header["Content-Type"] = "multipart/signed; protocol=application/pgp-signature; micalg=pgp-#{hash_algo.downcase}"
|
151
163
|
|
152
164
|
envelope.add_part payload
|
153
165
|
signature = RMail::Message.make_attachment sig, "application/pgp-signature", nil, "signature.asc"
|
@@ -212,9 +224,10 @@ EOS
|
|
212
224
|
unknown = false
|
213
225
|
all_output_lines = []
|
214
226
|
all_trusted = true
|
227
|
+
unknown_fingerprint = nil
|
215
228
|
|
216
229
|
verify_result.signatures.each do |signature|
|
217
|
-
output_lines, trusted = sig_output_lines signature
|
230
|
+
output_lines, trusted, unknown_fingerprint = sig_output_lines signature
|
218
231
|
all_output_lines << output_lines
|
219
232
|
all_output_lines.flatten!
|
220
233
|
all_trusted &&= trusted
|
@@ -229,7 +242,7 @@ EOS
|
|
229
242
|
end
|
230
243
|
|
231
244
|
if valid || !unknown
|
232
|
-
summary_line = simplify_sig_line(verify_result.signatures[0].to_s, all_trusted)
|
245
|
+
summary_line = simplify_sig_line(verify_result.signatures[0].to_s.dup, all_trusted)
|
233
246
|
end
|
234
247
|
|
235
248
|
if all_output_lines.length == 0
|
@@ -242,6 +255,8 @@ EOS
|
|
242
255
|
end
|
243
256
|
elsif !unknown
|
244
257
|
Chunk::CryptoNotice.new(:invalid, summary_line, all_output_lines)
|
258
|
+
elsif unknown_fingerprint
|
259
|
+
Chunk::CryptoNotice.new(:unknown_key, "Unable to determine validity of cryptographic signature", all_output_lines, unknown_fingerprint)
|
245
260
|
else
|
246
261
|
unknown_status all_output_lines
|
247
262
|
end
|
@@ -351,6 +366,31 @@ EOS
|
|
351
366
|
[notice, sig, msg]
|
352
367
|
end
|
353
368
|
|
369
|
+
def retrieve fingerprint
|
370
|
+
require 'net/http'
|
371
|
+
uri = URI($config[:keyserver_url] || KEYSERVER_URL)
|
372
|
+
unless uri.scheme == "http" and not uri.host.nil?
|
373
|
+
return "Invalid url: #{uri}"
|
374
|
+
end
|
375
|
+
|
376
|
+
fingerprint = "0x" + fingerprint unless fingerprint[0..1] == "0x"
|
377
|
+
params = {op: "get", search: fingerprint}
|
378
|
+
uri.query = URI.encode_www_form(params)
|
379
|
+
|
380
|
+
begin
|
381
|
+
res = Net::HTTP.get_response(uri)
|
382
|
+
rescue SocketError # Host doesn't exist or we couldn't connect
|
383
|
+
end
|
384
|
+
return "Couldn't get key from keyserver at this address: #{uri}" unless res.is_a?(Net::HTTPSuccess)
|
385
|
+
|
386
|
+
match = KEY_PATTERN.match(res.body)
|
387
|
+
return "No key found" unless match && match.length > 0
|
388
|
+
|
389
|
+
GPGME::Key.import(match[0])
|
390
|
+
|
391
|
+
return nil
|
392
|
+
end
|
393
|
+
|
354
394
|
private
|
355
395
|
|
356
396
|
def unknown_status lines=[]
|
@@ -394,6 +434,7 @@ private
|
|
394
434
|
rescue EOFError
|
395
435
|
from_key = nil
|
396
436
|
first_sig = "No public key available for #{signature.fingerprint}"
|
437
|
+
unknown_fpr = signature.fingerprint
|
397
438
|
end
|
398
439
|
|
399
440
|
time_line = "Signature made " + signature.timestamp.strftime("%a %d %b %Y %H:%M:%S %Z") +
|
@@ -422,7 +463,7 @@ private
|
|
422
463
|
output_lines << HookManager.run("sig-output",
|
423
464
|
{:signature => signature, :from_key => from_key})
|
424
465
|
end
|
425
|
-
return output_lines, trusted
|
466
|
+
return output_lines, trusted, unknown_fpr
|
426
467
|
end
|
427
468
|
|
428
469
|
def key_type key, fpr
|
@@ -435,6 +476,7 @@ private
|
|
435
476
|
when GPGME::PK_DSA then "DSA "
|
436
477
|
when GPGME::PK_ELG then "ElGamel "
|
437
478
|
when GPGME::PK_ELG_E then "ElGamel "
|
479
|
+
else "unknown key type (#{subkey.pubkey_algo}) "
|
438
480
|
end
|
439
481
|
end
|
440
482
|
|
@@ -443,7 +485,7 @@ private
|
|
443
485
|
# elsif only one account, then leave blank so gpg default will be user
|
444
486
|
# else set --local-user from_email_address
|
445
487
|
# NOTE: multiple signers doesn't seem to work with gpgme (2.0.2, 1.0.8)
|
446
|
-
#
|
488
|
+
#
|
447
489
|
def gen_sign_user_opts from
|
448
490
|
account = AccountManager.account_for from
|
449
491
|
account ||= AccountManager.default_account
|
data/lib/sup/draft.rb
CHANGED
@@ -16,7 +16,7 @@ class DraftManager
|
|
16
16
|
def write_draft
|
17
17
|
offset = @source.gen_offset
|
18
18
|
fn = @source.fn_for_offset offset
|
19
|
-
File.open(fn, "w") { |f| yield f }
|
19
|
+
File.open(fn, "w:UTF-8") { |f| yield f }
|
20
20
|
PollManager.poll_from @source
|
21
21
|
end
|
22
22
|
|
@@ -33,7 +33,7 @@ class DraftLoader < Source
|
|
33
33
|
yaml_properties
|
34
34
|
|
35
35
|
def initialize dir=Redwood::DRAFT_DIR
|
36
|
-
Dir.mkdir dir unless File.
|
36
|
+
Dir.mkdir dir unless File.exist? dir
|
37
37
|
super DraftManager.source_name, true, false
|
38
38
|
@dir = dir
|
39
39
|
@cur_offset = 0
|
@@ -61,8 +61,8 @@ class DraftLoader < Source
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def gen_offset
|
64
|
-
i =
|
65
|
-
while File.
|
64
|
+
i = @cur_offset
|
65
|
+
while File.exist? fn_for_offset(i)
|
66
66
|
i += 1
|
67
67
|
end
|
68
68
|
i
|
@@ -75,7 +75,7 @@ class DraftLoader < Source
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def load_message offset
|
78
|
-
raise SourceError, "Draft not found" unless File.
|
78
|
+
raise SourceError, "Draft not found" unless File.exist? fn_for_offset(offset)
|
79
79
|
File.open fn_for_offset(offset) do |f|
|
80
80
|
RMail::Mailbox::MBoxReader.new(f).each_message do |input|
|
81
81
|
return RMail::Parser.read(input)
|
@@ -85,7 +85,7 @@ class DraftLoader < Source
|
|
85
85
|
|
86
86
|
def raw_header offset
|
87
87
|
ret = ""
|
88
|
-
File.open
|
88
|
+
File.open(fn_for_offset(offset), "r:UTF-8") do |f|
|
89
89
|
until f.eof? || (l = f.gets) =~ /^$/
|
90
90
|
ret += l
|
91
91
|
end
|
@@ -94,13 +94,13 @@ class DraftLoader < Source
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def each_raw_message_line offset
|
97
|
-
File.open(fn_for_offset(offset)) do |f|
|
97
|
+
File.open(fn_for_offset(offset), "r:UTF-8") do |f|
|
98
98
|
yield f.gets until f.eof?
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
102
|
def raw_message offset
|
103
|
-
IO.read(fn_for_offset(offset))
|
103
|
+
IO.read(fn_for_offset(offset), :encoding => "UTF-8")
|
104
104
|
end
|
105
105
|
|
106
106
|
def start_offset; 0; end
|
data/lib/sup/hook.rb
CHANGED
@@ -83,7 +83,7 @@ class HookManager
|
|
83
83
|
@contexts = {}
|
84
84
|
@tags = {}
|
85
85
|
|
86
|
-
Dir.mkdir dir unless File.
|
86
|
+
Dir.mkdir dir unless File.exist? dir
|
87
87
|
end
|
88
88
|
|
89
89
|
attr_reader :tags
|
@@ -109,20 +109,20 @@ class HookManager
|
|
109
109
|
@descs[name] = desc
|
110
110
|
end
|
111
111
|
|
112
|
-
def print_hooks f=$stdout
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
EOS
|
117
|
-
|
118
|
-
HookManager.descs.sort.each do |name, desc|
|
119
|
-
f.puts <<EOS
|
112
|
+
def print_hooks pattern="", f=$stdout
|
113
|
+
matching_hooks = HookManager.descs.sort.keep_if {|name, desc| pattern.empty? or name.match(pattern)}.map do |name, desc|
|
114
|
+
<<EOS
|
120
115
|
#{name}
|
121
116
|
#{"-" * name.length}
|
122
117
|
File: #{fn_for name}
|
123
118
|
#{desc}
|
124
119
|
EOS
|
125
120
|
end
|
121
|
+
|
122
|
+
showing_str = matching_hooks.size == HookManager.descs.size ? "" : " (showing #{matching_hooks.size})"
|
123
|
+
f.puts "Have #{HookManager.descs.size} registered hooks#{showing_str}:"
|
124
|
+
f.puts
|
125
|
+
matching_hooks.each { |text| f.puts text }
|
126
126
|
end
|
127
127
|
|
128
128
|
def enabled? name; !hook_for(name).nil? end
|
data/lib/sup/index.rb
CHANGED
@@ -105,7 +105,7 @@ EOS
|
|
105
105
|
|
106
106
|
def save
|
107
107
|
debug "saving index and sources..."
|
108
|
-
FileUtils.mkdir_p @dir unless File.
|
108
|
+
FileUtils.mkdir_p @dir unless File.exist? @dir
|
109
109
|
SourceManager.save_sources
|
110
110
|
save_index
|
111
111
|
end
|
@@ -116,7 +116,7 @@ EOS
|
|
116
116
|
|
117
117
|
def load_index failsafe=false
|
118
118
|
path = File.join(@dir, 'xapian')
|
119
|
-
if File.
|
119
|
+
if File.exist? path
|
120
120
|
@xapian = Xapian::WritableDatabase.new(path, Xapian::DB_OPEN)
|
121
121
|
db_version = @xapian.get_metadata 'version'
|
122
122
|
db_version = '0' if db_version.empty?
|
@@ -516,19 +516,32 @@ EOS
|
|
516
516
|
qp.stemmer = Xapian::Stem.new($config[:stem_language])
|
517
517
|
qp.stemming_strategy = Xapian::QueryParser::STEM_SOME
|
518
518
|
qp.default_op = Xapian::Query::OP_AND
|
519
|
-
|
520
|
-
|
521
|
-
|
519
|
+
valuerangeprocessor = Xapian::NumberValueRangeProcessor.new(DATE_VALUENO,
|
520
|
+
'date:', true)
|
521
|
+
qp.add_valuerangeprocessor(valuerangeprocessor)
|
522
|
+
NORMAL_PREFIX.each { |k,info| info[:prefix].each {
|
523
|
+
|v| qp.add_prefix k, v }
|
524
|
+
}
|
525
|
+
BOOLEAN_PREFIX.each { |k,info| info[:prefix].each {
|
526
|
+
|v| qp.add_boolean_prefix k, v, info[:exclusive] }
|
527
|
+
}
|
522
528
|
|
523
529
|
begin
|
524
|
-
xapian_query = qp.parse_query(subs, Xapian::QueryParser::FLAG_PHRASE|
|
530
|
+
xapian_query = qp.parse_query(subs, Xapian::QueryParser::FLAG_PHRASE |
|
531
|
+
Xapian::QueryParser::FLAG_BOOLEAN |
|
532
|
+
Xapian::QueryParser::FLAG_LOVEHATE |
|
533
|
+
Xapian::QueryParser::FLAG_WILDCARD)
|
525
534
|
rescue RuntimeError => e
|
526
535
|
raise ParseError, "xapian query parser error: #{e}"
|
527
536
|
end
|
528
537
|
|
529
538
|
debug "parsed xapian query: #{Util::Query.describe(xapian_query, subs)}"
|
530
539
|
|
531
|
-
|
540
|
+
if xapian_query.nil? or xapian_query.empty?
|
541
|
+
raise ParseError, "couldn't parse \"#{s}\" as xapian query " \
|
542
|
+
"(special characters aren't indexed)"
|
543
|
+
end
|
544
|
+
|
532
545
|
query[:qobj] = xapian_query
|
533
546
|
query[:text] = s
|
534
547
|
query
|