schleuder 3.6.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/Rakefile +12 -14
- 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 +1 -1
- data/db/schema.rb +45 -47
- 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 +9 -11
- data/lib/schleuder/cli.rb +9 -188
- 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 +3 -3
- 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 -35
- 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 +11 -39
- data/lib/schleuder/list_builder.rb +16 -5
- data/lib/schleuder/listlogger.rb +1 -1
- data/lib/schleuder/mail/message.rb +82 -61
- data/lib/schleuder/runner.rb +18 -16
- data/lib/schleuder/subscription.rb +9 -10
- 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 +48 -36
- data/locales/en.yml +33 -21
- metadata +117 -53
- 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
@@ -20,7 +20,7 @@ module Schleuder
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def run
|
23
|
-
Schleuder.logger.info
|
23
|
+
Schleuder.logger.info 'Building new list'
|
24
24
|
|
25
25
|
if @listname.blank? || ! @listname.match(Conf::EMAIL_REGEXP)
|
26
26
|
return [nil, {'email' => ["'#{@listname}' is not a valid email address"]}]
|
@@ -63,13 +63,13 @@ module Schleuder
|
|
63
63
|
|
64
64
|
def gpg
|
65
65
|
@gpg_ctx ||= begin
|
66
|
-
ENV[
|
66
|
+
ENV['GNUPGHOME'] = @list_dir
|
67
67
|
GPGME::Ctx.new
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
71
|
def create_key(list)
|
72
|
-
Schleuder.logger.info
|
72
|
+
Schleuder.logger.info 'Generating key-pair, this could take a while...'
|
73
73
|
gpg.generate_key(key_params(list))
|
74
74
|
|
75
75
|
# Get key without knowing the fingerprint yet.
|
@@ -88,14 +88,14 @@ module Schleuder
|
|
88
88
|
def adduids(list, key)
|
89
89
|
# Add UIDs for -owner and -request.
|
90
90
|
[list.request_address, list.owner_address].each do |address|
|
91
|
-
err =
|
91
|
+
err = add_uid_to_key(list, address)
|
92
92
|
if err.present?
|
93
93
|
raise err
|
94
94
|
end
|
95
95
|
end
|
96
96
|
# Go through list.key() to re-fetch the key from the keyring, otherwise
|
97
97
|
# we don't see the new UIDs.
|
98
|
-
errors =
|
98
|
+
errors = set_primary_uid_of_key(list)
|
99
99
|
if errors.present?
|
100
100
|
raise errors
|
101
101
|
end
|
@@ -135,5 +135,16 @@ module Schleuder
|
|
135
135
|
end
|
136
136
|
end
|
137
137
|
|
138
|
+
def set_primary_uid_of_key(list)
|
139
|
+
errors, _ = GPGME::Ctx.gpgcli("--quick-set-primary-uid #{list.email} '#{list.email} <#{list.email}>'")
|
140
|
+
errors.join
|
141
|
+
end
|
142
|
+
|
143
|
+
def add_uid_to_key(list, email)
|
144
|
+
# Specifying the key via fingerprint apparently doesn't work.
|
145
|
+
errors, _ = GPGME::Ctx.gpgcli("--quick-adduid #{list.email} '#{list.email} <#{email}>'")
|
146
|
+
errors.join
|
147
|
+
end
|
148
|
+
|
138
149
|
end
|
139
150
|
end
|
data/lib/schleuder/listlogger.rb
CHANGED
@@ -17,7 +17,7 @@ module Schleuder
|
|
17
17
|
if logfiles_to_keep < 1
|
18
18
|
logfiles_to_keep = list.class.column_defaults['logfiles_to_keep']
|
19
19
|
end
|
20
|
-
suffix_now = Time.now.strftime(
|
20
|
+
suffix_now = Time.now.strftime('%Y%m%d').to_i
|
21
21
|
del_older_than = suffix_now - logfiles_to_keep
|
22
22
|
Pathname.glob("#{list.logfile}.????????").each do |file|
|
23
23
|
if file.basename.to_s.match(/\.([0-9]{8})$/)
|
@@ -24,7 +24,9 @@ module Mail
|
|
24
24
|
# Message#initialize.
|
25
25
|
def setup
|
26
26
|
if self.encrypted?
|
27
|
-
|
27
|
+
# Specify 'loopback'-pinentry-mode to ensure that gnupg never-ever
|
28
|
+
# tries to interactively ask for a passphrase.
|
29
|
+
new = self.decrypt(verify: true, pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK)
|
28
30
|
# Test if there's a signed multipart inside the ciphertext
|
29
31
|
# ("encapsulated" format of pgp/mime).
|
30
32
|
if encapsulated_signed?(new)
|
@@ -55,7 +57,7 @@ module Mail
|
|
55
57
|
end
|
56
58
|
|
57
59
|
# Delete the protected headers which might leak information.
|
58
|
-
if new.parts.first && new.parts.first.content_type ==
|
60
|
+
if new.parts.first && new.parts.first.content_type == 'text/rfc822-headers; protected-headers=v1'
|
59
61
|
new.parts.shift
|
60
62
|
end
|
61
63
|
|
@@ -82,7 +84,7 @@ module Mail
|
|
82
84
|
|
83
85
|
if self.protected_headers_subject.present?
|
84
86
|
new_part = Mail::Part.new
|
85
|
-
new_part.content_type =
|
87
|
+
new_part.content_type = 'text/rfc822-headers; protected-headers=v1'
|
86
88
|
new_part.body = "Subject: #{self.subject}\n"
|
87
89
|
clean.add_part new_part
|
88
90
|
end
|
@@ -141,7 +143,7 @@ module Mail
|
|
141
143
|
when 1
|
142
144
|
signatures.first
|
143
145
|
else
|
144
|
-
raise
|
146
|
+
raise 'Multiple signatures found! Cannot handle!'
|
145
147
|
end
|
146
148
|
end
|
147
149
|
|
@@ -161,8 +163,10 @@ module Mail
|
|
161
163
|
# subscription-assigned fingerprints are (should be) the ones of the
|
162
164
|
# primary keys, so we need to look up the key.
|
163
165
|
def signing_key
|
164
|
-
|
165
|
-
|
166
|
+
@signing_key ||= begin
|
167
|
+
if signature.present?
|
168
|
+
list.keys(signature.fpr).first
|
169
|
+
end
|
166
170
|
end
|
167
171
|
end
|
168
172
|
|
@@ -209,7 +213,7 @@ module Mail
|
|
209
213
|
end
|
210
214
|
|
211
215
|
def bounced?
|
212
|
-
@bounced ||= bounce_detected? || (error_status !=
|
216
|
+
@bounced ||= bounce_detected? || (error_status != 'unknown')
|
213
217
|
end
|
214
218
|
|
215
219
|
def error_status
|
@@ -224,23 +228,8 @@ module Mail
|
|
224
228
|
return []
|
225
229
|
end
|
226
230
|
|
227
|
-
@keywords =
|
228
|
-
|
229
|
-
lines = part.decoded.lines.map do |line|
|
230
|
-
# TODO: Find multiline arguments (add-key). Currently add-key has to
|
231
|
-
# read the whole body and hope for the best.
|
232
|
-
if look_for_keywords && (m = line.match(/^x-([^:\s]*)[:\s]*(.*)/i))
|
233
|
-
command = m[1].strip.downcase
|
234
|
-
arguments = m[2].to_s.strip.downcase.split(/[,; ]{1,}/)
|
235
|
-
@keywords << [command, arguments]
|
236
|
-
nil
|
237
|
-
else
|
238
|
-
if look_for_keywords && line.match(/\S+/i)
|
239
|
-
look_for_keywords = false
|
240
|
-
end
|
241
|
-
line
|
242
|
-
end
|
243
|
-
end
|
231
|
+
@keywords, lines = extract_keywords(part.decoded.lines)
|
232
|
+
new_body = lines.join
|
244
233
|
|
245
234
|
# Work around problems with re-encoding the body. If we delete the
|
246
235
|
# content-transfer-encoding prior to re-assigning the body, and let Mail
|
@@ -249,7 +238,6 @@ module Mail
|
|
249
238
|
part.content_transfer_encoding = nil
|
250
239
|
|
251
240
|
# Set the right charset on the now parsed body
|
252
|
-
new_body = lines.compact.join
|
253
241
|
part.charset = new_body.encoding.to_s
|
254
242
|
part.body = new_body
|
255
243
|
|
@@ -292,19 +280,19 @@ module Mail
|
|
292
280
|
if signing_key.present?
|
293
281
|
signature_state = signature.to_s
|
294
282
|
else
|
295
|
-
signature_state = I18n.t(
|
283
|
+
signature_state = I18n.t('signature_states.unknown', fingerprint: self.signature.fingerprint)
|
296
284
|
end
|
297
285
|
else
|
298
|
-
signature_state = I18n.t(
|
286
|
+
signature_state = I18n.t('signature_states.unsigned')
|
299
287
|
end
|
300
288
|
signature_state
|
301
289
|
end
|
302
290
|
|
303
291
|
def encryption_state
|
304
292
|
if was_encrypted?
|
305
|
-
encryption_state = I18n.t(
|
293
|
+
encryption_state = I18n.t('encryption_states.encrypted')
|
306
294
|
else
|
307
|
-
encryption_state = I18n.t(
|
295
|
+
encryption_state = I18n.t('encryption_states.unencrypted')
|
308
296
|
end
|
309
297
|
encryption_state
|
310
298
|
end
|
@@ -360,7 +348,7 @@ module Mail
|
|
360
348
|
self['List-Help'] = '<https://schleuder.org/>'
|
361
349
|
|
362
350
|
postmsg = if list.receive_admin_only
|
363
|
-
|
351
|
+
'NO (Admins only)'
|
364
352
|
elsif list.receive_authenticated_only
|
365
353
|
"<mailto:#{list.email}> (Subscribers only)"
|
366
354
|
else
|
@@ -437,7 +425,6 @@ module Mail
|
|
437
425
|
end
|
438
426
|
end
|
439
427
|
|
440
|
-
|
441
428
|
def attach_list_key!(list)
|
442
429
|
filename = "#{list.email}.asc"
|
443
430
|
self.add_file({
|
@@ -451,6 +438,40 @@ module Mail
|
|
451
438
|
|
452
439
|
private
|
453
440
|
|
441
|
+
|
442
|
+
def extract_keywords(content_lines)
|
443
|
+
keywords = []
|
444
|
+
in_keyword_block = false
|
445
|
+
found_blank_line = false
|
446
|
+
content_lines.each_with_index do |line, i|
|
447
|
+
if match = line.match(/^x-([^:\s]*)[:\s]*(.*)/i)
|
448
|
+
keyword = match[1].strip.downcase
|
449
|
+
arguments = match[2].to_s.strip.downcase.split(/[,; ]{1,}/)
|
450
|
+
keywords << [keyword, arguments]
|
451
|
+
in_keyword_block = true
|
452
|
+
|
453
|
+
# Set this line to nil to have it stripped from the message.
|
454
|
+
content_lines[i] = nil
|
455
|
+
elsif line.blank? && keywords.any?
|
456
|
+
# Look for blank lines after the first keyword had been found.
|
457
|
+
# These might mark the end of the keywords-block — unless more keywords follow.
|
458
|
+
found_blank_line = true
|
459
|
+
# Swallow the line: before the actual content begins we want to drop blank lines.
|
460
|
+
content_lines[i] = nil
|
461
|
+
# Stop interpreting the following line as argument to the previous keyword.
|
462
|
+
in_keyword_block = false
|
463
|
+
elsif in_keyword_block == true
|
464
|
+
# Interpret line as arguments to the previous keyword.
|
465
|
+
keywords[-1][-1] += line.downcase.strip.split(/[,; ]{1,}/)
|
466
|
+
content_lines[i] = nil
|
467
|
+
elsif found_blank_line
|
468
|
+
# Any line that isn't blank and does not start with "x-" stops the keyword parsing.
|
469
|
+
break
|
470
|
+
end
|
471
|
+
end
|
472
|
+
[keywords, content_lines.compact]
|
473
|
+
end
|
474
|
+
|
454
475
|
# mail.signed? throws an error if it finds
|
455
476
|
# pgp boundaries, so we must use the Mail::Gpg
|
456
477
|
# methods.
|
@@ -480,7 +501,7 @@ module Mail
|
|
480
501
|
end
|
481
502
|
|
482
503
|
def _add_subject_prefix(suffix)
|
483
|
-
attrib =
|
504
|
+
attrib = 'subject_prefix'
|
484
505
|
if suffix
|
485
506
|
attrib << "_#{suffix}"
|
486
507
|
end
|
@@ -540,7 +561,7 @@ module Mail
|
|
540
561
|
return '99' if unicode_subject.match(/auto.*reply|férias|ferias|Estarei ausente|estou ausente|vacation|vocation|(out|away).*office|on holiday|abwesenheits|autorespond|Automatische|eingangsbestätigung/i)
|
541
562
|
|
542
563
|
# Feedback-Type: abuse
|
543
|
-
return '96' if self.to_s.match(/Feedback-Type
|
564
|
+
return '96' if self.to_s.match(/Feedback-Type: abuse/i)
|
544
565
|
|
545
566
|
if self.parts[1]
|
546
567
|
match_parts = self.parts[1].body.match(/(Status:.|550 |#)([245]\.[0-9]{1,3}\.[0-9]{1,3})/)
|
@@ -563,41 +584,41 @@ module Mail
|
|
563
584
|
return true if self.subject.to_s.match(/(returned|undelivered) mail|mail delivery( failed)?|(delivery )(status notification|failure)|failure notice|undeliver(able|ed)( mail)?|return(ing message|ed) to sender/i)
|
564
585
|
return true if self.subject.to_s.match(/auto.*reply|vacation|vocation|(out|away).*office|on holiday|abwesenheits|autorespond|Automatische|eingangsbestätigung/i)
|
565
586
|
return true if self['precedence'].to_s.match(/auto.*(reply|responder|antwort)/i)
|
566
|
-
return true if self.from.to_s.match(/^(MAILER-DAEMON|POSTMASTER)
|
587
|
+
return true if self.from.to_s.match(/^(MAILER-DAEMON|POSTMASTER)@/i)
|
567
588
|
false
|
568
589
|
end
|
569
590
|
|
570
591
|
def detect_bounce_status_code_from_text(text)
|
571
592
|
# Parses a text and uses pattern matching to determines its error status (RFC 3463)
|
572
593
|
# from: https://github.com/mailtop/bounce_email
|
573
|
-
return
|
574
|
-
return
|
575
|
-
return
|
594
|
+
return '5.0.0' if text.match(/Status: 5\.0\.0/i)
|
595
|
+
return '5.1.1' if text.match(/no such (address|user)|Recipient address rejected|User unknown|does not like recipient|The recipient was unavailable to take delivery of the message|Sorry, no mailbox here by that name|invalid address|unknown user|unknown local part|user not found|invalid recipient|failed after I sent the message|did not reach the following recipient|nicht zugestellt werden|o pode ser entregue para um ou mais/i)
|
596
|
+
return '5.1.2' if text.match(/unrouteable mail domain|Esta casilla ha expirado por falta de uso|I couldn't find any host named/i)
|
576
597
|
if text.match(/mailbox is full|Mailbox quota (usage|disk) exceeded|quota exceeded|Over quota|User mailbox exceeds allowed size|Message rejected\. Not enough storage space|user has exhausted allowed storage space|too many messages on the server|mailbox is over quota|mailbox exceeds allowed size|excedeu a quota/i)
|
577
|
-
return
|
578
|
-
return
|
579
|
-
end
|
580
|
-
return
|
581
|
-
return
|
582
|
-
return
|
583
|
-
return
|
584
|
-
return
|
585
|
-
return
|
586
|
-
return
|
587
|
-
return
|
588
|
-
return
|
589
|
-
return
|
590
|
-
return
|
591
|
-
return
|
592
|
-
return
|
593
|
-
return
|
594
|
-
return
|
595
|
-
return
|
596
|
-
return
|
597
|
-
return
|
598
|
-
return
|
599
|
-
return
|
600
|
-
return
|
598
|
+
return '5.2.2' if text.match(/This is a permanent error||(Status: |)5\.2\.2/i)
|
599
|
+
return '4.2.2'
|
600
|
+
end
|
601
|
+
return '5.1.0' if text.match(/Address rejected/)
|
602
|
+
return '4.1.2' if text.match(/I couldn't find any host by that name/)
|
603
|
+
return '4.2.0' if text.match(/not yet been delivered/i)
|
604
|
+
return '5.1.1' if text.match(/mailbox unavailable|No such mailbox|RecipientNotFound|not found by SMTP address lookup|Status: 5\.1\.1/i)
|
605
|
+
return '5.2.3' if text.match(/Status: 5\.2\.3/i) # Too messages in folder
|
606
|
+
return '5.4.0' if text.match(/Status: 5\.4\.0/i) # too many hops
|
607
|
+
return '5.4.4' if text.match(/Unrouteable address/i)
|
608
|
+
return '4.4.7' if text.match(/retry timeout exceeded/i)
|
609
|
+
return '5.2.0' if text.match(/The account or domain may not exist, they may be blacklisted, or missing the proper dns entries./i)
|
610
|
+
return '5.5.4' if text.match(/554 TRANSACTION FAILED/i)
|
611
|
+
return '4.4.1' if text.match(/Status: 4.4.1|delivery temporarily suspended|wasn't able to establish an SMTP connection/i)
|
612
|
+
return '5.5.0' if text.match(/550 OU-002|Mail rejected by Windows Live Hotmail for policy reasons/i)
|
613
|
+
return '5.1.2' if text.match(/PERM_FAILURE: DNS Error: Domain name not found/i)
|
614
|
+
return '4.2.0' if text.match(/Delivery attempts will continue to be made for/i)
|
615
|
+
return '5.5.4' if text.match(/554 delivery error:/i)
|
616
|
+
return '5.1.1' if text.match(/550-5.1.1|This Gmail user does not exist/i)
|
617
|
+
return '5.7.1' if text.match(/5.7.1 Your message.*?was blocked by ROTA DNSBL/i) # AA added
|
618
|
+
return '5.7.2' if text.match(/not have permission to post messages to the group/i)
|
619
|
+
return '5.3.2' if text.match(/Technical details of permanent failure|Too many bad recipients/i) && (text.match(/The recipient server did not accept our requests to connect/i) || text.match(/Connection was dropped by remote host/i) || text.match(/Could not initiate SMTP conversation/i)) # AA added
|
620
|
+
return '4.3.2' if text.match(/Technical details of temporary failure/i) && (text.match(/The recipient server did not accept our requests to connect/i) || text.match(/Connection was dropped by remote host/i) || text.match(/Could not initiate SMTP conversation/i)) # AA added
|
621
|
+
return '5.0.0' if text.match(/Delivery to the following recipient failed permanently/i) # AA added
|
601
622
|
return '5.2.3' if text.match(/account closed|account has been disabled or discontinued|mailbox not found|prohibited by administrator|access denied|account does not exist/i)
|
602
623
|
end
|
603
624
|
end
|
data/lib/schleuder/runner.rb
CHANGED
@@ -4,12 +4,12 @@ module Schleuder
|
|
4
4
|
error = setup_list(recipient)
|
5
5
|
return error if error
|
6
6
|
|
7
|
-
logger.info
|
7
|
+
logger.info 'Parsing incoming email.'
|
8
8
|
|
9
9
|
# is it valid utf-8?
|
10
10
|
msg_scrubbed = false
|
11
11
|
unless msg.valid_encoding?
|
12
|
-
logger.warn
|
12
|
+
logger.warn 'Converting message due to invalid characters'
|
13
13
|
detection = CharlockHolmes::EncodingDetector.detect(msg)
|
14
14
|
begin
|
15
15
|
msg = CharlockHolmes::Converter.convert(msg, detection[:encoding], 'UTF-8')
|
@@ -18,7 +18,7 @@ module Schleuder
|
|
18
18
|
# so we scrub the invalid characters to be able to
|
19
19
|
# at least parse the message somehow. Though this might
|
20
20
|
# result in data loss.
|
21
|
-
logger.warn
|
21
|
+
logger.warn 'Scrubbing message due to invalid characters'
|
22
22
|
msg = msg.scrub
|
23
23
|
msg_scrubbed = true
|
24
24
|
end
|
@@ -27,7 +27,7 @@ module Schleuder
|
|
27
27
|
@mail = Mail.create_message_to_list(msg, recipient, list)
|
28
28
|
|
29
29
|
if msg_scrubbed
|
30
|
-
@mail.add_pseudoheader(:note, I18n.t(
|
30
|
+
@mail.add_pseudoheader(:note, I18n.t('pseudoheaders.scrubbed_message'))
|
31
31
|
end
|
32
32
|
|
33
33
|
error = run_filters('pre')
|
@@ -40,9 +40,10 @@ module Schleuder
|
|
40
40
|
rescue GPGME::Error::BadPassphrase,
|
41
41
|
GPGME::Error::DecryptFailed,
|
42
42
|
GPGME::Error::NoData,
|
43
|
-
GPGME::Error::NoSecretKey
|
43
|
+
GPGME::Error::NoSecretKey,
|
44
|
+
GPGME::Error::Canceled
|
44
45
|
|
45
|
-
logger.warn
|
46
|
+
logger.warn 'Decryption of incoming message failed.'
|
46
47
|
return Errors::DecryptionFailed.new(list)
|
47
48
|
end
|
48
49
|
|
@@ -50,29 +51,29 @@ module Schleuder
|
|
50
51
|
return error if error
|
51
52
|
|
52
53
|
if ! @mail.was_validly_signed?
|
53
|
-
logger.debug
|
54
|
+
logger.debug 'Message was not validly signed, adding subject_prefix_in'
|
54
55
|
@mail.add_subject_prefix_in!
|
55
56
|
end
|
56
57
|
|
57
58
|
if ! @mail.was_encrypted?
|
58
|
-
logger.debug
|
59
|
+
logger.debug 'Message was not encrypted, skipping keyword-handlers'
|
59
60
|
elsif @mail.was_validly_signed?
|
60
|
-
#
|
61
|
-
logger.debug
|
62
|
-
|
61
|
+
# run KeywordHandlers
|
62
|
+
logger.debug 'Message was encrypted and validly signed'
|
63
|
+
KeywordHandlersRunner.run(type: :list, list: list, mail: @mail).compact
|
63
64
|
end
|
64
65
|
|
65
66
|
# Don't send empty messages over the list.
|
66
67
|
if @mail.empty?
|
67
|
-
logger.info
|
68
|
+
logger.info 'Message found empty, not sending it to list.'
|
68
69
|
return Errors::MessageEmpty.new(@list)
|
69
70
|
end
|
70
71
|
|
71
|
-
logger.debug
|
72
|
+
logger.debug 'Adding subject_prefix'
|
72
73
|
@mail.add_subject_prefix!
|
73
74
|
|
74
75
|
# Subscriptions
|
75
|
-
logger.debug
|
76
|
+
logger.debug 'Creating clean copy of message'
|
76
77
|
copy = @mail.clean_copy(list.headers_to_meta.any?)
|
77
78
|
list.send_to_subscriptions(copy, @mail)
|
78
79
|
nil
|
@@ -105,10 +106,11 @@ module Schleuder
|
|
105
106
|
end
|
106
107
|
|
107
108
|
def filters_runner_pre_decryption
|
108
|
-
@filters_runner_pre_decryption ||= Filters::Runner.new(list,'pre')
|
109
|
+
@filters_runner_pre_decryption ||= Filters::Runner.new(list, 'pre')
|
109
110
|
end
|
111
|
+
|
110
112
|
def filters_runner_post_decryption
|
111
|
-
@filters_runner_post_decryption ||= Filters::Runner.new(list,'post')
|
113
|
+
@filters_runner_post_decryption ||= Filters::Runner.new(list, 'post')
|
112
114
|
end
|
113
115
|
|
114
116
|
def logger
|
@@ -4,26 +4,26 @@ module Schleuder
|
|
4
4
|
|
5
5
|
validates :list_id, inclusion: {
|
6
6
|
in: -> (id) { List.pluck(:id) },
|
7
|
-
message:
|
7
|
+
message: 'must refer to an existing list'
|
8
8
|
}
|
9
|
-
validates :email, presence: true, email: true
|
9
|
+
validates :email, presence: true, email: true
|
10
|
+
validates :email, uniqueness: { scope: :list_id, case_sensitive: true }
|
10
11
|
validates :fingerprint, allow_blank: true, fingerprint: true
|
11
12
|
validates :delivery_enabled, :admin, boolean: true
|
12
13
|
|
13
14
|
before_validation {
|
14
15
|
self.email = Mail::Address.new(self.email).address
|
16
|
+
self.email.downcase! if self.email.present?
|
15
17
|
}
|
16
18
|
|
17
19
|
default_scope { order(:email) }
|
18
20
|
|
19
|
-
scope :without_fingerprint, -> { where(fingerprint: [nil,'']) }
|
20
|
-
|
21
21
|
def to_s
|
22
22
|
email
|
23
23
|
end
|
24
24
|
|
25
25
|
def self.configurable_attributes
|
26
|
-
[
|
26
|
+
['fingerprint', 'admin', 'delivery_enabled']
|
27
27
|
end
|
28
28
|
|
29
29
|
def fingerprint=(arg)
|
@@ -38,7 +38,7 @@ module Schleuder
|
|
38
38
|
# TODO: make key-related methods a concern, so we don't have to go
|
39
39
|
# through the list and neither re-implement the methods here.
|
40
40
|
# Prefix '0x' to force GnuPG to match only hex-values, not UIDs.
|
41
|
-
list.keys("0x#{self.fingerprint}").first
|
41
|
+
@key ||= list.keys("0x#{self.fingerprint}").first
|
42
42
|
end
|
43
43
|
|
44
44
|
def send_mail(mail, incoming_mail=nil)
|
@@ -52,7 +52,7 @@ module Schleuder
|
|
52
52
|
notify_of_missed_message(:absent)
|
53
53
|
return false
|
54
54
|
else
|
55
|
-
list.logger.warn
|
55
|
+
list.logger.warn 'Sending plaintext because no key is present!'
|
56
56
|
end
|
57
57
|
elsif ! self.key.usable?
|
58
58
|
if self.list.send_encrypted_only?
|
@@ -87,7 +87,7 @@ module Schleuder
|
|
87
87
|
if self.list.munge_from? && ! incoming_mail.nil?
|
88
88
|
# If the option "munge_from" is set to true, we will add the original senders' from-header to ours.
|
89
89
|
# We munge the from-header to avoid issues with DMARC.
|
90
|
-
mail.from = I18n.t(
|
90
|
+
mail.from = I18n.t('header_munging', from: incoming_mail.from.first, list: self.list.email, list_address: self.list.email)
|
91
91
|
else
|
92
92
|
mail.from = self.list.email
|
93
93
|
end
|
@@ -100,7 +100,7 @@ module Schleuder
|
|
100
100
|
self.list.logger.warn "Not sending to #{self.email}: key is unusable because it is #{reason} and sending plain text not allowed"
|
101
101
|
mail = ensure_headers(Mail.new)
|
102
102
|
mail.subject = I18n.t('notice')
|
103
|
-
mail.body = I18n.t(
|
103
|
+
mail.body = I18n.t('missed_message_due_to_unusable_key', list_email: self.list.email) + I18n.t('errors.signoff')
|
104
104
|
mail.gpg self.list.gpg_sign_options
|
105
105
|
mail.deliver
|
106
106
|
end
|
@@ -112,6 +112,5 @@ module Schleuder
|
|
112
112
|
def delete_key
|
113
113
|
list.delete_key(self.fingerprint)
|
114
114
|
end
|
115
|
-
|
116
115
|
end
|
117
116
|
end
|