schleuder 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|