Class: Babylon::ClientConnection
- Inherits:
-
XmppConnection
- Object
- EventMachine::Connection
- XmppConnection
- Babylon::ClientConnection
- Defined in:
- lib/babylon/client_connection.rb
Overview
ClientConnection is in charge of the XMPP connection for a Regular XMPP Client. So far, SASL Plain authenticationonly is supported Upon stanza reception, and depending on the status (connected… etc), this component will handle or forward the stanzas.
Instance Attribute Summary collapse
-
#binding_iq_id ⇒ Object
readonly
Returns the value of attribute binding_iq_id.
-
#session_iq_id ⇒ Object
readonly
Returns the value of attribute session_iq_id.
Attributes inherited from XmppConnection
Class Method Summary collapse
-
.connect(params, handler = nil) ⇒ Object
Connects the ClientConnection based on SRV records for the jid’s domain, if no host or port has been specified.
Instance Method Summary collapse
-
#connection_completed ⇒ Object
Connection_completed is called when the connection (socket) has been established and is in charge of “building” the XML stream to establish the XMPP connection itself.
-
#initialize(params) ⇒ ClientConnection
constructor
Creates a new ClientConnection and waits for data in the stream.
-
#receive_stanza(stanza) ⇒ Object
Called upon stanza reception Marked as connected when the client has been SASLed, authenticated, biund to a resource and when the session has been created.
-
#stream_namespace ⇒ Object
Namespace of the client.
-
#stream_stanza ⇒ Object
Builds the stream stanza for this client.
Methods inherited from XmppConnection
max_stanza_size, max_stanza_size=, #post_init, #send_xml, #unbind
Constructor Details
#initialize(params) ⇒ ClientConnection
Creates a new ClientConnection and waits for data in the stream
13 14 15 16 |
# File 'lib/babylon/client_connection.rb', line 13 def initialize(params) super(params) @state = :wait_for_stream end |
Instance Attribute Details
#binding_iq_id ⇒ Object (readonly)
Returns the value of attribute binding_iq_id.
9 10 11 |
# File 'lib/babylon/client_connection.rb', line 9 def binding_iq_id @binding_iq_id end |
#session_iq_id ⇒ Object (readonly)
Returns the value of attribute session_iq_id.
9 10 11 |
# File 'lib/babylon/client_connection.rb', line 9 def session_iq_id @session_iq_id end |
Class Method Details
.connect(params, handler = nil) ⇒ Object
Connects the ClientConnection based on SRV records for the jid’s domain, if no host or port has been specified. In any case, we give priority to the specified host and port.
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 |
# File 'lib/babylon/client_connection.rb', line 21 def self.connect(params, handler = nil) return super(params, handler) if params["host"] && params["port"] begin srv = [] Resolv::DNS.open { |dns| # If ruby version is too old and SRV is unknown, this will raise a NameError # which is caught below host_from_jid = params["jid"].split("/").first.split("@").last Babylon.logger.debug { "RESOLVING: _xmpp-client._tcp.#{host_from_jid} (SRV)" } srv = dns.getresources("_xmpp-client._tcp.#{host_from_jid}", Resolv::DNS::Resource::IN::SRV) } # Sort SRV records: lowest priority first, highest weight first srv.sort! { |a,b| (a.priority != b.priority) ? (a.priority <=> b.priority) : (b.weight <=> a.weight) } # And now, for each record, let's try to connect. srv.each { |record| begin params["host"] = record.target.to_s params["port"] = Integer(record.port) super(params, handler) # Success break rescue NotConnected # Try next SRV record end } rescue NameError Babylon.logger.debug { "Resolv::DNS does not support SRV records. Please upgrade to ruby-1.8.3 or later! \n#{$!} : #{$!.backtrace.join("\n")}" } end end |
Instance Method Details
#connection_completed ⇒ Object
Connection_completed is called when the connection (socket) has been established and is in charge of “building” the XML stream to establish the XMPP connection itself. We use a “tweak” here to send only the starting tag of stream:stream
75 76 77 78 |
# File 'lib/babylon/client_connection.rb', line 75 def connection_completed super send_xml(stream_stanza) end |
#receive_stanza(stanza) ⇒ Object
Called upon stanza reception Marked as connected when the client has been SASLed, authenticated, biund to a resource and when the session has been created
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
# File 'lib/babylon/client_connection.rb', line 83 def receive_stanza(stanza) case @state when :connected super # Can be dispatched when :wait_for_stream_authenticated if stanza.name == "stream:stream" && stanza.attributes['id'] @state = :wait_for_bind end when :wait_for_stream if stanza.name == "stream:stream" && stanza.attributes['id'] @state = :wait_for_auth_mechanisms end when :wait_for_auth_mechanisms if stanza.name == "stream:features" if stanza.at("starttls") # we shall start tls doc = Nokogiri::XML::Document.new starttls = Nokogiri::XML::Node.new("starttls", doc) doc.add_child(starttls) starttls["xmlns"] = "urn:ietf:params:xml:ns:xmpp-tls" send_xml(starttls.to_s) @state = :wait_for_proceed elsif stanza.at("mechanisms") # tls is ok if stanza.at("mechanisms").children.map() { |m| m.text }.include? "PLAIN" doc = Nokogiri::XML::Document.new auth = Nokogiri::XML::Node.new("auth", doc) doc.add_child(auth) auth['mechanism'] = "PLAIN" auth["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl" auth.content = Base64::encode64([jid, jid.split("@").first, @password].join("\000")).gsub(/\s/, '') send_xml(auth.to_s) @state = :wait_for_success end end end when :wait_for_success if stanza.name == "success" # Yay! Success @state = :wait_for_stream_authenticated @parser.reset send_xml(stream_stanza) elsif stanza.name == "failure" if stanza.at("bad-auth") || stanza.at("not-authorized") raise AuthenticationError else end else # Hum Failure... end when :wait_for_bind if stanza.name == "stream:features" if stanza.at("bind") doc = Nokogiri::XML::Document.new # Let's build the binding_iq @binding_iq_id = Integer(rand(10000000)) iq = Nokogiri::XML::Node.new("iq", doc) doc.add_child(iq) iq["type"] = "set" iq["id"] = binding_iq_id.to_s bind = Nokogiri::XML::Node.new("bind", doc) bind["xmlns"] = "urn:ietf:params:xml:ns:xmpp-bind" iq.add_child(bind) resource = Nokogiri::XML::Node.new("resource", doc) if jid.split("/").size == 2 resource.content = (@jid.split("/").last) else resource.content = "babylon_client_#{binding_iq_id}" end bind.add_child(resource) send_xml(iq.to_s) @state = :wait_for_confirmed_binding end end when :wait_for_confirmed_binding if stanza.name == "iq" && stanza["type"] == "result" && Integer(stanza["id"]) == binding_iq_id if stanza.at("jid") @jid = stanza.at("jid").text end # And now, we must initiate the session @session_iq_id = Integer(rand(10000)) doc = Nokogiri::XML::Document.new iq = Nokogiri::XML::Node.new("iq", doc) doc.add_child(iq) iq["type"] = "set" iq["id"] = session_iq_id.to_s session = Nokogiri::XML::Node.new("session", doc) session["xmlns"] = "urn:ietf:params:xml:ns:xmpp-session" iq.add_child(session) send_xml(iq.to_s) @state = :wait_for_confirmed_session end when :wait_for_confirmed_session if stanza.name == "iq" && stanza["type"] == "result" && Integer(stanza["id"]) == session_iq_id # And now, send a presence! doc = Nokogiri::XML::Document.new presence = Nokogiri::XML::Node.new("presence", doc) send_xml(presence.to_s) begin @handler.on_connected(self) if @handler and @handler.respond_to?("on_connected") rescue Babylon.logger.error { "on_connected failed : #{$!}\n#{$!.backtrace.join("\n")}" } end @state = :connected end when :wait_for_proceed start_tls() # starting TLS @state = :wait_for_stream @parser.reset send_xml stream_stanza end end |
#stream_namespace ⇒ Object
Namespace of the client
205 206 207 |
# File 'lib/babylon/client_connection.rb', line 205 def stream_namespace "jabber:client" end |
#stream_stanza ⇒ Object
Builds the stream stanza for this client
58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/babylon/client_connection.rb', line 58 def stream_stanza doc = Nokogiri::XML::Document.new stream = Nokogiri::XML::Node.new("stream:stream", doc) doc.add_child(stream) stream["xmlns"] = stream_namespace stream["xmlns:stream"] = "http://etherx.jabber.org/streams" stream["to"] = jid.split("/").first.split("@").last stream["version"] = "1.0" paste_content_here = Nokogiri::XML::Node.new("paste_content_here", doc) stream.add_child(paste_content_here) doc.to_xml.split('<paste_content_here/>').first end |