schleuder 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +0 -0
- data/LICENSE +339 -0
- data/README +32 -0
- data/bin/schleuder +96 -0
- data/bin/schleuder-fix-gem-dependencies +30 -0
- data/bin/schleuder-init-setup +37 -0
- data/bin/schleuder-migrate-v2.1-to-v2.2 +205 -0
- data/bin/schleuder-newlist +384 -0
- data/contrib/check-expired-keys.rb +59 -0
- data/contrib/mutt-schleuder-colors.rc +10 -0
- data/contrib/mutt-schleuder-resend.vim +24 -0
- data/contrib/smtpserver.rb +76 -0
- data/ext/default-list.conf +146 -0
- data/ext/default-members.conf +7 -0
- data/ext/list.conf.example +14 -0
- data/ext/schleuder.conf +62 -0
- data/lib/schleuder.rb +49 -0
- data/lib/schleuder/archiver.rb +46 -0
- data/lib/schleuder/crypt.rb +188 -0
- data/lib/schleuder/errors.rb +5 -0
- data/lib/schleuder/list.rb +177 -0
- data/lib/schleuder/list_config.rb +146 -0
- data/lib/schleuder/log/listlogger.rb +56 -0
- data/lib/schleuder/log/outputter/emailoutputter.rb +118 -0
- data/lib/schleuder/log/outputter/metaemailoutputter.rb +50 -0
- data/lib/schleuder/log/schleuderlogger.rb +23 -0
- data/lib/schleuder/mail.rb +861 -0
- data/lib/schleuder/mailer.rb +26 -0
- data/lib/schleuder/member.rb +69 -0
- data/lib/schleuder/plugin.rb +54 -0
- data/lib/schleuder/processor.rb +363 -0
- data/lib/schleuder/schleuder_config.rb +72 -0
- data/lib/schleuder/storage.rb +84 -0
- data/lib/schleuder/utils.rb +80 -0
- data/lib/schleuder/version.rb +3 -0
- data/man/schleuder-newlist.8 +191 -0
- data/man/schleuder.8 +400 -0
- data/plugins/README +20 -0
- data/plugins/manage_keys_plugin.rb +113 -0
- data/plugins/manage_members_plugin.rb +152 -0
- data/plugins/manage_self_plugin.rb +26 -0
- data/plugins/resend_plugin.rb +35 -0
- data/plugins/version_plugin.rb +12 -0
- metadata +178 -0
- metadata.gz.sig +2 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
module Schleuder
|
2
|
+
class Archiver
|
3
|
+
def archive(mail)
|
4
|
+
Schleuder.log.info "Archiving email"
|
5
|
+
mail2archive = mail.individualize_member(_receiver)
|
6
|
+
|
7
|
+
# TODO: wrap that duplicated code out into it's dedicated method
|
8
|
+
begin
|
9
|
+
encrypted, errmsg = mail2archive.encrypt!(_receiver)
|
10
|
+
rescue GPGME::Error::UnusablePublicKey => e
|
11
|
+
# This exception is thrown, if the public key of a certain list
|
12
|
+
# member is not usable (because it is revoked, expired, disabled or
|
13
|
+
# invalid).
|
14
|
+
k = e.keys.first
|
15
|
+
key = mail2archive.crypt.get_key(k.fpr).first
|
16
|
+
errmsg = "#{e.message}: (#{k.class})\n#{key.to_s}"
|
17
|
+
encrypted = false
|
18
|
+
rescue GPGME::Error::General => e
|
19
|
+
errmsg = e.message
|
20
|
+
encrypted = false
|
21
|
+
end
|
22
|
+
|
23
|
+
if encrypted
|
24
|
+
_dump(mail2archive)
|
25
|
+
else
|
26
|
+
Schleuder.log.error("Could not encrypt message with list's key to archive it. Skipping archiving of message...\n\nError Message: #{errmsg}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def _dump(mail)
|
33
|
+
now = Time.now
|
34
|
+
dump_dir = File.join(Schleuder.list.listdir,'archive',[:year,:month,:day].collect{|m| now.send(m).to_s })
|
35
|
+
require 'fileutils'
|
36
|
+
FileUtils.mkdir_p dump_dir unless File.directory? dump_dir
|
37
|
+
msg_file = File.join(dump_dir,"#{Time.now.strftime('%H%M%S')}-#{mail.message_id[1..-2]}")
|
38
|
+
Schleuder.log.info("Archiving message to #{msg_file}")
|
39
|
+
File.open(msg_file,"w") { |f| f << mail.to_s }
|
40
|
+
end
|
41
|
+
|
42
|
+
def _receiver
|
43
|
+
@receiver ||= Member.new('email' => Schleuder.list.config.myaddr)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
module Schleuder
|
2
|
+
# Wrapper for ruby-gpgme. Method naming is not strictly logical, this might
|
3
|
+
# change but aliases will be set up then.
|
4
|
+
class Crypt
|
5
|
+
# Instantiates and stores password
|
6
|
+
def initialize(password)
|
7
|
+
# Check file permissions. ruby-gpgme unfortunately returns unspecific
|
8
|
+
# errors if it can't read files.
|
9
|
+
%w(pubring.gpg secring.gpg trustdb.gpg).each do |fn|
|
10
|
+
f = File.join(ENV['GNUPGHOME'], fn)
|
11
|
+
if ! File.readable?(f)
|
12
|
+
raise Errno::EACCES.new('%s is not readable' % f)
|
13
|
+
elsif ! File.writable?(f)
|
14
|
+
raise Errno::EACCES.new('%s is not writable' % f)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
@password = password
|
19
|
+
if GPGME.respond_to? 'check_version'
|
20
|
+
GPGME::check_version('0.0.0')
|
21
|
+
end
|
22
|
+
@ctx = GPGME::Ctx.new
|
23
|
+
# feed the passphrase into the Context
|
24
|
+
@ctx.set_passphrase_cb(method(:passfunc))
|
25
|
+
end
|
26
|
+
|
27
|
+
# Verify a gpg-signature. Use +signed_string+ if the signature is
|
28
|
+
# detached. Returns a GPGME::SignatureResult
|
29
|
+
def verify(sig, signed_string='')
|
30
|
+
in_signed = ''
|
31
|
+
if signed_string.empty?
|
32
|
+
# verify +sig+ as cleartext (aka pgp/inline) signature
|
33
|
+
Schleuder.log.debug 'No extra signed_string, verifying cleartext signature'
|
34
|
+
output = GPGME.verify(sig) do |sig|
|
35
|
+
in_signed = sig
|
36
|
+
end
|
37
|
+
else
|
38
|
+
# verify detached signature
|
39
|
+
Schleuder.log.debug 'Verifying detached signature'
|
40
|
+
# Don't know why we need a GPGME::Data object this time but without gpgme throws exceptions
|
41
|
+
plain = GPGME::Data.new
|
42
|
+
GPGME.verify(sig, signed_string, plain) do |sig|
|
43
|
+
in_signed = sig
|
44
|
+
end
|
45
|
+
output = signed_string
|
46
|
+
|
47
|
+
end
|
48
|
+
Schleuder.log.debug 'verify_result: ' + in_signed.inspect
|
49
|
+
|
50
|
+
[output, in_signed]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Decrypt a string.
|
54
|
+
def decrypt(str)
|
55
|
+
output = ""
|
56
|
+
in_encrypted = nil
|
57
|
+
in_signed = nil
|
58
|
+
|
59
|
+
# TODO: return ciphertext if missing key. Sensible e.g. if it is part
|
60
|
+
# of a nested MIME-message and encrypted to someone else on purpose.
|
61
|
+
# Breaking if even the whole message is not decryptable is a job for
|
62
|
+
# the processor.
|
63
|
+
|
64
|
+
# return input instead of empty String if not encrypted.
|
65
|
+
# String#content_type is provided by filemagic/ext.
|
66
|
+
unless str =~ /^-----BEGIN PGP MESSAGE-----/ || str.content_type.split('/').last.eql?('pgp')
|
67
|
+
output, in_signed = verify(str)
|
68
|
+
# match pgp-mime- and inline-pgp-signatures
|
69
|
+
if str =~ /^-----BEGIN PGP SIG/
|
70
|
+
Schleuder.log.debug 'found signed, not encrypted message, verifying'
|
71
|
+
output, in_signed = verify(str)
|
72
|
+
else
|
73
|
+
Schleuder.log.debug 'found not signed, not encrypted message, returning input'
|
74
|
+
output = str
|
75
|
+
end
|
76
|
+
else
|
77
|
+
Schleuder.log.debug 'found pgp content, decrypting and verifying with gpgme'
|
78
|
+
in_encrypted = true
|
79
|
+
output = GPGME.decrypt(str, :passphrase_callback => method(:passfunc)) do |sig|
|
80
|
+
in_signed = sig
|
81
|
+
end
|
82
|
+
if output.empty?
|
83
|
+
Exception.new("Output from GPGME.decrypt was empty!")
|
84
|
+
end
|
85
|
+
# TODO: return mailadresses or keys instead of signature-objects?
|
86
|
+
end
|
87
|
+
[output, in_encrypted, in_signed]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Encrypt a string to a single receiver and sign it. +receiver+ must be a
|
91
|
+
# Schleuder::Member
|
92
|
+
def encrypt_str(str, receiver)
|
93
|
+
# encrypt and sign and return encrypted data as string
|
94
|
+
# For some reason sometimes the last two characters of str are stolen
|
95
|
+
# unless we append a blank or newline... Life is hard...
|
96
|
+
GPGME.encrypt([receiver.key],
|
97
|
+
"#{str} ",
|
98
|
+
{:passphrase_callback => method(:passfunc),
|
99
|
+
:armor => true,
|
100
|
+
:sign => true,
|
101
|
+
:always_trust => true
|
102
|
+
})
|
103
|
+
end
|
104
|
+
|
105
|
+
# Lists all public keys matching +pattern+. Returns an array of
|
106
|
+
# GPGME::GpgKey's
|
107
|
+
def list_keys(pattern='')
|
108
|
+
GPGME.list_keys(pattern)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns the GPGME::GpgKey matching +pattern+. Log an error if more than
|
112
|
+
# one matches, because duplicated user-ids is a sensitive issue.
|
113
|
+
def get_key(pattern, only_valid_keys=false)
|
114
|
+
pattern = "<#{pattern}>" if pattern =~ /.*@.*/ && !(pattern =~ /^<.*>$/)
|
115
|
+
keys = list_keys(pattern)
|
116
|
+
|
117
|
+
keys.reject! { |key| [:revoked,:expired].include?(key.trust) } if only_valid_keys
|
118
|
+
|
119
|
+
if keys.empty?
|
120
|
+
[false, "no key found for #{pattern}."]
|
121
|
+
elsif keys.length > 1
|
122
|
+
Schleuder.log.warn "There's more than one key matching the pattern you gave me!"
|
123
|
+
Schleuder.log.debug { "Pattern: #{pattern.inspect}" }
|
124
|
+
Schleuder.log.debug { "Keys: #{keys.inspect}" }
|
125
|
+
[false, "no distinct key for #{pattern.inspect} found. Matching keys: #{key_infos(keys,only_valid_keys).join(', ')}"]
|
126
|
+
else
|
127
|
+
[keys.first]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Signs +string+ with the private key of the list (aka detached signature)
|
132
|
+
def sign(string)
|
133
|
+
GPGME::detach_sign(string, {:armor => true, :passphrase_callback => method(:passfunc)})
|
134
|
+
end
|
135
|
+
|
136
|
+
# Clearsigns +string+ with the private key of the list
|
137
|
+
def clearsign(string)
|
138
|
+
GPGME::clearsign(string, {:armor => true, :passphrase_callback => method(:passfunc)})
|
139
|
+
end
|
140
|
+
|
141
|
+
# Exports the public key matching +keyid+ as ascii key block.
|
142
|
+
def export(keyid)
|
143
|
+
GPGME.export(keyid, :armor=>:true)
|
144
|
+
end
|
145
|
+
|
146
|
+
# Delete the public key matching +pattern+ from the public key ring of the
|
147
|
+
# list
|
148
|
+
def delete_key(key)
|
149
|
+
msg = nil
|
150
|
+
key, msg = get_key(key) if key.kind_of?(String)
|
151
|
+
|
152
|
+
if key
|
153
|
+
@ctx.delete_key(key)
|
154
|
+
return true
|
155
|
+
else
|
156
|
+
msg
|
157
|
+
end
|
158
|
+
rescue => e
|
159
|
+
return e
|
160
|
+
end
|
161
|
+
|
162
|
+
# Import +keydata+ into public key ring of the list
|
163
|
+
def add_key(keydata)
|
164
|
+
GPGME.import(keydata)
|
165
|
+
end
|
166
|
+
|
167
|
+
def add_key_from_file(keyfile)
|
168
|
+
add_key(File.read(keyfile))
|
169
|
+
end
|
170
|
+
|
171
|
+
def key_descr(key)
|
172
|
+
key.to_s.split("\n").first.split[1..2].join(' ')
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def key_infos(keys, only_valid_keys=false)
|
178
|
+
keys.collect { |key| key_descr(key) + (!only_valid_keys && [:revoked, :expired].include?(key.trust)) ? " *#{key.trust}*" : '' }
|
179
|
+
end
|
180
|
+
|
181
|
+
def passfunc(hook, uid_hint, passphrase_info, prev_was_bad, fd)
|
182
|
+
io = IO.for_fd(fd, 'w')
|
183
|
+
io.puts @password
|
184
|
+
io.flush
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
module Schleuder
|
2
|
+
# Represents a single list: name, config, members.
|
3
|
+
class List
|
4
|
+
# Name of this list. Must match the name of the subdirectory in
|
5
|
+
# +SchleuderConfig::lists_dir+
|
6
|
+
attr_reader :listname
|
7
|
+
|
8
|
+
# Prepare some variables, set up the SchleuderLogger and set GNUPGHOME
|
9
|
+
def initialize(listname,newlist=false)
|
10
|
+
@listname = listname
|
11
|
+
@config = _load_config(false) if newlist
|
12
|
+
@log = ListLogger.new listname, listdir, config
|
13
|
+
|
14
|
+
# setting GNUPGHOME to list's home, to make use of the keys there
|
15
|
+
Schleuder.log.debug "setting ENV[GNUPGHOME] to #{listdir}"
|
16
|
+
ENV["GNUPGHOME"] = listdir
|
17
|
+
@members = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Provides an array of Schleuder::Member's, read from +members.conf+
|
21
|
+
def members
|
22
|
+
unless @members
|
23
|
+
Schleuder.log.debug("reading #{members_file}")
|
24
|
+
@members = YAML::load_file(members_file).collect do |h|
|
25
|
+
h.kind_of?(Schleuder::Member) ? h : Schleuder::Member.new(h,false)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
@members
|
29
|
+
end
|
30
|
+
|
31
|
+
def members_file
|
32
|
+
@members_file ||= File.join(listdir, Schleuder.config.lists_memberfile)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Saves an array of Schleuder::Member's into +members.conf++
|
36
|
+
def members=(arr)
|
37
|
+
Schleuder.log.debug 'writing members'
|
38
|
+
Schleuder.log.info("writing #{members_file}")
|
39
|
+
@members = arr.collect { |m| m.kind_of?(Hash) ? Member.new(m,false) : m }
|
40
|
+
_write(YAML.dump(@members.collect { |m| m.to_hash }), members_file)
|
41
|
+
@members
|
42
|
+
end
|
43
|
+
|
44
|
+
# Finds a member by email address.
|
45
|
+
def find_member_by_email(addresses)
|
46
|
+
addresses = Array(addresses)
|
47
|
+
members.detect { |m| addresses.include?(m.email) } || false
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_admin_by_email(addresses)
|
51
|
+
addresses = Array(addresses)
|
52
|
+
if admin_email = self.config.admins.detect { |a| addresses.include?(a.email) }
|
53
|
+
Member.new(:email => admin_email)
|
54
|
+
else
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def find_member_by_key(key)
|
60
|
+
Schleuder.log.debug "Looking for member for key #{key}"
|
61
|
+
find_by_key(members, key)
|
62
|
+
end
|
63
|
+
|
64
|
+
def find_admin_by_key(key)
|
65
|
+
Schleuder.log.debug "Looking for admin for key #{key}"
|
66
|
+
find_by_key(config.admins, key)
|
67
|
+
end
|
68
|
+
|
69
|
+
def find_admin_by_address(address)
|
70
|
+
config.admins.detect { |admin| admin.email == address }
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_member_by_address(address)
|
74
|
+
members.detect { |member| member.email == address }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Provides the list config as Schleuder::ListConfig-object
|
78
|
+
def config
|
79
|
+
@config ||= _load_config
|
80
|
+
end
|
81
|
+
|
82
|
+
# Saves +data+ into the list-config-file (default: list.conf). +data+ must
|
83
|
+
# be a Schleuder::ListConfig or valid input to +ListConfig.new+
|
84
|
+
def config=(data)
|
85
|
+
Schleuder.log.info("writing list-config for: #{listname}")
|
86
|
+
if data.is_a?(ListConfig)
|
87
|
+
@config = data
|
88
|
+
else
|
89
|
+
@config = ListConfig.new(data)
|
90
|
+
end
|
91
|
+
_write(YAML::dump(@config.to_hash), File.join(listdir, Schleuder.config.lists_configfile))
|
92
|
+
@config
|
93
|
+
end
|
94
|
+
|
95
|
+
# Builds the bounce-address for the list
|
96
|
+
def bounce_addr
|
97
|
+
@bounce_addr ||= self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-bounce@\2')
|
98
|
+
end
|
99
|
+
|
100
|
+
# Builds the owner-address for the list
|
101
|
+
def owner_addr
|
102
|
+
@owner_addr ||= self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-owner@\2')
|
103
|
+
end
|
104
|
+
|
105
|
+
# Builds the request-address for the list
|
106
|
+
def request_addr
|
107
|
+
@request_addr ||= self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-request@\2')
|
108
|
+
end
|
109
|
+
|
110
|
+
# builds the send-key-command-address for the list
|
111
|
+
def sendkey_addr
|
112
|
+
self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-sendkey@\2')
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.listdir(listname)
|
116
|
+
name = listname.split('@')
|
117
|
+
if name.size < 2
|
118
|
+
Schleuder.log.warn 'Listname should be a full email-address, using only the local part is deprecated. Please fix this!'
|
119
|
+
end
|
120
|
+
File.expand_path(File.join([Schleuder.config.lists_dir, name.reverse].flatten))
|
121
|
+
end
|
122
|
+
|
123
|
+
def listdir
|
124
|
+
@listdir ||= List.listdir(@listname)
|
125
|
+
end
|
126
|
+
|
127
|
+
def listid
|
128
|
+
@listid ||= config.myaddr.gsub(/@/, '.')
|
129
|
+
end
|
130
|
+
|
131
|
+
def key
|
132
|
+
@key ||= lookup_list_key
|
133
|
+
end
|
134
|
+
|
135
|
+
def key_fingerprint
|
136
|
+
key.subkeys.first.fingerprint
|
137
|
+
end
|
138
|
+
|
139
|
+
def archive(mail)
|
140
|
+
@list_archiver ||= Schleuder::Archiver.new
|
141
|
+
@list_archiver.archive(mail)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
# Loads the configuration
|
147
|
+
# fromfile = Whether to load the config from file.
|
148
|
+
def _load_config(fromfile=true)
|
149
|
+
Schleuder.log.debug("reading list-config for: #{@listname}") unless Schleuder.log.nil?
|
150
|
+
@config = ListConfig.new(File.join(listdir, Schleuder.config.lists_configfile),fromfile)
|
151
|
+
end
|
152
|
+
|
153
|
+
def find_by_key(ary, key)
|
154
|
+
return false unless key.kind_of?(GPGME::Key)
|
155
|
+
res = ary.detect { |elem| elem.kind_of?(Member) && elem.uses_key?(key) }
|
156
|
+
Schleuder.log.debug "Found #{res} for #{key}" unless res.nil?
|
157
|
+
res || false
|
158
|
+
end
|
159
|
+
|
160
|
+
def _write(data,filename)
|
161
|
+
File.open(filename, 'w') { |f| f << data }
|
162
|
+
end
|
163
|
+
|
164
|
+
def lookup_list_key
|
165
|
+
key, msg = crypt.get_key(config.key_fingerprint||config.myaddr)
|
166
|
+
unless key
|
167
|
+
raise "Could not find a key for this List! Reason: #{msg}"
|
168
|
+
end
|
169
|
+
key
|
170
|
+
end
|
171
|
+
|
172
|
+
def crypt
|
173
|
+
@@crypt ||= Crypt.new(nil)
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# the list config class - a simple container
|
2
|
+
|
3
|
+
module Schleuder
|
4
|
+
class ListConfig < Storage
|
5
|
+
|
6
|
+
# Options and their defaults
|
7
|
+
# If you want to change the defaults, edit conf/default-list.conf
|
8
|
+
|
9
|
+
# Emailaddress of the list
|
10
|
+
schleuder_attr :myaddr, ''
|
11
|
+
|
12
|
+
# Realname of this list address (mainly used for gpg key)
|
13
|
+
schleuder_attr :myname, ''
|
14
|
+
|
15
|
+
# Listadmin's emailaddress(es). Must be an array.
|
16
|
+
schleuder_attr :admins, []
|
17
|
+
|
18
|
+
# Default mime setting
|
19
|
+
schleuder_attr :default_mime, 'MIME'
|
20
|
+
|
21
|
+
# The gpg password
|
22
|
+
schleuder_attr :gpg_password, nil
|
23
|
+
|
24
|
+
# The fingerprint of the key used for this list
|
25
|
+
schleuder_attr :key_fingerprint, nil
|
26
|
+
|
27
|
+
# Wether sending emails in the clear is allowed or not.
|
28
|
+
schleuder_attr :send_encrypted_only, false
|
29
|
+
|
30
|
+
# Wether to accept only incoming emails that are encrypted
|
31
|
+
schleuder_attr :receive_encrypted_only, false
|
32
|
+
|
33
|
+
# Wether to accept only emails that are validly signed
|
34
|
+
schleuder_attr :receive_signed_only, false
|
35
|
+
|
36
|
+
# Wether to accept only emails that are validly signed by a list-member's key
|
37
|
+
schleuder_attr :receive_authenticated_only, false
|
38
|
+
|
39
|
+
# Wether to accept only emails that are validly signed by a list-admin's key
|
40
|
+
schleuder_attr :receive_admin_only, false
|
41
|
+
|
42
|
+
# Whether to accept only emails that are sent from a members address.
|
43
|
+
# NOTE: better rely on :receive_authenticated_only and ignore that option.
|
44
|
+
schleuder_attr :receive_from_member_emailaddresses_only, false
|
45
|
+
|
46
|
+
# Wether to keep the msgid or not
|
47
|
+
schleuder_attr :keep_msgid, true
|
48
|
+
|
49
|
+
# Footer for outgoing mails
|
50
|
+
schleuder_attr :public_footer, ''
|
51
|
+
|
52
|
+
# Subject prefix for incoming (signed) mails from listmembers
|
53
|
+
schleuder_attr :prefix, ''
|
54
|
+
|
55
|
+
# Subject prefix for incoming mails
|
56
|
+
schleuder_attr :prefix_in, ''
|
57
|
+
|
58
|
+
# Subject prefix for outgoing mails
|
59
|
+
schleuder_attr :prefix_out, ''
|
60
|
+
|
61
|
+
# The log_level (ERROR || WARN || INFO || DEBUG)
|
62
|
+
schleuder_attr :log_level, 'ERROR'
|
63
|
+
|
64
|
+
# Log to SYSLOG?
|
65
|
+
schleuder_attr :log_syslog, false
|
66
|
+
|
67
|
+
# Log to IO (writing into STDIN of another process/executable)
|
68
|
+
schleuder_attr :log_io, false
|
69
|
+
|
70
|
+
# Log to a file? If the path doesn't start with a slash the list-dir will
|
71
|
+
# be prefixed.
|
72
|
+
schleuder_attr :log_file, 'list.log'
|
73
|
+
|
74
|
+
# Which headers from original mail to include into the internal meta data
|
75
|
+
schleuder_attr :headers_to_meta, [:from, :to, :cc, :date]
|
76
|
+
|
77
|
+
# Restrict specific plugins to admin
|
78
|
+
schleuder_attr :keywords_admin_only, ['SAVE-MEMBERS', 'DEL-KEY']
|
79
|
+
|
80
|
+
# Notify admin if these keywords triggered commands.
|
81
|
+
schleuder_attr :keywords_admin_notify, [ 'X-ADD-KEY' ]
|
82
|
+
|
83
|
+
# Drop any bounces (incoming email not passing the receive_*_only-rules)
|
84
|
+
schleuder_attr :bounces_drop_all, false
|
85
|
+
|
86
|
+
# Drop bounces if they match one of these headers. Must be a hash, keys and values are case insensitive.
|
87
|
+
schleuder_attr :bounces_drop_on_headers, {'x-spam-flag' => 'yes'}
|
88
|
+
|
89
|
+
# Send a notice to admin(s) on bouncing or dropping
|
90
|
+
schleuder_attr :bounces_notify_admin, true
|
91
|
+
|
92
|
+
# Include RFC-compliant List-* Headers into member mails
|
93
|
+
schleuder_attr :include_list_headers, true
|
94
|
+
|
95
|
+
# Include OpenPGP-Header
|
96
|
+
schleuder_attr :include_openpgp_header, true
|
97
|
+
# Preferred way to receive emails to note in OpenPGP-Header ('sign'|'encrypt'|'signencrypt'|'unprotected'|'none')
|
98
|
+
# 'none' to not include a preference
|
99
|
+
# default: 'signencrypt'
|
100
|
+
schleuder_attr :openpgp_header_preference, 'signencrypt'
|
101
|
+
|
102
|
+
# If we want to dump the original incoming mail.
|
103
|
+
# ATTENTION: this stores the incoming e-mail on disk!
|
104
|
+
schleuder_attr :dump_incoming_mail, false
|
105
|
+
|
106
|
+
# Maximum size of message allowed on the list in kilobyte. All others will be bounced.
|
107
|
+
schleuder_attr :max_message_size, 10240 # 10MB
|
108
|
+
|
109
|
+
# Whether to archive messages sent to list members or not.
|
110
|
+
# default: false
|
111
|
+
schleuder_attr :archive, false
|
112
|
+
|
113
|
+
### END OF CONFIG OPTIONS
|
114
|
+
|
115
|
+
def initialize(config_file, fromfile=true)
|
116
|
+
# First Overload with default-list.conf then load our config
|
117
|
+
overload_from_file!(Schleuder.config.lists_default_conf)
|
118
|
+
# overload with config_file
|
119
|
+
super(config_file, fromfile)
|
120
|
+
|
121
|
+
# load admins as members
|
122
|
+
self.admins = self.admins
|
123
|
+
# compress fingerprint
|
124
|
+
self.key_fingerprint = self.key_fingerprint
|
125
|
+
end
|
126
|
+
|
127
|
+
def key_fingerprint=(fpr)
|
128
|
+
schleuder_attributes['key_fingerprint'] = Schleuder::Utils.compress_fingerprint(fpr)
|
129
|
+
end
|
130
|
+
|
131
|
+
def admins=(ary)
|
132
|
+
schleuder_attributes['admins'] = Array(ary).collect { |mem|
|
133
|
+
if mem.kind_of?(Member)
|
134
|
+
mem
|
135
|
+
else
|
136
|
+
if mem.kind_of?(Hash) && mem.has_key?("email")
|
137
|
+
Member.new(mem)
|
138
|
+
else
|
139
|
+
Schleuder.log.error "Wrong input: #{mem.inspect} is not suitable data for a Member."
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
}.compact
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|