blather 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|