blather 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +2 -0
- data/README.rdoc +54 -29
- data/Rakefile +94 -13
- data/VERSION.yml +4 -0
- data/examples/drb_client.rb +2 -4
- data/examples/echo.rb +13 -8
- data/examples/pubsub/cli.rb +64 -0
- data/examples/pubsub/ping_pong.rb +18 -0
- data/examples/pubsub/pubsub_dsl.rb +52 -0
- data/examples/pubsub_client.rb +39 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/ext/extconf.rb +65 -0
- data/lib/blather.rb +18 -121
- data/lib/blather/client.rb +13 -0
- data/lib/blather/client/client.rb +165 -0
- data/lib/blather/client/dsl.rb +99 -0
- data/lib/blather/client/pubsub.rb +53 -0
- data/lib/blather/client/pubsub/node.rb +27 -0
- data/lib/blather/core_ext/active_support.rb +1 -0
- data/lib/blather/core_ext/libxml.rb +7 -1
- data/lib/blather/errors.rb +39 -18
- data/lib/blather/errors/sasl_error.rb +87 -0
- data/lib/blather/errors/stanza_error.rb +262 -0
- data/lib/blather/errors/stream_error.rb +253 -0
- data/lib/blather/jid.rb +9 -16
- data/lib/blather/roster.rb +9 -0
- data/lib/blather/roster_item.rb +7 -4
- data/lib/blather/stanza.rb +19 -25
- data/lib/blather/stanza/disco.rb +9 -0
- data/lib/blather/stanza/disco/disco_info.rb +84 -0
- data/lib/blather/stanza/disco/disco_items.rb +59 -0
- data/lib/blather/stanza/iq.rb +16 -4
- data/lib/blather/stanza/iq/query.rb +6 -4
- data/lib/blather/stanza/iq/roster.rb +38 -38
- data/lib/blather/stanza/pubsub.rb +33 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +52 -0
- data/lib/blather/stanza/pubsub/errors.rb +9 -0
- data/lib/blather/stanza/pubsub/event.rb +21 -0
- data/lib/blather/stanza/pubsub/items.rb +59 -0
- data/lib/blather/stanza/pubsub/owner.rb +9 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +57 -0
- data/lib/blather/stream.rb +125 -57
- data/lib/blather/stream/client.rb +26 -0
- data/lib/blather/stream/component.rb +34 -0
- data/lib/blather/stream/parser.rb +17 -27
- data/lib/blather/stream/resource.rb +21 -24
- data/lib/blather/stream/sasl.rb +60 -37
- data/lib/blather/stream/session.rb +12 -19
- data/lib/blather/stream/stream_handler.rb +39 -0
- data/lib/blather/stream/tls.rb +22 -18
- data/lib/blather/xmpp_node.rb +91 -17
- data/spec/blather/core_ext/libxml_spec.rb +58 -0
- data/spec/blather/errors/sasl_error_spec.rb +56 -0
- data/spec/blather/errors/stanza_error_spec.rb +148 -0
- data/spec/blather/errors/stream_error_spec.rb +114 -0
- data/spec/blather/errors_spec.rb +40 -0
- data/spec/blather/jid_spec.rb +0 -7
- data/spec/blather/roster_item_spec.rb +5 -0
- data/spec/blather/roster_spec.rb +6 -6
- data/spec/blather/stanza/discos/disco_info_spec.rb +207 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +136 -0
- data/spec/blather/stanza/iq/query_spec.rb +9 -2
- data/spec/blather/stanza/iq/roster_spec.rb +117 -1
- data/spec/blather/stanza/iq_spec.rb +29 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +12 -1
- data/spec/blather/stanza/presence_spec.rb +29 -0
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +46 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +63 -0
- data/spec/blather/stanza/pubsub_spec.rb +26 -0
- data/spec/blather/stanza_spec.rb +13 -1
- data/spec/blather/stream/client_spec.rb +787 -0
- data/spec/blather/stream/component_spec.rb +86 -0
- data/spec/blather/xmpp_node_spec.rb +75 -22
- data/spec/fixtures/pubsub.rb +157 -0
- data/spec/spec_helper.rb +6 -14
- metadata +86 -74
- data/CHANGELOG +0 -5
- data/Manifest +0 -47
- data/blather.gemspec +0 -41
- data/lib/blather/stanza/error.rb +0 -31
- data/spec/blather/stream_spec.rb +0 -462
- data/spec/build_safe.rb +0 -20
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Prints out each roster entry
|
4
|
+
|
5
|
+
require 'blather/client'
|
6
|
+
|
7
|
+
when_ready do
|
8
|
+
roster.grouped.each do |group, items|
|
9
|
+
puts "#{'*'*3} #{group || 'Ungrouped'} #{'*'*3}"
|
10
|
+
items.each { |item| puts "- #{item.name} (#{item.jid})" }
|
11
|
+
puts
|
12
|
+
end
|
13
|
+
shutdown
|
14
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# This bot will reply to every message it receives. To end the game, send 'exit'
|
4
|
+
|
5
|
+
require 'xmpp4r/client'
|
6
|
+
include Jabber
|
7
|
+
|
8
|
+
# settings
|
9
|
+
if ARGV.length != 2
|
10
|
+
puts "Run with ./echo_thread.rb user@server/resource password"
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
myJID = JID.new(ARGV[0])
|
14
|
+
myPassword = ARGV[1]
|
15
|
+
cl = Client.new(myJID)
|
16
|
+
cl.connect
|
17
|
+
cl.auth(myPassword)
|
18
|
+
cl.send(Presence.new)
|
19
|
+
puts "Connected ! send messages to #{myJID.strip.to_s}."
|
20
|
+
mainthread = Thread.current
|
21
|
+
cl.add_message_callback do |m|
|
22
|
+
if m.type != :error
|
23
|
+
m2 = Message.new(m.from, "You sent: #{m.body}")
|
24
|
+
m2.type = m.type
|
25
|
+
cl.send(m2)
|
26
|
+
if m.body == 'exit'
|
27
|
+
m2 = Message.new(m.from, "Exiting ...")
|
28
|
+
m2.type = m.type
|
29
|
+
cl.send(m2)
|
30
|
+
mainthread.wakeup
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
Thread.stop
|
35
|
+
cl.close
|
data/ext/extconf.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'mkmf'
|
2
|
+
|
3
|
+
flags = []
|
4
|
+
|
5
|
+
case RUBY_PLATFORM.split('-',2)[1]
|
6
|
+
when 'mswin32', 'mingw32', 'bccwin32'
|
7
|
+
unless have_header('windows.h') and
|
8
|
+
have_header('winsock.h') and
|
9
|
+
have_library('kernel32') and
|
10
|
+
have_library('rpcrt4') and
|
11
|
+
have_library('gdi32')
|
12
|
+
exit
|
13
|
+
end
|
14
|
+
|
15
|
+
flags << "-D OS_WIN32"
|
16
|
+
flags << '-D BUILD_FOR_RUBY'
|
17
|
+
flags << "-EHs"
|
18
|
+
flags << "-GR"
|
19
|
+
|
20
|
+
dir_config('xml2')
|
21
|
+
exit unless have_library('xml2') && have_header('libxml/parser.h')
|
22
|
+
|
23
|
+
when /solaris/
|
24
|
+
unless have_library('pthread') and
|
25
|
+
have_library('nsl') and
|
26
|
+
have_library('socket')
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
flags << '-D OS_UNIX'
|
31
|
+
flags << '-D OS_SOLARIS8'
|
32
|
+
flags << '-D BUILD_FOR_RUBY'
|
33
|
+
|
34
|
+
dir_config('xml2')
|
35
|
+
exit unless have_library('xml2') && find_header('libxml/parser.h', '/usr/include/libxml2')
|
36
|
+
|
37
|
+
# on Unix we need a g++ link, not gcc.
|
38
|
+
#CONFIG['LDSHARED'] = "$(CXX) -shared"
|
39
|
+
|
40
|
+
when /darwin/
|
41
|
+
flags << '-DOS_UNIX'
|
42
|
+
flags << '-DBUILD_FOR_RUBY'
|
43
|
+
|
44
|
+
dir_config('xml2')
|
45
|
+
exit unless have_library('xml2') && find_header('libxml/parser.h', '/usr/include/libxml2')
|
46
|
+
# on Unix we need a g++ link, not gcc.
|
47
|
+
#CONFIG['LDSHARED'] = "$(CXX) " + CONFIG['LDSHARED'].split[1..-1].join(' ')
|
48
|
+
|
49
|
+
else
|
50
|
+
unless have_library('pthread')
|
51
|
+
exit
|
52
|
+
end
|
53
|
+
|
54
|
+
flags << '-DOS_UNIX'
|
55
|
+
flags << '-DBUILD_FOR_RUBY'
|
56
|
+
|
57
|
+
dir_config('xml2')
|
58
|
+
exit unless have_library('xml2') && find_header('libxml/parser.h', '/usr/include/libxml2')
|
59
|
+
# on Unix we need a g++ link, not gcc.
|
60
|
+
#CONFIG['LDSHARED'] = "$(CXX) -shared"
|
61
|
+
end
|
62
|
+
|
63
|
+
$CFLAGS += ' ' + flags.join(' ')
|
64
|
+
|
65
|
+
create_makefile "push_parser"
|
data/lib/blather.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
$:.unshift File.dirname(__FILE__)
|
2
|
+
$:.unshift File.join(File.dirname(__FILE__), '..')
|
2
3
|
|
3
4
|
# Require the necessary files
|
4
5
|
%w[
|
5
6
|
rubygems
|
6
|
-
xml/libxml
|
7
7
|
eventmachine
|
8
|
+
ext/push_parser
|
9
|
+
xml/libxml
|
8
10
|
digest/md5
|
9
11
|
logger
|
10
12
|
|
@@ -12,22 +14,34 @@ $:.unshift File.dirname(__FILE__)
|
|
12
14
|
blather/core_ext/libxml
|
13
15
|
|
14
16
|
blather/errors
|
17
|
+
blather/errors/sasl_error
|
18
|
+
blather/errors/stanza_error
|
19
|
+
blather/errors/stream_error
|
15
20
|
blather/jid
|
16
21
|
blather/roster
|
17
22
|
blather/roster_item
|
18
23
|
blather/xmpp_node
|
19
24
|
|
20
25
|
blather/stanza
|
21
|
-
blather/stanza/error
|
22
26
|
blather/stanza/iq
|
23
27
|
blather/stanza/iq/query
|
24
28
|
blather/stanza/iq/roster
|
29
|
+
blather/stanza/disco
|
30
|
+
blather/stanza/disco/disco_info
|
31
|
+
blather/stanza/disco/disco_items
|
25
32
|
blather/stanza/message
|
26
33
|
blather/stanza/presence
|
27
34
|
blather/stanza/presence/status
|
28
35
|
blather/stanza/presence/subscription
|
36
|
+
blather/stanza/pubsub
|
37
|
+
blather/stanza/pubsub/affiliations
|
38
|
+
blather/stanza/pubsub/subscriptions
|
39
|
+
blather/stanza/pubsub/items
|
29
40
|
|
30
41
|
blather/stream
|
42
|
+
blather/stream/client
|
43
|
+
blather/stream/component
|
44
|
+
blather/stream/stream_handler
|
31
45
|
blather/stream/parser
|
32
46
|
blather/stream/resource
|
33
47
|
blather/stream/sasl
|
@@ -38,123 +52,6 @@ $:.unshift File.dirname(__FILE__)
|
|
38
52
|
XML.indent_tree_output = false
|
39
53
|
|
40
54
|
module Blather
|
41
|
-
LOG = Logger.new(STDOUT)
|
42
|
-
|
43
|
-
# LOG.level = Logger::INFO
|
44
|
-
|
45
|
-
class Client
|
46
|
-
attr_accessor :jid,
|
47
|
-
:roster
|
48
|
-
|
49
|
-
def initialize
|
50
|
-
@state = :initializing
|
51
|
-
|
52
|
-
@status = Stanza::Presence::Status.new
|
53
|
-
@handlers = Hash.new { |h,k| h[k] = [] }
|
54
|
-
@roster = Roster.new self
|
55
|
-
|
56
|
-
setup_initial_handlers
|
57
|
-
end
|
58
|
-
|
59
|
-
def register_handler(type, &handler)
|
60
|
-
@handlers[type] << handler
|
61
|
-
end
|
62
|
-
|
63
|
-
def status
|
64
|
-
@status.state
|
65
|
-
end
|
66
|
-
|
67
|
-
def status=(state)
|
68
|
-
state, msg, to = state
|
69
|
-
|
70
|
-
status = Stanza::Presence::Status.new state, msg
|
71
|
-
status.to = to
|
72
|
-
@status = status unless to
|
73
|
-
|
74
|
-
write status
|
75
|
-
end
|
76
|
-
|
77
|
-
def write(data)
|
78
|
-
@stream.send(data) if @stream
|
79
|
-
end
|
80
|
-
|
81
|
-
def stream_started(stream)
|
82
|
-
@stream = stream
|
83
|
-
|
84
|
-
#retreive roster
|
85
|
-
write Stanza::Iq::Roster.new
|
86
|
-
end
|
87
|
-
|
88
|
-
def call(stanza)
|
89
|
-
stanza.handler_heirarchy.each do |type|
|
90
|
-
break if call_handler_for(type, stanza) && stanza.type == :iq
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def call_handler_for(type, stanza)
|
95
|
-
if @handlers[type]
|
96
|
-
@handlers[type].each { |handler| handler.call(stanza) }
|
97
|
-
true
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
protected
|
102
|
-
def setup_initial_handlers
|
103
|
-
register_handler :iq do |iq|
|
104
|
-
write(Stanza::Error.new_from(iq, 'service-unavailable', 'cancel').reply!) if [:set, :get].include?(iq.type)
|
105
|
-
end
|
106
|
-
|
107
|
-
register_handler :status do |status|
|
108
|
-
roster[status.from].status = status if roster[status.from]
|
109
|
-
end
|
110
|
-
|
111
|
-
register_handler :roster do |node|
|
112
|
-
roster.process node
|
113
|
-
if @state == :initializing
|
114
|
-
@state = :ready
|
115
|
-
write @status
|
116
|
-
call_handler_for :ready, nil
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
end #Client
|
122
|
-
|
123
|
-
module Application
|
124
|
-
@@client = Blather::Client.new
|
125
|
-
|
126
|
-
def setup(jid, password, host = nil, port = 5222)
|
127
|
-
at_exit do
|
128
|
-
trap(:INT) { EM.stop }
|
129
|
-
EM.run { Blather::Stream.start @@client, Blather::JID.new(jid), password, host, port }
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def daemonize
|
134
|
-
@daemonize = true
|
135
|
-
end
|
136
|
-
|
137
|
-
def handle(stanza_type, &block)
|
138
|
-
@@client.register_handler stanza_type, &block
|
139
|
-
end
|
140
|
-
|
141
|
-
def status(state = nil, msg = nil)
|
142
|
-
@@client.status = state, msg
|
143
|
-
end
|
144
|
-
|
145
|
-
def roster
|
146
|
-
@@client.roster
|
147
|
-
end
|
148
|
-
|
149
|
-
def write(stanza)
|
150
|
-
@@client.write(stanza)
|
151
|
-
end
|
152
|
-
|
153
|
-
def say(to, msg)
|
154
|
-
@@client.write Blather::Stanza::Message.new(to, msg)
|
155
|
-
end
|
156
|
-
end #Application
|
157
|
-
|
55
|
+
LOG = Logger.new(STDOUT) unless const_defined?(:LOG)
|
56
|
+
LOG.level = Logger::INFO
|
158
57
|
end
|
159
|
-
|
160
|
-
include Blather::Application
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[client dsl])
|
2
|
+
|
3
|
+
include Blather::DSL
|
4
|
+
|
5
|
+
at_exit do
|
6
|
+
unless client.setup?
|
7
|
+
if ARGV.length < 2
|
8
|
+
puts "Run with #{$0} user@server/resource password [host] [port]"
|
9
|
+
else
|
10
|
+
client.setup(*ARGV).run
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. blather])
|
2
|
+
require File.join(File.dirname(__FILE__), 'pubsub')
|
3
|
+
|
4
|
+
module Blather #:nodoc:
|
5
|
+
|
6
|
+
class Client #:nodoc:
|
7
|
+
attr_accessor :jid,
|
8
|
+
:roster
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@state = :initializing
|
12
|
+
|
13
|
+
@status = Stanza::Presence::Status.new
|
14
|
+
@handlers = {}
|
15
|
+
@tmp_handlers = {}
|
16
|
+
@roster = Roster.new self
|
17
|
+
|
18
|
+
setup_initial_handlers
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup?
|
22
|
+
@setup.is_a? Array
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup(jid, password, host = nil, port = nil)
|
26
|
+
@setup = [JID.new(jid), password]
|
27
|
+
@setup << host if host
|
28
|
+
@setup << port if port
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def run
|
33
|
+
raise 'not setup!' unless setup?
|
34
|
+
trap(:INT) { EM.stop }
|
35
|
+
EM.run {
|
36
|
+
klass = @setup[0].node ? Blather::Stream::Client : Blather::Stream::Component
|
37
|
+
klass.start self, *@setup
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def register_tmp_handler(id, &handler)
|
42
|
+
@tmp_handlers[id] = handler
|
43
|
+
end
|
44
|
+
|
45
|
+
def register_handler(type, *guards, &handler)
|
46
|
+
@handlers[type] ||= []
|
47
|
+
@handlers[type] << [guards, handler]
|
48
|
+
end
|
49
|
+
|
50
|
+
def status
|
51
|
+
@status.state
|
52
|
+
end
|
53
|
+
|
54
|
+
def status=(state)
|
55
|
+
state, msg, to = state
|
56
|
+
|
57
|
+
status = Stanza::Presence::Status.new state, msg
|
58
|
+
status.to = to
|
59
|
+
@status = status unless to
|
60
|
+
|
61
|
+
write status
|
62
|
+
end
|
63
|
+
|
64
|
+
def write(stanza)
|
65
|
+
stanza.from ||= jid if stanza.respond_to?(:from)
|
66
|
+
@stream.send(stanza) if @stream
|
67
|
+
end
|
68
|
+
|
69
|
+
def write_with_handler(stanza, &hanlder)
|
70
|
+
register_tmp_handler stanza.id, &handler
|
71
|
+
write stanza
|
72
|
+
end
|
73
|
+
|
74
|
+
def stream_started(stream)
|
75
|
+
@stream = stream
|
76
|
+
|
77
|
+
#retreive roster
|
78
|
+
if @stream.is_a?(Stream::Component)
|
79
|
+
@state = :ready
|
80
|
+
call_handler_for :ready, nil
|
81
|
+
else
|
82
|
+
r = Stanza::Iq::Roster.new
|
83
|
+
register_tmp_handler r.id do |node|
|
84
|
+
roster.process node
|
85
|
+
@state = :ready
|
86
|
+
write @status
|
87
|
+
call_handler_for :ready, nil
|
88
|
+
end
|
89
|
+
write r
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def stop
|
94
|
+
@stream.close_connection_after_writing
|
95
|
+
end
|
96
|
+
|
97
|
+
def stopped
|
98
|
+
EM.stop
|
99
|
+
end
|
100
|
+
|
101
|
+
def call(stanza)
|
102
|
+
if handler = @tmp_handlers.delete(stanza.id)
|
103
|
+
handler.call stanza
|
104
|
+
else
|
105
|
+
stanza.handler_heirarchy.each do |type|
|
106
|
+
break if call_handler_for(type, stanza) && (stanza.is_a?(BlatherError) || stanza.type == :iq)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def call_handler_for(type, stanza)
|
112
|
+
if @handlers[type]
|
113
|
+
@handlers[type].find { |guards, handler| handler.call(stanza) unless guarded?(guards, stanza) }
|
114
|
+
true
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
def setup_initial_handlers
|
120
|
+
register_handler :error do |err|
|
121
|
+
raise err
|
122
|
+
end
|
123
|
+
|
124
|
+
register_handler :iq do |iq|
|
125
|
+
write(StanzaError::ServiceUnavailable.new(iq, :cancel).to_node) if [:set, :get].include?(iq.type)
|
126
|
+
end
|
127
|
+
|
128
|
+
register_handler :status do |status|
|
129
|
+
roster[status.from].status = status if roster[status.from]
|
130
|
+
end
|
131
|
+
|
132
|
+
register_handler :roster do |node|
|
133
|
+
roster.process node
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# If any of the guards returns FALSE this returns true
|
139
|
+
def guarded?(guards, stanza)
|
140
|
+
guards.find do |guard|
|
141
|
+
case guard
|
142
|
+
when Symbol
|
143
|
+
!stanza.__send__(guard)
|
144
|
+
when Array
|
145
|
+
# return FALSE if any item is TRUE
|
146
|
+
!guard.detect { |condition| !guarded?([condition], stanza) }
|
147
|
+
when Hash
|
148
|
+
# return FALSE unless any inequality is found
|
149
|
+
guard.find do |method, value|
|
150
|
+
if value.is_a?(Regexp)
|
151
|
+
!stanza.__send__(method).to_s.match(value)
|
152
|
+
else
|
153
|
+
stanza.__send__(method) != value
|
154
|
+
end
|
155
|
+
end
|
156
|
+
when Proc
|
157
|
+
!guard.call(stanza)
|
158
|
+
else
|
159
|
+
raise "Bad guard: #{guard.inspect}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end #Client
|
165
|
+
end #Blather
|