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,28 @@
1
+ # Use this as a table for postfix to select addresses that schleuder
2
+ # thinks belong to it. This is useful when
3
+ # smtpd_reject_unlisted_recipient = yes (which is the default for
4
+ # modern Postfix)
5
+
6
+ # For example, you might dedicate Postfix's "virtual" domains to
7
+ # schleuder with the following set of configs in main.cf:
8
+ #
9
+ # virtual_domains = lists.example.org
10
+ # virtual_transport = schleuder
11
+ # virtual_alias_maps = hash:/etc/postfix/virtual_aliases
12
+ # virtual_mailbox_maps = sqlite:/etc/postfix/schleuder_sqlite.cf
13
+ # schleuder_destination_recipient_limit = 1
14
+
15
+ # it is not recommended to use this table for more powerful
16
+ # configuration options (e.g. transport_maps) because it could give
17
+ # the schleuder user (which can write the given sqlite database) the
18
+ # power to change settings for for other mail handled by this Postfix
19
+ # instance.
20
+
21
+ dbpath = /var/lib/schleuder/db.sqlite
22
+
23
+ query = select 'present' from lists
24
+ where email = '%s'
25
+ or email = replace('%s', '-bounce@', '@')
26
+ or email = replace('%s', '-owner@', '@')
27
+ or email = replace('%s', '-request@', '@')
28
+ or email = replace('%s', '-sendkey@', '@')
@@ -0,0 +1,10 @@
1
+ [Unit]
2
+ Description=Schleuder API daemon
3
+ After=local-fs.target network.target
4
+
5
+ [Service]
6
+ ExecStart=/usr/local/bin/schleuder-api-daemon
7
+ User=schleuder
8
+
9
+ [Install]
10
+ WantedBy=multi-user.target
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+
3
+ test -x /usr/local/bin/schleuder || exit 0
4
+
5
+ su -s /bin/sh schleuder -c "/usr/local/bin/schleuder refresh_keys"
6
+ su -s /bin/sh schleuder -c "/usr/local/bin/schleuder check_keys"
@@ -0,0 +1,61 @@
1
+ # Where are the list-directories stored (contain log-files and GnuPG-keyrings).
2
+ lists_dir: /var/lib/schleuder/lists
3
+
4
+ # Where to write list-logs. The actual log-file will be <lists_logs_base_dir>/<hostname>/<listname>/list.log.
5
+ listlogs_dir: /var/lib/schleuder/lists
6
+
7
+ # Schleuder reads plugins also from this directory.
8
+ plugins_dir: /etc/schleuder/plugins
9
+
10
+ # How verbose should Schleuder log to syslog? (list-specific messages are written to the list's log-file).
11
+ log_level: warn
12
+
13
+ # Which keyserver to refresh keys from (used by `schleuder refresh_keys`, meant
14
+ # to be run from cron weekly).
15
+ # If you have gnupg 2.1, we strongly suggest to use a hkps-keyserver:
16
+ #keyserver: hkps://hkps.pool.sks-keyservers.net
17
+ # If you have gnupg 2.1 and TOR running locally, use a onion-keyserver:
18
+ #keyserver: hkp://jirk5u4osbsr34t5.onion
19
+ # If you have an OS-wide defined keyserver, specify a blank value to have that
20
+ # one used:
21
+ #keyserver:
22
+ # The default works for all supported versions of gnupg:
23
+ keyserver: pool.sks-keyservers.net
24
+
25
+ # Who is maintaining the overall schleuder installation and should be
26
+ # notified about severe problems with lists.
27
+ # This address should be a postmaster-like account, especially it should
28
+ # not be another schleuder list.
29
+ # Is also used as an envelope sender of admin notifications.
30
+ superadmin: root@localhost
31
+
32
+ # For these options see documentation for ActionMailer::smtp_settings, e.g. <http://api.rubyonrails.org/classes/ActionMailer/Base.html>.
33
+ smtp_settings:
34
+ address: localhost
35
+ port: 25
36
+ #domain:
37
+ #enable_starttls_auto:
38
+ #openssl_verify_mode:
39
+ #authentication:
40
+ #user_name:
41
+ #password:
42
+
43
+ # The database to use. Unless you want to run the tests you only need the `production`-section.
44
+ database:
45
+ production:
46
+ adapter: 'sqlite3'
47
+ database: /var/lib/schleuder/db.sqlite
48
+ timeout: 5000
49
+
50
+ api:
51
+ host: localhost
52
+ port: 4443
53
+ # Certificate and key to use. You can create new ones with `schleuder cert generate`.
54
+ tls_cert_file: /etc/schleuder/schleuder-certificate.pem
55
+ tls_key_file: /etc/schleuder/schleuder-private-key.pem
56
+ # List of api_keys to allow access to the API.
57
+ # Example:
58
+ # valid_api_keys:
59
+ # - abcdef...
60
+ # - zyxwvu...
61
+ valid_api_keys:
@@ -0,0 +1,420 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Make sinatra use production as default-environment
4
+ ENV['RACK_ENV'] ||= 'production'
5
+
6
+ require 'sinatra/base'
7
+ require 'sinatra/json'
8
+ require 'sinatra/namespace'
9
+ require 'thin'
10
+ require_relative '../lib/schleuder.rb'
11
+
12
+
13
+ %w[tls_cert_file tls_key_file].each do |config_key|
14
+ path = Conf.api[config_key]
15
+ if ! File.readable?(path)
16
+ $stderr.puts "Error: '#{path}' is not a readable file (from #{config_key} in config)."
17
+ exit 1
18
+ end
19
+ end
20
+
21
+ class SchleuderApiDaemon < Sinatra::Base
22
+ register Sinatra::Namespace
23
+
24
+ configure do
25
+ set :server, :thin
26
+ set :port, Schleuder::Conf.api['port'] || 4443
27
+ set :bind, Schleuder::Conf.api['host'] || 'localhost'
28
+ if settings.development?
29
+ set :logging, Logger::DEBUG
30
+ else
31
+ set :logging, Logger::WARN
32
+ end
33
+ end
34
+
35
+ before do
36
+ authenticate!
37
+ cast_param_values
38
+ end
39
+
40
+ after do
41
+ # Return connection to pool after each request.
42
+ ActiveRecord::Base.connection.close
43
+ end
44
+
45
+ error do
46
+ exc = env['sinatra.error']
47
+ logger.error "Error: #{env['sinatra.error'].message}"
48
+ case exc
49
+ when Errno::EACCES
50
+ server_error(exc.message)
51
+ else
52
+ client_error(exc.to_s)
53
+ end
54
+ end
55
+
56
+ error 404 do
57
+ 'Not found'
58
+ end
59
+
60
+ get '/status.json' do
61
+ json status: :ok
62
+ end
63
+
64
+ get '/version.json' do
65
+ json version: Schleuder::VERSION
66
+ end
67
+
68
+ helpers do
69
+ def valid_credentials?
70
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
71
+ if @auth.provided? && @auth.basic? && @auth.credentials.present?
72
+ username, api_key = @auth.credentials
73
+ username == 'schleuder' && Conf.api_valid_api_keys.include?(api_key)
74
+ else
75
+ false
76
+ end
77
+ end
78
+
79
+ def authenticate!
80
+ # Be careful to use path_info() — it can be changed by other filters!
81
+ return if request.path_info == '/status.json'
82
+ if ! valid_credentials?
83
+ headers['WWW-Authenticate'] = 'Basic realm="Schleuder API Daemon"'
84
+ halt 401, "Not authorized\n"
85
+ end
86
+ end
87
+
88
+ def list(id_or_email=nil)
89
+ if id_or_email.blank?
90
+ if params[:list_id].present?
91
+ id_or_email = params[:list_id]
92
+ else
93
+ client_error "Parameter list_id is required"
94
+ end
95
+ end
96
+ if is_an_integer?(id_or_email)
97
+ list = List.where(id: id_or_email).first
98
+ else
99
+ # list_id is actually an email address
100
+ list = List.where(email: id_or_email).first
101
+ end
102
+ list || halt(404)
103
+ end
104
+
105
+ def subscription(id_or_email)
106
+ if is_an_integer?(id_or_email)
107
+ sub = Subscription.where(id: id_or_email.to_i).first
108
+ else
109
+ # Email
110
+ if params[:list_id].blank?
111
+ client_error "Parameter list_id is required when using email as identifier for subscriptions."
112
+ else
113
+ sub = list.subscriptions.where(email: id_or_email).first
114
+ end
115
+ end
116
+ sub || halt(404)
117
+ end
118
+
119
+ def requested_list_id
120
+ # ActiveResource doesn't want to use query-params with create(), so here
121
+ # list_id might be included in the request-body.
122
+ params['list_id'] || parsed_body['list_id'] || client_error('Need list_id')
123
+ end
124
+
125
+ def parsed_body
126
+ @parsed_body ||= begin
127
+ b = JSON.parse(request.body.read)
128
+ logger.debug "parsed body: #{b.inspect}"
129
+ b
130
+ end
131
+ end
132
+
133
+ def server_error(msg)
134
+ logger.warn msg
135
+ halt(500, json(error: msg))
136
+ end
137
+
138
+ # 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>.
139
+ def client_error(obj_or_msg, http_code=400)
140
+ text = case obj_or_msg
141
+ when String, Symbol
142
+ obj_or_msg.to_s
143
+ when ActiveRecord::Base
144
+ obj_or_msg.errors.full_messages
145
+ else
146
+ obj_or_msg
147
+ end
148
+ logger.error "Sending error to client: #{text.inspect}"
149
+ halt(http_code, json(errors: text))
150
+ end
151
+
152
+ # poor persons type casting
153
+ def cast_param_values
154
+ params.each do |key, value|
155
+ params[key] =
156
+ case value
157
+ when 'true' then true
158
+ when 'false' then false
159
+ when '0' then 0
160
+ when is_an_integer?(value) then value.to_i
161
+ else value
162
+ end
163
+ end
164
+ end
165
+
166
+ def key_to_hash(key, include_keydata=false)
167
+ hash = {
168
+ fingerprint: key.fingerprint,
169
+ email: key.email,
170
+ expiry: key.expires,
171
+ generated_at: key.generated_at,
172
+ primary_uid: key.primary_uid.uid,
173
+ oneline: key.oneline,
174
+ trust_issues: key.usability_issue
175
+ }
176
+ if include_keydata
177
+ hash[:description] = key.to_s
178
+ hash[:ascii] = key.armored
179
+ end
180
+ hash
181
+ end
182
+
183
+ def set_x_messages(messages)
184
+ if messages.present?
185
+ headers 'X-Messages' => Array(messages).join(' // ').gsub(/\n/, ' // ')
186
+ end
187
+ end
188
+
189
+ def find_key_material
190
+ key_material = parsed_body['key_material'].presence
191
+ # By convention key_material is either ASCII or base64-encoded.
192
+ if key_material && ! key_material.match('BEGIN PGP')
193
+ key_material = Base64.decode64(key_material)
194
+ end
195
+ key_material
196
+ end
197
+
198
+ def find_attributes_from_body(attribs)
199
+ Array(attribs).inject({}) do |memo, attrib|
200
+ if parsed_body.has_key?(attrib)
201
+ memo[attrib] = parsed_body[attrib]
202
+ end
203
+ memo
204
+ end
205
+ end
206
+
207
+ def is_an_integer?(input)
208
+ input.to_s.match(/^[0-9]+$/).present?
209
+ end
210
+ end
211
+
212
+ namespace '/lists' do
213
+ get '.json' do
214
+ json List.all, include: :subscriptions
215
+ end
216
+
217
+ post '.json' do
218
+ listname = parsed_body['email']
219
+ fingerprint = parsed_body['fingerprint']
220
+ adminaddress = parsed_body['adminaddress']
221
+ adminfingerprint = parsed_body['adminfingerprint']
222
+ adminkey = parsed_body['adminkey']
223
+ list, messages = ListBuilder.new({email: listname, fingerprint: fingerprint}, adminaddress, adminfingerprint, adminkey).run
224
+ if list.nil?
225
+ client_error(messages, 422)
226
+ elsif ! list.valid?
227
+ client_error(list, 422)
228
+ else
229
+ set_x_messages(messages)
230
+ body json(list)
231
+ end
232
+ end
233
+
234
+ get '/configurable_attributes.json' do
235
+ json(List.configurable_attributes) + "\n"
236
+ end
237
+
238
+ post '/send_list_key_to_subscriptions.json' do
239
+ json(result: list.send_list_key_to_subscriptions)
240
+ end
241
+
242
+ get '/new.json' do
243
+ json List.new
244
+ end
245
+
246
+ get '/:id.json' do |id|
247
+ json list(id)
248
+ end
249
+
250
+ put '/:id.json' do |id|
251
+ list = list(id)
252
+ if list.update(parsed_body)
253
+ 204
254
+ else
255
+ client_error(list)
256
+ end
257
+ end
258
+
259
+ patch '/:id.json' do |id|
260
+ list = list(id)
261
+ if list.update(parsed_body)
262
+ 204
263
+ else
264
+ client_error(list)
265
+ end
266
+ end
267
+
268
+ delete '/:id.json' do |id|
269
+ list = list(id)
270
+ if list.destroy
271
+ 200
272
+ else
273
+ client_error(list)
274
+ end
275
+ end
276
+ end
277
+
278
+ namespace '/subscriptions' do
279
+ get '.json' do
280
+ filterkeys = Subscription.configurable_attributes + [:list_id, :email]
281
+ filter = params.select do |param|
282
+ filterkeys.include?(param.to_sym)
283
+ end
284
+
285
+ logger.debug "Subscription filter: #{filter.inspect}"
286
+ if filter['list_id'] && ! is_an_integer?(filter['list_id'])
287
+ # Value is an email-address
288
+ if list = List.where(email: filter['list_id']).first
289
+ filter['list_id'] = list.id
290
+ else
291
+ status 404
292
+ return json(errors: 'No such list')
293
+ end
294
+ end
295
+
296
+ json Subscription.where(filter)
297
+ end
298
+
299
+ post '.json' do
300
+ begin
301
+ list = list(requested_list_id)
302
+ # We don't have to care about nil-values, subscribe() does that for us.
303
+ sub, msgs = list.subscribe(
304
+ parsed_body['email'],
305
+ parsed_body['fingerprint'],
306
+ parsed_body['admin'],
307
+ parsed_body['delivery_enabled'],
308
+ find_key_material
309
+ )
310
+ set_x_messages(msgs)
311
+ logger.debug "subcription: #{sub.inspect}"
312
+ if sub.valid?
313
+ logger.debug "Subscribed: #{sub.inspect}"
314
+ # TODO: why redirect instead of respond with result?
315
+ redirect to("/subscriptions/#{sub.id}.json"), 201
316
+ else
317
+ client_error(sub, 422)
318
+ end
319
+ rescue ActiveRecord::RecordNotUnique
320
+ logger.error "Already subscribed"
321
+ status 422
322
+ json errors: {email: ['is already subscribed']}
323
+ end
324
+ end
325
+
326
+ get '/configurable_attributes.json' do
327
+ json(Subscription.configurable_attributes) + "\n"
328
+ end
329
+
330
+ get '/new.json' do
331
+ json Subscription.new
332
+ end
333
+
334
+ get '/:id.json' do |id|
335
+ json subscription(id)
336
+ end
337
+
338
+ put '/:id.json' do |id|
339
+ sub = subscription(id)
340
+ list = sub.list
341
+ args = find_attributes_from_body(%w[email fingerprint admin delivery_enabled])
342
+ fingerprint, messages = list.import_key_and_find_fingerprint(find_key_material)
343
+ set_x_messages(messages)
344
+ # For an already existing subscription, only update fingerprint if a
345
+ # new one has been selected from the upload.
346
+ if fingerprint.present?
347
+ args["fingerprint"] = fingerprint
348
+ end
349
+ if sub.update(args)
350
+ 200
351
+ else
352
+ client_error(sub, 422)
353
+ end
354
+ end
355
+
356
+ patch '/:id.json' do |id|
357
+ sub = subscription(id)
358
+ if sub.update(parsed_body)
359
+ 200
360
+ else
361
+ client_error(sub)
362
+ end
363
+ end
364
+
365
+ delete '/:id.json' do |id|
366
+ if sub = subscription(id).destroy
367
+ 200
368
+ else
369
+ client_error(sub)
370
+ end
371
+ end
372
+ end
373
+
374
+ namespace '/keys' do
375
+ get '.json' do
376
+ keys = list.keys.sort_by(&:email).map do |key|
377
+ key_to_hash(key)
378
+ end
379
+ json keys
380
+ end
381
+
382
+ post '.json' do
383
+ input = parsed_body['keymaterial']
384
+ if ! input.match('BEGIN PGP')
385
+ input = Base64.decode64(input)
386
+ end
387
+ json list(requested_list_id).import_key(input)
388
+ end
389
+
390
+ get '/check_keys.json' do
391
+ json result: list.check_keys
392
+ end
393
+
394
+ get '/:fingerprint.json' do |fingerprint|
395
+ if key = list.key(fingerprint)
396
+ json key_to_hash(key, true)
397
+ else
398
+ 404
399
+ end
400
+ end
401
+
402
+ delete '/:fingerprint.json' do |fingerprint|
403
+ if list.delete_key(fingerprint)
404
+ 200
405
+ else
406
+ 404
407
+ end
408
+ end
409
+ end
410
+
411
+ def self.run!
412
+ super do |server|
413
+ server.ssl = true
414
+ server.ssl_options = {
415
+ :cert_chain_file => Conf.api['tls_cert_file'],
416
+ :private_key_file => Conf.api['tls_key_file']
417
+ }
418
+ end
419
+ end
420
+ end