schleuder 3.4.1 → 3.6.0
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 +4 -4
- data/README.md +10 -9
- data/Rakefile +6 -4
- data/db/migrate/20180723173900_add_deliver_selfsent_to_list.rb +11 -0
- data/db/migrate/20190906194820_add_autocrypt_header_to_list.rb +11 -0
- data/db/migrate/20200118170110_add_set_reply_to_to_sender_and_munge_from.rb +15 -0
- data/db/schema.rb +5 -1
- data/etc/list-defaults.yml +26 -0
- data/lib/schleuder.rb +10 -0
- data/lib/schleuder/cli.rb +1 -0
- data/lib/schleuder/conf.rb +7 -1
- data/lib/schleuder/gpgme/key.rb +4 -0
- data/lib/schleuder/list.rb +39 -4
- data/lib/schleuder/logger_notifications.rb +2 -0
- data/lib/schleuder/mail/message.rb +105 -22
- data/lib/schleuder/plugins/attach_listkey.rb +6 -10
- data/lib/schleuder/plugins/key_management.rb +2 -19
- data/lib/schleuder/plugins/resend.rb +14 -11
- data/lib/schleuder/runner.rb +30 -2
- data/lib/schleuder/subscription.rb +27 -4
- data/lib/schleuder/version.rb +1 -1
- data/locales/de.yml +5 -1
- data/locales/en.yml +5 -1
- metadata +27 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 648f81aa424a3214a9d03fb66e5b3e216c8990e092d1b5b18ab41a1f0a1e5fd3
|
4
|
+
data.tar.gz: 62a5a77189860b4228a2168690f3990d02e53ee4dfb6c458a0273b5407e8a2e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97a79b18f6655bd6f9c56beded7527e061fd955433ac2e436a28176eef9d8903916b16a1b0ee22a51f600fbd56d4639b5fadd5a53f3df08d4ca156fc805c9cbb
|
7
|
+
data.tar.gz: 6e6b4ea2847974d64a3b38a0d99ec0bfa5bb824a36d566fa731889a711b88f127ad6314f9f44b8dd99937977dc0f9d9f2530419762fae527bb24e37b10517343
|
data/README.md
CHANGED
@@ -15,14 +15,15 @@ Requirements
|
|
15
15
|
* gpgme
|
16
16
|
* sqlite3
|
17
17
|
* openssl
|
18
|
+
* icu
|
18
19
|
|
19
|
-
*If you use Debian
|
20
|
+
*If you use Debian buster or CentOS 7, please have a look at the [installation docs](https://schleuder.org/schleuder/docs/server-admins.html#installation). We do provide packages for those platforms, which simplify the installation a lot.*
|
20
21
|
|
21
22
|
*🛈 A note regarding Ubuntu: All Ubuntu versions up to and including 17.10 don't meet the requirements with their packaged versions of gnupg! To run Schleuder on Ubuntu you currently have to install a more recent version of gnupg manually. Only Ubuntu 18.04 ("bionic") provides modern enough versions of Schleuder's requirements.*
|
22
23
|
|
23
|
-
On systems that base on Debian
|
24
|
+
On systems that base on Debian 10 ("buster"), install the dependencies via
|
24
25
|
|
25
|
-
apt-get install ruby-dev gnupg2 libgpgme-dev libsqlite3-dev libssl-dev build-essential
|
26
|
+
apt-get install ruby-dev gnupg2 libgpgme-dev libsqlite3-dev libssl-dev build-essential libicu-dev
|
26
27
|
|
27
28
|
|
28
29
|
We **recommend** to also run a random number generator like [haveged](http://www.issihosts.com/haveged/). This ensures Schleuder won't be blocked by lacking entropy, which otherwise might happen especially during key generation.
|
@@ -47,15 +48,15 @@ Additionally these **rubygems** are required (will be installed automatically un
|
|
47
48
|
Installing Schleuder
|
48
49
|
------------
|
49
50
|
|
50
|
-
1. Download [the gem](https://schleuder.org/download/schleuder-3.
|
51
|
+
1. Download [the gem](https://schleuder.org/download/schleuder-3.6.0.gem) and [the OpenPGP-signature](https://schleuder.org/download/schleuder-3.6.0.gem.sig) and verify:
|
51
52
|
```
|
52
53
|
gpg --recv-key 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3
|
53
|
-
gpg --verify schleuder-3.
|
54
|
+
gpg --verify schleuder-3.6.0.gem.sig
|
54
55
|
```
|
55
56
|
|
56
57
|
2. If all went well install the gem:
|
57
58
|
```
|
58
|
-
gem install schleuder-3.
|
59
|
+
gem install schleuder-3.6.0.gem
|
59
60
|
```
|
60
61
|
|
61
62
|
3. Set up schleuder:
|
@@ -65,7 +66,7 @@ Installing Schleuder
|
|
65
66
|
This creates necessary directories, copies example configs, etc. If you see errors about missing write permissions please follow the advice given.
|
66
67
|
|
67
68
|
|
68
|
-
For further information on setup and configuration please read <https://schleuder.org/docs
|
69
|
+
For further information on setup and configuration please read <https://schleuder.org/schleuder/docs/server-admins.html>.
|
69
70
|
|
70
71
|
|
71
72
|
Command line usage
|
@@ -112,7 +113,7 @@ To execute the test suite run:
|
|
112
113
|
|
113
114
|
bundle exec rspec
|
114
115
|
|
115
|
-
Please note: Some of the specs use 'pgrep'. On systems that base on Debian
|
116
|
+
Please note: Some of the specs use 'pgrep'. On systems that base on Debian 10 ("buster") install it via
|
116
117
|
|
117
118
|
apt-get install procps
|
118
119
|
|
@@ -145,4 +146,4 @@ GNU GPL 3.0. Please see [LICENSE.txt](LICENSE.txt).
|
|
145
146
|
Alternative Download
|
146
147
|
--------------------
|
147
148
|
|
148
|
-
Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-3.
|
149
|
+
Alternatively to the gem-files you can download the latest release as [a tarball](https://schleuder.org/download/schleuder-3.6.0.tar.gz) and [its OpenPGP-signature](https://schleuder.org/download/schleuder-3.6.0.tar.gz.sig).
|
data/Rakefile
CHANGED
@@ -3,12 +3,14 @@ require_relative "lib/#{project}.rb"
|
|
3
3
|
|
4
4
|
@version = Schleuder::VERSION
|
5
5
|
@tagname = "#{project}-#{@version}"
|
6
|
-
@gpguid = '
|
6
|
+
@gpguid = 'B3D190D5235C74E1907EACFE898F2C91E2E6E1F3'
|
7
7
|
@filename_gem = "#{@tagname}.gem"
|
8
8
|
@filename_tarball = "#{@tagname}.tar.gz"
|
9
9
|
|
10
10
|
load "active_record/railties/databases.rake"
|
11
11
|
|
12
|
+
puts @filename_gem
|
13
|
+
|
12
14
|
# Configure ActiveRecord
|
13
15
|
ActiveRecord::Tasks::DatabaseTasks.tap do |config|
|
14
16
|
config.root = File.dirname(__FILE__)
|
@@ -89,7 +91,7 @@ end
|
|
89
91
|
|
90
92
|
desc 'OpenPGP-sign gem and tarball'
|
91
93
|
task :sign_tarball do
|
92
|
-
`gpg -u #{@gpguid} -b #{@filename_tarball}`
|
94
|
+
`gpg -v -u #{@gpguid} -b #{@filename_tarball}`
|
93
95
|
end
|
94
96
|
|
95
97
|
desc 'OpenPGP-sign gem'
|
@@ -110,7 +112,7 @@ end
|
|
110
112
|
desc 'Publish gem-file to rubygems.org'
|
111
113
|
task :publish_gem do
|
112
114
|
puts "Really push #{@filename_gem} to rubygems.org? [yN]"
|
113
|
-
if gets.match(/^y/i)
|
115
|
+
if $stdin.gets.match(/^y/i)
|
114
116
|
puts "Pushing..."
|
115
117
|
`gem push #{@filename_gem}`
|
116
118
|
else
|
@@ -132,7 +134,7 @@ desc 'Check if version-tag already exists'
|
|
132
134
|
task :check_version do
|
133
135
|
# Check if Schleuder::VERSION has been updated since last release
|
134
136
|
if `git tag`.match?(/^#{@tagname}$/)
|
135
|
-
$stderr.puts "Warning: Tag '#{@tagname}' already exists. Did you forget to update
|
137
|
+
$stderr.puts "Warning: Tag '#{@tagname}' already exists. Did you forget to update lib/#{project}/version.rb?"
|
136
138
|
$stderr.print "Delete tag to continue? [yN] "
|
137
139
|
if $stdin.gets.match(/^y/i)
|
138
140
|
`git tag -d #{@tagname}`
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AddDeliverSelfsentToList < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
if ! column_exists?(:lists, :deliver_selfsent)
|
4
|
+
add_column :lists, :deliver_selfsent, :boolean, default: true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def down
|
9
|
+
remove_column(:lists, :deliver_selfsent)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AddAutocryptHeaderToList < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
if ! column_exists?(:lists, :include_autocrypt_header)
|
4
|
+
add_column :lists, :include_autocrypt_header, :boolean, default: true
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def down
|
9
|
+
remove_column(:lists, :include_autocrypt_header)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class AddSetReplyToToSenderAndMungeFrom < ActiveRecord::Migration
|
2
|
+
def up
|
3
|
+
if ! column_exists?(:lists, :set_reply_to_to_sender)
|
4
|
+
add_column :lists, :set_reply_to_to_sender, :boolean, default: false
|
5
|
+
end
|
6
|
+
if ! column_exists?(:lists, :munge_from)
|
7
|
+
add_column :lists, :munge_from, :boolean, default: false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def down
|
12
|
+
remove_column(:lists, :set_reply_to_to_sender)
|
13
|
+
remove_column(:lists, :munge_from)
|
14
|
+
end
|
15
|
+
end
|
data/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20200118170110) do
|
15
15
|
|
16
16
|
create_table "lists", force: :cascade do |t|
|
17
17
|
t.datetime "created_at"
|
@@ -37,6 +37,7 @@ ActiveRecord::Schema.define(version: 20180110203100) do
|
|
37
37
|
t.boolean "keep_msgid", default: true
|
38
38
|
t.boolean "bounces_drop_all", default: false
|
39
39
|
t.boolean "bounces_notify_admins", default: true
|
40
|
+
t.boolean "deliver_selfsent", default: true
|
40
41
|
t.boolean "include_list_headers", default: true
|
41
42
|
t.boolean "include_openpgp_header", default: true
|
42
43
|
t.integer "max_message_size_kb", default: 10240
|
@@ -44,6 +45,9 @@ ActiveRecord::Schema.define(version: 20180110203100) do
|
|
44
45
|
t.boolean "forward_all_incoming_to_admins", default: false
|
45
46
|
t.integer "logfiles_to_keep", default: 2
|
46
47
|
t.text "internal_footer", default: ""
|
48
|
+
t.boolean "include_autocrypt_header", default: true
|
49
|
+
t.boolean "set_reply_to_to_sender", default: false
|
50
|
+
t.boolean "munge_from", default: false
|
47
51
|
end
|
48
52
|
|
49
53
|
create_table "subscriptions", force: :cascade do |t|
|
data/etc/list-defaults.yml
CHANGED
@@ -93,6 +93,9 @@ bounces_drop_on_headers:
|
|
93
93
|
# Send a notice to the list-admins whenever an email is bounced or dropped?
|
94
94
|
bounces_notify_admins: true
|
95
95
|
|
96
|
+
# Include Autocrypt header into emails?
|
97
|
+
include_autocrypt_header: true
|
98
|
+
|
96
99
|
# Include RFC-compliant List-* Headers into emails?
|
97
100
|
include_list_headers: true
|
98
101
|
|
@@ -123,3 +126,26 @@ language: en
|
|
123
126
|
# Forward a raw copy of all incoming emails to the list-admins?
|
124
127
|
# Mainly useful for debugging.
|
125
128
|
forward_all_incoming_to_admins: false
|
129
|
+
|
130
|
+
# Should e-mails be delivered to the original subscribed sender?
|
131
|
+
# Disabling this only works for signed e-mails; any e-mail that is unsigned
|
132
|
+
# sent to the list is treated as coming from an unknown source
|
133
|
+
deliver_selfsent: true
|
134
|
+
|
135
|
+
# Set reply-to header to original sender's reply-to?
|
136
|
+
# Enabling this will set the reply-to-header of emails sent by schleuder
|
137
|
+
# to the original sender's reply-to-header. If the original sender
|
138
|
+
# did not supply a reply-to-header, the original from-header will be used.
|
139
|
+
# This option can enabled for improved usability since this affect
|
140
|
+
# mail client's reply-to button to reply to the original sender instead of
|
141
|
+
# the whole list.
|
142
|
+
set_reply_to_to_sender: false
|
143
|
+
|
144
|
+
# Munge from-header?
|
145
|
+
# Enabling this option will add the original sender to the from-header.
|
146
|
+
# To avoid DMARC issues, we still use the list's address as from-address.
|
147
|
+
# However the sender's address will be included as displayed name.
|
148
|
+
# For example: "sender@sender.org via list@list.org" <list@list.org>
|
149
|
+
# This option can enabled for improved usability since this affect
|
150
|
+
# mail client's displayed name.
|
151
|
+
munge_from: false
|
data/lib/schleuder.rb
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
# default to UTF-8 encoding as early as possible for external
|
2
|
+
# data.
|
3
|
+
#
|
4
|
+
# this should ensure we are able to parse most incoming
|
5
|
+
# plain text mails in different charsets.
|
6
|
+
Encoding.default_external = Encoding::UTF_8
|
7
|
+
|
1
8
|
# Stdlib
|
2
9
|
require 'fileutils'
|
3
10
|
require 'singleton'
|
@@ -61,6 +68,9 @@ ENV["SCHLEUDER_CONFIG"] ||= '/etc/schleuder/schleuder.yml'
|
|
61
68
|
ENV["SCHLEUDER_LIST_DEFAULTS"] ||= '/etc/schleuder/list-defaults.yml'
|
62
69
|
ENV["SCHLEUDER_ENV"] ||= 'production'
|
63
70
|
ENV["SCHLEUDER_ROOT"] = rootdir.to_s
|
71
|
+
# Ensure that gnupg never-ever tries to ask for a passphrase.
|
72
|
+
ENV["GPG_TTY"] = "/nonexistant-#{rand}"
|
73
|
+
ENV["DISPLAY"] = nil
|
64
74
|
|
65
75
|
GPGME::Ctx.set_gpg_path_from_env
|
66
76
|
GPGME::Ctx.check_gpg_version
|
data/lib/schleuder/cli.rb
CHANGED
data/lib/schleuder/conf.rb
CHANGED
@@ -4,7 +4,13 @@ module Schleuder
|
|
4
4
|
class Conf
|
5
5
|
include Singleton
|
6
6
|
|
7
|
-
|
7
|
+
# since the regexp got only included into stdlib 2.2
|
8
|
+
# TODO: remove once 2.1 support dropped
|
9
|
+
if RUBY_VERSION < '2.2'
|
10
|
+
EMAIL_REGEXP = /\A[a-zA-Z0-9.!\#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/
|
11
|
+
else
|
12
|
+
EMAIL_REGEXP = URI::MailTo::EMAIL_REGEXP
|
13
|
+
end
|
8
14
|
# TODO: drop v3 keys and only accept length of 40
|
9
15
|
FINGERPRINT_REGEXP = /\A(0x)?[a-f0-9]{32}([a-f0-9]{8})?\z/i
|
10
16
|
|
data/lib/schleuder/gpgme/key.rb
CHANGED
@@ -57,6 +57,10 @@ module GPGME
|
|
57
57
|
"#{self.to_s}\n\n#{export(armor: true).read}"
|
58
58
|
end
|
59
59
|
|
60
|
+
def minimal
|
61
|
+
export(minimal: true).to_s
|
62
|
+
end
|
63
|
+
|
60
64
|
# Force encoding, some databases save "ASCII-8BIT" as binary data.
|
61
65
|
alias_method :orig_fingerprint, :fingerprint
|
62
66
|
def fingerprint
|
data/lib/schleuder/list.rb
CHANGED
@@ -19,6 +19,7 @@ module Schleuder
|
|
19
19
|
:receive_admin_only,
|
20
20
|
:keep_msgid,
|
21
21
|
:bounces_drop_all,
|
22
|
+
:deliver_selfsent,
|
22
23
|
:bounces_notify_admins,
|
23
24
|
:include_list_headers,
|
24
25
|
:include_openpgp_header,
|
@@ -66,6 +67,22 @@ module Schleuder
|
|
66
67
|
with: /\A[[:graph:]\s]*\z/i,
|
67
68
|
}
|
68
69
|
|
70
|
+
# Some users find it quite confusing when they click "reply-to" and the mail client
|
71
|
+
# doesn't reply to the sender of the mail but the whole mailing list. For those lists it can be
|
72
|
+
# considered to set this value to true. The recipients will then receive e-mails
|
73
|
+
# where the "reply-to" header will contain the reply-to address
|
74
|
+
# of the sender and thus reply to the sender when clicking "reply-to" in a client.
|
75
|
+
# If no "reply-to" is set, the "from"-header of the original sender will be used.
|
76
|
+
# The default is off.
|
77
|
+
validates :set_reply_to_to_sender, boolean: true
|
78
|
+
|
79
|
+
# Some users find it confusing when the "from" does not contain the original sender
|
80
|
+
# but the list address. For those lists it can be considered to set the munged header.
|
81
|
+
# This will result in a "from"-header like this: "originalsender@original.com via list@list.com"
|
82
|
+
# The default is off.
|
83
|
+
validates :munge_from, boolean: true
|
84
|
+
|
85
|
+
|
69
86
|
default_scope { order(:email) }
|
70
87
|
|
71
88
|
def self.configurable_attributes
|
@@ -146,6 +163,16 @@ module Schleuder
|
|
146
163
|
key.armored
|
147
164
|
end
|
148
165
|
|
166
|
+
def key_minimal_base64_encoded(fingerprint=self.fingerprint)
|
167
|
+
key = keys(fingerprint).first
|
168
|
+
|
169
|
+
if key.blank?
|
170
|
+
return false
|
171
|
+
end
|
172
|
+
|
173
|
+
Base64.strict_encode64(key.minimal)
|
174
|
+
end
|
175
|
+
|
149
176
|
def check_keys
|
150
177
|
now = Time.now
|
151
178
|
checkdate = now + (60 * 60 * 24 * 14) # two weeks
|
@@ -340,16 +367,24 @@ module Schleuder
|
|
340
367
|
true
|
341
368
|
end
|
342
369
|
|
343
|
-
def send_to_subscriptions(mail)
|
370
|
+
def send_to_subscriptions(mail, incoming_mail=nil)
|
344
371
|
logger.debug "Sending to subscriptions."
|
345
372
|
mail.add_internal_footer!
|
346
373
|
self.subscriptions.each do |subscription|
|
347
374
|
begin
|
348
|
-
|
349
|
-
|
350
|
-
else
|
375
|
+
|
376
|
+
if ! subscription.delivery_enabled
|
351
377
|
logger.info "Not sending to #{subscription.email}: delivery is disabled."
|
378
|
+
next
|
379
|
+
end
|
380
|
+
|
381
|
+
if ! self.deliver_selfsent && incoming_mail.was_validly_signed? && ( subscription == incoming_mail.signer )
|
382
|
+
logger.info "Not sending to #{subscription.email}: delivery of self sent is disabled."
|
383
|
+
next
|
352
384
|
end
|
385
|
+
|
386
|
+
subscription.send_mail(mail, incoming_mail)
|
387
|
+
|
353
388
|
rescue => exc
|
354
389
|
msg = I18n.t('errors.delivery_error',
|
355
390
|
{ email: subscription.email, error: exc.to_s })
|
@@ -17,6 +17,7 @@ module Mail
|
|
17
17
|
attr_accessor :original_message
|
18
18
|
attr_accessor :list
|
19
19
|
attr_accessor :protected_headers_subject
|
20
|
+
attr_writer :dynamic_pseudoheaders
|
20
21
|
|
21
22
|
# TODO: This should be in initialize(), but I couldn't understand the
|
22
23
|
# strange errors about wrong number of arguments when overriding
|
@@ -44,9 +45,7 @@ module Mail
|
|
44
45
|
# might be gone (e.g. request-keywords that delete subscriptions or
|
45
46
|
# keys).
|
46
47
|
new.signer
|
47
|
-
self.dynamic_pseudoheaders.
|
48
|
-
new.add_pseudoheader(str)
|
49
|
-
end
|
48
|
+
new.dynamic_pseudoheaders = self.dynamic_pseudoheaders.dup
|
50
49
|
|
51
50
|
# Store previously protected subject for later access.
|
52
51
|
# mail-gpg pulls headers from the decrypted mime parts "up" into the main
|
@@ -206,13 +205,17 @@ module Mail
|
|
206
205
|
@recipient.match(/-bounce@/).present? ||
|
207
206
|
# Empty Return-Path
|
208
207
|
self.return_path.to_s == '<>' ||
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
208
|
+
bounced?
|
209
|
+
end
|
210
|
+
|
211
|
+
def bounced?
|
212
|
+
@bounced ||= bounce_detected? || (error_status != "unknown")
|
214
213
|
end
|
215
214
|
|
215
|
+
def error_status
|
216
|
+
@error_status ||= detect_error_code
|
217
|
+
end
|
218
|
+
|
216
219
|
def keywords
|
217
220
|
return @keywords if @keywords
|
218
221
|
|
@@ -244,11 +247,11 @@ module Mail
|
|
244
247
|
# decide itself how to encode, it works. If we don't, some
|
245
248
|
# character-sequences are not properly re-encoded.
|
246
249
|
part.content_transfer_encoding = nil
|
247
|
-
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
part.body =
|
250
|
+
|
251
|
+
# Set the right charset on the now parsed body
|
252
|
+
new_body = lines.compact.join
|
253
|
+
part.charset = new_body.encoding.to_s
|
254
|
+
part.body = new_body
|
252
255
|
|
253
256
|
@keywords
|
254
257
|
end
|
@@ -266,20 +269,17 @@ module Mail
|
|
266
269
|
end
|
267
270
|
|
268
271
|
def add_pseudoheader(string_or_key, value=nil)
|
269
|
-
|
270
|
-
if value.present?
|
271
|
-
@dynamic_pseudoheaders << make_pseudoheader(string_or_key, value)
|
272
|
-
else
|
273
|
-
@dynamic_pseudoheaders << string_or_key.to_s
|
274
|
-
end
|
272
|
+
dynamic_pseudoheaders << make_pseudoheader(string_or_key, value)
|
275
273
|
end
|
276
274
|
|
277
275
|
def make_pseudoheader(key, value)
|
278
|
-
"#{key.to_s.camelize}: #{value.to_s}"
|
276
|
+
output = "#{key.to_s.camelize}: #{value.to_s}"
|
277
|
+
# wrap lines after 76 with 2 indents
|
278
|
+
output.gsub(/(.{1,76})( +|$)\n?/, " \\1\n").chomp.lstrip
|
279
279
|
end
|
280
280
|
|
281
281
|
def dynamic_pseudoheaders
|
282
|
-
@dynamic_pseudoheaders
|
282
|
+
@dynamic_pseudoheaders ||= []
|
283
283
|
end
|
284
284
|
|
285
285
|
def signature_state
|
@@ -330,7 +330,8 @@ module Mail
|
|
330
330
|
end
|
331
331
|
|
332
332
|
def pseudoheaders(list)
|
333
|
-
|
333
|
+
separator = '------------------------------------------------------------------------------'
|
334
|
+
(standard_pseudoheaders(list) + dynamic_pseudoheaders).flatten.join("\n") + "\n" + separator + "\n"
|
334
335
|
end
|
335
336
|
|
336
337
|
def add_msgids(list, orig)
|
@@ -345,6 +346,14 @@ module Mail
|
|
345
346
|
end
|
346
347
|
|
347
348
|
def add_list_headers(list)
|
349
|
+
if list.include_autocrypt_header
|
350
|
+
# Inject whitespaces, to let Mail break the string at these points
|
351
|
+
# leading to correct wrapping.
|
352
|
+
keydata = list.key_minimal_base64_encoded.gsub(/(.{78})/, '\1 ')
|
353
|
+
|
354
|
+
self['Autocrypt'] = "addr=#{list.email}; prefer-encrypt=mutual; keydata=#{keydata}"
|
355
|
+
end
|
356
|
+
|
348
357
|
if list.include_list_headers
|
349
358
|
self['List-Id'] = "<#{list.email.gsub('@', '.')}>"
|
350
359
|
self['List-Owner'] = "<mailto:#{list.owner_address}> (Use list's public key)"
|
@@ -517,5 +526,79 @@ module Mail
|
|
517
526
|
end
|
518
527
|
end.join(' ')
|
519
528
|
end
|
529
|
+
|
530
|
+
def detect_error_code
|
531
|
+
# Detects the error code of an email with different heuristics
|
532
|
+
# from: https://github.com/mailtop/bounce_email
|
533
|
+
|
534
|
+
# Custom status codes
|
535
|
+
unicode_subject = self.subject.to_s
|
536
|
+
unicode_subject = unicode_subject.encode('utf-8') if unicode_subject.respond_to?(:encode)
|
537
|
+
|
538
|
+
return '97' if unicode_subject.match(/delayed/i)
|
539
|
+
return '98' if unicode_subject.match(/(unzulässiger|unerlaubter) anhang/i)
|
540
|
+
return '99' if unicode_subject.match(/auto.*reply|férias|ferias|Estarei ausente|estou ausente|vacation|vocation|(out|away).*office|on holiday|abwesenheits|autorespond|Automatische|eingangsbestätigung/i)
|
541
|
+
|
542
|
+
# Feedback-Type: abuse
|
543
|
+
return '96' if self.to_s.match(/Feedback-Type\: abuse/i)
|
544
|
+
|
545
|
+
if self.parts[1]
|
546
|
+
match_parts = self.parts[1].body.match(/(Status:.|550 |#)([245]\.[0-9]{1,3}\.[0-9]{1,3})/)
|
547
|
+
code = match_parts[2] if match_parts
|
548
|
+
return code if code
|
549
|
+
end
|
550
|
+
|
551
|
+
# Now try getting it from correct part of tmail
|
552
|
+
code = detect_bounce_status_code_from_text(self.body)
|
553
|
+
return code if code
|
554
|
+
|
555
|
+
# OK getting desperate so try getting code from entire email
|
556
|
+
code = detect_bounce_status_code_from_text(self.to_s)
|
557
|
+
code || 'unknown'
|
558
|
+
end
|
559
|
+
|
560
|
+
def bounce_detected?
|
561
|
+
# Detects bounces from different parts of the email without error status codes
|
562
|
+
# from: https://github.com/mailtop/bounce_email
|
563
|
+
return true if self.subject.to_s.match(/(returned|undelivered) mail|mail delivery( failed)?|(delivery )(status notification|failure)|failure notice|undeliver(able|ed)( mail)?|return(ing message|ed) to sender/i)
|
564
|
+
return true if self.subject.to_s.match(/auto.*reply|vacation|vocation|(out|away).*office|on holiday|abwesenheits|autorespond|Automatische|eingangsbestätigung/i)
|
565
|
+
return true if self['precedence'].to_s.match(/auto.*(reply|responder|antwort)/i)
|
566
|
+
return true if self.from.to_s.match(/^(MAILER-DAEMON|POSTMASTER)\@/i)
|
567
|
+
false
|
568
|
+
end
|
569
|
+
|
570
|
+
def detect_bounce_status_code_from_text(text)
|
571
|
+
# Parses a text and uses pattern matching to determines its error status (RFC 3463)
|
572
|
+
# from: https://github.com/mailtop/bounce_email
|
573
|
+
return "5.0.0" if text.match(/Status: 5\.0\.0/i)
|
574
|
+
return "5.1.1" if text.match(/no such (address|user)|Recipient address rejected|User unknown|does not like recipient|The recipient was unavailable to take delivery of the message|Sorry, no mailbox here by that name|invalid address|unknown user|unknown local part|user not found|invalid recipient|failed after I sent the message|did not reach the following recipient|nicht zugestellt werden|o pode ser entregue para um ou mais/i)
|
575
|
+
return "5.1.2" if text.match(/unrouteable mail domain|Esta casilla ha expirado por falta de uso|I couldn't find any host named/i)
|
576
|
+
if text.match(/mailbox is full|Mailbox quota (usage|disk) exceeded|quota exceeded|Over quota|User mailbox exceeds allowed size|Message rejected\. Not enough storage space|user has exhausted allowed storage space|too many messages on the server|mailbox is over quota|mailbox exceeds allowed size|excedeu a quota/i)
|
577
|
+
return "5.2.2" if text.match(/This is a permanent error||(Status: |)5\.2\.2/i)
|
578
|
+
return "4.2.2"
|
579
|
+
end
|
580
|
+
return "5.1.0" if text.match(/Address rejected/)
|
581
|
+
return "4.1.2" if text.match(/I couldn't find any host by that name/)
|
582
|
+
return "4.2.0" if text.match(/not yet been delivered/i)
|
583
|
+
return "5.1.1" if text.match(/mailbox unavailable|No such mailbox|RecipientNotFound|not found by SMTP address lookup|Status: 5\.1\.1/i)
|
584
|
+
return "5.2.3" if text.match(/Status: 5\.2\.3/i) # Too messages in folder
|
585
|
+
return "5.4.0" if text.match(/Status: 5\.4\.0/i) # too many hops
|
586
|
+
return "5.4.4" if text.match(/Unrouteable address/i)
|
587
|
+
return "4.4.7" if text.match(/retry timeout exceeded/i)
|
588
|
+
return "5.2.0" if text.match(/The account or domain may not exist, they may be blacklisted, or missing the proper dns entries./i)
|
589
|
+
return "5.5.4" if text.match(/554 TRANSACTION FAILED/i)
|
590
|
+
return "4.4.1" if text.match(/Status: 4.4.1|delivery temporarily suspended|wasn't able to establish an SMTP connection/i)
|
591
|
+
return "5.5.0" if text.match(/550 OU\-002|Mail rejected by Windows Live Hotmail for policy reasons/i)
|
592
|
+
return "5.1.2" if text.match(/PERM_FAILURE: DNS Error: Domain name not found/i)
|
593
|
+
return "4.2.0" if text.match(/Delivery attempts will continue to be made for/i)
|
594
|
+
return "5.5.4" if text.match(/554 delivery error:/i)
|
595
|
+
return "5.1.1" if text.match(/550-5.1.1|This Gmail user does not exist/i)
|
596
|
+
return "5.7.1" if text.match(/5.7.1 Your message.*?was blocked by ROTA DNSBL/i) # AA added
|
597
|
+
return "5.7.2" if text.match(/not have permission to post messages to the group/i)
|
598
|
+
return "5.3.2" if text.match(/Technical details of permanent failure|Too many bad recipients/i) && (text.match(/The recipient server did not accept our requests to connect/i) || text.match(/Connection was dropped by remote host/i) || text.match(/Could not initiate SMTP conversation/i)) # AA added
|
599
|
+
return "4.3.2" if text.match(/Technical details of temporary failure/i) && (text.match(/The recipient server did not accept our requests to connect/i) || text.match(/Connection was dropped by remote host/i) || text.match(/Could not initiate SMTP conversation/i)) # AA added
|
600
|
+
return "5.0.0" if text.match(/Delivery to the following recipient failed permanently/i) # AA added
|
601
|
+
return '5.2.3' if text.match(/account closed|account has been disabled or discontinued|mailbox not found|prohibited by administrator|access denied|account does not exist/i)
|
602
|
+
end
|
520
603
|
end
|
521
604
|
end
|
@@ -1,16 +1,12 @@
|
|
1
1
|
module Schleuder
|
2
2
|
module ListPlugins
|
3
3
|
def self.attach_listkey(arguments, list, mail)
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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}"
|
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
|
14
10
|
nil
|
15
11
|
end
|
16
12
|
end
|
@@ -104,35 +104,18 @@ module Schleuder
|
|
104
104
|
# helper methods
|
105
105
|
private
|
106
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
107
|
def self.import_keys_from_attachments(list, mail)
|
125
108
|
mail.attachments.map do |attachment|
|
126
109
|
material = attachment.body.to_s
|
127
110
|
|
128
|
-
list.import_key(material)
|
111
|
+
list.import_key(material)
|
129
112
|
end
|
130
113
|
end
|
131
114
|
|
132
115
|
def self.import_key_from_body(list, mail)
|
133
116
|
key_material = mail.first_plaintext_part.body.to_s
|
134
117
|
|
135
|
-
list.import_key(key_material)
|
118
|
+
list.import_key(key_material)
|
136
119
|
end
|
137
120
|
end
|
138
121
|
end
|
@@ -56,6 +56,9 @@ module Schleuder
|
|
56
56
|
|
57
57
|
# Only continue if all recipients are still here.
|
58
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
|
59
62
|
return
|
60
63
|
end
|
61
64
|
|
@@ -117,22 +120,22 @@ module Schleuder
|
|
117
120
|
Array(recipients).inject({}) do |hash, email|
|
118
121
|
keys = mail.list.keys(email)
|
119
122
|
# Exclude unusable keys.
|
120
|
-
keys.select
|
121
|
-
case
|
123
|
+
usable_keys = keys.select { |key| key.usable_for?(:encrypt) }
|
124
|
+
case usable_keys.size
|
122
125
|
when 1
|
123
|
-
hash[email] =
|
126
|
+
hash[email] = usable_keys.first
|
124
127
|
when 0
|
125
128
|
if encrypted_only
|
126
129
|
# Don't add the email to the result to exclude it from the
|
127
130
|
# recipients.
|
128
|
-
|
131
|
+
add_resend_msg(mail, email, :error, 'not_resent_no_keys', usable_keys.size, keys.size)
|
129
132
|
else
|
130
133
|
hash[email] = ''
|
131
134
|
end
|
132
135
|
else
|
133
136
|
# Always report this situation, regardless of sending or not. It's
|
134
137
|
# bad and should be fixed.
|
135
|
-
|
138
|
+
add_resend_msg(mail, email, :notice, 'not_resent_encrypted_no_keys', usable_keys.size, keys.size)
|
136
139
|
if ! encrypted_only
|
137
140
|
hash[email] = ''
|
138
141
|
end
|
@@ -152,8 +155,8 @@ module Schleuder
|
|
152
155
|
gpg_opts
|
153
156
|
end
|
154
157
|
|
155
|
-
def self.
|
156
|
-
mail.add_pseudoheader(
|
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))
|
157
160
|
end
|
158
161
|
|
159
162
|
def self.add_error_header(mail, recipients_map)
|
@@ -163,15 +166,15 @@ module Schleuder
|
|
163
166
|
def self.add_resent_headers(mail, recipients_map, to_or_cc, sent_encrypted)
|
164
167
|
if sent_encrypted
|
165
168
|
prefix = I18n.t('plugins.resend.encrypted_to')
|
166
|
-
str = recipients_map.map do |email, key|
|
169
|
+
str = "\n" + recipients_map.map do |email, key|
|
167
170
|
"#{email} (#{key.fingerprint})"
|
168
|
-
end.join(
|
171
|
+
end.join(",\n")
|
169
172
|
else
|
170
173
|
prefix = I18n.t('plugins.resend.unencrypted_to')
|
171
|
-
str = recipients_map.keys.join(
|
174
|
+
str = ' ' + recipients_map.keys.join(", ")
|
172
175
|
end
|
173
176
|
headername = resent_header_name(to_or_cc)
|
174
|
-
mail.add_pseudoheader(headername, "#{prefix}
|
177
|
+
mail.add_pseudoheader(headername, "#{prefix}#{str}")
|
175
178
|
end
|
176
179
|
|
177
180
|
def self.resent_header_name(to_or_cc)
|
data/lib/schleuder/runner.rb
CHANGED
@@ -5,15 +5,43 @@ module Schleuder
|
|
5
5
|
return error if error
|
6
6
|
|
7
7
|
logger.info "Parsing incoming email."
|
8
|
+
|
9
|
+
# is it valid utf-8?
|
10
|
+
msg_scrubbed = false
|
11
|
+
unless msg.valid_encoding?
|
12
|
+
logger.warn "Converting message due to invalid characters"
|
13
|
+
detection = CharlockHolmes::EncodingDetector.detect(msg)
|
14
|
+
begin
|
15
|
+
msg = CharlockHolmes::Converter.convert(msg, detection[:encoding], 'UTF-8')
|
16
|
+
rescue ArgumentError
|
17
|
+
# it looks like even icu wasn't able to convert
|
18
|
+
# so we scrub the invalid characters to be able to
|
19
|
+
# at least parse the message somehow. Though this might
|
20
|
+
# result in data loss.
|
21
|
+
logger.warn "Scrubbing message due to invalid characters"
|
22
|
+
msg = msg.scrub
|
23
|
+
msg_scrubbed = true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
8
27
|
@mail = Mail.create_message_to_list(msg, recipient, list)
|
9
28
|
|
29
|
+
if msg_scrubbed
|
30
|
+
@mail.add_pseudoheader(:note, I18n.t("pseudoheaders.scrubbed_message"))
|
31
|
+
end
|
32
|
+
|
10
33
|
error = run_filters('pre')
|
11
34
|
return error if error
|
12
35
|
|
13
36
|
begin
|
14
37
|
# This decrypts, verifies, etc.
|
15
38
|
@mail = @mail.setup
|
16
|
-
|
39
|
+
|
40
|
+
rescue GPGME::Error::BadPassphrase,
|
41
|
+
GPGME::Error::DecryptFailed,
|
42
|
+
GPGME::Error::NoData,
|
43
|
+
GPGME::Error::NoSecretKey
|
44
|
+
|
17
45
|
logger.warn "Decryption of incoming message failed."
|
18
46
|
return Errors::DecryptionFailed.new(list)
|
19
47
|
end
|
@@ -46,7 +74,7 @@ module Schleuder
|
|
46
74
|
# Subscriptions
|
47
75
|
logger.debug "Creating clean copy of message"
|
48
76
|
copy = @mail.clean_copy(list.headers_to_meta.any?)
|
49
|
-
list.send_to_subscriptions(copy)
|
77
|
+
list.send_to_subscriptions(copy, @mail)
|
50
78
|
nil
|
51
79
|
end
|
52
80
|
|
@@ -10,6 +10,10 @@ module Schleuder
|
|
10
10
|
validates :fingerprint, allow_blank: true, fingerprint: true
|
11
11
|
validates :delivery_enabled, :admin, boolean: true
|
12
12
|
|
13
|
+
before_validation {
|
14
|
+
self.email = Mail::Address.new(self.email).address
|
15
|
+
}
|
16
|
+
|
13
17
|
default_scope { order(:email) }
|
14
18
|
|
15
19
|
scope :without_fingerprint, -> { where(fingerprint: [nil,'']) }
|
@@ -37,10 +41,10 @@ module Schleuder
|
|
37
41
|
list.keys("0x#{self.fingerprint}").first
|
38
42
|
end
|
39
43
|
|
40
|
-
def send_mail(mail)
|
44
|
+
def send_mail(mail, incoming_mail=nil)
|
41
45
|
list.logger.debug "Preparing sending to #{self.inspect}"
|
42
46
|
|
43
|
-
mail = ensure_headers(mail)
|
47
|
+
mail = ensure_headers(mail, incoming_mail)
|
44
48
|
gpg_opts = self.list.gpg_sign_options
|
45
49
|
|
46
50
|
if self.key.blank?
|
@@ -66,9 +70,28 @@ module Schleuder
|
|
66
70
|
mail.deliver
|
67
71
|
end
|
68
72
|
|
69
|
-
def ensure_headers(mail)
|
73
|
+
def ensure_headers(mail, incoming_mail=nil)
|
70
74
|
mail.to = self.email
|
71
|
-
|
75
|
+
|
76
|
+
if self.list.set_reply_to_to_sender? && ! incoming_mail.nil?
|
77
|
+
# If the option "set_reply_to_to_sender" is set to true, we will set the reply-to header
|
78
|
+
# to the reply-to header given by the original email. If no reply-to header exists in the original email,
|
79
|
+
# the original senders email will be used as reply-to.
|
80
|
+
if ! incoming_mail.reply_to.nil?
|
81
|
+
mail.reply_to = incoming_mail.reply_to
|
82
|
+
else
|
83
|
+
mail.reply_to = incoming_mail.from
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if self.list.munge_from? && ! incoming_mail.nil?
|
88
|
+
# If the option "munge_from" is set to true, we will add the original senders' from-header to ours.
|
89
|
+
# We munge the from-header to avoid issues with DMARC.
|
90
|
+
mail.from = I18n.t("header_munging", from: incoming_mail.from.first, list: self.list.email, list_address: self.list.email)
|
91
|
+
else
|
92
|
+
mail.from = self.list.email
|
93
|
+
end
|
94
|
+
|
72
95
|
mail.sender = self.list.bounce_address
|
73
96
|
mail
|
74
97
|
end
|
data/lib/schleuder/version.rb
CHANGED
data/locales/de.yml
CHANGED
@@ -121,7 +121,9 @@ de:
|
|
121
121
|
Oder, um einen Schlüssel per HTTP von einem Server zu laden:
|
122
122
|
X-FETCH-KEY: https://example.org/keys/mykey.asc
|
123
123
|
resend:
|
124
|
-
not_resent_no_keys: Resending an <%{email}> fehlgeschlagen (%{
|
124
|
+
not_resent_no_keys: Resending an <%{email}> fehlgeschlagen (%{all_keys} Schlüssel gefunden, davon %{usable_keys} nutzbar. Unverschlüsseltes Senden verboten).
|
125
|
+
not_resent_encrypted_no_keys: Verschlüsseltes Resending an <%{email}> fehlgeschlagen (%{all_keys} Schlüssel gefunden, davon %{usable_keys} nutzbar).
|
126
|
+
aborted: Resending an <%{email}> abgebrochen aufgrund anderer Probleme.
|
125
127
|
encrypted_to: Verschlüsselt an
|
126
128
|
unencrypted_to: Unverschlüsselt an
|
127
129
|
invalid_recipient: "Ungültige Emailadresse für resend: %{address}"
|
@@ -250,6 +252,7 @@ de:
|
|
250
252
|
fetch_key:
|
251
253
|
invalid_input: "Ungültige Angabe. Gültig sind: URLs, OpenPGP-Fingerabdrücke, oder Emailadressen."
|
252
254
|
pseudoheaders:
|
255
|
+
scrubbed_message: Diese Email enthielt ungültige Zeichen, die aus Verarbeitungsgründen möglicherweise entfernt wurden.
|
253
256
|
stripped_html_from_multialt: Diese Email enthielt einen alternativen HTML-Teil, der PGP-Daten beinhaltete. Der HTML-Teil wurde entfernt, um die Email sauberer analysieren zu können.
|
254
257
|
stripped_html_from_multialt_with_keywords: Diese Email enthielt Schlüsselwörter und einen alternativen HTML-Teil. Der HTML-Teil wurde entfernt, um zu verhindern dass diese Schlüsselwörter Aussenstehenden bekannt werden.
|
255
258
|
signature_states:
|
@@ -258,6 +261,7 @@ de:
|
|
258
261
|
encryption_states:
|
259
262
|
encrypted: "Verschlüsselt"
|
260
263
|
unencrypted: "Unverschlüsselt"
|
264
|
+
header_munging: "%{from} über %{list} <%{list_address}>"
|
261
265
|
|
262
266
|
activerecord:
|
263
267
|
errors:
|
data/locales/en.yml
CHANGED
@@ -125,7 +125,9 @@ en:
|
|
125
125
|
Or, to fetch a key keys by URL:
|
126
126
|
X-FETCH-KEY: https://example.org/keys/mykey.asc
|
127
127
|
resend:
|
128
|
-
not_resent_no_keys: Resending to <%{email}> failed (%{
|
128
|
+
not_resent_no_keys: Resending to <%{email}> failed (%{all_keys} keys found, of which %{usable_keys} can be used. Unencrypted sending not allowed).
|
129
|
+
not_resent_encrypted_no_keys: Resending as encrypted email to <%{email}> failed (%{all_keys} keys found, of which %{usable_keys} can be used).
|
130
|
+
aborted: Resending to <%{email}> aborted due to other errors.
|
129
131
|
encrypted_to: Encrypted to
|
130
132
|
unencrypted_to: Unencrypted to
|
131
133
|
invalid_recipient: "Invalid email-address for resending: %{address}"
|
@@ -254,6 +256,7 @@ en:
|
|
254
256
|
fetch_key:
|
255
257
|
invalid_input: "Invalid input. Allowed are: URLs, OpenPGP-fingerprints, or email-addresses."
|
256
258
|
pseudoheaders:
|
259
|
+
scrubbed_message: This message included invalid characters, which might have been removed to be able to process the message properly.
|
257
260
|
stripped_html_from_multialt: This message included an alternating HTML-part that contained PGP-data. The HTML-part was removed to enable parsing the message more properly.
|
258
261
|
stripped_html_from_multialt_with_keywords: This message included keywords and an alternating HTML-part. The HTML-part was removed to prevent the disclosure of these keywords to third parties.
|
259
262
|
signature_states:
|
@@ -262,6 +265,7 @@ en:
|
|
262
265
|
encryption_states:
|
263
266
|
encrypted: "Encrypted"
|
264
267
|
unencrypted: "Unencrypted"
|
268
|
+
header_munging: "%{from} via %{list} <%{list_address}>"
|
265
269
|
|
266
270
|
activerecord:
|
267
271
|
errors:
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: schleuder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- schleuder dev team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gpgme
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '2.0'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 2.0.
|
22
|
+
version: 2.0.19
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,7 +29,7 @@ dependencies:
|
|
29
29
|
version: '2.0'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 2.0.
|
32
|
+
version: 2.0.19
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: mail
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -51,9 +51,9 @@ dependencies:
|
|
51
51
|
- - "~>"
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0.3'
|
54
|
-
- - "
|
54
|
+
- - "<"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: 0.
|
56
|
+
version: 0.4.3
|
57
57
|
type: :runtime
|
58
58
|
prerelease: false
|
59
59
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -61,9 +61,9 @@ dependencies:
|
|
61
61
|
- - "~>"
|
62
62
|
- !ruby/object:Gem::Version
|
63
63
|
version: '0.3'
|
64
|
-
- - "
|
64
|
+
- - "<"
|
65
65
|
- !ruby/object:Gem::Version
|
66
|
-
version: 0.
|
66
|
+
version: 0.4.3
|
67
67
|
- !ruby/object:Gem::Dependency
|
68
68
|
name: activerecord
|
69
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -176,6 +176,20 @@ dependencies:
|
|
176
176
|
- - "~>"
|
177
177
|
- !ruby/object:Gem::Version
|
178
178
|
version: '1'
|
179
|
+
- !ruby/object:Gem::Dependency
|
180
|
+
name: charlock_holmes
|
181
|
+
requirement: !ruby/object:Gem::Requirement
|
182
|
+
requirements:
|
183
|
+
- - "~>"
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: 0.7.6
|
186
|
+
type: :runtime
|
187
|
+
prerelease: false
|
188
|
+
version_requirements: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - "~>"
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: 0.7.6
|
179
193
|
- !ruby/object:Gem::Dependency
|
180
194
|
name: rspec
|
181
195
|
requirement: !ruby/object:Gem::Requirement
|
@@ -249,7 +263,7 @@ dependencies:
|
|
249
263
|
description: |-
|
250
264
|
Schleuder is a group's email-gateway: subscribers can exchange encrypted emails among themselves, receive emails from non-subscribers and send emails to non-subscribers via the list.
|
251
265
|
|
252
|
-
(Please note: For some platforms there's a better way of installing Schleuder than `gem install`. See <https://schleuder.org/docs
|
266
|
+
(Please note: For some platforms there's a better way of installing Schleuder than `gem install`. See <https://schleuder.org/schleuder/docs/server-admins.html#installation> for details.)
|
253
267
|
email: team@schleuder.org
|
254
268
|
executables:
|
255
269
|
- schleuder
|
@@ -275,6 +289,9 @@ files:
|
|
275
289
|
- db/migrate/20160501172700_fix_headers_to_meta_defaults.rb
|
276
290
|
- db/migrate/20170713215059_add_internal_footer_to_list.rb
|
277
291
|
- db/migrate/20180110203100_add_sig_enc_to_headers_to_meta_defaults.rb
|
292
|
+
- db/migrate/20180723173900_add_deliver_selfsent_to_list.rb
|
293
|
+
- db/migrate/20190906194820_add_autocrypt_header_to_list.rb
|
294
|
+
- db/migrate/20200118170110_add_set_reply_to_to_sender_and_munge_from.rb
|
278
295
|
- db/schema.rb
|
279
296
|
- etc/init.d/schleuder-api-daemon
|
280
297
|
- etc/list-defaults.yml
|
@@ -391,7 +408,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
391
408
|
- !ruby/object:Gem::Version
|
392
409
|
version: '0'
|
393
410
|
requirements: []
|
394
|
-
rubyforge_project:
|
411
|
+
rubyforge_project:
|
395
412
|
rubygems_version: 2.7.6.2
|
396
413
|
signing_key:
|
397
414
|
specification_version: 4
|