schleuder 3.5.0 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -10
  3. data/Rakefile +12 -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 +10 -12
  30. data/lib/schleuder/cli.rb +9 -188
  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 +10 -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 -35
  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/mail/message.rb +135 -40
  72. data/lib/schleuder/runner.rb +18 -16
  73. data/lib/schleuder/subscription.rb +35 -13
  74. data/lib/schleuder/validators/boolean_validator.rb +1 -1
  75. data/lib/schleuder/validators/email_validator.rb +1 -1
  76. data/lib/schleuder/validators/fingerprint_validator.rb +1 -1
  77. data/lib/schleuder/validators/greater_than_zero_validator.rb +1 -1
  78. data/lib/schleuder/validators/no_line_breaks_validator.rb +1 -1
  79. data/lib/schleuder/version.rb +1 -1
  80. data/locales/de.yml +49 -36
  81. data/locales/en.yml +34 -21
  82. metadata +119 -54
  83. data/bin/pinentry-clearpassphrase +0 -72
  84. data/lib/schleuder/plugin_runners/base.rb +0 -91
  85. data/lib/schleuder/plugin_runners/list_plugins_runner.rb +0 -24
  86. data/lib/schleuder/plugin_runners/request_plugins_runner.rb +0 -27
  87. data/lib/schleuder/plugins/attach_listkey.rb +0 -13
  88. data/lib/schleuder/plugins/get_version.rb +0 -7
  89. data/lib/schleuder/plugins/key_management.rb +0 -138
  90. data/lib/schleuder/plugins/list_management.rb +0 -15
  91. data/lib/schleuder/plugins/resend.rb +0 -199
  92. data/lib/schleuder/plugins/sign_this.rb +0 -46
  93. data/lib/schleuder/plugins/subscription_management.rb +0 -207
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- # This file can be deleted once we cease to support gnupg 2.0.
4
-
5
- require 'fileutils'
6
- require 'cgi'
7
- require 'openssl'
8
-
9
- def respond(msg, flush=true)
10
- $stdout.puts msg
11
- if flush
12
- $stdout.flush
13
- end
14
- end
15
-
16
- def send_ok(flush=true)
17
- respond 'OK', flush
18
- end
19
-
20
- def send_password
21
- if File.exist?(OLDPWDSENTFILE)
22
- pwd = ''
23
- if File.exist?(EMPTYPWDSENTFILE1)
24
- FileUtils.touch(EMPTYPWDSENTFILE2)
25
- else
26
- FileUtils.touch(EMPTYPWDSENTFILE1)
27
- end
28
- else
29
- pwd = OLDPASSWD
30
- FileUtils.touch(OLDPWDSENTFILE)
31
- end
32
- respond "D #{pwd}"
33
- end
34
-
35
- def do_exit
36
- if File.exist?(EMPTYPWDSENTFILE2)
37
- FileUtils.rm_rf(TMPDIR)
38
- end
39
- exit 0
40
- end
41
-
42
- OLDPASSWD = CGI.escape(ENV['PINENTRY_USER_DATA'].to_s)
43
- if OLDPASSWD.empty?
44
- respond "Fatal error: passed PINENTRY_USER_DATA was empty, cannot continue"
45
- exit 1
46
- end
47
-
48
- # We need a static directory name to maintain the state across invocations of
49
- # this file.
50
- TMPDIR = File.join(ENV['GNUPGHOME'], '.tmp-pinentry-clearpassphrase')
51
- OLDPWDSENTFILE = File.join(TMPDIR, '1')
52
- EMPTYPWDSENTFILE1 = File.join(TMPDIR, '2')
53
- EMPTYPWDSENTFILE2 = File.join(TMPDIR, '3')
54
- if ! Dir.exist?(TMPDIR)
55
- Dir.mkdir(TMPDIR)
56
- end
57
-
58
- respond "OK - what's up?"
59
-
60
- while line = $stdin.gets do
61
- case line
62
- when /^GETPIN/
63
- send_password
64
- send_ok
65
- when /^BYE/
66
- send_ok false
67
- do_exit
68
- else
69
- send_ok
70
- end
71
- end
72
-
@@ -1,91 +0,0 @@
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
@@ -1,24 +0,0 @@
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
@@ -1,27 +0,0 @@
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
-
@@ -1,13 +0,0 @@
1
- module Schleuder
2
- module ListPlugins
3
- def self.attach_listkey(arguments, list, mail)
4
- new_part = Mail::Part.new
5
- new_part.body = list.export_key
6
- new_part.content_type = 'application/pgp-keys'
7
- new_part.content_description = "OpenPGP public key of #{list.email}"
8
- new_part.content_disposition = "attachment; filename=#{list.fingerprint}.pgpkey"
9
- mail.add_part new_part
10
- nil
11
- end
12
- end
13
- end
@@ -1,7 +0,0 @@
1
- module Schleuder
2
- module RequestPlugins
3
- def self.get_version(arguments, list, mail)
4
- Schleuder::VERSION
5
- end
6
- end
7
- end
@@ -1,138 +0,0 @@
1
- module Schleuder
2
- module RequestPlugins
3
- def self.add_key(arguments, list, mail)
4
-
5
- if mail.has_attachments?
6
- results = self.import_keys_from_attachments(list, mail)
7
- else
8
- results = [self.import_key_from_body(list, mail)]
9
- end
10
-
11
- import_stati = results.compact.collect(&:imports).flatten
12
-
13
- if import_stati.blank?
14
- return I18n.t('plugins.key_management.no_imports')
15
- end
16
-
17
- out = []
18
-
19
- import_stati.each do |import_status|
20
- if import_status.action == 'error'
21
- out << I18n.t("plugins.key_management.key_import_status.error", fingerprint: import_status.fingerprint)
22
- else
23
- key = list.gpg.find_distinct_key(import_status.fingerprint)
24
- if key
25
- out << I18n.t("plugins.key_management.key_import_status.#{import_status.action}", key_oneline: key.oneline)
26
- end
27
- end
28
- end
29
-
30
- out.join("\n\n")
31
- end
32
-
33
- def self.delete_key(arguments, list, mail)
34
- if arguments.blank?
35
- return I18n.t(
36
- "plugins.key_management.delete_key_requires_arguments"
37
- )
38
- end
39
-
40
- arguments.map do |argument|
41
- keys = list.keys(argument)
42
- case keys.size
43
- when 0
44
- I18n.t("errors.no_match_for", input: argument)
45
- when 1
46
- begin
47
- keys.first.delete!
48
- I18n.t('plugins.key_management.deleted', key_string: keys.first.oneline)
49
- rescue GPGME::Error::Conflict
50
- I18n.t('plugins.key_management.not_deletable', key_string: keys.first.oneline)
51
- end
52
- else
53
- I18n.t('errors.too_many_matching_keys', {
54
- input: argument,
55
- key_strings: keys.map(&:to_s).join("\n")
56
- })
57
- end
58
- end.join("\n\n")
59
- end
60
-
61
- def self.list_keys(arguments, list, mail)
62
- args = Array(arguments.presence || '')
63
- args.map do |argument|
64
- # In this case it shall be allowed to match keys by arbitrary
65
- # sub-strings, therefore we use `list.gpg` directly to not have the
66
- # input filtered.
67
- list.gpg.keys(argument).map do |key|
68
- key.to_s
69
- end
70
- end.join("\n\n")
71
- end
72
-
73
- def self.get_key(arguments, list, mail)
74
- arguments.map do |argument|
75
- keys = list.keys(argument)
76
- if keys.blank?
77
- I18n.t("errors.no_match_for", input: argument)
78
- else
79
- result = [I18n.t('plugins.key_management.matching_keys_intro', input: argument)]
80
- keys.each do |key|
81
- atchm = Mail::Part.new
82
- atchm.body = key.armored
83
- atchm.content_type = 'application/pgp-keys'
84
- atchm.content_disposition = "attachment; filename=#{key.fingerprint}.asc"
85
- result << atchm
86
- end
87
- result.flatten
88
- end
89
- end
90
- end
91
-
92
- def self.fetch_key(arguments, list, mail)
93
- if arguments.blank?
94
- return I18n.t(
95
- "plugins.key_management.fetch_key_requires_arguments"
96
- )
97
- end
98
-
99
- arguments.map do |argument|
100
- list.fetch_keys(argument)
101
- end
102
- end
103
-
104
- # helper methods
105
- private
106
-
107
- def self.is_armored_key?(material)
108
- return false unless /^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ =~ material
109
- return false unless /^-----END PGP PUBLIC KEY BLOCK-----$/ =~ material
110
-
111
- lines = material.split("\n").reject(&:empty?)
112
- # remove header
113
- lines.shift
114
- # remove tail
115
- lines.pop
116
- # verify the rest
117
- # TODO: verify length except for lasts lines?
118
- # headers according to https://tools.ietf.org/html/rfc4880#section-6.2
119
- lines.map do |line|
120
- /\A((comment|version|messageid|hash|charset):.*|[0-9a-z\/=+]+)\Z/i =~ line
121
- end.all?
122
- end
123
-
124
- def self.import_keys_from_attachments(list, mail)
125
- mail.attachments.map do |attachment|
126
- material = attachment.body.to_s
127
-
128
- list.import_key(material) if self.is_armored_key?(material)
129
- end
130
- end
131
-
132
- def self.import_key_from_body(list, mail)
133
- key_material = mail.first_plaintext_part.body.to_s
134
-
135
- list.import_key(key_material) if self.is_armored_key?(key_material)
136
- end
137
- end
138
- end
@@ -1,15 +0,0 @@
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
@@ -1,199 +0,0 @@
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
- recip_map.keys.each do |aborted_sender|
60
- mail.add_pseudoheader(:error, I18n.t("plugins.resend.aborted", email: aborted_sender))
61
- end
62
- return
63
- end
64
-
65
- if do_resend(mail, recip_map, :cc, encrypted_only)
66
- mail.add_subject_prefix_out!
67
- end
68
- end
69
-
70
- def self.resend_it(arguments, mail, encrypted_only)
71
- if ! resend_recipients_valid?(mail, arguments)
72
- return false
73
- end
74
-
75
- recip_map = map_with_keys(mail, arguments, encrypted_only)
76
-
77
- resent_stati = recip_map.map do |email, key|
78
- do_resend(mail, {email => key}, :to, encrypted_only)
79
- end
80
-
81
- if resent_stati.include?(true)
82
- # At least one message has been resent
83
- mail.add_subject_prefix_out!
84
- end
85
- end
86
-
87
- def self.do_resend(mail, recipients_map, to_or_cc, encrypted_only)
88
- if recipients_map.empty?
89
- return
90
- end
91
-
92
- gpg_opts = make_gpg_opts(mail, recipients_map, encrypted_only)
93
- if gpg_opts == false
94
- return false
95
- end
96
-
97
- # Compose and send email
98
- new = mail.clean_copy
99
- new[to_or_cc] = recipients_map.keys
100
- new.add_public_footer!
101
- new.sender = mail.list.bounce_address
102
- # `dup` gpg_opts because `deliver` changes their value and we need them
103
- # below to determine encryption!
104
- new.gpg gpg_opts.dup
105
-
106
- if new.deliver
107
- add_resent_headers(mail, recipients_map, to_or_cc, gpg_opts[:encrypt])
108
- return true
109
- else
110
- add_error_header(mail, recipients_map)
111
- return false
112
- end
113
- rescue Net::SMTPFatalError => exc
114
- add_error_header(mail, recipients_map)
115
- logger.error "Error while sending: #{exc}"
116
- return false
117
- end
118
-
119
- def self.map_with_keys(mail, recipients, encrypted_only)
120
- Array(recipients).inject({}) do |hash, email|
121
- keys = mail.list.keys(email)
122
- # Exclude unusable keys.
123
- usable_keys = keys.select { |key| key.usable_for?(:encrypt) }
124
- case usable_keys.size
125
- when 1
126
- hash[email] = usable_keys.first
127
- when 0
128
- if encrypted_only
129
- # Don't add the email to the result to exclude it from the
130
- # recipients.
131
- add_resend_msg(mail, email, :error, 'not_resent_no_keys', usable_keys.size, keys.size)
132
- else
133
- hash[email] = ''
134
- end
135
- else
136
- # Always report this situation, regardless of sending or not. It's
137
- # bad and should be fixed.
138
- add_resend_msg(mail, email, :notice, 'not_resent_encrypted_no_keys', usable_keys.size, keys.size)
139
- if ! encrypted_only
140
- hash[email] = ''
141
- end
142
- end
143
- hash
144
- end
145
- end
146
-
147
- def self.make_gpg_opts(mail, recipients_map, encrypted_only)
148
- gpg_opts = mail.list.gpg_sign_options
149
- # Do all recipients have a key?
150
- if recipients_map.values.map(&:class).uniq == [GPGME::Key]
151
- gpg_opts.merge!(encrypt: true)
152
- elsif encrypted_only
153
- false
154
- end
155
- gpg_opts
156
- end
157
-
158
- def self.add_resend_msg(mail, email, severity, msg, usable_keys_size, all_keys_size)
159
- mail.add_pseudoheader(severity, I18n.t("plugins.resend.#{msg}", email: email, usable_keys: usable_keys_size, all_keys: all_keys_size))
160
- end
161
-
162
- def self.add_error_header(mail, recipients_map)
163
- mail.add_pseudoheader(:error, "Resending to #{recipients_map.keys.join(', ')} failed, please check the logs!")
164
- end
165
-
166
- def self.add_resent_headers(mail, recipients_map, to_or_cc, sent_encrypted)
167
- if sent_encrypted
168
- prefix = I18n.t('plugins.resend.encrypted_to')
169
- str = "\n" + recipients_map.map do |email, key|
170
- "#{email} (#{key.fingerprint})"
171
- end.join(",\n")
172
- else
173
- prefix = I18n.t('plugins.resend.unencrypted_to')
174
- str = ' ' + recipients_map.keys.join(", ")
175
- end
176
- headername = resent_header_name(to_or_cc)
177
- mail.add_pseudoheader(headername, "#{prefix}#{str}")
178
- end
179
-
180
- def self.resent_header_name(to_or_cc)
181
- if to_or_cc.to_s == 'to'
182
- 'resent'
183
- else
184
- 'resent_cc'
185
- end
186
- end
187
-
188
- def self.resend_recipients_valid?(mail, recipients)
189
- all_valid = true
190
- Array(recipients).each do |address|
191
- if ! address.match(Conf::EMAIL_REGEXP)
192
- mail.add_pseudoheader(:error, I18n.t("plugins.resend.invalid_recipient", address: address))
193
- all_valid = false
194
- end
195
- end
196
- all_valid
197
- end
198
- end
199
- end