jabber4r-revive 0.9.0 → 0.10.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/.gitignore +5 -4
- data/.rspec +3 -3
- data/.travis.yml +7 -7
- data/CHANGELOG +11 -1
- data/Gemfile +3 -3
- data/README.md +29 -29
- data/Rakefile +70 -70
- data/jabber4r-revive.gemspec +25 -25
- data/lib/jabber4r.rb +38 -33
- data/lib/jabber4r/bosh.rb +21 -0
- data/lib/jabber4r/bosh/authentication.rb +13 -0
- data/lib/jabber4r/bosh/authentication/non_sasl.rb +219 -0
- data/lib/jabber4r/bosh/authentication/sasl.rb +239 -0
- data/lib/jabber4r/bosh/session.rb +144 -0
- data/lib/jabber4r/connection.rb +259 -258
- data/lib/jabber4r/debugger.rb +60 -60
- data/lib/jabber4r/jid.rb +20 -19
- data/lib/jabber4r/protocol.rb +249 -257
- data/lib/jabber4r/protocol/authentication.rb +14 -0
- data/lib/jabber4r/protocol/authentication/non_sasl.rb +138 -0
- data/lib/jabber4r/protocol/authentication/sasl.rb +88 -0
- data/lib/jabber4r/protocol/iq.rb +259 -259
- data/lib/jabber4r/protocol/message.rb +245 -245
- data/lib/jabber4r/protocol/parsed_xml_element.rb +207 -207
- data/lib/jabber4r/protocol/presence.rb +160 -160
- data/lib/jabber4r/protocol/xml_element.rb +143 -143
- data/lib/jabber4r/rexml_1.8_patch.rb +15 -15
- data/lib/jabber4r/roster.rb +38 -38
- data/lib/jabber4r/session.rb +615 -615
- data/lib/jabber4r/version.rb +10 -3
- data/spec/lib/jabber4r/bosh/authentication/non_sasl_spec.rb +79 -0
- data/spec/lib/jabber4r/bosh/authentication/sasl_spec.rb +42 -0
- data/spec/lib/jabber4r/bosh/session_spec.rb +406 -0
- data/spec/lib/jabber4r/bosh_spec.rb +0 -0
- data/spec/lib/jabber4r/connection_spec.rb +174 -174
- data/spec/lib/jabber4r/debugger_spec.rb +35 -35
- data/spec/lib/jabber4r/jid_spec.rb +197 -197
- data/spec/lib/jabber4r/protocol/authentication/non_sasl_spec.rb +79 -0
- data/spec/lib/jabber4r/protocol/authentication/sasl_spec.rb +42 -0
- data/spec/spec_helper.rb +11 -11
- data/spec/support/mocks/tcp_socket_mock.rb +8 -8
- metadata +61 -45
- data/Gemfile.lock +0 -45
- data/lib/jabber4r/bosh_session.rb +0 -224
- data/spec/lib/jabber4r/bosh_session_spec.rb +0 -150
data/lib/jabber4r/debugger.rb
CHANGED
@@ -1,61 +1,61 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
# License: see LICENSE
|
4
|
-
# Jabber4R - Jabber Instant Messaging Library for Ruby
|
5
|
-
# Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
|
6
|
-
# Copyright (C) 2013 Sergey Fedorov <strech_ftf@mail.ru>
|
7
|
-
|
8
|
-
require "singleton"
|
9
|
-
require "logger"
|
10
|
-
|
11
|
-
module Jabber
|
12
|
-
# Produces debug methods
|
13
|
-
#
|
14
|
-
# Example
|
15
|
-
#
|
16
|
-
# def warn(message)
|
17
|
-
# Debug.instance.send(:warn, message)
|
18
|
-
# end
|
19
|
-
# module_function m
|
20
|
-
[:warn, :debug, :info].each do |m|
|
21
|
-
define_method(m) do |message|
|
22
|
-
Debugger.send(m, message)
|
23
|
-
end
|
24
|
-
module_function m
|
25
|
-
end
|
26
|
-
|
27
|
-
class Debugger
|
28
|
-
include Singleton
|
29
|
-
|
30
|
-
attr_accessor :enabled, :logger
|
31
|
-
|
32
|
-
def initialize
|
33
|
-
@logger = Logger.new(STDOUT)
|
34
|
-
@enabled = false
|
35
|
-
end
|
36
|
-
|
37
|
-
class << self
|
38
|
-
def logger=(logger)
|
39
|
-
instance.logger = logger
|
40
|
-
end
|
41
|
-
|
42
|
-
def enable!
|
43
|
-
instance.enabled = true
|
44
|
-
end
|
45
|
-
|
46
|
-
def disable!
|
47
|
-
instance.enabled = false
|
48
|
-
end
|
49
|
-
|
50
|
-
def enabled?
|
51
|
-
instance.enabled
|
52
|
-
end
|
53
|
-
|
54
|
-
[:warn, :debug, :info].each do |m|
|
55
|
-
define_method(m) do |message|
|
56
|
-
enabled? && instance.logger.send(m, message)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end # class << self
|
60
|
-
end
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# License: see LICENSE
|
4
|
+
# Jabber4R - Jabber Instant Messaging Library for Ruby
|
5
|
+
# Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
|
6
|
+
# Copyright (C) 2013 Sergey Fedorov <strech_ftf@mail.ru>
|
7
|
+
|
8
|
+
require "singleton"
|
9
|
+
require "logger"
|
10
|
+
|
11
|
+
module Jabber
|
12
|
+
# Produces debug methods
|
13
|
+
#
|
14
|
+
# Example
|
15
|
+
#
|
16
|
+
# def warn(message)
|
17
|
+
# Debug.instance.send(:warn, message)
|
18
|
+
# end
|
19
|
+
# module_function m
|
20
|
+
[:warn, :debug, :info].each do |m|
|
21
|
+
define_method(m) do |message|
|
22
|
+
Debugger.send(m, message)
|
23
|
+
end
|
24
|
+
module_function m
|
25
|
+
end
|
26
|
+
|
27
|
+
class Debugger
|
28
|
+
include Singleton
|
29
|
+
|
30
|
+
attr_accessor :enabled, :logger
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@logger = Logger.new(STDOUT)
|
34
|
+
@enabled = false
|
35
|
+
end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
def logger=(logger)
|
39
|
+
instance.logger = logger
|
40
|
+
end
|
41
|
+
|
42
|
+
def enable!
|
43
|
+
instance.enabled = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def disable!
|
47
|
+
instance.enabled = false
|
48
|
+
end
|
49
|
+
|
50
|
+
def enabled?
|
51
|
+
instance.enabled
|
52
|
+
end
|
53
|
+
|
54
|
+
[:warn, :debug, :info].each do |m|
|
55
|
+
define_method(m) do |message|
|
56
|
+
enabled? && instance.logger.send(m, message)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end # class << self
|
60
|
+
end
|
61
61
|
end
|
data/lib/jabber4r/jid.rb
CHANGED
@@ -3,20 +3,21 @@
|
|
3
3
|
# License: see LICENSE
|
4
4
|
# Jabber4R - Jabber Instant Messaging Library for Ruby
|
5
5
|
# Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
|
6
|
+
# Copyright (C) 2013 Sergey Fedorov <strech_ftf@mail.ru>
|
6
7
|
|
7
8
|
module Jabber
|
8
|
-
# The Jabber ID class is used to hold a parsed jabber identifier (account+
|
9
|
+
# The Jabber ID class is used to hold a parsed jabber identifier (account+domain+resource)
|
9
10
|
class JID
|
10
|
-
PATTERN = /^(?:(?<node>[^@]*)@)??(?<
|
11
|
+
PATTERN = /^(?:(?<node>[^@]*)@)??(?<domain>[^@\/]*)(?:\/(?<resource>.*?))?$/.freeze
|
11
12
|
|
12
|
-
# The node (account)
|
13
|
+
# Public: The node (account)
|
13
14
|
attr_accessor :node
|
14
15
|
|
15
|
-
# The resource id
|
16
|
+
# Public: The resource id
|
16
17
|
attr_accessor :resource
|
17
18
|
|
18
|
-
# The
|
19
|
-
attr_accessor :
|
19
|
+
# Public: The domain indentificator (or IP address)
|
20
|
+
attr_accessor :domain
|
20
21
|
|
21
22
|
# Public: Convert something to Jabber::JID
|
22
23
|
#
|
@@ -30,34 +31,34 @@ module Jabber
|
|
30
31
|
end
|
31
32
|
|
32
33
|
# Constructs a JID from the supplied string of the format:
|
33
|
-
# node@
|
34
|
+
# node@domain[/resource] (e.g. "rich_kilmer@jabber.com/laptop")
|
34
35
|
#
|
35
36
|
# jid - String the jabber id string to parse
|
36
|
-
#
|
37
|
+
# domain - String the domain of jabber server (optional)
|
37
38
|
# resource - String the resource of jabber id (optional)
|
38
39
|
#
|
39
40
|
# Examples
|
40
41
|
#
|
41
42
|
# jid = Jabber::JID.new("strech@localhost/attach")
|
42
43
|
# jid.node # => "strech"
|
43
|
-
# jid.
|
44
|
+
# jid.domain # => "localhost"
|
44
45
|
# jid.resource # => "attach"
|
45
46
|
#
|
46
47
|
# Raises ArgumentError
|
47
48
|
# Returns nothing
|
48
|
-
def initialize(jid,
|
49
|
+
def initialize(jid, domain = nil, resource = nil)
|
49
50
|
raise ArgumentError, "Node can't be empty" if jid.to_s.empty?
|
50
51
|
|
51
|
-
@node, @
|
52
|
-
@node, @
|
52
|
+
@node, @domain, @resource = self.class.parse(jid)
|
53
|
+
@node, @domain = @domain, nil if @node.nil? && @domain
|
53
54
|
|
54
|
-
@
|
55
|
+
@domain = domain unless domain.nil?
|
55
56
|
@resource = resource unless resource.nil?
|
56
57
|
|
57
|
-
raise ArgumentError, "Couldn't create JID without
|
58
|
+
raise ArgumentError, "Couldn't create JID without domain" if @domain.to_s.empty?
|
58
59
|
end
|
59
60
|
|
60
|
-
# Public: Evalutes whether the node, resource and
|
61
|
+
# Public: Evalutes whether the node, resource and domain are the same
|
61
62
|
#
|
62
63
|
# jid - Jabber::JID the other jabber id
|
63
64
|
#
|
@@ -74,14 +75,14 @@ module Jabber
|
|
74
75
|
def same?(jid)
|
75
76
|
other_jid = self.class.to_jid(jid)
|
76
77
|
|
77
|
-
other_jid.node == node && other_jid.
|
78
|
+
other_jid.node == node && other_jid.domain == domain
|
78
79
|
end
|
79
80
|
|
80
81
|
# Public: Strip resource from jid and return new object
|
81
82
|
#
|
82
83
|
# Returns Jabber::JID
|
83
84
|
def strip
|
84
|
-
self.class.new(node,
|
85
|
+
self.class.new(node, domain)
|
85
86
|
end
|
86
87
|
|
87
88
|
# Public: Strip resource from jid and return the same object
|
@@ -97,7 +98,7 @@ module Jabber
|
|
97
98
|
#
|
98
99
|
# Returns String
|
99
100
|
def to_s
|
100
|
-
["#{node}@#{
|
101
|
+
["#{node}@#{domain}", resource].compact.join "/"
|
101
102
|
end
|
102
103
|
|
103
104
|
# Public: Override #hash to hash based on the to_s method
|
@@ -108,7 +109,7 @@ module Jabber
|
|
108
109
|
end
|
109
110
|
|
110
111
|
private
|
111
|
-
# Internal: Parse jid string for node,
|
112
|
+
# Internal: Parse jid string for node, domain, resource
|
112
113
|
#
|
113
114
|
# jid - String jabber id
|
114
115
|
#
|
data/lib/jabber4r/protocol.rb
CHANGED
@@ -1,257 +1,249 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
# License: see LICENSE
|
4
|
-
# Jabber4R - Jabber Instant Messaging Library for Ruby
|
5
|
-
# Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
|
6
|
-
|
7
|
-
require "singleton"
|
8
|
-
require "socket"
|
9
|
-
|
10
|
-
module Jabber
|
11
|
-
class JabberConnectionException < RuntimeError
|
12
|
-
attr_reader :data
|
13
|
-
|
14
|
-
def initialize(writing, data)
|
15
|
-
@writing = writing
|
16
|
-
@data = data
|
17
|
-
end
|
18
|
-
|
19
|
-
def writing?
|
20
|
-
@writing
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
##
|
25
|
-
# The Protocol module contains helper methods for constructing
|
26
|
-
# Jabber protocol elements and classes that implement protocol
|
27
|
-
# elements.
|
28
|
-
#
|
29
|
-
module Protocol
|
30
|
-
|
31
|
-
USE_PARSER = :rexml # either :rexml or :xmlparser
|
32
|
-
|
33
|
-
##
|
34
|
-
# The parser to use for stream processing. The current
|
35
|
-
# available parsers are:
|
36
|
-
#
|
37
|
-
# * Jabber::Protocol::ExpatJabberParser uses XMLParser
|
38
|
-
# * Jabber::Protocol::REXMLJabberParser uses REXML
|
39
|
-
#
|
40
|
-
# return:: [Class] The parser class
|
41
|
-
#
|
42
|
-
def Protocol.Parser
|
43
|
-
if USE_PARSER==:xmlparser
|
44
|
-
Jabber::Protocol::ExpatJabberParser
|
45
|
-
else
|
46
|
-
Jabber::Protocol::REXMLJabberParser
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
##
|
51
|
-
# Generates an open stream XML element
|
52
|
-
#
|
53
|
-
#
|
54
|
-
# return:: [String] The XML data to send
|
55
|
-
#
|
56
|
-
def self.gen_open_stream(
|
57
|
-
return ('<?xml version="1.0" encoding="UTF-8" ?><stream:stream to="'+
|
58
|
-
end
|
59
|
-
|
60
|
-
##
|
61
|
-
# Generates an close stream XML element
|
62
|
-
#
|
63
|
-
# return:: [String] The XML data to send
|
64
|
-
#
|
65
|
-
def self.gen_close_stream
|
66
|
-
return "</stream:stream>"
|
67
|
-
end
|
68
|
-
|
69
|
-
if USE_PARSER == :xmlparser
|
70
|
-
require 'xmlparser'
|
71
|
-
##
|
72
|
-
# The ExpatJabberParser uses XMLParser (expat) to parse the incoming XML stream
|
73
|
-
# of the Jabber protocol and fires ParsedXMLElements at the Connection
|
74
|
-
# instance.
|
75
|
-
#
|
76
|
-
class ExpatJabberParser
|
77
|
-
|
78
|
-
# status if the parser is started
|
79
|
-
attr_reader :started
|
80
|
-
|
81
|
-
##
|
82
|
-
# Constructs a parser for the supplied stream (socket input)
|
83
|
-
#
|
84
|
-
# stream:: [IO] Socket input stream
|
85
|
-
# listener:: [#receive(ParsedXMLElement)] The listener (usually a Jabber::Protocol::Connection instance
|
86
|
-
#
|
87
|
-
def initialize(stream, listener)
|
88
|
-
@stream = stream
|
89
|
-
def @stream.gets
|
90
|
-
super(">")
|
91
|
-
end
|
92
|
-
@listener = listener
|
93
|
-
end
|
94
|
-
|
95
|
-
##
|
96
|
-
# Begins parsing the XML stream and does not return until
|
97
|
-
# the stream closes.
|
98
|
-
#
|
99
|
-
def parse
|
100
|
-
@started = false
|
101
|
-
|
102
|
-
parser = XMLParser.new("UTF-8")
|
103
|
-
def parser.unknownEncoding(e)
|
104
|
-
raise "Unknown encoding #{e.to_s}"
|
105
|
-
end
|
106
|
-
def parser.default
|
107
|
-
end
|
108
|
-
|
109
|
-
begin
|
110
|
-
parser.parse(@stream) do |type, name, data|
|
111
|
-
begin
|
112
|
-
case type
|
113
|
-
when XMLParser::START_ELEM
|
114
|
-
case name
|
115
|
-
when "stream:stream"
|
116
|
-
openstream = ParsedXMLElement.new(name)
|
117
|
-
data.each {|key, value| openstream.add_attribute(key, value)}
|
118
|
-
@listener.receive(openstream)
|
119
|
-
@started = true
|
120
|
-
else
|
121
|
-
if @current.nil?
|
122
|
-
@current = ParsedXMLElement.new(name.clone)
|
123
|
-
else
|
124
|
-
@current = @current.add_child(name.clone)
|
125
|
-
end
|
126
|
-
data.each {|key, value| @current.add_attribute(key.clone, value.clone)}
|
127
|
-
end
|
128
|
-
when XMLParser::CDATA
|
129
|
-
@current.append_data(data.clone) if @current
|
130
|
-
when XMLParser::END_ELEM
|
131
|
-
case name
|
132
|
-
when "stream:stream"
|
133
|
-
@started = false
|
134
|
-
else
|
135
|
-
@listener.receive(@current) unless @current.element_parent
|
136
|
-
@current = @current.element_parent
|
137
|
-
end
|
138
|
-
end
|
139
|
-
rescue
|
140
|
-
puts "Error #{$!}"
|
141
|
-
end
|
142
|
-
end
|
143
|
-
rescue XMLParserError
|
144
|
-
line = parser.line
|
145
|
-
print "XML Parsing error(#{line}): #{$!}\n"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
else # USE REXML
|
150
|
-
require 'rexml/document'
|
151
|
-
require 'rexml/parsers/sax2parser'
|
152
|
-
require 'rexml/source'
|
153
|
-
|
154
|
-
##
|
155
|
-
# The REXMLJabberParser uses REXML to parse the incoming XML stream
|
156
|
-
# of the Jabber protocol and fires ParsedXMLElements at the Connection
|
157
|
-
# instance.
|
158
|
-
#
|
159
|
-
class REXMLJabberParser
|
160
|
-
# status if the parser is started
|
161
|
-
attr_reader :started
|
162
|
-
|
163
|
-
##
|
164
|
-
# Constructs a parser for the supplied stream (socket input)
|
165
|
-
#
|
166
|
-
# stream:: [IO] Socket input stream
|
167
|
-
# listener:: [Object.receive(ParsedXMLElement)] The listener (usually a Jabber::Protocol::Connection instance
|
168
|
-
#
|
169
|
-
def initialize(stream, listener)
|
170
|
-
@stream = stream
|
171
|
-
|
172
|
-
# this hack fixes REXML version "2.7.3" and "2.7.4"
|
173
|
-
if REXML::Version=="2.7.3" || REXML::Version=="2.7.4"
|
174
|
-
def @stream.read(len=nil)
|
175
|
-
len = 100 unless len
|
176
|
-
super(len)
|
177
|
-
end
|
178
|
-
def @stream.gets(char=nil)
|
179
|
-
super(">")
|
180
|
-
end
|
181
|
-
def @stream.readline(char=nil)
|
182
|
-
super(">")
|
183
|
-
end
|
184
|
-
def @stream.readlines(char=nil)
|
185
|
-
super(">")
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
@listener = listener
|
190
|
-
@current = nil
|
191
|
-
end
|
192
|
-
|
193
|
-
##
|
194
|
-
# Begins parsing the XML stream and does not return until
|
195
|
-
# the stream closes.
|
196
|
-
#
|
197
|
-
def parse
|
198
|
-
#puts "PARSE"
|
199
|
-
@started = false
|
200
|
-
begin
|
201
|
-
parser = REXML::Parsers::SAX2Parser.new @stream
|
202
|
-
|
203
|
-
parser.listen(:end_document) do
|
204
|
-
raise Jabber::ConnectionForceCloseError
|
205
|
-
end
|
206
|
-
|
207
|
-
parser.listen( :start_element ) do |uri, localname, qname, attributes|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
@
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
@current.append_data(text) if @current
|
237
|
-
end
|
238
|
-
parser.
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
@listener.parse_failure(e)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
end
|
254
|
-
end # USE_PARSER
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# License: see LICENSE
|
4
|
+
# Jabber4R - Jabber Instant Messaging Library for Ruby
|
5
|
+
# Copyright (C) 2002 Rich Kilmer <rich@infoether.com>
|
6
|
+
|
7
|
+
require "singleton"
|
8
|
+
require "socket"
|
9
|
+
|
10
|
+
module Jabber
|
11
|
+
class JabberConnectionException < RuntimeError
|
12
|
+
attr_reader :data
|
13
|
+
|
14
|
+
def initialize(writing, data)
|
15
|
+
@writing = writing
|
16
|
+
@data = data
|
17
|
+
end
|
18
|
+
|
19
|
+
def writing?
|
20
|
+
@writing
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# The Protocol module contains helper methods for constructing
|
26
|
+
# Jabber protocol elements and classes that implement protocol
|
27
|
+
# elements.
|
28
|
+
#
|
29
|
+
module Protocol
|
30
|
+
|
31
|
+
USE_PARSER = :rexml # either :rexml or :xmlparser
|
32
|
+
|
33
|
+
##
|
34
|
+
# The parser to use for stream processing. The current
|
35
|
+
# available parsers are:
|
36
|
+
#
|
37
|
+
# * Jabber::Protocol::ExpatJabberParser uses XMLParser
|
38
|
+
# * Jabber::Protocol::REXMLJabberParser uses REXML
|
39
|
+
#
|
40
|
+
# return:: [Class] The parser class
|
41
|
+
#
|
42
|
+
def Protocol.Parser
|
43
|
+
if USE_PARSER==:xmlparser
|
44
|
+
Jabber::Protocol::ExpatJabberParser
|
45
|
+
else
|
46
|
+
Jabber::Protocol::REXMLJabberParser
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Generates an open stream XML element
|
52
|
+
#
|
53
|
+
# domain:: [String] The domain being connected to
|
54
|
+
# return:: [String] The XML data to send
|
55
|
+
#
|
56
|
+
def self.gen_open_stream(domain)
|
57
|
+
return ('<?xml version="1.0" encoding="UTF-8" ?><stream:stream to="'+domain+'" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">')
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# Generates an close stream XML element
|
62
|
+
#
|
63
|
+
# return:: [String] The XML data to send
|
64
|
+
#
|
65
|
+
def self.gen_close_stream
|
66
|
+
return "</stream:stream>"
|
67
|
+
end
|
68
|
+
|
69
|
+
if USE_PARSER == :xmlparser
|
70
|
+
require 'xmlparser'
|
71
|
+
##
|
72
|
+
# The ExpatJabberParser uses XMLParser (expat) to parse the incoming XML stream
|
73
|
+
# of the Jabber protocol and fires ParsedXMLElements at the Connection
|
74
|
+
# instance.
|
75
|
+
#
|
76
|
+
class ExpatJabberParser
|
77
|
+
|
78
|
+
# status if the parser is started
|
79
|
+
attr_reader :started
|
80
|
+
|
81
|
+
##
|
82
|
+
# Constructs a parser for the supplied stream (socket input)
|
83
|
+
#
|
84
|
+
# stream:: [IO] Socket input stream
|
85
|
+
# listener:: [#receive(ParsedXMLElement)] The listener (usually a Jabber::Protocol::Connection instance
|
86
|
+
#
|
87
|
+
def initialize(stream, listener)
|
88
|
+
@stream = stream
|
89
|
+
def @stream.gets
|
90
|
+
super(">")
|
91
|
+
end
|
92
|
+
@listener = listener
|
93
|
+
end
|
94
|
+
|
95
|
+
##
|
96
|
+
# Begins parsing the XML stream and does not return until
|
97
|
+
# the stream closes.
|
98
|
+
#
|
99
|
+
def parse
|
100
|
+
@started = false
|
101
|
+
|
102
|
+
parser = XMLParser.new("UTF-8")
|
103
|
+
def parser.unknownEncoding(e)
|
104
|
+
raise "Unknown encoding #{e.to_s}"
|
105
|
+
end
|
106
|
+
def parser.default
|
107
|
+
end
|
108
|
+
|
109
|
+
begin
|
110
|
+
parser.parse(@stream) do |type, name, data|
|
111
|
+
begin
|
112
|
+
case type
|
113
|
+
when XMLParser::START_ELEM
|
114
|
+
case name
|
115
|
+
when "stream:stream"
|
116
|
+
openstream = ParsedXMLElement.new(name)
|
117
|
+
data.each {|key, value| openstream.add_attribute(key, value)}
|
118
|
+
@listener.receive(openstream)
|
119
|
+
@started = true
|
120
|
+
else
|
121
|
+
if @current.nil?
|
122
|
+
@current = ParsedXMLElement.new(name.clone)
|
123
|
+
else
|
124
|
+
@current = @current.add_child(name.clone)
|
125
|
+
end
|
126
|
+
data.each {|key, value| @current.add_attribute(key.clone, value.clone)}
|
127
|
+
end
|
128
|
+
when XMLParser::CDATA
|
129
|
+
@current.append_data(data.clone) if @current
|
130
|
+
when XMLParser::END_ELEM
|
131
|
+
case name
|
132
|
+
when "stream:stream"
|
133
|
+
@started = false
|
134
|
+
else
|
135
|
+
@listener.receive(@current) unless @current.element_parent
|
136
|
+
@current = @current.element_parent
|
137
|
+
end
|
138
|
+
end
|
139
|
+
rescue
|
140
|
+
puts "Error #{$!}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
rescue XMLParserError
|
144
|
+
line = parser.line
|
145
|
+
print "XML Parsing error(#{line}): #{$!}\n"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
else # USE REXML
|
150
|
+
require 'rexml/document'
|
151
|
+
require 'rexml/parsers/sax2parser'
|
152
|
+
require 'rexml/source'
|
153
|
+
|
154
|
+
##
|
155
|
+
# The REXMLJabberParser uses REXML to parse the incoming XML stream
|
156
|
+
# of the Jabber protocol and fires ParsedXMLElements at the Connection
|
157
|
+
# instance.
|
158
|
+
#
|
159
|
+
class REXMLJabberParser
|
160
|
+
# status if the parser is started
|
161
|
+
attr_reader :started
|
162
|
+
|
163
|
+
##
|
164
|
+
# Constructs a parser for the supplied stream (socket input)
|
165
|
+
#
|
166
|
+
# stream:: [IO] Socket input stream
|
167
|
+
# listener:: [Object.receive(ParsedXMLElement)] The listener (usually a Jabber::Protocol::Connection instance
|
168
|
+
#
|
169
|
+
def initialize(stream, listener)
|
170
|
+
@stream = stream
|
171
|
+
|
172
|
+
# this hack fixes REXML version "2.7.3" and "2.7.4"
|
173
|
+
if REXML::Version=="2.7.3" || REXML::Version=="2.7.4"
|
174
|
+
def @stream.read(len=nil)
|
175
|
+
len = 100 unless len
|
176
|
+
super(len)
|
177
|
+
end
|
178
|
+
def @stream.gets(char=nil)
|
179
|
+
super(">")
|
180
|
+
end
|
181
|
+
def @stream.readline(char=nil)
|
182
|
+
super(">")
|
183
|
+
end
|
184
|
+
def @stream.readlines(char=nil)
|
185
|
+
super(">")
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
@listener = listener
|
190
|
+
@current = nil
|
191
|
+
end
|
192
|
+
|
193
|
+
##
|
194
|
+
# Begins parsing the XML stream and does not return until
|
195
|
+
# the stream closes.
|
196
|
+
#
|
197
|
+
def parse
|
198
|
+
#puts "PARSE"
|
199
|
+
@started = false
|
200
|
+
begin
|
201
|
+
parser = REXML::Parsers::SAX2Parser.new @stream
|
202
|
+
|
203
|
+
parser.listen(:end_document) do
|
204
|
+
raise Jabber::ConnectionForceCloseError
|
205
|
+
end
|
206
|
+
|
207
|
+
parser.listen( :start_element ) do |uri, localname, qname, attributes|
|
208
|
+
case qname
|
209
|
+
when "stream:stream"
|
210
|
+
openstream = ParsedXMLElement.new(qname)
|
211
|
+
attributes.each { |attr, value| openstream.add_attribute(attr, value) }
|
212
|
+
@listener.receive(openstream)
|
213
|
+
@started = true
|
214
|
+
else
|
215
|
+
if @current.nil?
|
216
|
+
@current = ParsedXMLElement.new(qname)
|
217
|
+
else
|
218
|
+
@current = @current.add_child(qname)
|
219
|
+
end
|
220
|
+
attributes.each { |attr, value| @current.add_attribute(attr, value) }
|
221
|
+
end
|
222
|
+
end
|
223
|
+
parser.listen( :end_element ) do |uri, localname, qname|
|
224
|
+
case qname
|
225
|
+
when "stream:stream"
|
226
|
+
@started = false
|
227
|
+
else
|
228
|
+
@listener.receive(@current) unless @current.element_parent
|
229
|
+
@current = @current.element_parent
|
230
|
+
end
|
231
|
+
end
|
232
|
+
parser.listen( :characters ) do | text |
|
233
|
+
@current.append_data(text) if @current
|
234
|
+
end
|
235
|
+
parser.listen( :cdata ) do | text |
|
236
|
+
@current.append_data(text) if @current
|
237
|
+
end
|
238
|
+
parser.parse
|
239
|
+
rescue REXML::ParseException => e
|
240
|
+
@listener.parse_failure
|
241
|
+
rescue Jabber::ConnectionForceCloseError => e
|
242
|
+
@listener.parse_failure(e)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end # USE_PARSER
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|