mpx-catapult.rb

  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