1#!/usr/bin/env ruby
2#
3# Copyright (C) 2017 Denver Gingerich <denver@ossguy.com>
4#
5# This file is part of sgx-catapult.
6#
7# sgx-catapult is free software: you can redistribute it and/or modify it under
8# the terms of the GNU Affero General Public License as published by the Free
9# Software Foundation, either version 3 of the License, or (at your option) any
10# later version.
11#
12# sgx-catapult is distributed in the hope that it will be useful, but WITHOUT
13# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
15# details.
16#
17# You should have received a copy of the GNU Affero General Public License along
18# with sgx-catapult. If not, see <http://www.gnu.org/licenses/>.
19
20$stdout.sync = true
21
22puts "Soprani.ca/MMS Proxy for XMPP - Catapult\n"\
23 "==>> last commit of this version is " + `git rev-parse HEAD` + "\n"
24
25require 'em-hiredis'
26require 'em-http-request'
27require 'goliath'
28require 'uri'
29
30require_relative 'em_promise'
31
32t = Time.now
33puts "LOG %d.%09d: starting...\n\n" % [t.to_i, t.nsec]
34
35EM.next_tick do
36 REDIS = EM::Hiredis.connect
37end
38
39class WebhookHandler < Goliath::API
40 def media_request(env, user_id, token, secret, method, media_id)
41 if ![:get, :head].include?(method)
42 env.logger.debug 'ERROR: received non-HEAD/-GET request'
43 return EMPromise.reject(405)
44 end
45
46 EM::HttpRequest.new(
47 "https://api.catapult.inetwork.com/v1/users/"\
48 "#{user_id}/media/#{media_id}"
49 ).public_send(
50 method,
51 head: {
52 'Authorization' => [token, secret]
53 }
54 ).then { |http|
55 env.logger.debug "API response to send: #{http.response} "\
56 "with code #{http.response_header.status}"
57
58 case http.response_header.status
59 when 200
60 http
61 else
62 EMPromise.reject(http.response_header.status)
63 end
64 }
65 end
66
67 def response(env)
68 env.logger.debug 'ENV: ' + env.to_s
69 env.logger.debug 'path: ' + env['REQUEST_PATH']
70 env.logger.debug 'method: ' + env['REQUEST_METHOD']
71 env.logger.debug 'BODY: ' + Rack::Request.new(env).body.read
72
73 jid, media_id = env['REQUEST_PATH'].split('/')[-2..-1]
74 cred_key = "catapult_cred-#{URI.unescape(jid)}"
75
76 REDIS.lrange(cred_key, 0, 2).then { |creds|
77 if creds.length < 3
78 EMPromise.reject(404)
79 else
80 media_request(
81 env,
82 *creds,
83 env['REQUEST_METHOD'].downcase.to_sym,
84 media_id
85 )
86 end
87 }.then { |http|
88 clength = http.response_header['content-length']
89 [200, {'Content-Length' => clength}, http.response]
90 }.catch { |code|
91 if code.is_a?(Integer)
92 EMPromise.reject(code)
93 else
94 env.logger.error("ERROR: #{code.inspect}")
95 EMPromise.reject(500)
96 end
97 }.catch { |code|
98 [
99 code,
100 {'Content-Type' => 'text/plain;charset=utf-8'},
101 case code
102 when 404
103 "not found\n"
104 when 405
105 "only HEAD and GET are allowed\n"
106 else
107 "unexpected error\n"
108 end
109 ]
110 }.sync
111 end
112end