sprsquish-blather 0.3.4 → 0.4.0
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 +1 -1
- data/README.rdoc +41 -12
- data/examples/echo.rb +1 -1
- data/examples/execute.rb +0 -5
- data/examples/pubsub/cli.rb +64 -0
- data/examples/pubsub/ping_pong.rb +18 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/lib/blather/client/client.rb +19 -13
- data/lib/blather/client/dsl/pubsub.rb +133 -0
- data/lib/blather/client/dsl.rb +16 -0
- data/lib/blather/client.rb +1 -1
- data/lib/blather/core_ext/active_support/inheritable_attributes.rb +117 -0
- data/lib/blather/core_ext/active_support.rb +1 -117
- data/lib/blather/core_ext/nokogiri.rb +35 -0
- data/lib/blather/errors/sasl_error.rb +3 -1
- data/lib/blather/errors/stanza_error.rb +10 -17
- data/lib/blather/errors/stream_error.rb +11 -14
- data/lib/blather/errors.rb +3 -20
- data/lib/blather/jid.rb +1 -0
- data/lib/blather/roster.rb +9 -0
- data/lib/blather/roster_item.rb +6 -1
- data/lib/blather/stanza/disco/disco_info.rb +45 -33
- data/lib/blather/stanza/disco/disco_items.rb +32 -21
- data/lib/blather/stanza/disco.rb +7 -1
- data/lib/blather/stanza/iq/query.rb +16 -8
- data/lib/blather/stanza/iq/roster.rb +33 -22
- data/lib/blather/stanza/iq.rb +13 -8
- data/lib/blather/stanza/message.rb +20 -31
- data/lib/blather/stanza/presence/status.rb +13 -21
- data/lib/blather/stanza/presence/subscription.rb +11 -16
- data/lib/blather/stanza/presence.rb +3 -5
- data/lib/blather/stanza/pubsub/affiliations.rb +50 -0
- data/lib/blather/stanza/pubsub/create.rb +43 -0
- data/lib/blather/stanza/pubsub/errors.rb +9 -0
- data/lib/blather/stanza/pubsub/event.rb +77 -0
- data/lib/blather/stanza/pubsub/items.rb +63 -0
- data/lib/blather/stanza/pubsub/publish.rb +58 -0
- data/lib/blather/stanza/pubsub/retract.rb +53 -0
- data/lib/blather/stanza/pubsub/subscribe.rb +42 -0
- data/lib/blather/stanza/pubsub/subscription.rb +66 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +55 -0
- data/lib/blather/stanza/pubsub/unsubscribe.rb +42 -0
- data/lib/blather/stanza/pubsub.rb +63 -0
- data/lib/blather/stanza/pubsub_owner/delete.rb +34 -0
- data/lib/blather/stanza/pubsub_owner/purge.rb +34 -0
- data/lib/blather/stanza/pubsub_owner.rb +41 -0
- data/lib/blather/stanza.rb +35 -18
- data/lib/blather/stream/client.rb +1 -2
- data/lib/blather/stream/component.rb +9 -5
- data/lib/blather/stream/features/resource.rb +63 -0
- data/lib/blather/stream/{sasl.rb → features/sasl.rb} +53 -52
- data/lib/blather/stream/features/session.rb +44 -0
- data/lib/blather/stream/features/tls.rb +28 -0
- data/lib/blather/stream/features.rb +53 -0
- data/lib/blather/stream/parser.rb +70 -46
- data/lib/blather/stream.rb +76 -168
- data/lib/blather/xmpp_node.rb +113 -52
- data/lib/blather.rb +35 -12
- data/spec/blather/client/client_spec.rb +44 -58
- data/spec/blather/client/dsl/pubsub_spec.rb +465 -0
- data/spec/blather/client/dsl_spec.rb +19 -6
- data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
- data/spec/blather/errors/sasl_error_spec.rb +8 -8
- data/spec/blather/errors/stanza_error_spec.rb +25 -33
- data/spec/blather/errors/stream_error_spec.rb +21 -16
- data/spec/blather/errors_spec.rb +4 -11
- data/spec/blather/jid_spec.rb +31 -30
- data/spec/blather/roster_item_spec.rb +34 -23
- data/spec/blather/roster_spec.rb +27 -12
- data/spec/blather/stanza/discos/disco_info_spec.rb +61 -42
- data/spec/blather/stanza/discos/disco_items_spec.rb +47 -35
- data/spec/blather/stanza/iq/query_spec.rb +34 -11
- data/spec/blather/stanza/iq/roster_spec.rb +47 -30
- data/spec/blather/stanza/iq_spec.rb +19 -14
- data/spec/blather/stanza/message_spec.rb +30 -17
- data/spec/blather/stanza/presence/status_spec.rb +43 -20
- data/spec/blather/stanza/presence/subscription_spec.rb +41 -21
- data/spec/blather/stanza/presence_spec.rb +34 -21
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
- data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
- data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
- data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
- data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
- data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
- data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
- data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
- data/spec/blather/stanza/pubsub_spec.rb +62 -0
- data/spec/blather/stanza_spec.rb +53 -38
- data/spec/blather/stream/client_spec.rb +231 -88
- data/spec/blather/stream/component_spec.rb +14 -5
- data/spec/blather/stream/parser_spec.rb +145 -0
- data/spec/blather/xmpp_node_spec.rb +192 -96
- data/spec/fixtures/pubsub.rb +311 -0
- data/spec/spec_helper.rb +5 -4
- metadata +53 -17
- data/Rakefile +0 -139
- data/ext/extconf.rb +0 -65
- data/ext/push_parser.c +0 -209
- data/lib/blather/core_ext/libxml.rb +0 -28
- data/lib/blather/stream/resource.rb +0 -48
- data/lib/blather/stream/session.rb +0 -36
- data/lib/blather/stream/stream_handler.rb +0 -39
- data/lib/blather/stream/tls.rb +0 -33
- data/spec/blather/core_ext/libxml_spec.rb +0 -58
@@ -1,64 +1,71 @@
|
|
1
1
|
module Blather # :nodoc:
|
2
2
|
class Stream # :nodoc:
|
3
3
|
|
4
|
-
class SASL <
|
4
|
+
class SASL < Features # :nodoc:
|
5
5
|
class UnknownMechanism < BlatherError
|
6
|
-
|
7
|
-
handler_heirarchy << :unknown_mechanism
|
6
|
+
register :sasl_unknown_mechanism
|
8
7
|
end
|
9
8
|
|
10
9
|
SASL_NS = 'urn:ietf:params:xml:ns:xmpp-sasl'.freeze
|
10
|
+
register SASL_NS
|
11
11
|
|
12
|
-
def initialize(stream,
|
13
|
-
super
|
14
|
-
@jid = jid
|
15
|
-
@pass =
|
16
|
-
@mechanism_idx = 0
|
12
|
+
def initialize(stream, succeed, fail)
|
13
|
+
super
|
14
|
+
@jid = @stream.jid
|
15
|
+
@pass = @stream.password
|
17
16
|
@mechanisms = []
|
18
17
|
end
|
19
18
|
|
20
|
-
def
|
21
|
-
|
22
|
-
|
19
|
+
def receive_data(stanza)
|
20
|
+
@node = stanza
|
21
|
+
case stanza.element_name
|
22
|
+
when 'mechanisms'
|
23
|
+
@mechanisms = stanza.children.map { |m| m.content.downcase }
|
24
|
+
next!
|
25
|
+
when 'failure'
|
26
|
+
next!
|
27
|
+
when 'success'
|
28
|
+
@stream.start
|
23
29
|
else
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
30
|
+
if self.respond_to?(stanza.element_name)
|
31
|
+
self.__send__(stanza.element_name)
|
32
|
+
else
|
33
|
+
fail! UnknownResponse.new(stanza)
|
28
34
|
end
|
29
35
|
end
|
36
|
+
end
|
30
37
|
|
31
|
-
|
32
|
-
|
33
|
-
|
38
|
+
protected
|
39
|
+
def next!
|
40
|
+
if @jid.node == ''
|
41
|
+
process_anonymous
|
34
42
|
else
|
35
|
-
|
36
|
-
@
|
37
|
-
@failure.call UnknownMechanism.new("Unknown SASL mechanism (#{mechanism})")
|
38
|
-
false
|
43
|
+
@idx = @idx ? @idx+1 : 0
|
44
|
+
authenticate_with @mechanisms[@idx]
|
39
45
|
end
|
40
46
|
end
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# run out of them or none work
|
46
|
-
def handle(node)
|
47
|
-
if node.element_name == 'failure'
|
48
|
-
if @mechanisms[@mechanism_idx += 1]
|
49
|
-
set_mechanism
|
50
|
-
authenticate
|
51
|
-
else
|
52
|
-
failure node
|
53
|
-
end
|
48
|
+
def process_anonymous
|
49
|
+
if @mechanisms.include?('anonymous')
|
50
|
+
authenticate_with 'anonymous'
|
54
51
|
else
|
55
|
-
|
52
|
+
fail! BlatherError.new('The server does not support ANONYMOUS login. You must provide a node in the JID')
|
56
53
|
end
|
57
54
|
end
|
58
55
|
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
def authenticate_with(method)
|
57
|
+
method = case method
|
58
|
+
when 'digest-md5' then DigestMD5
|
59
|
+
when 'plain' then Plain
|
60
|
+
when 'anonymous' then Anonymous
|
61
|
+
when nil then fail!(SASLError.import(@node))
|
62
|
+
else next!
|
63
|
+
end
|
64
|
+
|
65
|
+
if method.is_a?(Module)
|
66
|
+
extend method
|
67
|
+
authenticate
|
68
|
+
end
|
62
69
|
end
|
63
70
|
|
64
71
|
##
|
@@ -70,19 +77,13 @@ class Stream # :nodoc:
|
|
70
77
|
##
|
71
78
|
# Builds a standard auth node
|
72
79
|
def auth_node(mechanism, content = nil)
|
73
|
-
node = XMPPNode.new 'auth'
|
74
|
-
node
|
75
|
-
node
|
80
|
+
node = XMPPNode.new 'auth'
|
81
|
+
node.content = content if content
|
82
|
+
node.namespace = SASL_NS
|
83
|
+
node[:mechanism] = mechanism
|
76
84
|
node
|
77
85
|
end
|
78
86
|
|
79
|
-
##
|
80
|
-
# Respond to the <mechanisms> node sent by the server
|
81
|
-
def mechanisms
|
82
|
-
@mechanisms = @node.children
|
83
|
-
authenticate if set_mechanism
|
84
|
-
end
|
85
|
-
|
86
87
|
##
|
87
88
|
# Digest MD5 authentication
|
88
89
|
module DigestMD5 # :nodoc:
|
@@ -111,7 +112,7 @@ class Stream # :nodoc:
|
|
111
112
|
key, value = statement.split('=')
|
112
113
|
res[key] = value.delete('"') unless key.empty?
|
113
114
|
end
|
114
|
-
|
115
|
+
Blather.logger.debug "CHALLENGE DECODE: #{res.inspect}"
|
115
116
|
|
116
117
|
@nonce ||= res['nonce']
|
117
118
|
@realm ||= res['realm']
|
@@ -129,7 +130,7 @@ class Stream # :nodoc:
|
|
129
130
|
# Send challenge response
|
130
131
|
def respond
|
131
132
|
node = XMPPNode.new 'response'
|
132
|
-
node
|
133
|
+
node.namespace = SASL_NS
|
133
134
|
|
134
135
|
unless @initial_response_sent
|
135
136
|
@initial_response_sent = true
|
@@ -146,8 +147,8 @@ class Stream # :nodoc:
|
|
146
147
|
@response[:response] = generate_response
|
147
148
|
@response.each { |k,v| @response[k] = "\"#{v}\"" unless [:nc, :qop, :response, :charset].include?(k) }
|
148
149
|
|
149
|
-
|
150
|
-
|
150
|
+
Blather.logger.debug "CHALLENGE RESPONSE: #{@response.inspect}"
|
151
|
+
Blather.logger.debug "CH RESP TXT: #{@response.map { |k,v| "#{k}=#{v}" } * ','}"
|
151
152
|
|
152
153
|
# order is to simplify testing
|
153
154
|
# Ruby 1.9 eliminates the need for this with ordered hashes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Blather # :nodoc:
|
2
|
+
class Stream # :nodoc:
|
3
|
+
|
4
|
+
class Session < Features # :nodoc:
|
5
|
+
SESSION_NS = 'urn:ietf:params:xml:ns:xmpp-session'.freeze
|
6
|
+
register SESSION_NS
|
7
|
+
|
8
|
+
def initialize(stream, succeed, fail)
|
9
|
+
super
|
10
|
+
@to = @stream.jid.domain
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive_data(stanza)
|
14
|
+
@node = stanza
|
15
|
+
case stanza.element_name
|
16
|
+
when 'session' then session
|
17
|
+
when 'iq' then check_response
|
18
|
+
else fail!(UnknownResponse.new(stanza))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def check_response
|
24
|
+
if @node[:type] == 'result'
|
25
|
+
succeed!
|
26
|
+
else
|
27
|
+
fail!(StanzaError.import(@node))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# Send a start session command
|
33
|
+
def session
|
34
|
+
response = Stanza::Iq.new :set
|
35
|
+
response.to = @to
|
36
|
+
response << (sess = XMPPNode.new('session', response.document))
|
37
|
+
sess.namespace = SESSION_NS
|
38
|
+
|
39
|
+
@stream.send response
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Blather # :nodoc:
|
2
|
+
class Stream # :nodoc:
|
3
|
+
|
4
|
+
class TLS < Features # :nodoc:
|
5
|
+
class TLSFailure < BlatherError
|
6
|
+
register :tls_failure
|
7
|
+
end
|
8
|
+
|
9
|
+
TLS_NS = 'urn:ietf:params:xml:ns:xmpp-tls'.freeze
|
10
|
+
register TLS_NS
|
11
|
+
|
12
|
+
def receive_data(stanza)
|
13
|
+
case stanza.element_name
|
14
|
+
when 'starttls'
|
15
|
+
@stream.send "<starttls xmlns='#{TLS_NS}'/>"
|
16
|
+
when 'proceed'
|
17
|
+
@stream.start_tls
|
18
|
+
@stream.start
|
19
|
+
# succeed!
|
20
|
+
else
|
21
|
+
fail! TLSFailure.new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end #TLS
|
26
|
+
|
27
|
+
end #Stream
|
28
|
+
end #Blather
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Blather # :nodoc:
|
2
|
+
class Stream # :nodoc:
|
3
|
+
|
4
|
+
class Features
|
5
|
+
@@features = {}
|
6
|
+
def self.register(ns)
|
7
|
+
@@features[ns] = self
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.from_namespace(ns)
|
11
|
+
@@features[ns]
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(stream, succeed, fail)
|
15
|
+
@stream = stream
|
16
|
+
@succeed = succeed
|
17
|
+
@fail = fail
|
18
|
+
end
|
19
|
+
|
20
|
+
def receive_data(stanza)
|
21
|
+
if @feature
|
22
|
+
@feature.receive_data stanza
|
23
|
+
else
|
24
|
+
@features ||= stanza
|
25
|
+
next!
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def next!
|
30
|
+
@idx = @idx ? @idx+1 : 0
|
31
|
+
if stanza = @features.children[@idx]
|
32
|
+
if stanza.namespaces['xmlns'] && (klass = self.class.from_namespace(stanza.namespaces['xmlns']))
|
33
|
+
@feature = klass.new @stream, proc { next! }, @fail
|
34
|
+
@feature.receive_data stanza
|
35
|
+
else
|
36
|
+
next!
|
37
|
+
end
|
38
|
+
else
|
39
|
+
succeed!
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def succeed!
|
44
|
+
@succeed.call
|
45
|
+
end
|
46
|
+
|
47
|
+
def fail!(msg)
|
48
|
+
@fail.call msg
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end #Stream
|
53
|
+
end #Blather
|
@@ -1,9 +1,10 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
1
3
|
module Blather # :nodoc:
|
2
4
|
class Stream # :nodoc:
|
3
5
|
|
4
6
|
class Parser # :nodoc:
|
5
|
-
|
6
|
-
ERROR_REGEX = /^<(stream:[a-z]+)/.freeze
|
7
|
+
NS_TO_IGNORE = %w[jabber:client jabber:component:accept]
|
7
8
|
|
8
9
|
@@debug = !false
|
9
10
|
def self.debug; @@debug; end
|
@@ -12,70 +13,93 @@ class Stream # :nodoc:
|
|
12
13
|
def initialize(receiver)
|
13
14
|
@receiver = receiver
|
14
15
|
@current = nil
|
15
|
-
@
|
16
|
+
@namespaces = {}
|
17
|
+
@namespace_definitions = []
|
18
|
+
@parser = Nokogiri::XML::SAX::PushParser.new self
|
16
19
|
end
|
17
20
|
|
18
21
|
def receive_data(string)
|
19
|
-
|
20
|
-
@
|
21
|
-
|
22
|
+
Blather.logger.debug "PARSING: (#{string})" if @@debug
|
23
|
+
@parser << string
|
24
|
+
self
|
22
25
|
end
|
26
|
+
alias_method :<<, :receive_data
|
23
27
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
e = XMPPNode.new elem
|
28
|
-
attrs.each { |k,v| e.attributes[k] = v if k }
|
28
|
+
def start_document; end
|
29
|
+
def end_document; end
|
30
|
+
def warning(*args); end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
else
|
33
|
-
e.namespace = {prefix => uri}
|
34
|
-
end
|
32
|
+
def start_element_ns(elem, attrs, prefix, uri, namespaces)
|
33
|
+
Blather.logger.debug "START ELEM: (#{{:elem => elem, :attrs => attrs, :prefix => prefix, :uri => uri, :ns => namespaces}.inspect})" if @@debug
|
35
34
|
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
args = [elem]
|
36
|
+
args << @current.document if @current
|
37
|
+
node = XMPPNode.new *args
|
38
|
+
node.document.root = node unless @current
|
39
39
|
|
40
|
-
|
41
|
-
@current << e if @current
|
42
|
-
@current = e
|
40
|
+
attrs.each { |k,v| node[k] = v if k }
|
43
41
|
|
42
|
+
if !@receiver.stopped?
|
43
|
+
@current << node if @current
|
44
|
+
@current = node
|
44
45
|
end
|
45
|
-
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
namespaces.delete_if { |pre, href| NS_TO_IGNORE.include? href }
|
48
|
+
@namespace_definitions.push []
|
49
|
+
namespaces.each do |pre, href|
|
50
|
+
next if @namespace_definitions.flatten.include?(@namespaces[[pre, href]])
|
51
|
+
ns = node.add_namespace(pre, href)
|
52
|
+
@namespaces[[pre, href]] ||= ns
|
53
|
+
end
|
54
|
+
@namespaces[[prefix, uri]] ||= node.add_namespace(prefix, uri) if prefix && !namespaces[prefix]
|
55
|
+
node.namespace = @namespaces[[prefix, uri]]
|
56
|
+
|
57
|
+
deliver(node) if elem == 'stream'
|
58
|
+
|
59
|
+
=begin
|
60
|
+
$stderr.puts "\n\n"
|
61
|
+
$stderr.puts [elem, attrs, prefix, uri, namespaces].inspect
|
62
|
+
$stderr.puts @namespaces.inspect
|
63
|
+
$stderr.puts [@namespaces[[prefix, uri]].prefix, @namespaces[[prefix, uri]].href].inspect if @namespaces[[prefix, uri]]
|
64
|
+
$stderr.puts node.inspect
|
65
|
+
$stderr.puts node.document.to_s.gsub(/\n\s*/,'')
|
66
|
+
=end
|
50
67
|
end
|
51
68
|
|
52
|
-
def
|
53
|
-
|
69
|
+
def end_element_ns(elem, prefix, uri)
|
70
|
+
Blather.logger.debug "END ELEM: #{{:elem => elem, :prefix => prefix, :uri => uri}.inspect}" if @@debug
|
54
71
|
|
55
|
-
if
|
56
|
-
|
57
|
-
|
58
|
-
|
72
|
+
if elem == 'stream'
|
73
|
+
node = XMPPNode.new('end')
|
74
|
+
node.namespace = {prefix => uri}
|
75
|
+
deliver node
|
76
|
+
elsif @current.parent != @current.document
|
77
|
+
@namespace_definitions.pop
|
59
78
|
@current = @current.parent
|
60
|
-
|
61
79
|
else
|
62
|
-
|
63
|
-
XML::Document.new.root = c
|
64
|
-
@receiver.receive c
|
65
|
-
|
80
|
+
deliver @current
|
66
81
|
end
|
67
82
|
end
|
68
83
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
84
|
+
def characters(chars = '')
|
85
|
+
Blather.logger.debug "CHARS: #{chars}" if @@debug
|
86
|
+
@current << Nokogiri::XML::Text.new(chars, @current.document) if @current
|
87
|
+
end
|
88
|
+
|
89
|
+
def warning(msg)
|
90
|
+
Blather.logger.debug "PARSE WARNING: #{msg}" if @@debug
|
91
|
+
end
|
92
|
+
|
93
|
+
def error(msg)
|
94
|
+
raise ParseError.new(msg)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def deliver(node)
|
99
|
+
@current, @namespaces, @namespace_definitions = nil, {}, []
|
100
|
+
@receiver.receive node
|
77
101
|
end
|
78
102
|
end #Parser
|
79
103
|
|
80
104
|
end #Stream
|
81
|
-
end #Blather
|
105
|
+
end #Blather
|