schleuder 2.2.4 → 3.2.2

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.
Files changed (141) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +138 -0
  3. data/Rakefile +136 -0
  4. data/bin/pinentry-clearpassphrase +72 -0
  5. data/bin/schleuder +9 -89
  6. data/bin/schleuder-api-daemon +4 -0
  7. data/db/migrate/20140501103532_create_lists.rb +39 -0
  8. data/db/migrate/20140501112859_create_subscriptions.rb +21 -0
  9. data/db/migrate/201508092100_add_language_to_lists.rb +11 -0
  10. data/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb +8 -0
  11. data/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb +11 -0
  12. data/db/migrate/201508141727_change_send_encrypted_only_default.rb +8 -0
  13. data/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb +11 -0
  14. data/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb +14 -0
  15. data/db/migrate/201508261815_strip_gpg_passphrase.rb +11 -0
  16. data/db/migrate/201508261827_remove_default_mime.rb +9 -0
  17. data/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb +8 -0
  18. data/db/migrate/20170713215059_add_internal_footer_to_list.rb +11 -0
  19. data/db/schema.rb +62 -0
  20. data/etc/init.d/schleuder-api-daemon +87 -0
  21. data/etc/list-defaults.yml +123 -0
  22. data/etc/postfix/schleuder_sqlite.cf +28 -0
  23. data/etc/schleuder-api-daemon.service +10 -0
  24. data/etc/schleuder.cron.weekly +6 -0
  25. data/etc/schleuder.yml +61 -0
  26. data/lib/schleuder-api-daemon.rb +420 -0
  27. data/lib/schleuder.rb +81 -47
  28. data/lib/schleuder/cli.rb +334 -0
  29. data/lib/schleuder/cli/cert.rb +24 -0
  30. data/lib/schleuder/cli/schleuder_cert_manager.rb +84 -0
  31. data/lib/schleuder/cli/subcommand_fix.rb +11 -0
  32. data/lib/schleuder/conf.rb +131 -0
  33. data/lib/schleuder/errors/active_model_error.rb +15 -0
  34. data/lib/schleuder/errors/base.rb +17 -0
  35. data/lib/schleuder/errors/decryption_failed.rb +16 -0
  36. data/lib/schleuder/errors/fatal_error.rb +13 -0
  37. data/lib/schleuder/errors/file_not_found.rb +14 -0
  38. data/lib/schleuder/errors/invalid_listname.rb +13 -0
  39. data/lib/schleuder/errors/key_adduid_failed.rb +13 -0
  40. data/lib/schleuder/errors/key_generation_failed.rb +16 -0
  41. data/lib/schleuder/errors/keyword_admin_only.rb +13 -0
  42. data/lib/schleuder/errors/list_exists.rb +13 -0
  43. data/lib/schleuder/errors/list_not_found.rb +14 -0
  44. data/lib/schleuder/errors/list_property_missing.rb +14 -0
  45. data/lib/schleuder/errors/listdir_problem.rb +16 -0
  46. data/lib/schleuder/errors/loading_list_settings_failed.rb +14 -0
  47. data/lib/schleuder/errors/message_empty.rb +14 -0
  48. data/lib/schleuder/errors/message_not_from_admin.rb +13 -0
  49. data/lib/schleuder/errors/message_sender_not_subscribed.rb +13 -0
  50. data/lib/schleuder/errors/message_too_big.rb +14 -0
  51. data/lib/schleuder/errors/message_unauthenticated.rb +13 -0
  52. data/lib/schleuder/errors/message_unencrypted.rb +13 -0
  53. data/lib/schleuder/errors/message_unsigned.rb +13 -0
  54. data/lib/schleuder/errors/standard_error.rb +5 -0
  55. data/lib/schleuder/errors/too_many_keys.rb +17 -0
  56. data/lib/schleuder/errors/unknown_list_option.rb +14 -0
  57. data/lib/schleuder/filters/auth_filter.rb +39 -0
  58. data/lib/schleuder/filters/bounces_filter.rb +12 -0
  59. data/lib/schleuder/filters/forward_filter.rb +17 -0
  60. data/lib/schleuder/filters/forward_incoming.rb +13 -0
  61. data/lib/schleuder/filters/hotmail_message_filter.rb +25 -0
  62. data/lib/schleuder/filters/max_message_size.rb +14 -0
  63. data/lib/schleuder/filters/request_filter.rb +26 -0
  64. data/lib/schleuder/filters/send_key_filter.rb +20 -0
  65. data/lib/schleuder/filters/strip_alternative_filter.rb +21 -0
  66. data/lib/schleuder/filters_runner.rb +83 -0
  67. data/lib/schleuder/gpgme/ctx.rb +274 -0
  68. data/lib/schleuder/gpgme/import_status.rb +27 -0
  69. data/lib/schleuder/gpgme/key.rb +212 -0
  70. data/lib/schleuder/gpgme/sub_key.rb +13 -0
  71. data/lib/schleuder/gpgme/user_id.rb +22 -0
  72. data/lib/schleuder/list.rb +318 -127
  73. data/lib/schleuder/list_builder.rb +139 -0
  74. data/lib/schleuder/listlogger.rb +31 -0
  75. data/lib/schleuder/logger.rb +23 -0
  76. data/lib/schleuder/logger_notifications.rb +69 -0
  77. data/lib/schleuder/mail/message.rb +482 -0
  78. data/lib/schleuder/mail/parts_list.rb +9 -0
  79. data/lib/schleuder/plugin_runners/base.rb +91 -0
  80. data/lib/schleuder/plugin_runners/list_plugins_runner.rb +24 -0
  81. data/lib/schleuder/plugin_runners/request_plugins_runner.rb +27 -0
  82. data/lib/schleuder/plugins/attach_listkey.rb +17 -0
  83. data/lib/schleuder/plugins/get_version.rb +7 -0
  84. data/lib/schleuder/plugins/key_management.rb +113 -0
  85. data/lib/schleuder/plugins/list_management.rb +15 -0
  86. data/lib/schleuder/plugins/resend.rb +196 -0
  87. data/lib/schleuder/plugins/sign_this.rb +46 -0
  88. data/lib/schleuder/plugins/subscription_management.rb +140 -0
  89. data/lib/schleuder/runner.rb +130 -0
  90. data/lib/schleuder/subscription.rb +98 -0
  91. data/lib/schleuder/validators/boolean_validator.rb +7 -0
  92. data/lib/schleuder/validators/email_validator.rb +7 -0
  93. data/lib/schleuder/validators/fingerprint_validator.rb +7 -0
  94. data/lib/schleuder/validators/greater_than_zero_validator.rb +7 -0
  95. data/lib/schleuder/validators/no_line_breaks_validator.rb +7 -0
  96. data/lib/schleuder/version.rb +1 -1
  97. data/locales/de.yml +179 -0
  98. data/locales/en.yml +179 -0
  99. metadata +305 -108
  100. checksums.yaml.gz.sig +0 -3
  101. data.tar.gz.sig +0 -2
  102. data/LICENSE +0 -339
  103. data/README +0 -32
  104. data/bin/schleuder-fix-gem-dependencies +0 -37
  105. data/bin/schleuder-init-setup +0 -37
  106. data/bin/schleuder-migrate-v2.1-to-v2.2 +0 -225
  107. data/bin/schleuder-newlist +0 -413
  108. data/contrib/check-expired-keys.rb +0 -60
  109. data/contrib/mutt-schleuder-colors.rc +0 -10
  110. data/contrib/mutt-schleuder-resend.vim +0 -24
  111. data/contrib/smtpserver.rb +0 -76
  112. data/ext/default-list.conf +0 -149
  113. data/ext/default-members.conf +0 -7
  114. data/ext/list.conf.example +0 -14
  115. data/ext/schleuder.conf +0 -64
  116. data/lib/schleuder/archiver.rb +0 -46
  117. data/lib/schleuder/crypt.rb +0 -210
  118. data/lib/schleuder/errors.rb +0 -5
  119. data/lib/schleuder/list_config.rb +0 -146
  120. data/lib/schleuder/log/listlogger.rb +0 -57
  121. data/lib/schleuder/log/outputter/emailoutputter.rb +0 -120
  122. data/lib/schleuder/log/outputter/metaemailoutputter.rb +0 -50
  123. data/lib/schleuder/log/schleuderlogger.rb +0 -34
  124. data/lib/schleuder/mail.rb +0 -873
  125. data/lib/schleuder/mailer.rb +0 -26
  126. data/lib/schleuder/member.rb +0 -69
  127. data/lib/schleuder/plugin.rb +0 -54
  128. data/lib/schleuder/processor.rb +0 -363
  129. data/lib/schleuder/schleuder_config.rb +0 -75
  130. data/lib/schleuder/storage.rb +0 -84
  131. data/lib/schleuder/utils.rb +0 -80
  132. data/man/schleuder-newlist.8 +0 -174
  133. data/man/schleuder.8 +0 -416
  134. data/plugins/README +0 -20
  135. data/plugins/manage_keys_plugin.rb +0 -113
  136. data/plugins/manage_members_plugin.rb +0 -156
  137. data/plugins/manage_self_plugin.rb +0 -26
  138. data/plugins/resend_plugin.rb +0 -35
  139. data/plugins/sign_this_plugin.rb +0 -14
  140. data/plugins/version_plugin.rb +0 -12
  141. metadata.gz.sig +0 -0
@@ -0,0 +1,14 @@
1
+ module Schleuder
2
+ module Errors
3
+ class LoadingListSettingsFailed < Base
4
+ def initialize
5
+ @config_file = ENV['SCHLEUDER_LIST_DEFAULTS']
6
+ end
7
+
8
+ def message
9
+ t('errors.loading_list_settings_failed', config_file: @config_file)
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,14 @@
1
+ module Schleuder
2
+ module Errors
3
+ class MessageEmpty < Base
4
+ def initialize(list)
5
+ set_default_locale
6
+ @request_address = list.request_address
7
+ end
8
+
9
+ def message
10
+ t('errors.message_empty', { request_address: @request_address })
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Schleuder
2
+ module Errors
3
+ class MessageNotFromAdmin < Base
4
+ def initialize(list)
5
+ set_default_locale
6
+ end
7
+
8
+ def message
9
+ t('errors.message_not_from_admin')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Schleuder
2
+ module Errors
3
+ class MessageSenderNotSubscribed < Base
4
+ def initialize(list)
5
+ set_default_locale
6
+ end
7
+
8
+ def message
9
+ t('errors.message_sender_not_subscribed')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module Schleuder
2
+ module Errors
3
+ class MessageTooBig < Base
4
+ def initialize(list)
5
+ set_default_locale
6
+ @allowed_size = list.max_message_size_kb
7
+ end
8
+
9
+ def message
10
+ t('errors.message_too_big', { allowed_size: @allowed_size })
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module Schleuder
2
+ module Errors
3
+ class MessageUnauthenticated < Base
4
+ def initialize
5
+ set_default_locale
6
+ end
7
+
8
+ def message
9
+ t('errors.message_unauthenticated')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Schleuder
2
+ module Errors
3
+ class MessageUnencrypted < Base
4
+ def initialize(list)
5
+ set_default_locale
6
+ end
7
+
8
+ def message
9
+ t('errors.message_unencrypted')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Schleuder
2
+ module Errors
3
+ class MessageUnsigned < Base
4
+ def initialize(list)
5
+ set_default_locale
6
+ end
7
+
8
+ def message
9
+ t('errors.message_unsigned')
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class StandardError
2
+ def message_with_backtrace
3
+ "#{message}\n#{self.backtrace.join("\n")}\n"
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ module Schleuder
2
+ module Errors
3
+ class TooManyKeys < Base
4
+ def initialize(listdir, listname)
5
+ @listdir = listdir
6
+ @listname = listname
7
+ end
8
+
9
+ def message
10
+ t('errors.too_many_keys',
11
+ { listdir: @listdir,
12
+ listname: @listname })
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,14 @@
1
+ module Schleuder
2
+ module Errors
3
+ class UnknownListOption < Base
4
+ def initialize(exception)
5
+ @option = exception.attribute
6
+ @config_file = ENV['SCHLEUDER_LIST_DEFAULTS']
7
+ end
8
+
9
+ def message
10
+ t('errors.unknown_list_option', option: @option, config_file: @config_file)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,39 @@
1
+ module Schleuder
2
+ module Filters
3
+
4
+ def self.receive_encrypted_only(list, mail)
5
+ if list.receive_encrypted_only? && ! mail.was_encrypted?
6
+ list.logger.info "Rejecting mail as unencrypted"
7
+ return Errors::MessageUnencrypted.new(list)
8
+ end
9
+ end
10
+
11
+ def self.receive_signed_only(list, mail)
12
+ if list.receive_signed_only? && ! mail.was_validly_signed?
13
+ list.logger.info "Rejecting mail as unsigned"
14
+ return Errors::MessageUnsigned.new(list)
15
+ end
16
+ end
17
+
18
+ def self.receive_authenticated_only(list, mail)
19
+ if list.receive_authenticated_only? && ( ! mail.was_encrypted? || ! mail.was_validly_signed? )
20
+ list.logger.info "Rejecting mail as unauthenticated"
21
+ return Errors::MessageUnauthenticated.new
22
+ end
23
+ end
24
+
25
+ def self.receive_from_subscribed_emailaddresses_only(list, mail)
26
+ if list.receive_from_subscribed_emailaddresses_only? && list.subscriptions.where(email: mail.from.first).blank?
27
+ list.logger.info "Rejecting mail as not from subscribed address."
28
+ return Errors::MessageSenderNotSubscribed.new(list)
29
+ end
30
+ end
31
+
32
+ def self.receive_admin_only(list, mail)
33
+ if list.receive_admin_only? && ( ! mail.was_validly_signed? || ! mail.signer.admin? )
34
+ list.logger.info "Rejecting mail as not from admin."
35
+ return Errors::MessageNotFromAdmin.new(list)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ module Schleuder
2
+ module Filters
3
+ def self.forward_bounce_to_admins(list, mail)
4
+ if mail.automated_message?
5
+ list.logger.info "Forwarding automated message to admins"
6
+ list.logger.notify_admin I18n.t(:forward_automated_message_to_admins), mail.original_message, I18n.t('automated_message_subject')
7
+ exit
8
+ end
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,17 @@
1
+ module Schleuder
2
+ module Filters
3
+ def self.forward_to_owner(list, mail)
4
+ return if ! mail.to_owner?
5
+
6
+ list.logger.debug "Forwarding addressed to -owner"
7
+ mail.add_pseudoheader(:note, I18n.t(:owner_forward_prefix))
8
+ cleanmail = mail.clean_copy(true)
9
+ list.admins.each do |admin|
10
+ list.logger.debug "Forwarding message to #{admin}"
11
+ admin.send_mail(cleanmail)
12
+ end
13
+ exit
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,13 @@
1
+ module Schleuder
2
+ module Filters
3
+
4
+ def self.forward_all_incoming_to_admins(list, mail)
5
+ if list.forward_all_incoming_to_admins
6
+ list.logger.notify_admin I18n.t(:forward_all_incoming_to_admins), mail.original_message, I18n.t('incoming_message')
7
+ end
8
+ end
9
+
10
+ end
11
+ end
12
+
13
+
@@ -0,0 +1,25 @@
1
+ module Schleuder
2
+ module Filters
3
+
4
+ # Outlook / Hotmail seems to dismantle multipart/encrypted messages and
5
+ # put them again together as multipart/mixed, which is wrong and makes
6
+ # it problematic to correctly detect the message as a valid pgp/mime-mail.
7
+ # Here we fix the mail to be a proper pgp/mime aka. multipart/encrypted
8
+ # message, so further processing will detect it properly.
9
+ # See #211 and #246 for background
10
+ def self.fix_hotmail_messages!(list, mail)
11
+ if mail.header['X-OriginatorOrg'].to_s.match(/(hotmail|outlook).com/) &&
12
+ !mail[:content_type].blank? &&
13
+ mail[:content_type].content_type == 'multipart/mixed' && mail.parts.size > 2 &&
14
+ mail.parts[0][:content_type].content_type == 'text/plain' &&
15
+ mail.parts[0].body.to_s.blank? &&
16
+ mail.parts[1][:content_type].content_type == 'application/pgp-encrypted' &&
17
+ mail.parts[2][:content_type].content_type == 'application/octet-stream'
18
+ mail.parts.delete_at(0)
19
+ mail.content_type = [:multipart, :encrypted, {protocol: "application/pgp-encrypted", boundary: mail.boundary}]
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+
@@ -0,0 +1,14 @@
1
+ module Schleuder
2
+ module Filters
3
+
4
+ def self.max_message_size(list, mail)
5
+ if (mail.raw_source.size / 1024) > list.max_message_size_kb
6
+ list.logger.info "Rejecting mail as too big"
7
+ return Errors::MessageTooBig.new(list)
8
+ end
9
+ end
10
+
11
+ end
12
+ end
13
+
14
+
@@ -0,0 +1,26 @@
1
+ module Schleuder
2
+ module Filters
3
+ def self.request(list, mail)
4
+ return if ! mail.request?
5
+
6
+ list.logger.debug "Request-message"
7
+
8
+ if ! mail.was_encrypted? || ! mail.was_validly_signed?
9
+ list.logger.debug "Error: Message was not encrypted and validly signed"
10
+ return Errors::MessageUnauthenticated.new
11
+ end
12
+
13
+ if mail.keywords.empty?
14
+ output = I18n.t(:no_keywords_error)
15
+ else
16
+ output = PluginRunners::RequestPluginsRunner.run(list, mail)
17
+ output = output.flatten.map(&:presence).compact
18
+ if output.blank?
19
+ output = I18n.t(:no_output_result)
20
+ end
21
+ end
22
+ mail.reply_to_signer(output)
23
+ exit
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ module Schleuder
2
+ module Filters
3
+ def self.send_key(list, mail)
4
+ return if ! mail.sendkey_request?
5
+
6
+ list.logger.debug "Sending public key as reply."
7
+
8
+ out = mail.reply
9
+ out.from = list.email
10
+ # We're not sending to a subscribed address, so we need to specify a envelope-sender manually.
11
+ out.sender = list.bounce_address
12
+ out.body = I18n.t(:list_public_key_attached)
13
+ out.attach_list_key!(list)
14
+ # TODO: find out why the gpg-module puts all the headers into the first mime-part, too
15
+ out.gpg list.gpg_sign_options
16
+ out.deliver
17
+ exit
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Schleuder
2
+ module Filters
3
+
4
+ def self.strip_html_from_alternative!(list, mail)
5
+ if mail[:content_type].blank? ||
6
+ mail[:content_type].content_type != 'multipart/alternative' ||
7
+ ! mail.to_s.include?('BEGIN PGP ')
8
+ return false
9
+ end
10
+
11
+ Schleuder.logger.debug "Stripping html-part from multipart/alternative-message"
12
+ mail.parts.delete_if do |part|
13
+ part[:content_type].content_type == 'text/html'
14
+ end
15
+ mail.content_type = 'multipart/mixed'
16
+ mail.add_pseudoheader(:note, I18n.t("pseudoheaders.stripped_html_from_multialt"))
17
+ end
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,83 @@
1
+ module Schleuder
2
+ module Filters
3
+ class Runner
4
+ # To define priority sort this.
5
+ # The method `setup` parses, decrypts etc.
6
+ # the mail sent to the list. So before
7
+ # calling setup we do all the things
8
+ # that won't require e.g. validation of
9
+ # the sender.
10
+ PRE_SETUP_FILTERS = %w[
11
+ forward_bounce_to_admins
12
+ forward_all_incoming_to_admins
13
+ send_key
14
+ fix_hotmail_messages!
15
+ strip_html_from_alternative!
16
+ ]
17
+ # message size must be checked after
18
+ # decryption as gpg heavily compresses
19
+ # messages.
20
+ POST_SETUP_FILTERS = %w[
21
+ request
22
+ max_message_size
23
+ forward_to_owner
24
+ receive_admin_only
25
+ receive_authenticated_only
26
+ receive_signed_only
27
+ receive_encrypted_only
28
+ receive_from_subscribed_emailaddresses_only
29
+ ]
30
+
31
+ attr_reader :list
32
+
33
+ def initialize(list)
34
+ @list = list
35
+ end
36
+
37
+ def run(mail, filters)
38
+ filters.map do |cmd|
39
+ list.logger.debug "Calling filter #{cmd}"
40
+ response = Filters.send(cmd, list, mail)
41
+ if stop?(response)
42
+ if bounce?(response, mail)
43
+ return response
44
+ else
45
+ return nil
46
+ end
47
+ end
48
+ end
49
+ nil
50
+ end
51
+ private
52
+
53
+ def stop?(response)
54
+ response.kind_of?(StandardError)
55
+ end
56
+
57
+ def bounce?(response, mail)
58
+ if list.bounces_drop_all
59
+ list.logger.debug "Dropping bounce as configurated"
60
+ notify_admins(I18n.t('.bounces_drop_all'), mail.original_message)
61
+ return false
62
+ end
63
+
64
+ list.bounces_drop_on_headers.each do |key, value|
65
+ if mail[key].to_s.match(/#{value}/i)
66
+ list.logger.debug "Incoming message header key '#{key}' matches value '#{value}': dropping the bounce."
67
+ notify_admins(I18n.t('.bounces_drop_on_headers', key: key, value: value), mail.original_message)
68
+ return false
69
+ end
70
+ end
71
+
72
+ list.logger.debug "Bouncing message"
73
+ true
74
+ end
75
+
76
+ def notify_admins(reason, original_message)
77
+ if list.bounces_notify_admins?
78
+ list.logger.notify_admin reason, original_message, I18n.t('notice')
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end