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