2008-04-30 17:08:39 -07:00
|
|
|
const EXPORTED_SYMBOLS = ['XmppClient', 'HTTPPollingTransport', 'PlainAuthenticator', 'Md5DigestAuthenticator'];
|
2008-04-30 16:27:32 -07:00
|
|
|
|
|
|
|
// See www.xulplanet.com/tutorials/mozsdk/sockets.php
|
|
|
|
// http://www.xmpp.org/specs/rfc3920.html
|
|
|
|
// http://www.process-one.net/docs/ejabberd/guide_en.html
|
|
|
|
// http://www.xulplanet.com/tutorials/mozsdk/xmlparse.php
|
|
|
|
// http://developer.mozilla.org/en/docs/xpcshell
|
|
|
|
// http://developer.mozilla.org/en/docs/Writing_xpcshell-based_unit_tests
|
|
|
|
|
2008-06-04 17:00:02 -07:00
|
|
|
// IM level protocol stuff: presence announcements, conversations, etc.
|
|
|
|
// ftp://ftp.isi.edu/in-notes/rfc3921.txt
|
|
|
|
|
2008-04-30 16:27:32 -07:00
|
|
|
var Cc = Components.classes;
|
|
|
|
var Ci = Components.interfaces;
|
2008-04-30 16:55:34 -07:00
|
|
|
var Cu = Components.utils;
|
2008-04-30 16:27:32 -07:00
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
function LOG(aMsg) {
|
|
|
|
dump("Weave::XMPPClient: " + aMsg + "\n");
|
|
|
|
}
|
|
|
|
|
2008-04-30 16:55:34 -07:00
|
|
|
Cu.import("resource://weave/xmpp/transportLayer.js");
|
|
|
|
Cu.import("resource://weave/xmpp/authenticationLayer.js");
|
2008-04-30 16:27:32 -07:00
|
|
|
|
2008-04-30 16:55:34 -07:00
|
|
|
function XmppClient( clientName, realm, clientPassword, transport, authenticator ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
this._init( clientName, realm, clientPassword, transport, authenticator );
|
|
|
|
}
|
2008-04-30 16:55:34 -07:00
|
|
|
XmppClient.prototype = {
|
2008-06-04 12:14:28 -07:00
|
|
|
//connection status codes:
|
|
|
|
NOT_CONNECTED: 0,
|
|
|
|
CALLED_SERVER: 1,
|
|
|
|
AUTHENTICATING: 2,
|
|
|
|
CONNECTED: 3,
|
|
|
|
FAILED: -1,
|
|
|
|
|
|
|
|
// IQ stanza status codes:
|
|
|
|
IQ_WAIT: 0,
|
|
|
|
IQ_OK: 1,
|
|
|
|
IQ_ERROR: -1,
|
|
|
|
|
|
|
|
_init: function( clientName, realm, clientPassword, transport, authenticator ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
this._myName = clientName;
|
|
|
|
this._realm = realm;
|
|
|
|
this._fullName = clientName + "@" + realm;
|
|
|
|
this._myPassword = clientPassword;
|
|
|
|
this._connectionStatus = this.NOT_CONNECTED;
|
|
|
|
this._error = null;
|
|
|
|
this._streamOpen = false;
|
|
|
|
this._transportLayer = transport;
|
|
|
|
this._authenticationLayer = authenticator;
|
2008-06-04 12:14:28 -07:00
|
|
|
LOG("initialized auth with clientName=" + clientName + ", realm=" + realm + ", pw=" + clientPassword);
|
2008-04-30 16:27:32 -07:00
|
|
|
this._authenticationLayer.initialize( clientName, realm, clientPassword );
|
|
|
|
this._messageHandlers = [];
|
|
|
|
this._iqResponders = [];
|
|
|
|
this._nextIqId = 0;
|
|
|
|
this._pendingIqs = {};
|
2008-06-12 17:35:44 -07:00
|
|
|
this._callbackOnConnect = null;
|
2008-04-30 16:27:32 -07:00
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
__parser: null,
|
2008-04-30 16:27:32 -07:00
|
|
|
get _parser() {
|
2008-06-04 12:14:28 -07:00
|
|
|
if (!this.__parser)
|
2008-04-30 16:27:32 -07:00
|
|
|
this.__parser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance( Ci.nsIDOMParser );
|
|
|
|
return this.__parser;
|
|
|
|
},
|
|
|
|
|
|
|
|
__threadManager: null,
|
|
|
|
get _threadManager() {
|
|
|
|
if (!this.__threadManager)
|
|
|
|
this.__threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
|
|
|
|
return this.__threadManager;
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
parseError: function( streamErrorNode ) {
|
|
|
|
LOG( "Uh-oh, there was an error!" );
|
2008-04-30 16:27:32 -07:00
|
|
|
var error = streamErrorNode.childNodes[0];
|
2008-06-04 12:14:28 -07:00
|
|
|
LOG( "Name: " + error.nodeName + " Value: " + error.nodeValue );
|
2008-04-30 16:27:32 -07:00
|
|
|
this._error = error.nodeName;
|
|
|
|
this.disconnect();
|
|
|
|
/* Note there can be an optional <text>bla bla </text> node inside
|
|
|
|
stream: error giving additional info; there can also optionally
|
|
|
|
be an app-specific condition element qualified by an app-defined
|
|
|
|
namespace */
|
|
|
|
},
|
|
|
|
|
2008-06-12 17:35:44 -07:00
|
|
|
_finishConnectionAttempt: function() {
|
|
|
|
if ( this._callbackOnConnect ) {
|
|
|
|
this._callbackOnConnect.call();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
setError: function( errorText ) {
|
|
|
|
LOG( "Error: " + errorText );
|
2008-04-30 16:27:32 -07:00
|
|
|
this._error = errorText;
|
|
|
|
this._connectionStatus = this.FAILED;
|
2008-06-12 17:35:44 -07:00
|
|
|
this._finishConnectionAttempt();
|
2008-04-30 16:27:32 -07:00
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
onIncomingData: function( messageText ) {
|
2008-06-04 14:02:47 -07:00
|
|
|
LOG("onIncomingData(): rcvd: " + messageText);
|
2008-04-30 16:27:32 -07:00
|
|
|
var responseDOM = this._parser.parseFromString( messageText, "text/xml" );
|
|
|
|
|
2008-06-04 17:36:37 -07:00
|
|
|
// Handle server disconnection
|
|
|
|
if (messageText.match("^</stream:stream>$")) {
|
|
|
|
this._handleServerDisconnection();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Detect parse errors, and attempt to handle them in the valid cases.
|
2008-06-04 14:02:47 -07:00
|
|
|
|
2008-06-04 17:36:37 -07:00
|
|
|
if (responseDOM.documentElement.nodeName == "parsererror" ) {
|
2008-06-04 14:02:47 -07:00
|
|
|
/*
|
|
|
|
Before giving up, remember that XMPP doesn't close the top-level
|
|
|
|
<stream:stream> element until the communication is done; this means
|
|
|
|
that what we get from the server is often technically only an
|
|
|
|
xml fragment. Try manually appending the closing tag to simulate
|
|
|
|
a complete xml document and then parsing that. */
|
|
|
|
|
2008-04-30 16:27:32 -07:00
|
|
|
var response = messageText + this._makeClosingXml();
|
|
|
|
responseDOM = this._parser.parseFromString( response, "text/xml" );
|
|
|
|
}
|
2008-06-04 12:14:28 -07:00
|
|
|
|
2008-04-30 16:27:32 -07:00
|
|
|
if ( responseDOM.documentElement.nodeName == "parsererror" ) {
|
|
|
|
/* If that still doesn't work, it might be that we're getting a fragment
|
2008-06-04 12:14:28 -07:00
|
|
|
with multiple top-level tags, which is a no-no. Try wrapping it
|
|
|
|
all inside one proper top-level stream element and parsing. */
|
2008-04-30 16:27:32 -07:00
|
|
|
response = this._makeHeaderXml( this._fullName ) + messageText + this._makeClosingXml();
|
|
|
|
responseDOM = this._parser.parseFromString( response, "text/xml" );
|
|
|
|
}
|
2008-06-04 12:14:28 -07:00
|
|
|
|
2008-04-30 16:27:32 -07:00
|
|
|
if ( responseDOM.documentElement.nodeName == "parsererror" ) {
|
|
|
|
/* Still can't parse it, give up. */
|
|
|
|
this.setError( "Can't parse incoming XML." );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-06-04 17:36:37 -07:00
|
|
|
// Message is parseable, now look for message-level errors.
|
|
|
|
|
2008-04-30 16:27:32 -07:00
|
|
|
var rootElem = responseDOM.documentElement;
|
|
|
|
|
2008-06-04 17:36:37 -07:00
|
|
|
var errors = rootElem.getElementsByTagName( "stream:error" );
|
|
|
|
if ( errors.length > 0 ) {
|
|
|
|
this.setError( errors[0].firstChild.nodeName );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Stream is valid.
|
|
|
|
|
|
|
|
// Detect and handle mid-authentication steps.
|
2008-04-30 16:27:32 -07:00
|
|
|
if ( this._connectionStatus == this.CALLED_SERVER ) {
|
|
|
|
// skip TLS, go straight to SALS. (encryption should be negotiated
|
|
|
|
// at the HTTP layer, i.e. use HTTPS)
|
|
|
|
|
|
|
|
//dispatch whatever the next stage of the connection protocol is.
|
|
|
|
response = this._authenticationLayer.generateResponse( rootElem );
|
|
|
|
if ( response == false ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
this.setError( this._authenticationLayer.getError() );
|
2008-04-30 16:27:32 -07:00
|
|
|
} else if ( response == this._authenticationLayer.COMPLETION_CODE ){
|
2008-06-04 12:14:28 -07:00
|
|
|
this._connectionStatus = this.CONNECTED;
|
2008-06-12 17:35:44 -07:00
|
|
|
this._finishConnectionAttempt();
|
2008-04-30 16:27:32 -07:00
|
|
|
} else {
|
2008-06-04 12:14:28 -07:00
|
|
|
this._transportLayer.send( response );
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-06-04 17:36:37 -07:00
|
|
|
// Detect and handle regular communication.
|
2008-04-30 16:27:32 -07:00
|
|
|
if ( this._connectionStatus == this.CONNECTED ) {
|
|
|
|
var presences = rootElem.getElementsByTagName( "presence" );
|
|
|
|
if (presences.length > 0 ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
var from = presences[0].getAttribute( "from" );
|
|
|
|
if ( from != undefined ) {
|
|
|
|
LOG( "I see that " + from + " is online." );
|
|
|
|
}
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
2008-06-04 17:36:37 -07:00
|
|
|
|
2008-04-30 16:27:32 -07:00
|
|
|
if ( rootElem.nodeName == "message" ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
this.processIncomingMessage( rootElem );
|
2008-04-30 16:27:32 -07:00
|
|
|
} else {
|
2008-06-04 12:14:28 -07:00
|
|
|
var messages = rootElem.getElementsByTagName( "message" );
|
|
|
|
if (messages.length > 0 ) {
|
|
|
|
for ( var message in messages ) {
|
|
|
|
this.processIncomingMessage( messages[ message ] );
|
|
|
|
}
|
|
|
|
}
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
2008-06-04 17:36:37 -07:00
|
|
|
|
2008-04-30 16:27:32 -07:00
|
|
|
if ( rootElem.nodeName == "iq" ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
this.processIncomingIq( rootElem );
|
2008-04-30 16:27:32 -07:00
|
|
|
} else {
|
2008-06-04 12:14:28 -07:00
|
|
|
var iqs = rootElem.getElementsByTagName( "iq" );
|
|
|
|
if ( iqs.length > 0 ) {
|
|
|
|
for ( var iq in iqs ) {
|
|
|
|
this.processIncomingIq( iqs[ iq ] );
|
|
|
|
}
|
|
|
|
}
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
processIncomingMessage: function( messageElem ) {
|
|
|
|
LOG( "in processIncomingMessage: messageElem is a " + messageElem );
|
2008-04-30 16:27:32 -07:00
|
|
|
var from = messageElem.getAttribute( "from" );
|
|
|
|
var contentElem = messageElem.firstChild;
|
|
|
|
// Go down till we find the element with nodeType = 3 (TEXT_NODE)
|
|
|
|
while ( contentElem.nodeType != 3 ) {
|
|
|
|
contentElem = contentElem.firstChild;
|
|
|
|
}
|
2008-06-04 12:14:28 -07:00
|
|
|
LOG( "Incoming message to you from " + from + ":" + contentElem.nodeValue );
|
2008-04-30 16:27:32 -07:00
|
|
|
for ( var x in this._messageHandlers ) {
|
|
|
|
// TODO do messages have standard place for metadata?
|
|
|
|
// will want to have handlers that trigger only on certain metadata.
|
|
|
|
this._messageHandlers[x].handle( contentElem.nodeValue, from );
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
processIncomingIq: function( iqElem ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
/* This processes both kinds of incoming IQ stanzas --
|
|
|
|
ones that are new (initated by another jabber client) and those that
|
|
|
|
are responses to ones we sent out previously. We can tell the
|
|
|
|
difference by the type attribute. */
|
|
|
|
var buddy = iqElem.getAttribute( "from " );
|
|
|
|
var id = iqElem.getAttribute( id );
|
|
|
|
|
|
|
|
switch( iqElem.getAttribute( "type" ) ) {
|
|
|
|
case "get":
|
|
|
|
/* Someone is asking us for the value of a variable.
|
|
|
|
Delegate this to the registered iqResponder; package the answer
|
|
|
|
up in an IQ stanza of the same ID and send it back to the asker. */
|
|
|
|
var variable = iqElem.firstChild.firstChild.getAttribute( "var" );
|
|
|
|
// TODO what to do here if there's more than one registered
|
|
|
|
// iqResponder?
|
|
|
|
var value = this._iqResponders[0].get( variable );
|
|
|
|
var query = "<query><getresult value='" + value + "'/></query>";
|
|
|
|
var xml = _makeIqXml( this._fullName, buddy, "result", id, query );
|
|
|
|
this._transportLayer.send( xml );
|
|
|
|
break;
|
|
|
|
case "set":
|
|
|
|
/* Someone is telling us to set the value of a variable.
|
2008-06-04 12:14:28 -07:00
|
|
|
Delegate this to the registered iqResponder; we can reply
|
|
|
|
either with an empty iq type="result" stanza, or else an
|
|
|
|
iq type="error" stanza */
|
2008-04-30 16:27:32 -07:00
|
|
|
var variable = iqElem.firstChild.firstChild.getAttribute( "var" );
|
|
|
|
var newValue = iqElem.firstChild.firstChildgetAttribute( "value" );
|
|
|
|
// TODO what happens when there's more than one reigistered
|
|
|
|
// responder?
|
|
|
|
// TODO give the responder a chance to say "no" and give an error.
|
|
|
|
this._iqResponders[0].set( variable, value );
|
|
|
|
var xml = _makeIqXml( this._fullName, buddy, "result", id, "<query/>" );
|
|
|
|
this._transportLayer.send( xml );
|
|
|
|
break;
|
|
|
|
case "result":
|
|
|
|
/* If all is right with the universe, then the id of this iq stanza
|
2008-06-04 12:14:28 -07:00
|
|
|
corresponds to a set or get stanza that we sent out, so it should
|
|
|
|
be in our pending dictionary.
|
2008-04-30 16:27:32 -07:00
|
|
|
*/
|
|
|
|
if ( this._pendingIqs[ id ] == undefined ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
this.setError( "Unexpected IQ reply id" + id );
|
|
|
|
return;
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
|
|
|
/* The result stanza may have a query with a value in it, in
|
2008-06-04 12:14:28 -07:00
|
|
|
which case this is the value of the variable we requested.
|
|
|
|
If there's no value, it was probably a set query, and should
|
|
|
|
just be considred a success. */
|
2008-04-30 16:27:32 -07:00
|
|
|
var newValue = iqElem.firstChild.firstChild.getAttribute( "value" );
|
|
|
|
if ( newValue != undefined ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
this._pendingIqs[ id ].value = newValue;
|
2008-04-30 16:27:32 -07:00
|
|
|
} else {
|
2008-06-04 12:14:28 -07:00
|
|
|
this._pendingIqs[ id ].value = true;
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
|
|
|
this._pendingIqs[ id ].status = this.IQ_OK;
|
|
|
|
break;
|
|
|
|
case "error":
|
|
|
|
/* Dig down through the element tree till we find the one with
|
2008-06-04 12:14:28 -07:00
|
|
|
the error text... */
|
2008-04-30 16:27:32 -07:00
|
|
|
var elems = iqElem.getElementsByTagName( "error" );
|
|
|
|
var errorNode = elems[0].firstChild;
|
|
|
|
if ( errorNode.nodeValue != null ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
this.setError( errorNode.nodeValue );
|
2008-04-30 16:27:32 -07:00
|
|
|
} else {
|
2008-06-04 12:14:28 -07:00
|
|
|
this.setError( errorNode.nodeName );
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
|
|
|
if ( this._pendingIqs[ id ] != undefined ) {
|
2008-06-04 12:14:28 -07:00
|
|
|
this._pendingIqs[ id ].status = this.IQ_ERROR;
|
2008-04-30 16:27:32 -07:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
registerMessageHandler: function( handlerObject ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
/* messageHandler object must have
|
|
|
|
handle( messageText, from ) method.
|
|
|
|
*/
|
|
|
|
this._messageHandlers.push( handlerObject );
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
registerIQResponder: function( handlerObject ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
/* IQResponder object must have
|
|
|
|
.get( variable ) and
|
|
|
|
.set( variable, newvalue ) methods. */
|
|
|
|
this._iqResponders.push( handlerObject );
|
|
|
|
},
|
2008-06-04 12:14:28 -07:00
|
|
|
|
|
|
|
onTransportError: function( errorText ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
this.setError( errorText );
|
|
|
|
},
|
2008-06-04 12:14:28 -07:00
|
|
|
|
2008-06-12 17:35:44 -07:00
|
|
|
connect: function( host, callback ) {
|
|
|
|
/* Do the handshake to connect with the server and authenticate.
|
|
|
|
callback is optional: if provided, it will be called (with no arguments)
|
|
|
|
when the connection has either succeeded or failed. */
|
|
|
|
if ( callback ) {
|
|
|
|
this._callbackOnConnect = callback;
|
|
|
|
}
|
2008-06-04 18:34:37 -07:00
|
|
|
this._transportLayer.connect();
|
2008-04-30 16:27:32 -07:00
|
|
|
this._transportLayer.setCallbackObject( this );
|
|
|
|
this._transportLayer.send( this._makeHeaderXml( host ) );
|
|
|
|
this._connectionStatus = this.CALLED_SERVER;
|
|
|
|
// Now we wait... the rest of the protocol will be driven by
|
|
|
|
// onIncomingData.
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
_makeHeaderXml: function( recipient ) {
|
|
|
|
return "<?xml version='1.0'?><stream:stream to='" +
|
|
|
|
recipient +
|
|
|
|
"' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>";
|
2008-04-30 16:27:32 -07:00
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
_makeMessageXml: function( messageText, fullName, recipient ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
/* a "message stanza". Note the message element must have the
|
|
|
|
full namespace info or it will be rejected. */
|
2008-06-04 12:14:28 -07:00
|
|
|
var msgXml = "<message xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='" +
|
|
|
|
fullName + "' to='" + recipient + "' xml:lang='en'><body>" +
|
|
|
|
messageText + "</body></message>";
|
|
|
|
LOG( "Message xml: " );
|
|
|
|
LOG( msgXml );
|
2008-04-30 16:27:32 -07:00
|
|
|
return msgXml;
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
_makePresenceXml: function( fullName ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
// a "presence stanza", sent to announce my presence to the server;
|
|
|
|
// the server is supposed to multiplex this to anyone subscribed to
|
|
|
|
// presence notifications.
|
|
|
|
return "<presence from ='" + fullName + "'><show/></presence>";
|
|
|
|
},
|
2008-06-04 12:14:28 -07:00
|
|
|
|
|
|
|
_makeIqXml: function( fullName, recipient, type, id, query ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
/* an "iq (info/query) stanza". This can be used for structured data
|
|
|
|
exchange: I send an <iq type='get' id='1'> containing a query,
|
|
|
|
and get back an <iq type='result' id='1'> containing the answer to my
|
|
|
|
query. I can also send an <iq type='set' id='2'> to set a value
|
|
|
|
remotely. The recipient answers with either <iq type='result'> or
|
|
|
|
<iq type='error'>, with an id matching the id of my set or get. */
|
|
|
|
|
|
|
|
//Useful!!
|
|
|
|
return "<iq xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' from='" + fullName + "' to='" + recipient + "' type='" + type + "' id='" + id + "'>" + query + "</iq>";
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
_makeClosingXml: function () {
|
2008-04-30 16:27:32 -07:00
|
|
|
return "</stream:stream>";
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
_generateIqId: function() {
|
2008-04-30 16:27:32 -07:00
|
|
|
// Each time this is called, it returns an ID that has not
|
|
|
|
// previously been used this session.
|
|
|
|
var id = "client_" + this._nextIqId;
|
|
|
|
this._nextIqId = this._nextIqId + 1;
|
|
|
|
return id;
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
_sendIq: function( recipient, query, type ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
var id = this._generateIqId();
|
|
|
|
this._pendingIqs[ id ] = { status: this.IQ_WAIT };
|
|
|
|
this._transportLayer.send( this._makeIqXml( this._fullName,
|
2008-06-04 12:14:28 -07:00
|
|
|
recipient,
|
|
|
|
type,
|
|
|
|
id,
|
|
|
|
query ) );
|
2008-04-30 16:27:32 -07:00
|
|
|
/* And then wait for a response with the same ID to come back...
|
|
|
|
When we get a reply, the pendingIq dictionary entry will have
|
|
|
|
its status set to IQ_OK or IQ_ERROR and, if it's IQ_OK and
|
|
|
|
this was a query that's supposed to return a value, the value
|
|
|
|
will be in the value field of the entry. */
|
|
|
|
var thread = this._threadManager.currentThread;
|
|
|
|
while( this._pendingIqs[ id ].status == this.IQ_WAIT ) {
|
|
|
|
thread.processNextEvent( true );
|
|
|
|
}
|
|
|
|
if ( this._pendingIqs[ id ].status == this.IQ_OK ) {
|
|
|
|
return this._pendingIqs[ id ].value;
|
|
|
|
} else if ( this._pendingIqs[ id ].status == this.IQ_ERROR ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Can't happen?
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
iqGet: function( recipient, variable ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
var query = "<query><getvar var='" + variable + "'/></query>";
|
|
|
|
return this._sendIq( recipient, query, "get" );
|
|
|
|
},
|
2008-06-04 12:14:28 -07:00
|
|
|
|
|
|
|
iqSet: function( recipient, variable, value ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
var query = "<query><setvar var='" + variable + "' value='" + value + "'/></query>";
|
|
|
|
return this._sendIq( recipient, query, "set" );
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
sendMessage: function( recipient, messageText ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
// OK so now I'm doing that part, but what am I supposed to do with the
|
|
|
|
// new JID that I'm bound to??
|
|
|
|
var body = this._makeMessageXml( messageText, this._fullName, recipient );
|
|
|
|
this._transportLayer.send( body );
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
announcePresence: function() {
|
|
|
|
this._transportLayer.send( this._makePresenceXml(this._myName) );
|
2008-04-30 16:27:32 -07:00
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
subscribeForPresence: function( buddyId ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
// OK, there are 'subscriptions' and also 'rosters'...?
|
|
|
|
//this._transportLayer.send( "<presence to='" + buddyId + "' type='subscribe'/>" );
|
|
|
|
// TODO
|
|
|
|
// other side must then approve this by sending back a presence to
|
|
|
|
// me with type ='subscribed'.
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
disconnect: function() {
|
2008-04-30 16:27:32 -07:00
|
|
|
// todo: only send closing xml if the stream has not already been
|
|
|
|
// closed (if there was an error, the server will have closed the stream.)
|
|
|
|
this._transportLayer.send( this._makeClosingXml() );
|
2008-06-04 14:02:47 -07:00
|
|
|
|
|
|
|
this.waitForDisconnect();
|
|
|
|
},
|
|
|
|
|
|
|
|
_handleServerDisconnection: function() {
|
2008-04-30 16:27:32 -07:00
|
|
|
this._transportLayer.disconnect();
|
2008-06-04 14:02:47 -07:00
|
|
|
this._connectionStatus = this.NOT_CONNECTED;
|
2008-04-30 16:27:32 -07:00
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
waitForConnection: function( ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
var thread = this._threadManager.currentThread;
|
|
|
|
while ( this._connectionStatus != this.CONNECTED &&
|
2008-06-04 12:14:28 -07:00
|
|
|
this._connectionStatus != this.FAILED ) {
|
2008-04-30 16:27:32 -07:00
|
|
|
thread.processNextEvent( true );
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2008-06-04 12:14:28 -07:00
|
|
|
waitForDisconnect: function() {
|
2008-04-30 16:27:32 -07:00
|
|
|
var thread = this._threadManager.currentThread;
|
|
|
|
while ( this._connectionStatus == this.CONNECTED ) {
|
|
|
|
thread.processNextEvent( true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|