schleuder 3.2.2 → 3.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +21 -11
- data/Rakefile +18 -10
- data/bin/schleuder +2 -1
- data/bin/schleuder-api-daemon +3 -2
- data/db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb +30 -0
- data/db/migrate/20180723173900_add_deliver_selfsent_to_list.rb +11 -0
- data/db/migrate/20190906194820_add_autocrypt_header_to_list.rb +11 -0
- data/db/schema.rb +4 -2
- data/etc/list-defaults.yml +13 -3
- data/etc/schleuder.yml +11 -0
- data/lib/schleuder-api-daemon.rb +9 -354
- data/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb +143 -0
- data/lib/schleuder-api-daemon/routes/key.rb +40 -0
- data/lib/schleuder-api-daemon/routes/list.rb +69 -0
- data/lib/schleuder-api-daemon/routes/status.rb +5 -0
- data/lib/schleuder-api-daemon/routes/subscription.rb +99 -0
- data/lib/schleuder-api-daemon/routes/version.rb +5 -0
- data/lib/schleuder.rb +12 -3
- data/lib/schleuder/cli.rb +33 -3
- data/lib/schleuder/cli/subcommand_fix.rb +1 -1
- data/lib/schleuder/conf.rb +7 -1
- data/lib/schleuder/errors/active_model_error.rb +2 -5
- data/lib/schleuder/errors/decryption_failed.rb +2 -7
- data/lib/schleuder/errors/key_adduid_failed.rb +1 -5
- data/lib/schleuder/errors/key_generation_failed.rb +1 -8
- data/lib/schleuder/errors/keyword_admin_only.rb +1 -5
- data/lib/schleuder/errors/list_not_found.rb +1 -5
- data/lib/schleuder/errors/listdir_problem.rb +2 -7
- data/lib/schleuder/errors/loading_list_settings_failed.rb +2 -5
- data/lib/schleuder/errors/message_empty.rb +1 -5
- data/lib/schleuder/errors/message_not_from_admin.rb +2 -5
- data/lib/schleuder/errors/message_sender_not_subscribed.rb +2 -5
- data/lib/schleuder/errors/message_too_big.rb +2 -5
- data/lib/schleuder/errors/message_unauthenticated.rb +1 -4
- data/lib/schleuder/errors/message_unencrypted.rb +2 -5
- data/lib/schleuder/errors/message_unsigned.rb +2 -5
- data/lib/schleuder/errors/too_many_keys.rb +1 -8
- data/lib/schleuder/filters/{request_filter.rb → post_decryption/10_request.rb} +0 -0
- data/lib/schleuder/filters/{max_message_size.rb → post_decryption/20_max_message_size.rb} +0 -0
- data/lib/schleuder/filters/{forward_filter.rb → post_decryption/30_forward_to_owner.rb} +0 -0
- data/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb +10 -0
- data/lib/schleuder/filters/post_decryption/90_strip_html_from_alternative_if_keywords_present.rb +21 -0
- data/lib/schleuder/filters/{bounces_filter.rb → pre_decryption/10_forward_bounce_to_admins.rb} +0 -0
- data/lib/schleuder/filters/{forward_incoming.rb → pre_decryption/20_forward_all_incoming_to_admins.rb} +0 -0
- data/lib/schleuder/filters/{send_key_filter.rb → pre_decryption/30_send_key.rb} +0 -0
- data/lib/schleuder/filters/{hotmail_message_filter.rb → pre_decryption/40_fix_exchange_messages.rb} +5 -3
- data/lib/schleuder/filters/{strip_alternative_filter.rb → pre_decryption/50_strip_html_from_alternative.rb} +1 -1
- data/lib/schleuder/filters_runner.rb +41 -31
- data/lib/schleuder/gpgme/ctx.rb +24 -3
- data/lib/schleuder/gpgme/import_status.rb +13 -7
- data/lib/schleuder/gpgme/key.rb +8 -0
- data/lib/schleuder/list.rb +26 -4
- data/lib/schleuder/logger_notifications.rb +8 -1
- data/lib/schleuder/mail/encrypted_part.rb +14 -0
- data/lib/schleuder/mail/gpg.rb +15 -0
- data/lib/schleuder/mail/message.rb +97 -49
- data/lib/schleuder/plugins/attach_listkey.rb +6 -10
- data/lib/schleuder/plugins/key_management.rb +34 -26
- data/lib/schleuder/plugins/resend.rb +14 -11
- data/lib/schleuder/plugins/subscription_management.rb +70 -3
- data/lib/schleuder/runner.rb +49 -10
- data/lib/schleuder/subscription.rb +5 -9
- data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
- data/lib/schleuder/version.rb +1 -1
- data/locales/de.yml +101 -9
- data/locales/en.yml +107 -11
- metadata +72 -34
- data/lib/schleuder/errors/file_not_found.rb +0 -14
- data/lib/schleuder/errors/invalid_listname.rb +0 -13
- data/lib/schleuder/errors/list_exists.rb +0 -13
- data/lib/schleuder/errors/unknown_list_option.rb +0 -14
- data/lib/schleuder/filters/auth_filter.rb +0 -39
@@ -0,0 +1,143 @@
|
|
1
|
+
module SchleuderApiDaemonHelper
|
2
|
+
def valid_credentials?
|
3
|
+
@auth ||= Rack::Auth::Basic::Request.new(request.env)
|
4
|
+
if @auth.provided? && @auth.basic? && @auth.credentials.present?
|
5
|
+
username, api_key = @auth.credentials
|
6
|
+
username == 'schleuder' && Conf.api_valid_api_keys.include?(api_key)
|
7
|
+
else
|
8
|
+
false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def authenticate!
|
13
|
+
# Be careful to use path_info() — it can be changed by other filters!
|
14
|
+
return if request.path_info == '/status.json'
|
15
|
+
if ! valid_credentials?
|
16
|
+
headers['WWW-Authenticate'] = 'Basic realm="Schleuder API Daemon"'
|
17
|
+
halt 401, "Not authorized\n"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def list(id_or_email=nil)
|
22
|
+
if id_or_email.blank?
|
23
|
+
if params[:list_id].present?
|
24
|
+
id_or_email = params[:list_id]
|
25
|
+
else
|
26
|
+
client_error "Parameter list_id is required"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
if is_an_integer?(id_or_email)
|
30
|
+
list = List.where(id: id_or_email).first
|
31
|
+
else
|
32
|
+
# list_id is actually an email address
|
33
|
+
list = List.where(email: id_or_email).first
|
34
|
+
end
|
35
|
+
list || halt(404)
|
36
|
+
end
|
37
|
+
|
38
|
+
def subscription(id_or_email)
|
39
|
+
if is_an_integer?(id_or_email)
|
40
|
+
sub = Subscription.where(id: id_or_email.to_i).first
|
41
|
+
else
|
42
|
+
# Email
|
43
|
+
if params[:list_id].blank?
|
44
|
+
client_error "Parameter list_id is required when using email as identifier for subscriptions."
|
45
|
+
else
|
46
|
+
sub = list.subscriptions.where(email: id_or_email).first
|
47
|
+
end
|
48
|
+
end
|
49
|
+
sub || halt(404)
|
50
|
+
end
|
51
|
+
|
52
|
+
def requested_list_id
|
53
|
+
# ActiveResource doesn't want to use query-params with create(), so here
|
54
|
+
# list_id might be included in the request-body.
|
55
|
+
params['list_id'] || parsed_body['list_id'] || client_error('Need list_id')
|
56
|
+
end
|
57
|
+
|
58
|
+
def parsed_body
|
59
|
+
@parsed_body ||= begin
|
60
|
+
b = JSON.parse(request.body.read)
|
61
|
+
logger.debug "parsed body: #{b.inspect}"
|
62
|
+
b
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def server_error(msg)
|
67
|
+
logger.warn msg
|
68
|
+
halt(500, json(error: msg))
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO: unify error messages. This method currently sends an old error format. See <https://github.com/rails/activeresource/blob/d6a5186/lib/active_resource/base.rb#L227>.
|
72
|
+
def client_error(obj_or_msg, http_code=400)
|
73
|
+
text = case obj_or_msg
|
74
|
+
when String, Symbol
|
75
|
+
obj_or_msg.to_s
|
76
|
+
when ActiveRecord::Base
|
77
|
+
obj_or_msg.errors.full_messages
|
78
|
+
else
|
79
|
+
obj_or_msg
|
80
|
+
end
|
81
|
+
logger.error "Sending error to client: #{text.inspect}"
|
82
|
+
halt(http_code, json(errors: text))
|
83
|
+
end
|
84
|
+
|
85
|
+
# poor persons type casting
|
86
|
+
def cast_param_values
|
87
|
+
params.each do |key, value|
|
88
|
+
params[key] =
|
89
|
+
case value
|
90
|
+
when 'true' then true
|
91
|
+
when 'false' then false
|
92
|
+
when '0' then 0
|
93
|
+
when is_an_integer?(value) then value.to_i
|
94
|
+
else value
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def key_to_hash(key, include_keydata=false)
|
100
|
+
hash = {
|
101
|
+
fingerprint: key.fingerprint,
|
102
|
+
email: key.email,
|
103
|
+
expiry: key.expires,
|
104
|
+
generated_at: key.generated_at,
|
105
|
+
primary_uid: key.primary_uid.uid,
|
106
|
+
oneline: key.oneline,
|
107
|
+
trust_issues: key.usability_issue
|
108
|
+
}
|
109
|
+
if include_keydata
|
110
|
+
hash[:description] = key.to_s
|
111
|
+
hash[:ascii] = key.armored
|
112
|
+
end
|
113
|
+
hash
|
114
|
+
end
|
115
|
+
|
116
|
+
def set_x_messages(messages)
|
117
|
+
if messages.present?
|
118
|
+
headers 'X-Messages' => Array(messages).join(' // ').gsub(/\n/, ' // ')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def find_key_material
|
123
|
+
key_material = parsed_body['key_material'].presence
|
124
|
+
# By convention key_material is either ASCII or base64-encoded.
|
125
|
+
if key_material && ! key_material.match('BEGIN PGP')
|
126
|
+
key_material = Base64.decode64(key_material)
|
127
|
+
end
|
128
|
+
key_material
|
129
|
+
end
|
130
|
+
|
131
|
+
def find_attributes_from_body(attribs)
|
132
|
+
Array(attribs).inject({}) do |memo, attrib|
|
133
|
+
if parsed_body.has_key?(attrib)
|
134
|
+
memo[attrib] = parsed_body[attrib]
|
135
|
+
end
|
136
|
+
memo
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def is_an_integer?(input)
|
141
|
+
input.to_s.match(/^[0-9]+$/).present?
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class SchleuderApiDaemon < Sinatra::Base
|
2
|
+
register Sinatra::Namespace
|
3
|
+
|
4
|
+
namespace '/keys' do
|
5
|
+
get '.json' do
|
6
|
+
keys = list.keys.sort_by(&:email).map do |key|
|
7
|
+
key_to_hash(key)
|
8
|
+
end
|
9
|
+
json keys
|
10
|
+
end
|
11
|
+
|
12
|
+
post '.json' do
|
13
|
+
input = parsed_body['keymaterial']
|
14
|
+
if ! input.match('BEGIN PGP')
|
15
|
+
input = Base64.decode64(input)
|
16
|
+
end
|
17
|
+
json list(requested_list_id).import_key(input)
|
18
|
+
end
|
19
|
+
|
20
|
+
get '/check_keys.json' do
|
21
|
+
json result: list.check_keys
|
22
|
+
end
|
23
|
+
|
24
|
+
get '/:fingerprint.json' do |fingerprint|
|
25
|
+
if key = list.key(fingerprint)
|
26
|
+
json key_to_hash(key, true)
|
27
|
+
else
|
28
|
+
404
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
delete '/:fingerprint.json' do |fingerprint|
|
33
|
+
if list.delete_key(fingerprint)
|
34
|
+
200
|
35
|
+
else
|
36
|
+
404
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class SchleuderApiDaemon < Sinatra::Base
|
2
|
+
register Sinatra::Namespace
|
3
|
+
|
4
|
+
namespace '/lists' do
|
5
|
+
get '.json' do
|
6
|
+
json List.all, include: :subscriptions
|
7
|
+
end
|
8
|
+
|
9
|
+
post '.json' do
|
10
|
+
listname = parsed_body['email']
|
11
|
+
fingerprint = parsed_body['fingerprint']
|
12
|
+
adminaddress = parsed_body['adminaddress']
|
13
|
+
adminfingerprint = parsed_body['adminfingerprint']
|
14
|
+
adminkey = parsed_body['adminkey']
|
15
|
+
list, messages = ListBuilder.new({email: listname, fingerprint: fingerprint}, adminaddress, adminfingerprint, adminkey).run
|
16
|
+
if list.nil?
|
17
|
+
client_error(messages, 422)
|
18
|
+
elsif ! list.valid?
|
19
|
+
client_error(list, 422)
|
20
|
+
else
|
21
|
+
set_x_messages(messages)
|
22
|
+
body json(list)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
get '/configurable_attributes.json' do
|
27
|
+
json(List.configurable_attributes) + "\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
post '/send_list_key_to_subscriptions.json' do
|
31
|
+
json(result: list.send_list_key_to_subscriptions)
|
32
|
+
end
|
33
|
+
|
34
|
+
get '/new.json' do
|
35
|
+
json List.new
|
36
|
+
end
|
37
|
+
|
38
|
+
get '/:id.json' do |id|
|
39
|
+
json list(id)
|
40
|
+
end
|
41
|
+
|
42
|
+
put '/:id.json' do |id|
|
43
|
+
list = list(id)
|
44
|
+
if list.update(parsed_body)
|
45
|
+
204
|
46
|
+
else
|
47
|
+
client_error(list)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
patch '/:id.json' do |id|
|
52
|
+
list = list(id)
|
53
|
+
if list.update(parsed_body)
|
54
|
+
204
|
55
|
+
else
|
56
|
+
client_error(list)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
delete '/:id.json' do |id|
|
61
|
+
list = list(id)
|
62
|
+
if list.destroy
|
63
|
+
200
|
64
|
+
else
|
65
|
+
client_error(list)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
class SchleuderApiDaemon < Sinatra::Base
|
2
|
+
register Sinatra::Namespace
|
3
|
+
|
4
|
+
namespace '/subscriptions' do
|
5
|
+
get '.json' do
|
6
|
+
filterkeys = Subscription.configurable_attributes + [:list_id, :email]
|
7
|
+
filter = params.select do |param|
|
8
|
+
filterkeys.include?(param.to_sym)
|
9
|
+
end
|
10
|
+
|
11
|
+
logger.debug "Subscription filter: #{filter.inspect}"
|
12
|
+
if filter['list_id'] && ! is_an_integer?(filter['list_id'])
|
13
|
+
# Value is an email-address
|
14
|
+
if list = List.where(email: filter['list_id']).first
|
15
|
+
filter['list_id'] = list.id
|
16
|
+
else
|
17
|
+
status 404
|
18
|
+
return json(errors: 'No such list')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
json Subscription.where(filter)
|
23
|
+
end
|
24
|
+
|
25
|
+
post '.json' do
|
26
|
+
begin
|
27
|
+
list = list(requested_list_id)
|
28
|
+
# We don't have to care about nil-values, subscribe() does that for us.
|
29
|
+
sub, msgs = list.subscribe(
|
30
|
+
parsed_body['email'],
|
31
|
+
parsed_body['fingerprint'],
|
32
|
+
parsed_body['admin'],
|
33
|
+
parsed_body['delivery_enabled'],
|
34
|
+
find_key_material
|
35
|
+
)
|
36
|
+
set_x_messages(msgs)
|
37
|
+
logger.debug "subcription: #{sub.inspect}"
|
38
|
+
if sub.valid?
|
39
|
+
logger.debug "Subscribed: #{sub.inspect}"
|
40
|
+
# TODO: why redirect instead of respond with result?
|
41
|
+
redirect to("/subscriptions/#{sub.id}.json"), 201
|
42
|
+
else
|
43
|
+
client_error(sub, 422)
|
44
|
+
end
|
45
|
+
rescue ActiveRecord::RecordNotUnique
|
46
|
+
logger.error "Already subscribed"
|
47
|
+
status 422
|
48
|
+
json errors: {email: ['is already subscribed']}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
get '/configurable_attributes.json' do
|
53
|
+
json(Subscription.configurable_attributes) + "\n"
|
54
|
+
end
|
55
|
+
|
56
|
+
get '/new.json' do
|
57
|
+
json Subscription.new
|
58
|
+
end
|
59
|
+
|
60
|
+
get '/:id.json' do |id|
|
61
|
+
json subscription(id)
|
62
|
+
end
|
63
|
+
|
64
|
+
put '/:id.json' do |id|
|
65
|
+
sub = subscription(id)
|
66
|
+
list = sub.list
|
67
|
+
args = find_attributes_from_body(%w[email fingerprint admin delivery_enabled])
|
68
|
+
fingerprint, messages = list.import_key_and_find_fingerprint(find_key_material)
|
69
|
+
set_x_messages(messages)
|
70
|
+
# For an already existing subscription, only update fingerprint if a
|
71
|
+
# new one has been selected from the upload.
|
72
|
+
if fingerprint.present?
|
73
|
+
args["fingerprint"] = fingerprint
|
74
|
+
end
|
75
|
+
if sub.update(args)
|
76
|
+
200
|
77
|
+
else
|
78
|
+
client_error(sub, 422)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
patch '/:id.json' do |id|
|
83
|
+
sub = subscription(id)
|
84
|
+
if sub.update(parsed_body)
|
85
|
+
200
|
86
|
+
else
|
87
|
+
client_error(sub)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
delete '/:id.json' do |id|
|
92
|
+
if sub = subscription(id).destroy
|
93
|
+
200
|
94
|
+
else
|
95
|
+
client_error(sub)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/schleuder.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# default to UTF-8 encoding as early as possible for external
|
2
|
+
# data.
|
3
|
+
#
|
4
|
+
# this should ensure we are able to parse most incoming
|
5
|
+
# plain text mails in different charsets.
|
6
|
+
Encoding.default_external = Encoding::UTF_8
|
7
|
+
|
1
8
|
# Stdlib
|
2
9
|
require 'fileutils'
|
3
10
|
require 'singleton'
|
@@ -22,6 +29,8 @@ $:.unshift libdir
|
|
22
29
|
# Monkeypatches
|
23
30
|
require 'schleuder/mail/parts_list.rb'
|
24
31
|
require 'schleuder/mail/message.rb'
|
32
|
+
require 'schleuder/mail/gpg.rb'
|
33
|
+
require 'schleuder/mail/encrypted_part.rb'
|
25
34
|
require 'schleuder/gpgme/import_status.rb'
|
26
35
|
require 'schleuder/gpgme/key.rb'
|
27
36
|
require 'schleuder/gpgme/sub_key.rb'
|
@@ -46,9 +55,6 @@ Dir["#{libdir}/schleuder/plugins/*.rb"].each do |file|
|
|
46
55
|
require file
|
47
56
|
end
|
48
57
|
require 'schleuder/filters_runner'
|
49
|
-
Dir["#{libdir}/schleuder/filters/*.rb"].each do |file|
|
50
|
-
require file
|
51
|
-
end
|
52
58
|
Dir["#{libdir}/schleuder/validators/*.rb"].each do |file|
|
53
59
|
require file
|
54
60
|
end
|
@@ -62,6 +68,9 @@ ENV["SCHLEUDER_CONFIG"] ||= '/etc/schleuder/schleuder.yml'
|
|
62
68
|
ENV["SCHLEUDER_LIST_DEFAULTS"] ||= '/etc/schleuder/list-defaults.yml'
|
63
69
|
ENV["SCHLEUDER_ENV"] ||= 'production'
|
64
70
|
ENV["SCHLEUDER_ROOT"] = rootdir.to_s
|
71
|
+
# Ensure that gnupg never-ever tries to ask for a passphrase.
|
72
|
+
ENV["GPG_TTY"] = "/nonexistant-#{rand}"
|
73
|
+
ENV["DISPLAY"] = nil
|
65
74
|
|
66
75
|
GPGME::Ctx.set_gpg_path_from_env
|
67
76
|
GPGME::Ctx.check_gpg_version
|
data/lib/schleuder/cli.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'yaml'
|
3
3
|
require 'gpgme'
|
4
|
+
require 'charlock_holmes'
|
4
5
|
|
5
6
|
require_relative '../schleuder'
|
6
7
|
require 'schleuder/cli/subcommand_fix'
|
@@ -62,11 +63,14 @@ module Schleuder
|
|
62
63
|
list.logger.notify_admin(msg, nil, I18n.t('check_keys'))
|
63
64
|
end
|
64
65
|
end
|
66
|
+
permission_notice
|
65
67
|
end
|
66
68
|
|
67
69
|
desc 'refresh_keys [list1@example.com]', "Refresh all keys of all list from the keyservers sequentially (one by one or on the passed list). (This is supposed to be run from cron weekly.)"
|
68
70
|
def refresh_keys(list=nil)
|
71
|
+
GPGME::Ctx.send_notice_if_gpg_does_not_know_import_filter
|
69
72
|
work_on_lists(:refresh_keys,list)
|
73
|
+
permission_notice
|
70
74
|
end
|
71
75
|
|
72
76
|
desc 'pin_keys [list1@example.com]', "Find keys for subscriptions without a pinned key and try to pin a certain key (one by one or based on the passed list)."
|
@@ -122,6 +126,7 @@ module Schleuder
|
|
122
126
|
end
|
123
127
|
|
124
128
|
say "Schleuder has been set up. You can now create a new list using `schleuder-cli`.\nWe hope you enjoy!"
|
129
|
+
permission_notice
|
125
130
|
rescue => exc
|
126
131
|
fatal exc.message
|
127
132
|
end
|
@@ -257,6 +262,7 @@ Please notify the users and admins of this list of these changes.
|
|
257
262
|
if messages.present?
|
258
263
|
say messages.gsub(' // ', "\n")
|
259
264
|
end
|
265
|
+
permission_notice
|
260
266
|
rescue => exc
|
261
267
|
fatal "#{exc}\n#{exc.backtrace.first}"
|
262
268
|
end
|
@@ -315,11 +321,15 @@ Please notify the users and admins of this list of these changes.
|
|
315
321
|
private
|
316
322
|
|
317
323
|
def work_on_lists(subj, list=nil)
|
318
|
-
|
319
|
-
List.all
|
324
|
+
if list.nil?
|
325
|
+
selected_lists = List.all
|
320
326
|
else
|
321
|
-
List.where(email: list)
|
327
|
+
selected_lists = List.where(email: list)
|
328
|
+
if selected_lists.blank?
|
329
|
+
error("No list with this address exists: #{list.inspect}")
|
330
|
+
end
|
322
331
|
end
|
332
|
+
|
323
333
|
selected_lists.each do |list|
|
324
334
|
I18n.locale = list.language
|
325
335
|
output = list.send(subj)
|
@@ -330,5 +340,25 @@ Please notify the users and admins of this list of these changes.
|
|
330
340
|
end
|
331
341
|
end
|
332
342
|
|
343
|
+
# Make this class exit with code 1 in case of an error. See <https://github.com/erikhuda/thor/issues/244>.
|
344
|
+
def self.exit_on_failure?
|
345
|
+
true
|
346
|
+
end
|
347
|
+
|
348
|
+
def permission_notice
|
349
|
+
if Process.euid == 0
|
350
|
+
dirs = [Conf.lists_dir, Conf.listlogs_dir]
|
351
|
+
if Conf.database['adapter'] == 'sqlite3'
|
352
|
+
dirs << Conf.database['database']
|
353
|
+
end
|
354
|
+
dirs_sentence = dirs.uniq.map { |dir| enquote(dir) }.to_sentence
|
355
|
+
say "Warning: this process was run as root -- please make sure that all files in #{dirs_sentence} have correct file system permissions for the user that is running both, schleuder from the MTA and `schleuder-api-daemon`."
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def enquote(string)
|
360
|
+
"\`#{string}\`"
|
361
|
+
end
|
362
|
+
|
333
363
|
end
|
334
364
|
end
|