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 'goliath'
 26require 'net/http'
 27require 'redis/connection/hiredis'
 28require 'uri'
 29require 'webrick'
 30
 31if ARGV.size != 3
 32	puts "Usage: mpx-catapult.rb <http_listen_port> "\
 33		"<redis_hostname> <redis_port>"
 34	exit 0
 35end
 36
 37t = Time.now
 38puts "LOG %d.%09d: starting...\n\n" % [t.to_i, t.nsec]
 39
 40class WebhookHandler < Goliath::API
 41	def response(env)
 42		puts 'ENV: ' + env.to_s
 43		puts 'path: ' + env['REQUEST_PATH']
 44		puts 'method: ' + env['REQUEST_METHOD']
 45		puts 'BODY: ' + Rack::Request.new(env).body.read
 46
 47		cred_key = "catapult_cred-" + WEBrick::HTTPUtils.unescape(
 48			env['REQUEST_PATH'].split('/', 3)[1])
 49
 50		# TODO: connect at start of program instead
 51		conn = Hiredis::Connection.new
 52		begin
 53			conn.connect(ARGV[1], ARGV[2].to_i)
 54		rescue => e
 55			puts 'ERROR: Redis connection failed: ' + e.inspect
 56			return [
 57				500,
 58				{'Content-Type' => 'text/plain'},
 59				e.inspect
 60			]
 61		end
 62
 63		conn.write ["EXISTS", cred_key]
 64		if conn.read == 0
 65			conn.disconnect
 66
 67			puts 'ERROR: invalid path rqst: ' + env['REQUEST_PATH']
 68			return [
 69				404,
 70				{'Content-Type' => 'text/plain'},
 71				'not found'
 72			]
 73		end
 74
 75		conn.write ["LRANGE", cred_key, 0, 2]
 76		user_id, api_token, api_secret = conn.read
 77		conn.disconnect
 78
 79		uri = URI.parse('https://api.catapult.inetwork.com')
 80		http = Net::HTTP.new(uri.host, uri.port)
 81		http.use_ssl = true
 82		request = ''
 83		if env['REQUEST_METHOD'] == 'GET'
 84			request = Net::HTTP::Get.new('/v1/users/' + user_id +
 85				'/media/' +env['REQUEST_PATH'].split('/', 3)[2])
 86		elsif env['REQUEST_METHOD'] == 'HEAD'
 87			request = Net::HTTP::Head.new('/v1/users/' + user_id +
 88				'/media/' +env['REQUEST_PATH'].split('/', 3)[2])
 89		else
 90			puts 'ERROR: received non-HEAD/-GET request'
 91			return [
 92				500,
 93				{'Content-Type' => 'text/plain'},
 94				e.inspect
 95			]
 96		end
 97		request.basic_auth api_token, api_secret
 98		response = http.request(request)
 99
100		puts 'API response to send: ' + response.to_s + ' with code ' +
101			response.code + ', body <omitted_due_to_length>'
102
103		if response.code != '200'
104			puts 'ERROR: unexpected return code ' + response.code
105
106			if response.code == '404'
107				return [
108					404,
109					{'Content-Type' => 'text/plain'},
110					'not found'
111				]
112			end
113
114			return [
115				response.code,
116				{'Content-Type' => 'text/plain'},
117				'unexpected error'
118			]
119		end
120
121		# TODO: maybe need to reflect more headers (multi-part?)
122		[200, {'Content-Length' => response['content-length']},
123			response.body]
124	end
125end
126
127EM.run do
128	server = Goliath::Server.new('0.0.0.0', ARGV[0].to_i)
129	server.api = WebhookHandler.new
130	server.app = Goliath::Rack::Builder.build(server.api.class, server.api)
131	server.logger = Log4r::Logger.new('goliath')
132	server.logger.add(Log4r::StdoutOutputter.new('console'))
133	server.logger.level = Log4r::INFO
134	server.start
135end