schleuder 3.5.3 → 4.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -21
  3. data/Rakefile +15 -12
  4. data/bin/schleuder +1 -1
  5. data/db/migrate/20140501103532_create_lists.rb +1 -1
  6. data/db/migrate/20140501112859_create_subscriptions.rb +1 -1
  7. data/db/migrate/{201508092100_add_language_to_lists.rb → 20150809210000_add_language_to_lists.rb} +1 -1
  8. data/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb +1 -1
  9. data/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb +1 -1
  10. data/db/migrate/{201508141727_change_send_encrypted_only_default.rb → 20150814172700_change_send_encrypted_only_default.rb} +1 -1
  11. data/db/migrate/{201508222143_add_logfiles_to_keep_to_lists.rb → 20150822214300_add_logfiles_to_keep_to_lists.rb} +1 -1
  12. 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
  13. data/db/migrate/{201508261815_strip_gpg_passphrase.rb → 20150826181500_strip_gpg_passphrase.rb} +1 -1
  14. data/db/migrate/{201508261827_remove_default_mime.rb → 20150826182700_remove_default_mime.rb} +1 -1
  15. data/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb +1 -1
  16. data/db/migrate/20170713215059_add_internal_footer_to_list.rb +1 -1
  17. data/db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb +1 -1
  18. data/db/migrate/20180723173900_add_deliver_selfsent_to_list.rb +1 -1
  19. data/db/migrate/20190906194820_add_autocrypt_header_to_list.rb +1 -1
  20. data/db/migrate/20200118170110_add_set_reply_to_to_sender_and_munge_from.rb +15 -0
  21. data/db/schema.rb +45 -45
  22. data/etc/list-defaults.yml +18 -0
  23. data/etc/postfix/schleuder_sqlite.cf +1 -1
  24. data/etc/schleuder-weekly-key-maintenance.service +9 -0
  25. data/etc/schleuder-weekly-key-maintenance.timer +9 -0
  26. data/etc/schleuder.yml +3 -3
  27. data/lib/schleuder-api-daemon/helpers/schleuder-api-daemon-helper.rb +3 -3
  28. data/lib/schleuder-api-daemon/routes/subscription.rb +4 -4
  29. data/lib/schleuder.rb +13 -12
  30. data/lib/schleuder/cli.rb +9 -189
  31. data/lib/schleuder/cli/cert.rb +2 -2
  32. data/lib/schleuder/cli/cli_helper.rb +14 -0
  33. data/lib/schleuder/cli/schleuder_cert_manager.rb +4 -4
  34. data/lib/schleuder/conf.rb +4 -4
  35. data/lib/schleuder/errors/base.rb +2 -2
  36. data/lib/schleuder/errors/decryption_failed.rb +1 -1
  37. data/lib/schleuder/errors/fatal_error.rb +1 -1
  38. data/lib/schleuder/errors/key_adduid_failed.rb +1 -1
  39. data/lib/schleuder/errors/key_generation_failed.rb +1 -1
  40. data/lib/schleuder/errors/message_empty.rb +1 -1
  41. data/lib/schleuder/errors/message_too_big.rb +1 -1
  42. data/lib/schleuder/errors/too_many_keys.rb +1 -1
  43. data/lib/schleuder/filters/post_decryption/10_request.rb +3 -3
  44. data/lib/schleuder/filters/post_decryption/20_max_message_size.rb +1 -1
  45. data/lib/schleuder/filters/post_decryption/30_forward_to_owner.rb +1 -1
  46. data/lib/schleuder/filters/post_decryption/40_receive_admin_only.rb +1 -1
  47. data/lib/schleuder/filters/post_decryption/50_receive_authenticated_only.rb +1 -1
  48. data/lib/schleuder/filters/post_decryption/60_receive_signed_only.rb +1 -1
  49. data/lib/schleuder/filters/post_decryption/70_receive_encrypted_only.rb +1 -1
  50. data/lib/schleuder/filters/post_decryption/80_receive_from_subscribed_emailaddresses_only.rb +1 -1
  51. data/lib/schleuder/filters/pre_decryption/10_forward_bounce_to_admins.rb +1 -1
  52. data/lib/schleuder/filters/pre_decryption/30_send_key.rb +1 -1
  53. data/lib/schleuder/filters/pre_decryption/40_fix_exchange_messages.rb +1 -1
  54. data/lib/schleuder/filters/pre_decryption/50_strip_html_from_alternative.rb +2 -2
  55. data/lib/schleuder/filters_runner.rb +9 -9
  56. data/lib/schleuder/gpgme/ctx.rb +15 -67
  57. data/lib/schleuder/gpgme/key.rb +4 -136
  58. data/lib/schleuder/gpgme/user_id.rb +2 -0
  59. data/lib/schleuder/keyword_handlers/attach_list_key.rb +17 -0
  60. data/lib/schleuder/keyword_handlers/base.rb +36 -0
  61. data/lib/schleuder/keyword_handlers/get_version.rb +11 -0
  62. data/lib/schleuder/keyword_handlers/key_management.rb +141 -0
  63. data/lib/schleuder/keyword_handlers/list_management.rb +19 -0
  64. data/lib/schleuder/keyword_handlers/resend.rb +208 -0
  65. data/lib/schleuder/keyword_handlers/sign_this.rb +54 -0
  66. data/lib/schleuder/keyword_handlers/subscription_management.rb +213 -0
  67. data/lib/schleuder/keyword_handlers_runner.rb +146 -0
  68. data/lib/schleuder/list.rb +28 -40
  69. data/lib/schleuder/list_builder.rb +16 -5
  70. data/lib/schleuder/listlogger.rb +1 -1
  71. data/lib/schleuder/logger.rb +2 -6
  72. data/lib/schleuder/mail/{encrypted_part.rb → gpg/encrypted_part.rb} +0 -0
  73. data/lib/schleuder/mail/gpg/sign_part.rb +33 -0
  74. data/lib/schleuder/mail/message.rb +135 -40
  75. data/lib/schleuder/runner.rb +18 -16
  76. data/lib/schleuder/subscription.rb +35 -13
  77. data/lib/schleuder/validators/boolean_validator.rb +1 -1
  78. data/lib/schleuder/validators/email_validator.rb +1 -1
  79. data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
  80. data/lib/schleuder/validators/greater_than_zero_validator.rb +1 -1
  81. data/lib/schleuder/validators/no_line_breaks_validator.rb +1 -1
  82. data/lib/schleuder/version.rb +1 -1
  83. data/locales/de.yml +49 -36
  84. data/locales/en.yml +34 -21
  85. metadata +131 -79
  86. data/bin/pinentry-clearpassphrase +0 -72
  87. data/lib/schleuder/plugin_runners/base.rb +0 -91
  88. data/lib/schleuder/plugin_runners/list_plugins_runner.rb +0 -24
  89. data/lib/schleuder/plugin_runners/request_plugins_runner.rb +0 -27
  90. data/lib/schleuder/plugins/attach_listkey.rb +0 -13
  91. data/lib/schleuder/plugins/get_version.rb +0 -7
  92. data/lib/schleuder/plugins/key_management.rb +0 -121
  93. data/lib/schleuder/plugins/list_management.rb +0 -15
  94. data/lib/schleuder/plugins/resend.rb +0 -199
  95. data/lib/schleuder/plugins/sign_this.rb +0 -46
  96. 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 "Building new list"
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["GNUPGHOME"] = @list_dir
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 "Generating key-pair, this could take a while..."
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 = key.adduid(list.email, address)
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 = list.key.set_primary_uid(list.email)
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
@@ -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("%Y%m%d").to_i
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})$/)
@@ -7,13 +7,9 @@ module Schleuder
7
7
  class Logger < Syslog::Logger
8
8
  include LoggerNotifications
9
9
  def initialize
10
- if RUBY_VERSION.to_f < 2.1
11
- super('Schleuder')
12
- else
13
- super('Schleuder', Syslog::LOG_MAIL)
14
- end
10
+ super('Schleuder', Syslog::LOG_MAIL)
15
11
  # We need some sender-address different from the superadmin-address.
16
- @from = "#{`whoami`.chomp}@#{`hostname`.chomp}"
12
+ @from = "#{Etc.getlogin}@#{Socket.gethostname}"
17
13
  @adminaddresses = Conf.superadmin
18
14
  @level = ::Logger.const_get(Conf.log_level.upcase)
19
15
  end
@@ -0,0 +1,33 @@
1
+ module Mail
2
+ module Gpg
3
+ class SignPart < Mail::Part
4
+ # Copied verbatim from mail-gpg v.0.4.2. This code was changed in
5
+ # <https://github.com/jkraemer/mail-gpg/commit/5fded41ccee4a58f848a2f8e7bd53d11236f8984>,
6
+ # which breaks verifying some encapsulated (signed-then-encrypted)
7
+ # messages. See
8
+ # <https://github.com/jkraemer/mail-gpg/pull/40#issue-95776382> for
9
+ # details.
10
+ def self.verify_signature(plain_part, signature_part, options = {})
11
+ if !(signature_part.has_content_type? &&
12
+ ('application/pgp-signature' == signature_part.mime_type))
13
+ return false
14
+ end
15
+
16
+ # Work around the problem that plain_part.raw_source prefixes an
17
+ # erroneous CRLF, <https://github.com/mikel/mail/issues/702>.
18
+ if ! plain_part.raw_source.empty?
19
+ plaintext = [ plain_part.header.raw_source,
20
+ "\r\n\r\n",
21
+ plain_part.body.raw_source
22
+ ].join
23
+ else
24
+ plaintext = plain_part.encoded
25
+ end
26
+
27
+ signature = signature_part.body.encoded
28
+ GpgmeHelper.sign_verify(plaintext, signature, options)
29
+ end
30
+ end
31
+ end
32
+ end
33
+
@@ -24,7 +24,9 @@ module Mail
24
24
  # Message#initialize.
25
25
  def setup
26
26
  if self.encrypted?
27
- new = self.decrypt(verify: true)
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 == "text/rfc822-headers; protected-headers=v1"
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 = "text/rfc822-headers; protected-headers=v1"
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 "Multiple signatures found! Cannot handle!"
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
- if signature.present?
165
- @signing_key ||= list.keys(signature.fpr).first
166
+ @signing_key ||= begin
167
+ if signature.present?
168
+ list.keys(signature.fpr).first
169
+ end
166
170
  end
167
171
  end
168
172
 
@@ -205,17 +209,17 @@ module Mail
205
209
  @recipient.match(/-bounce@/).present? ||
206
210
  # Empty Return-Path
207
211
  self.return_path.to_s == '<>' ||
208
- # Auto-Submitted exists and does not equal 'no' and:
209
- # - no cron header is present
210
- # - no Jenkins job notification header is present
211
- # as these emails have the auto-submitted header.
212
- ( self['Auto-Submitted'].present? && \
213
- self['Auto-Submitted'].to_s.downcase != 'no' && \
214
- !self['X-Cron-Env'].present? && \
215
- !self['X-Jenkins-Job'].present? && \
216
- self.subject.to_s !~ /\A\*\*\* SECURITY information.*\*\*\*\Z/)
212
+ bounced?
213
+ end
214
+
215
+ def bounced?
216
+ @bounced ||= bounce_detected? || (error_status != 'unknown')
217
217
  end
218
218
 
219
+ def error_status
220
+ @error_status ||= detect_error_code
221
+ end
222
+
219
223
  def keywords
220
224
  return @keywords if @keywords
221
225
 
@@ -224,23 +228,8 @@ module Mail
224
228
  return []
225
229
  end
226
230
 
227
- @keywords = []
228
- look_for_keywords = true
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("signature_states.unknown", fingerprint: self.signature.fingerprint)
283
+ signature_state = I18n.t('signature_states.unknown', fingerprint: self.signature.fingerprint)
296
284
  end
297
285
  else
298
- signature_state = I18n.t("signature_states.unsigned")
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("encryption_states.encrypted")
293
+ encryption_state = I18n.t('encryption_states.encrypted')
306
294
  else
307
- encryption_state = I18n.t("encryption_states.unencrypted")
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
- "NO (Admins only)"
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 = "subject_prefix"
504
+ attrib = 'subject_prefix'
484
505
  if suffix
485
506
  attrib << "_#{suffix}"
486
507
  end
@@ -526,5 +547,79 @@ module Mail
526
547
  end
527
548
  end.join(' ')
528
549
  end
550
+
551
+ def detect_error_code
552
+ # Detects the error code of an email with different heuristics
553
+ # from: https://github.com/mailtop/bounce_email
554
+
555
+ # Custom status codes
556
+ unicode_subject = self.subject.to_s
557
+ unicode_subject = unicode_subject.encode('utf-8') if unicode_subject.respond_to?(:encode)
558
+
559
+ return '97' if unicode_subject.match(/delayed/i)
560
+ return '98' if unicode_subject.match(/(unzulässiger|unerlaubter) anhang/i)
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)
562
+
563
+ # Feedback-Type: abuse
564
+ return '96' if self.to_s.match(/Feedback-Type: abuse/i)
565
+
566
+ if self.parts[1]
567
+ match_parts = self.parts[1].body.match(/(Status:.|550 |#)([245]\.[0-9]{1,3}\.[0-9]{1,3})/)
568
+ code = match_parts[2] if match_parts
569
+ return code if code
570
+ end
571
+
572
+ # Now try getting it from correct part of tmail
573
+ code = detect_bounce_status_code_from_text(self.body)
574
+ return code if code
575
+
576
+ # OK getting desperate so try getting code from entire email
577
+ code = detect_bounce_status_code_from_text(self.to_s)
578
+ code || 'unknown'
579
+ end
580
+
581
+ def bounce_detected?
582
+ # Detects bounces from different parts of the email without error status codes
583
+ # from: https://github.com/mailtop/bounce_email
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)
585
+ return true if self.subject.to_s.match(/auto.*reply|vacation|vocation|(out|away).*office|on holiday|abwesenheits|autorespond|Automatische|eingangsbestätigung/i)
586
+ return true if self['precedence'].to_s.match(/auto.*(reply|responder|antwort)/i)
587
+ return true if self.from.to_s.match(/^(MAILER-DAEMON|POSTMASTER)@/i)
588
+ false
589
+ end
590
+
591
+ def detect_bounce_status_code_from_text(text)
592
+ # Parses a text and uses pattern matching to determines its error status (RFC 3463)
593
+ # from: https://github.com/mailtop/bounce_email
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)
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)
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
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)
623
+ end
529
624
  end
530
625
  end
@@ -4,12 +4,12 @@ module Schleuder
4
4
  error = setup_list(recipient)
5
5
  return error if error
6
6
 
7
- logger.info "Parsing incoming email."
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 "Converting message due to invalid characters"
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 "Scrubbing message due to invalid characters"
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("pseudoheaders.scrubbed_message"))
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 "Decryption of incoming message failed."
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 "Message was not validly signed, adding subject_prefix_in"
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 "Message was not encrypted, skipping plugins"
59
+ logger.debug 'Message was not encrypted, skipping keyword-handlers'
59
60
  elsif @mail.was_validly_signed?
60
- # Plugins
61
- logger.debug "Message was encrypted and validly signed"
62
- PluginRunners::ListPluginsRunner.run(list, @mail).compact
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 "Message found empty, not sending it to list."
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 "Adding subject_prefix"
72
+ logger.debug 'Adding subject_prefix'
72
73
  @mail.add_subject_prefix!
73
74
 
74
75
  # Subscriptions
75
- logger.debug "Creating clean copy of message"
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