📄 protocol.rb
字号:
# License: see LICENSE.txt# Jabber4R - Jabber Instant Messaging Library for Ruby# Copyright (C) 2002 Rich Kilmer <rich@infoether.com># require 'singleton'require 'socket'module Jabber class JabberConnectionException < RuntimeError attr_reader :data def initialize(writing, data) @writing = writing @data = data end def writing? @writing end end ## # The Protocol module contains helper methods for constructing # Jabber protocol elements and classes that implement protocol # elements. # module Protocol USE_PARSER = :rexml # either :rexml or :xmlparser ## # The parser to use for stream processing. The current # available parsers are: # # * Jabber::Protocol::ExpatJabberParser uses XMLParser # * Jabber::Protocol::REXMLJabberParser uses REXML # # return:: [Class] The parser class # def Protocol.Parser if USE_PARSER==:xmlparser Jabber::Protocol::ExpatJabberParser else Jabber::Protocol::REXMLJabberParser end end ## # The connection class encapsulates the connection to the Jabber # service including managing the socket and controlling the parsing # of the Jabber XML stream. # class Connection DISCONNECTED = 1 CONNECTED = 2 attr_reader :host, :port, :status, :input, :output def initialize(host, port=5222) @host = host @port = port @status = DISCONNECTED @filters = {} @threadBlocks = {} @pollCounter = 10 end ## # Connects to the Jabber server through a TCP Socket and # starts the Jabber parser. # def connect @socket = TCPSocket.new(@host, @port) @parser = Jabber::Protocol.Parser.new(@socket, self) @parserThread = Thread.new {@parser.parse} @pollThread = Thread.new {poll} @status = CONNECTED end ## # Mounts a block to handle exceptions if they occur during the # poll send. This will likely be the first indication that # the socket dropped in a Jabber Session. # def on_connection_exception(&block) @exception_block = block end def parse_failure Thread.new {@exception_block.call if @exception_block} end ## # Returns if this connection is connected to a Jabber service # # return:: [Boolean] Connection status # def is_connected? return @status == CONNECTED end ## # Returns if this connection is NOT connected to a Jabber service # # return:: [Boolean] Connection status # def is_disconnected? return @status == DISCONNECTED end ## # Processes a received ParsedXMLElement and executes # registered thread blocks and filters against it. # # element:: [ParsedXMLElement] The received element # def receive(element) while @threadBlocks.size==0 && @filters.size==0 sleep 0.1 end Jabber::DEBUG && puts("RECEIVED:\n#{element.to_s}") @threadBlocks.each do |thread, proc| begin proc.call(element) if element.element_consumed? @threadBlocks.delete(thread) thread.wakeup if thread.alive? return end rescue Exception => error puts error.to_s puts error.backtrace.join("\n") end end @filters.each_value do |proc| begin proc.call(element) return if element.element_consumed? rescue Exception => error puts error.to_s puts error.backtrace.join("\n") end end end ## # Sends XML data to the socket and (optionally) waits # to process received data. # # xml:: [String] The xml data to send # proc:: [Proc = nil] The optional proc # &block:: [Block] The optional block # def send(xml, proc=nil, &block) Jabber::DEBUG && puts("SENDING:\n#{ xml.kind_of?(String) ? xml : xml.to_s }") xml = xml.to_s if not xml.kind_of? String block = proc if proc @threadBlocks[Thread.current]=block if block begin @socket << xml rescue raise JabberConnectionException.new(true, xml) end @pollCounter = 10 end ## # Starts a polling thread to send "keep alive" data to prevent # the Jabber connection from closing for inactivity. # def poll sleep 10 while true sleep 2 @pollCounter = @pollCounter - 1 if @pollCounter < 0 begin send(" \t ") rescue Thread.new {@exception_block.call if @exception_block} break end end end end ## # Adds a filter block/proc to process received XML messages # # xml:: [String] The xml data to send # proc:: [Proc = nil] The optional proc # &block:: [Block] The optional block # def add_filter(ref, proc=nil, &block) block = proc if proc raise "Must supply a block or Proc object to the addFilter method" if block.nil? @filters[ref] = block end def delete_filter(ref) @filters.delete(ref) end ## # Closes the connection to the Jabber service # def close @parserThread.kill if @parserThread @pollThread.kill @socket.close if @socket @status = DISCONNECTED end end ## # Generates an open stream XML element # # host:: [String] The host being connected to # return:: [String] The XML data to send # def Protocol.gen_open_stream(host) return ('<?xml version="1.0" encoding="UTF-8" ?><stream:stream to="'+host+'" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams">') end ## # Generates an close stream XML element # # return:: [String] The XML data to send # def Protocol.gen_close_stream return "</stream:stream>" end ## # The presence class is used to construct presence messages to # send to the Jabber service. # class Presence attr_accessor :to, :from, :id, :type # The state to show (chat, xa, dnd, away) attr_accessor :show # The status message attr_accessor :status attr_accessor :priority ## # Constructs a Presence object w/the supplied id # # id:: [String] The message ID # show:: [String] The state to show # status:: [String] The status message # def initialize(id, show=nil, status=nil) @id = id @show = show if show @status = status if status end ## # Generate a presence object for initial presence notification # # id:: [String] The message ID # show:: [String] The state to show # status:: [String] The status message # return:: [Jabber::Protocol::Presence] The newly created Presence object # def Presence.gen_initial(id, show=nil, status=nil) Presence.new(id, show, status) end ## # Generate a presence object w/show="normal" (normal availability) # # id:: [String] The message ID # status:: [String=nil] The status message # return:: [Jabber::Protocol::Presence] The newly created Presence object # def Presence.gen_normal(id, status=nil) Presence.new(id, "normal", status) end ## # Generate a presence object w/show="chat" (free for chat) # # id:: [String] The message ID # status:: [String=nil] The status message # return:: [Jabber::Protocol::Presence] The newly created Presence object # def Presence.gen_chat(id, status=nil) Presence.new(id, "chat", status) end ## # Generate a presence object w/show="xa" (extended away) # # id:: [String] The message ID # status:: [String=nil] The status message # return:: [Jabber::Protocol::Presence] The newly created Presence object # def Presence.gen_xa(id, status=nil) Presence.new(id, "xa", status) end ## # Generate a presence object w/show="dnd" (do not disturb) # # id:: [String] The message ID # status:: [String=nil] The status message # return:: [Jabber::Protocol::Presence] The newly created Presence object # def Presence.gen_dnd(id, status=nil) Presence.new(id, "dnd", status) end ## # Generate a presence object w/show="away" (away from resource) # # id:: [String] The message ID # status:: [String=nil] The status message # return:: [Jabber::Protocol::Presence] The newly created Presence object # def Presence.gen_away(id, status=nil) Presence.new(id, "away", status) end ## # Generate a presence object w/show="unavailable" (not free for chat) # # id:: [String] The message ID # status:: [String=nil] The status message # return:: [Jabber::Protocol::Presence] The newly created Presence object # def Presence.gen_unavailable(id, status=nil) p = Presence.new(id) p.type="unavailable" p end def Presence.gen_new_subscription(to) p = Presence.new(Jabber.gen_random_id) p.type = "subscribe" p.to = to p end def Presence.gen_accept_subscription(id, jid) p = Presence.new(id) p.type = "subscribed" p.to = jid p end def Presence.gen_accept_unsubscription(id, jid) p = Presence.new(id) p.type = "unsubscribed" p.to = jid p end ## # Generates the xml representation of this Presence object # # return:: [String] The presence XML message to send the Jabber service # def to_xml e = XMLElement.new("presence") e.add_attribute("id", @id) if @id e.add_attribute("from", @from) if @from e.add_attribute("to", @to) if @to e.add_attribute("type", @type) if @type e.add_child("show").add_data(@show) if @show e.add_child("status").add_data(@status) if @status e.add_child("priority") if @priority e.to_s end ## # see _to_xml # def to_s to_xml end end ## # A class used to build/parse IQ requests/responses # class Iq attr_accessor :session,:to, :from, :id, :type, :xmlns, :data,:error,:errorcode ERROR="error" GET="get" SET="set" RESULT="result" ## # Factory to build an IQ object from xml element # # session:: [Jabber::Session] The Jabber session instance # element:: [Jabber::Protocol::ParsedXMLElement] The received XML object # return:: [Jabber::Protocol::Iq] The newly created Iq object # def Iq.from_element(session, element) iq = Iq.new(session) iq.from = Jabber::JID.new(element.attr_from) if element.attr_from iq.to = Jabber::JID.new(element.attr_to) if element.attr_to iq.type = element.attr_type iq.id = element.attr_id iq.session=session iq.xmlns=element.query.attr_xmlns iq.data=element.query return iq end ## # Default constructor to build an Iq object # session:: [Jabber::Session] The Jabber session instance # id:: [String=nil] The (optional) id of the Iq object def initialize(session,id=nil) @session=session @id=id end ## # Return an IQ object that uses the jabber:iq:private namespace # def Iq.get_private(session,id,ename,ns) iq=Iq.new(session,id) iq.type="get" iq.xmlns="jabber:iq:private" iq.data=XMLElement.new(ename,{'xmlns' => ns}); return iq end ## # Generates an IQ roster request XML element # # id:: [String] The message id # return:: [String] The XML data to send # def Iq.gen_roster(session, id) iq = Iq.new(session, id) iq.type = "get" iq.xmlns = "jabber:iq:roster" return iq #return XMLElement.new("iq", {"type"=>"get", "id"=>id}).add_child("query", {"xmlns"=>"jabber:iq:roster"}).to_s end ## # Generates an IQ authortization request XML element # # id:: [String] The message id # username:: [String] The username # password:: [String] The password # email:: [String] The email address of the account # name:: [String] The full name # return:: [String] The XML data to send
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -