schleuder 3.2.2 → 3.3.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 +4 -4
- data/README.md +20 -10
- data/Rakefile +16 -8
- 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/schema.rb +2 -2
- data/etc/list-defaults.yml +4 -2
- 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 +2 -3
- data/lib/schleuder/cli.rb +24 -0
- 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/{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 +1 -1
- data/lib/schleuder/gpgme/import_status.rb +13 -7
- data/lib/schleuder/gpgme/key.rb +4 -0
- data/lib/schleuder/list.rb +7 -4
- data/lib/schleuder/mail/encrypted_part.rb +14 -0
- data/lib/schleuder/mail/gpg.rb +15 -0
- data/lib/schleuder/mail/message.rb +70 -30
- data/lib/schleuder/plugins/key_management.rb +32 -7
- data/lib/schleuder/plugins/subscription_management.rb +70 -3
- data/lib/schleuder/runner.rb +19 -8
- 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 +96 -8
- data/locales/en.yml +102 -10
- metadata +48 -27
- 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
data/lib/schleuder/gpgme/ctx.rb
CHANGED
@@ -19,7 +19,7 @@ module GPGME
|
|
19
19
|
case import_result.imports.size
|
20
20
|
when 1
|
21
21
|
import_status = import_result.imports.first
|
22
|
-
if import_status.action == '
|
22
|
+
if import_status.action == 'error'
|
23
23
|
[nil, "Key #{import_status.fpr} could not be imported!"]
|
24
24
|
else
|
25
25
|
[import_status.fpr, nil]
|
@@ -2,17 +2,23 @@ module GPGME
|
|
2
2
|
class ImportStatus
|
3
3
|
attr_reader :action
|
4
4
|
|
5
|
-
# Unfortunately in initialize() @status and @result are not yet
|
5
|
+
# Unfortunately in initialize() @status and @result are not yet initialized.
|
6
6
|
def set_action
|
7
|
-
@action ||= if self.
|
8
|
-
'imported'
|
9
|
-
elsif self.result == 0
|
10
|
-
'unchanged'
|
11
|
-
else
|
7
|
+
@action ||= if self.result > 0
|
12
8
|
# An error happened.
|
13
9
|
# TODO: Give details by going through the list of errors in
|
14
10
|
# "gpg-errors.h" and find out which is present here.
|
15
|
-
'
|
11
|
+
'error'
|
12
|
+
else
|
13
|
+
# TODO: refactor with Ctx#translate_import_data
|
14
|
+
case self.status
|
15
|
+
when 0
|
16
|
+
'unchanged'
|
17
|
+
when IMPORT_NEW
|
18
|
+
'imported'
|
19
|
+
else
|
20
|
+
'updated'
|
21
|
+
end
|
16
22
|
end
|
17
23
|
self
|
18
24
|
end
|
data/lib/schleuder/gpgme/key.rb
CHANGED
@@ -208,5 +208,9 @@ module GPGME
|
|
208
208
|
pinentry = File.join(ENV['SCHLEUDER_ROOT'], 'bin', 'pinentry-clearpassphrase')
|
209
209
|
GPGME::Ctx.spawn_daemon('gpg-agent', "--use-standard-socket --pinentry-program #{pinentry}")
|
210
210
|
end
|
211
|
+
|
212
|
+
def self.valid_fingerprint?(fp)
|
213
|
+
fp =~ Schleuder::Conf::FINGERPRINT_REGEXP
|
214
|
+
end
|
211
215
|
end
|
212
216
|
end
|
data/lib/schleuder/list.rb
CHANGED
@@ -124,7 +124,7 @@ module Schleuder
|
|
124
124
|
|
125
125
|
def import_key_and_find_fingerprint(key_material)
|
126
126
|
return nil if key_material.blank?
|
127
|
-
|
127
|
+
|
128
128
|
import_result = import_key(key_material)
|
129
129
|
gpg.interpret_import_result(import_result)
|
130
130
|
end
|
@@ -251,9 +251,8 @@ module Schleuder
|
|
251
251
|
end
|
252
252
|
|
253
253
|
def fingerprint=(arg)
|
254
|
-
# Strip whitespace from incoming arg.
|
255
254
|
if arg
|
256
|
-
write_attribute(:fingerprint, arg.gsub(/\s*/, '').chomp)
|
255
|
+
write_attribute(:fingerprint, arg.gsub(/\s*/, '').gsub(/^0x/, '').chomp.upcase)
|
257
256
|
end
|
258
257
|
end
|
259
258
|
|
@@ -346,7 +345,11 @@ module Schleuder
|
|
346
345
|
mail.add_internal_footer!
|
347
346
|
self.subscriptions.each do |subscription|
|
348
347
|
begin
|
349
|
-
subscription.
|
348
|
+
if subscription.delivery_enabled
|
349
|
+
subscription.send_mail(mail)
|
350
|
+
else
|
351
|
+
logger.info "Not sending to #{subscription.email}: delivery is disabled."
|
352
|
+
end
|
350
353
|
rescue => exc
|
351
354
|
msg = I18n.t('errors.delivery_error',
|
352
355
|
{ email: subscription.email, error: exc.to_s })
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Mail
|
2
|
+
module Gpg
|
3
|
+
class EncryptedPart < Mail::Part
|
4
|
+
alias_method :initialize_mailgpg, :initialize
|
5
|
+
|
6
|
+
def initialize(cleartext_mail, options = {})
|
7
|
+
if cleartext_mail.protected_headers_subject
|
8
|
+
cleartext_mail.content_type_parameters['protected-headers'] = 'v1'
|
9
|
+
end
|
10
|
+
initialize_mailgpg(cleartext_mail, options)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Mail
|
2
|
+
module Gpg
|
3
|
+
class << self
|
4
|
+
alias_method :encrypt_mailgpg, :encrypt
|
5
|
+
|
6
|
+
def encrypt(cleartext_mail, options={})
|
7
|
+
encrypted_mail = encrypt_mailgpg(cleartext_mail, options)
|
8
|
+
if cleartext_mail.protected_headers_subject
|
9
|
+
encrypted_mail.subject = cleartext_mail.protected_headers_subject
|
10
|
+
end
|
11
|
+
encrypted_mail
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -16,6 +16,7 @@ module Mail
|
|
16
16
|
attr_accessor :recipient
|
17
17
|
attr_accessor :original_message
|
18
18
|
attr_accessor :list
|
19
|
+
attr_accessor :protected_headers_subject
|
19
20
|
|
20
21
|
# TODO: This should be in initialize(), but I couldn't understand the
|
21
22
|
# strange errors about wrong number of arguments when overriding
|
@@ -23,12 +24,9 @@ module Mail
|
|
23
24
|
def setup
|
24
25
|
if self.encrypted?
|
25
26
|
new = self.decrypt(verify: true)
|
26
|
-
## Work around a bug in mail-gpg: when decrypting pgp/mime the
|
27
|
-
## Date-header is not copied.
|
28
|
-
#new.date ||= self.date
|
29
27
|
# Test if there's a signed multipart inside the ciphertext
|
30
28
|
# ("encapsulated" format of pgp/mime).
|
31
|
-
if new
|
29
|
+
if encapsulated_signed?(new)
|
32
30
|
new = new.verify
|
33
31
|
end
|
34
32
|
elsif self.signed?
|
@@ -49,6 +47,20 @@ module Mail
|
|
49
47
|
self.dynamic_pseudoheaders.each do |str|
|
50
48
|
new.add_pseudoheader(str)
|
51
49
|
end
|
50
|
+
|
51
|
+
# Store previously protected subject for later access.
|
52
|
+
# mail-gpg pulls headers from the decrypted mime parts "up" into the main
|
53
|
+
# headers, which reveals protected subjects.
|
54
|
+
if self.subject != new.subject
|
55
|
+
new.protected_headers_subject = self.subject.dup
|
56
|
+
|
57
|
+
# Delete the protected headers which might leak information.
|
58
|
+
if new.parts.first.content_type == "text/rfc822-headers; protected-headers=v1"
|
59
|
+
new.parts.shift
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
52
64
|
new
|
53
65
|
end
|
54
66
|
|
@@ -58,6 +70,7 @@ module Mail
|
|
58
70
|
clean.gpg self.list.gpg_sign_options
|
59
71
|
clean.from = list.email
|
60
72
|
clean.subject = self.subject
|
73
|
+
clean.protected_headers_subject = self.protected_headers_subject
|
61
74
|
|
62
75
|
clean.add_msgids(list, self)
|
63
76
|
clean.add_list_headers(list)
|
@@ -69,6 +82,13 @@ module Mail
|
|
69
82
|
clean.add_part new_part
|
70
83
|
end
|
71
84
|
|
85
|
+
if self.protected_headers_subject.present?
|
86
|
+
new_part = Mail::Part.new
|
87
|
+
new_part.content_type = "text/rfc822-headers; protected-headers=v1"
|
88
|
+
new_part.body = "Subject: #{self.subject}\n"
|
89
|
+
clean.add_part new_part
|
90
|
+
end
|
91
|
+
|
72
92
|
# Attach body or mime-parts in a new wrapper-part, to preserve the
|
73
93
|
# original mime-structure.
|
74
94
|
# We can't use self.to_s here — that includes all the headers we *don't*
|
@@ -203,15 +223,19 @@ module Mail
|
|
203
223
|
end
|
204
224
|
|
205
225
|
@keywords = []
|
226
|
+
look_for_keywords = true
|
206
227
|
lines = part.decoded.lines.map do |line|
|
207
228
|
# TODO: Find multiline arguments (add-key). Currently add-key has to
|
208
229
|
# read the whole body and hope for the best.
|
209
|
-
if line.match(/^x-([^:\s]*)[:\s]*(.*)/i)
|
210
|
-
command =
|
211
|
-
arguments =
|
230
|
+
if look_for_keywords && (m = line.match(/^x-([^:\s]*)[:\s]*(.*)/i))
|
231
|
+
command = m[1].strip.downcase
|
232
|
+
arguments = m[2].to_s.strip.downcase.split(/[,; ]{1,}/)
|
212
233
|
@keywords << [command, arguments]
|
213
234
|
nil
|
214
235
|
else
|
236
|
+
if look_for_keywords && line.match(/\S+/i)
|
237
|
+
look_for_keywords = false
|
238
|
+
end
|
215
239
|
line
|
216
240
|
end
|
217
241
|
end
|
@@ -259,17 +283,7 @@ module Mail
|
|
259
283
|
@dynamic_pseudoheaders || []
|
260
284
|
end
|
261
285
|
|
262
|
-
def
|
263
|
-
if @standard_pseudoheaders.present?
|
264
|
-
return @standard_pseudoheaders
|
265
|
-
else
|
266
|
-
@standard_pseudoheaders = []
|
267
|
-
end
|
268
|
-
|
269
|
-
Array(list.headers_to_meta).each do |field|
|
270
|
-
@standard_pseudoheaders << make_pseudoheader(field.to_s, self.header[field.to_s])
|
271
|
-
end
|
272
|
-
|
286
|
+
def signature_state
|
273
287
|
# Careful to add information about the incoming signature. GPGME
|
274
288
|
# throws exceptions if it doesn't know the key.
|
275
289
|
if self.signature.present?
|
@@ -277,22 +291,41 @@ module Mail
|
|
277
291
|
# for that manually and provide our own fallback. (Calling
|
278
292
|
# `signature.key` results in an EOFError in that case.)
|
279
293
|
if signing_key.present?
|
280
|
-
|
294
|
+
signature_state = signature.to_s
|
281
295
|
else
|
282
|
-
|
283
|
-
msg = "Unknown signature by unknown key 0x#{self.signature.fingerprint}"
|
296
|
+
signature_state = I18n.t("signature_states.unknown", fingerprint: self.signature.fingerprint)
|
284
297
|
end
|
285
298
|
else
|
286
|
-
|
287
|
-
|
299
|
+
signature_state = I18n.t("signature_states.unsigned")
|
300
|
+
end
|
301
|
+
signature_state
|
302
|
+
end
|
303
|
+
|
304
|
+
def encryption_state
|
305
|
+
if was_encrypted?
|
306
|
+
encryption_state = I18n.t("encryption_states.encrypted")
|
307
|
+
else
|
308
|
+
encryption_state = I18n.t("encryption_states.unencrypted")
|
309
|
+
end
|
310
|
+
encryption_state
|
311
|
+
end
|
312
|
+
|
313
|
+
def standard_pseudoheaders(list)
|
314
|
+
if @standard_pseudoheaders.present?
|
315
|
+
return @standard_pseudoheaders
|
316
|
+
else
|
317
|
+
@standard_pseudoheaders = []
|
318
|
+
end
|
319
|
+
|
320
|
+
Array(list.headers_to_meta).each do |field|
|
321
|
+
value = case field.to_s
|
322
|
+
when 'sig' then signature_state
|
323
|
+
when 'enc' then encryption_state
|
324
|
+
else self.header[field.to_s]
|
325
|
+
end
|
326
|
+
@standard_pseudoheaders << make_pseudoheader(field.to_s, value)
|
288
327
|
end
|
289
|
-
@standard_pseudoheaders << make_pseudoheader(:sig, msg)
|
290
328
|
|
291
|
-
# TODO: I18n
|
292
|
-
@standard_pseudoheaders << make_pseudoheader(
|
293
|
-
:enc,
|
294
|
-
was_encrypted? ? 'Encrypted' : 'Unencrypted'
|
295
|
-
)
|
296
329
|
|
297
330
|
@standard_pseudoheaders
|
298
331
|
end
|
@@ -316,7 +349,7 @@ module Mail
|
|
316
349
|
if list.include_list_headers
|
317
350
|
self['List-Id'] = "<#{list.email.gsub('@', '.')}>"
|
318
351
|
self['List-Owner'] = "<mailto:#{list.owner_address}> (Use list's public key)"
|
319
|
-
self['List-Help'] = '<https://schleuder.
|
352
|
+
self['List-Help'] = '<https://schleuder.org/>'
|
320
353
|
|
321
354
|
postmsg = if list.receive_admin_only
|
322
355
|
"NO (Admins only)"
|
@@ -410,6 +443,13 @@ module Mail
|
|
410
443
|
|
411
444
|
private
|
412
445
|
|
446
|
+
# mail.signed? throws an error if it finds
|
447
|
+
# pgp boundaries, so we must use the Mail::Gpg
|
448
|
+
# methods.
|
449
|
+
def encapsulated_signed?(mail)
|
450
|
+
(mail.verify_result.nil? || mail.verify_result.signatures.empty?) && \
|
451
|
+
(Mail::Gpg.signed_mime?(mail) || Mail::Gpg.signed_inline?(mail))
|
452
|
+
end
|
413
453
|
|
414
454
|
def add_footer!(footer_attribute)
|
415
455
|
if self.list.blank? || self.list.send(footer_attribute).to_s.empty?
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Schleuder
|
2
2
|
module RequestPlugins
|
3
3
|
def self.add_key(arguments, list, mail)
|
4
|
-
out = [I18n.t('plugins.key_management.import_result')]
|
5
4
|
|
6
5
|
if mail.has_attachments?
|
7
6
|
results = self.import_keys_from_attachments(list, mail)
|
@@ -9,15 +8,35 @@ module Schleuder
|
|
9
8
|
results = [self.import_key_from_body(list, mail)]
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
import_stati = results.compact.collect(&:imports).flatten
|
12
|
+
|
13
|
+
if import_stati.blank?
|
14
|
+
return I18n.t('plugins.key_management.no_imports')
|
15
15
|
end
|
16
16
|
|
17
|
-
out
|
17
|
+
out = []
|
18
|
+
|
19
|
+
import_stati.each do |import_status|
|
20
|
+
if import_status.action == 'error'
|
21
|
+
out << I18n.t("plugins.key_management.key_import_status.error", fingerprint: import_status.fingerprint)
|
22
|
+
else
|
23
|
+
key = list.gpg.find_distinct_key(import_status.fingerprint)
|
24
|
+
if key
|
25
|
+
out << I18n.t("plugins.key_management.key_import_status.#{import_status.action}", key_oneline: key.oneline)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
out.join("\n\n")
|
18
31
|
end
|
19
32
|
|
20
33
|
def self.delete_key(arguments, list, mail)
|
34
|
+
if arguments.blank?
|
35
|
+
return I18n.t(
|
36
|
+
"plugins.key_management.delete_key_requires_arguments"
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
21
40
|
arguments.map do |argument|
|
22
41
|
keys = list.keys(argument)
|
23
42
|
case keys.size
|
@@ -26,9 +45,9 @@ module Schleuder
|
|
26
45
|
when 1
|
27
46
|
begin
|
28
47
|
keys.first.delete!
|
29
|
-
I18n.t('plugins.key_management.deleted', key_string: keys.first.
|
48
|
+
I18n.t('plugins.key_management.deleted', key_string: keys.first.oneline)
|
30
49
|
rescue GPGME::Error::Conflict
|
31
|
-
I18n.t('plugins.key_management.not_deletable', key_string: keys.first.
|
50
|
+
I18n.t('plugins.key_management.not_deletable', key_string: keys.first.oneline)
|
32
51
|
end
|
33
52
|
else
|
34
53
|
I18n.t('errors.too_many_matching_keys', {
|
@@ -71,6 +90,12 @@ module Schleuder
|
|
71
90
|
end
|
72
91
|
|
73
92
|
def self.fetch_key(arguments, list, mail)
|
93
|
+
if arguments.blank?
|
94
|
+
return I18n.t(
|
95
|
+
"plugins.key_management.fetch_key_requires_arguments"
|
96
|
+
)
|
97
|
+
end
|
98
|
+
|
74
99
|
arguments.map do |argument|
|
75
100
|
list.fetch_keys(argument)
|
76
101
|
end
|
@@ -1,12 +1,18 @@
|
|
1
1
|
module Schleuder
|
2
2
|
module RequestPlugins
|
3
3
|
def self.subscribe(arguments, list, mail)
|
4
|
+
if arguments.blank?
|
5
|
+
return I18n.t(
|
6
|
+
"plugins.subscription_management.subscribe_requires_arguments"
|
7
|
+
)
|
8
|
+
end
|
9
|
+
|
4
10
|
email = arguments.shift
|
5
11
|
|
6
12
|
if arguments.present?
|
7
13
|
# Collect all arguments that look like fingerprint-material
|
8
14
|
fingerprint = ''
|
9
|
-
while arguments.first.present? && arguments.first.match(
|
15
|
+
while arguments.first.present? && arguments.first.match(/^(0x)?[a-f0-9]+$/i)
|
10
16
|
fingerprint << arguments.shift
|
11
17
|
end
|
12
18
|
# Use possibly remaining args as flags.
|
@@ -37,6 +43,13 @@ module Schleuder
|
|
37
43
|
# If no address was given we unsubscribe the sender.
|
38
44
|
email = arguments.first.presence || mail.signer.email
|
39
45
|
|
46
|
+
# Refuse to unsubscribe the last admin.
|
47
|
+
if list.admins.size == 1 && list.admins.first.email == email
|
48
|
+
return I18n.t(
|
49
|
+
"plugins.subscription_management.cannot_unsubscribe_last_admin", email: email
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
40
53
|
# TODO: May signers have multiple UIDs? We don't match those currently.
|
41
54
|
if ! list.from_admin?(mail) && email != mail.signer.email
|
42
55
|
# Only admins may unsubscribe others.
|
@@ -98,6 +111,12 @@ module Schleuder
|
|
98
111
|
end
|
99
112
|
|
100
113
|
def self.set_fingerprint(arguments, list, mail)
|
114
|
+
if arguments.blank?
|
115
|
+
return I18n.t(
|
116
|
+
"plugins.subscription_management.set_fingerprint_requires_arguments"
|
117
|
+
)
|
118
|
+
end
|
119
|
+
|
101
120
|
if arguments.first.match(/@/)
|
102
121
|
if arguments.first == mail.signer.email || list.from_admin?(mail)
|
103
122
|
email = arguments.shift
|
@@ -118,8 +137,15 @@ module Schleuder
|
|
118
137
|
)
|
119
138
|
end
|
120
139
|
|
121
|
-
|
140
|
+
fingerprint = arguments.join
|
141
|
+
unless GPGME::Key.valid_fingerprint?(fingerprint)
|
142
|
+
return I18n.t(
|
143
|
+
"plugins.subscription_management.set_fingerprint_requires_valid_fingerprint",
|
144
|
+
fingerprint: fingerprint
|
145
|
+
)
|
146
|
+
end
|
122
147
|
|
148
|
+
sub.fingerprint = fingerprint
|
123
149
|
if sub.save
|
124
150
|
I18n.t(
|
125
151
|
"plugins.subscription_management.fingerprint_set",
|
@@ -130,7 +156,48 @@ module Schleuder
|
|
130
156
|
I18n.t(
|
131
157
|
"plugins.subscription_management.setting_fingerprint_failed",
|
132
158
|
email: email,
|
133
|
-
fingerprint:
|
159
|
+
fingerprint: sub.fingerprint,
|
160
|
+
errors: sub.errors.to_a.join("\n")
|
161
|
+
)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.unset_fingerprint(arguments, list, mail)
|
166
|
+
if arguments.blank?
|
167
|
+
return I18n.t(
|
168
|
+
"plugins.subscription_management.unset_fingerprint_requires_arguments"
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
172
|
+
email = arguments.first
|
173
|
+
unless email == mail.signer.email || list.from_admin?(mail)
|
174
|
+
return I18n.t(
|
175
|
+
"plugins.subscription_management.unset_fingerprint_only_self"
|
176
|
+
)
|
177
|
+
end
|
178
|
+
if email == mail.signer.email && list.from_admin?(mail) && arguments.last != 'force'
|
179
|
+
return I18n.t(
|
180
|
+
"plugins.subscription_management.unset_fingerprint_requires_arguments"
|
181
|
+
)
|
182
|
+
end
|
183
|
+
|
184
|
+
sub = list.subscriptions.where(email: email).first
|
185
|
+
if sub.blank?
|
186
|
+
return I18n.t(
|
187
|
+
"plugins.subscription_management.is_not_subscribed", email: email
|
188
|
+
)
|
189
|
+
end
|
190
|
+
|
191
|
+
sub.fingerprint = ''
|
192
|
+
if sub.save
|
193
|
+
I18n.t(
|
194
|
+
"plugins.subscription_management.fingerprint_unset",
|
195
|
+
email: email
|
196
|
+
)
|
197
|
+
else
|
198
|
+
I18n.t(
|
199
|
+
"plugins.subscription_management.unsetting_fingerprint_failed",
|
200
|
+
email: email,
|
134
201
|
errors: sub.errors.to_a.join("\n")
|
135
202
|
)
|
136
203
|
end
|