4 * XXX For reference only until this code is updated to match new opensrf.js layout XXX
10 // ------------------------------------------------------------------
11 // Houses the jabber transport code
13 // 1. jabber_connection - high level jabber component
14 // 2. jabber_message - message class
15 // 3. jabber_socket - socket handling code (shouldn't have to
16 // use this class directly)
18 // Requires oils_utils.js
19 // ------------------------------------------------------------------
25 // ------------------------------------------------------------------
27 // High level transport code
29 // ------------------------------------------------------------------
31 // ------------------------------------------------------------------
32 jabber_connection.prototype = new transport_connection();
33 jabber_connection.prototype.constructor = jabber_connection;
34 jabber_connection.baseClass = transport_connection.prototype.constructor;
36 /** Initializes a jabber_connection object */
37 function jabber_connection( username, password, resource ) {
39 this.username = username;
40 this.password = password;
41 this.resource = resource;
42 this.socket = new jabber_socket();
48 /** Connects to the Jabber server. 'timeout' is the connect timeout
51 jabber_connection.prototype.connect = function( host, port, timeout ) {
53 return this.socket.connect(
54 this.username, this.password, this.resource, host, port, timeout );
57 /** Sends a message to 'recipient' with the provided message
60 jabber_connection.prototype.send = function( recipient, thread, body ) {
61 var jid = this.username+"@"+this.host+"/"+this.resource;
62 var msg = new jabber_message( jid, recipient, thread, body );
63 return this.socket.tcp_send( msg.to_string() );
66 /** This method will wait at most 'timeout' milliseconds
67 * for a Jabber message to arrive. If one arrives
68 * it is returned to the caller, other it returns null
70 jabber_connection.prototype.recv = function( timeout ) {
71 return this.socket.recv( timeout );
74 /** Disconnects from the jabber server */
75 jabber_connection.prototype.disconnect = function() {
76 return this.socket.disconnect();
79 /** Returns true if we are currently connected to the
82 jabber_connection.prototype.connected = function() {
83 return this.socket.connected();
88 // ------------------------------------------------------------------
90 // High level message handling code
93 jabber_message.prototype = new transport_message();
94 jabber_message.prototype.constructor = jabber_message;
95 jabber_message.prototype.baseClass = transport_message.prototype.constructor;
97 /** Builds a jabber_message object */
98 function jabber_message( sender, recipient, thread, body ) {
100 if( sender == null || recipient == null || recipient.length < 1 ) { return; }
102 this.doc = new DOMParser().parseFromString("<message></message>", "text/xml");
103 this.root = this.doc.documentElement;
104 this.root.setAttribute( "from", sender );
105 this.root.setAttribute( "to", recipient );
107 var body_node = this.doc.createElement("body");
108 body_node.appendChild( this.doc.createTextNode( body ) );
110 var thread_node = this.doc.createElement("thread");
111 thread_node.appendChild( this.doc.createTextNode( thread ) );
113 this.root.appendChild( body_node );
114 this.root.appendChild( thread_node );
118 /** Builds a new message from raw xml.
119 * If the message is a Jabber error message, then msg.is_error_msg
122 jabber_message.prototype.from_xml = function( xml ) {
123 var msg = new jabber_message();
124 msg.doc = new DOMParser().parseFromString( xml, "text/xml" );
125 msg.root = msg.doc.documentElement;
127 if( msg.root.getAttribute( "type" ) == "error" ) {
128 msg.is_error_msg = true;
130 this.is_error_msg = false;
136 /** Returns the 'from' field of the message */
137 jabber_message.prototype.get_sender = function() {
138 return this.root.getAttribute( "from" );
141 /** Returns the jabber thread */
142 jabber_message.prototype.get_thread = function() {
143 var nodes = this.root.getElementsByTagName( "thread" );
144 var thread_node = nodes.item(0);
145 return thread_node.firstChild.nodeValue;
148 /** Returns the message body */
149 jabber_message.prototype.get_body = function() {
150 var nodes = this.root.getElementsByTagName( "body" );
151 var body_node = nodes.item(0);
152 new Logger().transport( "Get Body returning:\n" + body_node.textContent, Logger.DEBUG );
153 return body_node.textContent;
156 /** Returns the message as a whole as an XML string */
157 jabber_message.prototype.to_string = function() {
158 return new XMLSerializer().serializeToString(this.root);
162 // ------------------------------------------------------------------
165 /** Initializes a new jabber_socket object */
166 function jabber_socket() {
168 this.is_connected = false;
176 /** Connects to the jabber server */
177 jabber_socket.prototype.connect =
178 function( username, password, resource, host, port, timeout ) {
180 var starttime = new Date().getTime();
182 // there has to be at least some kind of timeout
183 if( ! timeout || timeout < 100 ) { timeout = 1000; }
187 this.xpcom_init( host, port );
188 this.tcp_send( "<stream:stream to='"+host
189 +"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>" );
191 if( !this.tcp_recv( timeout ) ) { throw 1; }
194 throw new oils_ex_transport( "Could not open a socket to the transport server\n"
195 + "Server: " + host + " Port: " + port );
198 // Send the auth packet
199 this.tcp_send( "<iq id='123456789' type='set'><query xmlns='jabber:iq:auth'><username>"
200 + username + "</username><password>" + password +
201 "</password><resource>" + resource + "</resource></query></iq>" );
203 var cur = new Date().getTime();
204 var remaining = timeout - ( cur - starttime );
205 this.tcp_recv( remaining );
207 if( ! this.connected() ) {
208 throw new oils_ex_transport( "Connection to transport server timed out" );
215 /** Sets up all of the xpcom components */
216 jabber_socket.prototype.xpcom_init = function( host, port ) {
218 var transportService =
219 Components.classes["@mozilla.org/network/socket-transport-service;1"]
220 .getService(Components.interfaces.nsISocketTransportService);
222 this.transport = transportService.createTransport( null, 0, host, port, null);
224 // ------------------------------------------------------------------
225 // Build the stream objects
226 // ------------------------------------------------------------------
227 this.outstream = this.transport.openOutputStream(0,0,0);
229 var stream = this.transport.openInputStream(0,0,0);
231 this.instream = Components.classes["@mozilla.org/scriptableinputstream;1"]
232 .createInstance(Components.interfaces.nsIScriptableInputStream);
234 this.instream.init(stream);
238 /** Send data to the TCP pipe */
239 jabber_socket.prototype.tcp_send = function( data ) {
240 new Logger().transport( "Sending Data: \n" + data, Logger.INFO );
241 this.outstream.write(data,data.length);
245 /** Accepts data coming directly from the socket. If we're not
246 * connected, we pass it off to procecc_connect(). Otherwise,
247 * this method adds the data to the local buffer.
249 jabber_socket.prototype.process_data = function( data ) {
251 new Logger().transport( "Received TCP data: " + data, Logger.DEBUG );
253 if( ! this.connected() ) {
254 this.process_connect( data );
262 /** Processes connect data to verify we are logged in correctly */
263 jabber_socket.prototype.process_connect = function( data ) {
265 var reg = /type=["\']result["\']/;
268 if( reg.exec( data ) ) {
269 this.is_connected = true;
271 if( err.exec( data ) ) {
272 //throw new oils_ex_transport( "Server returned: \n" + data );
273 throw new oils_ex_jabber_auth( "Server returned: \n" + data );
274 // Throw exception, return something...
279 /** Waits up to at most 'timeout' milliseconds for data to arrive
280 * in the TCP buffer. If there is at least one byte of data
281 * in the buffer, then all of the data that is in the buffer is sent
282 * to the process_data method for processing and the method returns.
284 jabber_socket.prototype.tcp_recv = function( timeout ) {
286 var count = this.instream.available();
287 var did_receive = false;
289 // ------------------------------------------------------------------
290 // If there is any data in the tcp buffer, process it and return
291 // ------------------------------------------------------------------
296 new Logger().transport(
297 "before process data", Logger.DEBUG );
299 this.process_data( this.instream.read( count ) );
301 new Logger().transport(
302 "after process data", Logger.DEBUG );
304 count = this.instream.available();
306 new Logger().transport(
307 "received " + count + " bytes" , Logger.DEBUG );
312 // ------------------------------------------------------------------
313 // Do the timeout dance
314 // ------------------------------------------------------------------
316 // ------------------------------------------------------------------
317 // If there is no data in the buffer, wait up to timeout seconds
318 // for some data to arrive. Once it arrives, suck it all out
319 // and send it on for processing
320 // ------------------------------------------------------------------
323 now = new Date().getTime();
326 // ------------------------------------------------------------------
327 // Loop and poll for data every 50 ms.
328 // ------------------------------------------------------------------
329 while( ((now-then) <= timeout) && count <= 0 ) {
331 count = this.instream.available();
332 now = new Date().getTime();
335 // ------------------------------------------------------------------
336 // if we finally get some data, process it.
337 // ------------------------------------------------------------------
341 while( count > 0 ) { // pull in all of the data there is
342 this.process_data( this.instream.read( count ) );
343 count = this.instream.available();
352 /** If a message is already sitting in the queue, it is returned.
353 * If not, this method waits till at most 'timeout' milliseconds
354 * for a full jabber message to arrive and then returns that.
355 * If none ever arrives, returns null.
357 jabber_socket.prototype.recv = function( timeout ) {
360 now = new Date().getTime();
363 var first_pass = true;
364 while( ((now-then) <= timeout) ) {
366 if( this.buffer.length == 0 || !first_pass ) {
367 if( ! this.tcp_recv( timeout ) ) {
373 //new Logger().transport( "\n\nTCP Buffer Before: \n" + this.buffer, Logger.DEBUG );
375 var buf = this.buffer;
378 new Logger().transport( "CURRENT BUFFER\n" + buf,
381 buf = buf.replace( /\n/g, '' ); // remove pesky newlines
383 var reg = /<message.*?>.*?<\/message>/;
384 var iqr = /<iq.*?>.*?<\/iq>/;
385 var out = reg.exec(buf);
389 var msg_xml = out[0];
390 this.buffer = buf.substring( msg_xml.length, buf.length );
391 new Logger().transport( "Building Jabber message\n\n" + msg_xml, Logger.DEBUG );
392 var jab_msg = new jabber_message().from_xml( msg_xml );
393 if( jab_msg.is_error_msg ) {
394 new Logger().transport( "Received Jabber error message \n\n" + msg_xml, Logger.ERROR );
405 var msg_xml = out[0];
406 this.buffer = buf.substring( msg_xml.length, buf.length );
407 process_iq_data( msg_xml );
415 now = new Date().getTime();
421 jabber_socket.prototype.process_iq_data = function( data ) {
422 new Logger().transport( "IQ Packet received... Not Implemented\n" + data, Logger.ERROR );
425 /** Disconnects from the jabber server and closes down shop */
426 jabber_socket.prototype.disconnect = function() {
427 this.tcp_send( "</stream:stream>" );
428 this.instream.close();
429 this.outstream.close();
432 /** True if connected */
433 jabber_socket.prototype.connected = function() {
434 return this.is_connected;