1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
# =XMPP4R - XMPP Library for Ruby
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
# Website::http://xmpp4r.github.io
require 'rexml/parsers/sax2parser'
require 'rexml/source'
require 'xmpp4r/rexmladdons'
module Jabber
##
# The StreamParser uses REXML to parse the incoming XML stream
# of the Jabber protocol and fires XMPPStanza at the Connection
# instance.
#
class StreamParser
# status if the parser is started
attr_reader :started
##
# Constructs a parser for the supplied stream (socket input)
#
# stream:: [IO] Socket input stream
# listener:: [Object.receive(XMPPStanza)] The listener (usually a Jabber::Protocol::Connection instance)
#
def initialize(stream, listener)
@stream = stream
@listener = listener
@current = nil
end
##
# Begins parsing the XML stream and does not return until
# the stream closes.
#
def parse
@started = false
begin
parser = REXML::Parsers::SAX2Parser.new @stream
parser.listen( :start_element ) do |uri, localname, qname, attributes|
e = REXML::Element.new(qname)
if attributes.kind_of? Hash
unnormalized_attributes = {}
attributes.each_pair do |key, value|
unnormalized_attributes[key] = REXML::Text::unnormalize(value)
end
elsif attributes.kind_of? Array
unnormalized_attributes = []
attributes.each do |value|
unnormalized_attributes << [value[0], REXML::Text::unnormalize(value[1])]
end
end
e.add_attributes unnormalized_attributes
@current = @current.nil? ? e : @current.add_element(e)
# Handling <stream:stream> not only when it is being
# received as a top-level tag but also as a child of the
# top-level element itself. This way, we handle stream
# restarts (ie. after SASL authentication).
if @current.name == 'stream' and @current.parent.nil?
@started = true
@listener.receive(@current)
@current = nil
end
end
parser.listen( :end_element ) do |uri, localname, qname|
if qname == 'stream:stream' and @current.nil?
@started = false
@listener.parser_end
else
@listener.receive(@current) unless @current.parent
@current = @current.parent
end
end
parser.listen( :end_document ) do
raise Jabber::ServerDisconnected, "Server Disconnected!"
end
parser.listen( :characters ) do | text |
@current.add(REXML::Text.new(text.to_s, @current.whitespace, nil, true)) if @current
end
parser.listen( :cdata ) do | text |
@current.add(REXML::CData.new(text)) if @current
end
parser.parse
rescue REXML::ParseException => e
@listener.parse_failure(e)
end
end
end
end
|