schleuder 3.5.3 → 4.0.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 +10 -21
- data/Rakefile +15 -12
- data/bin/schleuder +1 -1
- data/db/migrate/20140501103532_create_lists.rb +1 -1
- data/db/migrate/20140501112859_create_subscriptions.rb +1 -1
- data/db/migrate/{201508092100_add_language_to_lists.rb → 20150809210000_add_language_to_lists.rb} +1 -1
- data/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb +1 -1
- data/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb +1 -1
- data/db/migrate/{201508141727_change_send_encrypted_only_default.rb → 20150814172700_change_send_encrypted_only_default.rb} +1 -1
- data/db/migrate/{201508222143_add_logfiles_to_keep_to_lists.rb → 20150822214300_add_logfiles_to_keep_to_lists.rb} +1 -1
- data/db/migrate/{201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb → 20150826172300_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb} +1 -1
- data/db/migrate/{201508261815_strip_gpg_passphrase.rb → 20150826181500_strip_gpg_passphrase.rb} +1 -1
- data/db/migrate/{201508261827_remove_default_mime.rb → 20150826182700_remove_default_mime.rb} +1 -1
- data/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb +1 -1
- data/db/migrate/20170713215059_add_internal_footer_to_list.rb +1 -1
- data/db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb +1 -1
- data/db/migrate/20180723173900_add_deliver_selfsent_to_list.rb +1 -1
- data/db/migrate/20190906194820_add_autocrypt_header_to_list.rb +1 -1
- data/db/migrate/20200118170110_add_set_reply_to_to_sender_and_munge_from.rb +15 -0
- data/db/schema.rb +45 -45
- data/etc/list-defaults.yml +18 -0
- data/etc/postfix/schleuder_sqlite.cf +1 -1
- data/etc/schleuder-weekly-key-maintenance.service +9 -0
- data/etc/schleuder-weekly-key-maintenance.timer +9 -0
- data/etc/schleuder.yml +3 -3
- data/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb +3 -3
- data/lib/schleuder-api-daemon/routes/subscription.rb +4 -4
- data/lib/schleuder.rb +13 -12
- data/lib/schleuder/cli.rb +9 -189
- data/lib/schleuder/cli/cert.rb +2 -2
- data/lib/schleuder/cli/cli_helper.rb +14 -0
- data/lib/schleuder/cli/schleuder_cert_manager.rb +4 -4
- data/lib/schleuder/conf.rb +4 -4
- data/lib/schleuder/errors/base.rb +2 -2
- data/lib/schleuder/errors/decryption_failed.rb +1 -1
- data/lib/schleuder/errors/fatal_error.rb +1 -1
- data/lib/schleuder/errors/key_adduid_failed.rb +1 -1
- data/lib/schleuder/errors/key_generation_failed.rb +1 -1
- data/lib/schleuder/errors/message_empty.rb +1 -1
- data/lib/schleuder/errors/message_too_big.rb +1 -1
- data/lib/schleuder/errors/too_many_keys.rb +1 -1
- data/lib/schleuder/filters/post_decryption/10_request.rb +3 -3
- data/lib/schleuder/filters/post_decryption/20_max_message_size.rb +1 -1
- data/lib/schleuder/filters/post_decryption/30_forward_to_owner.rb +1 -1
- data/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb +1 -1
- data/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb +1 -1
- data/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb +1 -1
- data/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb +1 -1
- data/lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb +1 -1
- data/lib/schleuder/filters/pre_decryption/10_forward_bounce_to_admins.rb +1 -1
- data/lib/schleuder/filters/pre_decryption/30_send_key.rb +1 -1
- data/lib/schleuder/filters/pre_decryption/40_fix_exchange_messages.rb +1 -1
- data/lib/schleuder/filters/pre_decryption/50_strip_html_from_alternative.rb +2 -2
- data/lib/schleuder/filters_runner.rb +9 -9
- data/lib/schleuder/gpgme/ctx.rb +15 -67
- data/lib/schleuder/gpgme/key.rb +4 -136
- data/lib/schleuder/gpgme/user_id.rb +2 -0
- data/lib/schleuder/keyword_handlers/attach_list_key.rb +17 -0
- data/lib/schleuder/keyword_handlers/base.rb +36 -0
- data/lib/schleuder/keyword_handlers/get_version.rb +11 -0
- data/lib/schleuder/keyword_handlers/key_management.rb +141 -0
- data/lib/schleuder/keyword_handlers/list_management.rb +19 -0
- data/lib/schleuder/keyword_handlers/resend.rb +208 -0
- data/lib/schleuder/keyword_handlers/sign_this.rb +54 -0
- data/lib/schleuder/keyword_handlers/subscription_management.rb +213 -0
- data/lib/schleuder/keyword_handlers_runner.rb +146 -0
- data/lib/schleuder/list.rb +28 -40
- data/lib/schleuder/list_builder.rb +16 -5
- data/lib/schleuder/listlogger.rb +1 -1
- data/lib/schleuder/logger.rb +2 -6
- data/lib/schleuder/mail/{encrypted_part.rb → gpg/encrypted_part.rb} +0 -0
- data/lib/schleuder/mail/gpg/sign_part.rb +33 -0
- data/lib/schleuder/mail/message.rb +135 -40
- data/lib/schleuder/runner.rb +18 -16
- data/lib/schleuder/subscription.rb +35 -13
- data/lib/schleuder/validators/boolean_validator.rb +1 -1
- data/lib/schleuder/validators/email_validator.rb +1 -1
- data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
- data/lib/schleuder/validators/greater_than_zero_validator.rb +1 -1
- data/lib/schleuder/validators/no_line_breaks_validator.rb +1 -1
- data/lib/schleuder/version.rb +1 -1
- data/locales/de.yml +49 -36
- data/locales/en.yml +34 -21
- metadata +131 -79
- data/bin/pinentry-clearpassphrase +0 -72
- data/lib/schleuder/plugin_runners/base.rb +0 -91
- data/lib/schleuder/plugin_runners/list_plugins_runner.rb +0 -24
- data/lib/schleuder/plugin_runners/request_plugins_runner.rb +0 -27
- data/lib/schleuder/plugins/attach_listkey.rb +0 -13
- data/lib/schleuder/plugins/get_version.rb +0 -7
- data/lib/schleuder/plugins/key_management.rb +0 -121
- data/lib/schleuder/plugins/list_management.rb +0 -15
- data/lib/schleuder/plugins/resend.rb +0 -199
- data/lib/schleuder/plugins/sign_this.rb +0 -46
- data/lib/schleuder/plugins/subscription_management.rb +0 -207
@@ -0,0 +1,54 @@
|
|
1
|
+
module Schleuder
|
2
|
+
module KeywordHandlers
|
3
|
+
class SignThis < Base
|
4
|
+
handles_request_keyword 'sign-this', with_method: :sign_this
|
5
|
+
|
6
|
+
def sign_this
|
7
|
+
if @mail.has_attachments?
|
8
|
+
@list.logger.debug "Signing each attachment's body"
|
9
|
+
intro = I18n.t('keyword_handlers.sign_this.signatures_attached')
|
10
|
+
parts = @mail.attachments.map do |attachment|
|
11
|
+
make_signature_part(attachment)
|
12
|
+
end
|
13
|
+
[intro, parts].flatten
|
14
|
+
elsif @mail.first_plaintext_part.body.to_s.present?
|
15
|
+
@list.logger.debug 'Clear-signing first available text/plain part'
|
16
|
+
clearsign(@mail.first_plaintext_part.body.to_s)
|
17
|
+
else
|
18
|
+
@list.logger.debug 'Found no attachments and an empty body - sending error message'
|
19
|
+
I18n.t('keyword_handlers.sign_this.no_content_found')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
|
27
|
+
def make_signature_part(attachment)
|
28
|
+
material = attachment.body.to_s
|
29
|
+
return nil if material.strip.blank?
|
30
|
+
file_basename = attachment.filename.presence || Digest::SHA256.hexdigest(material)
|
31
|
+
@list.logger.debug "Signing #{file_basename}"
|
32
|
+
filename = "#{file_basename}.sig"
|
33
|
+
part = Mail::Part.new
|
34
|
+
part.body = detachsign(material)
|
35
|
+
part.content_type = 'application/pgp-signature'
|
36
|
+
part.content_disposition = "attachment; filename=#{filename}"
|
37
|
+
part.content_description = "OpenPGP signature for '#{file_basename}'"
|
38
|
+
part
|
39
|
+
end
|
40
|
+
|
41
|
+
def detachsign(thing)
|
42
|
+
crypto.sign(thing, mode: GPGME::SIG_MODE_DETACH).to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def clearsign(string)
|
46
|
+
crypto.clearsign(string.to_s).to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def crypto
|
50
|
+
@crypto ||= GPGME::Crypto.new(armor: true)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
module Schleuder
|
2
|
+
module KeywordHandlers
|
3
|
+
class SubscriptionManangement < Base
|
4
|
+
handles_request_keyword 'subscribe', with_method: :subscribe
|
5
|
+
handles_request_keyword 'unsubscribe', with_method: :unsubscribe
|
6
|
+
handles_request_keyword 'list-subscriptions', with_method: :list_subscriptions
|
7
|
+
handles_request_keyword 'set-fingerprint', with_method: :set_fingerprint
|
8
|
+
handles_request_keyword 'unset-fingerprint', with_method: :unset_fingerprint
|
9
|
+
|
10
|
+
def subscribe
|
11
|
+
if @arguments.blank?
|
12
|
+
return I18n.t(
|
13
|
+
'keyword_handlers.subscription_management.subscribe_requires_arguments'
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
email = @arguments.shift.to_s.downcase
|
18
|
+
|
19
|
+
if @arguments.present?
|
20
|
+
# Collect all arguments that look like fingerprint-material
|
21
|
+
fingerprint = ''
|
22
|
+
while @arguments.first.present? && @arguments.first.match(/^(0x)?[a-f0-9]+$/i)
|
23
|
+
fingerprint << @arguments.shift.downcase
|
24
|
+
end
|
25
|
+
# Use possibly remaining args as flags.
|
26
|
+
adminflag = @arguments.shift.to_s.downcase.presence
|
27
|
+
deliveryflag = @arguments.shift.to_s.downcase.presence
|
28
|
+
end
|
29
|
+
|
30
|
+
sub, _ = @list.subscribe(email, fingerprint, adminflag, deliveryflag)
|
31
|
+
|
32
|
+
if sub.persisted?
|
33
|
+
I18n.t(
|
34
|
+
'keyword_handlers.subscription_management.subscribed',
|
35
|
+
email: sub.email,
|
36
|
+
fingerprint: sub.fingerprint,
|
37
|
+
admin: sub.admin,
|
38
|
+
delivery_enabled: sub.delivery_enabled
|
39
|
+
)
|
40
|
+
else
|
41
|
+
I18n.t(
|
42
|
+
'keyword_handlers.subscription_management.subscribing_failed',
|
43
|
+
email: sub.email,
|
44
|
+
errors: sub.errors.full_messages.join(".\n")
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def unsubscribe
|
50
|
+
# If no address was given we unsubscribe the sender.
|
51
|
+
email = @arguments.first.to_s.downcase.presence || @mail.signer.email
|
52
|
+
|
53
|
+
# Refuse to unsubscribe the last admin.
|
54
|
+
if @list.admins.size == 1 && @list.admins.first.email == email
|
55
|
+
return I18n.t(
|
56
|
+
'keyword_handlers.subscription_management.cannot_unsubscribe_last_admin', email: email
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
# TODO: May signers have multiple UIDs? We don't match those currently.
|
61
|
+
if ! @list.from_admin?(@mail) && email != @mail.signer.email
|
62
|
+
# Only admins may unsubscribe others.
|
63
|
+
return I18n.t(
|
64
|
+
'keyword_handlers.subscription_management.forbidden', email: email
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
sub = @list.subscriptions.where(email: email).first
|
69
|
+
|
70
|
+
if sub.blank?
|
71
|
+
return I18n.t(
|
72
|
+
'keyword_handlers.subscription_management.is_not_subscribed', email: email
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
if res = sub.delete
|
77
|
+
I18n.t(
|
78
|
+
'keyword_handlers.subscription_management.unsubscribed', email: email
|
79
|
+
)
|
80
|
+
else
|
81
|
+
I18n.t(
|
82
|
+
'keyword_handlers.subscription_management.unsubscribing_failed',
|
83
|
+
email: email,
|
84
|
+
error: res.errors.to_a
|
85
|
+
)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def list_subscriptions
|
90
|
+
subs = if @arguments.blank?
|
91
|
+
@list.subscriptions.all.to_a
|
92
|
+
else
|
93
|
+
@arguments.map do |argument|
|
94
|
+
@list.subscriptions.where('email like ?', "%#{argument}%").to_a
|
95
|
+
end.flatten
|
96
|
+
end
|
97
|
+
|
98
|
+
if subs.blank?
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
|
102
|
+
out = [ I18n.t('keyword_handlers.subscription_management.list_of_subscriptions') ]
|
103
|
+
|
104
|
+
out << subs.map do |subscription|
|
105
|
+
# Fingerprints are at most 40 characters long, and lines shouldn't
|
106
|
+
# exceed 80 characters if possible.
|
107
|
+
s = subscription.email
|
108
|
+
if subscription.fingerprint.present?
|
109
|
+
s << "\t0x#{subscription.fingerprint}"
|
110
|
+
end
|
111
|
+
if ! subscription.delivery_enabled?
|
112
|
+
s << "\tDelivery disabled!"
|
113
|
+
end
|
114
|
+
s
|
115
|
+
end
|
116
|
+
|
117
|
+
out.join("\n")
|
118
|
+
end
|
119
|
+
|
120
|
+
def set_fingerprint
|
121
|
+
if @arguments.blank?
|
122
|
+
return I18n.t(
|
123
|
+
'keyword_handlers.subscription_management.set_fingerprint_requires_arguments'
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
if @arguments.first.match(/@/)
|
128
|
+
email = @arguments.shift.downcase
|
129
|
+
if email != @mail.signer.email && ! @list.from_admin?(@mail)
|
130
|
+
return I18n.t(
|
131
|
+
'keyword_handlers.subscription_management.set_fingerprint_only_self'
|
132
|
+
)
|
133
|
+
end
|
134
|
+
else
|
135
|
+
email = @mail.signer.email
|
136
|
+
end
|
137
|
+
|
138
|
+
sub = @list.subscriptions.where(email: email).first
|
139
|
+
|
140
|
+
if sub.blank?
|
141
|
+
return I18n.t(
|
142
|
+
'keyword_handlers.subscription_management.is_not_subscribed', email: email
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
fingerprint = @arguments.join
|
147
|
+
unless GPGME::Key.valid_fingerprint?(fingerprint)
|
148
|
+
return I18n.t(
|
149
|
+
'keyword_handlers.subscription_management.set_fingerprint_requires_valid_fingerprint',
|
150
|
+
fingerprint: fingerprint
|
151
|
+
)
|
152
|
+
end
|
153
|
+
|
154
|
+
sub.fingerprint = fingerprint
|
155
|
+
if sub.save
|
156
|
+
I18n.t(
|
157
|
+
'keyword_handlers.subscription_management.fingerprint_set',
|
158
|
+
email: email,
|
159
|
+
fingerprint: sub.fingerprint
|
160
|
+
)
|
161
|
+
else
|
162
|
+
I18n.t(
|
163
|
+
'keyword_handlers.subscription_management.setting_fingerprint_failed',
|
164
|
+
email: email,
|
165
|
+
fingerprint: sub.fingerprint,
|
166
|
+
errors: sub.errors.to_a.join("\n")
|
167
|
+
)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def unset_fingerprint
|
172
|
+
if @arguments.blank?
|
173
|
+
return I18n.t(
|
174
|
+
'keyword_handlers.subscription_management.unset_fingerprint_requires_arguments'
|
175
|
+
)
|
176
|
+
end
|
177
|
+
|
178
|
+
email = @arguments.shift.to_s.downcase
|
179
|
+
if email != @mail.signer.email && ! @list.from_admin?(mail)
|
180
|
+
return I18n.t(
|
181
|
+
'keyword_handlers.subscription_management.unset_fingerprint_only_self'
|
182
|
+
)
|
183
|
+
end
|
184
|
+
if email == @mail.signer.email && @list.from_admin?(@mail) && @arguments.last.to_s.downcase != 'force'
|
185
|
+
return I18n.t(
|
186
|
+
'keyword_handlers.subscription_management.unset_fingerprint_requires_arguments'
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
sub = @list.subscriptions.where(email: email).first
|
191
|
+
if sub.blank?
|
192
|
+
return I18n.t(
|
193
|
+
'keyword_handlers.subscription_management.is_not_subscribed', email: email
|
194
|
+
)
|
195
|
+
end
|
196
|
+
|
197
|
+
sub.fingerprint = ''
|
198
|
+
if sub.save
|
199
|
+
I18n.t(
|
200
|
+
'keyword_handlers.subscription_management.fingerprint_unset',
|
201
|
+
email: email
|
202
|
+
)
|
203
|
+
else
|
204
|
+
I18n.t(
|
205
|
+
'keyword_handlers.subscription_management.unsetting_fingerprint_failed',
|
206
|
+
email: email,
|
207
|
+
errors: sub.errors.to_a.join("\n")
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
module Schleuder
|
2
|
+
class KeywordHandlersRunner
|
3
|
+
REGISTERED_KEYWORDS = {list: {}, request: {}}
|
4
|
+
RESERVED_KEYWORDS = %w[list-name]
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_reader :keywords
|
8
|
+
|
9
|
+
def register_keyword(type:, keyword:, handler_class:, handler_method:, aliases:)
|
10
|
+
assert_valid_input!(type: type, keyword: keyword, handler_class: handler_class, handler_method: handler_method)
|
11
|
+
|
12
|
+
identifiers = [keyword] + Array(aliases)
|
13
|
+
identifiers.each do |identifier|
|
14
|
+
REGISTERED_KEYWORDS[type.to_sym][identifier.to_s.dasherize] = {
|
15
|
+
klass: handler_class,
|
16
|
+
method: handler_method.to_sym
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run(type:, list:, mail:)
|
22
|
+
list.logger.debug "Starting #{self}"
|
23
|
+
assert_valid_type!(type)
|
24
|
+
load_additional_keyword_handlers
|
25
|
+
|
26
|
+
error = check_unknown_keywords(mail, type)
|
27
|
+
return error if error.present?
|
28
|
+
|
29
|
+
error = check_mandatory_keywords(mail, list)
|
30
|
+
return [error] if error.present?
|
31
|
+
|
32
|
+
output = mail.keywords.map do |keyword, arguments|
|
33
|
+
if ! is_reserved_keyword?(keyword)
|
34
|
+
run_handler(mail, list, type, keyword.to_s.dasherize, Array(arguments))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
output.flatten.compact
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
|
45
|
+
def check_unknown_keywords(mail, type)
|
46
|
+
known_keywords = REGISTERED_KEYWORDS[type.to_sym].keys + RESERVED_KEYWORDS
|
47
|
+
given_keywords = mail.keywords.map(&:first)
|
48
|
+
unknown_keywords = given_keywords - known_keywords
|
49
|
+
if unknown_keywords.present?
|
50
|
+
error_messages = unknown_keywords.map do |keyword|
|
51
|
+
I18n.t('errors.unknown_keyword', keyword: keyword)
|
52
|
+
end
|
53
|
+
return error_messages
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def run_handler(mail, list, type, keyword, arguments)
|
58
|
+
list.logger.debug "run_handler() with keyword '#{keyword}'"
|
59
|
+
|
60
|
+
if list.admin_only?(keyword) && !list.from_admin?(mail)
|
61
|
+
return Schleuder::Errors::KeywordAdminOnly.new(keyword).to_s
|
62
|
+
end
|
63
|
+
|
64
|
+
keyword_data = REGISTERED_KEYWORDS[type.to_sym][keyword]
|
65
|
+
handler_class = keyword_data[:klass]
|
66
|
+
handler_method = keyword_data[:method]
|
67
|
+
output = handler_class.new(mail: mail, arguments: arguments).send(handler_method)
|
68
|
+
|
69
|
+
if list.keywords_admin_notify.include?(keyword)
|
70
|
+
notify_admins(type, mail, list, keyword, arguments, output)
|
71
|
+
end
|
72
|
+
return output
|
73
|
+
rescue => exc
|
74
|
+
# Log to system, this information is probably more useful for
|
75
|
+
# system-admins than for list-admins.
|
76
|
+
Schleuder.logger.error(exc.message_with_backtrace)
|
77
|
+
I18n.t('keyword_handlers.handler_failed', keyword: keyword)
|
78
|
+
end
|
79
|
+
|
80
|
+
def notify_admins(type, mail, list, keyword, arguments, response)
|
81
|
+
msg = I18n.t("keyword_handlers.keyword_admin_notify.#{type}",
|
82
|
+
sender: mail.signer,
|
83
|
+
keyword: keyword,
|
84
|
+
arguments: arguments.join(' '),
|
85
|
+
response: Array(response).join("\n\n")
|
86
|
+
)
|
87
|
+
list.logger.notify_admin(msg, nil, 'Notice')
|
88
|
+
end
|
89
|
+
|
90
|
+
def load_additional_keyword_handlers
|
91
|
+
Dir["#{Schleuder::Conf.keyword_handlers_dir}/*.rb"].each do |file|
|
92
|
+
load file
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def check_mandatory_keywords(mail, list)
|
97
|
+
return nil if mail.keywords.blank?
|
98
|
+
|
99
|
+
listname_keyword = mail.keywords.assoc('list-name')
|
100
|
+
if listname_keyword.blank?
|
101
|
+
return I18n.t(:missing_listname_keyword_error)
|
102
|
+
else
|
103
|
+
listname_args = listname_keyword.last
|
104
|
+
if ! [list.email, list.request_address].include?(listname_args.first)
|
105
|
+
return I18n.t(:wrong_listname_keyword_error)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def is_reserved_keyword?(keyword)
|
111
|
+
RESERVED_KEYWORDS.include?(keyword)
|
112
|
+
end
|
113
|
+
|
114
|
+
def assert_valid_input!(type:, keyword:, handler_class:, handler_method:)
|
115
|
+
assert_valid_type!(type)
|
116
|
+
assert_valid_keyword!(keyword)
|
117
|
+
assert_valid_handler_class!(handler_class)
|
118
|
+
assert_valid_handler_method!(handler_method)
|
119
|
+
end
|
120
|
+
|
121
|
+
def assert_valid_type!(type)
|
122
|
+
if ! REGISTERED_KEYWORDS.keys.include?(type)
|
123
|
+
raise ArgumentError.new("Argument must be one of #{REGISTERED_KEYWORDS.keys.inspect}, got: #{type.inspect}")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def assert_valid_keyword!(keyword)
|
128
|
+
if keyword.blank?
|
129
|
+
raise ArgumentError.new("Invalid keyword: #{keyword.inspect}")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def assert_valid_handler_class!(handler_class)
|
134
|
+
if ! handler_class.is_a?(Class)
|
135
|
+
raise ArgumentError.new("Invalid input for handler_class: #{handler_class.inspect} is not a class")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def assert_valid_handler_method!(handler_method)
|
140
|
+
if handler_method.blank?
|
141
|
+
raise ArgumentError.new("Invalid input for handler_method: #{handler_method.inspect} is not a valid method name")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/schleuder/list.rb
CHANGED
@@ -29,14 +29,14 @@ module Schleuder
|
|
29
29
|
:keywords_admin_notify do |record, attrib, value|
|
30
30
|
value.each do |word|
|
31
31
|
if word !~ /\A[a-z_-]+\z/i
|
32
|
-
record.errors.add(attrib, I18n.t(
|
32
|
+
record.errors.add(attrib, I18n.t('errors.invalid_characters'))
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
36
36
|
validates_each :bounces_drop_on_headers do |record, attrib, value|
|
37
37
|
value.each do |key, val|
|
38
38
|
if key.to_s !~ /\A[a-z-]+\z/i || val.to_s !~ /\A[[:graph:]]+\z/i
|
39
|
-
record.errors.add(attrib, I18n.t(
|
39
|
+
record.errors.add(attrib, I18n.t('errors.invalid_characters'))
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -67,6 +67,22 @@ module Schleuder
|
|
67
67
|
with: /\A[[:graph:]\s]*\z/i,
|
68
68
|
}
|
69
69
|
|
70
|
+
# Some users find it quite confusing when they click "reply-to" and the mail client
|
71
|
+
# doesn't reply to the sender of the mail but the whole mailing list. For those lists it can be
|
72
|
+
# considered to set this value to true. The recipients will then receive e-mails
|
73
|
+
# where the "reply-to" header will contain the reply-to address
|
74
|
+
# of the sender and thus reply to the sender when clicking "reply-to" in a client.
|
75
|
+
# If no "reply-to" is set, the "from"-header of the original sender will be used.
|
76
|
+
# The default is off.
|
77
|
+
validates :set_reply_to_to_sender, boolean: true
|
78
|
+
|
79
|
+
# Some users find it confusing when the "from" does not contain the original sender
|
80
|
+
# but the list address. For those lists it can be considered to set the munged header.
|
81
|
+
# This will result in a "from"-header like this: "originalsender@original.com via list@list.com"
|
82
|
+
# The default is off.
|
83
|
+
validates :munge_from, boolean: true
|
84
|
+
|
85
|
+
|
70
86
|
default_scope { order(:email) }
|
71
87
|
|
72
88
|
def self.configurable_attributes
|
@@ -92,10 +108,6 @@ module Schleuder
|
|
92
108
|
subscriptions.where(admin: true)
|
93
109
|
end
|
94
110
|
|
95
|
-
def subscriptions_without_fingerprint
|
96
|
-
subscriptions.without_fingerprint
|
97
|
-
end
|
98
|
-
|
99
111
|
def key(fingerprint=self.fingerprint)
|
100
112
|
keys(fingerprint).first
|
101
113
|
end
|
@@ -108,17 +120,6 @@ module Schleuder
|
|
108
120
|
gpg.find_keys(identifier, secret_only)
|
109
121
|
end
|
110
122
|
|
111
|
-
# TODO: find better name for this method. It does more than the current
|
112
|
-
# name suggests (filtering for capability).
|
113
|
-
def distinct_key(identifier)
|
114
|
-
keys = keys(identifier).select { |key| key.usable_for?(:encrypt) }
|
115
|
-
if keys.size == 1
|
116
|
-
return keys.first
|
117
|
-
else
|
118
|
-
return nil
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
123
|
def import_key(importable)
|
123
124
|
gpg.keyimport(importable)
|
124
125
|
end
|
@@ -177,19 +178,19 @@ module Schleuder
|
|
177
178
|
end
|
178
179
|
|
179
180
|
text = ''
|
180
|
-
expiring.each do |key,days|
|
181
|
-
text << I18n.t('key_expires',
|
181
|
+
expiring.each do |key, days|
|
182
|
+
text << I18n.t('key_expires',
|
182
183
|
days: days,
|
183
|
-
|
184
|
-
|
184
|
+
key_summary: key.summary
|
185
|
+
)
|
185
186
|
text << "\n"
|
186
187
|
end
|
187
188
|
|
188
|
-
unusable.each do |key,usability_issue|
|
189
|
-
text << I18n.t('key_unusable',
|
189
|
+
unusable.each do |key, usability_issue|
|
190
|
+
text << I18n.t('key_unusable',
|
190
191
|
usability_issue: usability_issue,
|
191
|
-
|
192
|
-
|
192
|
+
key_summary: key.summary
|
193
|
+
)
|
193
194
|
text << "\n"
|
194
195
|
end
|
195
196
|
text
|
@@ -203,19 +204,6 @@ module Schleuder
|
|
203
204
|
gpg.fetch_key(input)
|
204
205
|
end
|
205
206
|
|
206
|
-
def pin_keys
|
207
|
-
updated_emails = subscriptions_without_fingerprint.collect do |subscription|
|
208
|
-
key = distinct_key(subscription.email)
|
209
|
-
if key
|
210
|
-
subscription.update(fingerprint: key.fingerprint)
|
211
|
-
"#{subscription.email}: #{key.fingerprint}"
|
212
|
-
else
|
213
|
-
nil
|
214
|
-
end
|
215
|
-
end
|
216
|
-
updated_emails.compact.join("\n")
|
217
|
-
end
|
218
|
-
|
219
207
|
def self.by_recipient(recipient)
|
220
208
|
listname = recipient.gsub(/-(sendkey|request|owner|bounce)@/, '@')
|
221
209
|
where(email: listname).first
|
@@ -352,7 +340,7 @@ module Schleuder
|
|
352
340
|
end
|
353
341
|
|
354
342
|
def send_to_subscriptions(mail, incoming_mail=nil)
|
355
|
-
logger.debug
|
343
|
+
logger.debug 'Sending to subscriptions.'
|
356
344
|
mail.add_internal_footer!
|
357
345
|
self.subscriptions.each do |subscription|
|
358
346
|
begin
|
@@ -367,7 +355,7 @@ module Schleuder
|
|
367
355
|
next
|
368
356
|
end
|
369
357
|
|
370
|
-
subscription.send_mail(mail)
|
358
|
+
subscription.send_mail(mail, incoming_mail)
|
371
359
|
|
372
360
|
rescue => exc
|
373
361
|
msg = I18n.t('errors.delivery_error',
|