sgx-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
 20require 'blather/client/dsl'
 21require 'json'
 22require 'net/http'
 23require 'uri'
 24
 25if ARGV.size != 4 then
 26	puts "Usage: sgx-catapult.rb <component_jid> <component_password> " +
 27		"<server_hostname> <server_port>"
 28	exit 0
 29end
 30
 31module SGXcatapult
 32	extend Blather::DSL
 33
 34	def self.run
 35		client.run
 36	end
 37
 38	def self.error_msg(orig, query_node, type, name, text = nil)
 39		orig.add_child(query_node)
 40		orig.type = :error
 41
 42		error = Nokogiri::XML::Node.new 'error', orig.document
 43		error['type'] = type
 44		orig.add_child(error)
 45
 46		suberr = Nokogiri::XML::Node.new name, orig.document
 47		suberr['xmlns'] = 'urn:ietf:params:xml:ns:xmpp-stanzas'
 48		error.add_child(suberr)
 49
 50		# TODO: add some explanatory xml:lang='en' text (see text param)
 51		puts "RESPONSE3: #{orig.inspect}"
 52		return orig
 53	end
 54
 55	setup ARGV[0], ARGV[1], ARGV[2], ARGV[3]
 56
 57	message :chat?, :body do |m|
 58		begin
 59			puts "#{m.from.to_s} -> #{m.to.to_s} #{m.body}"
 60			msg = Blather::Stanza::Message.new(m.from, 'thx for "' +
 61				m.body + '"')
 62			msg.from = m.to
 63			write_to_stream msg
 64		rescue => e
 65			# TODO: do something better with this info
 66			say m.from, e.inspect
 67		end
 68	end
 69
 70	iq '/iq/ns:query', :ns =>
 71		'http://jabber.org/protocol/disco#items' do |i, xpath_result|
 72
 73		write_to_stream i.reply
 74	end
 75
 76	iq '/iq/ns:query', :ns =>
 77		'http://jabber.org/protocol/disco#info' do |i, xpath_result|
 78
 79		msg = i.reply
 80		msg.identities = [{:name =>
 81			'Soprani.ca Gateway to XMPP - Catapult',
 82			:type => 'sms-ctplt', :category => 'gateway'}]
 83		msg.features = ["jabber:iq:register",
 84			"jabber:iq:gateway", "jabber:iq:private",
 85			"http://jabber.org/protocol/disco#info",
 86			"http://jabber.org/protocol/commands",
 87			"http://jabber.org/protocol/muc"]
 88		write_to_stream msg
 89	end
 90
 91	iq '/iq/ns:query', :ns => 'jabber:iq:register' do |i, qn|
 92		puts "IQ: #{i.inspect}"
 93
 94		if i.type == :set
 95			xn = qn.children.find { |v| v.element_name == "x" }
 96
 97			user_id = ''
 98			api_token = ''
 99			api_secret = ''
100			phone_num = ''
101
102			if xn.nil?
103				user_id = qn.children.find {
104					|v| v.element_name == "nick" }
105				api_token = qn.children.find {
106					|v| v.element_name == "username" }
107				api_secret = qn.children.find {
108					|v| v.element_name == "password" }
109				phone_num = qn.children.find {
110					|v| v.element_name == "phone" }
111			else
112				for field in xn.children
113					if field.element_name == "field"
114						val = field.children.find { |v|
115						v.element_name == "value" }
116
117						case field['var']
118						when 'nick'
119							user_id = val.text
120						when 'username'
121							api_token = val.text
122						when 'password'
123							api_secret = val.text
124						when 'phone'
125							phone_num = val.text
126						else
127							# TODO: error
128							puts "?: " +field['var']
129						end
130					end
131				end
132			end
133
134			uri = URI.parse('https://api.catapult.inetwork.com')
135			http = Net::HTTP.new(uri.host, uri.port)
136			http.use_ssl = true
137			request = Net::HTTP::Get.new('/v1/users/' + user_id +
138				'/phoneNumbers/' + phone_num)
139			request.basic_auth api_token, api_secret
140			response = http.request(request)
141
142			puts 'API response: ' + response.to_s + ' with code ' +
143				response.code + ', body "' + response.body + '"'
144
145			if response.code == '200'
146				params = JSON.parse response.body
147				if params['numberState'] == 'enabled'
148					write_to_stream i.reply
149				else
150					# TODO: add text re number disabled
151					write_to_stream error_msg(i.reply, qn,
152						:modify, 'not-acceptable')
153				end
154			elsif response.code == '401'
155				# TODO: add text re bad credentials
156				write_to_stream error_msg(i.reply, qn, :auth,
157					'not-authorized')
158			elsif response.code == '404'
159				# TODO: add text re number not found or disabled
160				write_to_stream error_msg(i.reply, qn, :cancel,
161					'item-not-found')
162			else
163				# TODO: add text re misc error, and mention code
164				write_to_stream error_msg(i.reply, qn, :modify,
165					'not-acceptable')
166			end
167
168		elsif i.type == :get
169			orig = i.reply
170
171			msg = Nokogiri::XML::Node.new 'query',orig.document
172			msg['xmlns'] = 'jabber:iq:register'
173			n1 = Nokogiri::XML::Node.new 'instructions',msg.document
174			n1.content= "Enter the information from your Account " +
175				"page as well as the Phone Number\nin your " +
176				"account you want to use (ie. '+12345678901')" +
177				".\nUser Id is nick, API Token is username, " +
178				"API Secret is password, Phone Number is phone"+
179				".\n\nThe source code for this gateway is at " +
180				"https://github.com/ossguy/sgx-catapult ." +
181				"\nCopyright (C) 2017  Denver Gingerich, " +
182				"licensed under AGPLv3+."
183			n2 = Nokogiri::XML::Node.new 'nick',msg.document
184			n3 = Nokogiri::XML::Node.new 'username',msg.document
185			n4 = Nokogiri::XML::Node.new 'password',msg.document
186			n5 = Nokogiri::XML::Node.new 'phone',msg.document
187			msg.add_child(n1)
188			msg.add_child(n2)
189			msg.add_child(n3)
190			msg.add_child(n4)
191			msg.add_child(n5)
192
193			x = Blather::Stanza::X.new :form, [
194				{:required => true, :type => :"text-single",
195				:label => 'User Id', :var => 'nick'},
196				{:required => true, :type => :"text-single",
197				:label => 'API Token', :var => 'username'},
198				{:required => true, :type => :"text-private",
199				:label => 'API Secret', :var => 'password'},
200				{:required => true, :type => :"text-single",
201				:label => 'Phone Number', :var => 'phone'}
202			]
203			x.title= 'Register for ' +
204				'Soprani.ca Gateway to XMPP - Catapult'
205			x.instructions= "Enter the details from your Account " +
206				"page as well as the Phone Number\nin your " +
207				"account you want to use (ie. '+12345678901')" +
208				".\n\nThe source code for this gateway is at " +
209				"https://github.com/ossguy/sgx-catapult ." +
210				"\nCopyright (C) 2017  Denver Gingerich, " +
211				"licensed under AGPLv3+."
212			msg.add_child(x)
213
214			orig.add_child(msg)
215			puts "RESPONSE2: #{orig.inspect}"
216			write_to_stream orig
217			puts "SENT"
218		end
219	end
220
221	subscription(:request?) do |s|
222		# TODO: are these the best to return?  really need '!' here?
223		#write_to_stream s.approve!
224		#write_to_stream s.request!
225	end
226end
227
228[:INT, :TERM].each do |sig|
229	trap(sig) {
230		puts 'Shutting down gateway...'
231		SGXcatapult.shutdown
232		puts 'Gateway has terminated.'
233
234		EM.stop
235	}
236end
237
238EM.run do
239	SGXcatapult.run
240end