sprsquish-blather 0.1
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/CHANGELOG +1 -0
- data/LICENSE +20 -0
- data/README.rdoc +3 -0
- data/blather.gemspec +73 -0
- data/lib/autotest/discover.rb +1 -0
- data/lib/autotest/spec.rb +60 -0
- data/lib/blather/callback.rb +24 -0
- data/lib/blather/client.rb +81 -0
- data/lib/blather/core/errors.rb +24 -0
- data/lib/blather/core/jid.rb +105 -0
- data/lib/blather/core/roster.rb +62 -0
- data/lib/blather/core/roster_item.rb +64 -0
- data/lib/blather/core/stanza.rb +90 -0
- data/lib/blather/core/stream.rb +179 -0
- data/lib/blather/core/sugar.rb +148 -0
- data/lib/blather/core/xmpp_node.rb +95 -0
- data/lib/blather/extensions/last_activity.rb +57 -0
- data/lib/blather/extensions/version.rb +85 -0
- data/lib/blather.rb +54 -0
- data/spec/blather/core/jid_spec.rb +78 -0
- data/spec/blather/core/roster_item_spec.rb +80 -0
- data/spec/blather/core/roster_spec.rb +79 -0
- data/spec/blather/core/stanza_spec.rb +95 -0
- data/spec/blather/core/stream_spec.rb +263 -0
- data/spec/blather/core/xmpp_node_spec.rb +130 -0
- data/spec/spec_helper.rb +49 -0
- metadata +116 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
module Stream
|
4
|
+
|
5
|
+
# Connect to the server
|
6
|
+
def self.start(client, jid, pass, host = nil, port = 5222)
|
7
|
+
jid = JID.new jid
|
8
|
+
host ||= jid.domain
|
9
|
+
|
10
|
+
EM.connect host, port, self, client, jid, pass
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(client, jid, pass)
|
14
|
+
super()
|
15
|
+
|
16
|
+
@client = client
|
17
|
+
|
18
|
+
self.jid = jid
|
19
|
+
@pass = pass
|
20
|
+
|
21
|
+
@to = @jid.domain
|
22
|
+
@id = nil
|
23
|
+
@lang = 'en'
|
24
|
+
@version = '1.0'
|
25
|
+
@namespace = 'jabber:client'
|
26
|
+
|
27
|
+
@parser = Parser.new self
|
28
|
+
end
|
29
|
+
|
30
|
+
def connection_completed
|
31
|
+
# @keepalive = EM::Timer.new(60) { send_data ' ' }
|
32
|
+
@state = :stopped
|
33
|
+
dispatch
|
34
|
+
end
|
35
|
+
|
36
|
+
def receive_data(data)
|
37
|
+
@parser.parse data
|
38
|
+
|
39
|
+
rescue => e
|
40
|
+
@client.respond_to?(:rescue) ? @client.rescue(e) : raise(e)
|
41
|
+
end
|
42
|
+
|
43
|
+
def unbind
|
44
|
+
# @keepalive.cancel
|
45
|
+
@state == :stopped
|
46
|
+
end
|
47
|
+
|
48
|
+
def receive(node)
|
49
|
+
LOG.debug "\n"+('-'*30)+"\n"
|
50
|
+
LOG.debug "RECEIVING (#{node.element_name}) #{node}"
|
51
|
+
@node = node
|
52
|
+
|
53
|
+
case @node.element_name
|
54
|
+
when 'stream:stream'
|
55
|
+
@state = :ready if @state == :stopped
|
56
|
+
|
57
|
+
when 'stream:end'
|
58
|
+
@state = :stopped
|
59
|
+
|
60
|
+
when 'stream:features'
|
61
|
+
@features = @node.children
|
62
|
+
@state = :features
|
63
|
+
dispatch
|
64
|
+
|
65
|
+
when 'stream:error'
|
66
|
+
raise StreamError.new(@node)
|
67
|
+
|
68
|
+
else
|
69
|
+
dispatch
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def send(stanza)
|
75
|
+
#TODO Queue if not ready
|
76
|
+
LOG.debug "SENDING: (#{caller[1]}) #{stanza}"
|
77
|
+
send_data stanza.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def stopped?
|
81
|
+
@state == :stopped
|
82
|
+
end
|
83
|
+
|
84
|
+
def ready?
|
85
|
+
@state == :ready
|
86
|
+
end
|
87
|
+
|
88
|
+
def jid=(new_jid)
|
89
|
+
LOG.debug "NEW JID: #{new_jid}"
|
90
|
+
new_jid = JID.new new_jid
|
91
|
+
@client.jid = new_jid
|
92
|
+
@jid = new_jid
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
def dispatch
|
97
|
+
__send__ @state
|
98
|
+
end
|
99
|
+
|
100
|
+
def start
|
101
|
+
send <<-STREAM
|
102
|
+
<stream:stream
|
103
|
+
to='#{@to}'
|
104
|
+
xmlns='#{@namespace}'
|
105
|
+
xmlns:stream='http://etherx.jabber.org/streams'
|
106
|
+
version='#{@version}'
|
107
|
+
xml:lang='#{@lang}'
|
108
|
+
>
|
109
|
+
STREAM
|
110
|
+
end
|
111
|
+
|
112
|
+
def stop
|
113
|
+
send '</stream:stream>'
|
114
|
+
end
|
115
|
+
|
116
|
+
def stopped
|
117
|
+
start
|
118
|
+
end
|
119
|
+
|
120
|
+
def ready
|
121
|
+
@client.call @node.to_stanza
|
122
|
+
end
|
123
|
+
|
124
|
+
def features
|
125
|
+
feature = @features.first
|
126
|
+
LOG.debug "FEATURE: #{feature}"
|
127
|
+
@state = case feature ? feature['xmlns'] : nil
|
128
|
+
when 'urn:ietf:params:xml:ns:xmpp-tls' then :establish_tls
|
129
|
+
when 'urn:ietf:params:xml:ns:xmpp-sasl' then :authenticate_sasl
|
130
|
+
when 'urn:ietf:params:xml:ns:xmpp-bind' then :bind_resource
|
131
|
+
when 'urn:ietf:params:xml:ns:xmpp-session' then :establish_session
|
132
|
+
else :ready
|
133
|
+
end
|
134
|
+
|
135
|
+
dispatch unless ready?
|
136
|
+
end
|
137
|
+
|
138
|
+
def establish_tls
|
139
|
+
unless @tls
|
140
|
+
@tls = TLS.new self
|
141
|
+
@tls.success { LOG.debug "TLS: SUCCESS"; @tls = nil; start }
|
142
|
+
@tls.failure { LOG.debug "TLS: FAILURE"; stop }
|
143
|
+
@node = @features.shift
|
144
|
+
end
|
145
|
+
@tls.receive @node
|
146
|
+
end
|
147
|
+
|
148
|
+
def authenticate_sasl
|
149
|
+
unless @sasl
|
150
|
+
@sasl = SASL.new(self, @jid, @pass)
|
151
|
+
@sasl.success { LOG.debug "SASL SUCCESS"; @sasl = nil; start }
|
152
|
+
@sasl.failure { LOG.debug "SASL FAIL"; stop }
|
153
|
+
@node = @features.shift
|
154
|
+
end
|
155
|
+
@sasl.receive @node
|
156
|
+
end
|
157
|
+
|
158
|
+
def bind_resource
|
159
|
+
unless @resource
|
160
|
+
@resource = Resource.new self, @jid
|
161
|
+
@resource.success { |jid| LOG.debug "RESOURCE: SUCCESS"; @resource = nil; self.jid = jid; @state = :features; dispatch }
|
162
|
+
@resource.failure { LOG.debug "RESOURCE: FAILURE"; stop }
|
163
|
+
@node = @features.shift
|
164
|
+
end
|
165
|
+
@resource.receive @node
|
166
|
+
end
|
167
|
+
|
168
|
+
def establish_session
|
169
|
+
unless @session
|
170
|
+
@session = Session.new self, @to
|
171
|
+
@session.success { LOG.debug "SESSION: SUCCESS"; @session = nil; @client.stream_started(self); @state = :features; dispatch }
|
172
|
+
@session.failure { LOG.debug "SESSION: FAILURE"; stop }
|
173
|
+
@node = @features.shift
|
174
|
+
end
|
175
|
+
@session.receive @node
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module LibXML
|
2
|
+
module XML
|
3
|
+
|
4
|
+
class Attributes
|
5
|
+
def remove(name)
|
6
|
+
name = name.to_s
|
7
|
+
self.each { |a| a.remove! or break if a.name == name }
|
8
|
+
end
|
9
|
+
end #Attributes
|
10
|
+
|
11
|
+
end #XML
|
12
|
+
end #LibXML
|
13
|
+
|
14
|
+
class Class # :nodoc:
|
15
|
+
def class_inheritable_reader(*syms)
|
16
|
+
syms.each do |sym|
|
17
|
+
next if sym.is_a?(Hash)
|
18
|
+
class_eval <<-EOS
|
19
|
+
def self.#{sym}
|
20
|
+
read_inheritable_attribute(:#{sym})
|
21
|
+
end
|
22
|
+
|
23
|
+
def #{sym}
|
24
|
+
self.class.#{sym}
|
25
|
+
end
|
26
|
+
EOS
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def class_inheritable_writer(*syms)
|
31
|
+
syms.each do |sym|
|
32
|
+
class_eval <<-EOS
|
33
|
+
def self.#{sym}=(obj)
|
34
|
+
write_inheritable_attribute(:#{sym}, obj)
|
35
|
+
end
|
36
|
+
EOS
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def class_inheritable_array_writer(*syms)
|
41
|
+
syms.each do |sym|
|
42
|
+
class_eval <<-EOS
|
43
|
+
def self.#{sym}=(obj)
|
44
|
+
write_inheritable_array(:#{sym}, obj)
|
45
|
+
end
|
46
|
+
EOS
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def class_inheritable_hash_writer(*syms)
|
51
|
+
syms.each do |sym|
|
52
|
+
class_eval <<-EOS
|
53
|
+
def self.#{sym}=(obj)
|
54
|
+
write_inheritable_hash(:#{sym}, obj)
|
55
|
+
end
|
56
|
+
EOS
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def class_inheritable_accessor(*syms)
|
61
|
+
class_inheritable_reader(*syms)
|
62
|
+
class_inheritable_writer(*syms)
|
63
|
+
end
|
64
|
+
|
65
|
+
def class_inheritable_array(*syms)
|
66
|
+
class_inheritable_reader(*syms)
|
67
|
+
class_inheritable_array_writer(*syms)
|
68
|
+
end
|
69
|
+
|
70
|
+
def class_inheritable_hash(*syms)
|
71
|
+
class_inheritable_reader(*syms)
|
72
|
+
class_inheritable_hash_writer(*syms)
|
73
|
+
end
|
74
|
+
|
75
|
+
def inheritable_attributes
|
76
|
+
@inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
|
77
|
+
end
|
78
|
+
|
79
|
+
def write_inheritable_attribute(key, value)
|
80
|
+
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
|
81
|
+
@inheritable_attributes = {}
|
82
|
+
end
|
83
|
+
inheritable_attributes[key] = value
|
84
|
+
end
|
85
|
+
|
86
|
+
def write_inheritable_array(key, elements)
|
87
|
+
write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
|
88
|
+
write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
|
89
|
+
end
|
90
|
+
|
91
|
+
def write_inheritable_hash(key, hash)
|
92
|
+
write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
|
93
|
+
write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
|
94
|
+
end
|
95
|
+
|
96
|
+
def read_inheritable_attribute(key)
|
97
|
+
inheritable_attributes[key]
|
98
|
+
end
|
99
|
+
|
100
|
+
def reset_inheritable_attributes
|
101
|
+
@inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
# Prevent this constant from being created multiple times
|
106
|
+
EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
|
107
|
+
|
108
|
+
def inherited_with_inheritable_attributes(child)
|
109
|
+
inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
|
110
|
+
|
111
|
+
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
|
112
|
+
new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
|
113
|
+
else
|
114
|
+
new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
|
115
|
+
memo.update(key => value.duplicable? ? value.dup : value)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
|
120
|
+
end
|
121
|
+
|
122
|
+
alias inherited_without_inheritable_attributes inherited
|
123
|
+
alias inherited inherited_with_inheritable_attributes
|
124
|
+
end #Class
|
125
|
+
|
126
|
+
class Object
|
127
|
+
def duplicable?; true; end
|
128
|
+
end
|
129
|
+
|
130
|
+
class NilClass #:nodoc:
|
131
|
+
def duplicable?; false; end
|
132
|
+
end
|
133
|
+
|
134
|
+
class FalseClass #:nodoc:
|
135
|
+
def duplicable?; false; end
|
136
|
+
end
|
137
|
+
|
138
|
+
class TrueClass #:nodoc:
|
139
|
+
def duplicable?; false; end
|
140
|
+
end
|
141
|
+
|
142
|
+
class Symbol #:nodoc:
|
143
|
+
def duplicable?; false; end
|
144
|
+
end
|
145
|
+
|
146
|
+
class Numeric #:nodoc:
|
147
|
+
def duplicable?; false; end
|
148
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Blather
|
2
|
+
|
3
|
+
class XMPPNode < XML::Node
|
4
|
+
@@registrations = {}
|
5
|
+
|
6
|
+
alias_method :element_name, :name
|
7
|
+
|
8
|
+
class_inheritable_accessor :xmlns,
|
9
|
+
:name
|
10
|
+
|
11
|
+
def self.new(name = nil, content = nil)
|
12
|
+
name ||= self.name
|
13
|
+
|
14
|
+
args = []
|
15
|
+
args << name.to_s if name
|
16
|
+
args << content if content
|
17
|
+
|
18
|
+
elem = super *args
|
19
|
+
elem.xmlns = xmlns
|
20
|
+
elem
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.register(name, xmlns = nil)
|
24
|
+
self.name = name.to_s
|
25
|
+
self.xmlns = xmlns
|
26
|
+
@@registrations[[name, xmlns]] = self
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.class_from_registration(name, xmlns)
|
30
|
+
name = name.to_s
|
31
|
+
@@registrations[[name, xmlns]] || @@registrations[[name, nil]]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.import(node)
|
35
|
+
klass = class_from_registration(node.element_name, node.xmlns)
|
36
|
+
if klass && klass != self
|
37
|
+
klass.import(node)
|
38
|
+
else
|
39
|
+
new(node.element_name).inherit(node)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_stanza
|
44
|
+
self.class.import self
|
45
|
+
end
|
46
|
+
|
47
|
+
def xmlns=(ns)
|
48
|
+
attributes.remove :xmlns
|
49
|
+
self['xmlns'] = ns if ns
|
50
|
+
end
|
51
|
+
|
52
|
+
def xmlns
|
53
|
+
self['xmlns']
|
54
|
+
end
|
55
|
+
|
56
|
+
def remove_child(name, ns = nil)
|
57
|
+
name = name.to_s
|
58
|
+
self.each { |n| n.remove! if n.element_name == name && (!ns || n.xmlns == ns) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def remove_children(name)
|
62
|
+
name = name.to_s
|
63
|
+
self.find(name).each { |n| n.remove! }
|
64
|
+
end
|
65
|
+
|
66
|
+
def content_from(name)
|
67
|
+
name = name.to_s
|
68
|
+
(child = self.detect { |n| n.element_name == name }) ? child.content : nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def copy(deep = true)
|
72
|
+
self.class.new(self.element_name).inherit(self)
|
73
|
+
end
|
74
|
+
|
75
|
+
def inherit(stanza)
|
76
|
+
inherit_attrs stanza.attributes
|
77
|
+
stanza.children.each { |c| self << c.copy(true) }
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def inherit_attrs(attrs)
|
82
|
+
attrs.each { |a| self[a.name] = a.value }
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def to_s
|
87
|
+
super.gsub(">\n<", '><')
|
88
|
+
end
|
89
|
+
|
90
|
+
def find(what, nslist = nil)
|
91
|
+
(self.doc ? super(what, nslist) : select { |i| i.element_name == what})
|
92
|
+
end
|
93
|
+
end #XMPPNode
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Blather
|
2
|
+
module Extensions
|
3
|
+
|
4
|
+
module LastActivity
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
@@last_activity = Time.now
|
8
|
+
|
9
|
+
alias_method :send_data_without_activity, :send_data
|
10
|
+
def send_data(data)
|
11
|
+
@@last_activity = Time.now
|
12
|
+
send_data_without_activity data
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def last_activity
|
18
|
+
(Time.now - @@last_activity).to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def receive_last_activity(stanza)
|
22
|
+
send_data stanza.reply!(last_activity) if stanza.type == 'get'
|
23
|
+
end
|
24
|
+
end #LastActivity
|
25
|
+
|
26
|
+
class LastActivityStanza < Query
|
27
|
+
register :last_activity, nil, 'jabber:iq:last'
|
28
|
+
|
29
|
+
def self.new(type = :get, seconds = nil)
|
30
|
+
elem = super type
|
31
|
+
elem.seconds = seconds
|
32
|
+
elem
|
33
|
+
end
|
34
|
+
|
35
|
+
def seconds=(seconds)
|
36
|
+
query.attributes.remove :seconds
|
37
|
+
query['seconds'] = seconds.to_i.to_s if seconds
|
38
|
+
end
|
39
|
+
|
40
|
+
def seconds
|
41
|
+
(query['seconds'] || 0).to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
def reply(seconds)
|
45
|
+
elem = super()
|
46
|
+
elem.last_activity = seconds
|
47
|
+
end
|
48
|
+
|
49
|
+
def reply!(seconds)
|
50
|
+
self.last_activity = seconds
|
51
|
+
super()
|
52
|
+
end
|
53
|
+
end #LastActivityStanza
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Blather::Client.__send__ :include, Blather::Extensions::LastActivity
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Blather
|
2
|
+
module Extensions
|
3
|
+
|
4
|
+
module Version
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
@@version = {}
|
8
|
+
|
9
|
+
def self.version(name, ver, os = nil)
|
10
|
+
@@version = {:name => name, :version => ver, :os => os}
|
11
|
+
end
|
12
|
+
|
13
|
+
add_callback(:iq, 100) do |c, s|
|
14
|
+
if s.detect { |n| n['xmlns'] == 'jabber:iq:version' }
|
15
|
+
ver = VersionStanza.new('result', c.version)
|
16
|
+
ver.id = s.id
|
17
|
+
ver.from = c.jid
|
18
|
+
ver.to = s.from
|
19
|
+
c.send_data ver
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def version
|
26
|
+
@@version
|
27
|
+
end
|
28
|
+
end #Version
|
29
|
+
|
30
|
+
class VersionStanza < Iq
|
31
|
+
def self.new(type = 'result', ver = {})
|
32
|
+
elem = super(type)
|
33
|
+
|
34
|
+
query = XML::Node.new('query')
|
35
|
+
query.xmlns = 'jabber:iq:version'
|
36
|
+
elem << query
|
37
|
+
|
38
|
+
elem.name = ver[:name] if ver[:name]
|
39
|
+
elem.version = ver[:version] if ver[:version]
|
40
|
+
elem.os = ver[:os] if ver[:os]
|
41
|
+
|
42
|
+
elem
|
43
|
+
end
|
44
|
+
|
45
|
+
def query
|
46
|
+
@query ||= self.detect { |n| n.name == 'query' }
|
47
|
+
end
|
48
|
+
|
49
|
+
def name=(name)
|
50
|
+
query.each { |n| n.remove! or break if n.name == 'name' }
|
51
|
+
query << XML::Node.new('name', name)
|
52
|
+
end
|
53
|
+
|
54
|
+
def name
|
55
|
+
if name = query.detect { |n| n.name == 'name' }
|
56
|
+
name.content
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def version=(version)
|
61
|
+
query.each { |n| n.remove! or break if n.name == 'version' }
|
62
|
+
query << XML::Node.new('version', version)
|
63
|
+
end
|
64
|
+
|
65
|
+
def version
|
66
|
+
if version = query.detect { |n| n.version == 'version' }
|
67
|
+
version.content
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def os=(os)
|
72
|
+
query.each { |n| n.remove! or break if n.name == 'os' }
|
73
|
+
query << XML::Node.new('os', os)
|
74
|
+
end
|
75
|
+
|
76
|
+
def os
|
77
|
+
if os = query.detect { |n| n.os == 'os' }
|
78
|
+
os.content
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end #VersionStanza
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Blather::Client.__send__ :include, Blather::Extensions::Version
|
data/lib/blather.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
%w[
|
4
|
+
rubygems
|
5
|
+
xml/libxml
|
6
|
+
eventmachine
|
7
|
+
digest/md5
|
8
|
+
logger
|
9
|
+
|
10
|
+
blather/callback
|
11
|
+
|
12
|
+
blather/core/errors
|
13
|
+
blather/core/jid
|
14
|
+
blather/core/roster
|
15
|
+
blather/core/roster_item
|
16
|
+
blather/core/sugar
|
17
|
+
blather/core/xmpp_node
|
18
|
+
|
19
|
+
blather/core/stanza
|
20
|
+
blather/core/stanza/iq
|
21
|
+
blather/core/stanza/iq/query
|
22
|
+
blather/core/stanza/iq/roster
|
23
|
+
blather/core/stanza/message
|
24
|
+
blather/core/stanza/presence
|
25
|
+
blather/core/stanza/presence/status
|
26
|
+
blather/core/stanza/presence/subscription
|
27
|
+
|
28
|
+
blather/core/stream
|
29
|
+
blather/core/stream/parser
|
30
|
+
blather/core/stream/resource
|
31
|
+
blather/core/stream/sasl
|
32
|
+
blather/core/stream/session
|
33
|
+
blather/core/stream/tls
|
34
|
+
].each { |r| require r }
|
35
|
+
|
36
|
+
XML::Parser.indent_tree_output = false
|
37
|
+
|
38
|
+
module Blather
|
39
|
+
LOG = Logger.new STDOUT
|
40
|
+
|
41
|
+
def run(jid, password, client, host = nil, port = 5222)
|
42
|
+
EM.run { Stream.start client, JID.new(jid), password, host, port }
|
43
|
+
end
|
44
|
+
module_function :run
|
45
|
+
|
46
|
+
MAJOR = 0
|
47
|
+
MINOR = 1
|
48
|
+
VERSION = [MAJOR, MINOR]*'.'
|
49
|
+
|
50
|
+
def version
|
51
|
+
VERSION
|
52
|
+
end
|
53
|
+
module_function :version
|
54
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[.. .. spec_helper])
|
2
|
+
|
3
|
+
describe 'Blather::JID' do
|
4
|
+
it 'does nothing if creaded from JID' do
|
5
|
+
jid = JID.new 'n@d/r'
|
6
|
+
JID.new(jid).object_id.must_equal jid.object_id
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'creates a new JID from (n,d,r)' do
|
10
|
+
jid = JID.new('n', 'd', 'r')
|
11
|
+
jid.node.must_equal 'n'
|
12
|
+
jid.domain.must_equal 'd'
|
13
|
+
jid.resource.must_equal 'r'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'creates a new JID from (n,d)' do
|
17
|
+
jid = JID.new('n', 'd')
|
18
|
+
jid.node.must_equal 'n'
|
19
|
+
jid.domain.must_equal 'd'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'creates a new JID from (n@d)' do
|
23
|
+
jid = JID.new('n@d')
|
24
|
+
jid.node.must_equal 'n'
|
25
|
+
jid.domain.must_equal 'd'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'creates a new JID from (n@d/r)' do
|
29
|
+
jid = JID.new('n@d/r')
|
30
|
+
jid.node.must_equal 'n'
|
31
|
+
jid.domain.must_equal 'd'
|
32
|
+
jid.resource.must_equal 'r'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'requires at least a node' do
|
36
|
+
proc { JID.new }.must_raise ArgumentError
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'ensures length of node is no more than 1023 characters' do
|
40
|
+
proc { JID.new('n'*1024) }.must_raise Blather::ArgumentError
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'ensures length of domain is no more than 1023 characters' do
|
44
|
+
proc { JID.new('n', 'd'*1024) }.must_raise Blather::ArgumentError
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'ensures length of resource is no more than 1023 characters' do
|
48
|
+
proc { JID.new('n', 'd', 'r'*1024) }.must_raise Blather::ArgumentError
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'compares JIDs' do
|
52
|
+
(JID.new('a@b/c') <=> JID.new('d@e/f')).must_equal -1
|
53
|
+
(JID.new('a@b/c') <=> JID.new('a@b/c')).must_equal 0
|
54
|
+
(JID.new('d@e/f') <=> JID.new('a@b/c')).must_equal 1
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'checks for equality' do
|
58
|
+
(JID.new('n@d/r') == JID.new('n@d/r')).must_equal true
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'will strip' do
|
62
|
+
jid = JID.new('n@d/r')
|
63
|
+
jid.stripped.must_equal JID.new('n@d')
|
64
|
+
jid.must_equal JID.new('n@d/r')
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'will strip itself' do
|
68
|
+
jid = JID.new('n@d/r')
|
69
|
+
jid.strip!
|
70
|
+
jid.must_equal JID.new('n@d')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'has a string representation' do
|
74
|
+
JID.new('n@d/r').to_s.must_equal 'n@d/r'
|
75
|
+
JID.new('n', 'd', 'r').to_s.must_equal 'n@d/r'
|
76
|
+
JID.new('n', 'd').to_s.must_equal 'n@d'
|
77
|
+
end
|
78
|
+
end
|