mojodna-switchboard 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +31 -0
- data/bin/switchboard +35 -0
- data/examples/election_results.rb +61 -0
- data/lib/switchboard/colors.rb +10 -0
- data/lib/switchboard/commands/command.rb +85 -0
- data/lib/switchboard/commands/config/config.rb +20 -0
- data/lib/switchboard/commands/config.rb +1 -0
- data/lib/switchboard/commands/default.rb +47 -0
- data/lib/switchboard/commands/help/help.rb +15 -0
- data/lib/switchboard/commands/help.rb +1 -0
- data/lib/switchboard/commands/pubsub/pubsub.rb +16 -0
- data/lib/switchboard/commands/pubsub/subscribe.rb +35 -0
- data/lib/switchboard/commands/pubsub/subscriptions.rb +32 -0
- data/lib/switchboard/commands/pubsub/unsubscribe.rb +31 -0
- data/lib/switchboard/commands/pubsub.rb +4 -0
- data/lib/switchboard/commands/roster/add.rb +26 -0
- data/lib/switchboard/commands/roster/list.rb +24 -0
- data/lib/switchboard/commands/roster/remove.rb +29 -0
- data/lib/switchboard/commands/roster/roster.rb +7 -0
- data/lib/switchboard/commands/roster.rb +4 -0
- data/lib/switchboard/commands.rb +6 -0
- data/lib/switchboard/core.rb +324 -0
- data/lib/switchboard/instance_exec.rb +16 -0
- data/lib/switchboard/jacks/auto_accept.rb +16 -0
- data/lib/switchboard/jacks/debug.rb +17 -0
- data/lib/switchboard/jacks/notify.rb +15 -0
- data/lib/switchboard/jacks/oauth_pubsub.rb +50 -0
- data/lib/switchboard/jacks/roster_debug.rb +23 -0
- data/lib/switchboard/jacks.rb +5 -0
- data/lib/switchboard/oauth/request_proxy/mock_request.rb +21 -0
- data/lib/switchboard/settings.rb +32 -0
- data/lib/switchboard/switchboard.rb +5 -0
- data/lib/switchboard/version.rb +3 -0
- data/lib/switchboard/xmpp4r/pubsub/helper/oauth_service_helper.rb +107 -0
- data/lib/switchboard.rb +1 -0
- data/switchboard.gemspec +14 -0
- metadata +113 -0
@@ -0,0 +1,324 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin
|
3
|
+
require 'xmpp4r'
|
4
|
+
rescue LoadError => e
|
5
|
+
gem = e.message.split("--").last.strip
|
6
|
+
puts "The #{gem} gem is required."
|
7
|
+
end
|
8
|
+
|
9
|
+
# allow local library modifications/additions to be loaded
|
10
|
+
$: << File.join(File.dirname(__FILE__))
|
11
|
+
|
12
|
+
require 'switchboard/instance_exec'
|
13
|
+
require 'xmpp4r/roster'
|
14
|
+
|
15
|
+
|
16
|
+
module Switchboard
|
17
|
+
class Core
|
18
|
+
include Timeout
|
19
|
+
|
20
|
+
attr_reader :client, :jacks, :roster, :settings
|
21
|
+
|
22
|
+
def initialize(settings = Switchboard::Settings.new, spin = true, &block)
|
23
|
+
# register a handler for SIGINTs
|
24
|
+
trap(:INT) do
|
25
|
+
# exit on a second ^C
|
26
|
+
trap(:INT) do
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
@deferreds.each do |name, deferred|
|
31
|
+
puts "Killing #{name}" if debug?
|
32
|
+
deferred.kill
|
33
|
+
end
|
34
|
+
|
35
|
+
shutdown!
|
36
|
+
end
|
37
|
+
|
38
|
+
@settings = settings
|
39
|
+
@loop = spin
|
40
|
+
@shutdown = false
|
41
|
+
@deferreds = {}
|
42
|
+
@main = block if block_given?
|
43
|
+
|
44
|
+
# TODO jid may already have a resource, so account for that
|
45
|
+
@client = Jabber::Client.new([settings["jid"], settings["resource"]] * "/")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Turn the hydrant on.
|
49
|
+
def run!
|
50
|
+
startup
|
51
|
+
|
52
|
+
if @main
|
53
|
+
instance_eval(&@main)
|
54
|
+
elsif loop?
|
55
|
+
sleep 5 while !shutdown?
|
56
|
+
end
|
57
|
+
|
58
|
+
shutdown
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO don't start threads yet; wait until all startup hooks have been run
|
62
|
+
def defer(callback_name, timeout = 30, &block)
|
63
|
+
puts "Deferring to #{callback_name}..." if debug?
|
64
|
+
@deferreds[callback_name.to_sym] = Thread.new do
|
65
|
+
|
66
|
+
begin
|
67
|
+
|
68
|
+
timeout(timeout) do
|
69
|
+
results = instance_eval(&block)
|
70
|
+
send(callback_name.to_sym, results)
|
71
|
+
end
|
72
|
+
|
73
|
+
puts "Done with #{callback_name}." if debug?
|
74
|
+
# TODO make this thread-safe
|
75
|
+
@deferreds.delete(callback_name.to_sym)
|
76
|
+
|
77
|
+
rescue Timeout::Error
|
78
|
+
puts "Deferred method timed out."
|
79
|
+
rescue
|
80
|
+
puts "An error occurred while running a deferred: #{$!}"
|
81
|
+
puts $!.backtrace * "\n"
|
82
|
+
puts "Initiating shutdown..."
|
83
|
+
@shutdown = true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Connect a jack to the switchboard
|
89
|
+
def plug!(*jacks)
|
90
|
+
@jacks ||= []
|
91
|
+
jacks.each do |jack|
|
92
|
+
puts "Connecting jack: #{jack}" if debug?
|
93
|
+
@jacks << jack
|
94
|
+
jack.connect(self)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Register a hook to run when the Jabber::Client encounters an exception.
|
99
|
+
def on_exception(&block)
|
100
|
+
register_hook(:exception, &block)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Register a hook to run when iq stanzas are received.
|
104
|
+
def on_iq(&block)
|
105
|
+
register_hook(:iq, &block)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Register a hook to run when message stanzas are received.
|
109
|
+
def on_message(&block)
|
110
|
+
register_hook(:message, &block)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Register a hook to run when presence stanzas are received.
|
114
|
+
def on_presence(&block)
|
115
|
+
register_hook(:presence, &block)
|
116
|
+
end
|
117
|
+
|
118
|
+
def on_roster_presence(&block)
|
119
|
+
register_hook(:roster_presence, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
def on_roster_query(&block)
|
123
|
+
register_hook(:roster_query, &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
def on_roster_subscription(&block)
|
127
|
+
register_hook(:roster_subscription, &block)
|
128
|
+
end
|
129
|
+
|
130
|
+
def on_roster_subscription_request(&block)
|
131
|
+
register_hook(:roster_subscription_request, &block)
|
132
|
+
end
|
133
|
+
|
134
|
+
def on_roster_loaded(&block)
|
135
|
+
register_hook(:roster_loaded, &block)
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_roster_update(&block)
|
139
|
+
register_hook(:roster_update, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Register a startup hook.
|
143
|
+
# Hooks will be given 5 seconds to complete before moving on.
|
144
|
+
def on_startup(&block)
|
145
|
+
register_hook(:startup, &block)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Register a shutdown hook.
|
149
|
+
# Hooks will be given 5 seconds to complete before moving on.
|
150
|
+
def on_shutdown(&block)
|
151
|
+
register_hook(:shutdown, &block)
|
152
|
+
end
|
153
|
+
|
154
|
+
protected
|
155
|
+
|
156
|
+
def connect!
|
157
|
+
client.connect
|
158
|
+
client.auth(settings["password"])
|
159
|
+
@roster = Jabber::Roster::Helper.new(client)
|
160
|
+
end
|
161
|
+
|
162
|
+
def connected?
|
163
|
+
@connected
|
164
|
+
end
|
165
|
+
|
166
|
+
def debug?
|
167
|
+
settings["debug"]
|
168
|
+
end
|
169
|
+
|
170
|
+
def disconnect
|
171
|
+
presence(:unavailable)
|
172
|
+
client.close
|
173
|
+
end
|
174
|
+
|
175
|
+
def register_hook(name, &block)
|
176
|
+
@hooks ||= {}
|
177
|
+
@hooks[name.to_sym] ||= []
|
178
|
+
|
179
|
+
puts "Registering #{name} hook" if debug?
|
180
|
+
@hooks[name.to_sym] << block
|
181
|
+
end
|
182
|
+
|
183
|
+
def on(name, *args)
|
184
|
+
@hooks ||= {}
|
185
|
+
@hooks[name.to_sym] ||= []
|
186
|
+
@hooks[name.to_sym].each do |hook|
|
187
|
+
execute_hook(hook, *args)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def execute_hook(hook, *args)
|
192
|
+
timeout(1) do
|
193
|
+
instance_exec(*args, &hook)
|
194
|
+
end
|
195
|
+
rescue Timeout::Error
|
196
|
+
puts "Hook timed out, consider deferring it."
|
197
|
+
rescue
|
198
|
+
puts "An error occurred while running the hook; shutting down..."
|
199
|
+
puts $!
|
200
|
+
puts $!.backtrace * "\n"
|
201
|
+
shutdown
|
202
|
+
raise
|
203
|
+
end
|
204
|
+
|
205
|
+
def loop?
|
206
|
+
@loop
|
207
|
+
end
|
208
|
+
|
209
|
+
def presence(status = nil, to = nil)
|
210
|
+
presence = Jabber::Presence.new(nil, status)
|
211
|
+
presence.to = to
|
212
|
+
client.send(presence)
|
213
|
+
end
|
214
|
+
|
215
|
+
def register_default_callbacks
|
216
|
+
client.on_exception do |e, stream, where|
|
217
|
+
on(:exception, e, stream, where)
|
218
|
+
|
219
|
+
case where
|
220
|
+
when :something
|
221
|
+
else
|
222
|
+
puts "Caught #{e.inspect} on #{stream} at #{where}. You might want to consider handling this."
|
223
|
+
raise e
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
client.add_presence_callback do |presence|
|
228
|
+
on(:presence, presence)
|
229
|
+
end
|
230
|
+
|
231
|
+
client.add_message_callback do |message|
|
232
|
+
on(:message, message)
|
233
|
+
end
|
234
|
+
|
235
|
+
client.add_iq_callback do |iq|
|
236
|
+
on(:iq, iq)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def register_roster_callbacks
|
241
|
+
# presence from someone on my roster
|
242
|
+
roster.add_presence_callback do |item, old_presence, new_presence|
|
243
|
+
on(:roster_presence, item, old_presence, new_presence)
|
244
|
+
end
|
245
|
+
|
246
|
+
# roster query completed (rarely used)
|
247
|
+
roster.add_query_callback do |query|
|
248
|
+
on(:roster_query, query)
|
249
|
+
end
|
250
|
+
|
251
|
+
# roster subscription
|
252
|
+
roster.add_subscription_callback do |item, presence|
|
253
|
+
# confirmation that we were able to subscribe to someone else
|
254
|
+
on(:roster_subscription, item, presence)
|
255
|
+
end
|
256
|
+
|
257
|
+
roster.add_subscription_request_callback do |item, presence|
|
258
|
+
# someone wants to subscribe to me!
|
259
|
+
on(:roster_subscription_request, item, presence)
|
260
|
+
end
|
261
|
+
|
262
|
+
# roster was updated (rarely used)
|
263
|
+
roster.add_update_callback do |old_item, new_item|
|
264
|
+
# roster has been updated; don't care
|
265
|
+
# puts "update: #{old_item.inspect}, #{new_item.inspect}"
|
266
|
+
on(:roster_update, old_item, new_item)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def startup
|
271
|
+
begin
|
272
|
+
timeout(30) do
|
273
|
+
connect!
|
274
|
+
@connected = true
|
275
|
+
|
276
|
+
register_default_callbacks
|
277
|
+
register_roster_callbacks
|
278
|
+
|
279
|
+
# tell others that we're online
|
280
|
+
presence
|
281
|
+
|
282
|
+
# wait for the roster to load
|
283
|
+
roster.wait_for_roster
|
284
|
+
end
|
285
|
+
rescue Timeout::Error
|
286
|
+
puts "Startup took too long. Shutting down."
|
287
|
+
shutdown(false)
|
288
|
+
exit 1
|
289
|
+
end
|
290
|
+
|
291
|
+
# roster has now been loaded
|
292
|
+
on(:roster_loaded)
|
293
|
+
|
294
|
+
puts "Core startup completed." if debug?
|
295
|
+
|
296
|
+
# run startup hooks
|
297
|
+
on(:startup)
|
298
|
+
|
299
|
+
puts "=> Switchboard started."
|
300
|
+
end
|
301
|
+
|
302
|
+
def shutdown!
|
303
|
+
puts "Shutdown initiated."
|
304
|
+
@shutdown = true
|
305
|
+
end
|
306
|
+
|
307
|
+
def shutdown(run_hooks = true)
|
308
|
+
while (pending = @deferreds.select { |k,d| d.alive? }.length) > 0
|
309
|
+
puts "Waiting for #{pending} thread(s) to finish" if debug?
|
310
|
+
sleep 1
|
311
|
+
end
|
312
|
+
|
313
|
+
# run shutdown hooks
|
314
|
+
on(:shutdown) if run_hooks
|
315
|
+
|
316
|
+
puts "Shutting down..." if debug?
|
317
|
+
disconnect if connected?
|
318
|
+
end
|
319
|
+
|
320
|
+
def shutdown?
|
321
|
+
@shutdown
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Kernel
|
2
|
+
|
3
|
+
# Like instance_eval but allows parameters to be passed.
|
4
|
+
|
5
|
+
def instance_exec(*args, &block)
|
6
|
+
mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
|
7
|
+
Object.class_eval{ define_method(mname, &block) }
|
8
|
+
begin
|
9
|
+
ret = send(mname, *args)
|
10
|
+
ensure
|
11
|
+
Object.class_eval{ undef_method(mname) } rescue nil
|
12
|
+
end
|
13
|
+
ret
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class AutoAcceptJack
|
2
|
+
def self.connect(switchboard)
|
3
|
+
# complain if subscription requests were denied
|
4
|
+
switchboard.on_roster_subscription do |item, subscription|
|
5
|
+
unless subscription.type == :subscribed
|
6
|
+
puts "My subscription request was denied!"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# auto-accept subscription requests
|
11
|
+
switchboard.on_roster_subscription_request do |item, subscription|
|
12
|
+
puts "Accepting subscription from #{subscription.from}"
|
13
|
+
roster.accept_subscription(subscription.from)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'switchboard/colors'
|
2
|
+
|
3
|
+
class DebugJack
|
4
|
+
def self.connect(switchboard)
|
5
|
+
switchboard.on_presence do |presence|
|
6
|
+
puts "<< #{presence.to_s}".green
|
7
|
+
end
|
8
|
+
|
9
|
+
switchboard.on_message do |message|
|
10
|
+
puts "<< #{message.to_s}".blue
|
11
|
+
end
|
12
|
+
|
13
|
+
switchboard.on_iq do |iq|
|
14
|
+
puts "<< #{iq.to_s}".yellow
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class NotifyJack
|
2
|
+
def self.connect(switchboard)
|
3
|
+
switchboard.on_roster_loaded do
|
4
|
+
roster.items.each do |jid, item|
|
5
|
+
presence(nil, jid)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
switchboard.on_shutdown do
|
10
|
+
roster.items.each do |jid, item|
|
11
|
+
presence(:unavailable, jid)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
begin
|
3
|
+
require 'oauth'
|
4
|
+
rescue LoadError => e
|
5
|
+
gem = e.message.split("--").last.strip
|
6
|
+
puts "The #{gem} gem is required."
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'oauth/consumer'
|
10
|
+
require 'oauth/request_proxy/mock_request'
|
11
|
+
require 'xmpp4r/pubsub'
|
12
|
+
require 'xmpp4r/pubsub/helper/oauth_service_helper'
|
13
|
+
|
14
|
+
class OAuthPubSubJack
|
15
|
+
def self.connect(switchboard)
|
16
|
+
switchboard.on_startup do
|
17
|
+
@pubsub = Jabber::PubSub::OAuthServiceHelper.new(client, settings["pubsub.server"])
|
18
|
+
|
19
|
+
@oauth_consumer = OAuth::Consumer.new(settings["oauth.consumer_key"], settings["oauth.consumer_secret"])
|
20
|
+
@oauth_token = OAuth::Token.new(settings["oauth.token"], settings["oauth.token_secret"])
|
21
|
+
# this is Fire Eagle-specific
|
22
|
+
@general_token = OAuth::Token.new(settings["oauth.general_token"], settings["oauth.general_token_secret"])
|
23
|
+
|
24
|
+
@pubsub.add_event_callback do |event|
|
25
|
+
on(:pubsub_event, event)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO add the ability to define accessors
|
30
|
+
def switchboard.general_token
|
31
|
+
@general_token
|
32
|
+
end
|
33
|
+
|
34
|
+
def switchboard.oauth_consumer
|
35
|
+
@oauth_consumer
|
36
|
+
end
|
37
|
+
|
38
|
+
def switchboard.oauth_token
|
39
|
+
@oauth_token
|
40
|
+
end
|
41
|
+
|
42
|
+
def switchboard.pubsub
|
43
|
+
@pubsub
|
44
|
+
end
|
45
|
+
|
46
|
+
def switchboard.on_pubsub_event(&block)
|
47
|
+
register_hook(:pubsub_event, &block)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class RosterDebugJack
|
2
|
+
def self.connect(switchboard)
|
3
|
+
switchboard.on_roster_presence do |item, old_presence, new_presence|
|
4
|
+
puts "[presence] << #{item.inspect}: #{old_presence.to_s}, #{new_presence.to_s}"
|
5
|
+
end
|
6
|
+
|
7
|
+
switchboard.on_roster_query do |query|
|
8
|
+
puts "[roster query] << #{query.to_s}"
|
9
|
+
end
|
10
|
+
|
11
|
+
switchboard.on_roster_subscription do |item, subscription|
|
12
|
+
puts "[subscription] << #{item.inspect}: #{subscription.to_s}"
|
13
|
+
end
|
14
|
+
|
15
|
+
switchboard.on_roster_subscription_request do |item, subscription|
|
16
|
+
puts "[subscription request] << #{item.inspect}: #{subscription.to_s}"
|
17
|
+
end
|
18
|
+
|
19
|
+
switchboard.on_roster_update do |old_item, new_item|
|
20
|
+
puts "[update] #{old_item.inspect}, #{new_item.inspect}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'oauth/request_proxy/base'
|
2
|
+
|
3
|
+
module OAuth
|
4
|
+
module RequestProxy
|
5
|
+
class MockRequest < OAuth::RequestProxy::Base
|
6
|
+
proxies Hash
|
7
|
+
|
8
|
+
def parameters
|
9
|
+
@request["parameters"]
|
10
|
+
end
|
11
|
+
|
12
|
+
def method
|
13
|
+
@request["method"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def uri
|
17
|
+
@request["uri"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Switchboard
|
2
|
+
class Settings
|
3
|
+
DEFAULT_PATH = File.join(ENV["HOME"], ".switchboardrc")
|
4
|
+
|
5
|
+
def initialize(path = DEFAULT_PATH)
|
6
|
+
@path = path
|
7
|
+
|
8
|
+
if File.exists?(path)
|
9
|
+
@config = YAML.load(File.read(path))
|
10
|
+
end
|
11
|
+
|
12
|
+
@config ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(key)
|
16
|
+
Switchboard::Command::OPTIONS[key] || @config[key] || Switchboard::Command::DEFAULT_OPTIONS[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method :[], :get
|
20
|
+
|
21
|
+
def set!(key, value)
|
22
|
+
@config[key] = value
|
23
|
+
write
|
24
|
+
end
|
25
|
+
|
26
|
+
def write
|
27
|
+
open(@path, "w") do |f|
|
28
|
+
f << @config.to_yaml
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module Jabber
|
2
|
+
module PubSub
|
3
|
+
# PubSub service helper for use with OAuth-authenticated nodes
|
4
|
+
class OAuthServiceHelper < ServiceHelper
|
5
|
+
def initialize(stream, pubsubjid)
|
6
|
+
super(stream, pubsubjid)
|
7
|
+
end
|
8
|
+
|
9
|
+
# override #get_subscriptions_from_all_nodes to add an oauth element
|
10
|
+
def get_subscriptions_from_all_nodes(oauth_consumer, oauth_token)
|
11
|
+
iq = basic_pubsub_query(:get)
|
12
|
+
|
13
|
+
iq.pubsub.add(create_oauth_node(oauth_consumer, oauth_token))
|
14
|
+
|
15
|
+
entities = iq.pubsub.add(REXML::Element.new('subscriptions'))
|
16
|
+
res = nil
|
17
|
+
@stream.send_with_id(iq) { |reply|
|
18
|
+
if reply.pubsub.first_element('subscriptions')
|
19
|
+
res = []
|
20
|
+
reply.pubsub.first_element('subscriptions').each_element('subscription') { |subscription|
|
21
|
+
res << Jabber::PubSub::Subscription.import(subscription)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
}
|
25
|
+
|
26
|
+
res
|
27
|
+
end
|
28
|
+
|
29
|
+
# override #subscribe_to to add an oauth element
|
30
|
+
def subscribe_to(node, oauth_consumer, oauth_token)
|
31
|
+
iq = basic_pubsub_query(:set)
|
32
|
+
sub = REXML::Element.new('subscribe')
|
33
|
+
sub.attributes['node'] = node
|
34
|
+
sub.attributes['jid'] = @stream.jid.strip.to_s
|
35
|
+
|
36
|
+
sub.add(create_oauth_node(oauth_consumer, oauth_token))
|
37
|
+
|
38
|
+
iq.pubsub.add(sub)
|
39
|
+
res = nil
|
40
|
+
@stream.send_with_id(iq) do |reply|
|
41
|
+
pubsubanswer = reply.pubsub
|
42
|
+
if pubsubanswer.first_element('subscription')
|
43
|
+
res = PubSub::Subscription.import(pubsubanswer.first_element('subscription'))
|
44
|
+
end
|
45
|
+
end # @stream.send_with_id(iq)
|
46
|
+
res
|
47
|
+
end
|
48
|
+
|
49
|
+
# override #unsubscribe_from to add an oauth element
|
50
|
+
def unsubscribe_from(node, oauth_consumer, oauth_token, subid=nil)
|
51
|
+
iq = basic_pubsub_query(:set)
|
52
|
+
unsub = PubSub::Unsubscribe.new
|
53
|
+
unsub.node = node
|
54
|
+
unsub.jid = @stream.jid.strip
|
55
|
+
|
56
|
+
unsub.add(create_oauth_node(oauth_consumer, oauth_token))
|
57
|
+
|
58
|
+
iq.pubsub.add(unsub)
|
59
|
+
ret = false
|
60
|
+
@stream.send_with_id(iq) { |reply|
|
61
|
+
ret = reply.kind_of?(Jabber::Iq) and reply.type == :result
|
62
|
+
} # @stream.send_with_id(iq)
|
63
|
+
ret
|
64
|
+
end
|
65
|
+
|
66
|
+
protected
|
67
|
+
|
68
|
+
# add the OAuth sauce (XEP-235)
|
69
|
+
def create_oauth_node(oauth_consumer, oauth_token)
|
70
|
+
require 'oauth/signature/hmac/sha1'
|
71
|
+
require 'cgi'
|
72
|
+
|
73
|
+
request = OAuth::RequestProxy.proxy \
|
74
|
+
"method" => "iq",
|
75
|
+
"uri" => [@stream.jid.strip.to_s, @pubsubjid.strip.to_s] * "&",
|
76
|
+
"parameters" => {
|
77
|
+
"oauth_consumer_key" => oauth_consumer.key,
|
78
|
+
"oauth_token" => oauth_token.token,
|
79
|
+
"oauth_signature_method" => "HMAC-SHA1"
|
80
|
+
}
|
81
|
+
|
82
|
+
signature = OAuth::Signature.sign(request, :consumer => oauth_consumer, :token => oauth_token)
|
83
|
+
|
84
|
+
oauth = REXML::Element.new("oauth")
|
85
|
+
oauth.attributes['xmlns'] = 'urn:xmpp:oauth'
|
86
|
+
|
87
|
+
oauth_consumer_key = REXML::Element.new("oauth_consumer_key")
|
88
|
+
oauth_consumer_key.text = oauth_consumer.key
|
89
|
+
oauth.add(oauth_consumer_key)
|
90
|
+
|
91
|
+
oauth_token_node = REXML::Element.new("oauth_token")
|
92
|
+
oauth_token_node.text = oauth_token.token
|
93
|
+
oauth.add(oauth_token_node)
|
94
|
+
|
95
|
+
oauth_signature_method = REXML::Element.new("oauth_signature_method")
|
96
|
+
oauth_signature_method.text = "HMAC-SHA1"
|
97
|
+
oauth.add(oauth_signature_method)
|
98
|
+
|
99
|
+
oauth_signature = REXML::Element.new("oauth_signature")
|
100
|
+
oauth_signature.text = signature
|
101
|
+
oauth.add(oauth_signature)
|
102
|
+
|
103
|
+
oauth
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/lib/switchboard.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'switchboard/switchboard'
|
data/switchboard.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "switchboard"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.summary = "XMPP toolkit"
|
5
|
+
s.description = "A toolkit for assembling XMPP clients and interacting with XMPP servers."
|
6
|
+
s.authors = ["Seth Fitzsimmons"]
|
7
|
+
s.email = ["seth@mojodna.net"]
|
8
|
+
|
9
|
+
s.files = ["bin", "bin/switchboard", "examples/election_results.rb", "github-test.rb", "lib", "lib/switchboard", "lib/switchboard/colors.rb", "lib/switchboard/commands", "lib/switchboard/commands/command.rb", "lib/switchboard/commands/config", "lib/switchboard/commands/config/config.rb", "lib/switchboard/commands/config.rb", "lib/switchboard/commands/default.rb", "lib/switchboard/commands/help", "lib/switchboard/commands/help/help.rb", "lib/switchboard/commands/help.rb", "lib/switchboard/commands/pubsub", "lib/switchboard/commands/pubsub/pubsub.rb", "lib/switchboard/commands/pubsub/subscribe.rb", "lib/switchboard/commands/pubsub/subscriptions.rb", "lib/switchboard/commands/pubsub/unsubscribe.rb", "lib/switchboard/commands/pubsub.rb", "lib/switchboard/commands/roster", "lib/switchboard/commands/roster/add.rb", "lib/switchboard/commands/roster/list.rb", "lib/switchboard/commands/roster/remove.rb", "lib/switchboard/commands/roster/roster.rb", "lib/switchboard/commands/roster.rb", "lib/switchboard/commands.rb", "lib/switchboard/core.rb", "lib/switchboard/instance_exec.rb", "lib/switchboard/jacks", "lib/switchboard/jacks/auto_accept.rb", "lib/switchboard/jacks/debug.rb", "lib/switchboard/jacks/notify.rb", "lib/switchboard/jacks/oauth_pubsub.rb", "lib/switchboard/jacks/roster_debug.rb", "lib/switchboard/jacks.rb", "lib/switchboard/oauth", "lib/switchboard/oauth/request_proxy", "lib/switchboard/oauth/request_proxy/mock_request.rb", "lib/switchboard/settings.rb", "lib/switchboard/switchboard.rb", "lib/switchboard/version.rb", "lib/switchboard/xmpp4r", "lib/switchboard/xmpp4r/pubsub", "lib/switchboard/xmpp4r/pubsub/helper", "lib/switchboard/xmpp4r/pubsub/helper/oauth_service_helper.rb", "lib/switchboard.rb", "README.markdown", "switchboard-0.0.1.gem", "switchboard.gemspec"]
|
10
|
+
s.executables = ["switchboard"]
|
11
|
+
s.require_paths = ["lib"]
|
12
|
+
|
13
|
+
s.add_dependency("xmpp4r")
|
14
|
+
end
|