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'
21
22if ARGV.size != 4 then
23 puts "Usage: sgx-catapult.rb <component_jid> <component_password> " +
24 "<server_hostname> <server_port>"
25 exit 0
26end
27
28module SGXcatapult
29 extend Blather::DSL
30
31 def self.run
32 client.run
33 end
34
35 setup ARGV[0], ARGV[1], ARGV[2], ARGV[3]
36
37 message :chat?, :body do |m|
38 begin
39 puts "#{m.from.to_s} -> #{m.to.to_s} #{m.body}"
40 msg = Blather::Stanza::Message.new(m.from, 'thx for "' +
41 m.body + '"')
42 msg.from = m.to
43 write_to_stream msg
44 rescue => e
45 # TODO: do something better with this info
46 say m.from, e.inspect
47 end
48 end
49
50 iq do |i|
51 puts "IQ: #{i.inspect}"
52
53 if i.type == :set
54 puts "received set, likely for jabber:iq:register"
55
56 # TODO: resilient error handling; what if no query node?
57
58 qn = i.children.find { |v| v.element_name == "query" }
59 # TODO: add below check - as-written has unmatched end
60 i #if qn.namespace.href != 'jabber:iq:register'
61 # # TODO: error
62 # puts "weird xmlns: " + qn.namespace.href
63 # next
64 #end
65
66 xn = qn.children.find { |v| v.element_name == "x" }
67 for field in xn.children
68 if field.element_name == "field"
69 val = field.children.find { |v|
70 v.element_name == "value" }
71
72 case field['var']
73 when 'nick'
74 puts "id: " + val.text
75 when 'username'
76 puts "token: " + val.text
77 when 'password'
78 puts "secret: " + val.text
79 when 'phone'
80 puts "phone num: " + val.text
81 when 'FORM_TYPE'
82 puts "FORM_TYPE: " + val.text
83 else
84 # TODO: error
85 puts "weird var: " +field['var']
86 end
87 end
88 end
89
90 # success (for now)
91 msg = Blather::Stanza::Iq.new
92 msg.id = i.id
93 msg.to = i.from
94 msg.type = 'result'
95
96 puts "RESPONSE3: #{msg.inspect}"
97 write_to_stream msg
98 puts "SENT"
99
100 # TODO: implement this (verify/save data, return result)
101 next
102 end
103
104 query_node = i.children.find { |v| v.element_name == "query" }
105 if query_node.namespace.href ==
106 'http://jabber.org/protocol/disco#items'
107
108 msg = Blather::Stanza::Iq::DiscoItems.new
109 msg.id = i.id
110 msg.to = i.from
111 msg.type = 'result'
112
113 puts "RESPONSE0: #{msg.inspect}"
114 write_to_stream msg
115 puts "SENT"
116 elsif query_node.namespace.href ==
117 'http://jabber.org/protocol/disco#info'
118
119 msg = Blather::Stanza::Iq::DiscoInfo.new
120 msg.id = i.id
121 msg.to = i.from
122 msg.type = 'result'
123
124 msg.identities = [{:name =>
125 'Soprani.ca Gateway to XMPP - Catapult',
126 :type => 'sms-ctplt', :category => 'gateway'}]
127 msg.features = ["jabber:iq:register",
128 "jabber:iq:gateway", "jabber:iq:private",
129 "http://jabber.org/protocol/disco#info",
130 "http://jabber.org/protocol/commands",
131 "http://jabber.org/protocol/muc"]
132 puts "RESPONSE1: #{msg.inspect}"
133 write_to_stream msg
134 puts "SENT"
135 elsif query_node.namespace.href == 'jabber:iq:register'
136 orig = Blather::Stanza::Iq.new
137 orig.id = i.id
138 orig.to = i.from
139 orig.type = 'result'
140
141 msg = Nokogiri::XML::Node.new 'query',orig.document
142 msg['xmlns'] = 'jabber:iq:register'
143 n1 = Nokogiri::XML::Node.new 'instructions',msg.document
144 n1.content= "Enter the information from your Account " +
145 "page as well as the Phone Number\nin your " +
146 "account you want to use (ie. '+12345678901')" +
147 ".\nUser Id is nick, API Token is username, " +
148 "API Secret is password, Phone Number is phone"+
149 ".\n\nThe source code for this gateway is at " +
150 "https://github.com/ossguy/sgx-catapult ." +
151 "\nCopyright (C) 2017 Denver Gingerich, " +
152 "licensed under AGPLv3+."
153 n2 = Nokogiri::XML::Node.new 'nick',msg.document
154 n3 = Nokogiri::XML::Node.new 'username',msg.document
155 n4 = Nokogiri::XML::Node.new 'password',msg.document
156 n5 = Nokogiri::XML::Node.new 'phone',msg.document
157 msg.add_child(n1)
158 msg.add_child(n2)
159 msg.add_child(n3)
160 msg.add_child(n4)
161 msg.add_child(n5)
162
163 x = Nokogiri::XML::Node.new 'x',orig.document
164 x['xmlns'] = 'jabber:x:data'
165 x['type'] = 'form'
166 msg.add_child(x)
167
168 title = Nokogiri::XML::Node.new 'title',orig.document
169 title.content= 'Register for ' +
170 'Soprani.ca Gateway to XMPP - Catapult'
171 x.add_child(title)
172
173 instr = Nokogiri::XML::Node.new 'instructions',
174 orig.document
175 instr.content= "Enter the details from your Account " +
176 "page as well as the Phone Number\nin your " +
177 "account you want to use (ie. '+12345678901')" +
178 ".\n\nThe source code for this gateway is at " +
179 "https://github.com/ossguy/sgx-catapult ." +
180 "\nCopyright (C) 2017 Denver Gingerich, " +
181 "licensed under AGPLv3+."
182 x.add_child(instr)
183
184 f1 = Nokogiri::XML::Node.new 'field',orig.document
185 f1['type'] = 'hidden'
186 f1['var'] = 'FORM_TYPE'
187 v1 = Nokogiri::XML::Node.new 'value',orig.document
188 v1.content= 'jabber:iq:register'
189 f1.add_child(v1)
190 x.add_child(f1)
191
192 f2 = Nokogiri::XML::Node.new 'field',orig.document
193 f2['type'] = 'text-single'
194 f2['label'] = 'User Id'
195 f2['var'] = 'nick'
196 v2 = Nokogiri::XML::Node.new 'required',orig.document
197 f2.add_child(v2)
198 x.add_child(f2)
199
200 f3 = Nokogiri::XML::Node.new 'field',orig.document
201 f3['type'] = 'text-single'
202 f3['label'] = 'API Token'
203 f3['var'] = 'username'
204 v3 = Nokogiri::XML::Node.new 'required',orig.document
205 f3.add_child(v3)
206 x.add_child(f3)
207
208 f4 = Nokogiri::XML::Node.new 'field',orig.document
209 f4['type'] = 'text-private'
210 f4['label'] = 'API Secret'
211 f4['var'] = 'password'
212 v4 = Nokogiri::XML::Node.new 'required',orig.document
213 f4.add_child(v4)
214 x.add_child(f4)
215
216 f5 = Nokogiri::XML::Node.new 'field',orig.document
217 f5['type'] = 'text-single'
218 f5['label'] = 'Phone Number'
219 f5['var'] = 'phone'
220 v5 = Nokogiri::XML::Node.new 'required',orig.document
221 f5.add_child(v5)
222 x.add_child(f5)
223
224 orig.add_child(msg)
225 puts "RESPONSE2: #{orig.inspect}"
226 write_to_stream orig
227 puts "SENT"
228 end
229 end
230
231 subscription(:request?) do |s|
232 # TODO: fix these - they don't actual work; write does not exist
233 #write(s.approve!)
234 #write(s.request!)
235 end
236end
237
238[:INT, :TERM].each do |sig|
239 trap(sig) {
240 puts 'Shutting down gateway...'
241 SGXcatapult.shutdown
242 puts 'Gateway has terminated.'
243
244 EM.stop
245 }
246end
247
248EM.run do
249 SGXcatapult.run
250end