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.
- checksums.yaml +5 -5
- data/README.md +138 -0
- data/Rakefile +136 -0
- data/bin/pinentry-clearpassphrase +72 -0
- data/bin/schleuder +9 -89
- data/bin/schleuder-api-daemon +4 -0
- data/db/migrate/20140501103532_create_lists.rb +39 -0
- data/db/migrate/20140501112859_create_subscriptions.rb +21 -0
- data/db/migrate/201508092100_add_language_to_lists.rb +11 -0
- data/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb +8 -0
- data/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb +11 -0
- data/db/migrate/201508141727_change_send_encrypted_only_default.rb +8 -0
- data/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb +11 -0
- data/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb +14 -0
- data/db/migrate/201508261815_strip_gpg_passphrase.rb +11 -0
- data/db/migrate/201508261827_remove_default_mime.rb +9 -0
- data/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb +8 -0
- data/db/migrate/20170713215059_add_internal_footer_to_list.rb +11 -0
- data/db/schema.rb +62 -0
- data/etc/init.d/schleuder-api-daemon +87 -0
- data/etc/list-defaults.yml +123 -0
- data/etc/postfix/schleuder_sqlite.cf +28 -0
- data/etc/schleuder-api-daemon.service +10 -0
- data/etc/schleuder.cron.weekly +6 -0
- data/etc/schleuder.yml +61 -0
- data/lib/schleuder-api-daemon.rb +420 -0
- data/lib/schleuder.rb +81 -47
- data/lib/schleuder/cli.rb +334 -0
- data/lib/schleuder/cli/cert.rb +24 -0
- data/lib/schleuder/cli/schleuder_cert_manager.rb +84 -0
- data/lib/schleuder/cli/subcommand_fix.rb +11 -0
- data/lib/schleuder/conf.rb +131 -0
- data/lib/schleuder/errors/active_model_error.rb +15 -0
- data/lib/schleuder/errors/base.rb +17 -0
- data/lib/schleuder/errors/decryption_failed.rb +16 -0
- data/lib/schleuder/errors/fatal_error.rb +13 -0
- data/lib/schleuder/errors/file_not_found.rb +14 -0
- data/lib/schleuder/errors/invalid_listname.rb +13 -0
- data/lib/schleuder/errors/key_adduid_failed.rb +13 -0
- data/lib/schleuder/errors/key_generation_failed.rb +16 -0
- data/lib/schleuder/errors/keyword_admin_only.rb +13 -0
- data/lib/schleuder/errors/list_exists.rb +13 -0
- data/lib/schleuder/errors/list_not_found.rb +14 -0
- data/lib/schleuder/errors/list_property_missing.rb +14 -0
- data/lib/schleuder/errors/listdir_problem.rb +16 -0
- data/lib/schleuder/errors/loading_list_settings_failed.rb +14 -0
- data/lib/schleuder/errors/message_empty.rb +14 -0
- data/lib/schleuder/errors/message_not_from_admin.rb +13 -0
- data/lib/schleuder/errors/message_sender_not_subscribed.rb +13 -0
- data/lib/schleuder/errors/message_too_big.rb +14 -0
- data/lib/schleuder/errors/message_unauthenticated.rb +13 -0
- data/lib/schleuder/errors/message_unencrypted.rb +13 -0
- data/lib/schleuder/errors/message_unsigned.rb +13 -0
- data/lib/schleuder/errors/standard_error.rb +5 -0
- data/lib/schleuder/errors/too_many_keys.rb +17 -0
- data/lib/schleuder/errors/unknown_list_option.rb +14 -0
- data/lib/schleuder/filters/auth_filter.rb +39 -0
- data/lib/schleuder/filters/bounces_filter.rb +12 -0
- data/lib/schleuder/filters/forward_filter.rb +17 -0
- data/lib/schleuder/filters/forward_incoming.rb +13 -0
- data/lib/schleuder/filters/hotmail_message_filter.rb +25 -0
- data/lib/schleuder/filters/max_message_size.rb +14 -0
- data/lib/schleuder/filters/request_filter.rb +26 -0
- data/lib/schleuder/filters/send_key_filter.rb +20 -0
- data/lib/schleuder/filters/strip_alternative_filter.rb +21 -0
- data/lib/schleuder/filters_runner.rb +83 -0
- data/lib/schleuder/gpgme/ctx.rb +274 -0
- data/lib/schleuder/gpgme/import_status.rb +27 -0
- data/lib/schleuder/gpgme/key.rb +212 -0
- data/lib/schleuder/gpgme/sub_key.rb +13 -0
- data/lib/schleuder/gpgme/user_id.rb +22 -0
- data/lib/schleuder/list.rb +318 -127
- data/lib/schleuder/list_builder.rb +139 -0
- data/lib/schleuder/listlogger.rb +31 -0
- data/lib/schleuder/logger.rb +23 -0
- data/lib/schleuder/logger_notifications.rb +69 -0
- data/lib/schleuder/mail/message.rb +482 -0
- data/lib/schleuder/mail/parts_list.rb +9 -0
- data/lib/schleuder/plugin_runners/base.rb +91 -0
- data/lib/schleuder/plugin_runners/list_plugins_runner.rb +24 -0
- data/lib/schleuder/plugin_runners/request_plugins_runner.rb +27 -0
- data/lib/schleuder/plugins/attach_listkey.rb +17 -0
- data/lib/schleuder/plugins/get_version.rb +7 -0
- data/lib/schleuder/plugins/key_management.rb +113 -0
- data/lib/schleuder/plugins/list_management.rb +15 -0
- data/lib/schleuder/plugins/resend.rb +196 -0
- data/lib/schleuder/plugins/sign_this.rb +46 -0
- data/lib/schleuder/plugins/subscription_management.rb +140 -0
- data/lib/schleuder/runner.rb +130 -0
- data/lib/schleuder/subscription.rb +98 -0
- data/lib/schleuder/validators/boolean_validator.rb +7 -0
- data/lib/schleuder/validators/email_validator.rb +7 -0
- data/lib/schleuder/validators/fingerprint_validator.rb +7 -0
- data/lib/schleuder/validators/greater_than_zero_validator.rb +7 -0
- data/lib/schleuder/validators/no_line_breaks_validator.rb +7 -0
- data/lib/schleuder/version.rb +1 -1
- data/locales/de.yml +179 -0
- data/locales/en.yml +179 -0
- metadata +305 -108
- checksums.yaml.gz.sig +0 -3
- data.tar.gz.sig +0 -2
- data/LICENSE +0 -339
- data/README +0 -32
- data/bin/schleuder-fix-gem-dependencies +0 -37
- data/bin/schleuder-init-setup +0 -37
- data/bin/schleuder-migrate-v2.1-to-v2.2 +0 -225
- data/bin/schleuder-newlist +0 -413
- data/contrib/check-expired-keys.rb +0 -60
- data/contrib/mutt-schleuder-colors.rc +0 -10
- data/contrib/mutt-schleuder-resend.vim +0 -24
- data/contrib/smtpserver.rb +0 -76
- data/ext/default-list.conf +0 -149
- data/ext/default-members.conf +0 -7
- data/ext/list.conf.example +0 -14
- data/ext/schleuder.conf +0 -64
- data/lib/schleuder/archiver.rb +0 -46
- data/lib/schleuder/crypt.rb +0 -210
- data/lib/schleuder/errors.rb +0 -5
- data/lib/schleuder/list_config.rb +0 -146
- data/lib/schleuder/log/listlogger.rb +0 -57
- data/lib/schleuder/log/outputter/emailoutputter.rb +0 -120
- data/lib/schleuder/log/outputter/metaemailoutputter.rb +0 -50
- data/lib/schleuder/log/schleuderlogger.rb +0 -34
- data/lib/schleuder/mail.rb +0 -873
- data/lib/schleuder/mailer.rb +0 -26
- data/lib/schleuder/member.rb +0 -69
- data/lib/schleuder/plugin.rb +0 -54
- data/lib/schleuder/processor.rb +0 -363
- data/lib/schleuder/schleuder_config.rb +0 -75
- data/lib/schleuder/storage.rb +0 -84
- data/lib/schleuder/utils.rb +0 -80
- data/man/schleuder-newlist.8 +0 -174
- data/man/schleuder.8 +0 -416
- data/plugins/README +0 -20
- data/plugins/manage_keys_plugin.rb +0 -113
- data/plugins/manage_members_plugin.rb +0 -156
- data/plugins/manage_self_plugin.rb +0 -26
- data/plugins/resend_plugin.rb +0 -35
- data/plugins/sign_this_plugin.rb +0 -14
- data/plugins/version_plugin.rb +0 -12
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Schleuder
|
|
2
|
+
module RequestPlugins
|
|
3
|
+
def self.sign_this(arguments, list, mail)
|
|
4
|
+
if mail.has_attachments?
|
|
5
|
+
list.logger.debug "Signing each attachment's body"
|
|
6
|
+
intro = I18n.t('plugins.signatures_attached')
|
|
7
|
+
parts = mail.attachments.map do |attachment|
|
|
8
|
+
make_signature_part(attachment, list)
|
|
9
|
+
end
|
|
10
|
+
[intro, parts].flatten
|
|
11
|
+
else
|
|
12
|
+
list.logger.debug "Clear-signing first available text/plain part"
|
|
13
|
+
clearsign(mail.first_plaintext_part)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# helper methods
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def self.make_signature_part(attachment, list)
|
|
21
|
+
material = attachment.body.to_s
|
|
22
|
+
return nil if material.strip.blank?
|
|
23
|
+
file_basename = attachment.filename.presence || Digest::SHA256.hexdigest(material)
|
|
24
|
+
list.logger.debug "Signing #{file_basename}"
|
|
25
|
+
filename = "#{file_basename}.sig"
|
|
26
|
+
part = Mail::Part.new
|
|
27
|
+
part.body = detachsign(material)
|
|
28
|
+
part.content_type = 'application/pgp-signature'
|
|
29
|
+
part.content_disposition = "attachment; filename=#{filename}"
|
|
30
|
+
part.content_description = "OpenPGP signature for '#{file_basename}'"
|
|
31
|
+
part
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.detachsign(thing)
|
|
35
|
+
crypto.sign(thing, mode: GPGME::SIG_MODE_DETACH).to_s
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.clearsign(mail)
|
|
39
|
+
crypto.clearsign(mail.body.to_s).to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.crypto
|
|
43
|
+
@crypto ||= GPGME::Crypto.new(armor: true)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module Schleuder
|
|
2
|
+
module RequestPlugins
|
|
3
|
+
def self.subscribe(arguments, list, mail)
|
|
4
|
+
email = arguments.shift
|
|
5
|
+
|
|
6
|
+
if arguments.present?
|
|
7
|
+
# Collect all arguments that look like fingerprint-material
|
|
8
|
+
fingerprint = ''
|
|
9
|
+
while arguments.first.present? && arguments.first.match(/\A(0x)?[a-f0-9]+/i)
|
|
10
|
+
fingerprint << arguments.shift
|
|
11
|
+
end
|
|
12
|
+
# Use possibly remaining args as flags.
|
|
13
|
+
adminflag = arguments.shift
|
|
14
|
+
deliveryflag = arguments.shift
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
sub, _ = list.subscribe(email, fingerprint, adminflag, deliveryflag)
|
|
18
|
+
|
|
19
|
+
if sub.persisted?
|
|
20
|
+
I18n.t(
|
|
21
|
+
"plugins.subscription_management.subscribed",
|
|
22
|
+
email: sub.email,
|
|
23
|
+
fingerprint: sub.fingerprint,
|
|
24
|
+
admin: sub.admin,
|
|
25
|
+
delivery_enabled: sub.delivery_enabled
|
|
26
|
+
)
|
|
27
|
+
else
|
|
28
|
+
I18n.t(
|
|
29
|
+
"plugins.subscription_management.subscribing_failed",
|
|
30
|
+
email: sub.email,
|
|
31
|
+
errors: sub.errors.full_messages.join(".\n")
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.unsubscribe(arguments, list, mail)
|
|
37
|
+
# If no address was given we unsubscribe the sender.
|
|
38
|
+
email = arguments.first.presence || mail.signer.email
|
|
39
|
+
|
|
40
|
+
# TODO: May signers have multiple UIDs? We don't match those currently.
|
|
41
|
+
if ! list.from_admin?(mail) && email != mail.signer.email
|
|
42
|
+
# Only admins may unsubscribe others.
|
|
43
|
+
return I18n.t(
|
|
44
|
+
"plugins.subscription_management.forbidden", email: email
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
sub = list.subscriptions.where(email: email).first
|
|
49
|
+
|
|
50
|
+
if sub.blank?
|
|
51
|
+
return I18n.t(
|
|
52
|
+
"plugins.subscription_management.is_not_subscribed", email: email
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if res = sub.delete
|
|
57
|
+
I18n.t(
|
|
58
|
+
"plugins.subscription_management.unsubscribed", email: email
|
|
59
|
+
)
|
|
60
|
+
else
|
|
61
|
+
I18n.t(
|
|
62
|
+
"plugins.subscription_management.unsubscribing_failed",
|
|
63
|
+
email: email,
|
|
64
|
+
error: res.errors.to_a
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.list_subscriptions(arguments, list, mail)
|
|
70
|
+
subs = if arguments.blank?
|
|
71
|
+
list.subscriptions.all.to_a
|
|
72
|
+
else
|
|
73
|
+
arguments.map do |argument|
|
|
74
|
+
list.subscriptions.where("email like ?", "%#{argument}%").to_a
|
|
75
|
+
end.flatten
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if subs.blank?
|
|
79
|
+
return nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
out = [ I18n.t("plugins.subscription_management.list_of_subscriptions") ]
|
|
83
|
+
|
|
84
|
+
out << subs.map do |subscription|
|
|
85
|
+
# Fingerprints are at most 40 characters long, and lines shouldn't
|
|
86
|
+
# exceed 80 characters if possible.
|
|
87
|
+
s = subscription.email
|
|
88
|
+
if subscription.fingerprint.present?
|
|
89
|
+
s << "\t0x#{subscription.fingerprint}"
|
|
90
|
+
end
|
|
91
|
+
if ! subscription.delivery_enabled?
|
|
92
|
+
s << "\tDelivery disabled!"
|
|
93
|
+
end
|
|
94
|
+
s
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
out.join("\n")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def self.set_fingerprint(arguments, list, mail)
|
|
101
|
+
if arguments.first.match(/@/)
|
|
102
|
+
if arguments.first == mail.signer.email || list.from_admin?(mail)
|
|
103
|
+
email = arguments.shift
|
|
104
|
+
else
|
|
105
|
+
return I18n.t(
|
|
106
|
+
"plugins.subscription_management.set_fingerprint_only_self"
|
|
107
|
+
)
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
email = mail.signer.email
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
sub = list.subscriptions.where(email: email).first
|
|
114
|
+
|
|
115
|
+
if sub.blank?
|
|
116
|
+
return I18n.t(
|
|
117
|
+
"plugins.subscription_management.is_not_subscribed", email: email
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
sub.fingerprint = arguments.join
|
|
122
|
+
|
|
123
|
+
if sub.save
|
|
124
|
+
I18n.t(
|
|
125
|
+
"plugins.subscription_management.fingerprint_set",
|
|
126
|
+
email: email,
|
|
127
|
+
fingerprint: sub.fingerprint
|
|
128
|
+
)
|
|
129
|
+
else
|
|
130
|
+
I18n.t(
|
|
131
|
+
"plugins.subscription_management.setting_fingerprint_failed",
|
|
132
|
+
email: email,
|
|
133
|
+
fingerprint: arguments.last,
|
|
134
|
+
errors: sub.errors.to_a.join("\n")
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module Schleuder
|
|
2
|
+
class Runner
|
|
3
|
+
def run(msg, recipient)
|
|
4
|
+
error = setup_list(recipient)
|
|
5
|
+
return error if error
|
|
6
|
+
|
|
7
|
+
logger.info "Parsing incoming email."
|
|
8
|
+
@mail = Mail.create_message_to_list(msg, recipient, list)
|
|
9
|
+
|
|
10
|
+
error = run_filters(Filters::Runner::PRE_SETUP_FILTERS)
|
|
11
|
+
return error if error
|
|
12
|
+
|
|
13
|
+
begin
|
|
14
|
+
# This decrypts, verifies, etc.
|
|
15
|
+
@mail = @mail.setup
|
|
16
|
+
rescue GPGME::Error::DecryptFailed
|
|
17
|
+
logger.warn "Decryption of incoming message failed."
|
|
18
|
+
return Errors::DecryptionFailed.new(list)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
error = run_filters(Filters::Runner::POST_SETUP_FILTERS)
|
|
22
|
+
return error if error
|
|
23
|
+
|
|
24
|
+
if ! @mail.was_validly_signed?
|
|
25
|
+
logger.debug "Message was not validly signed, adding subject_prefix_in"
|
|
26
|
+
@mail.add_subject_prefix_in!
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if ! @mail.was_encrypted?
|
|
30
|
+
logger.debug "Message was not encrypted, skipping plugins"
|
|
31
|
+
elsif @mail.was_validly_signed?
|
|
32
|
+
# Plugins
|
|
33
|
+
logger.debug "Message was encrypted and validly signed"
|
|
34
|
+
PluginRunners::ListPluginsRunner.run(list, @mail).compact
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Don't send empty messages over the list.
|
|
38
|
+
if @mail.empty?
|
|
39
|
+
logger.info "Message found empty, not sending it to list."
|
|
40
|
+
return Errors::MessageEmpty.new(@list)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
logger.debug "Adding subject_prefix"
|
|
44
|
+
@mail.add_subject_prefix!
|
|
45
|
+
|
|
46
|
+
# Subscriptions
|
|
47
|
+
logger.debug "Creating clean copy of message"
|
|
48
|
+
copy = @mail.clean_copy(true)
|
|
49
|
+
list.send_to_subscriptions(copy)
|
|
50
|
+
nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def list
|
|
56
|
+
@list
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def run_filters(filters)
|
|
60
|
+
error = filters_runner.run(@mail, filters)
|
|
61
|
+
if error
|
|
62
|
+
if list.bounces_notify_admins?
|
|
63
|
+
text = "#{I18n.t('.bounces_notify_admins')}\n\n#{error}"
|
|
64
|
+
# TODO: raw_source is mostly blank?
|
|
65
|
+
logger.notify_admin text, @mail.original_message, I18n.t('notice')
|
|
66
|
+
end
|
|
67
|
+
return error
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def filters_runner
|
|
72
|
+
@filters_runner ||= Filters::Runner.new(list)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def logger
|
|
76
|
+
list.present? && list.logger || Schleuder.logger
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def log_and_return(error, reveal_error=false)
|
|
80
|
+
Schleuder.logger.error(error)
|
|
81
|
+
if reveal_error
|
|
82
|
+
error
|
|
83
|
+
else
|
|
84
|
+
# Return an unrevealing error, the sender and all bystanders don't need to know these details.
|
|
85
|
+
Errors::FatalError.new
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def setup_list(recipient)
|
|
90
|
+
return @list if @list
|
|
91
|
+
|
|
92
|
+
logger.info "Loading list '#{recipient}'"
|
|
93
|
+
if ! @list = List.by_recipient(recipient)
|
|
94
|
+
return log_and_return(Errors::ListNotFound.new(recipient), true)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Check neccessary permissions of crucial files.
|
|
98
|
+
if ! File.exist?(@list.listdir)
|
|
99
|
+
return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_existing))
|
|
100
|
+
elsif ! File.directory?(@list.listdir)
|
|
101
|
+
return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_a_directory))
|
|
102
|
+
elsif ! File.readable?(@list.listdir)
|
|
103
|
+
return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_readable))
|
|
104
|
+
elsif ! File.writable?(@list.listdir)
|
|
105
|
+
return log_and_return(Errors::ListdirProblem.new(@list.listdir, :not_writable))
|
|
106
|
+
else
|
|
107
|
+
if File.exist?(@list.logfile) && ! File.writable?(@list.logfile)
|
|
108
|
+
return log_and_return(Errors::ListdirProblem.new(@list.logfile, :not_writable))
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Check basic sanity of list.
|
|
113
|
+
%w[fingerprint key secret_key admins].each do |attrib|
|
|
114
|
+
if @list.send(attrib).blank?
|
|
115
|
+
return log_and_return(Errors::ListPropertyMissing.new(@list.listdir, attrib))
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Set locale
|
|
120
|
+
if I18n.available_locales.include?(@list.language.to_sym)
|
|
121
|
+
I18n.locale = @list.language.to_sym
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# This cannot be put in List, as Mail wouldn't know it then.
|
|
125
|
+
logger.debug "Setting GNUPGHOME to #{@list.listdir}"
|
|
126
|
+
ENV['GNUPGHOME'] = @list.listdir
|
|
127
|
+
nil
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module Schleuder
|
|
2
|
+
class Subscription < ActiveRecord::Base
|
|
3
|
+
belongs_to :list
|
|
4
|
+
|
|
5
|
+
validates :list_id, inclusion: {
|
|
6
|
+
in: -> (id) { List.pluck(:id) },
|
|
7
|
+
message: "must refer to an existing list"
|
|
8
|
+
}
|
|
9
|
+
validates :email, presence: true, email: true, uniqueness: {scope: :list_id}
|
|
10
|
+
validates :fingerprint, allow_blank: true, fingerprint: true
|
|
11
|
+
validates :delivery_enabled, :admin, boolean: true
|
|
12
|
+
|
|
13
|
+
default_scope { order(:email) }
|
|
14
|
+
|
|
15
|
+
scope :without_fingerprint, -> { where(fingerprint: [nil,'']) }
|
|
16
|
+
|
|
17
|
+
def to_s
|
|
18
|
+
email
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.configurable_attributes
|
|
22
|
+
[:fingerprint, :admin, :delivery_enabled]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def fingerprint=(arg)
|
|
26
|
+
# Allow input to contain whitespace and '0x'-prefix, but don't store it
|
|
27
|
+
# into the DB.
|
|
28
|
+
value = arg.to_s.gsub(/\s*/, '').gsub(/^0x/, '').chomp
|
|
29
|
+
write_attribute(:fingerprint, value)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def key
|
|
33
|
+
# TODO: make key-related methods a concern, so we don't have to go
|
|
34
|
+
# through the list and neither re-implement the methods here.
|
|
35
|
+
# Prefix '0x' to force GnuPG to match only hex-values, not UIDs.
|
|
36
|
+
list.keys("0x#{self.fingerprint}").first
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def send_mail(mail)
|
|
40
|
+
list.logger.debug "Preparing sending to #{self.inspect}"
|
|
41
|
+
|
|
42
|
+
if ! self.delivery_enabled
|
|
43
|
+
list.logger.info "Not sending to #{self.email}: delivery is disabled."
|
|
44
|
+
return false
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
mail = ensure_headers(mail)
|
|
48
|
+
gpg_opts = self.list.gpg_sign_options
|
|
49
|
+
|
|
50
|
+
if self.key.blank?
|
|
51
|
+
if self.list.send_encrypted_only?
|
|
52
|
+
notify_of_missed_message(:absent)
|
|
53
|
+
return false
|
|
54
|
+
else
|
|
55
|
+
list.logger.warn "Sending plaintext because no key is present!"
|
|
56
|
+
end
|
|
57
|
+
elsif ! self.key.usable?
|
|
58
|
+
if self.list.send_encrypted_only?
|
|
59
|
+
notify_of_missed_message(key.usability_issue)
|
|
60
|
+
return false
|
|
61
|
+
else
|
|
62
|
+
list.logger.warn "Sending plaintext because assigned key is #{key.usability_issue}!"
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
gpg_opts.merge!(encrypt: true, keys: {self.email => "0x#{self.fingerprint}"})
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
list.logger.info "Sending message to #{self.email}"
|
|
69
|
+
mail.gpg gpg_opts
|
|
70
|
+
mail.deliver
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def ensure_headers(mail)
|
|
74
|
+
mail.to = self.email
|
|
75
|
+
mail.from = self.list.email
|
|
76
|
+
mail.sender = self.list.bounce_address
|
|
77
|
+
mail
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def notify_of_missed_message(reason)
|
|
81
|
+
self.list.logger.warn "Not sending to #{self.email}: key is unusable because it is #{reason} and sending plain text not allowed"
|
|
82
|
+
mail = ensure_headers(Mail.new)
|
|
83
|
+
mail.subject = I18n.t('notice')
|
|
84
|
+
mail.body = I18n.t("missed_message_due_to_unusable_key", list_email: self.list.email) + I18n.t('errors.signoff')
|
|
85
|
+
mail.gpg self.list.gpg_sign_options
|
|
86
|
+
mail.deliver
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def admin?
|
|
90
|
+
self.admin == true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def delete_key
|
|
94
|
+
list.delete_key(self.fingerprint)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|