em-xmpp 0.0.11 → 0.0.12
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/bin/xmig +40 -37
- data/em-xmpp.gemspec +1 -0
- data/lib/em-xmpp.rb +0 -2
- data/lib/em-xmpp/component.rb +18 -0
- data/lib/em-xmpp/connection.rb +35 -103
- data/lib/em-xmpp/context.rb +37 -19
- data/lib/em-xmpp/entity.rb +219 -223
- data/lib/em-xmpp/evented.rb +183 -0
- data/lib/em-xmpp/handler.rb +89 -86
- data/lib/em-xmpp/helpers.rb +39 -38
- data/lib/em-xmpp/namespaces.rb +1 -0
- data/lib/em-xmpp/non-em.rb +95 -0
- data/lib/em-xmpp/version.rb +1 -1
- data/lib/em-xmpp/xml_builder.rb +160 -0
- data/lib/em-xmpp/xml_parser.rb +344 -0
- data/samples/hello.rb +6 -3
- data/samples/non-em-hello.rb +90 -0
- metadata +25 -9
- data/lib/em-xmpp/connector.rb +0 -244
data/lib/em-xmpp/helpers.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
|
2
|
+
require 'em-xmpp/namespaces'
|
2
3
|
require 'em-xmpp/nodes'
|
3
4
|
require 'em-xmpp/conversation'
|
5
|
+
require 'fiber'
|
6
|
+
|
4
7
|
module EM::Xmpp
|
5
8
|
module Helpers
|
6
9
|
include EM::Xmpp::Namespaces
|
7
10
|
def get_roster
|
8
11
|
f = Fiber.current
|
9
12
|
|
10
|
-
roster = iq_stanza
|
11
|
-
iq.query(:xmlns => Roster)
|
12
|
-
end
|
13
|
+
roster = iq_stanza(x('query',:xmlns => Roster))
|
13
14
|
|
14
15
|
send_stanza(roster) do |response|
|
15
|
-
f.resume response.bit
|
16
|
+
f.resume response.bit(:roster).items
|
16
17
|
end
|
17
18
|
|
18
19
|
Fiber.yield
|
@@ -158,17 +159,17 @@ module EM::Xmpp
|
|
158
159
|
until state.finished?
|
159
160
|
state.status = 'executing'
|
160
161
|
|
161
|
-
reply = query.reply
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
build_form(
|
169
|
-
|
170
|
-
|
171
|
-
|
162
|
+
reply = query.reply(
|
163
|
+
x('command',{:xmlns => EM::Xmpp::Namespaces::Commands, :sessionid => sess_id, :node => query.node, :status => state.status},
|
164
|
+
x('actions',
|
165
|
+
x_if(state.can_prev?,'prev'),
|
166
|
+
x_if(state.can_complete?,'complete'),
|
167
|
+
x_if(state.can_next?,'next')
|
168
|
+
),
|
169
|
+
build_form(state.form,'form'),
|
170
|
+
x_if(state.flash,'note',{:type => state.flash.level}, state.flash.msg)
|
171
|
+
)
|
172
|
+
)
|
172
173
|
|
173
174
|
user_answer = conv.send_stanza reply
|
174
175
|
state.last_answer = user_answer
|
@@ -187,40 +188,40 @@ module EM::Xmpp
|
|
187
188
|
state.status = 'completed'
|
188
189
|
end
|
189
190
|
|
190
|
-
finalizer = state.last_answer.ctx.bit(:command).reply
|
191
|
-
|
192
|
-
|
193
|
-
build_form(
|
194
|
-
|
195
|
-
|
191
|
+
finalizer = state.last_answer.ctx.bit(:command).reply(
|
192
|
+
x('command',{:xmlns => EM::Xmpp::Namespaces::Commands, :sessionid => sess_id, :node => query.node, :status => state.status},
|
193
|
+
x_if(state.flash,'note',{:type => state.flash.level}, state.flash.msg),
|
194
|
+
state.result ? build_form(state.result,'result') : nil
|
195
|
+
)
|
196
|
+
)
|
196
197
|
send_stanza finalizer
|
197
198
|
end
|
198
199
|
end
|
199
200
|
|
200
|
-
def build_form(
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
form.fields.
|
201
|
+
def build_form(form,type='submit')
|
202
|
+
x('x',{:xmlns => DataForms, :type => type},
|
203
|
+
x_if(form.title,'title',form.title),
|
204
|
+
x_if(form.instructions,'instructions',form.instructions),
|
205
|
+
form.fields.map do |field|
|
205
206
|
args = {'var' => field.var}
|
206
207
|
args = args.merge('type' => field.type) unless field.type.nil? or field.type.empty?
|
207
208
|
args = args.merge('label' => field.label) unless field.label.nil? or field.label.empty?
|
208
|
-
x
|
209
|
-
(field.options||[]).
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
end
|
214
|
-
(field.values||[]).
|
215
|
-
|
209
|
+
x('field',args,
|
210
|
+
(field.options||[]).map do |opt_value|
|
211
|
+
x('option',
|
212
|
+
x('value',opt_value)
|
213
|
+
)
|
214
|
+
end,
|
215
|
+
(field.values||[]).map do |value|
|
216
|
+
x('value',value)
|
216
217
|
end
|
217
|
-
|
218
|
+
)
|
218
219
|
end
|
219
|
-
|
220
|
+
)
|
220
221
|
end
|
221
222
|
|
222
|
-
def build_submit_form(
|
223
|
-
build_form(
|
223
|
+
def build_submit_form(form)
|
224
|
+
build_form(form,'submit')
|
224
225
|
end
|
225
226
|
|
226
227
|
end
|
data/lib/em-xmpp/namespaces.rb
CHANGED
@@ -0,0 +1,95 @@
|
|
1
|
+
|
2
|
+
require 'em-xmpp'
|
3
|
+
require 'em-xmpp/namespaces'
|
4
|
+
require 'em-xmpp/evented'
|
5
|
+
require 'em-xmpp/jid'
|
6
|
+
require 'em-xmpp/component'
|
7
|
+
require 'em-xmpp/resolver'
|
8
|
+
|
9
|
+
require 'socket'
|
10
|
+
require 'openssl'
|
11
|
+
|
12
|
+
module EM::Xmpp
|
13
|
+
module NonEM
|
14
|
+
class Connection
|
15
|
+
include Namespaces
|
16
|
+
include Evented
|
17
|
+
|
18
|
+
attr_reader :jid, :pass, :user_data
|
19
|
+
|
20
|
+
def initialize(jid, pass, mod=nil, cfg={})
|
21
|
+
@jid = jid
|
22
|
+
@component = jid.node.nil?
|
23
|
+
self.extend Component if component?
|
24
|
+
@pass = pass.dup.freeze
|
25
|
+
self.extend mod if mod
|
26
|
+
certdir = cfg[:certificates]
|
27
|
+
@user_data = cfg[:data]
|
28
|
+
@certstore = if certdir
|
29
|
+
CertStore.new(certdir)
|
30
|
+
else
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
@ssl = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.start(jid, pass=nil, mod=nil, cfg={}, server=nil, port=5222, &blk)
|
37
|
+
jid = JID.parse jid
|
38
|
+
if server.nil?
|
39
|
+
record = Resolver.resolve jid.domain
|
40
|
+
if record
|
41
|
+
server = record.target.to_s
|
42
|
+
port = record.port
|
43
|
+
else
|
44
|
+
server = jid.domain
|
45
|
+
end
|
46
|
+
end
|
47
|
+
obj = self.new(jid,pass,mod,cfg)
|
48
|
+
obj.start(server,port)
|
49
|
+
obj
|
50
|
+
end
|
51
|
+
|
52
|
+
def start(server,port)
|
53
|
+
@skt = TCPSocket.open(server,port)
|
54
|
+
end
|
55
|
+
|
56
|
+
ChunkSize = 1450
|
57
|
+
|
58
|
+
def event_loop
|
59
|
+
prepare_parser!
|
60
|
+
set_negotiation_handler!
|
61
|
+
catch :stop do
|
62
|
+
loop do
|
63
|
+
tick=5
|
64
|
+
ok = IO.select([@skt], nil, nil, tick)
|
65
|
+
if ok
|
66
|
+
if @ssl
|
67
|
+
dat = @ssl.sysread(ChunkSize)
|
68
|
+
puts "<< in\n#{dat}\n" if $DEBUG
|
69
|
+
receive_raw(dat)
|
70
|
+
else
|
71
|
+
dat = @skt.recv(ChunkSize)
|
72
|
+
puts "<< in\n#{dat}\n" if $DEBUG
|
73
|
+
receive_raw(dat)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def send_raw(dat)
|
81
|
+
puts ">> out\n#{dat}\n" if $DEBUG
|
82
|
+
if @ssl
|
83
|
+
@ssl.syswrite dat
|
84
|
+
else
|
85
|
+
@skt << dat if @skt
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def initiate_tls
|
90
|
+
@ssl = OpenSSL::SSL::SSLSocket.new(@skt).connect
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
data/lib/em-xmpp/version.rb
CHANGED
@@ -0,0 +1,160 @@
|
|
1
|
+
require 'ox'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Ox
|
5
|
+
module Builder
|
6
|
+
# args = attributes and/or children in any order, multiple appearance is possible
|
7
|
+
# @overload build(name,attributes,children)
|
8
|
+
# @param [String] name name of the Element
|
9
|
+
# @param [Hash] attributes
|
10
|
+
# @param [String|Element|Array] children text, child element or array of elements
|
11
|
+
def x(name, *args)
|
12
|
+
n = Element.new(name)
|
13
|
+
yielded = if block_given?
|
14
|
+
yield
|
15
|
+
else
|
16
|
+
[]
|
17
|
+
end
|
18
|
+
unless yielded.is_a?(Array)
|
19
|
+
yielded = [yielded]
|
20
|
+
end
|
21
|
+
values = args + yielded
|
22
|
+
|
23
|
+
values.each do |val|
|
24
|
+
case val
|
25
|
+
when Hash
|
26
|
+
val.each { |k,v| n[k.to_s] = v }
|
27
|
+
when Array
|
28
|
+
val.each { |c| n << c if c}
|
29
|
+
else
|
30
|
+
n << val if val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
n
|
35
|
+
end
|
36
|
+
def x_if(condition, *args)
|
37
|
+
x(*args) if condition
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module Nokogiri::AlternativeBuilder
|
43
|
+
class Element
|
44
|
+
attr_reader :name, :children, :attributes
|
45
|
+
def initialize(name)
|
46
|
+
@name = name
|
47
|
+
@children = []
|
48
|
+
@attributes = {}
|
49
|
+
end
|
50
|
+
def << n
|
51
|
+
@children << n
|
52
|
+
end
|
53
|
+
def []= k,v
|
54
|
+
@attributes[k] = v
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def x(name, *args)
|
59
|
+
n = Element.new(name)
|
60
|
+
yielded = if block_given?
|
61
|
+
yield
|
62
|
+
else
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
unless yielded.is_a?(Array)
|
66
|
+
yielded = [yielded]
|
67
|
+
end
|
68
|
+
values = args + yielded
|
69
|
+
|
70
|
+
values.each do |val|
|
71
|
+
case val
|
72
|
+
when Hash
|
73
|
+
val.each { |k,v| n[k.to_s] = v }
|
74
|
+
when Array
|
75
|
+
val.each { |c| n << c if c}
|
76
|
+
else
|
77
|
+
n << val if val
|
78
|
+
end
|
79
|
+
end
|
80
|
+
n
|
81
|
+
end
|
82
|
+
def x_if(condition, *args)
|
83
|
+
x(*args) if condition
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
module EM::Xmpp
|
88
|
+
module NokogiriXmlBuilder
|
89
|
+
include Nokogiri::AlternativeBuilder
|
90
|
+
class OutgoingStanza
|
91
|
+
include NokogiriXmlBuilder
|
92
|
+
|
93
|
+
attr_reader :xml,:params
|
94
|
+
|
95
|
+
def initialize(*args,&blk)
|
96
|
+
@root = x(*args,&blk)
|
97
|
+
@doc = build_doc_from_element_root @root
|
98
|
+
@xml = @doc.root.to_xml
|
99
|
+
@params = @root.attributes
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def build_doc_from_element_root(root)
|
105
|
+
doc = Nokogiri::XML::Document.new
|
106
|
+
root_node = build_tree doc, root
|
107
|
+
doc.root = root_node
|
108
|
+
doc
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_xml(*args)
|
113
|
+
root = x(*args)
|
114
|
+
doc = Nokogiri::XML::Document.new
|
115
|
+
doc.root = build_tree(doc, root)
|
116
|
+
ret = doc.root.to_xml
|
117
|
+
ret
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def build_tree(doc, node_info)
|
123
|
+
node = node_info
|
124
|
+
if node_info.respond_to?(:name)
|
125
|
+
node = node_for_info doc, node_info
|
126
|
+
list = node_info.children.map{|child| build_tree doc, child}
|
127
|
+
list.each{|l| node << l }
|
128
|
+
end
|
129
|
+
node
|
130
|
+
end
|
131
|
+
|
132
|
+
def node_for_info(doc, node_info)
|
133
|
+
node = Nokogiri::XML::Node.new(node_info.name, doc)
|
134
|
+
node_info.attributes.each_pair {|k,v| node[k] = v}
|
135
|
+
node
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
module OxXmlBuilder
|
140
|
+
include Ox::Builder
|
141
|
+
|
142
|
+
class OutgoingStanza
|
143
|
+
include OxXmlBuilder
|
144
|
+
attr_reader :xml,:params
|
145
|
+
|
146
|
+
def initialize(*args,&blk)
|
147
|
+
node = x(*args,&blk)
|
148
|
+
@xml = Ox.dump(node)
|
149
|
+
@params = node.attributes
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def build_xml(*args)
|
154
|
+
Ox.dump(x(*args))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
#XmlBuilder = OxXmlBuilder
|
159
|
+
XmlBuilder = NokogiriXmlBuilder
|
160
|
+
end
|
@@ -0,0 +1,344 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'ox'
|
3
|
+
|
4
|
+
#workarounds
|
5
|
+
module Ox
|
6
|
+
module XPathSubset
|
7
|
+
class Query
|
8
|
+
attr_reader :type, :name, :ns
|
9
|
+
def initialize(t,e,n)
|
10
|
+
@type, @name, @ns = t, e, n
|
11
|
+
end
|
12
|
+
|
13
|
+
def match(elem, ns_mapping)
|
14
|
+
wanted_ns = ns_mapping[ns]
|
15
|
+
|
16
|
+
same_value = elem.value == name
|
17
|
+
same_ns = elem.xmlns == wanted_ns
|
18
|
+
|
19
|
+
matching = same_value & same_ns
|
20
|
+
end
|
21
|
+
|
22
|
+
def find(elem, ns_mapping)
|
23
|
+
ret = []
|
24
|
+
ret << elem if match(elem, ns_mapping)
|
25
|
+
case type
|
26
|
+
when 'normal'
|
27
|
+
elem.nodes.reject{|n| n.is_a?(String)}.each do |n|
|
28
|
+
ret << n if match(n, ns_mapping)
|
29
|
+
end
|
30
|
+
when 'relative', 'anywhere' #TODO: for anywhere, should go to the root first, is that even possible?
|
31
|
+
elem.nodes.reject{|n| n.is_a?(String)}.each do |n|
|
32
|
+
find(n, ns_mapping).each {|m| ret << m}
|
33
|
+
end
|
34
|
+
else
|
35
|
+
raise NotImplementedError
|
36
|
+
end
|
37
|
+
|
38
|
+
ret
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Element
|
44
|
+
attr_accessor :xmlns
|
45
|
+
def parse_xpath(path)
|
46
|
+
queries = path.split('|').map(&:strip).map do |str|
|
47
|
+
kind = if str.start_with?("//")
|
48
|
+
'anywhere'
|
49
|
+
elsif str.start_with?(".//")
|
50
|
+
'relative'
|
51
|
+
else
|
52
|
+
'normal'
|
53
|
+
end
|
54
|
+
ns,name = str.split(':',2)
|
55
|
+
if name
|
56
|
+
ns = ns.tr('/.','')
|
57
|
+
else
|
58
|
+
name,ns = ns,nil unless name
|
59
|
+
end
|
60
|
+
XPathSubset::Query.new(kind, name, ns)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def xpath(path,ns_mapping)
|
65
|
+
queries = parse_xpath(path)
|
66
|
+
elems = []
|
67
|
+
queries.each do |q|
|
68
|
+
q.find(self,ns_mapping).each do |n|
|
69
|
+
elems << n
|
70
|
+
end
|
71
|
+
end
|
72
|
+
elems.uniq
|
73
|
+
end
|
74
|
+
|
75
|
+
def children
|
76
|
+
text ? [] : nodes
|
77
|
+
end
|
78
|
+
|
79
|
+
def content
|
80
|
+
text
|
81
|
+
end
|
82
|
+
|
83
|
+
def any?
|
84
|
+
text
|
85
|
+
end
|
86
|
+
|
87
|
+
def child
|
88
|
+
nodes.first
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module EM::Xmpp
|
94
|
+
module XmlParser
|
95
|
+
|
96
|
+
class ForwardingParser < ::Nokogiri::XML::SAX::PushParser
|
97
|
+
def initialize(receiver)
|
98
|
+
doc = ForwardingDocument.new
|
99
|
+
doc.recipient = receiver
|
100
|
+
super doc
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
#XML SAX document which delegates its method to a recipient object
|
105
|
+
class ForwardingDocument < ::Nokogiri::XML::SAX::Document
|
106
|
+
attr_accessor :recipient
|
107
|
+
%w{xmldecl start_document end_document start_element_namespace end_element characters
|
108
|
+
comment warning error cdata_block}.each do |meth|
|
109
|
+
meth2 = "xml_#{meth}"
|
110
|
+
define_method(meth) do |*args|
|
111
|
+
recipient.send(meth2, *args) if recipient
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
### XML world
|
119
|
+
module XmlInterface
|
120
|
+
def xml_xmldecl(version,encoding,standalone)
|
121
|
+
raise NotImplementedError
|
122
|
+
end
|
123
|
+
def xml_start_document
|
124
|
+
raise NotImplementedError
|
125
|
+
end
|
126
|
+
def xml_end_document
|
127
|
+
raise NotImplementedError
|
128
|
+
end
|
129
|
+
def xml_start_element_namespace(name, attrs=[],prefix=nil,uri=nil,ns=[])
|
130
|
+
raise NotImplementedError
|
131
|
+
end
|
132
|
+
def xml_end_element(name)
|
133
|
+
raise NotImplementedError
|
134
|
+
end
|
135
|
+
def xml_characters(txt)
|
136
|
+
raise NotImplementedError
|
137
|
+
end
|
138
|
+
def xml_error(err)
|
139
|
+
raise RuntimeError, err
|
140
|
+
end
|
141
|
+
def xml_stream_closing
|
142
|
+
raise NotImplementedError
|
143
|
+
end
|
144
|
+
def xml_comment(comm)
|
145
|
+
raise NotImplementedError
|
146
|
+
end
|
147
|
+
|
148
|
+
def xml_warning(warn)
|
149
|
+
raise NotImplementedError
|
150
|
+
end
|
151
|
+
|
152
|
+
def xml_cdata_block(data)
|
153
|
+
raise NotImplementedError
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module Nokogiri
|
158
|
+
include XmlInterface
|
159
|
+
def xml_xmldecl(version,encoding,standalone)
|
160
|
+
end
|
161
|
+
|
162
|
+
def xml_start_document
|
163
|
+
#XXX set namespaces and stream prefix
|
164
|
+
# namespace may depend on the type of connection ('jabber:client' or
|
165
|
+
# 'jabber:server')
|
166
|
+
# currently we do not set any stream's namespace, hence when builidng stanza,
|
167
|
+
# we must explicitely avoid writing the namespace of iq/presence/message XML nodes
|
168
|
+
@streamdoc = ::Nokogiri::XML::Document.new
|
169
|
+
end
|
170
|
+
|
171
|
+
def xml_end_document
|
172
|
+
@streamdoc = @stanza = @stack = @xml_parser = nil
|
173
|
+
end
|
174
|
+
|
175
|
+
def xml_start_element_namespace(name, attrs=[],prefix=nil,uri=nil,ns=[])
|
176
|
+
node = ::Nokogiri::XML::Node.new(name, @streamdoc)
|
177
|
+
attrs.each do |attr|
|
178
|
+
#attr is a Struct with members localname/prefix/uri/value
|
179
|
+
node[attr.localname] = attr.value
|
180
|
+
end
|
181
|
+
#XXX - if prefix is there maybe we do not want to set uri as default
|
182
|
+
node.default_namespace = uri if uri
|
183
|
+
|
184
|
+
ns.each do |pfx,href|
|
185
|
+
node.add_namespace_definition pfx, href
|
186
|
+
end
|
187
|
+
|
188
|
+
# puts "starting: #{name}, stack:#{@stack.size}" if $DEBUG
|
189
|
+
case @stack.size
|
190
|
+
when 0 #the streaming tag starts
|
191
|
+
stream_support(node)
|
192
|
+
when 1 #a stanza starts
|
193
|
+
set_current_stanza!(node)
|
194
|
+
stanza_start node
|
195
|
+
else
|
196
|
+
@stack.last.add_child node
|
197
|
+
end
|
198
|
+
|
199
|
+
@stack << node
|
200
|
+
end
|
201
|
+
def xml_end_element(name)
|
202
|
+
node = @stack.pop
|
203
|
+
#puts "ending: #{name}, stack:#{@stack.size}" if $DEBUG
|
204
|
+
|
205
|
+
case @stack.size
|
206
|
+
when 0 #i.e., the stream support ends
|
207
|
+
xml_stream_closing
|
208
|
+
when 1 #i.e., we've finished a stanza
|
209
|
+
raise RuntimeError, "should end on a stanza" unless node == @stanza
|
210
|
+
stanza_end node
|
211
|
+
else
|
212
|
+
#the stanza keeps growing
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def xml_characters(txt)
|
217
|
+
@stack.last << ::Nokogiri::XML::Text.new(txt, @streamdoc)
|
218
|
+
end
|
219
|
+
|
220
|
+
def xml_stream_closing
|
221
|
+
close_xml_stream
|
222
|
+
close_connection
|
223
|
+
end
|
224
|
+
|
225
|
+
def stream_support(node)
|
226
|
+
@stanza = ::Nokogiri::XML::Node.new('dummy', @streamdoc)
|
227
|
+
node << @stanza
|
228
|
+
|
229
|
+
@streamdoc.root = node
|
230
|
+
end
|
231
|
+
|
232
|
+
def set_current_stanza!(node)
|
233
|
+
@stanza.remove
|
234
|
+
|
235
|
+
@stanza = node
|
236
|
+
@streamdoc.root << @stanza
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
module Ox
|
241
|
+
include XmlInterface
|
242
|
+
def xml_xmldecl(version,encoding,standalone)
|
243
|
+
end
|
244
|
+
|
245
|
+
def xml_start_document
|
246
|
+
#XXX set namespaces and stream prefix
|
247
|
+
# namespace may depend on the type of connection ('jabber:client' or
|
248
|
+
# 'jabber:server')
|
249
|
+
# currently we do not set any stream's namespace, hence when builidng stanza,
|
250
|
+
# we must explicitely avoid writing the namespace of iq/presence/message XML nodes
|
251
|
+
end
|
252
|
+
|
253
|
+
def xml_end_document
|
254
|
+
@stanza = @stack = @xml_parser = nil
|
255
|
+
end
|
256
|
+
|
257
|
+
def xml_start_element_namespace(name, attrs=[],prefix=nil,uri=nil,ns=[])
|
258
|
+
node = ::Ox::Element.new(name)
|
259
|
+
node.xmlns = uri
|
260
|
+
attrs.each do |attr|
|
261
|
+
#attr is a Struct with members localname/prefix/uri/value
|
262
|
+
node[attr.localname] = attr.value
|
263
|
+
end
|
264
|
+
|
265
|
+
case @stack.size
|
266
|
+
when 0 #the streaming tag starts
|
267
|
+
stream_support(node)
|
268
|
+
stream_start node
|
269
|
+
when 1 #a stanza starts
|
270
|
+
set_current_stanza!(node)
|
271
|
+
stanza_start node
|
272
|
+
else
|
273
|
+
@stack.last << node
|
274
|
+
end
|
275
|
+
|
276
|
+
@stack << node
|
277
|
+
@text = nil
|
278
|
+
end
|
279
|
+
|
280
|
+
def xml_end_element(name)
|
281
|
+
node = @stack.pop
|
282
|
+
if @text
|
283
|
+
node << @text
|
284
|
+
@text = nil
|
285
|
+
end
|
286
|
+
#puts "ending: #{name}, stack:#{@stack.size}" if $DEBUG
|
287
|
+
|
288
|
+
case @stack.size
|
289
|
+
when 0 #i.e., the stream support ends
|
290
|
+
xml_stream_closing
|
291
|
+
when 1 #i.e., we've finished a stanza
|
292
|
+
raise RuntimeError, "should end on a stanza" unless node == @stanza
|
293
|
+
stanza_end node
|
294
|
+
else
|
295
|
+
#the stanza keeps growing
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def xml_characters(txt)
|
300
|
+
if @text
|
301
|
+
@text<<txt
|
302
|
+
else
|
303
|
+
@text=txt
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def xml_error(err)
|
308
|
+
#raise RuntimeError, err
|
309
|
+
end
|
310
|
+
|
311
|
+
def xml_stream_closing
|
312
|
+
close_xml_stream
|
313
|
+
close_connection
|
314
|
+
end
|
315
|
+
|
316
|
+
def xml_comment(comm)
|
317
|
+
raise NotImplementedError
|
318
|
+
end
|
319
|
+
|
320
|
+
def xml_warning(warn)
|
321
|
+
raise NotImplementedError
|
322
|
+
end
|
323
|
+
|
324
|
+
def xml_cdata_block(data)
|
325
|
+
raise NotImplementedError
|
326
|
+
end
|
327
|
+
|
328
|
+
### XMPP World
|
329
|
+
|
330
|
+
def stream_support(node)
|
331
|
+
@stanza = ::Ox::Element.new('dummy')
|
332
|
+
node << @stanza
|
333
|
+
|
334
|
+
@streamdoc_root = node
|
335
|
+
end
|
336
|
+
|
337
|
+
def set_current_stanza!(node)
|
338
|
+
@stanza = node
|
339
|
+
@streamdoc_root.nodes[0] = @stanza
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
end
|
344
|
+
end
|