schleuder 2.2.4 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +138 -0
  3. data/Rakefile +136 -0
  4. data/bin/pinentry-clearpassphrase +72 -0
  5. data/bin/schleuder +9 -89
  6. data/bin/schleuder-api-daemon +4 -0
  7. data/db/migrate/20140501103532_create_lists.rb +39 -0
  8. data/db/migrate/20140501112859_create_subscriptions.rb +21 -0
  9. data/db/migrate/201508092100_add_language_to_lists.rb +11 -0
  10. data/db/migrate/20150812165700_change_keywords_admin_only_defaults.rb +8 -0
  11. data/db/migrate/20150813235800_add_forward_all_incoming_to_admins.rb +11 -0
  12. data/db/migrate/201508141727_change_send_encrypted_only_default.rb +8 -0
  13. data/db/migrate/201508222143_add_logfiles_to_keep_to_lists.rb +11 -0
  14. data/db/migrate/201508261723_rename_delivery_disabled_to_delivery_enabled_and_change_default.rb +14 -0
  15. data/db/migrate/201508261815_strip_gpg_passphrase.rb +11 -0
  16. data/db/migrate/201508261827_remove_default_mime.rb +9 -0
  17. data/db/migrate/20160501172700_fix_headers_to_meta_defaults.rb +8 -0
  18. data/db/migrate/20170713215059_add_internal_footer_to_list.rb +11 -0
  19. data/db/schema.rb +62 -0
  20. data/etc/init.d/schleuder-api-daemon +87 -0
  21. data/etc/list-defaults.yml +123 -0
  22. data/etc/postfix/schleuder_sqlite.cf +28 -0
  23. data/etc/schleuder-api-daemon.service +10 -0
  24. data/etc/schleuder.cron.weekly +6 -0
  25. data/etc/schleuder.yml +61 -0
  26. data/lib/schleuder-api-daemon.rb +420 -0
  27. data/lib/schleuder.rb +81 -47
  28. data/lib/schleuder/cli.rb +334 -0
  29. data/lib/schleuder/cli/cert.rb +24 -0
  30. data/lib/schleuder/cli/schleuder_cert_manager.rb +84 -0
  31. data/lib/schleuder/cli/subcommand_fix.rb +11 -0
  32. data/lib/schleuder/conf.rb +131 -0
  33. data/lib/schleuder/errors/active_model_error.rb +15 -0
  34. data/lib/schleuder/errors/base.rb +17 -0
  35. data/lib/schleuder/errors/decryption_failed.rb +16 -0
  36. data/lib/schleuder/errors/fatal_error.rb +13 -0
  37. data/lib/schleuder/errors/file_not_found.rb +14 -0
  38. data/lib/schleuder/errors/invalid_listname.rb +13 -0
  39. data/lib/schleuder/errors/key_adduid_failed.rb +13 -0
  40. data/lib/schleuder/errors/key_generation_failed.rb +16 -0
  41. data/lib/schleuder/errors/keyword_admin_only.rb +13 -0
  42. data/lib/schleuder/errors/list_exists.rb +13 -0
  43. data/lib/schleuder/errors/list_not_found.rb +14 -0
  44. data/lib/schleuder/errors/list_property_missing.rb +14 -0
  45. data/lib/schleuder/errors/listdir_problem.rb +16 -0
  46. data/lib/schleuder/errors/loading_list_settings_failed.rb +14 -0
  47. data/lib/schleuder/errors/message_empty.rb +14 -0
  48. data/lib/schleuder/errors/message_not_from_admin.rb +13 -0
  49. data/lib/schleuder/errors/message_sender_not_subscribed.rb +13 -0
  50. data/lib/schleuder/errors/message_too_big.rb +14 -0
  51. data/lib/schleuder/errors/message_unauthenticated.rb +13 -0
  52. data/lib/schleuder/errors/message_unencrypted.rb +13 -0
  53. data/lib/schleuder/errors/message_unsigned.rb +13 -0
  54. data/lib/schleuder/errors/standard_error.rb +5 -0
  55. data/lib/schleuder/errors/too_many_keys.rb +17 -0
  56. data/lib/schleuder/errors/unknown_list_option.rb +14 -0
  57. data/lib/schleuder/filters/auth_filter.rb +39 -0
  58. data/lib/schleuder/filters/bounces_filter.rb +12 -0
  59. data/lib/schleuder/filters/forward_filter.rb +17 -0
  60. data/lib/schleuder/filters/forward_incoming.rb +13 -0
  61. data/lib/schleuder/filters/hotmail_message_filter.rb +25 -0
  62. data/lib/schleuder/filters/max_message_size.rb +14 -0
  63. data/lib/schleuder/filters/request_filter.rb +26 -0
  64. data/lib/schleuder/filters/send_key_filter.rb +20 -0
  65. data/lib/schleuder/filters/strip_alternative_filter.rb +21 -0
  66. data/lib/schleuder/filters_runner.rb +83 -0
  67. data/lib/schleuder/gpgme/ctx.rb +274 -0
  68. data/lib/schleuder/gpgme/import_status.rb +27 -0
  69. data/lib/schleuder/gpgme/key.rb +212 -0
  70. data/lib/schleuder/gpgme/sub_key.rb +13 -0
  71. data/lib/schleuder/gpgme/user_id.rb +22 -0
  72. data/lib/schleuder/list.rb +318 -127
  73. data/lib/schleuder/list_builder.rb +139 -0
  74. data/lib/schleuder/listlogger.rb +31 -0
  75. data/lib/schleuder/logger.rb +23 -0
  76. data/lib/schleuder/logger_notifications.rb +69 -0
  77. data/lib/schleuder/mail/message.rb +482 -0
  78. data/lib/schleuder/mail/parts_list.rb +9 -0
  79. data/lib/schleuder/plugin_runners/base.rb +91 -0
  80. data/lib/schleuder/plugin_runners/list_plugins_runner.rb +24 -0
  81. data/lib/schleuder/plugin_runners/request_plugins_runner.rb +27 -0
  82. data/lib/schleuder/plugins/attach_listkey.rb +17 -0
  83. data/lib/schleuder/plugins/get_version.rb +7 -0
  84. data/lib/schleuder/plugins/key_management.rb +113 -0
  85. data/lib/schleuder/plugins/list_management.rb +15 -0
  86. data/lib/schleuder/plugins/resend.rb +196 -0
  87. data/lib/schleuder/plugins/sign_this.rb +46 -0
  88. data/lib/schleuder/plugins/subscription_management.rb +140 -0
  89. data/lib/schleuder/runner.rb +130 -0
  90. data/lib/schleuder/subscription.rb +98 -0
  91. data/lib/schleuder/validators/boolean_validator.rb +7 -0
  92. data/lib/schleuder/validators/email_validator.rb +7 -0
  93. data/lib/schleuder/validators/fingerprint_validator.rb +7 -0
  94. data/lib/schleuder/validators/greater_than_zero_validator.rb +7 -0
  95. data/lib/schleuder/validators/no_line_breaks_validator.rb +7 -0
  96. data/lib/schleuder/version.rb +1 -1
  97. data/locales/de.yml +179 -0
  98. data/locales/en.yml +179 -0
  99. metadata +305 -108
  100. checksums.yaml.gz.sig +0 -3
  101. data.tar.gz.sig +0 -2
  102. data/LICENSE +0 -339
  103. data/README +0 -32
  104. data/bin/schleuder-fix-gem-dependencies +0 -37
  105. data/bin/schleuder-init-setup +0 -37
  106. data/bin/schleuder-migrate-v2.1-to-v2.2 +0 -225
  107. data/bin/schleuder-newlist +0 -413
  108. data/contrib/check-expired-keys.rb +0 -60
  109. data/contrib/mutt-schleuder-colors.rc +0 -10
  110. data/contrib/mutt-schleuder-resend.vim +0 -24
  111. data/contrib/smtpserver.rb +0 -76
  112. data/ext/default-list.conf +0 -149
  113. data/ext/default-members.conf +0 -7
  114. data/ext/list.conf.example +0 -14
  115. data/ext/schleuder.conf +0 -64
  116. data/lib/schleuder/archiver.rb +0 -46
  117. data/lib/schleuder/crypt.rb +0 -210
  118. data/lib/schleuder/errors.rb +0 -5
  119. data/lib/schleuder/list_config.rb +0 -146
  120. data/lib/schleuder/log/listlogger.rb +0 -57
  121. data/lib/schleuder/log/outputter/emailoutputter.rb +0 -120
  122. data/lib/schleuder/log/outputter/metaemailoutputter.rb +0 -50
  123. data/lib/schleuder/log/schleuderlogger.rb +0 -34
  124. data/lib/schleuder/mail.rb +0 -873
  125. data/lib/schleuder/mailer.rb +0 -26
  126. data/lib/schleuder/member.rb +0 -69
  127. data/lib/schleuder/plugin.rb +0 -54
  128. data/lib/schleuder/processor.rb +0 -363
  129. data/lib/schleuder/schleuder_config.rb +0 -75
  130. data/lib/schleuder/storage.rb +0 -84
  131. data/lib/schleuder/utils.rb +0 -80
  132. data/man/schleuder-newlist.8 +0 -174
  133. data/man/schleuder.8 +0 -416
  134. data/plugins/README +0 -20
  135. data/plugins/manage_keys_plugin.rb +0 -113
  136. data/plugins/manage_members_plugin.rb +0 -156
  137. data/plugins/manage_self_plugin.rb +0 -26
  138. data/plugins/resend_plugin.rb +0 -35
  139. data/plugins/sign_this_plugin.rb +0 -14
  140. data/plugins/version_plugin.rb +0 -12
  141. metadata.gz.sig +0 -0
@@ -0,0 +1,13 @@
1
+ module GPGME
2
+ class SubKey
3
+ # Overwrite to specify the full fingerprint instead of the short key-ID.
4
+ def to_s
5
+ sprintf("%s %4d%s/%s %s\n",
6
+ secret? ? 'ssc' : 'sub',
7
+ length,
8
+ pubkey_algo_letter,
9
+ fingerprint,
10
+ timestamp.strftime('%Y-%m-%d'))
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ module GPGME
2
+ class UserID
3
+ def name
4
+ sanitize_encoding(@name)
5
+ end
6
+ def comment
7
+ sanitize_encoding(@comment)
8
+ end
9
+ def uid
10
+ sanitize_encoding(@uid)
11
+ end
12
+
13
+ private
14
+ def sanitize_encoding(str)
15
+ if str.is_a?(String) && str.encoding != 'UTF-8'
16
+ str.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: '')
17
+ else
18
+ str
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,193 +1,384 @@
1
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 = ListConfig.new 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)
2
+ class List < ActiveRecord::Base
3
+
4
+ has_many :subscriptions, dependent: :destroy
5
+ before_destroy :delete_listdirs
6
+
7
+ serialize :headers_to_meta, JSON
8
+ serialize :bounces_drop_on_headers, JSON
9
+ serialize :keywords_admin_only, JSON
10
+ serialize :keywords_admin_notify, JSON
11
+
12
+ validates :email, presence: true, uniqueness: true, email: true
13
+ validates :fingerprint, presence: true, fingerprint: true
14
+ validates :send_encrypted_only,
15
+ :receive_encrypted_only,
16
+ :receive_signed_only,
17
+ :receive_authenticated_only,
18
+ :receive_from_subscribed_emailaddresses_only,
19
+ :receive_admin_only,
20
+ :keep_msgid,
21
+ :bounces_drop_all,
22
+ :bounces_notify_admins,
23
+ :include_list_headers,
24
+ :include_openpgp_header,
25
+ :forward_all_incoming_to_admins, boolean: true
26
+ validates_each :headers_to_meta,
27
+ :keywords_admin_only,
28
+ :keywords_admin_notify do |record, attrib, value|
29
+ value.each do |word|
30
+ if word !~ /\A[a-z_-]+\z/i
31
+ record.errors.add(attrib, I18n.t("errors.invalid_characters"))
32
+ end
33
+ end
34
+ end
35
+ validates_each :bounces_drop_on_headers do |record, attrib, value|
36
+ value.each do |key, val|
37
+ if key.to_s !~ /\A[a-z-]+\z/i || val.to_s !~ /\A[[:graph:]]+\z/i
38
+ record.errors.add(attrib, I18n.t("errors.invalid_characters"))
39
+ end
40
+ end
26
41
  end
42
+ validates :subject_prefix,
43
+ :subject_prefix_in,
44
+ :subject_prefix_out,
45
+ no_line_breaks: true
46
+ validates :openpgp_header_preference,
47
+ presence: true,
48
+ inclusion: {
49
+ in: %w(sign encrypt signencrypt unprotected none),
50
+ }
51
+ validates :max_message_size_kb, :logfiles_to_keep, greater_than_zero: true
52
+ validates :log_level,
53
+ presence: true,
54
+ inclusion: {
55
+ in: %w(debug info warn error),
56
+ }
57
+ validates :language,
58
+ presence: true,
59
+ inclusion: {
60
+ # TODO: find out why we break translations and available_locales if we use I18n.available_locales here.
61
+ in: %w(de en),
62
+ }
63
+ validates :public_footer, :internal_footer,
64
+ allow_blank: true,
65
+ format: {
66
+ with: /\A[[:graph:]\s]*\z/i,
67
+ }
68
+
69
+ default_scope { order(:email) }
70
+
71
+ def self.configurable_attributes
72
+ @configurable_attributes ||= begin
73
+ all = self.validators.map(&:attributes).flatten.uniq.compact.sort
74
+ all - [:email, :fingerprint]
27
75
  end
28
- @members
29
76
  end
30
77
 
31
- def members_file
32
- @members_file ||= File.join(listdir, Schleuder.config.lists_memberfile)
78
+ def logfile
79
+ @logfile ||= File.join(Conf.listlogs_dir, self.email.split('@').reverse, 'list.log')
80
+ end
81
+
82
+ def logger
83
+ @logger ||= Listlogger.new(self)
33
84
  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) : m }
40
- _write(YAML.dump(@members.collect { |m| m.to_hash }), members_file)
41
- @members
85
+
86
+ def to_s
87
+ email
42
88
  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
89
+
90
+ def admins
91
+ subscriptions.where(admin: true)
48
92
  end
49
93
 
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
94
+ def subscriptions_without_fingerprint
95
+ subscriptions.without_fingerprint
57
96
  end
58
97
 
59
- def find_member_by_key(key)
60
- Schleuder.log.debug "Looking for member for key #{key}"
61
- find_by_key(members, key)
98
+ def key(fingerprint=self.fingerprint)
99
+ keys(fingerprint).first
62
100
  end
63
101
 
64
- def find_admin_by_key(key)
65
- Schleuder.log.debug "Looking for admin for key #{key}"
66
- find_by_key(config.admins, key)
102
+ def secret_key
103
+ keys(self.fingerprint, true).first
67
104
  end
68
105
 
69
- def find_admin_by_address(address)
70
- config.admins.detect { |admin| admin.email == address }
106
+ def keys(identifier=nil, secret_only=nil)
107
+ gpg.find_keys(identifier, secret_only)
71
108
  end
72
109
 
73
- def find_member_by_address(address)
74
- members.detect { |member| member.email == address }
110
+ # TODO: find better name for this method. It does more than the current
111
+ # name suggests (filtering for capability).
112
+ def distinct_key(identifier)
113
+ keys = keys(identifier).select { |key| key.usable_for?(:encrypt) }
114
+ if keys.size == 1
115
+ return keys.first
116
+ else
117
+ return nil
118
+ end
75
119
  end
76
120
 
77
- # Provides the list config as Schleuder::ListConfig-object
78
- def config
79
- @config ||= _load_config
121
+ def import_key(importable)
122
+ gpg.keyimport(importable)
80
123
  end
81
124
 
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
125
+ def import_key_and_find_fingerprint(key_material)
126
+ return nil if key_material.blank?
127
+
128
+ import_result = import_key(key_material)
129
+ gpg.interpret_import_result(import_result)
130
+ end
131
+
132
+ def delete_key(fingerprint)
133
+ if key = keys(fingerprint).first
134
+ key.delete!
135
+ true
88
136
  else
89
- @config = ListConfig.new(data)
137
+ false
138
+ end
139
+ end
140
+
141
+ def export_key(fingerprint=self.fingerprint)
142
+ key = keys(fingerprint).first
143
+ if key.blank?
144
+ return false
90
145
  end
91
- _write(YAML::dump(@config.to_hash), configfile)
92
- @config
146
+ key.armored
147
+ end
148
+
149
+ def check_keys
150
+ now = Time.now
151
+ checkdate = now + (60 * 60 * 24 * 14) # two weeks
152
+ unusable = []
153
+ expiring = []
154
+
155
+ keys.each do |key|
156
+ expiry = key.subkeys.first.expires
157
+ if expiry && expiry > now && expiry < checkdate
158
+ # key expires in the near future
159
+ expdays = ((expiry - now)/86400).to_i
160
+ expiring << [key, expdays]
161
+ end
162
+
163
+ if ! key.usable?
164
+ unusable << [key, key.usability_issue]
165
+ end
166
+ end
167
+
168
+ text = ''
169
+ expiring.each do |key,days|
170
+ text << I18n.t('key_expires', {
171
+ days: days,
172
+ key_oneline: key.oneline
173
+ })
174
+ text << "\n"
175
+ end
176
+
177
+ unusable.each do |key,usability_issue|
178
+ text << I18n.t('key_unusable', {
179
+ usability_issue: usability_issue,
180
+ key_oneline: key.oneline
181
+ })
182
+ text << "\n"
183
+ end
184
+ text
185
+ end
186
+
187
+ def refresh_keys
188
+ gpg.refresh_keys(self.keys)
189
+ end
190
+
191
+ def fetch_keys(input)
192
+ gpg.fetch_key(input)
93
193
  end
94
194
 
95
- # Returns path to the list configuration file
96
- def configfile
97
- # If `lists_configfile` ends with a '/', assume its pointing to a
98
- # directory where configuration files are named "$DOMAIN/$LISTNAME.conf".
99
- if Schleuder.config.lists_configfile.end_with?('/')
100
- local_part, domain = listname.split('@')
101
- if domain.nil?
102
- # pre 2.2 style
103
- File.join(Schleuder.config.lists_configfile, "#{local_part}.conf")
195
+ def pin_keys
196
+ updated_emails = subscriptions_without_fingerprint.collect do |subscription|
197
+ key = distinct_key(subscription.email)
198
+ if key
199
+ subscription.update(fingerprint: key.fingerprint)
200
+ "#{subscription.email}: #{key.fingerprint}"
104
201
  else
105
- File.join(Schleuder.config.lists_configfile, domain, "#{local_part}.conf")
202
+ nil
106
203
  end
107
- else
108
- File.join(@listdir, Schleuder.config.lists_configfile)
109
204
  end
205
+ updated_emails.compact.join("\n")
110
206
  end
111
207
 
112
- # Builds the bounce-address for the list
113
- def bounce_addr
114
- @bounce_addr ||= self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-bounce@\2')
208
+ def self.by_recipient(recipient)
209
+ listname = recipient.gsub(/-(sendkey|request|owner|bounce)@/, '@')
210
+ where(email: listname).first
115
211
  end
116
212
 
117
- # Builds the owner-address for the list
118
- def owner_addr
119
- @owner_addr ||= self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-owner@\2')
213
+ def sendkey_address
214
+ @sendkey_address ||= email.gsub('@', '-sendkey@')
120
215
  end
121
216
 
122
- # Builds the request-address for the list
123
- def request_addr
124
- @request_addr ||= self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-request@\2')
217
+ def request_address
218
+ @request_address ||= email.gsub('@', '-request@')
125
219
  end
126
220
 
127
- # builds the send-key-command-address for the list
128
- def sendkey_addr
129
- self.config.myaddr.gsub(/^(.*)@(.*)$/, '\1-sendkey@\2')
221
+ def owner_address
222
+ @owner_address ||= email.gsub('@', '-owner@')
130
223
  end
131
224
 
132
- def self.listdir(listname)
133
- name = listname.split('@')
134
- if name.size < 2
135
- Schleuder.log.warn 'Listname should be a full email-address, using only the local part is deprecated. Please fix this!'
225
+ def bounce_address
226
+ @bounce_address ||= email.gsub('@', '-bounce@')
227
+ end
228
+
229
+ def gpg
230
+ @gpg_ctx ||= begin
231
+ # TODO: figure out why set it again...
232
+ # Set GNUPGHOME when list is created.
233
+ set_gnupg_home
234
+ GPGME::Ctx.new armor: true
235
+ end
236
+ end
237
+
238
+ # TODO: place this somewhere sensible.
239
+ # Call cleanup when script finishes.
240
+ #Signal.trap(0, proc { @list.cleanup })
241
+ def cleanup
242
+ if @gpg_agent_pid
243
+ Process.kill('TERM', @gpg_agent_pid.to_i)
136
244
  end
137
- File.expand_path(File.join([Schleuder.config.lists_dir, name.reverse].flatten))
245
+ rescue => e
246
+ $stderr.puts "Failed to kill gpg-agent: #{e}"
247
+ end
248
+
249
+ def gpg_sign_options
250
+ {sign: true, sign_as: self.fingerprint}
251
+ end
252
+
253
+ def fingerprint=(arg)
254
+ # Strip whitespace from incoming arg.
255
+ if arg
256
+ write_attribute(:fingerprint, arg.gsub(/\s*/, '').chomp)
257
+ end
258
+ end
259
+
260
+ def self.listdir(listname)
261
+ File.join(
262
+ Conf.lists_dir,
263
+ listname.split('@').reverse
264
+ )
138
265
  end
139
266
 
140
267
  def listdir
141
- @listdir ||= List.listdir(@listname)
268
+ @listdir ||= self.class.listdir(self.email)
142
269
  end
143
270
 
144
- def listid
145
- @listid ||= config.myaddr.gsub(/@/, '.')
271
+ # A convenience-method to simplify other code.
272
+ def subscribe(email, fingerprint=nil, adminflag=nil, deliveryflag=nil, key_material=nil)
273
+ messages = nil
274
+ args = {
275
+ list_id: self.id,
276
+ email: email
277
+ }
278
+ if key_material.present?
279
+ fingerprint, messages = import_key_and_find_fingerprint(key_material)
280
+ end
281
+ args[:fingerprint] = fingerprint
282
+ # ActiveRecord does not treat nil as falsy for boolean columns, so we
283
+ # have to avoid that in order to not receive an invalid object. The
284
+ # database will use the column's default-value if no value is being
285
+ # given. (I'd rather not duplicate the defaults here.)
286
+ if ! adminflag.nil?
287
+ args[:admin] = adminflag
288
+ end
289
+ if ! deliveryflag.nil?
290
+ args[:delivery_enabled] = deliveryflag
291
+ end
292
+ subscription = Subscription.create(args)
293
+ [subscription, messages]
146
294
  end
147
295
 
148
- def key
149
- @key ||= lookup_list_key
296
+ def unsubscribe(email, delete_key=false)
297
+ sub = subscriptions.where(email: email).first
298
+ if sub.blank?
299
+ false
300
+ end
301
+
302
+ if ! sub.destroy
303
+ return sub
304
+ end
305
+
306
+ if delete_key
307
+ sub.delete_key
308
+ end
150
309
  end
151
310
 
152
- def key_fingerprint
153
- key.subkeys.first.fingerprint
311
+ def keywords_admin_notify
312
+ Array(read_attribute(:keywords_admin_notify))
154
313
  end
155
314
 
156
- def archive(mail)
157
- @list_archiver ||= Schleuder::Archiver.new
158
- @list_archiver.archive(mail)
315
+ def keywords_admin_only
316
+ Array(read_attribute(:keywords_admin_only))
159
317
  end
160
318
 
161
- private
319
+ def admin_only?(keyword)
320
+ keywords_admin_only.include?(keyword)
321
+ end
162
322
 
163
- # Loads the configuration
164
- def _load_config
165
- Schleuder.log.debug("reading list-config for: #{@listname}") unless Schleuder.log.nil?
166
- @config = ListConfig.new(configfile)
323
+ def from_admin?(mail)
324
+ return false if ! mail.was_validly_signed?
325
+ admins.find do |admin|
326
+ admin.fingerprint == mail.signing_key.fingerprint
327
+ end.presence || false
167
328
  end
168
329
 
169
- def find_by_key(ary, key)
170
- return false unless key.kind_of?(GPGME::Key)
171
- res = ary.detect { |elem| elem.kind_of?(Member) && elem.uses_key?(key) }
172
- Schleuder.log.debug "Found #{res} for #{key}" unless res.nil?
173
- res || false
330
+ def set_attribute(attrib, value)
331
+ self.send("#{attrib}=", value)
174
332
  end
175
333
 
176
- def _write(data,filename)
177
- File.open(filename, 'w') { |f| f << data }
334
+ def send_list_key_to_subscriptions
335
+ mail = Mail.new
336
+ mail.from = self.email
337
+ mail.subject = I18n.t('list_public_key_subject')
338
+ mail.body = I18n.t('list_public_key_attached')
339
+ mail.attach_list_key!(self)
340
+ send_to_subscriptions(mail)
341
+ true
178
342
  end
179
343
 
180
- def lookup_list_key
181
- key, msg = crypt.get_key(config.key_fingerprint||config.myaddr)
182
- unless key
183
- raise "Could not find a key for this List! Reason: #{msg}"
344
+ def send_to_subscriptions(mail)
345
+ logger.debug "Sending to subscriptions."
346
+ mail.add_internal_footer!
347
+ self.subscriptions.each do |subscription|
348
+ begin
349
+ subscription.send_mail(mail)
350
+ rescue => exc
351
+ msg = I18n.t('errors.delivery_error',
352
+ { email: subscription.email, error: exc.to_s })
353
+ logger.error msg
354
+ logger.error exc
355
+ end
184
356
  end
185
- key
186
357
  end
187
358
 
188
- def crypt
189
- @@crypt ||= Crypt.new(nil)
359
+ private
360
+
361
+ def set_gnupg_home
362
+ ENV['GNUPGHOME'] = listdir
190
363
  end
191
364
 
365
+ def delete_listdirs
366
+ if File.exists?(self.listdir)
367
+ FileUtils.rm_rf(self.listdir, secure: true)
368
+ Schleuder.logger.info "Deleted #{self.listdir}"
369
+ end
370
+ # If listlogs_dir is different from lists_dir, the logfile still exists
371
+ # and needs to be deleted, too.
372
+ logfile_dir = File.dirname(self.logfile)
373
+ if File.exists?(logfile_dir)
374
+ FileUtils.rm_rf(logfile_dir, secure: true)
375
+ Schleuder.logger.info "Deleted #{logfile_dir}"
376
+ end
377
+ true
378
+ rescue => exc
379
+ # Don't use list-logger here — if the list-dir isn't present we can't log to it!
380
+ Schleuder.logger.error "Error while deleting listdir: #{exc}"
381
+ return false
382
+ end
192
383
  end
193
384
  end