|
|
|
/*
|
|
|
|
A very simple xmpp client
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
var sys = require('sys'),
|
|
|
|
xmpp = require('node-xmpp'),
|
|
|
|
colors = require('colors'),
|
|
|
|
events = require('events');
|
|
|
|
|
|
|
|
var BasicClient = function(params, callback) {
|
|
|
|
events.EventEmitter.call(this);
|
|
|
|
this.idle = (new Date().getTime());
|
|
|
|
this._iq = 0;
|
|
|
|
this._iqCallback = {};
|
|
|
|
this._iqHandler = {};
|
|
|
|
var jabber = this;
|
|
|
|
this.params = params;
|
|
|
|
var jid = new xmpp.JID(params.jid);
|
|
|
|
this.params.host = (params.host == null) ? jid.domain : params.host;
|
|
|
|
this.params.nickname = (params.resource == null) ? jid.user : params.resource;
|
|
|
|
|
|
|
|
this.xmpp = new xmpp.Client(params);
|
|
|
|
/*
|
|
|
|
this.xmpp.addListener('rawStanza', function(stanza) {
|
|
|
|
sys.debug("RAW: "[jabber.color] + stanza.toString().white);
|
|
|
|
});
|
|
|
|
*/
|
|
|
|
this.xmpp.addListener('authFail', function(e) {
|
|
|
|
jabber.emit('authFail', e);
|
|
|
|
});
|
|
|
|
this.xmpp.addListener('error', function(e) {
|
|
|
|
jabber.emit('error', e);
|
|
|
|
});
|
|
|
|
|
|
|
|
this.xmpp.addListener('stanza', function(stanza) {
|
|
|
|
switch(stanza.name) {
|
|
|
|
case 'iq':
|
|
|
|
switch(stanza.attrs.type) {
|
|
|
|
case 'error':
|
|
|
|
var q = stanza.getChild('query');
|
|
|
|
if(q !== undefined) {
|
|
|
|
if(q.attrs.xmlns != null && jabber._iqHandler[q.attrs.xmlns] != null) {
|
|
|
|
return jabber._iqHandler[q.attrs.xmlns].call(jabber, stanza);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof jabber._iqCallback[stanza.attrs.id] !== "undefined") {
|
|
|
|
jabber._iqCallback[stanza.attrs.id].error.apply(jabber, [stanza]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'result':
|
|
|
|
if (typeof jabber._iqCallback[stanza.attrs.id] === "undefined") {
|
|
|
|
var q = stanza.getChild('query');
|
|
|
|
if(q !== undefined) {
|
|
|
|
if(q.attrs.xmlns != null && jabber._iqHandler[q.attrs.xmlns] != null) {
|
|
|
|
return jabber._iqHandler[q.attrs.xmlns].call(jabber, stanza);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (typeof jabber._iqCallback[stanza.attrs.id] !== "undefined") {
|
|
|
|
jabber._iqCallback[stanza.attrs.id].success.apply(jabber, [stanza]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
jabber.emit('iq', stanza);
|
|
|
|
var q = stanza.getChild('query');
|
|
|
|
if(q == undefined) {
|
|
|
|
var q = stanza.getChild('time');
|
|
|
|
if(q !== undefined) {
|
|
|
|
if(q.attrs.xmlns != null && jabber._iqHandler[q.attrs.xmlns] != null) {
|
|
|
|
jabber._iqHandler[q.attrs.xmlns].call(jabber, stanza);
|
|
|
|
} else {
|
|
|
|
jabber.emit('iq:unknow', stanza);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
jabber.emit('iq:unknow', stanza);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(q.attrs.xmlns != null && jabber._iqHandler[q.attrs.xmlns] != null) {
|
|
|
|
jabber._iqHandler[q.attrs.xmlns].call(jabber, stanza);
|
|
|
|
} else {
|
|
|
|
jabber.emit('iq:unknow', stanza);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'presence':
|
|
|
|
var newPresence = false;
|
|
|
|
if(stanza.attrs.type == 'error') {
|
|
|
|
jabber.emit('presence:error', stanza);
|
|
|
|
} else {
|
|
|
|
// Skip self presence
|
|
|
|
if (jabber.jid.user + "@" + jabber.jid.domain + "/" + jabber.jid.resource === stanza.attrs.from) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// This is a presences for a conference
|
|
|
|
if (stanza.attrs.from.indexOf('conference') === -1 || stanza.attrs.from.indexOf('conference') < stanza.attrs.from.indexOf('@')) {
|
|
|
|
if (stanza.attrs.from.toString().indexOf('/') > -1) {
|
|
|
|
var user = stanza.attrs.from.split('/')[0];
|
|
|
|
var resource = stanza.attrs.from.substr(stanza.attrs.from.indexOf('/')+1);
|
|
|
|
} else {
|
|
|
|
var user = stanza.attrs.from.toString();
|
|
|
|
var resource = "unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof jabber.presences.users === "undefined" || jabber.presences.users === null) {
|
|
|
|
jabber.presences.users = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stanza.attrs.type === "unavailable") {
|
|
|
|
if (typeof jabber.presences.users[user] !== "undefined" && jabber.presences[user] !== null) {
|
|
|
|
delete jabber.presences[user][resource];
|
|
|
|
|
|
|
|
if (Object.keys(jabber.presences[user]).length === 0) {
|
|
|
|
delete jabber.presences[user];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (typeof jabber.presences.users[user] === "undefined") {
|
|
|
|
jabber.presences.users[user] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof jabber.presences.users[user][resource] === "undefined") {
|
|
|
|
jabber.presences.users[user][resource] = {};
|
|
|
|
newPresence = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
jabber.presences.users[user][resource] = {
|
|
|
|
user: user,
|
|
|
|
resource: resource,
|
|
|
|
jid: stanza.attrs.from,
|
|
|
|
priority: stanza.getChildText('priority'),
|
|
|
|
show: stanza.getChildText('show'),
|
|
|
|
status: stanza.getChildText('status')
|
|
|
|
};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var conference = stanza.attrs.from.split('/')[0];
|
|
|
|
var nickname = stanza.attrs.from.substr(stanza.attrs.from.indexOf('/')+1);
|
|
|
|
|
|
|
|
// Kill self references
|
|
|
|
if (nickname === jabber.params.nickname) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stanza.attrs.type === "unavailable") {
|
|
|
|
delete jabber.presences.muc[conference][nickname];
|
|
|
|
} else {
|
|
|
|
if (typeof jabber.presences.muc[conference] === "undefined") {
|
|
|
|
jabber.presences.muc[conference] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof jabber.presences.muc[conference][nickname] === "undefined") {
|
|
|
|
jabber.presences.muc[conference][nickname] = {};
|
|
|
|
newPresence = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
jabber.presences.muc[conference][nickname] = {
|
|
|
|
priority: stanza.getChildText("priority"),
|
|
|
|
show: stanza.getChildText("show"),
|
|
|
|
status: stanza.getChildText("status")
|
|
|
|
};
|
|
|
|
|
|
|
|
// Look to see if we have access to the user"s full details or if this is an anonymous room
|
|
|
|
if (typeof stanza.getChild("x", "http://jabber.org/protocol/muc#user") !== "undefined") {
|
|
|
|
var userDetails = stanza.getChild("x", "http://jabber.org/protocol/muc#user").getChild("item");
|
|
|
|
|
|
|
|
jabber.presences.muc[conference][nickname].affiliation = userDetails.attrs.affiliation;
|
|
|
|
jabber.presences.muc[conference][nickname].role = userDetails.attrs.role;
|
|
|
|
|
|
|
|
if (typeof userDetails.attrs.jid !== "undefined") {
|
|
|
|
var user = userDetails.attrs.jid.split("/")[0];
|
|
|
|
var resource = userDetails.attrs.jid.split("/")[1];
|
|
|
|
|
|
|
|
jabber.presences.muc[conference][nickname].jid = user;
|
|
|
|
jabber.presences.muc[conference][nickname].resource = resource;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
jabber.emit('presence', stanza, newPresence);
|
|
|
|
} catch (err) {
|
|
|
|
console.log("Something went wrong parsing presences: " + err.toString());
|
|
|
|
console.log(stanza.toString());
|
|
|
|
console.log(err.stack);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'message':
|
|
|
|
jabber.emit('message', stanza);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.log(stanza.toString());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
this.xmpp.addListener('online', function() {
|
|
|
|
jabber.jid = this.jid;
|
|
|
|
jabber.emit('online');
|
|
|
|
callback.apply(jabber);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
sys.inherits(BasicClient, events.EventEmitter);
|
|
|
|
exports.BasicClient = BasicClient;
|
|
|
|
|
|
|
|
BasicClient.prototype.unidle = function() {
|
|
|
|
this.idle = (new Date().getTime());
|
|
|
|
};
|
|
|
|
|
|
|
|
BasicClient.prototype.message = function(to, message) {
|
|
|
|
this.xmpp.send(new xmpp.Element('message', {
|
|
|
|
to: to,
|
|
|
|
type: 'chat'})
|
|
|
|
.c('body').t(message));
|
|
|
|
|
|
|
|
this.unidle();
|
|
|
|
};
|
|
|
|
|
|
|
|
BasicClient.prototype.presence = function(status, message)
|
|
|
|
{
|
|
|
|
if (typeof status === "undefined")
|
|
|
|
{
|
|
|
|
var status = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof message === "undefined")
|
|
|
|
{
|
|
|
|
var message = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
this.xmpp.send(new xmpp.Element('presence', { type: 'chat'}).
|
|
|
|
c('show').t(status).up().
|
|
|
|
c('status').t(message)
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
BasicClient.prototype.iq = function(to, query, callback, error, type) {
|
|
|
|
if(error == undefined) error = function(stanza) { sys.error((this.jid + " : " + stanza.toString()).red);};
|
|
|
|
var n = 'node' + this._iq++;
|
|
|
|
this._iqCallback[n] = {};
|
|
|
|
this._iqCallback[n].success = callback;
|
|
|
|
this._iqCallback[n].error = error;
|
|
|
|
if (typeof type === "undefined") {
|
|
|
|
var type = "get";
|
|
|
|
}
|
|
|
|
|
|
|
|
var attrs = {
|
|
|
|
type: type,
|
|
|
|
id: n
|
|
|
|
};
|
|
|
|
if(to != null) {
|
|
|
|
attrs.to = to;
|
|
|
|
};
|
|
|
|
this.xmpp.send(new xmpp.Element('iq', attrs).cnode(query).tree());
|
|
|
|
return n;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Answer an iq query
|
|
|
|
*/
|
|
|
|
BasicClient.prototype.resultIq = function(iqGet, result) {
|
|
|
|
if (typeof iqGet === "undefined" || typeof result === "string") {
|
|
|
|
this.xmpp.send(result);
|
|
|
|
} else {
|
|
|
|
this.xmpp.send(new xmpp.Element('iq', {
|
|
|
|
type: 'result',
|
|
|
|
from: iqGet.attrs.to,
|
|
|
|
to: iqGet.attrs.from,
|
|
|
|
id: iqGet.attrs.id
|
|
|
|
}).cnode(result).tree());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
BasicClient.prototype.registerIqHandler = function(xmlns, action) {
|
|
|
|
this._iqHandler[xmlns] = action;
|
|
|
|
};
|