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,9 @@
1
+ module Mail
2
+ class PartsList
3
+ # Disable sorting of mime-parts completely.
4
+ # Can't be done during runtime on the body-instance (`body#set_sort_order`)
5
+ # because MailGpg exchanges the body-instances when encrypting/signing.
6
+ def sort!(*args)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,91 @@
1
+ module Schleuder
2
+ module PluginRunners
3
+ module Base
4
+ def run(list, mail)
5
+ list.logger.debug "Starting #{self}"
6
+ @list = list
7
+ @mail = mail
8
+ setup
9
+
10
+ output = mail.keywords.map do |keyword, arguments|
11
+ run_plugin(keyword, arguments)
12
+ end
13
+
14
+ output.flatten.compact
15
+ end
16
+
17
+
18
+ private
19
+
20
+
21
+ def run_plugin(keyword, arguments)
22
+ @list.logger.debug "Running keyword '#{keyword}'"
23
+
24
+ error = check_admin_only(keyword)
25
+ return error if error
26
+
27
+ command = keyword.gsub('-', '_')
28
+ if ['list_name', 'listname'].include? (command)
29
+ return nil
30
+ elsif ! @plugin_module.respond_to?(command)
31
+ return I18n.t('plugins.unknown_keyword', keyword: keyword)
32
+ else
33
+ response = run_command(command, arguments)
34
+ if @list.keywords_admin_notify.include?(keyword)
35
+ notify_admins(keyword, arguments, response)
36
+ end
37
+ return response
38
+ end
39
+ rescue => exc
40
+ # Log to system, this information is probably more useful for
41
+ # system-admins than for list-admins.
42
+ Schleuder.logger.error(exc.message_with_backtrace)
43
+ I18n.t("plugins.plugin_failed", keyword: keyword)
44
+ end
45
+
46
+ def run_command(command, arguments)
47
+ out = @plugin_module.send(command, arguments, @list, @mail)
48
+ Array(out).flatten
49
+ end
50
+
51
+ def check_admin_only(keyword)
52
+ if @list.admin_only?(keyword) && ! @list.from_admin?(@mail)
53
+ @list.logger.debug "Error: Keyword is admin-only, sent by non-admin"
54
+ Schleuder::Errors::KeywordAdminOnly.new(keyword).to_s
55
+ else
56
+ false
57
+ end
58
+ end
59
+
60
+ def setup
61
+ check_listname_keyword
62
+ load_plugin_files
63
+ @plugin_module = self.name.demodulize.gsub("Runner", "").constantize
64
+ end
65
+
66
+ def check_listname_keyword
67
+ return nil if @mail.keywords.blank?
68
+
69
+ listname_kw = @mail.keywords.assoc('list-name') || @mail.keywords.assoc('listname')
70
+ if listname_kw.blank?
71
+ @mail.reply_to_signer I18n.t(:missing_listname_keyword_error)
72
+ exit
73
+ else
74
+ listname_args = listname_kw.last
75
+ if ! [@list.email, @list.request_address].include?(listname_args.first)
76
+ @mail.reply_to_signer I18n.t(:wrong_listname_keyword_error)
77
+ exit
78
+ end
79
+ end
80
+ end
81
+
82
+ def load_plugin_files
83
+ @list.logger.debug "Loading plugins"
84
+ Dir["#{Schleuder::Conf.plugins_dir}/*.rb"].each do |file|
85
+ require file
86
+ end
87
+ end
88
+
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,24 @@
1
+ module Schleuder
2
+ module PluginRunners
3
+ module ListPluginsRunner
4
+ extend Base
5
+
6
+ def self.notify_admins(keyword, arguments, response)
7
+ if arguments.blank?
8
+ msg = I18n.t('plugins.keyword_admin_notify_lists_without_arguments',
9
+ signer: @mail.signer,
10
+ keyword: keyword
11
+ )
12
+ else
13
+ msg = I18n.t('plugins.keyword_admin_notify_lists',
14
+ signer: @mail.signer,
15
+ keyword: keyword,
16
+ arguments: arguments.join(' ')
17
+ )
18
+ end
19
+ @list.logger.notify_admin(msg, nil, 'Notice')
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ module Schleuder
2
+ module PluginRunners
3
+ module RequestPluginsRunner
4
+ extend Base
5
+
6
+ def self.notify_admins(keyword, arguments, response)
7
+ if arguments.blank?
8
+ explanation = I18n.t('plugins.keyword_admin_notify_request_without_arguments',
9
+ signer: @mail.signer,
10
+ keyword: keyword
11
+ )
12
+ else
13
+ explanation = I18n.t('plugins.keyword_admin_notify_request',
14
+ signer: @mail.signer,
15
+ keyword: keyword,
16
+ arguments: arguments.join(' ')
17
+ )
18
+ end
19
+ response = response.join("\n\n")
20
+ msg = "#{explanation}\n\n#{response}"
21
+ @list.logger.notify_admin(msg, nil, 'Notice')
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+
@@ -0,0 +1,17 @@
1
+ module Schleuder
2
+ module ListPlugins
3
+ def self.attach_listkey(arguments, list, mail)
4
+ filename = "#{list.fingerprint}.pgpkey"
5
+ # "Mail" only really converts to multipart if the content-type is blank.
6
+ mail.content_type = nil
7
+ mail.add_file({
8
+ filename: filename,
9
+ content: list.export_key
10
+ })
11
+ mail.attachments[filename].content_type = 'application/pgp-keys'
12
+ mail.attachments[filename].content_description = "OpenPGP public key of #{list.email}"
13
+ mail.attachments[filename].content_disposition = "attachment; filename=#{filename}"
14
+ nil
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,7 @@
1
+ module Schleuder
2
+ module RequestPlugins
3
+ def self.get_version(arguments, list, mail)
4
+ Schleuder::VERSION
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,113 @@
1
+ module Schleuder
2
+ module RequestPlugins
3
+ def self.add_key(arguments, list, mail)
4
+ out = [I18n.t('plugins.key_management.import_result')]
5
+
6
+ if mail.has_attachments?
7
+ results = self.import_keys_from_attachments(list, mail)
8
+ else
9
+ results = [self.import_key_from_body(list, mail)]
10
+ end
11
+
12
+ out << results.compact.collect(&:imports).flatten.map do |import_status|
13
+ str = I18n.t("plugins.key_management.key_import_status.#{import_status.action}")
14
+ "#{import_status.fpr}: #{str}"
15
+ end
16
+
17
+ out.join("\n")
18
+ end
19
+
20
+ def self.delete_key(arguments, list, mail)
21
+ arguments.map do |argument|
22
+ keys = list.keys(argument)
23
+ case keys.size
24
+ when 0
25
+ I18n.t("errors.no_match_for", input: argument)
26
+ when 1
27
+ begin
28
+ keys.first.delete!
29
+ I18n.t('plugins.key_management.deleted', key_string: keys.first.fingerprint)
30
+ rescue GPGME::Error::Conflict
31
+ I18n.t('plugins.key_management.not_deletable', key_string: keys.first.fingerprint)
32
+ end
33
+ else
34
+ I18n.t('errors.too_many_matching_keys', {
35
+ input: argument,
36
+ key_strings: keys.map(&:to_s).join("\n")
37
+ })
38
+ end
39
+ end.join("\n\n")
40
+ end
41
+
42
+ def self.list_keys(arguments, list, mail)
43
+ args = Array(arguments.presence || '')
44
+ args.map do |argument|
45
+ # In this case it shall be allowed to match keys by arbitrary
46
+ # sub-strings, therefore we use `list.gpg` directly to not have the
47
+ # input filtered.
48
+ list.gpg.keys(argument).map do |key|
49
+ key.to_s
50
+ end
51
+ end.join("\n\n")
52
+ end
53
+
54
+ def self.get_key(arguments, list, mail)
55
+ arguments.map do |argument|
56
+ keys = list.keys(argument)
57
+ if keys.blank?
58
+ I18n.t("errors.no_match_for", input: argument)
59
+ else
60
+ result = [I18n.t('plugins.key_management.matching_keys_intro', input: argument)]
61
+ keys.each do |key|
62
+ atchm = Mail::Part.new
63
+ atchm.body = key.armored
64
+ atchm.content_type = 'application/pgp-keys'
65
+ atchm.content_disposition = "attachment; filename=#{key.fingerprint}.asc"
66
+ result << atchm
67
+ end
68
+ result.flatten
69
+ end
70
+ end
71
+ end
72
+
73
+ def self.fetch_key(arguments, list, mail)
74
+ arguments.map do |argument|
75
+ list.fetch_keys(argument)
76
+ end
77
+ end
78
+
79
+ # helper methods
80
+ private
81
+
82
+ def self.is_armored_key?(material)
83
+ return false unless /^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ =~ material
84
+ return false unless /^-----END PGP PUBLIC KEY BLOCK-----$/ =~ material
85
+
86
+ lines = material.split("\n").reject(&:empty?)
87
+ # remove header
88
+ lines.shift
89
+ # remove tail
90
+ lines.pop
91
+ # verify the rest
92
+ # TODO: verify length except for lasts lines?
93
+ # headers according to https://tools.ietf.org/html/rfc4880#section-6.2
94
+ lines.map do |line|
95
+ /\A((comment|version|messageid|hash|charset):.*|[0-9a-z\/=+]+)\Z/i =~ line
96
+ end.all?
97
+ end
98
+
99
+ def self.import_keys_from_attachments(list, mail)
100
+ mail.attachments.map do |attachment|
101
+ material = attachment.body.to_s
102
+
103
+ list.import_key(material) if self.is_armored_key?(material)
104
+ end
105
+ end
106
+
107
+ def self.import_key_from_body(list, mail)
108
+ key_material = mail.first_plaintext_part.body.to_s
109
+
110
+ list.import_key(key_material) if self.is_armored_key?(key_material)
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,15 @@
1
+ module Schleuder
2
+ module RequestPlugins
3
+ def self.get_logfile(arguments, list, mail)
4
+ if File.readable?(list.logfile)
5
+ attachment = Mail::Part.new
6
+ attachment.body = File.read(list.logfile)
7
+ attachment.content_disposition = "inline; filename=#{list.email}.log"
8
+ intro = I18n.t("plugins.list_management.logfile_attached", listname: list.email)
9
+ [intro, attachment]
10
+ else
11
+ I18n.t("plugins.list_management.no_logfile", listname: list.email)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,196 @@
1
+ module Schleuder
2
+ module ListPlugins
3
+ def self.resend(arguments, list, mail)
4
+ resend_it(arguments, mail, false)
5
+ end
6
+
7
+ def self.resend_enc(arguments, list, mail)
8
+ resend_encrypted_only(arguments, list, mail)
9
+ end
10
+
11
+ def self.resend_encrypted_only(arguments, list, mail)
12
+ resend_it(arguments, mail, true)
13
+ end
14
+
15
+ def self.resend_cc(arguments, list, mail)
16
+ resend_it_cc(arguments, mail, false)
17
+ end
18
+
19
+ def self.resend_cc_enc(arguments, list, mail)
20
+ resend_cc_encrypted_only(arguments, list, mail)
21
+ end
22
+
23
+ def self.resend_cc_encrypted_only(arguments, list, mail)
24
+ resend_it_cc(arguments, mail, true)
25
+ end
26
+
27
+ def self.resend_unencrypted(arguments, list, mail)
28
+ do_resend_unencrypted(arguments, list, mail, :to)
29
+ end
30
+
31
+ def self.resend_cc_unencrypted(arguments, list, mail)
32
+ do_resend_unencrypted(arguments, list, mail, :cc)
33
+ end
34
+
35
+ # helper methods
36
+ private
37
+
38
+ def self.do_resend_unencrypted(arguments, list, mail, target)
39
+ if ! resend_recipients_valid?(mail, arguments)
40
+ return false
41
+ end
42
+
43
+ recip_map = Hash[Array(arguments).map{|email| [email,''] }]
44
+
45
+ if do_resend(mail, recip_map, target, false)
46
+ mail.add_subject_prefix_out!
47
+ end
48
+ end
49
+
50
+ def self.resend_it_cc(arguments, mail, encrypted_only)
51
+ if ! resend_recipients_valid?(mail, arguments)
52
+ return false
53
+ end
54
+
55
+ recip_map = map_with_keys(mail, arguments, encrypted_only)
56
+
57
+ # Only continue if all recipients are still here.
58
+ if recip_map.size < arguments.size
59
+ return
60
+ end
61
+
62
+ if do_resend(mail, recip_map, :cc, encrypted_only)
63
+ mail.add_subject_prefix_out!
64
+ end
65
+ end
66
+
67
+ def self.resend_it(arguments, mail, encrypted_only)
68
+ if ! resend_recipients_valid?(mail, arguments)
69
+ return false
70
+ end
71
+
72
+ recip_map = map_with_keys(mail, arguments, encrypted_only)
73
+
74
+ resent_stati = recip_map.map do |email, key|
75
+ do_resend(mail, {email => key}, :to, encrypted_only)
76
+ end
77
+
78
+ if resent_stati.include?(true)
79
+ # At least one message has been resent
80
+ mail.add_subject_prefix_out!
81
+ end
82
+ end
83
+
84
+ def self.do_resend(mail, recipients_map, to_or_cc, encrypted_only)
85
+ if recipients_map.empty?
86
+ return
87
+ end
88
+
89
+ gpg_opts = make_gpg_opts(mail, recipients_map, encrypted_only)
90
+ if gpg_opts == false
91
+ return false
92
+ end
93
+
94
+ # Compose and send email
95
+ new = mail.clean_copy
96
+ new[to_or_cc] = recipients_map.keys
97
+ new.add_public_footer!
98
+ new.sender = mail.list.bounce_address
99
+ # `dup` gpg_opts because `deliver` changes their value and we need them
100
+ # below to determine encryption!
101
+ new.gpg gpg_opts.dup
102
+
103
+ if new.deliver
104
+ add_resent_headers(mail, recipients_map, to_or_cc, gpg_opts[:encrypt])
105
+ return true
106
+ else
107
+ add_error_header(mail, recipients_map)
108
+ return false
109
+ end
110
+ rescue Net::SMTPFatalError => exc
111
+ add_error_header(mail, recipients_map)
112
+ logger.error "Error while sending: #{exc}"
113
+ return false
114
+ end
115
+
116
+ def self.map_with_keys(mail, recipients, encrypted_only)
117
+ Array(recipients).inject({}) do |hash, email|
118
+ keys = mail.list.keys(email)
119
+ # Exclude unusable keys.
120
+ keys.select! { |key| key.usable_for?(:encrypt) }
121
+ case keys.size
122
+ when 1
123
+ hash[email] = keys.first
124
+ when 0
125
+ if encrypted_only
126
+ # Don't add the email to the result to exclude it from the
127
+ # recipients.
128
+ add_keys_error(mail, email, keys.size)
129
+ else
130
+ hash[email] = ''
131
+ end
132
+ else
133
+ # Always report this situation, regardless of sending or not. It's
134
+ # bad and should be fixed.
135
+ add_keys_error(mail, email, keys.size)
136
+ if ! encrypted_only
137
+ hash[email] = ''
138
+ end
139
+ end
140
+ hash
141
+ end
142
+ end
143
+
144
+ def self.make_gpg_opts(mail, recipients_map, encrypted_only)
145
+ gpg_opts = mail.list.gpg_sign_options
146
+ # Do all recipients have a key?
147
+ if recipients_map.values.map(&:class).uniq == [GPGME::Key]
148
+ gpg_opts.merge!(encrypt: true)
149
+ elsif encrypted_only
150
+ false
151
+ end
152
+ gpg_opts
153
+ end
154
+
155
+ def self.add_keys_error(mail, email, keys_size)
156
+ mail.add_pseudoheader(:error, I18n.t("plugins.resend.not_resent_no_keys", email: email, num_keys: keys_size))
157
+ end
158
+
159
+ def self.add_error_header(mail, recipients_map)
160
+ mail.add_pseudoheader(:error, "Resending to #{recipients_map.keys.join(', ')} failed, please check the logs!")
161
+ end
162
+
163
+ def self.add_resent_headers(mail, recipients_map, to_or_cc, sent_encrypted)
164
+ if sent_encrypted
165
+ prefix = I18n.t('plugins.resend.encrypted_to')
166
+ str = recipients_map.map do |email, key|
167
+ "#{email} (#{key.fingerprint})"
168
+ end.join(', ')
169
+ else
170
+ prefix = I18n.t('plugins.resend.unencrypted_to')
171
+ str = recipients_map.keys.join(', ')
172
+ end
173
+ headername = resent_header_name(to_or_cc)
174
+ mail.add_pseudoheader(headername, "#{prefix} #{str}")
175
+ end
176
+
177
+ def self.resent_header_name(to_or_cc)
178
+ if to_or_cc.to_s == 'to'
179
+ 'resent'
180
+ else
181
+ 'resent_cc'
182
+ end
183
+ end
184
+
185
+ def self.resend_recipients_valid?(mail, recipients)
186
+ all_valid = true
187
+ Array(recipients).each do |address|
188
+ if ! address.match(Conf::EMAIL_REGEXP)
189
+ mail.add_pseudoheader(:error, I18n.t("plugins.resend.invalid_recipient", address: address))
190
+ all_valid = false
191
+ end
192
+ end
193
+ all_valid
194
+ end
195
+ end
196
+ end