[DRE-commits] [schleuder] 03/13: New upstream version 3.2.0
Georg Faerber
georg-alioth-guest at moszumanska.debian.org
Tue Oct 24 12:31:00 UTC 2017
This is an automated email from the git hooks/post-receive script.
georg-alioth-guest pushed a commit to branch debian/3.2
in repository schleuder.
commit 6ac52b1ef90e21c74154d4605b58418c3da9bd5b
Author: Georg Faerber <georg at riseup.net>
Date: Mon Oct 23 11:50:14 2017 +0200
New upstream version 3.2.0
---
.gitattributes | 2 +
.gitignore | 1 +
.travis.yml | 1 +
CHANGELOG.md | 28 ++
Gemfile.lock | 34 +-
README.md | 8 +-
bin/schleuder-api-daemon | 359 +-------------
db/migrate/20140501103532_create_lists.rb | 64 +--
db/migrate/20140501112859_create_subscriptions.rb | 26 +-
db/migrate/201508092100_add_language_to_lists.rb | 10 +-
...813235800_add_forward_all_incoming_to_admins.rb | 10 +-
.../201508222143_add_logfiles_to_keep_to_lists.rb | 10 +-
...abled_to_delivery_enabled_and_change_default.rb | 6 +-
db/migrate/201508261815_strip_gpg_passphrase.rb | 4 +-
.../20170713215059_add_internal_footer_to_list.rb | 11 +
db/schema.rb | 3 +-
etc/list-defaults.yml | 7 +-
etc/schleuder.yml | 3 +
.../schleuder-api-daemon.rb | 93 +++-
lib/schleuder/conf.rb | 4 +-
lib/schleuder/gpgme/ctx.rb | 49 +-
lib/schleuder/gpgme/key.rb | 35 ++
lib/schleuder/list.rb | 55 ++-
lib/schleuder/list_builder.rb | 30 +-
lib/schleuder/mail/message.rb | 48 +-
lib/schleuder/plugin_runners/base.rb | 4 +-
lib/schleuder/plugins/key_management.rb | 5 +-
lib/schleuder/plugins/resend.rb | 44 +-
lib/schleuder/plugins/sign_this.rb | 3 +
lib/schleuder/plugins/subscription_management.rb | 19 +-
lib/schleuder/version.rb | 2 +-
locales/de.yml | 23 +-
locales/en.yml | 23 +-
schleuder.gemspec | 5 +-
spec/factories/lists.rb | 1 +
spec/fixtures/expiring_key.txt | 30 ++
spec/fixtures/mails/qp-encoding-clear.eml | 25 +
.../mails/qp-encoding-encrypted+signed.eml | 65 +++
spec/fixtures/mails/qp-encoding-encrypted.eml | 47 ++
spec/fixtures/partially_expired_key.txt | 36 ++
spec/fixtures/revoked_key.txt | 33 ++
spec/fixtures/schleuder_at_example_public_key.txt | 2 +
spec/fixtures/signonly_key.txt | 18 +
spec/schleuder-certificate.pem | 21 +
spec/schleuder-private-key.pem | 27 +
spec/schleuder.yml | 10 +-
.../api_daemon/api_daemon_spec_helper.rb | 17 +
.../integration/api_daemon/authorization_spec.rb | 20 +
spec/schleuder/integration/api_daemon/list_spec.rb | 29 ++
.../integration/api_daemon/subscription_spec.rb | 68 +++
spec/schleuder/integration/cli_spec.rb | 5 +-
spec/schleuder/integration/keywords_spec.rb | 547 ++++++++++++++++++---
spec/schleuder/runner_spec.rb | 83 ++++
spec/schleuder/unit/conf_spec.rb | 38 ++
spec/schleuder/unit/filters_spec.rb | 2 +-
spec/schleuder/unit/gpgme_ctx.rb | 22 +
spec/schleuder/unit/gpgme_key.rb | 50 ++
spec/schleuder/unit/list_builder_spec.rb | 42 +-
spec/schleuder/unit/list_spec.rb | 218 +++++++-
spec/schleuder/unit/logger_notifications_spec.rb | 29 ++
spec/schleuder/unit/message_spec.rb | 24 +
spec/spec_helper.rb | 34 +-
62 files changed, 1959 insertions(+), 613 deletions(-)
diff --git a/.gitattributes b/.gitattributes
index d547568..48142d7 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,3 @@
+.gitlab-ci.yml export-ignore
/gems/ export-ignore
+/utils export-ignore
diff --git a/.gitignore b/.gitignore
index fde347c..4294f3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ vendor
db/*.sqlite3
testmails/*
.pc/
+coverage/
diff --git a/.travis.yml b/.travis.yml
index 6801136..1f934b2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,4 @@
+dist: trusty
language: ruby
cache: bundler
rvm:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c60a57..33c464b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,34 @@ Change Log
This project adheres to [Semantic Versioning](http://semver.org/).
+## [3.2.0] / 2017-10-23
+
+### Added
+
+* Internal footer: to be appended to each email that is sent to a subscribed address. Will not be included in messages to non-subscribed addresses. This change requires a change to the database, don't forget to run `schleuder install` after updating the code.
+* Optionally use an OS-wide defined keyserver by configuring a blank value for the keyserver.
+* Added keywords `X-RESEND-UNENCRYPTED` and `X-RESEND-CC-UNENCRYPTED` to enforce outgoing email(s) in cleartext regardless of whether we would find a key for the recipient or not.
+
+
+### Changed
+
+* Public footer: Whitespace is not anymore stripped from the value of public_footer.
+* The API does not include anymore each key's key-data in response to `/keys.json`. This avoids performance problems with even medium sized keyrings.
+* The short representation of GnuPG keys became more human-friendly. Besides the fingerprint we now show the email-address of the first UID, the generation-date, and optionally the expiration-date.
+* Log the full exception when sending a message fails. (Thanks, Lunar!)
+* When creating a new list, we do not anymore look for a matching key for the admin-address in the list's keyring. We don't want to look up keys for subscriptions by email at all. (This was anyway only useful in the corner case where you prefilled a keyring to use for the new list.)
+* API: Access to `/status.json` is now allowed without authentication.
+* Deprecate X-LISTNAME in favour of X-LIST-NAME, for the sake of consistency in spelling keywords (but X-LISTNAME is still supported). (Thanks, maxigas!)
+
+### Fixed
+
+* X-SUBSCRIBE now handles the combination of space-separated fingerprint and additional arguments (admin-flag, delivery-enabled-flag) correctly.
+* Fixed broken encoding of certain character-sequences in encrypted+signed messages.
+* X-LIST-KEYS again works without arguments.
+* X-RESEND now checks the given arguments to be valid email-addresses, and blocks resending if any one is found invalid.
+* X-RESEND now respects the encoding the mail was sent with. (Thanks, Lunar!)
+
+
## [3.1.2] / 2017-07-13
### Changed
diff --git a/Gemfile.lock b/Gemfile.lock
index 47d725b..c990c2a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,10 +1,10 @@
PATH
remote: .
specs:
- schleuder (3.1.2)
+ schleuder (3.2.0)
activerecord (~> 4.1)
mail-gpg (~> 0.3.0)
- rake (~> 12)
+ rake (>= 10.5.0)
sinatra (~> 1)
sinatra-contrib (~> 1)
sqlite3 (~> 1)
@@ -27,19 +27,21 @@ GEM
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
+ ansi (1.5.0)
arel (6.0.4)
backports (3.6.8)
builder (3.2.2)
daemons (1.2.4)
database_cleaner (1.5.3)
diff-lcs (1.2.5)
+ docile (1.1.5)
eventmachine (1.2.3)
factory_girl (4.8.0)
activesupport (>= 3.0.0)
- gpgme (2.0.12)
- mini_portile2 (~> 2.1.0)
+ gpgme (2.0.13)
+ mini_portile2 (~> 2.1)
hirb (0.7.3)
- i18n (0.7.0)
+ i18n (0.8.1)
json (1.8.5)
mail (2.6.4)
mime-types (>= 1.16, < 4)
@@ -49,10 +51,10 @@ GEM
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
- mini_portile2 (2.1.0)
+ mini_portile2 (2.2.0)
minitest (5.10.1)
multi_json (1.12.1)
- rack (1.6.5)
+ rack (1.6.8)
rack-protection (1.5.3)
rack
rack-test (0.6.3)
@@ -71,7 +73,16 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.5.0)
rspec-support (3.5.0)
- sinatra (1.4.7)
+ simplecov (0.15.1)
+ docile (~> 1.1.0)
+ json (>= 1.8, < 3)
+ simplecov-html (~> 0.10.0)
+ simplecov-console (0.4.2)
+ ansi
+ hirb
+ simplecov
+ simplecov-html (0.10.2)
+ sinatra (1.4.8)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
@@ -88,8 +99,8 @@ GEM
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
thor (0.19.4)
- thread_safe (0.3.5)
- tilt (2.0.5)
+ thread_safe (0.3.6)
+ tilt (2.0.8)
tzinfo (1.2.2)
thread_safe (~> 0.1)
@@ -102,6 +113,7 @@ DEPENDENCIES
hirb
rspec (~> 3.5.0)
schleuder!
+ simplecov-console
BUNDLED WITH
- 1.14.6
+ 1.13.7
diff --git a/README.md b/README.md
index 6a229c3..705e696 100644
--- a/README.md
+++ b/README.md
@@ -43,15 +43,15 @@ Additionally these **rubygems** are required (will be installed automatically un
Installing Schleuder
------------
-1. Download [the gem](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.1.2.gem) and [the OpenPGP-signature](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.1.2.gem.sig) and verify:
+1. Download [the gem](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.2.0.gem) and [the OpenPGP-signature](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.2.0.gem.sig) and verify:
```
gpg --recv-key 0xB3D190D5235C74E1907EACFE898F2C91E2E6E1F3
- gpg --verify schleuder-3.1.2.gem.sig
+ gpg --verify schleuder-3.2.0.gem.sig
```
2. If all went well install the gem:
```
- gem install schleuder-3.1.2.gem
+ gem install schleuder-3.2.0.gem
```
3. Set up schleuder:
@@ -131,4 +131,4 @@ GNU GPL 3.0. Please see [LICENSE.txt](LICENSE.txt).
Alternative Download
--------------------
-Alternatively to the gem-files you can download the latest release as [a tarball](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.1.2.tar.gz) and [its OpenPGP-signature](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.1.2.tar.gz.sig).
+Alternatively to the gem-files you can download the latest release as [a tarball](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.2.0.tar.gz) and [its OpenPGP-signature](https://0xacab.org/schleuder/schleuder/raw/master/gems/schleuder-3.2.0.tar.gz.sig).
diff --git a/bin/schleuder-api-daemon b/bin/schleuder-api-daemon
index 80eaeaf..8e81a82 100755
--- a/bin/schleuder-api-daemon
+++ b/bin/schleuder-api-daemon
@@ -1,359 +1,4 @@
#!/usr/bin/env ruby
-# Make sinatra use production as default-environment
-ENV['RACK_ENV'] ||= 'production'
-
-require 'sinatra/base'
-require 'sinatra/json'
-require 'sinatra/namespace'
-require 'thin'
-require_relative '../lib/schleuder.rb'
-
-
-%w[tls_cert_file tls_key_file].each do |config_key|
- path = Conf.api[config_key]
- if ! File.readable?(path)
- $stderr.puts "Error: '#{path}' is not a readable file (from #{config_key} in config)."
- exit 1
- end
-end
-
-class SchleuderApiDaemon < Sinatra::Base
- register Sinatra::Namespace
- use Rack::Auth::Basic, "Schleuder API Daemon" do |username, key|
- username == 'schleuder' && Conf.api_valid_api_keys.include?(key)
- end
-
- configure do
- set :server, :thin
- set :port, Schleuder::Conf.api['port'] || 4443
- set :bind, Schleuder::Conf.api['host'] || 'localhost'
- if settings.development?
- set :logging, Logger::DEBUG
- else
- set :logging, Logger::WARN
- end
- end
-
- before do
- cast_param_values
- end
-
- after do
- # Return connection to pool after each request.
- ActiveRecord::Base.connection.close
- end
-
- error do
- exc = env['sinatra.error']
- logger.error "Error: #{env['sinatra.error'].message}"
- case exc
- when Errno::EACCES
- server_error(exc.message)
- else
- client_error(exc.to_s)
- end
- end
-
- error 404 do
- 'Not found'
- end
-
- get '/status.json' do
- json status: :ok
- end
-
- get '/version.json' do
- json version: Schleuder::VERSION
- end
-
- helpers do
- def list(id_or_email=nil)
- if id_or_email.blank?
- if params[:list_id].present?
- id_or_email = params[:list_id]
- else
- client_error "Parameter list_id is required"
- end
- end
- if id_or_email.to_i == 0
- # list_id is actually an email address
- list = List.where(email: id_or_email).first
- else
- list = List.where(id: id_or_email).first
- end
- list || halt(404)
- end
-
- def subscription(id_or_email)
- if id_or_email.to_i == 0
- # Email
- if params[:list_id].blank?
- client_error "Parameter list_id is required when using email as identifier for subscriptions."
- else
- sub = list.subscriptions.where(email: id_or_email).first
- end
- else
- sub = Subscription.where(id: id_or_email.to_i).first
- end
- sub || halt(404)
- end
-
- def requested_list_id
- # ActiveResource doesn't want to use query-params with create(), so here
- # list_id might be included in the request-body.
- params['list_id'] || parsed_body['list_id'] || client_error('Need list_id')
- end
-
- def parsed_body
- @parsed_body ||= begin
- b = JSON.parse(request.body.read)
- logger.debug "parsed body: #{b.inspect}"
- b
- end
- end
-
- def server_error(msg)
- logger.warn msg
- halt(500, json(error: msg))
- end
-
- # TODO: unify error messages. This method currently sends an old error format. See <https://github.com/rails/activeresource/blob/d6a5186/lib/active_resource/base.rb#L227>.
- def client_error(obj_or_msg, http_code=400)
- text = case obj_or_msg
- when String, Symbol
- obj_or_msg.to_s
- when ActiveRecord::Base
- obj_or_msg.errors.full_messages
- else
- obj_or_msg
- end
- logger.error "Sending error to client: #{text.inspect}"
- halt(http_code, json(errors: text))
- end
-
- # poor persons type casting
- def cast_param_values
- params.each do |key, value|
- params[key] =
- case value
- when 'true' then true
- when 'false' then false
- when '0' then 0
- when value.to_i > 0 then value.to_i
- else value
- end
- end
- end
-
- def key_to_json(key)
- {
- fingerprint: key.fingerprint,
- email: key.email,
- ascii: key.armored,
- expiry: key.expires,
- trust_issues: key.usability_issue
- }
- end
- end
-
- namespace '/lists' do
- get '.json' do
- json List.all, include: :subscriptions
- end
-
- post '.json' do
- listname = parsed_body['email']
- fingerprint = parsed_body['fingerprint']
- adminaddress = parsed_body['adminaddress']
- adminkey = parsed_body['adminkey']
- list, messages = ListBuilder.new({email: listname, fingerprint: fingerprint}, adminaddress, adminkey).run
- if list.nil?
- client_error(messages, 422)
- elsif ! list.valid?
- client_error(list, 422)
- else
- if messages.present?
- headers 'X-Messages' => messages.join(' // ').gsub(/\n/, ' // ')
- end
- body json(list)
- end
- end
-
- get '/configurable_attributes.json' do
- json(List.configurable_attributes) + "\n"
- end
-
- post '/send_list_key_to_subscriptions.json' do
- json(result: list.send_list_key_to_subscriptions)
- end
-
- get '/new.json' do
- json List.new
- end
-
- get '/:id.json' do |id|
- json list(id)
- end
-
- put '/:id.json' do |id|
- list = list(id)
- if list.update(parsed_body)
- 204
- else
- client_error(list)
- end
- end
-
- patch '/:id.json' do |id|
- list = list(id)
- if list.update(parsed_body)
- 204
- else
- client_error(list)
- end
- end
-
- delete '/:id.json' do |id|
- list = list(id)
- if list.destroy
- 200
- else
- client_error(list)
- end
- end
- end
-
- namespace '/subscriptions' do
- get '.json' do
- filterkeys = Subscription.configurable_attributes + [:list_id, :email]
- filter = params.select do |param|
- filterkeys.include?(param.to_sym)
- end
-
- logger.debug "Subscription filter: #{filter.inspect}"
- if filter['list_id'] && filter['list_id'].to_i == 0
- # Value is an email-address
- if list = List.where(email: filter['list_id']).first
- filter['list_id'] = list.id
- else
- status 404
- return json(errors: 'No such list')
- end
- end
-
- json Subscription.where(filter)
- end
-
- post '.json' do
- begin
- list = list(requested_list_id)
- sub = list.subscribe(
- parsed_body['email'],
- parsed_body['fingerprint'],
- parsed_body['admin'],
- parsed_body['delivery_enabled']
- )
- logger.debug "subcription: #{sub.inspect}"
- if sub.valid?
- logger.debug "Subscribed: #{sub.inspect}"
- redirect to("/subscriptions/#{sub.id}.json"), 201
- else
- client_error(sub, 422)
- end
- rescue ActiveRecord::RecordNotUnique
- logger.error "Already subscribed"
- status 422
- json errors: {email: ['is already subscribed']}
- end
- end
-
- get '/configurable_attributes.json' do
- json(Subscription.configurable_attributes) + "\n"
- end
-
- get '/new.json' do
- json Subscription.new
- end
-
- get '/:id.json' do |id|
- json subscription(id)
- end
-
- put '/:id.json' do |id|
- sub = subscription(id)
- if sub.update(parsed_body)
- 200
- else
- client_error(sub, 422)
- end
- end
-
- patch '/:id.json' do |id|
- sub = subscription(id)
- if sub.update(parsed_body)
- 200
- else
- client_error(sub)
- end
- end
-
- delete '/:id.json' do |id|
- if sub = subscription(id).destroy
- 200
- else
- client_error(sub)
- end
- end
- end
-
- namespace '/keys' do
- get '.json' do
- keys = list.keys.sort_by(&:email).map do |key|
- key_to_json(key)
- end
- json keys
- end
-
- post '.json' do
- input = parsed_body['keymaterial']
- if ! input.match('BEGIN PGP')
- input = Base64.decode64(input)
- end
- json list(requested_list_id).import_key(input)
- end
-
- get '/check_keys.json' do
- json result: list.check_keys
- end
-
- get '/:fingerprint.json' do |fingerprint|
- if key = list.key(fingerprint)
- json key_to_json(key)
- else
- 404
- end
- end
-
- delete '/:fingerprint.json' do |fingerprint|
- if list.delete_key(fingerprint)
- 200
- else
- 404
- end
- end
- end
-
- def self.run!
- super do |server|
- server.ssl = true
- server.ssl_options = {
- :cert_chain_file => Conf.api['tls_cert_file'],
- :private_key_file => Conf.api['tls_key_file']
- }
- end
- end
-
- # Run this class as application
- run!
-end
+require_relative '../lib/schleuder-api-daemon'
+SchleuderApiDaemon.run!
\ No newline at end of file
diff --git a/db/migrate/20140501103532_create_lists.rb b/db/migrate/20140501103532_create_lists.rb
index a3329d7..c091ddb 100644
--- a/db/migrate/20140501103532_create_lists.rb
+++ b/db/migrate/20140501103532_create_lists.rb
@@ -1,33 +1,39 @@
class CreateLists < ActiveRecord::Migration
- def change
- create_table :lists do |t|
- t.timestamps
- t.string :email
- t.string :fingerprint
- t.string :gpg_passphrase
- t.string :log_level, default: 'warn'
- t.string :default_mime, default: 'mime'
- t.string :subject_prefix, default: ''
- t.string :subject_prefix_in, default: ''
- t.string :subject_prefix_out, default: ''
- t.string :openpgp_header_preference, default: 'signencrypt'
- t.text :public_footer, default: ''
- t.text :headers_to_meta, default: '["from","to","date",":cc"]'
- t.text :bounces_drop_on_headers, default: '{"x-spam-flag":"yes"}'
- t.text :keywords_admin_only, default: '["unsubscribe", "unsubscribe", "delete-key"]'
- t.text :keywords_admin_notify, default: '["add-key"]'
- t.boolean :send_encrypted_only, default: false
- t.boolean :receive_encrypted_only, default: false
- t.boolean :receive_signed_only, default: false
- t.boolean :receive_authenticated_only, default: false
- t.boolean :receive_from_subscribed_emailaddresses_only, default: false
- t.boolean :receive_admin_only, default: false
- t.boolean :keep_msgid, default: true
- t.boolean :bounces_drop_all, default: false
- t.boolean :bounces_notify_admins, default: true
- t.boolean :include_list_headers, default: true
- t.boolean :include_openpgp_header, default: true
- t.integer :max_message_size_kb, default: 10240
+ def up
+ if ! table_exists?(:lists)
+ create_table :lists do |t|
+ t.timestamps
+ t.string :email
+ t.string :fingerprint
+ t.string :gpg_passphrase
+ t.string :log_level, default: 'warn'
+ t.string :default_mime, default: 'mime'
+ t.string :subject_prefix, default: ''
+ t.string :subject_prefix_in, default: ''
+ t.string :subject_prefix_out, default: ''
+ t.string :openpgp_header_preference, default: 'signencrypt'
+ t.text :public_footer, default: ''
+ t.text :headers_to_meta, default: '["from","to","date",":cc"]'
+ t.text :bounces_drop_on_headers, default: '{"x-spam-flag":"yes"}'
+ t.text :keywords_admin_only, default: '["unsubscribe", "unsubscribe", "delete-key"]'
+ t.text :keywords_admin_notify, default: '["add-key"]'
+ t.boolean :send_encrypted_only, default: false
+ t.boolean :receive_encrypted_only, default: false
+ t.boolean :receive_signed_only, default: false
+ t.boolean :receive_authenticated_only, default: false
+ t.boolean :receive_from_subscribed_emailaddresses_only, default: false
+ t.boolean :receive_admin_only, default: false
+ t.boolean :keep_msgid, default: true
+ t.boolean :bounces_drop_all, default: false
+ t.boolean :bounces_notify_admins, default: true
+ t.boolean :include_list_headers, default: true
+ t.boolean :include_openpgp_header, default: true
+ t.integer :max_message_size_kb, default: 10240
+ end
+ end
+
+ def down
+ drop_table(:lists)
end
end
end
diff --git a/db/migrate/20140501112859_create_subscriptions.rb b/db/migrate/20140501112859_create_subscriptions.rb
index dc4029f..890dddb 100644
--- a/db/migrate/20140501112859_create_subscriptions.rb
+++ b/db/migrate/20140501112859_create_subscriptions.rb
@@ -1,15 +1,21 @@
class CreateSubscriptions < ActiveRecord::Migration
- def change
- create_table :subscriptions do |t|
- t.integer :list_id
- t.string :email
- t.string :fingerprint
- t.boolean :admin, default: false
- t.boolean :delivery_disabled, default: false
- t.timestamps
+ def up
+ if ! table_exists?(:subscriptions)
+ create_table :subscriptions do |t|
+ t.integer :list_id
+ t.string :email
+ t.string :fingerprint
+ t.boolean :admin, default: false
+ t.boolean :delivery_disabled, default: false
+ t.timestamps
+ end
+
+ add_index :subscriptions, :list_id
+ add_index :subscriptions, [:email, :list_id], unique: true
end
+ end
- add_index :subscriptions, :list_id
- add_index :subscriptions, [:email, :list_id], unique: true
+ def down
+ drop_table(:subscriptions)
end
end
diff --git a/db/migrate/201508092100_add_language_to_lists.rb b/db/migrate/201508092100_add_language_to_lists.rb
index a7f5df6..972a090 100644
--- a/db/migrate/201508092100_add_language_to_lists.rb
+++ b/db/migrate/201508092100_add_language_to_lists.rb
@@ -1,5 +1,11 @@
class AddLanguageToLists < ActiveRecord::Migration
- def change
- add_column :lists, :language, :string, default: 'en'
+ def up
+ if ! column_exists?(:lists, :language)
+ add_column :lists, :language, :string, default: 'en'
+ end
+ end
+
+ def down
+ remove_column(:lists, :language)
end
end
diff --git a/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb b/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb
index 48171c6..ba89dae 100644
--- a/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb
+++ b/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb
@@ -1,5 +1,11 @@
class AddForwardAllIncomingToAdmins < ActiveRecord::Migration
- def change
- add_column :lists, :forward_all_incoming_to_admins, :boolean, default: false
+ def up
+ if ! column_exists?(:lists, :forward_all_incoming_to_admins)
+ add_column :lists, :forward_all_incoming_to_admins, :boolean, default: false
+ end
+ end
+
+ def down
+ remove_column(:lists, :forward_all_incoming_to_admins)
end
end
diff --git a/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb b/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb
index 66b6bf8..af264ae 100644
--- a/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb
+++ b/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb
@@ -1,5 +1,11 @@
class AddLogfilesToKeepToLists < ActiveRecord::Migration
- def change
- add_column :lists, :logfiles_to_keep, :integer, default: 2
+ def up
+ if ! column_exists?(:lists, :logfiles_to_keep)
+ add_column :lists, :logfiles_to_keep, :integer, default: 2
+ end
+ end
+
+ def down
+ remove_column(:lists, :logfiles_to_keep)
end
end
diff --git a/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb b/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb
index 132ac14..18b1bf7 100644
--- a/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb
+++ b/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb
@@ -1,7 +1,9 @@
class RenameDeliveryDisabledToDeliveryEnabledAndChangeDefault < ActiveRecord::Migration
def up
- rename_column :subscriptions, :delivery_disabled, :delivery_enabled
- change_column_default :subscriptions, :delivery_enabled, true
+ if column_exists?(:subscriptions, :delivery_disabled)
+ rename_column :subscriptions, :delivery_disabled, :delivery_enabled
+ change_column_default :subscriptions, :delivery_enabled, true
+ end
end
def down
diff --git a/db/migrate/201508261815_strip_gpg_passphrase.rb b/db/migrate/201508261815_strip_gpg_passphrase.rb
index 891905d..7fde0fb 100644
--- a/db/migrate/201508261815_strip_gpg_passphrase.rb
+++ b/db/migrate/201508261815_strip_gpg_passphrase.rb
@@ -1,6 +1,8 @@
class StripGpgPassphrase < ActiveRecord::Migration
def up
- remove_column :lists, :gpg_passphrase
+ if column_exists?(:lists, :gpg_passphrase)
+ remove_column :lists, :gpg_passphrase
+ end
end
def down
diff --git a/db/migrate/20170713215059_add_internal_footer_to_list.rb b/db/migrate/20170713215059_add_internal_footer_to_list.rb
new file mode 100644
index 0000000..5321fdf
--- /dev/null
+++ b/db/migrate/20170713215059_add_internal_footer_to_list.rb
@@ -0,0 +1,11 @@
+class AddInternalFooterToList < ActiveRecord::Migration
+ def up
+ if ! column_exists?(:lists, :internal_footer)
+ add_column :lists, :internal_footer, :text, default: ''
+ end
+ end
+
+ def down
+ remove_column(:lists, :internal_footer)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c084225..6ab8e9a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160501172700) do
+ActiveRecord::Schema.define(version: 20170713215059) do
create_table "lists", force: :cascade do |t|
t.datetime "created_at"
@@ -43,6 +43,7 @@ ActiveRecord::Schema.define(version: 20160501172700) do
t.string "language", limit: 255, default: "en"
t.boolean "forward_all_incoming_to_admins", default: false
t.integer "logfiles_to_keep", default: 2
+ t.text "internal_footer", default: ""
end
create_table "subscriptions", force: :cascade do |t|
diff --git a/etc/list-defaults.yml b/etc/list-defaults.yml
index 3a413db..f830f94 100644
--- a/etc/list-defaults.yml
+++ b/etc/list-defaults.yml
@@ -60,7 +60,12 @@ keywords_admin_only:
keywords_admin_notify:
- add-key
-# Public footer to append each email that is sent to non-subscribed addresses.
+# Footer to append to each email that is sent to a subscribed address. Will not
+# be included in messages to non-subscribed addresses.
+internal_footer:
+
+# Footer to append to each email that is sent to non-subscribed addresses. Will
+# not be included in messages to subscribed addresses.
public_footer:
# Prefix to be inserted into the subject of every email that is validly signed
diff --git a/etc/schleuder.yml b/etc/schleuder.yml
index 116e7ac..f95cb54 100644
--- a/etc/schleuder.yml
+++ b/etc/schleuder.yml
@@ -16,6 +16,9 @@ log_level: warn
#keyserver: hkps://hkps.pool.sks-keyservers.net
# If you have gnupg 2.1 and TOR running locally, use a onion-keyserver:
#keyserver: hkp://jirk5u4osbsr34t5.onion
+# If you have an OS-wide defined keyserver, specify a blank value to have that
+# one used:
+#keyserver:
# The default works for all supported versions of gnupg:
keyserver: pool.sks-keyservers.net
diff --git a/bin/schleuder-api-daemon b/lib/schleuder-api-daemon.rb
similarity index 75%
copy from bin/schleuder-api-daemon
copy to lib/schleuder-api-daemon.rb
index 80eaeaf..4999d49 100755
--- a/bin/schleuder-api-daemon
+++ b/lib/schleuder-api-daemon.rb
@@ -20,9 +20,6 @@ end
class SchleuderApiDaemon < Sinatra::Base
register Sinatra::Namespace
- use Rack::Auth::Basic, "Schleuder API Daemon" do |username, key|
- username == 'schleuder' && Conf.api_valid_api_keys.include?(key)
- end
configure do
set :server, :thin
@@ -36,6 +33,7 @@ class SchleuderApiDaemon < Sinatra::Base
end
before do
+ authenticate!
cast_param_values
end
@@ -68,6 +66,25 @@ class SchleuderApiDaemon < Sinatra::Base
end
helpers do
+ def valid_credentials?
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
+ if @auth.provided? && @auth.basic? && @auth.credentials.present?
+ username, api_key = @auth.credentials
+ username == 'schleuder' && Conf.api_valid_api_keys.include?(api_key)
+ else
+ false
+ end
+ end
+
+ def authenticate!
+ # Be careful to use path_info() — it can be changed by other filters!
+ return if request.path_info == '/status.json'
+ if ! valid_credentials?
+ headers['WWW-Authenticate'] = 'Basic realm="Schleuder API Daemon"'
+ halt 401, "Not authorized\n"
+ end
+ end
+
def list(id_or_email=nil)
if id_or_email.blank?
if params[:list_id].present?
@@ -146,14 +163,45 @@ class SchleuderApiDaemon < Sinatra::Base
end
end
- def key_to_json(key)
- {
+ def key_to_hash(key, include_keydata=false)
+ hash = {
fingerprint: key.fingerprint,
email: key.email,
- ascii: key.armored,
expiry: key.expires,
+ generated_at: key.generated_at,
+ primary_uid: key.primary_uid.uid,
+ oneline: key.oneline,
trust_issues: key.usability_issue
}
+ if include_keydata
+ hash[:description] = key.to_s
+ hash[:ascii] = key.armored
+ end
+ hash
+ end
+
+ def set_x_messages(messages)
+ if messages.present?
+ headers 'X-Messages' => Array(messages).join(' // ').gsub(/\n/, ' // ')
+ end
+ end
+
+ def find_key_material
+ key_material = parsed_body['key_material'].presence
+ # By convention key_material is either ASCII or base64-encoded.
+ if key_material && ! key_material.match('BEGIN PGP')
+ key_material = Base64.decode64(key_material)
+ end
+ key_material
+ end
+
+ def find_attributes_from_body(attribs)
+ Array(attribs).inject({}) do |memo, attrib|
+ if parsed_body.has_key?(attrib)
+ memo[attrib] = parsed_body[attrib]
+ end
+ memo
+ end
end
end
@@ -166,16 +214,15 @@ class SchleuderApiDaemon < Sinatra::Base
listname = parsed_body['email']
fingerprint = parsed_body['fingerprint']
adminaddress = parsed_body['adminaddress']
+ adminfingerprint = parsed_body['adminfingerprint']
adminkey = parsed_body['adminkey']
- list, messages = ListBuilder.new({email: listname, fingerprint: fingerprint}, adminaddress, adminkey).run
+ list, messages = ListBuilder.new({email: listname, fingerprint: fingerprint}, adminaddress, adminfingerprint, adminkey).run
if list.nil?
client_error(messages, 422)
elsif ! list.valid?
client_error(list, 422)
else
- if messages.present?
- headers 'X-Messages' => messages.join(' // ').gsub(/\n/, ' // ')
- end
+ set_x_messages(messages)
body json(list)
end
end
@@ -248,15 +295,19 @@ class SchleuderApiDaemon < Sinatra::Base
post '.json' do
begin
list = list(requested_list_id)
- sub = list.subscribe(
+ # We don't have to care about nil-values, subscribe() does that for us.
+ sub, msgs = list.subscribe(
parsed_body['email'],
parsed_body['fingerprint'],
parsed_body['admin'],
- parsed_body['delivery_enabled']
+ parsed_body['delivery_enabled'],
+ find_key_material
)
+ set_x_messages(msgs)
logger.debug "subcription: #{sub.inspect}"
if sub.valid?
logger.debug "Subscribed: #{sub.inspect}"
+ # TODO: why redirect instead of respond with result?
redirect to("/subscriptions/#{sub.id}.json"), 201
else
client_error(sub, 422)
@@ -282,7 +333,16 @@ class SchleuderApiDaemon < Sinatra::Base
put '/:id.json' do |id|
sub = subscription(id)
- if sub.update(parsed_body)
+ list = sub.list
+ args = find_attributes_from_body(%w[email fingerprint admin delivery_enabled])
+ fingerprint, messages = list.import_key_and_find_fingerprint(find_key_material)
+ set_x_messages(messages)
+ # For an already existing subscription, only update fingerprint if a
+ # new one has been selected from the upload.
+ if fingerprint.present?
+ args["fingerprint"] = fingerprint
+ end
+ if sub.update(args)
200
else
client_error(sub, 422)
@@ -310,7 +370,7 @@ class SchleuderApiDaemon < Sinatra::Base
namespace '/keys' do
get '.json' do
keys = list.keys.sort_by(&:email).map do |key|
- key_to_json(key)
+ key_to_hash(key)
end
json keys
end
@@ -329,7 +389,7 @@ class SchleuderApiDaemon < Sinatra::Base
get '/:fingerprint.json' do |fingerprint|
if key = list.key(fingerprint)
- json key_to_json(key)
+ json key_to_hash(key, true)
else
404
end
@@ -353,7 +413,4 @@ class SchleuderApiDaemon < Sinatra::Base
}
end
end
-
- # Run this class as application
- run!
end
diff --git a/lib/schleuder/conf.rb b/lib/schleuder/conf.rb
index 6eff819..7744953 100644
--- a/lib/schleuder/conf.rb
+++ b/lib/schleuder/conf.rb
@@ -1,3 +1,5 @@
+require 'erb'
+
module Schleuder
class Conf
include Singleton
@@ -120,7 +122,7 @@ module Schleuder
def load_config_file(filename)
file = Pathname.new(filename)
if file.readable?
- YAML.load(file.read)
+ YAML.load(ERB.new(file.read).result)
else
{}
end
diff --git a/lib/schleuder/gpgme/ctx.rb b/lib/schleuder/gpgme/ctx.rb
index 429d619..8333efa 100644
--- a/lib/schleuder/gpgme/ctx.rb
+++ b/lib/schleuder/gpgme/ctx.rb
@@ -14,11 +14,39 @@ module GPGME
result
end
+ # TODO: find solution for I18n — could be a different language in API-clients than here!
+ def interpret_import_result(import_result)
+ case import_result.imports.size
+ when 1
+ import_status = import_result.imports.first
+ if import_status.action == 'not imported'
+ [nil, "Key #{import_status.fpr} could not be imported!"]
+ else
+ [import_status.fpr, nil]
+ end
+ when 0
+ [nil, "The given key material did not contain any keys!"]
+ else
+ # TODO: report import-stati of the keys?
+ [nil, "The given key material contained more than one key, could not determine which fingerprint to use. Please set it manually!"]
+ end
+ end
+
def find_keys(input=nil, secret_only=nil)
_, input = clean_and_classify_input(input)
keys(input, secret_only)
end
+ def find_distinct_key(input=nil, secret_only=nil)
+ _, input = clean_and_classify_input(input)
+ keys = keys(input, secret_only)
+ if keys.size == 1
+ keys.first
+ else
+ nil
+ end
+ end
+
def clean_and_classify_input(input)
case input
when /.*?([^ <>]+@[^ <>]+).*?/
@@ -70,7 +98,7 @@ module GPGME
end
def refresh_key(fingerprint)
- args = "--keyserver #{Conf.keyserver} --refresh-keys #{fingerprint}"
+ args = "#{keyserver_arg} --refresh-keys #{fingerprint}"
gpgerr, gpgout, exitcode = self.class.gpgcli(args)
if exitcode > 0
@@ -84,7 +112,7 @@ module GPGME
else
lines = translate_output('key_updated', gpgout).reject do |line|
# Reduce the noise a little.
- line.match(/.* \(unchanged\)\.$/)
+ line.match(/.* \(unchanged\):$/)
end
lines.join("\n")
end
@@ -107,13 +135,13 @@ module GPGME
def fetch_key_gpg_arguments_for(input)
case input
when Conf::FINGERPRINT_REGEXP
- "--keyserver #{Conf.keyserver} --recv-key #{input}"
+ "#{keyserver_arg} --recv-key #{input}"
when /^http/
"--fetch-key #{input}"
when /@/
# --recv-key doesn't work with email-addresses, so we use --locate-key
# restricted to keyservers.
- "--keyserver #{Conf.keyserver} --auto-key-locate keyserver --locate-key #{input}"
+ "#{keyserver_arg} --auto-key-locate keyserver --locate-key #{input}"
else
[nil, I18n.t("fetch_key.invalid_input")]
end
@@ -122,8 +150,9 @@ module GPGME
def translate_output(locale_key, gpgoutput)
import_states = translate_import_data(gpgoutput)
strings = import_states.map do |fingerprint, states|
- I18n.t(locale_key, { fingerprint: fingerprint,
- states: states.join(', ') })
+ key = find_distinct_key(fingerprint)
+ I18n.t(locale_key, { key_oneline: key.oneline,
+ states: states.to_sentence })
end
strings
end
@@ -219,5 +248,13 @@ module GPGME
File.delete(path)
end
end
+
+ def keyserver_arg
+ if Conf.keyserver.present?
+ "--keyserver #{Conf.keyserver}"
+ else
+ ""
+ end
+ end
end
end
diff --git a/lib/schleuder/gpgme/key.rb b/lib/schleuder/gpgme/key.rb
index a4bca4f..f6bd06d 100644
--- a/lib/schleuder/gpgme/key.rb
+++ b/lib/schleuder/gpgme/key.rb
@@ -18,6 +18,41 @@ module GPGME
s
end
+ def generated_at
+ primary_subkey.timestamp
+ end
+
+ def expired?
+ expired.present?
+ end
+
+ def oneline
+ @oneline ||=
+ begin
+ datefmt = '%Y-%m-%d'
+ attribs = [
+ "0x#{fingerprint}",
+ email,
+ generated_at.strftime(datefmt)
+ ]
+ if usability_issue.present?
+ case usability_issue
+ when :expired
+ attribs << "[expired: #{expires.strftime(datefmt)}]"
+ when :revoked
+ # TODO: add revocation date when it's available.
+ attribs << "[revoked]"
+ else
+ attribs << "[#{usability_issue}]"
+ end
+ end
+ if expires? && ! expired?
+ attribs << "[expires: #{expires.strftime(datefmt)}]"
+ end
+ attribs.join(' ')
+ end
+ end
+
def armored
"#{self.to_s}\n\n#{export(armor: true).read}"
end
diff --git a/lib/schleuder/list.rb b/lib/schleuder/list.rb
index c15aba3..d981ba7 100644
--- a/lib/schleuder/list.rb
+++ b/lib/schleuder/list.rb
@@ -60,7 +60,7 @@ module Schleuder
# TODO: find out why we break translations and available_locales if we use I18n.available_locales here.
in: %w(de en),
}
- validates :public_footer,
+ validates :public_footer, :internal_footer,
allow_blank: true,
format: {
with: /\A[[:graph:]\s]*\z/i,
@@ -107,6 +107,8 @@ module Schleuder
gpg.find_keys(identifier, secret_only)
end
+ # TODO: find better name for this method. It does more than the current
+ # name suggests (filtering for capability).
def distinct_key(identifier)
keys = keys(identifier).select { |key| key.usable_for?(:encrypt) }
if keys.size == 1
@@ -120,6 +122,13 @@ module Schleuder
gpg.keyimport(importable)
end
+ def import_key_and_find_fingerprint(key_material)
+ return nil if key_material.blank?
+
+ import_result = import_key(key_material)
+ gpg.interpret_import_result(import_result)
+ end
+
def delete_key(fingerprint)
if key = keys(fingerprint).first
key.delete!
@@ -160,8 +169,7 @@ module Schleuder
expiring.each do |key,days|
text << I18n.t('key_expires', {
days: days,
- fingerprint: key.fingerprint,
- email: key.email
+ key_oneline: key.oneline
})
text << "\n"
end
@@ -169,8 +177,7 @@ module Schleuder
unusable.each do |key,usability_issue|
text << I18n.t('key_unusable', {
usability_issue: usability_issue,
- fingerprint: key.fingerprint,
- email: key.email
+ key_oneline: key.oneline
})
text << "\n"
end
@@ -261,21 +268,29 @@ module Schleuder
@listdir ||= self.class.listdir(self.email)
end
- # TODO: get rid of this method in the future
- def subscribe(email, fingerprint=nil, adminflag=false, deliveryflag=true)
- # Ensure we have true or false as values for these two attributes.
- admin = (['true', '1'].include? adminflag.to_s)
- delivery_enabled = !(['false', '0'].include? deliveryflag.to_s)
-
- sub = Subscription.new(
+ # A convenience-method to simplify other code.
+ def subscribe(email, fingerprint=nil, adminflag=nil, deliveryflag=nil, key_material=nil)
+ messages = nil
+ args = {
list_id: self.id,
- email: email,
- fingerprint: fingerprint,
- admin: admin,
- delivery_enabled: delivery_enabled
- )
- sub.save
- sub
+ email: email
+ }
+ if key_material.present?
+ fingerprint, messages = import_key_and_find_fingerprint(key_material)
+ end
+ args[:fingerprint] = fingerprint
+ # ActiveRecord does not treat nil as falsy for boolean columns, so we
+ # have to avoid that in order to not receive an invalid object. The
+ # database will use the column's default-value if no value is being
+ # given. (I'd rather not duplicate the defaults here.)
+ if ! adminflag.nil?
+ args[:admin] = adminflag
+ end
+ if ! deliveryflag.nil?
+ args[:delivery_enabled] = deliveryflag
+ end
+ subscription = Subscription.create(args)
+ [subscription, messages]
end
def unsubscribe(email, delete_key=false)
@@ -328,6 +343,7 @@ module Schleuder
def send_to_subscriptions(mail)
logger.debug "Sending to subscriptions."
+ mail.add_internal_footer!
self.subscriptions.each do |subscription|
begin
subscription.send_mail(mail)
@@ -335,6 +351,7 @@ module Schleuder
msg = I18n.t('errors.delivery_error',
{ email: subscription.email, error: exc.to_s })
logger.error msg
+ logger.error exc
end
end
end
diff --git a/lib/schleuder/list_builder.rb b/lib/schleuder/list_builder.rb
index 70d814d..de7e3a0 100644
--- a/lib/schleuder/list_builder.rb
+++ b/lib/schleuder/list_builder.rb
@@ -1,10 +1,11 @@
module Schleuder
class ListBuilder
- def initialize(list_attributes, adminemail=nil, adminkey=nil)
+ def initialize(list_attributes, adminemail=nil, adminfingerprint=nil, adminkey=nil)
@list_attributes = list_attributes.with_indifferent_access
@listname = list_attributes[:email]
@fingerprint = list_attributes[:fingerprint]
@adminemail = adminemail
+ @adminfingerprint = adminfingerprint
@adminkey = adminkey
end
@@ -48,31 +49,16 @@ module Schleuder
list.save!
- if @adminkey.present?
- import_result = list.import_key(@adminkey)
- # Get the fingerprint of the imported key if it was exactly one. If it
- # was imported or was already present doesn't matter.
- if import_result.considered == 1
- admin_fpr = import_result.imports.first.fpr
- end
- end
-
- if @adminemail.present?
- # Try if we can find the admin-key "manually". Maybe it's present
- # in the keyring aleady.
- if admin_fpr.blank?
- key = list.distinct_key(@adminemail)
- if key
- admin_fpr = key.fingerprint
- end
- end
- sub = list.subscribe(@adminemail, admin_fpr, true)
+ if @adminemail.blank?
+ msg = nil
+ else
+ sub, msg = list.subscribe(@adminemail, @adminfingerprint, true, true, @adminkey)
if sub.errors.present?
- raise ActiveModelError.new(sub.errors)
+ raise Errors::ActiveModelError.new(sub.errors)
end
end
- list
+ [list, msg]
end
def gpg
diff --git a/lib/schleuder/mail/message.rb b/lib/schleuder/mail/message.rb
index 778ec69..c7569a8 100644
--- a/lib/schleuder/mail/message.rb
+++ b/lib/schleuder/mail/message.rb
@@ -88,6 +88,7 @@ module Mail
# We copied the content-headers, so we need to copy the body encoded.
# Otherwise the content might become unlegible.
wrapper_part.body = self.body.encoded
+ wrapper_part.charset = self.body.charset || self.class.default_charset
end
clean.add_part(wrapper_part)
@@ -99,17 +100,13 @@ module Mail
self.parts.unshift(parts.delete_at(parts.size-1))
end
- def add_footer!
+ def add_public_footer!
# Add public_footer unless it's empty?.
- if self.list.present? && ! self.list.public_footer.to_s.strip.empty?
- footer_part = Mail::Part.new
- footer_part.body = list.public_footer.strip
- if parts.size == 1 && parts.first.mime_type == 'multipart/mixed' && parts.first.parts.size == 1 && parts.first.parts.first.mime_type == 'text/plain'
- self.parts.first.add_part footer_part
- else
- self.add_part footer_part
- end
- end
+ add_footer!(:public_footer)
+ end
+
+ def add_internal_footer!
+ add_footer!(:internal_footer)
end
def was_encrypted?
@@ -207,7 +204,7 @@ module Mail
end
@keywords = []
- part.body = part.decoded.lines.map.with_index do |line, i|
+ lines = part.decoded.lines.map.with_index do |line, i|
# Break after some lines to not run all the way through maybe huge emails.
if i > 1000
break
@@ -222,7 +219,14 @@ module Mail
else
line
end
- end.compact.join
+ end
+
+ # Work around problems with re-encoding the body. If we delete the
+ # content-transfer-encoding prior to re-assigning the body, and let Mail
+ # decide itself how to encode, it works. If we don't, some
+ # character-sequences are not properly re-encoded.
+ part.content_transfer_encoding = nil
+ part.body = lines.compact.join
@keywords
end
@@ -408,6 +412,26 @@ module Mail
private
+ def add_footer!(footer_attribute)
+ if self.list.blank? || self.list.send(footer_attribute).to_s.empty?
+ return
+ end
+ footer_part = Mail::Part.new
+ footer_part.body = self.list.send(footer_attribute).to_s
+ if wrapped_single_text_part?
+ self.parts.first.add_part footer_part
+ else
+ self.add_part footer_part
+ end
+ end
+
+ def wrapped_single_text_part?
+ parts.size == 1 &&
+ parts.first.mime_type == 'multipart/mixed' &&
+ parts.first.parts.size == 1 &&
+ parts.first.parts.first.mime_type == 'text/plain'
+ end
+
def _add_subject_prefix(suffix)
attrib = "subject_prefix"
if suffix
diff --git a/lib/schleuder/plugin_runners/base.rb b/lib/schleuder/plugin_runners/base.rb
index 9448d05..57d55f5 100644
--- a/lib/schleuder/plugin_runners/base.rb
+++ b/lib/schleuder/plugin_runners/base.rb
@@ -25,7 +25,7 @@ module Schleuder
return error if error
command = keyword.gsub('-', '_')
- if command == 'listname'
+ if ['list_name', 'listname'].include? (command)
return nil
elsif ! @plugin_module.respond_to?(command)
return I18n.t('plugins.unknown_keyword', keyword: keyword)
@@ -66,7 +66,7 @@ module Schleuder
def check_listname_keyword
return nil if @mail.keywords.blank?
- listname_kw = @mail.keywords.assoc('listname')
+ listname_kw = @mail.keywords.assoc('list-name') || @mail.keywords.assoc('listname')
if listname_kw.blank?
@mail.reply_to_signer I18n.t(:missing_listname_keyword_error)
exit
diff --git a/lib/schleuder/plugins/key_management.rb b/lib/schleuder/plugins/key_management.rb
index 8cfdf74..4826547 100644
--- a/lib/schleuder/plugins/key_management.rb
+++ b/lib/schleuder/plugins/key_management.rb
@@ -40,7 +40,7 @@ module Schleuder
end
def self.list_keys(arguments, list, mail)
- args = arguments.presence
+ args = Array(arguments.presence || '')
args.map do |argument|
# In this case it shall be allowed to match keys by arbitrary
# sub-strings, therefore we use `list.gpg` directly to not have the
@@ -76,6 +76,9 @@ module Schleuder
end
end
+ # helper methods
+ private
+
def self.is_armored_key?(material)
return false unless /^-----BEGIN PGP PUBLIC KEY BLOCK-----$/ =~ material
return false unless /^-----END PGP PUBLIC KEY BLOCK-----$/ =~ material
diff --git a/lib/schleuder/plugins/resend.rb b/lib/schleuder/plugins/resend.rb
index 5422ccd..9c439c4 100644
--- a/lib/schleuder/plugins/resend.rb
+++ b/lib/schleuder/plugins/resend.rb
@@ -24,7 +24,34 @@ module Schleuder
resend_it_cc(arguments, mail, true)
end
+ def self.resend_unencrypted(arguments, list, mail)
+ do_resend_unencrypted(arguments, list, mail, :to)
+ end
+
+ def self.resend_cc_unencrypted(arguments, list, mail)
+ do_resend_unencrypted(arguments, list, mail, :cc)
+ end
+
+ # helper methods
+ private
+
+ def self.do_resend_unencrypted(arguments, list, mail, target)
+ if ! resend_recipients_valid?(mail, arguments)
+ return false
+ end
+
+ recip_map = Hash[Array(arguments).map{|email| [email,''] }]
+
+ if do_resend(mail, recip_map, target, false)
+ mail.add_subject_prefix_out!
+ end
+ end
+
def self.resend_it_cc(arguments, mail, encrypted_only)
+ if ! resend_recipients_valid?(mail, arguments)
+ return false
+ end
+
recip_map = map_with_keys(mail, arguments, encrypted_only)
# Only continue if all recipients are still here.
@@ -38,6 +65,10 @@ module Schleuder
end
def self.resend_it(arguments, mail, encrypted_only)
+ if ! resend_recipients_valid?(mail, arguments)
+ return false
+ end
+
recip_map = map_with_keys(mail, arguments, encrypted_only)
resent_stati = recip_map.map do |email, key|
@@ -63,7 +94,7 @@ module Schleuder
# Compose and send email
new = mail.clean_copy
new[to_or_cc] = recipients_map.keys
- new.add_footer!
+ new.add_public_footer!
new.sender = mail.list.bounce_address
# `dup` gpg_opts because `deliver` changes their value and we need them
# below to determine encryption!
@@ -150,5 +181,16 @@ module Schleuder
'resent_cc'
end
end
+
+ def self.resend_recipients_valid?(mail, recipients)
+ all_valid = true
+ Array(recipients).each do |address|
+ if ! address.match(Conf::EMAIL_REGEXP)
+ mail.add_pseudoheader(:error, I18n.t("plugins.resend.invalid_recipient", address: address))
+ all_valid = false
+ end
+ end
+ all_valid
+ end
end
end
diff --git a/lib/schleuder/plugins/sign_this.rb b/lib/schleuder/plugins/sign_this.rb
index 4a7062d..86a7841 100644
--- a/lib/schleuder/plugins/sign_this.rb
+++ b/lib/schleuder/plugins/sign_this.rb
@@ -14,6 +14,9 @@ module Schleuder
end
end
+ # helper methods
+ private
+
def self.make_signature_part(attachment, list)
material = attachment.body.to_s
return nil if material.strip.blank?
diff --git a/lib/schleuder/plugins/subscription_management.rb b/lib/schleuder/plugins/subscription_management.rb
index d95ec02..541b5c9 100644
--- a/lib/schleuder/plugins/subscription_management.rb
+++ b/lib/schleuder/plugins/subscription_management.rb
@@ -3,17 +3,18 @@ module Schleuder
def self.subscribe(arguments, list, mail)
email = arguments.shift
- # Pop the last two in order to be tolerant about spaces in the
- # fingerprint.
- deliveryflag = arguments.pop
- adminflag = arguments.pop
- # Use the remainders as fingerprint. This enables tolerating spaces.
- fingerprint = arguments.join
- if fingerprint.present?
- fingerprint.sub!(/^0x/, '')
+ if arguments.present?
+ # Collect all arguments that look like fingerprint-material
+ fingerprint = ''
+ while arguments.first.present? && arguments.first.match(/\A(0x)?[a-f0-9]+/i)
+ fingerprint << arguments.shift
+ end
+ # Use possibly remaining args as flags.
+ adminflag = arguments.shift
+ deliveryflag = arguments.shift
end
- sub = list.subscribe(email, fingerprint, adminflag, deliveryflag)
+ sub, _ = list.subscribe(email, fingerprint, adminflag, deliveryflag)
if sub.persisted?
I18n.t(
diff --git a/lib/schleuder/version.rb b/lib/schleuder/version.rb
index f7ea37f..23ec4b1 100644
--- a/lib/schleuder/version.rb
+++ b/lib/schleuder/version.rb
@@ -1,3 +1,3 @@
module Schleuder
- VERSION = '3.1.2'
+ VERSION = '3.2.0'
end
diff --git a/locales/de.yml b/locales/de.yml
index 2d97958..390a44e 100644
--- a/locales/de.yml
+++ b/locales/de.yml
@@ -7,6 +7,8 @@ de:
inclusion: "muss einem der folgenden Werte entsprechen: debug, info, warn, error"
openpgp_header_preference:
inclusion: "muss einem der folgenden Werte entsprechen: sign, encrypt, signencrypt, unprotected, none"
+ internal_footer:
+ invalid: "enthält nicht druckbare Zeichen"
public_footer:
invalid: "enthält nicht druckbare Zeichen"
invalid_email: "ist keine valide E-Mail-Adresse"
@@ -95,6 +97,7 @@ de:
not_resent_no_keys: Resending an <%{email}> fehlgeschlagen (%{num_keys} Schlüssel gefunden und unverschlüsseltes Senden verboten).
encrypted_to: Verschlüsselt an
unencrypted_to: Unverschlüsselt an
+ invalid_recipient: "Ungültige Emailadresse für resend: %{address}"
subscription_management:
forbidden: "Fehler: Du bist nicht berechtigt, das Abo für %{email} zu löschen."
is_not_subscribed: Kein Abo für %{email} gefunden.
@@ -127,8 +130,8 @@ de:
no_output_result: Deine Email ergab keinen Ausgabe-Text.
owner_forward_prefix: Die folgende Email ging für die Listen-Besitzer/innen ein.
no_keywords_error: Deine Email enthielt keine Schlüsselwörter, daher gab es nichts zu tun.
- missing_listname_keyword_error: Deine Email enthielt nicht das notwendige X-LISTNAME-Schlüsselwort, daher wurde sie zurückgewiesen.
- wrong_listname_keyword_error: Deine Email enthielt ein falsches X-LISTNAME-Schlüsselwort. Der Wert dieses Schlüsselworts muss der Emailadresse dieser Liste gleichen.
+ missing_listname_keyword_error: Deine Email enthielt nicht das notwendige X-LIST-NAME-Schlüsselwort, daher wurde sie zurückgewiesen.
+ wrong_listname_keyword_error: Deine Email enthielt ein falsches X-LIST-NAME-Schlüsselwort. Der Wert dieses Schlüsselworts muss der Emailadresse dieser Liste gleichen.
bounces_drop_all: Die angehängte Email hätte zurückgewiesen (bounced) werden sollen, wurde aber stillschweigend fallen gelassen, weil die Konfiguration dieser Liste definiert, dass für diese Liste nie Email zurückgewiesen werden soll.
bounces_drop_on_headers: "Die angehängte Email hätte zurückgewiesen (bounce) werden sollen, wurde aber stillschweigend fallen gelassen, weil diese Kopfzeile gefunden wurde: %{key}: %{value}"
bounces_notify_admins: "Die angehängte Email wurde mit folgender Nachricht zurückgewiesen:"
@@ -139,15 +142,23 @@ de:
automated_message_subject: Automatische Nachricht empfangen
check_keys: Schlüsselprüfung
check_keys_intro: "Bitte kümmere dich um die folgenden Schlüssel für Liste %{email}."
- key_expires: Schlüssel %{fingerprint} %{email} läuft in %{days} Tagen ab.
- key_unusable: Schlüssel %{fingerprint} %{email} ist %{usability_issue}.
+ key_expires: |
+ Dieser Schlüssel läuft in %{days} Tagen ab:
+ %{key_oneline}
+ key_unusable: |
+ Dieser Schlüssel ist %{usability_issue}:
+ %{key_oneline}
missed_message_due_to_unusable_key: "Du hast eine Email von %{list_email} verpasst weil mit deinem Abo kein (benutzbarer) OpenPGP-Schlüssel verknüpft ist. Bitte kümmere dich darum."
refresh_keys: Schlüsselaktualisierung
refresh_keys_intro: "Die Aktualisierung aller Schlüssel des Schlüsselrings für Liste %{email} ergab dies:"
pin_keys: Schlüsselpinning
pin_keys_intro: "Die Überprüfung aller Abos der Liste %{email} ergab, dass wir für folgende Abos einen Schlüssel zur Verwendung festgelegt haben:"
- key_updated: Schlüssel %{fingerprint} wurde aktualisiert (%{states}).
- key_fetched: Schlüssel %{fingerprint} wurde geholt (%{states}).
+ key_updated: |
+ Dieser Schlüssel wurde aktualisiert (%{states}):
+ %{key_oneline}
+ key_fetched: |
+ Dieser Schlüssel wurde geholt (%{states}):
+ %{key_oneline}
import_states:
unchanged: unverändert
new_key: neuer Schlüssel
diff --git a/locales/en.yml b/locales/en.yml
index bf3c7b4..d6bd874 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -7,6 +7,8 @@ en:
inclusion: "must be one of: debug, info, warn, error"
openpgp_header_preference:
inclusion: "must be one of: sign, encrypt, signencrypt, unprotected, none"
+ internal_footer:
+ invalid: "includes non-printable characters"
public_footer:
invalid: "includes non-printable characters"
invalid_email: "is not a valid email address"
@@ -95,6 +97,7 @@ en:
not_resent_no_keys: Resending to <%{email}> failed (%{num_keys} keys found and unencrypted sending disallowed).
encrypted_to: Encrypted to
unencrypted_to: Unencrypted to
+ invalid_recipient: "Invalid email-address for resending: %{address}"
subscription_management:
forbidden: "Error: You're not allowed to unsubscribe %{email}."
is_not_subscribed: "%{email} is not subscribed."
@@ -127,8 +130,8 @@ en:
no_output_result: Your message resulted in no output.
owner_forward_prefix: The following message was received for the list-owners.
no_keywords_error: Your message didn't contain any keywords, thus there was nothing to do.
- missing_listname_keyword_error: Your message didn't contain the mandatory X-LISTNAME-keyword, thus it was rejected.
- wrong_listname_keyword_error: Your message contained a wrong X-LISTNAME-keyword. The value of that keyword must match the email address of this list.
+ missing_listname_keyword_error: Your message didn't contain the mandatory X-LIST-NAME-keyword, thus it was rejected.
+ wrong_listname_keyword_error: Your message contained a wrong X-LIST-NAME-keyword. The value of that keyword must match the email address of this list.
bounces_drop_all: The attached message should have been bounced but was dropped without further notice because the list's configuration defines that no message should ever be bounced.
bounces_drop_on_headers: "The attached message should have been bounced but was dropped without further notice because it matched this header-line: %{key}: %{value}"
bounces_notify_admins: "The attached message was bounced with the following notice:"
@@ -139,15 +142,23 @@ en:
automated_message_subject: Automated message received
check_keys: Keys check
check_keys_intro: "Please take care of these keys for list %{email}."
- key_expires: Key %{fingerprint} expires in %{days} days.
- key_unusable: Key %{fingerprint} is %{usability_issue}.
+ key_expires: |
+ This key expires in %{days} days:
+ %{key_oneline}
+ key_unusable: |
+ This key is %{usability_issue}:
+ %{key_oneline}
missed_message_due_to_unusable_key: "You missed an email from %{list_email} because your subscription isn't associated with a (usable) OpenPGP key. Please fix this."
refresh_keys: Keys update
refresh_keys_intro: "Refreshing all keys from the keyring of list %{email} resulted in this:"
pin_keys: Keys pinning
pin_keys_intro: "While checking all subscriptions of list %{email} we were pinning a matching key for the following subscriptions:"
- key_updated: Key %{fingerprint} was updated (%{states}).
- key_fetched: Key %{fingerprint} was fetched (%{states}).
+ key_updated: |
+ This key was updated (%{states}):
+ %{key_oneline}
+ key_fetched: |
+ This key was fetched (%{states}):
+ %{key_oneline}
import_states:
unchanged: unchanged
new_key: new key
diff --git a/schleuder.gemspec b/schleuder.gemspec
index 40d4a0e..a0c489f 100644
--- a/schleuder.gemspec
+++ b/schleuder.gemspec
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
s.homepage = "https://schleuder.nadir.org/"
s.summary = "Schleuder is a gpg-enabled mailinglist with remailing-capabilities."
s.description = "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.\n\nSchleuder takes care of all decryption and (re-)encryption, stripping of headers, and more. Schleuder can also send out its own public key upon request and process administrative commands by email."
- s.files = `git ls-files lib locales etc db/schema.rb README.md Rakefile bin/pinentry-clearpassphrase`.split
+ s.files = `git ls-files lib locales etc db README.md Rakefile bin/pinentry-clearpassphrase`.split
s.executables = %w[schleuder schleuder-api-daemon]
s.platform = Gem::Platform::RUBY
s.require_path = 'lib'
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.license = 'GPL-3.0'
s.add_runtime_dependency 'mail-gpg', '~> 0.3.0'
s.add_runtime_dependency 'activerecord', '~> 4.1'
- s.add_runtime_dependency 'rake', '~> 12'
+ s.add_runtime_dependency 'rake', '>= 10.5.0'
s.add_runtime_dependency 'sqlite3', '~> 1'
s.add_runtime_dependency 'sinatra', '~> 1'
s.add_runtime_dependency 'sinatra-contrib', '~> 1'
@@ -32,6 +32,7 @@ Gem::Specification.new do |s|
s.add_development_dependency 'hirb'
s.add_development_dependency 'factory_girl'
s.add_development_dependency 'database_cleaner'
+ s.add_development_dependency 'simplecov-console'
s.post_install_message = "
Please consider additionally installing schleuder-cli (allows to
diff --git a/spec/factories/lists.rb b/spec/factories/lists.rb
index 4c8d38a..c572f86 100644
--- a/spec/factories/lists.rb
+++ b/spec/factories/lists.rb
@@ -7,6 +7,7 @@ FactoryGirl.define do
subject_prefix_in nil
subject_prefix_out nil
openpgp_header_preference "signencrypt"
+ internal_footer nil
public_footer nil
headers_to_meta ["from", "to", "cc", "date"]
bounces_drop_on_headers "x-spam-flag" => true
diff --git a/spec/fixtures/expiring_key.txt b/spec/fixtures/expiring_key.txt
new file mode 100644
index 0000000..e514153
--- /dev/null
+++ b/spec/fixtures/expiring_key.txt
@@ -0,0 +1,30 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFmCxIEBCADUiLMwu7I/EuyyNlidrllyVt2GkXVwSa1yIR3nA1KyDqizspih
+SL4eVaZOvVWyJaGxIFbmG5WHCQi0YuuDv0jcGYT3lmyPumrDYyaU5GHm05gmZ+R4
++oSjJG/v5z7L6au2G/+Iw5pQg819VviJ6po+QwmQeUkHd7xhiniPq7aoVFcoltgE
+bQF8sPWJ1jyFnTmL36MECgAP9MKPfcSuHzUWJxiDSo8Siqaf7uUY7F2Gz9pBYuiv
+g6PJqnagtjZGPgxKqQak/IRvPjk6WWIGpaO9fBl1GQ/W3iuhkObBWuHNdErygQX8
+0VDc/wRfIeFsMjaPQ/nQOALgzsTGZWpdO/T9ABEBAAG0JmV4cGlyaW5nIGtleSA8
+ZXhwaXJpbmdrZXlAZXhhbXBsZS5vcmc+iQFUBBMBCAA+FiEEQh+/cZBkATZ4hZPN
+num+WSnKzCAFAlmCxIECGwMFCSWYBgAFCwkIBwIGFQgJCgsCBBYCAwECHgECF4AA
+CgkQnum+WSnKzCBnQwgAmCy016/VxvLHSZ45TuYO60dGfrmu6nA3/F2qKjXsszlw
+UL+7jiFZZzLRQF8GJGnHJnhg6mCz8yQKam8pOqaYCqVvc19GyzPdQ22uiOA0QcuE
+0kHd/ncAEtRJ3q+yeb7rcxVlHDHx2VpzNY3d2aRsgSYin+iAWKROUt3efZyTquzJ
+etOl6SvbwTv+qqDmrEj/zNQmr1Dic+KtLvK8ClyXrfZou5wCGsX14fKk7xkhNPBS
+f2oCaPjMjGsh9gZ+nZfpv+ppTAbZRPSKHuQrWksFGGxDTMigQORNtwA2SBljmL63
+neqajK086P3U3dZm4lD4dPCG/V2VVYgLsooCzJZUYbkBDQRZgsSBAQgAx9bBVzuv
+AlLbPIqHyXsjurosvoolUKAjdWFaGCK1lZHdy3RwuVVdgWA7qXpr/T6sQ0OzxaK/
+HT/1G0haCKd3zC2dD3FUQJHzIPXqE4x36a+PF/qq2vVe43+8Lwz2ceopHprHP7Sx
+anqnagiV/JQNJna7WTk4RM/61oHiBNWyusEhW2vynTs92ltEL4Esh3BKpyQ6mmV+
+DLGalraMSDcuGRzwQMTlRrXygxq2WHc9px3M3PfMBSj86ouhSOkwOPI3eV4XPzB+
+uXxwcZTsUxPm8BVVt5OAmrLaaX2K/AWEsZlVdSJpZrFD2WMMo3YRKrwvM8AHhuQO
+FJKaHPibi+a3zwARAQABiQE8BBgBCAAmFiEEQh+/cZBkATZ4hZPNnum+WSnKzCAF
+AlmCxIECGwwFCSWYBgAACgkQnum+WSnKzCAwMAf+L5ZFRTgha0AKnmWTR7JA2weg
+EPPDt33AHZFOCYcxuHm8TPgl4OuSvoxxWovCm56/nIiWnqojNVlWCQxAHHQmC1dA
+xJ7EDqdqCqVvRyRCzUsONICKeHdAoNA7TWw/DF+JZn2l+ud2w+EiQt9qy3sQXZeG
+SOG6s5kSPxxuwPmmMmCu5u+QI4elcHaw9RLGFbwlO4VlBS/n0k6P+UDY/4Eqkyi0
+L7GCFtw3ZKjRR24MKYdGRbv7hpzvp6JyrbYA9H4wGJir43Dk/EH7Ul+4Y8FcHFk4
+FU9yPzi0dW07tKD29F0GxRdqtIs0rWHIbUJVskDwDhJdteIsO/bTuKN4tZvtcQ==
+=c/Mw
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/spec/fixtures/mails/qp-encoding-clear.eml b/spec/fixtures/mails/qp-encoding-clear.eml
new file mode 100644
index 0000000..e3ca166
--- /dev/null
+++ b/spec/fixtures/mails/qp-encoding-clear.eml
@@ -0,0 +1,25 @@
+Date: Tue, 11 Jul 2017 12:00:40 +0200
+From: test at localhost
+To: schleuder at example.org
+Subject: Input Test QP encoding broken
+Message-ID: <20170711100040.GF7104 at blabla>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=utf-8
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+On 17-07-11 11:59:56, schleuder at nadir.org wrote:
+> From: foo
+> To: schleuder at example.org
+> Cc:=20
+> Date: Tue, 11 Jul 2017 11:59:49 +0200
+> Sig: Good signature from somethingsomething
+> Enc: Encrypted
+
+> foo foo foo
+> foo foo foo
+> foo foo foo
+>=20
+> [1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=3D867031
+
+https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=3D867031
diff --git a/spec/fixtures/mails/qp-encoding-encrypted+signed.eml b/spec/fixtures/mails/qp-encoding-encrypted+signed.eml
new file mode 100644
index 0000000..60f6783
--- /dev/null
+++ b/spec/fixtures/mails/qp-encoding-encrypted+signed.eml
@@ -0,0 +1,65 @@
+From: test at localhost
+Subject: something
+To: schleuder at example.org
+Message-ID: <381fb765-1e53-4578-bc93-bff7e689cbaa at example.org>
+Date: Fri, 21 Jul 2017 11:50:35 +0200
+MIME-Version: 1.0
+Content-Type: multipart/encrypted;
+ protocol="application/pgp-encrypted";
+ boundary="OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3"
+
+This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
+--OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3
+Content-Type: application/pgp-encrypted
+Content-Description: PGP/MIME version identification
+
+Version: 1
+
+--OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3
+Content-Type: application/octet-stream; name="encrypted.asc"
+Content-Description: OpenPGP encrypted message
+Content-Disposition: inline; filename="encrypted.asc"
+
+-----BEGIN PGP MESSAGE-----
+
+hQIMA691X8Gl2MArAQ/7BrJwnNXjz+DhuRAyShy4LttCKX0RRG2rV2FegzYdfd40
+oB9ZPXtyL8L9fQ3uwUYpiexCqbR1yYNqPRkRZeHBOAVtHiUruZvuL+xq6yBXq3nt
+5Cz/TqN79B1G1+YyX+m5DOwURfiV104lBtfVE2g+XIG/+HGBBuviRT3VJrPQw1ld
+oVaQNf6KQORaUwYUkLxlX+M1MdPxA7MxKmCxVBJpakGtgKTihUcE9u0g5r3kYQke
+qyXglGu6RaFtztk5+JSrwn+z59Yjp+uD+qdYdtvXCkPHNWABT1CRwy2c7zUExWlh
+0sCxOwi6YeNJOBP1U2ceMNEHTe2XOarY5HOL1TW+bH8Ta1lCvGtwoZrwMJuDDpW0
+hpnE2YZmjh6M/WV5+ToJpcw6u5K2xBVB1hLWgLf5e8H9tVFhBywvjXt6Q1PKcMk1
+MfqAfYN22UYWbpbZ+AABccR+bAyECPspYbEjiZ9rjRDaDyjc5m6Jq4ctqy1ETtCA
+or/0sf8a+8Cxc9+tG3iaQ1f63LJTm914GJQfBb1Yi1PoEPOPxveUBcC88EVaaNvP
+PdnRx+L633FDDT2BHiYcK1/dD1BVjvZECtRK/522Y46epJi7E+U7b/RjOYcnfS4z
+ob344qvOhN+m0RJmEZVbYfr6UhxPMyQcDcX4kzpugVr/nwIk3mh3OBUuHeTovALS
+6gHa460vxp16neb5thpp96g60VC3R93KXTCPyfg5XvkziKPw/UM7vmz/Rqb4tOOb
+eMcsH73E34ONqfcPzX+Lrg7RqXhK9EZ5iuU3bFIEu1Xp0ixlXGPotyY/VR8P/+zu
+Hd8C2xoE8o7SeqVIf7UIViPV1fzB/DxHwa1Q+5rJMoDvP0CTqgC5YaRw24ab7Zbh
+GfKAPoAEiufwgAtsVNE9yF7+fb6U0AZ8qh0Jos+ksCA6FGOflnxwqCEYrH9hT7n6
+kAeVkoAoWH1YtNTQerMIASOOpjYMHu5AQnJ6RFOKDvYy+SQycTLz0DjkCCGakBPv
+ToyndsuGZRsnpP7ctO7CgtpCxFq8YpbZ/YzZ8aOf3UjW4K7dB4sgf2RrSCdCevK/
+8tDB/0a/t45KQoZiTVwOmWp3neL+r6rhelgAYKxV7fdIHn5C/ROH9Rm3m0Fua2mb
+onuelGo5bdndcl1BrkiA2iv03reOR6ID68JFJvp4KvZflu898jB2ZIkWw4vYaHDg
+Z5TnI9LlnJUqVFXn9SF3x8TkRdJysw6w4el8GFKzwxe7/y0CfH9MCCUTQ3pdpfna
+WTs00ElvgsWvIE8Nhz2cRnWfleex3/LJQhm/K24iQL45xR3RoGaO93KrbBZkUQp3
+0Nu+laHElEyA97YGxs36/3rinT6EMrq6gATrNHaXM94zPQR2g7+ooHTUR/RCvRLM
+80V5cpgcGkqT6Yy+wFDsTFaMJdH8ZlrOSdtthlePGqx11gvFOL8AJpGNMOq05OuV
+xcS7XpirPqgrblLdvgOQFM6D70nJ7g0HlMPyAm45+PyHPg4qVYXj91441mbnZfSC
+QUTtnJBUEp9ibpzckDn1i5ZYPEZ6QmIWfPc2heO7w3wEznwknTHfh0B2zjNAm9/B
+riW4xSvfIfpGyZnWrW/fGGYXYegiW0ljh49qBnTzEwSmr01YUoIl4mPA/dDou3RV
+WD/MgfvN9cYy4mZBMmwC45gwkU1gUELGHMfnOtLSdJ8MbOiW6CdIlvesO4KdrDdC
+rHrVBxdMXKSk1alcRi3KXzv86yF3q/XWyzC/BGVE9WF3DuG90NadnGDTP+IcGvy8
+fyaQhUIZ0AM/XawW/y/TBTbb2mjti6FFMsMy5aTg7OJO9kCgdkjgihQXJK1Y3jwQ
+Zk/qsvRDZc5u50z9+/Ln3OKoRw1V7dNTYwn30iiQ0jXWN3qfrl61aB3EprJuoCCO
+N97Jyq+s3aZNwf73VaIqjcKtIt9/ASvA2RczgcBzANG0C9GCeKXianQz14Qr0uvv
+8wG+thSAkGJgFlBEMfKXTYaRJnp+K0yjeBAP/JKVyltUYfamXLV9XLHn8sqsGwgL
+5lgrPeq0K7vTzCuhdWrP8kbACFvcXzJVi6wJ6q9r9w5sOnPIezjOX+42so6VB/lB
+j3MMeAdEIkzHuY3JrUseYp3O+/xjQzKw/8ep8IXL53sq+nLHeqJotN5geMVHJlIe
+fjKBmPwNygUETOV/8xfmjSqbQ5mLiy9BwtwUkRjkPV7iSl5GU4zreMEO79E2dWfL
+xzaqyAiBk4um86YPQBklXXFJlvyd9z/RJZiiSEdUcuF8NAtQhE0cwrkfK4EGeftT
+Xe7gviDKl62tMcXuN8d5E0RUeVdCGEaSQQy7
+=QAIj
+-----END PGP MESSAGE-----
+
+--OguUqTN7k1TEvsvAHoUI3Hv5qStt1SOH3--
diff --git a/spec/fixtures/mails/qp-encoding-encrypted.eml b/spec/fixtures/mails/qp-encoding-encrypted.eml
new file mode 100644
index 0000000..f349df7
--- /dev/null
+++ b/spec/fixtures/mails/qp-encoding-encrypted.eml
@@ -0,0 +1,47 @@
+Date: Tue, 11 Jul 2017 12:00:40 +0200
+From: test at localhost
+To: schleuder at example.org
+Subject: Input Test QP encoding broken encrypted
+Message-ID: <20170711100040.GF7104 at blabla>
+MIME-Version: 1.0
+Content-Type: multipart/encrypted; protocol="application/pgp-encrypted";
+ boundary="4f28nU6agdXSinmL"
+Content-Disposition: inline
+
+
+--4f28nU6agdXSinmL
+Content-Type: application/pgp-encrypted
+Content-Disposition: attachment
+
+Version: 1
+
+--4f28nU6agdXSinmL
+Content-Type: application/octet-stream
+Content-Disposition: attachment; filename="msg.asc"
+
+-----BEGIN PGP MESSAGE-----
+
+hQIMA691X8Gl2MArARAAreFTl/Yz+V0YBDfl54Zz+S3rENPDKXEmNJNzcYoLIEuH
+beoeWMHY9nzD7QkaNaeNL3foK9gr62njvrf6KsQVgnoUE8cQFhk2FJkPoylWGAnT
+2zI/dZLpMit/J3WSG1U02C/lOXN7M1kuvx7O/tGiKRtudyCwGO+qrAp/BCEIwBDz
+sJcEo9HAU4odV1oPFGDDw+y+EfOmTj+afplsSft0hDmI+hWnkjaWKCxhmR5BEWr2
+fBkaAzH4rMls8g22llZrL3Rens4UT/htOMiDXoxBQ4oE3P+sb8WesmpSJwU77y3J
+7DBdwBfI0q4kI3EjOBMvOQPLCYZl5+3LckQRl5QGPaBC+BaeAsXS2dEPRl5Xw1Ae
+ZzxTgusV7V0hDhNG3z3SzmczfUZ7+6OQ7HsOhHAVa3KmuWpaw8Dr5s2n6AhzZNGs
+A8HmWQETa9DxGQTKrcijB5LyIFTo8d5Hc144En9demO0ApBd3Jg5lhkAr+vAL/z0
+OkL35vosRWMncHx2GB6ido7IoMz4zXiKW34ezXJWvH5SwRdjOuy6WMvOzGToxJcn
+fK55f/xoq63N/uhHuVy8BH56AjI5OAfEowZ1jb/pGGRGM36JwrdwInbwSMU/X34r
+6ERESKrDqRvk27u0C10iAMFMfIdQVlfJN+kKXxyKDxOaukbSexnaFBNyi07b7DzS
+wLUBqDDjF8zXjCQNvOlqcBrVht3e0H4YqUFPfXy+fuGYL6FWobjuUDN9ZYbcuDwu
+w6ioBRoFmUwvOzTI9S40+JDR8RcFPbZuYYzBEZnqNJapsy7XrvXQUXlmOB7klm0s
+4m/d5edXlp93KqbO+oJ05Bse88cXvfDIrRKkV+YSWOB9VxAES3SK79QQRYlJlEdn
+zzcmSt8ODewc6WfO93u0gG/2xCftEHpi/iHFlgrzSVuZMa0Vn5AXva819aJ3t9D7
+OYKEp2IARQHSB30W7iIOK/cyS6ymE27137uETNQ7FinES9929E9WCd+3vk/lSZLr
+DnhSuGSNRV36srVsN7SkbbrvfF6iipJDC3Y1GQVmngpu6nU1r0/koqGkeIJr7Sao
+mGIgpVBBZYbhS3LBw6h87gtYEbKirwNLuP+WHY2uQkLIBCv7/ugrATHn503nwwEp
+GNFu+Bhh6FQZhMfgIlSHpV5jsHeMQFqy/xgW5VgnWhqZAEEKW1AA
+=IB9S
+-----END PGP MESSAGE-----
+
+--4f28nU6agdXSinmL--
+
diff --git a/spec/fixtures/partially_expired_key.txt b/spec/fixtures/partially_expired_key.txt
new file mode 100644
index 0000000..3c9079b
--- /dev/null
+++ b/spec/fixtures/partially_expired_key.txt
@@ -0,0 +1,36 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBExlilkBCACb2AQyclf7latAIE1kCTfKQ9jmcKyf959ymyhzoeNmBDpKjILC
+7MOXtICo/V/xAzhWBK/vT9+56brGUBTugnW3yK+zllQprI3kIYaRS1SrbmKVwVse
+9qLVUL1BssohFaEeQqT4MNh62ziJymqCguGEGXpYlEqzEDTmmhTANiPKRBZDrdfq
+3FU3OJUMTGzuG34mKmXMRr0azprF228LUujMMKyWhG1hxh3El04C4jPuMSbaVcwN
+E2rgIg8jaNAQuSyXkaprPZ8/nRG8UFGRtCMEIEh6Ou6KybF1NI9LQKCwcsGcLHKU
+2u/8vOCExxdwl9Jjlqmof4FQV7bT++6SC0n3ABEBAAG0EWV4cGlyZWQgPGJsYUBm
+b28+iQE+BBMBAgAoBQJMZYpZAhsDBQkAAVGABgsJCAcDAgYVCAIJCgsEFgIDAQIe
+AQIXgAAKCRD3Gj+EEtg4iV6yB/4uDLoN1+TswtGjpUlu2CyjHe7pb05dAU4sWfTV
+I+fxBuyEo+cf/23nOeoGyltBDR0heSg3TIfXQrbWD4WoVsOXPaT0fq6UEzeadkmn
+A5NN3PGkv46o3ZSF1ltkY9ybMgnmRLHYCojSu5bSBMRVyurr0ozwNRPtFUTka8Lj
+wxiwDJ394D5y6PjL56FPkUdKydzFGV2ptSKsqyAJvMBeGlQ4I6TpiBx0Lz2A1Qn+
+4uXgTVPqgalC5YKTTTjOfQcieOOeqtI0LHqDpS/DIPLnwTUCN8OL2TQIeDudm3YI
+P8FCKvImh840vTbpgFSgQeaJzJFv9UrloNyyvbVtaeoxnxoBiQFPBBMBCgA5AhsD
+BgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgBYhBJh2nooQkfNr2IQD7PcaP4QS2DiJ
+BQJZ6HpOAAoJEPcaP4QS2DiJfQwH/2+mSIWXyc8hJyFyPiKFsiwb1JDsAV4DSSUh
+x+KYZsX0biBx6DRBf0IXt2QdrbowPEo9oSnpiiy4tbnM9N2ccvrlls63OKzAXDCF
+ckdB9VmlDRZkPPo9It1EzX0E6x9BXmGXFl5yoGPcsXMRmf2CyQxMDWB7yNfuGFCT
+QC+6qsNJtP8sOk+EZz/5elSq6d3F+k6YzfHrCvRu7mr5ugBtEsp8epSo5gswjOIi
+GAhHX1AaZbxwvWAdR8B3A6RbOqQVZnzsiWbpOg1/Bgg0Td33sh1DdXh63ouuHIdM
+baqT6t8gJbHBaTmfFoT7WPEA8zCDfqU0xeP7AWwSs4KITXC+KoS5AQ0ETGWKWQEI
+AMMZt/RMSzkcOltQLvy0l60ZaKmZBFOryBL10OgqMbma+WkUBE9MAm97CAjsMgJc
+y1Vjw/x4VuxkMYwRN266dp260O/I/0n/C6SdgcmQTmMl5mMQrKh+tYzUEfEdvZuQ
+1TSGvwRU28SFWMBvrZwk+Pl6aL/dSHKcugmus74BlKv0GrZBbfye1JTFNcztVsZR
+HUxZlKgn6ASD8h5HYcnOILJvWaGW+j2lmolP3n1s1VD/FTg0e1hxrlxXdfzwCV3g
+PDPYcz48gFuyLPcLd8gw2tHFG8wJTg+CyNJkWhHFx0NFPk6MHE2BDhSZZAfSxMXD
+1PHwHY63C6C4bBpchXcWlEMAEQEAAYkBJQQYAQIADwUCTGWKWQIbDAUJAAFRgAAK
+CRD3Gj+EEtg4iaqeB/9BC3RLq2Un04ySeYCvak2D4NCQKpM1pKT+JCPiV4YbCSlF
+WgflUVNnSeSZmNwNNOsKOPmsQNpGjxDfwqwHWmjw/bkAGgrRvbysfDwjcvYHKXX4
+gHv500pXIUlV8cBg5Cxsj+n/Yp21lkhunGdFUdj5WW7an9/Hcmo+aoKJpUxxQXDn
+lCNIpoj6iZ+c3RYbKsrVG5v3bktyHS9mTFXCZOx7uq5+DSLBNEQzL1Vl8xq8yGQC
+0zYCyIQbyPebBT0lB8GReq1bEbZW+2wEnSstp+HmxHc2CO1Ha9tJ3MlEJXbE7jPo
+OuHc+6DdF4eZwtQVMM8T6+Z4f7GkRnaeG81EUOGZ
+=xAyq
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/spec/fixtures/revoked_key.txt b/spec/fixtures/revoked_key.txt
new file mode 100644
index 0000000..0db6629
--- /dev/null
+++ b/spec/fixtures/revoked_key.txt
@@ -0,0 +1,33 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQGiBEjVO7oRBADQvT6wtD2IzzIiK0NbrcilCKCp4MWb8cYXTXguwPQI6y0Nerz4
+3YLDaGdb7idJAybemG/BHNVUG90XnCE5XhKKzcjFfklCly+RzRDhJqfBn7z8eH5e
+dsK6J0X1Vgeo02tqA4xd3EDK8rdqL2yZfl/2egH8+85R3gDk+kqkfEp4pwCgp6VO
+aEGVKE6G3fX1R6bSUNJybI0D/jhUEj/HXCs44AF+zIKKSzTCwzfdvjHjjs2uoF7t
+UJa89W7f0EIhFa10RizIfCsivmPxXPxtCvIYGPrssZdFTSgrmy9jU/9pivueyFWY
+TM8iKelPanlmwP5tzXJt4LRzU3OpsneUSyu60Tpad1trK/2QeqFRNYFVr7OFnGWB
+rME5BADOU8Zz+Xc65uKcBtjQXd2razdSuil4fgjHicmIwNYnD2aj6lnc+fnfuAYx
+NPVxUoWfCS4OaSq09OANwFzyWHRGyS/IetaP1kIaxwkOe1j6r0IiF6ZFpZt1DFYF
+ikgnJBCYDvG95j/8M6Ta05KERkxbjq2QSW4F4s0ax/Au1tztc4hnBCARCAAnBQJM
+ikhtIB0BUGxlYXNlIHVzZSAweDA2OUU1NURFIGluc3RlYWQuAAoJEAmKyDpMACjp
+D3sAnjJSaWwbbbPzYug/Jkg+G4Qy+NlKAJ4geMqNPlyPzCci1vqGpTMZwbPm8rQT
+cGF6IDxwYXpAbmFkaXIub3JnPohiBBMRAgAiAhsDAh4BAheABQJKAbofBwsJCAcD
+AgEEFQoIAwUWAgMBAAAKCRAJisg6TAAo6TZkAKCVGY5i5Vty1AHB+Oxw74ek33cP
+lACgjiUAdpcJ0Nb0xjQQT1JPW79LIe6IYAQTEQIAIQUCSNU7ugIbAwcLCQgHAwIB
+BBUCCAMEFgIDAQIeAQIXgAAKCRAJisg6TAAo6X7NAJiXMEyWnG743jgn7kapIylO
+wcAQAJ4742pWNW+u2pyrA2tSTnB5B7g9ILkCDQRI1Tu6EAgA0LZj6Cy3t6vCvbyg
+8tNXEBA3fED2nAFnmsMmQhlOhy/yqpa3WFnxZhcqi8v81E9bCQrhh3hGIi/aLdJT
+6ENxAvDs2vdNAL3Tz3hB/bTGtSLLbgU64xVuMFD7nNeT17GRmabm8utEnKgf6o/c
+n4+nS1YPQq64b3CpId7Va67nF58fxBGuNgFy7uNIN6zqblHgPWD/cr2hR5hPX9kk
+xMgS42PFBqoJZ0I+uaTZRf6pGlsEW9OPIfojK1ttRJKLiykwzxZHRW2qVdl+SiWS
+HH4TtcxG8HNWShdybimBy4fGM2bIk3P6d/t3gQL5/Q5LEBScoDNOYqqUCXcpdGiA
+Ji+CEwADBggAjuxoMm6Mc2ST4B0RkiA3PE3rCFhoc4gzjksTNyYuZj9w9JSADa3V
+l73Su4D4YByCOYxVpdde703dFIlLf4/Gbzdx6ejj5g9Nk3ARR3LtEJsno97HIYQK
+uM0iyTW1cajAJR7gI+K+YQ07G8vFV81t4DlPD9dHJPV6blgy/HgapnthWJBB5L6b
+MaAwk+inqbkisyaxE4Ov6mOULqhzyDkGZXlgeaa0wUcyg28WRulPGOG/xICpuM6a
+FNRKvuBbrX2RH2AiLXwAfclrIe7x7a2RgQcb1tcvfxoQIP7gwAGtsP98dPc1pkma
+0GMMcA57HTRKe/T/ZrAVqF3aXdBL48Nc3IhJBBgRAgAJBQJI1Tu6AhsMAAoJEAmK
+yDpMACjpNwsAnRkZCkPV+RkZmOtoc/UgmZ7AtI3mAJ4vDlEFfTV315Uv73jwVJzi
+9PDUAQ==
+=Latx
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/spec/fixtures/schleuder_at_example_public_key.txt b/spec/fixtures/schleuder_at_example_public_key.txt
index 211da08..e7423ca 100644
--- a/spec/fixtures/schleuder_at_example_public_key.txt
+++ b/spec/fixtures/schleuder_at_example_public_key.txt
@@ -1,3 +1,5 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
mQINBFhGvz0BEADXbbTWo/PStyTznAo/f1UobY0EiVPNKNERvYua2Pnq8BwOQ5bS
qvmWTb+9Fe9PRpmmqqKKeKjMX9yNjYZweqZ2Tda6C9B6shLme/bWotoXYCEjmP4t
g9uWJmu2x+UHH+SSH752eDlTOtz9mZqNjcKlwvZtHcfu3FwvPQ7xqYt3f2Q/e8ES
diff --git a/spec/fixtures/signonly_key.txt b/spec/fixtures/signonly_key.txt
new file mode 100644
index 0000000..040fce1
--- /dev/null
+++ b/spec/fixtures/signonly_key.txt
@@ -0,0 +1,18 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQENBFmC2g4BCADDRv16LnKRbVT9uyjtNkReqM0abvWUKZOBGYr1JV8t8hEeMKwv
+BpPwh1AZyw+Ihhn/BybwyBDfqmInKWE81bGUl0ZRTaX2i/5JP7uTIcn/7DY3O4rN
+Hx/h/Xworqq34MhdoxJCsxFlaEyPaNg9bNNVknoKDRpPTMWBCOhLlvJTr2Lr6gbk
+bDt62fRR6qrwA7Um28jz2j0Dn0bFKv7mDFlQ4zVL89z93oa5G5JRztogWv4FyawM
+slSrvLhMt+Jw8HOL2w0vyPuZTcITgy8mcyajF2/NnP6aW5p6ujXIw4od3gJtDUOI
+Uec+ZMFSDvD1jzv42ksHz5D4SkuWWrtffwfxABEBAAG0H3NpZ25vbmx5IDxzaWdu
+b25seUBleGFtcGxlLm9yZz6JAU4EEwEKADgWIQSxzYuxXCZzxr/Y+ktwss8p4BrV
+PgUCWYLaDgIbAwULCQgHAwUVCgkICwUWAgMBAAIeAQIXgAAKCRBwss8p4BrVPgj+
+B/9hs+NMmdjOM/3KGrL65qVKwAe/zR0xVhmnT+kg9XkCuH1dKT2vae5zkzvU883R
+DTwk5nFpKFuklYDyTtpOxZkmNXzYIBihFdqWLdg9SO/ydXwFB5dcsinAadWYTNg6
+NS6hA5hG7Uc3zush8DzvUvR9JPdKTXLZ4zKNZLJ/SdLQO+e3lgDkfkJ9RH4VNFja
+Fys4nXVLOAnj39Yngcz6e1xngvqM4453/XgHtxg9AnDj4w09LgdoVfonPwKiLiZ7
+f7eFeaHdQr+OWI/0A4Nghzd1SHnBSvlDS7XRiuiu1SYkDWK6M8c7p+It3wHAUeG3
+tXmKTndc5oY20DAh14ez7kt6
+=SgbM
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/spec/schleuder-certificate.pem b/spec/schleuder-certificate.pem
new file mode 100644
index 0000000..1c9ac1d
--- /dev/null
+++ b/spec/schleuder-certificate.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDeDCCAmCgAwIBAgIBADANBgkqhkiG9w0BAQsFADA1MQswCQYDVQQGEwJNVzES
+MBAGA1UECgwJU2NobGV1ZGVyMRIwEAYDVQQLDAlzY2hsZXVkZXIwHhcNMTcwNjA3
+MTU1NDU2WhcNMjcwNjA1MTU1NDU2WjA1MQswCQYDVQQGEwJNVzESMBAGA1UECgwJ
+U2NobGV1ZGVyMRIwEAYDVQQLDAlzY2hsZXVkZXIwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCkEknthf/N325Yo2Vi+7JAXyewBmgAN+fQapB8KAba8BrN
+8TIKi7otJ4GA2TNyYtSlXnEUslXu9YHSevDgrJpnzE0pnOmbDA21yyAZk1kG4NMO
+fnxw0pjyLIcOF+Do2PCI6K5ipKUZParGiBcdaJpJhRkePw5ILpGu5B3VwgLlQ+H/
+vl0dC2+dLrEIVrTs5aUw5EFx/CS5i0KnYDo0K/jvDeFN2xa2tlfVOcQ9ZtVwe+Pl
+Rl2Kue/RcyIwfE/0M9ijys9xC7lrfmJdeBZ0i0sRoz/eTH5BAUwAGQeMAX4C58Yz
+qHg9WxIPUsFCvHGqIqg8IWjvfX8s2u6HEpk5HWcrAgMBAAGjgZIwgY8wDwYDVR0T
+AQH/BAUwAwEB/zAdBgNVHQ4EFgQUB/5P+h1owyWXwRKYnnsFrUyyYEswXQYDVR0j
+BFYwVIAUB/5P+h1owyWXwRKYnnsFrUyyYEuhOaQ3MDUxCzAJBgNVBAYTAk1XMRIw
+EAYDVQQKDAlTY2hsZXVkZXIxEjAQBgNVBAsMCXNjaGxldWRlcoIBADANBgkqhkiG
+9w0BAQsFAAOCAQEAHRz4AmJWrOe9ieIh8I+qAqUsXtX9Hgh/2RvlNTNs/NvSHb8d
+xgrncM2ULzWeq+y7Egj61+85x2xXSplgruUFW04Rql01fzBAFafJqk2SvHEzdLIx
+1mzHth/d7mFFMg0HYT4iiwhiklH3jrDJHT7EUunRQQEhgunWdXw57BiAppG2bW0U
+J9LHcGMsxjR6JxbgwQoCZRUBwiGAKd5P4dLc7sELx+wdjU+ChHNf2c7kzVi//Oa2
+Ky7jgEuQ0TWmCzAKtx8L1WDu8x0I1QoGrUV0mLVkoCt8nPjDhbxM4hQq/QgkNZ/C
+wK+nz/RKC99U6r8eaofY2MJA8lr1+69JfzkelQ==
+-----END CERTIFICATE-----
diff --git a/spec/schleuder-private-key.pem b/spec/schleuder-private-key.pem
new file mode 100644
index 0000000..80f25b4
--- /dev/null
+++ b/spec/schleuder-private-key.pem
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEApBJJ7YX/zd9uWKNlYvuyQF8nsAZoADfn0GqQfCgG2vAazfEy
+Cou6LSeBgNkzcmLUpV5xFLJV7vWB0nrw4KyaZ8xNKZzpmwwNtcsgGZNZBuDTDn58
+cNKY8iyHDhfg6NjwiOiuYqSlGT2qxogXHWiaSYUZHj8OSC6RruQd1cIC5UPh/75d
+HQtvnS6xCFa07OWlMORBcfwkuYtCp2A6NCv47w3hTdsWtrZX1TnEPWbVcHvj5UZd
+irnv0XMiMHxP9DPYo8rPcQu5a35iXXgWdItLEaM/3kx+QQFMABkHjAF+AufGM6h4
+PVsSD1LBQrxxqiKoPCFo731/LNruhxKZOR1nKwIDAQABAoIBAQCYF5wQYzdOUOCp
+qk5CA7Cpm4ve0RF3oltyCFcHwNMaAZnXbs9El2JumUCjgLUARD17TqDk3qxqZ4uA
+4haJL3ey4OBmwt6KrBHJhBKtornUdnUv6nDQ5WiClmRb3CbRssjHIWsGZjnlvBSj
+FWTYDi94F7nBIBLNNt41kaFWlhK5E0fsPwd5efvNbZpJSwDPZ/iG1dwyZhCGmfu2
+HeziDRDxBNDRvPFdwj2l4Oty55vqLXJuQ4nHIHk+DkPd4RtLn8xLFLdm1iFbFSFd
+k7/0bqpzF3+8o0lXIY3jTKH/Z1C57NAW3enjVADRuqKNZjzIsBKKCF9u/ZkfCLMC
+EvPlyhYBAoGBANdtJ2O4pgBho6r4Pe/iyeSTS8knr6AU+ViQMfti6qHbBH918wCa
+ohj5tlEovZedwaKTB58hNy6U7CT5uVK5L8LKXqBYul97ONWlSumj2J++cAsnPCyx
+p+zYjUm3KI/jUx/vGccOsd1eMGvhYXWBkRU9JG4qL+12W2SgE7Q+XEWxAoGBAML5
+CpYt2JGE27HkQBnyaApQAARKShQhUrsHohApZpyZGkwbIqmmjEaCY1nqk70s1LLg
+nU1vmlnRXNetIhUcj+JtnHKJDqMEvZM/eFxQIXKdsVcD6WQf2u9ZU31TW8uH5G88
+eadEHCRrTe/xvEyHlDlIXUkEJwmJDF+36QZQ/sWbAoGBAMNwA4w0uGUgL5usGoTG
++uKjvt1/Y5WXcZ8nMjEeTD8Ks8nu98ZUgzqlUQHQNDCYrlMPkJqNR8K62IGzDK4/
+01Skw7Q0yuBUqfspOg082AoUexGjRrRFeFMnIwb9Y48mbQNLp9cvPa3XBZbZodE4
++qaKEcLgAxsrhT6E+1tKN+wBAoGAD5SBEREmzjIUsDlyGeCyCajs52rcUpF7H/Dz
+NWFpjrf5Tv2YHoBtkzDWKZhCKArOEGE8kLSLXAQL7Dwsjg1TPh/OMaTcI5C8aWjY
+AGBy28rYIgDxBIw7HYdA0bH4kuIQEgd+HSynJw3gE314s5Dd+lnbAnuvduaZs4hp
+uZR9V2MCgYB8mmk+KEiD2I3hgkBZSk6eKf1NJOrYO4xdwCb42XArERO4PtiQBdP2
+VfsXhPnkkrZSDJ3/vZGw+uiI7u5UD8fl3ATIRMeYYqyiTeFM4ELPToF6hDhtcf/q
+l0fkQB4Je9KT8zqCDkvytvDjTTXGrxDEPTEPL1OymX4SiIRJaRSEQw==
+-----END RSA PRIVATE KEY-----
diff --git a/spec/schleuder.yml b/spec/schleuder.yml
index 992da66..1d2bffc 100644
--- a/spec/schleuder.yml
+++ b/spec/schleuder.yml
@@ -1,9 +1,13 @@
database:
test:
adapter: sqlite3
- database: db/test.sqlite3
-lists_dir: /tmp/schleuder-test/
-listlogs_dir: /tmp/schleuder-test/
+ database: <%= ENV["SCHLEUDER_DB_PATH"] || 'db/test.sqlite3' %>
+lists_dir: <%= ENV["SCHLEUDER_TMP_DIR"] || '/tmp/schleuder-test/' %>
+listlogs_dir: <%= ENV["SCHLEUDER_TMP_DIR"] || '/tmp/schleuder-test/' %>
smtp_settings:
port: 2523
keyserver: hkp://127.0.0.1:9999
+api:
+ tls_cert_file: 'spec/schleuder-certificate.pem'
+ tls_key_file: 'spec/schleuder-private-key.pem'
+ valid_api_keys: 'test_api_key'
diff --git a/spec/schleuder/integration/api_daemon/api_daemon_spec_helper.rb b/spec/schleuder/integration/api_daemon/api_daemon_spec_helper.rb
new file mode 100644
index 0000000..b29fca6
--- /dev/null
+++ b/spec/schleuder/integration/api_daemon/api_daemon_spec_helper.rb
@@ -0,0 +1,17 @@
+ENV['RACK_ENV'] = 'test'
+
+require 'spec_helper'
+require 'rack/test'
+
+require 'schleuder-api-daemon'
+
+module RSpecMixin
+ include Rack::Test::Methods
+ def app() SchleuderApiDaemon end
+end
+
+RSpec.configure { |c| c.include RSpecMixin } # For RSpec 2.x and 3.x
+
+def authorize!
+ basic_authorize 'schleuder', 'test_api_key'
+end
diff --git a/spec/schleuder/integration/api_daemon/authorization_spec.rb b/spec/schleuder/integration/api_daemon/authorization_spec.rb
new file mode 100644
index 0000000..b5d849f
--- /dev/null
+++ b/spec/schleuder/integration/api_daemon/authorization_spec.rb
@@ -0,0 +1,20 @@
+require_relative 'api_daemon_spec_helper'
+
+describe 'authorization via api' do
+ it 'allows un-authorized access to /status.json' do
+ get '/status.json'
+ expect(last_response).to be_ok
+ end
+
+ it 'blocks un-authorized access to other URLs' do
+ get '/lists.json'
+ expect(last_response.status).to be(401)
+ end
+
+ it 'allows authorized access' do
+ authorize!
+ get '/status.json'
+ expect(last_response).to be_ok
+ end
+
+end
diff --git a/spec/schleuder/integration/api_daemon/list_spec.rb b/spec/schleuder/integration/api_daemon/list_spec.rb
new file mode 100644
index 0000000..e71a937
--- /dev/null
+++ b/spec/schleuder/integration/api_daemon/list_spec.rb
@@ -0,0 +1,29 @@
+require_relative 'api_daemon_spec_helper'
+
+describe 'lists via api' do
+
+ before :each do
+ end
+
+ it 'creates a list' do
+ authorize!
+ list = create(:list)
+ parameters = {
+ email: 'new_testlist at example.com',
+ fingerprint: list.fingerprint
+ }
+ expect {
+ post '/lists.json', parameters.to_json
+ expect(last_response.status).to be 200
+ }.to change { List.count }.by 1
+ end
+
+ it 'shows a list' do
+ authorize!
+ list = create(:list)
+ get "lists/#{list.id}.json"
+ expect(last_response.status).to be 200
+ expect(JSON.parse(last_response.body)['email']).to eq list.email
+ end
+
+end
diff --git a/spec/schleuder/integration/api_daemon/subscription_spec.rb b/spec/schleuder/integration/api_daemon/subscription_spec.rb
new file mode 100644
index 0000000..f7ed4b1
--- /dev/null
+++ b/spec/schleuder/integration/api_daemon/subscription_spec.rb
@@ -0,0 +1,68 @@
+require_relative 'api_daemon_spec_helper'
+
+describe 'subscription via api' do
+
+ before :each do
+ @list = List.last || create(:list)
+ @email = create(:subscription).email
+ end
+
+ it 'doesn\'t subscribe new member without authorization' do
+ parameters = {'list_id' => @list.id, :email => @email}
+ expect {
+ post '/subscriptions.json', parameters.to_json
+ expect(last_response.status).to be 401
+ }.to change { Subscription.count }.by 0
+ end
+
+ it 'subscribes new member to a list' do
+ authorize!
+ parameters = {'list_id' => @list.id, :email => @email}
+ expect {
+ post '/subscriptions.json', parameters.to_json
+ expect(last_response.status).to be 201
+ }.to change { Subscription.count }.by 1
+ expect(Subscription.where(:email => @email).first.admin?).to be false
+ expect(Subscription.where(:email => @email).first.delivery_enabled).to be true
+ end
+
+ it 'subscribes an admin user' do
+ authorize!
+ parameters = {'list_id' => @list.id, :email => @email, :admin => true}
+ expect {
+ post '/subscriptions.json', parameters.to_json
+ expect(last_response.status).to be 201
+ }.to change { Subscription.count }.by 1
+ expect(Subscription.where(:email => @email).first.admin?).to be true
+ end
+
+ it 'subscribes an admin user with a truthy value' do
+ authorize!
+ parameters = {'list_id' => @list.id, :email => @email, :admin => '1'}
+ expect {
+ post '/subscriptions.json', parameters.to_json
+ expect(last_response.status).to be 201
+ }.to change { Subscription.count }.by 1
+ expect(Subscription.where(:email => @email).first.admin?).to be true
+ end
+
+ it 'subscribes an user and unsets delivery flag' do
+ authorize!
+ parameters = {'list_id' => @list.id, :email => @email, :delivery_enabled => false}
+ expect {
+ post '/subscriptions.json', parameters.to_json
+ expect(last_response.status).to be 201
+ }.to change { Subscription.count }.by 1
+ expect(Subscription.where(:email => @email).first.delivery_enabled).to be false
+ end
+
+ it 'unsubscribes members' do
+ authorize!
+ subscription = create(:subscription, :list_id => @list.id)
+ parameters = {'list_id' => @list.id, :email => @email, :delivery_enabled => false}
+ expect {
+ delete "/subscriptions/#{subscription.id}.json"
+ expect(last_response.status).to be 200
+ }.to change { Subscription.count }.by -1
+ end
+end
diff --git a/spec/schleuder/integration/cli_spec.rb b/spec/schleuder/integration/cli_spec.rb
index 65a771c..aa43648 100644
--- a/spec/schleuder/integration/cli_spec.rb
+++ b/spec/schleuder/integration/cli_spec.rb
@@ -73,6 +73,7 @@ describe 'cli' do
expect(list.subject_prefix_out).to eq '[out]'
expect(list.max_message_size_kb).to eq 10240
expect(list.public_footer).to eq "-- \nfooter"
+ expect(list.internal_footer).to be_nil
end
it "imports the subscriptions" do
@@ -146,7 +147,7 @@ describe 'cli' do
mail = Mail::TestMailer.deliveries.first
expect(Mail::TestMailer.deliveries.length).to eq 1
- expect(mail.first_plaintext_part.body.to_s).to eql("Refreshing all keys from the keyring of list #{list.email} resulted in this:\n\nKey 98769E8A1091F36BD88403ECF71A3F8412D83889 was updated (new signatures).\nKey 6EE51D78FD0B33DE65CCF69D2104E20E20889F66 was updated (new user-IDs, new signatures).")
+ expect(mail.first_plaintext_part.body.to_s).to match(/Refreshing all keys from the keyring of list #{list.email} resulted in this:\n\nThis key was updated \(new signatures\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]\n\nThis key was updated \(new user-IDs and new signatures\):\n0x6EE51D78FD0B33DE65CCF69D2104E20E20889F66 new at example.org \d{4}-\d{2}-\d{2}\n/)
teardown_list_and_mailer(list)
end
@@ -165,7 +166,7 @@ describe 'cli' do
mail = Mail::TestMailer.deliveries.first
expect(Mail::TestMailer.deliveries.length).to eq 1
- expect(mail.first_plaintext_part.body.to_s).to eql("Refreshing all keys from the keyring of list #{list1.email} resulted in this:\n\nKey 98769E8A1091F36BD88403ECF71A3F8412D83889 was updated (new signatures).\nKey 6EE51D78FD0B33DE65CCF69D2104E20E20889F66 was updated (new user-IDs, new signatures).")
+ expect(mail.first_plaintext_part.body.to_s).to match(/Refreshing all keys from the keyring of list #{list1.email} resulted in this:\n\nThis key was updated \(new signatures\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]\n\nThis key was updated \(new user-IDs and new signatures\):\n0x6EE51D78FD0B33DE65CCF69D2104E20E20889F66 new at example.org \d{4}-\d{2}-\d{2}\n/)
teardown_list_and_mailer(list1)
teardown_list_and_mailer(list2)
diff --git a/spec/schleuder/integration/keywords_spec.rb b/spec/schleuder/integration/keywords_spec.rb
index 8c0c739..bbb30cf 100644
--- a/spec/schleuder/integration/keywords_spec.rb
+++ b/spec/schleuder/integration/keywords_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require "spec_helper"
describe "user sends keyword" do
@@ -15,7 +16,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-SUBSCRIBE: test at example.org"
+ mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test at example.org"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -58,7 +59,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-SUBSCRIBE: test at example.org 0x#{list.fingerprint} true false"
+ mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test at example.org 0x#{list.fingerprint} true false"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -86,6 +87,91 @@ describe "user sends keyword" do
teardown_list_and_mailer(list)
end
+ it "x-subscribe with one attribute and spaces-separated fingerprint" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test at example.org 0x#{list.fingerprint.dup.insert(4, ' ')} true"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
+ subscription = list.subscriptions.where(email: 'test at example.org').first
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("test at example.org has been subscribed")
+ expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint.downcase}/)
+ expect(message.to_s).to include("Admin? true")
+ expect(message.to_s).to include("Email-delivery enabled? true")
+
+ expect(subscription).to be_present
+ expect(subscription.fingerprint).to eql(list.fingerprint.downcase)
+ expect(subscription.admin).to eql(true)
+ expect(subscription.delivery_enabled).to eql(true)
+
+ teardown_list_and_mailer(list)
+ end
+
+
+ it "x-subscribe without attributes, but with spaces-separated fingerprint" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test at example.org 0x#{list.fingerprint.dup.insert(4, ' ')}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
+ subscription = list.subscriptions.where(email: 'test at example.org').first
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("test at example.org has been subscribed")
+ expect(message.to_s).to match(/Fingerprint:\s+#{list.fingerprint.downcase}/)
+ expect(message.to_s).to include("Admin? false")
+ expect(message.to_s).to include("Email-delivery enabled? true")
+
+ expect(subscription).to be_present
+ expect(subscription.fingerprint).to eql(list.fingerprint.downcase)
+ expect(subscription.admin).to eql(false)
+ expect(subscription.delivery_enabled).to eql(true)
+
+ teardown_list_and_mailer(list)
+ end
+
it "x-subscribe with attributes and spaces-separated fingerprint" do
list = create(:list)
list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
@@ -100,7 +186,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-SUBSCRIBE: test at example.org 0x#{list.fingerprint.dup.insert(4, ' ')} true false"
+ mail.body = "x-list-name: #{list.email}\nX-SUBSCRIBE: test at example.org 0x#{list.fingerprint.dup.insert(4, ' ')} true false"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -144,7 +230,7 @@ describe "user sends keyword" do
sign_as: '59C71FB38AEE22E091C78259D06350440F759BD3'
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-UNSUBSCRIBE:"
+ mail.body = "x-list-name: #{list.email}\nX-UNSUBSCRIBE:"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -180,7 +266,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-UNSUBSCRIBE: test at example.org"
+ mail.body = "x-list-name: #{list.email}\nX-UNSUBSCRIBE: test at example.org"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -214,7 +300,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-UNSUBSCRIBE: test at example.org"
+ mail.body = "x-list-name: #{list.email}\nX-UNSUBSCRIBE: test at example.org"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -251,7 +337,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-set-fingerprint: schleuder at example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
+ mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: schleuder at example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -290,7 +376,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-set-fingerprint: schleuder at example.org C4D6 0F88 3378 9C7C AA44 496F D3FF A661 3AB1 0ECE"
+ mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: schleuder at example.org C4D6 0F88 3378 9C7C AA44 496F D3FF A661 3AB1 0ECE"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -329,7 +415,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-set-fingerprint: C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
+ mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -368,7 +454,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-set-fingerprint: test at example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
+ mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: test at example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -407,7 +493,7 @@ describe "user sends keyword" do
sign_as: '59C71FB38AEE22E091C78259D06350440F759BD3'
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-set-fingerprint: test at example.org 59C71FB38AEE22E091C78259D06350440F759BD3"
+ mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: test at example.org 59C71FB38AEE22E091C78259D06350440F759BD3"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -444,7 +530,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-set-fingerprint: blabla"
+ mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: blabla"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -480,7 +566,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-set-fingerprint: bla at example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
+ mail.body = "x-list-name: #{list.email}\nX-set-fingerprint: bla at example.org C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -513,7 +599,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-list-subscriptions:"
+ mail.body = "x-list-name: #{list.email}\nX-list-subscriptions:"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -548,7 +634,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-list-subscriptions:"
+ mail.body = "x-list-name: #{list.email}\nX-list-subscriptions:"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -588,7 +674,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-list-subscriptions: example.org"
+ mail.body = "x-list-name: #{list.email}\nX-list-subscriptions: example.org"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -622,7 +708,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-list-subscriptions: blabla"
+ mail.body = "x-list-name: #{list.email}\nX-list-subscriptions: blabla"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -657,7 +743,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
keymaterial = File.read('spec/fixtures/example_key.txt')
- mail.body = "x-listname: #{list.email}\nX-ADD-KEY:\n#{keymaterial}"
+ mail.body = "x-list-name: #{list.email}\nX-ADD-KEY:\n#{keymaterial}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -690,7 +776,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-ADD-KEY:"
+ mail.body = "x-list-name: #{list.email}\nX-ADD-KEY:"
mail.add_file('spec/fixtures/example_key.txt')
mail.deliver
@@ -724,7 +810,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: lala!"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: lala!"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -756,7 +842,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: admin at example.org"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: admin at example.org"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -771,7 +857,7 @@ describe "user sends keyword" do
raw = Mail::TestMailer.deliveries.first
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
- expect(message.to_s).to include("Key 98769E8A1091F36BD88403ECF71A3F8412D83889 was fetched (new key)")
+ expect(message.first_plaintext_part.body.to_s).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]\n/)
teardown_list_and_mailer(list)
end
@@ -790,7 +876,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: something at localhost"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: something at localhost"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -824,7 +910,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: http://127.0.0.1:9999/keys/example.asc"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: http://127.0.0.1:9999/keys/example.asc"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -839,7 +925,7 @@ describe "user sends keyword" do
raw = Mail::TestMailer.deliveries.first
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
- expect(message.to_s).to include("Key 98769E8A1091F36BD88403ECF71A3F8412D83889 was fetched (new key)")
+ expect(message.first_plaintext_part.body.to_s).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]\n/)
teardown_list_and_mailer(list)
end
@@ -859,7 +945,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
url = "http://127.0.0.1:9999/foo"
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: #{url}"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: #{url}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -893,7 +979,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: 0x0000000000000000000000000000000000000000"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: 0x0000000000000000000000000000000000000000"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -927,7 +1013,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: 0x98769E8A1091F36BD88403ECF71A3F8412D83889"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: 0x98769E8A1091F36BD88403ECF71A3F8412D83889"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -942,7 +1028,7 @@ describe "user sends keyword" do
raw = Mail::TestMailer.deliveries.first
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
- expect(message.to_s).to include("Key 98769E8A1091F36BD88403ECF71A3F8412D83889 was fetched (new key)")
+ expect(message.first_plaintext_part.body.to_s).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]\n/)
teardown_list_and_mailer(list)
end
@@ -961,7 +1047,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-fetch-KEY: 0x59C71FB38AEE22E091C78259D06350440F759BD3"
+ mail.body = "x-list-name: #{list.email}\nX-fetch-KEY: 0x59C71FB38AEE22E091C78259D06350440F759BD3"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -976,7 +1062,7 @@ describe "user sends keyword" do
raw = Mail::TestMailer.deliveries.first
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
- expect(message.first_plaintext_part.body.to_s).to eql("Key 59C71FB38AEE22E091C78259D06350440F759BD3 was fetched (unchanged).")
+ expect(message.first_plaintext_part.body.to_s).to match(/This key was fetched \(unchanged\):\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder at example.org \d{4}-\d{2}-\d{2}/)
teardown_list_and_mailer(list)
end
@@ -996,7 +1082,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
content_body = "Hello again!\n"
- mail.body = "x-listname: #{list.email}\nX-resend: someone at example.org\n#{content_body}"
+ mail.body = "x-list-name: #{list.email}\nX-resend: someone at example.org\n#{content_body}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1021,6 +1107,128 @@ describe "user sends keyword" do
teardown_list_and_mailer(list)
end
+ it "x-resend does not include internal_footer" do
+ list = create(
+ :list,
+ internal_footer: "-- \nsomething private",
+ public_footer: "-- \nsomething public"
+ )
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "Hello again!\n"
+ mail.body = "x-list-name: #{list.email}\nX-resend: someone at example.org\n#{content_body}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ resent_message = raw.verify
+ resent_message_body = resent_message.parts.map { |p| p.body.to_s }.join
+
+ expect(resent_message_body).not_to include(list.internal_footer)
+ expect(resent_message_body).to eql(content_body + list.public_footer.to_s)
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-resend with iso-8859-1 body" do
+ list = create(:list, public_footer: "-- \nblablabla")
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "Hello again! ¡Hola!\n"
+ mail.charset = 'iso-8859-1'
+ mail.body = "x-list-name: #{list.email}\nX-resend: someone at example.org\n#{content_body}".encode('iso-8859-1')
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ resent_message = raw.verify
+ raw = Mail::TestMailer.deliveries.last
+ message = Mail.create_message_to_list(raw.to_s, list.email, list).setup
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("Resent: Unencrypted to someone at example.org")
+ expect(message.parts[1].body.to_s.force_encoding(message.parts[1].charset)).to eql(content_body.encode(message.parts[1].charset))
+ expect(resent_message.to).to include("someone at example.org")
+ expect(resent_message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
+ expect(resent_message.parts[0].body.to_s).to eql(content_body.encode(resent_message.parts[0].charset))
+ expect(resent_message.parts[1].body.to_s).to eql(list.public_footer.to_s)
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-resend with utf-8 body and umlauts" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "This is a test\nAnd here are some umlauts:ÄäÖöÜüß"
+ mail.charset = 'utf-8'
+ mail.body = "x-list-name: #{list.email}\nX-resend: someone at example.org\n#{content_body}".encode('utf-8')
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ resent_message = raw.verify
+ raw = Mail::TestMailer.deliveries.last
+ message = Mail.create_message_to_list(raw.to_s, list.email, list).setup
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.to_s).to include("Resent: Unencrypted to someone at example.org")
+ expect(message.parts[1].body.to_s.force_encoding(message.parts[1].charset)).to eql(content_body.encode(message.parts[1].charset))
+ expect(resent_message.to).to include("someone at example.org")
+ expect(resent_message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
+ expect(resent_message.parts[0].body.to_s).to eql(content_body.encode(resent_message.parts[0].charset))
+
+ teardown_list_and_mailer(list)
+ end
+
it "x-resend with admin-notification" do
list = create(:list, keywords_admin_notify: ['resend'])
list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
@@ -1036,7 +1244,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
content_body = "Hello again!\n"
- mail.body = "x-listname: #{list.email}\nX-resend: someone at example.org\n#{content_body}"
+ mail.body = "x-list-name: #{list.email}\nX-resend: someone at example.org\n#{content_body}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1062,7 +1270,7 @@ describe "user sends keyword" do
teardown_list_and_mailer(list)
end
- it "x-resend without x-listname" do
+ it "x-resend without x-list-name" do
list = create(:list)
list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
ENV['GNUPGHOME'] = list.listdir
@@ -1092,7 +1300,7 @@ describe "user sends keyword" do
expect(message.to).to eql(['schleuder at example.org'])
expect(message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
- expect(message.to_s).to include("Your message didn't contain the mandatory X-LISTNAME-keyword, thus it was rejected.")
+ expect(message.to_s).to include("Your message didn't contain the mandatory X-LIST-NAME-keyword, thus it was rejected.")
teardown_list_and_mailer(list)
end
@@ -1114,7 +1322,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
content_body = "Hello again!\n"
- mail.body = "x-listname: #{list.email}\nX-resend-encrypted-only: bla at foo\n#{content_body}"
+ mail.body = "x-list-name: #{list.email}\nX-resend-encrypted-only: bla at foo\n#{content_body}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1136,6 +1344,45 @@ describe "user sends keyword" do
teardown_list_and_mailer(list)
end
+ it "x-resend-unencrypted with matching key" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ list.import_key(File.read("spec/fixtures/bla_foo_key.txt"))
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "Hello again!\n"
+ mail.body = "x-list-name: #{list.email}\nX-resend-unencrypted: bla at foo\n#{content_body}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ resent_message = Mail::TestMailer.deliveries.first
+ raw = Mail::TestMailer.deliveries.last
+ message = Mail.create_message_to_list(raw.to_s, list.email, list).setup
+
+ expect(list.keys('bla at foo').size).to eql(1)
+ expect(resent_message.to).to eql(['bla at foo'])
+ expect(resent_message.content_type).to_not match(/^multipart\/encrypted.*application\/pgp-encrypted/)
+ expect(resent_message.first_plaintext_part.body.to_s).to include('Hello again!')
+ expect(message.first_plaintext_part.body.to_s).to include("Resent: Unencrypted to bla at foo")
+
+ teardown_list_and_mailer(list)
+ end
+
it "x-resend with expired key" do
list = create(:list)
list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
@@ -1152,7 +1399,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
content_body = "Hello again!\n"
- mail.body = "x-listname: #{list.email}\nX-resend-encrypted-only: bla at foo\n#{content_body}"
+ mail.body = "x-list-name: #{list.email}\nX-resend-encrypted-only: bla at foo\n#{content_body}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1171,7 +1418,7 @@ describe "user sends keyword" do
teardown_list_and_mailer(list)
end
- it "x-resend with wrong x-listname" do
+ it "x-resend with wrong x-list-name" do
list = create(:list)
list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
ENV['GNUPGHOME'] = list.listdir
@@ -1186,7 +1433,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
content_body = "Hello again!\n"
- mail.body = "x-listname: somethingelse at example.org\nX-resend: someone at example.org\n#{content_body}"
+ mail.body = "x-list-name: somethingelse at example.org\nX-resend: someone at example.org\n#{content_body}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1201,7 +1448,44 @@ describe "user sends keyword" do
expect(message.to).to eql(['schleuder at example.org'])
expect(message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
- expect(message.to_s).to include("Your message contained a wrong X-LISTNAME-keyword. The value of that keyword must match the email address of this list.")
+ expect(message.to_s).to include("Your message contained a wrong X-LIST-NAME-keyword. The value of that keyword must match the email address of this list.")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-resend with invalid recipient" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.email => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ content_body = "Hello again!\n"
+ invalid_recipient = '`ls`bla'
+ mail.body = "x-list-name: #{list.email}\nX-resend: #{invalid_recipient}\n#{content_body}"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.email)
+ rescue SystemExit
+ end
+ delivered_emails = Mail::TestMailer.deliveries
+ raw = delivered_emails.first
+ message = Mail.create_message_to_list(raw.to_s, list.email, list).setup
+
+ expect(delivered_emails.size).to eql(1)
+ expect(message.to_s).not_to include("Resent: Unencrypted to someone at example.org")
+ expect(message.to_s).to include("Error: Invalid email-address for resending: #{invalid_recipient}")
teardown_list_and_mailer(list)
end
@@ -1221,7 +1505,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
signed_text = "signed\nsigned\nsigned\n\n"
- mail.body = "x-listname: #{list.email}\nx-sign-this:\n#{signed_text}"
+ mail.body = "x-list-name: #{list.email}\nx-sign-this:\n#{signed_text}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1254,7 +1538,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
keywords = Mail::Part.new
- keywords.body = "\n\nx-listname: #{list.email}\nx-sign-this:"
+ keywords.body = "\n\nx-list-name: #{list.email}\nx-sign-this:"
mail.parts << keywords
signed_content = File.read('spec/fixtures/example_key.txt')
mail.attachments['example_key.txt'] = { mime_type: 'application/pgp-key',
@@ -1301,7 +1585,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-list-KEYs: der at ex"
+ mail.body = "x-list-name: #{list.email}\nX-list-KEYs: der at ex"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1315,7 +1599,7 @@ describe "user sends keyword" do
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(message.to).to eql(['schleuder at example.org'])
- expect(message.to_s).to include("pub 4096R/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06")
+ expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 \d{4}-\d{2}-\d{2}/)
expect(message.to_s.scan(/^pub /).size).to eql(1)
teardown_list_and_mailer(list)
@@ -1335,7 +1619,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-list-KEYs: @schleuder"
+ mail.body = "x-list-name: #{list.email}\nX-list-KEYs: @schleuder"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1349,7 +1633,7 @@ describe "user sends keyword" do
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(message.to).to eql(['schleuder at example.org'])
- expect(message.to_s).to include("pub 4096R/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06")
+ expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 \d{4}-\d{2}-\d{2}/)
expect(message.to_s.scan(/^pub /).size).to eql(1)
teardown_list_and_mailer(list)
@@ -1369,7 +1653,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-list-KEYs: 0x59C71FB38AEE22E091C78259D06350440F759BD3"
+ mail.body = "x-list-name: #{list.email}\nX-list-KEYs: 0x59C71FB38AEE22E091C78259D06350440F759BD3"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1383,7 +1667,7 @@ describe "user sends keyword" do
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(message.to).to eql(['schleuder at example.org'])
- expect(message.to_s).to include("pub 4096R/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06")
+ expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 \d{4}-\d{2}-\d{2}/)
expect(message.to_s.scan(/^pub /).size).to eql(1)
teardown_list_and_mailer(list)
@@ -1404,7 +1688,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-GET-KEY: 0x59C71FB38AEE22E091C78259D06350440F759BD3"
+ mail.body = "x-list-name: #{list.email}\nX-GET-KEY: 0x59C71FB38AEE22E091C78259D06350440F759BD3"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1418,7 +1702,7 @@ describe "user sends keyword" do
message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
expect(message.to).to eql(['schleuder at example.org'])
- expect(message.to_s).to include("pub 4096R/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06")
+ expect(message.to_s).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 \d{4}-\d{2}-\d{2}/)
expect(message.to_s).to include("-----BEGIN PGP PUBLIC KEY")
teardown_list_and_mailer(list)
@@ -1438,7 +1722,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-get-KEY: blabla"
+ mail.body = "x-list-name: #{list.email}\nX-get-KEY: blabla"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1472,7 +1756,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-get-KEY:"
+ mail.body = "x-list-name: #{list.email}\nX-get-KEY:"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1507,7 +1791,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-delete-KEY: C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
+ mail.body = "x-list-name: #{list.email}\nX-delete-KEY: C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1541,7 +1825,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-delete-KEY: lala"
+ mail.body = "x-list-name: #{list.email}\nX-delete-KEY: lala"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1576,7 +1860,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-delete-KEY: schleuder"
+ mail.body = "x-list-name: #{list.email}\nX-delete-KEY: schleuder"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1611,7 +1895,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-get-logfile"
+ mail.body = "x-list-name: #{list.email}\nX-get-logfile"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1647,7 +1931,7 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
- mail.body = "x-listname: #{list.email}\nX-get-logfile"
+ mail.body = "x-list-name: #{list.email}\nX-get-logfile"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1682,7 +1966,7 @@ describe "user sends keyword" do
}
mail.gpg(gpg_opts)
content_body = "something something list-key"
- mail.body = "x-listname: #{list.email}\nX-attach-listkey\n#{content_body}"
+ mail.body = "x-list-name: #{list.email}\nX-attach-listkey\n#{content_body}"
mail.deliver
encrypted_mail = Mail::TestMailer.deliveries.first
@@ -1699,7 +1983,7 @@ describe "user sends keyword" do
expect(message.parts.last.parts.length).to eql(2)
expect(message.parts.last.parts.first.body.to_s).to eql(content_body)
expect(message.parts.last.parts.last.content_type.to_s).to eql("application/pgp-keys")
- expect(message.parts.last.parts.last.body.decoded).to include("pub 4096R/59C71FB38AEE22E091C78259D06350440F759BD3 2016-12-06")
+ expect(message.parts.last.parts.last.body.decoded).to match(/pub 4096R\/59C71FB38AEE22E091C78259D06350440F759BD3 \d{4}-\d{2}-\d{2}/)
expect(message.parts.last.parts.last.body.decoded).to include("-----BEGIN PGP PUBLIC KEY BLOCK-----")
expect(message.parts.last.parts.last.body.decoded).to include("mQINBFhGvz0BEADXbbTWo/PStyTznAo/f1UobY0EiVPNKNERvYua2Pnq8BwOQ5bS")
@@ -1720,6 +2004,40 @@ describe "user sends keyword" do
sign_as: list.admins.first.fingerprint
}
mail.gpg(gpg_opts)
+ mail.body = "x-list-name: #{list.email}\nX-get-version"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
+
+ expect(message.to).to eql(['schleuder at example.org'])
+ expect(message.first_plaintext_part.body.to_s.lines.size).to eql(1)
+ expect(message.first_plaintext_part.body.to_s).to eql(Schleuder::VERSION)
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-get-version with depreciated x-listname keyword" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
mail.body = "x-listname: #{list.email}\nX-get-version"
mail.deliver
@@ -1739,5 +2057,116 @@ describe "user sends keyword" do
teardown_list_and_mailer(list)
end
+
+ it "x-list-keys without arguments" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ list.import_key(File.read('spec/fixtures/example_key.txt'))
+ list.import_key(File.read('spec/fixtures/bla_foo_key.txt'))
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-list-name: #{list.email}\nX-list-keys"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
+
+ expect(message.first_plaintext_part.body.to_s.lines.size).to eql(16)
+ expect(message.first_plaintext_part.body.to_s).to include("59C71FB38AEE22E091C78259D06350440F759BD3")
+ expect(message.first_plaintext_part.body.to_s).to include("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE")
+ expect(message.first_plaintext_part.body.to_s).to include("87E65ED2081AE3D16BE4F0A5EBDBE899251F2412")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-list-keys with one argument" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ list.import_key(File.read('spec/fixtures/example_key.txt'))
+ list.import_key(File.read('spec/fixtures/bla_foo_key.txt'))
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-list-name: #{list.email}\nX-list-keys schleuder2"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
+
+ expect(message.first_plaintext_part.body.to_s.lines.size).to eql(4)
+ expect(message.first_plaintext_part.body.to_s).not_to include("59C71FB38AEE22E091C78259D06350440F759BD3")
+ expect(message.first_plaintext_part.body.to_s).to include("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE")
+ expect(message.first_plaintext_part.body.to_s).not_to include("87E65ED2081AE3D16BE4F0A5EBDBE899251F2412")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "x-list-keys with two arguments" do
+ list = create(:list)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ list.import_key(File.read('spec/fixtures/example_key.txt'))
+ list.import_key(File.read('spec/fixtures/bla_foo_key.txt'))
+ ENV['GNUPGHOME'] = list.listdir
+ mail = Mail.new
+ mail.to = list.request_address
+ mail.from = list.admins.first.email
+ gpg_opts = {
+ encrypt: true,
+ keys: {list.request_address => list.fingerprint},
+ sign: true,
+ sign_as: list.admins.first.fingerprint
+ }
+ mail.gpg(gpg_opts)
+ mail.body = "x-list-name: #{list.email}\nX-list-keys schleuder2 bla"
+ mail.deliver
+
+ encrypted_mail = Mail::TestMailer.deliveries.first
+ Mail::TestMailer.deliveries.clear
+
+ begin
+ Schleuder::Runner.new().run(encrypted_mail.to_s, list.request_address)
+ rescue SystemExit
+ end
+ raw = Mail::TestMailer.deliveries.first
+ message = Mail.create_message_to_list(raw.to_s, list.request_address, list).setup
+
+ expect(message.first_plaintext_part.body.to_s.lines.size).to eql(10)
+ expect(message.first_plaintext_part.body.to_s).not_to include("59C71FB38AEE22E091C78259D06350440F759BD3")
+ expect(message.first_plaintext_part.body.to_s).to include("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE")
+ expect(message.first_plaintext_part.body.to_s).to include("87E65ED2081AE3D16BE4F0A5EBDBE899251F2412")
+
+ teardown_list_and_mailer(list)
+ end
end
diff --git a/spec/schleuder/runner_spec.rb b/spec/schleuder/runner_spec.rb
index 63c92c3..9f8fee6 100644
--- a/spec/schleuder/runner_spec.rb
+++ b/spec/schleuder/runner_spec.rb
@@ -126,6 +126,42 @@ describe Schleuder::Runner do
teardown_list_and_mailer(list)
end
+
+ it "includes the internal_footer" do
+ list = create(
+ :list,
+ send_encrypted_only: false,
+ internal_footer: "-- \nfor our eyes only!"
+ )
+ list.subscribe("admin at example.org", nil, true)
+ mail = File.read("spec/fixtures/mails/plain/thunderbird.eml")
+
+ Schleuder::Runner.new().run(mail, list.email)
+ message = Mail::TestMailer.deliveries.first
+
+ expect(message.parts.first.parts.last.body.to_s).to eql(list.internal_footer)
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "does not include the public_footer" do
+ public_footer = "-- \nsomething public blabla"
+ list = create(
+ :list,
+ send_encrypted_only: false,
+ internal_footer: "-- \nfor our eyes only!",
+ public_footer: public_footer
+ )
+ list.subscribe("admin at example.org", nil, true)
+ mail = File.read("spec/fixtures/mails/plain/thunderbird.eml")
+
+ Schleuder::Runner.new().run(mail, list.email)
+ message = Mail::TestMailer.deliveries.first
+
+ expect(message.parts.first.to_s).not_to include(list.public_footer)
+
+ teardown_list_and_mailer(list)
+ end
end
it "delivers a signed error message if a subscription's key is expired on a encrypted-only list" do
@@ -205,6 +241,53 @@ describe Schleuder::Runner do
teardown_list_and_mailer(list)
end
+ context "Quoted-Printable encoding" do
+ it "is handled properly in cleartext emails" do
+ list = create(:list, send_encrypted_only: false)
+ list.subscribe("admin at example.org", nil, true)
+ mail = File.read('spec/fixtures/mails/qp-encoding-clear.eml')
+
+ Schleuder::Runner.new().run(mail, list.email)
+ message = Mail::TestMailer.deliveries.first
+ content_part = message.parts.first
+
+ expect(content_part.parts.last.content_transfer_encoding).to eql('quoted-printable')
+ expect(content_part.parts.last.body.encoded).to include('=3D86')
+ expect(content_part.parts.last.body.encoded).not_to include('=86')
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "is handled properly in encrypted+signed emails" do
+ list = create(:list, send_encrypted_only: false)
+ list.subscribe("admin at example.org", "59C71FB38AEE22E091C78259D06350440F759BD3", true)
+ mail = File.read('spec/fixtures/mails/qp-encoding-encrypted+signed.eml')
+
+ Schleuder::Runner.new().run(mail, list.email)
+ raw = Mail::TestMailer.deliveries.first
+ message = Mail.create_message_to_list(raw.to_s, list.email, list).setup
+ content_part = message.parts.last.first_plaintext_part
+
+ expect(content_part.decoded).to include('bug=86')
+ teardown_list_and_mailer(list)
+ end
+
+ it "is handled properly in encrypted emails" do
+ list = create(:list, send_encrypted_only: false)
+ list.subscribe("admin at example.org", nil, true)
+ mail = File.read('spec/fixtures/mails/qp-encoding-encrypted.eml')
+
+ Schleuder::Runner.new().run(mail, list.email)
+ message = Mail::TestMailer.deliveries.first
+ content_part = message.parts.first
+
+ expect(content_part.parts.last.content_transfer_encoding).to eql('quoted-printable')
+ expect(content_part.parts.last.body.encoded).to include('=3D86')
+ expect(content_part.parts.last.body.encoded).not_to include('=86')
+
+ teardown_list_and_mailer(list)
+ end
+ end
end
end
diff --git a/spec/schleuder/unit/conf_spec.rb b/spec/schleuder/unit/conf_spec.rb
new file mode 100644
index 0000000..d032767
--- /dev/null
+++ b/spec/schleuder/unit/conf_spec.rb
@@ -0,0 +1,38 @@
+require "spec_helper"
+
+describe Schleuder::Conf do
+ it "reads ERB code in config files" do
+ # Suppress warnings about already defined constants
+ # if using "load" further below
+ verbose_orig = $VERBOSE
+ $VERBOSE = nil
+
+ # Define constants
+ val_old = "val_old"
+
+ # Check if env var is set
+ if not ENV["SCHLEUDER_DB_PATH"].nil?
+ val_old = ENV["SCHLEUDER_DB_PATH"]
+ end
+
+ # Set env var, reload the config and check whether the correct value
+ # is returned
+ val_test = "SCHLEUDER_ERB_TEST"
+ ENV["SCHLEUDER_DB_PATH"] = val_test
+ load "schleuder/conf.rb"
+ expect(Schleuder::Conf.database["database"]).to eql(val_test)
+
+ # Reset the env var
+ ENV["SCHLEUDER_DB_PATH"] = nil
+
+ # Set the env var to the original value
+ if val_old != "val_old"
+ ENV["SCHLEUDER_DB_PATH"] = val_old
+ end
+
+ load "schleuder/conf.rb"
+
+ # Set verbose level to original value
+ $VERBOSE = $verbose_orig
+ end
+end
diff --git a/spec/schleuder/unit/filters_spec.rb b/spec/schleuder/unit/filters_spec.rb
index 2eef9b9..4d40e09 100644
--- a/spec/schleuder/unit/filters_spec.rb
+++ b/spec/schleuder/unit/filters_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
describe Schleuder::Filters do
- context '.fix_hostmail_messages!' do
+ context '.fix_hotmail_messages!' do
it "fixes pgp/mime-messages that were mangled by hotmail" do
message = Mail.read("spec/fixtures/mails/hotmail.eml")
Schleuder::Filters.fix_hotmail_messages!(nil, message)
diff --git a/spec/schleuder/unit/gpgme_ctx.rb b/spec/schleuder/unit/gpgme_ctx.rb
index 24a14d0..06648f4 100644
--- a/spec/schleuder/unit/gpgme_ctx.rb
+++ b/spec/schleuder/unit/gpgme_ctx.rb
@@ -168,4 +168,26 @@ describe GPGME::Ctx do
expect(out.class).to eql(NilClass)
expect(exitcode.class).to eql(Integer)
end
+
+ context "#keyserver_arg" do
+ it "returns keyserver-args if a keyserver is configured" do
+ list = create(:list)
+
+ keyserver_args = list.gpg.send(:keyserver_arg)
+
+ expect(keyserver_args).to eql("--keyserver #{Conf.keyserver}")
+ end
+
+ it "returns a blank string if the keyserver-option is set to a blank value" do
+ oldval = Conf.instance.config['keyserver']
+ Conf.instance.config['keyserver'] = ''
+ list = create(:list)
+
+ keyserver_args = list.gpg.send(:keyserver_arg)
+
+ expect(keyserver_args).to eql("")
+
+ Conf.instance.config['keyserver'] = oldval
+ end
+ end
end
diff --git a/spec/schleuder/unit/gpgme_key.rb b/spec/schleuder/unit/gpgme_key.rb
new file mode 100644
index 0000000..245c132
--- /dev/null
+++ b/spec/schleuder/unit/gpgme_key.rb
@@ -0,0 +1,50 @@
+require "spec_helper"
+
+describe GPGME::Key do
+ describe "#oneline" do
+ it "displays the expected basic attributes" do
+ list = create(:list)
+
+ key = list.key
+
+ expect(key.oneline).to match(/0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder at example.org \d{4}-\d{2}-\d{2}/)
+ end
+
+ it "displays the expected attributes for an expiring key" do
+ list = create(:list)
+ list.import_key(File.read("spec/fixtures/expiring_key.txt"))
+
+ key = list.key("421FBF7190640136788593CD9EE9BE5929CACC20")
+
+ expect(key.oneline).to match(/0x421FBF7190640136788593CD9EE9BE5929CACC20 expiringkey at example.org \d{4}-\d{2}-\d{2} \[expires: \d{4}-\d{2}-\d{2}\]/)
+ end
+
+ it "displays the expected attributes for an expired key" do
+ list = create(:list)
+ list.import_key(File.read("spec/fixtures/expired_key.txt"))
+
+ key = list.key("98769E8A1091F36BD88403ECF71A3F8412D83889")
+
+ expect(key.oneline).to match(/0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/)
+ end
+
+ it "displays the expected attributes for a revoked key" do
+ list = create(:list)
+ list.import_key(File.read("spec/fixtures/revoked_key.txt"))
+
+ key = list.key("7E783CDE6D1EFE6D2409739C098AC83A4C0028E9")
+
+ expect(key.oneline).to match(/0x7E783CDE6D1EFE6D2409739C098AC83A4C0028E9 paz at nadir.org \d{4}-\d{2}-\d{2} \[revoked\]/)
+ end
+
+ # gpgme.rb doesn't report missing encryption-capability properly yet.
+ it "displays the expected attributes for a key that's not capable of encryption" do
+ list = create(:list)
+ list.import_key(File.read("spec/fixtures/signonly_key.txt"))
+
+ key = list.key("B1CD8BB15C2673C6BFD8FA4B70B2CF29E01AD53E")
+
+ expect(key.oneline).to match(/0xB1CD8BB15C2673C6BFD8FA4B70B2CF29E01AD53E signonly at example.org \d{4}-\d{2}-\d{2} \[not capable of encryption\]/)
+ end
+ end
+end
diff --git a/spec/schleuder/unit/list_builder_spec.rb b/spec/schleuder/unit/list_builder_spec.rb
index 40d7040..f715a6d 100644
--- a/spec/schleuder/unit/list_builder_spec.rb
+++ b/spec/schleuder/unit/list_builder_spec.rb
@@ -6,7 +6,7 @@ describe Schleuder::ListBuilder do
listname = "list-#{rand}@example.org"
adminaddress = 'schleuder2 at example.org'
adminkey = File.read('spec/fixtures/example_key.txt')
- list, messages = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, adminkey).run
+ list, messages = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run
expect(list).to be_an_instance_of Schleuder::List
expect(list).to be_valid
expect(messages).to be_blank
@@ -16,7 +16,7 @@ describe Schleuder::ListBuilder do
listname = "list-#{rand}"
adminaddress = 'schleuder2 at example.org'
adminkey = File.read('spec/fixtures/example_key.txt')
- list, messages = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, adminkey).run
+ list, messages = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run
expect(list).to be_nil
expect(messages).to be_an_instance_of Hash
expect(messages.keys).to eq ['email']
@@ -27,7 +27,7 @@ describe Schleuder::ListBuilder do
listname = "list-#{rand}@example.org"
adminaddress = 'schleuder2 at example.org'
adminkey = File.read('spec/fixtures/example_key.txt')
- list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, adminkey).run
+ list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run
expect(File.directory?(list.listdir)).to be true
end
@@ -35,7 +35,7 @@ describe Schleuder::ListBuilder do
listname = "list-#{rand}@example.org"
adminaddress = 'schleuder2 at example.org'
adminkey = File.read('spec/fixtures/example_key.txt')
- list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, adminkey).run
+ list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run
uids = list.key.uids.map(&:email)
expect(uids).to include(list.email)
expect(uids).to include(list.request_address)
@@ -46,10 +46,38 @@ describe Schleuder::ListBuilder do
listname = "list-#{rand}@example.org"
adminaddress = 'schleuder2 at example.org'
adminkey = File.read('spec/fixtures/example_key.txt')
- list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, adminkey).run
+ list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, nil, adminkey).run
+
+ subscription_emails = list.subscriptions.map(&:email)
+ keys_fingerprints = list.keys.map(&:fingerprint)
+
+ expect(subscription_emails).to eq [adminaddress]
+ expect(keys_fingerprints).to include("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE")
+ end
+
+ it "subscribes the adminaddress and respects the given adminfingerprint" do
+ listname = "list-#{rand}@example.org"
+ adminaddress = 'schleuder2 at example.org'
+ list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, "59C71FB38AEE22E091C78259D06350440F759BD3").run
+
+ subscription_emails = list.subscriptions.map(&:email)
+ admin_subscription = list.admins.first
+
+ expect(subscription_emails).to eq [adminaddress]
+ expect(admin_subscription.fingerprint).to eql("59C71FB38AEE22E091C78259D06350440F759BD3")
+ end
+
+ it "subscribes the adminaddress and ignores the adminfingerprint if an adminkey was given" do
+ listname = "list-#{rand}@example.org"
+ adminaddress = 'schleuder2 at example.org'
+ adminkey = File.read('spec/fixtures/example_key.txt')
+ list, _ = ListBuilder.new({email: listname, fingerprint: nil}, adminaddress, "59C71FB38AEE22E091C78259D06350440F759BD3", adminkey).run
+
subscription_emails = list.subscriptions.map(&:email)
- keys_emails = list.keys.map(&:uids).flatten.map(&:email)
+ subscription_fingerprints = list.subscriptions.map(&:fingerprint)
+
expect(subscription_emails).to eq [adminaddress]
- expect(keys_emails).to include(adminaddress)
+ expect(subscription_fingerprints).to eq ["C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"]
end
+
end
diff --git a/spec/schleuder/unit/list_spec.rb b/spec/schleuder/unit/list_spec.rb
index 61d3875..ea23063 100644
--- a/spec/schleuder/unit/list_spec.rb
+++ b/spec/schleuder/unit/list_spec.rb
@@ -24,6 +24,7 @@ describe Schleuder::List do
it { is_expected.to respond_to :subject_prefix_in }
it { is_expected.to respond_to :subject_prefix_out }
it { is_expected.to respond_to :openpgp_header_preference }
+ it { is_expected.to respond_to :internal_footer }
it { is_expected.to respond_to :public_footer }
it { is_expected.to respond_to :headers_to_meta }
it { is_expected.to respond_to :bounces_drop_on_headers }
@@ -185,7 +186,14 @@ describe Schleuder::List do
expect(list.errors.messages[:language]).to include("must be one of: en, de")
end
- it "is invalid if public footer include a non-printable characters" do
+ it "is invalid if internal_footer includes a non-printable character" do
+ list = build(:list, internal_footer: "\a")
+
+ expect(list).not_to be_valid
+ expect(list.errors.messages[:internal_footer]).to include("includes non-printable characters")
+ end
+
+ it "is invalid if public_footer includes a non-printable character" do
list = build(:list, public_footer: "\a")
expect(list).not_to be_valid
@@ -197,7 +205,7 @@ describe Schleuder::List do
expect(Schleuder::List.configurable_attributes).to eq [
:bounces_drop_all, :bounces_drop_on_headers, :bounces_notify_admins,
:forward_all_incoming_to_admins, :headers_to_meta, :include_list_headers,
- :include_openpgp_header, :keep_msgid, :keywords_admin_notify, :keywords_admin_only,
+ :include_openpgp_header, :internal_footer, :keep_msgid, :keywords_admin_notify, :keywords_admin_only,
:language, :log_level, :logfiles_to_keep, :max_message_size_kb, :openpgp_header_preference,
:public_footer, :receive_admin_only, :receive_authenticated_only, :receive_encrypted_only,
:receive_from_subscribed_emailaddresses_only, :receive_signed_only, :send_encrypted_only,
@@ -349,6 +357,8 @@ describe Schleuder::List do
it "exports the key with the fingerprint of the list if no argument is given" do
list = create(:list, email: "schleuder at example.org")
expected_public_key = File.read("spec/fixtures/schleuder_at_example_public_key.txt")
+ # Get rid of the first, opening line, so we don't compare against optional comments in the output, etc.
+ expected_public_key = expected_public_key.split("\n").slice(1..-1).join("\n")
expect(list.export_key()).to include expected_public_key
end
@@ -357,6 +367,8 @@ describe Schleuder::List do
it "exports the key with the given fingerprint" do
list = create(:list, email: "schleuder at example.org")
expected_public_key = File.read("spec/fixtures/schleuder_at_example_public_key.txt")
+ # Get rid of the first, opening line, so we don't compare against optional comments in the output, etc.
+ expected_public_key = expected_public_key.split("\n").slice(1..-1).join("\n")
expect(
list.export_key("59C71FB38AEE22E091C78259D06350440F759BD3")
@@ -367,33 +379,43 @@ describe Schleuder::List do
it "adds a mesage if a key expires in two weeks or less" do
list = create(:list)
key = double("key")
+ generation_time = Time.now - 1.year
+ expiry_time = Time.now + 7.days
allow_any_instance_of(GPGME::Key).to receive(:subkeys).and_return(key)
allow(key).to receive(:first).and_return(key)
- allow(key).to receive(:expires).and_return(Time.now + 7.days)
+ allow(key).to receive(:timestamp).and_return(generation_time)
+ allow(key).to receive(:expires?).and_return(true)
+ allow(key).to receive(:expired?).and_return(false)
+ allow(key).to receive(:expired).and_return(false)
+ allow(key).to receive(:any?).and_return(false)
+ allow(key).to receive(:expires).and_return(expiry_time)
allow(key).to receive(:fingerprint).and_return("59C71FB38AEE22E091C78259D06350440F759BD3")
- expect(list.check_keys).to eq "Key 59C71FB38AEE22E091C78259D06350440F759BD3 expires in 6 days.\n"
+ datefmt = "%Y-%m-%d"
+ generation_date = generation_time.strftime(datefmt)
+ expiry_date = expiry_time.strftime(datefmt)
+ expect(list.check_keys).to eq "This key expires in 6 days:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder at example.org #{generation_date} [expires: #{expiry_date}]\n\n"
end
it "adds a message if a key is revoked" do
list = create(:list)
allow_any_instance_of(GPGME::Key).to receive(:trust).and_return(:revoked)
- expect(list.check_keys).to eq "Key 59C71FB38AEE22E091C78259D06350440F759BD3 is revoked.\n"
+ expect(list.check_keys).to match(/This key is revoked:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder at example.org \d{4}-\d{2}-\d{2} \[revoked\]\n\n/)
end
it "adds a message if a key is disabled" do
list = create(:list)
allow_any_instance_of(GPGME::Key).to receive(:trust).and_return(:disabled)
- expect(list.check_keys).to eq "Key 59C71FB38AEE22E091C78259D06350440F759BD3 is disabled.\n"
+ expect(list.check_keys).to match(/This key is disabled:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder at example.org \d{4}-\d{2}-\d{2} \[disabled\]\n\n/)
end
it "adds a message if a key is invalid" do
list = create(:list)
allow_any_instance_of(GPGME::Key).to receive(:trust).and_return(:invalid)
- expect(list.check_keys).to eq "Key 59C71FB38AEE22E091C78259D06350440F759BD3 is invalid.\n"
+ expect(list.check_keys).to match(/This key is invalid:\n0x59C71FB38AEE22E091C78259D06350440F759BD3 schleuder at example.org \d{4}-\d{2}-\d{2} \[invalid\]\n\n/)
end
end
@@ -463,7 +485,7 @@ describe Schleuder::List do
output = list.fetch_keys('98769E8A1091F36BD88403ECF71A3F8412D83889')
end
- expect(output).to include("98769E8A1091F36BD88403ECF71A3F8412D83889 was fetched (new key)")
+ expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/)
teardown_list_and_mailer(list)
end
@@ -477,7 +499,7 @@ describe Schleuder::List do
output = list.fetch_keys('http://127.0.0.1:9999/keys/example.asc')
end
- expect(output).to include("98769E8A1091F36BD88403ECF71A3F8412D83889 was fetched (new key)")
+ expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/)
teardown_list_and_mailer(list)
end
@@ -491,7 +513,7 @@ describe Schleuder::List do
output = list.fetch_keys('admin at example.org')
end
- expect(output).to include("98769E8A1091F36BD88403ECF71A3F8412D83889 was fetched (new key)")
+ expect(output).to match(/This key was fetched \(new key\):\n0x98769E8A1091F36BD88403ECF71A3F8412D83889 bla at foo \d{4}-\d{2}-\d{2} \[expired: \d{4}-\d{2}-\d{2}\]/)
teardown_list_and_mailer(list)
end
@@ -510,4 +532,180 @@ describe Schleuder::List do
expect(raw.parts.first.parts.last.body.to_s).to include("-----BEGIN PGP PUBLIC KEY BLOCK-----")
end
end
+
+ describe "#subscribe" do
+ it "subscribes and ignores nil-values for admin and delivery_enabled" do
+ list = create(:list)
+ sub, _ = list.subscribe("admin at example.org", nil, nil, nil)
+
+ expect(sub.admin?).to be(false)
+ expect(sub.delivery_enabled?).to be(true)
+ end
+
+ it "subscribes and sets the fingerprint from key material that contains exactly one key" do
+ list = create(:list)
+ key_material = File.read("spec/fixtures/example_key.txt")
+ sub, msgs = list.subscribe("admin at example.org", "", true, true, key_material)
+
+ expect(msgs).to be(nil)
+ expect(sub.fingerprint).to eql("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE")
+ expect(list.subscriptions.size).to be(1)
+ expect(list.subscriptions.first.fingerprint).to eql("C4D60F8833789C7CAA44496FD3FFA6613AB10ECE")
+ expect(list.keys.size).to be(2)
+ expect(list.keys.map(&:fingerprint)).to eql(["59C71FB38AEE22E091C78259D06350440F759BD3", "C4D60F8833789C7CAA44496FD3FFA6613AB10ECE"])
+ end
+
+ it "subscribes and does not set the fingerprint from key material containing multiple keys" do
+ list = create(:list)
+ key_material = File.read("spec/fixtures/example_key.txt")
+ key_material << File.read("spec/fixtures/olduid_key.txt")
+ sub, msgs = list.subscribe("admin at example.org", "", true, true, key_material)
+
+ expect(msgs).to eql("The given key material contained more than one key, could not determine which fingerprint to use. Please set it manually!")
+ expect(sub.fingerprint).to be_blank
+ expect(list.subscriptions.size).to be(1)
+ expect(list.subscriptions.first.fingerprint).to be_blank
+ expect(list.keys.size).to be(3)
+ expect(list.keys.map(&:fingerprint)).to eql(["59C71FB38AEE22E091C78259D06350440F759BD3", "C4D60F8833789C7CAA44496FD3FFA6613AB10ECE", "6EE51D78FD0B33DE65CCF69D2104E20E20889F66"])
+ end
+
+ it "subscribes and does not set the fingerprint from key material containing no keys" do
+ list = create(:list)
+ key_material = "blabla"
+ sub, msgs = list.subscribe("admin at example.org", "", true, true, key_material)
+
+ expect(msgs).to eql("The given key material did not contain any keys!")
+ expect(sub.fingerprint).to be_blank
+ expect(list.subscriptions.size).to be(1)
+ expect(list.subscriptions.first.fingerprint).to be_blank
+ expect(list.keys.size).to be(1)
+ expect(list.keys.map(&:fingerprint)).to eql(["59C71FB38AEE22E091C78259D06350440F759BD3"])
+ end
+
+ it "subscribes and ignores a given fingerprint if key material is given, too" do
+ list = create(:list)
+ key_material = "blabla"
+ sub, msgs = list.subscribe("admin at example.org", "C4D60F8833789C7CAA44496FD3FFA6613AB10ECE", true, true, key_material)
+
+ expect(msgs).to eql("The given key material did not contain any keys!")
+ expect(sub.fingerprint).to be_blank
+ expect(list.subscriptions.size).to be(1)
+ expect(list.subscriptions.first.fingerprint).to be_blank
+ expect(list.keys.size).to be(1)
+ expect(list.keys.map(&:fingerprint)).to eql(["59C71FB38AEE22E091C78259D06350440F759BD3"])
+ end
+ end
+
+ describe "#send_to_subscriptions" do
+ it "sends the message to all subscribers" do
+ list = create(:list, send_encrypted_only: false)
+ sub, msgs = list.subscribe("admin at example.org", nil, true)
+ sub, msgs = list.subscribe("user1 at example.org")
+ sub, msgs = list.subscribe("user2 at example.org")
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = 'something at localhost'
+ mail.subject = 'Something'
+ mail.body = "Some content"
+
+ Schleuder::Runner.new().run(mail, list.email)
+ messages = Mail::TestMailer.deliveries
+ recipients = messages.map { |m| m.to.first }.sort
+
+ expect(messages.size).to be(3)
+ expect(recipients).to eql(['admin at example.org', 'user1 at example.org', 'user2 at example.org'])
+ expect(messages[0].parts.first.parts.last.body.to_s).to eql("Some content")
+ expect(messages[0].subject).to eql("Something")
+ expect(messages[1].parts.first.parts.last.body.to_s).to eql("Some content")
+ expect(messages[1].subject).to eql("Something")
+ expect(messages[2].parts.first.parts.last.body.to_s).to eql("Some content")
+ expect(messages[2].subject).to eql("Something")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "sends the message to all subscribers, in the clear if one's key is unusable, if send_encrypted_only is false" do
+ list = create(:list, send_encrypted_only: false)
+ sub, msgs = list.subscribe("admin at example.org", nil, true)
+ key_material = File.read("spec/fixtures/expired_key.txt")
+ sub, msgs = list.subscribe("user1 at example.org", nil, false, true, key_material)
+ sub, msgs = list.subscribe("user2 at example.org")
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = 'something at localhost'
+ mail.subject = 'Something'
+ mail.body = "Some content"
+
+ Schleuder::Runner.new().run(mail, list.email)
+ messages = Mail::TestMailer.deliveries
+ recipients = messages.map { |m| m.to.first }.sort
+
+ expect(messages.size).to be(3)
+ expect(recipients).to eql(['admin at example.org', 'user1 at example.org', 'user2 at example.org'])
+ expect(messages[0].parts.first.parts.last.body.to_s).to eql("Some content")
+ expect(messages[0].subject).to eql("Something")
+ expect(messages[1].parts.first.parts.last.body.to_s).to eql("Some content")
+ expect(messages[1].subject).to eql("Something")
+ expect(messages[2].parts.first.parts.last.body.to_s).to eql("Some content")
+ expect(messages[2].subject).to eql("Something")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "sends the message only to subscribers with available keys if send_encrypted_only is true, and a notification to the other subscribers" do
+ list = create(:list, send_encrypted_only: true)
+ sub, msgs = list.subscribe("admin at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ sub, msgs = list.subscribe("user1 at example.org")
+ sub, msgs = list.subscribe("user2 at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3')
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = 'something at localhost'
+ mail.subject = 'Something'
+ mail.body = "Some content"
+
+ Schleuder::Runner.new().run(mail, list.email)
+ messages = Mail::TestMailer.deliveries
+ recipients = messages.map { |m| m.to.first }.sort
+
+ expect(messages.size).to be(3)
+ expect(recipients).to eql(['admin at example.org', 'user1 at example.org', 'user2 at example.org'])
+ expect(messages[0].parts.last.body.to_s).to include("-----BEGIN PGP MESSAGE-----")
+ expect(messages[0].subject).to eql("Something")
+ expect(messages[1].parts.first.body.to_s).to include("You missed an email")
+ expect(messages[1].subject).to eql("Notice")
+ expect(messages[2].parts.last.body.to_s).to include("-----BEGIN PGP MESSAGE-----")
+ expect(messages[2].subject).to eql("Something")
+
+ teardown_list_and_mailer(list)
+ end
+
+ it "sends the message only to subscribers with usable keys if send_encrypted_only is true, and a notification to the other subscribers" do
+ list = create(:list, send_encrypted_only: true)
+ key_material = File.read("spec/fixtures/partially_expired_key.txt")
+ sub, msgs = list.subscribe("admin at example.org", nil, true, true, key_material)
+ key_material = File.read("spec/fixtures/expired_key.txt")
+ sub, msgs = list.subscribe("user1 at example.org", nil, false, true, key_material)
+ sub, msgs = list.subscribe("user2 at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3')
+ mail = Mail.new
+ mail.to = list.email
+ mail.from = 'something at localhost'
+ mail.subject = 'Something'
+ mail.body = "Some content"
+
+ Schleuder::Runner.new().run(mail, list.email)
+ messages = Mail::TestMailer.deliveries
+ recipients = messages.map { |m| m.to.first }.sort
+
+ expect(messages.size).to be(3)
+ expect(recipients).to eql(['admin at example.org', 'user1 at example.org', 'user2 at example.org'])
+ expect(messages[0].parts.first.body.to_s).to include("You missed an email")
+ expect(messages[0].subject).to eql("Notice")
+ expect(messages[1].parts.first.body.to_s).to include("You missed an email")
+ expect(messages[1].subject).to eql("Notice")
+ expect(messages[2].parts.last.body.to_s).to include("-----BEGIN PGP MESSAGE-----")
+ expect(messages[2].subject).to eql("Something")
+
+ teardown_list_and_mailer(list)
+ end
+ end
end
diff --git a/spec/schleuder/unit/logger_notifications_spec.rb b/spec/schleuder/unit/logger_notifications_spec.rb
index 3fe5186..5a77821 100644
--- a/spec/schleuder/unit/logger_notifications_spec.rb
+++ b/spec/schleuder/unit/logger_notifications_spec.rb
@@ -81,4 +81,33 @@ describe Schleuder::LoggerNotifications do
expect(message.parts.first.parts[2].body.to_s).to include("Subject: A subject")
expect(message.parts.first.parts[2][:content_type].content_type).to eql("message/rfc822")
end
+
+ it "notifies admins encryptedly if their key is usable" do
+ list = create(:list, send_encrypted_only: false)
+ list.subscribe("schleuder at example.org", '59C71FB38AEE22E091C78259D06350440F759BD3', true)
+ mail = Mail.new
+ mail.subject = "A subject"
+ list.logger.notify_admin(["Something", "anotherthing"], mail.to_s, I18n.t('notice'))
+
+ message = Mail::TestMailer.deliveries.first
+
+ expect(message.subject).to eql('Notice')
+ expect(message.parts.size).to be(2)
+ expect(message.parts.last.body.to_s).to include('-----BEGIN PGP MESSAGE-----')
+ end
+
+ it "notifies admins in the clear if their key is unusable" do
+ list = create(:list, send_encrypted_only: false)
+ key_material = File.read("spec/fixtures/partially_expired_key.txt")
+ list.subscribe("schleuder at example.org", nil, true, true, key_material)
+ mail = Mail.new
+ mail.subject = "A subject"
+ list.logger.notify_admin("Something", mail.to_s, I18n.t('notice'))
+
+ message = Mail::TestMailer.deliveries.first
+
+ expect(message.subject).to eql('Notice')
+ expect(message.parts.size).to be(2)
+ expect(message.parts.first.parts.first.body.to_s).to eql('Something')
+ end
end
diff --git a/spec/schleuder/unit/message_spec.rb b/spec/schleuder/unit/message_spec.rb
index be3c2f5..72926ed 100644
--- a/spec/schleuder/unit/message_spec.rb
+++ b/spec/schleuder/unit/message_spec.rb
@@ -100,5 +100,29 @@ describe Mail::Message do
expect(message.subject).to eql('Re: [prefix] test')
end
end
+
+ it "adds list#public_footer as last mime-part without changing its value" do
+ footer = "\n\n-- \nblabla\n blabla\n "
+ list = create(:list)
+ list.public_footer = footer
+ mail = Mail.new
+ mail.body = 'blabla'
+ mail.list = list
+ mail.add_public_footer!
+
+ expect(mail.parts.last.body.to_s).to eql(footer)
+ end
+
+ it "adds list#internal_footer as last mime-part without changing its value" do
+ footer = "\n\n-- \nblabla\n blabla\n "
+ list = create(:list)
+ list.internal_footer = footer
+ mail = Mail.new
+ mail.body = 'blabla'
+ mail.list = list
+ mail.add_internal_footer!
+
+ expect(mail.parts.last.body.to_s).to eql(footer)
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 6348995..51f3d7f 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,12 +1,24 @@
ENV['SCHLEUDER_ENV'] ||= 'test'
ENV['SCHLEUDER_CONFIG'] = 'spec/schleuder.yml'
ENV["SCHLEUDER_LIST_DEFAULTS"] = "etc/list-defaults.yml"
-require 'bundler/setup'
-Bundler.setup
+if ENV['USE_BUNDLER'] != 'false'
+ require 'bundler/setup'
+ Bundler.setup
+end
+# We need to do this before requiring any other code
+# Check env if we want to run code coverage analysis
+if ENV['CHECK_CODE_COVERAGE'] != 'false'
+ require 'simplecov'
+ require 'simplecov-console'
+ SimpleCov::Formatter::Console.table_options = {max_width: 400}
+ SimpleCov.formatter = SimpleCov::Formatter::Console
+ SimpleCov.start
+end
require 'schleuder'
require 'schleuder/cli'
require 'database_cleaner'
require 'factory_girl'
+require 'net/http'
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
@@ -34,6 +46,8 @@ RSpec.configure do |config|
config.after(:each) do |example|
FileUtils.rm_rf(Dir["spec/gnupg/pubring.gpg~"])
+ `gpgconf --kill dirmngr > /dev/null 2>&1`
+ `gpgconf --kill gpg-agent > /dev/null 2>&1`
end
config.after(:suite) do
@@ -62,8 +76,22 @@ RSpec.configure do |config|
def with_sks_mock
pid = Process.spawn('spec/sks-mock.rb', [:out, :err] => ["/tmp/sks-mock.log", 'w'])
- sleep 1
+ uri = URI.parse("http://127.0.0.1:9999/status")
+ attempts = 5
+ begin
+ sleep 1
+ Net::HTTP.get(uri)
+ rescue Errno::ECONNREFUSED => exc
+ attempts -= 1
+ if attempts > 0
+ retry
+ else
+ raise "sks-mock.rb failed to start, cannot continue: #{exc}"
+ end
+ end
+
yield
+
Process.kill 'TERM', pid
Process.wait pid
end
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/schleuder.git
More information about the Pkg-ruby-extras-commits
mailing list