schleuder 2.2.4 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
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