1
0
Fork 0

Added inbound bytestreams and other modifications and fixes

master
Matthieu Lalonde 14 years ago committed by xSmurf
parent 0c275f54ad
commit a29539e44e

@ -2,7 +2,6 @@ exports.BasicClient = require('./xmpp-client/basic-client').BasicClient;
exports.Client = require('./xmpp-client/client').Client;
exports.Jid = require('./xmpp-client/client').Jid;
exports.Room = require('./xmpp-client/room').Room;
exports.BytestreamServer = require('./xmpp-client/bytestream-server').BytestreamServer;
exports.Bytestream = require('./xmpp-client/bytestream').Bytestream;
exports.Pubsub = require('./xmpp-client/bytestream').Pubsub;
exports.Bytestreams = require('./xmpp-client/bytestreams').Bytestreams;
exports.Pubsub = require('./xmpp-client/pubsub').Pubsub;
exports.XMLNS = require('./xmpp-client/xmlns');

@ -5,6 +5,7 @@
var sys = require('sys'),
xmpp = require('node-xmpp'),
util = require('util'),
colors = require('colors'),
events = require('events');
@ -72,11 +73,19 @@ var BasicClient = function(params, callback) {
} else {
jabber.emit('iq:unknow', stanza);
}
} else {
if (typeof jabber._iqCallback[stanza.attrs.id] !== "undefined") {
jabber._iqCallback[stanza.attrs.id].success.apply(jabber, [stanza]);
} else if(stanza.attrs.xmlns != null && jabber._iqHandler[stanza.attrs.xmlns] != null) {
jabber._iqHandler[stanza.attrs.xmlns].call(jabber, stanza);
} else {
jabber.emit('iq:unknow', stanza);
}
}
} else {
if(q.attrs.xmlns != null && jabber._iqHandler[q.attrs.xmlns] != null) {
if (typeof jabber._iqCallback[stanza.attrs.id] !== "undefined") {
jabber._iqCallback[stanza.attrs.id].success.apply(jabber, [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);

@ -5,11 +5,12 @@ var sys = require("sys"),
colors = require("colors"),
events = require("events");
var BytestreamServer = function(client) {
var BytestreamServer = function(parent) {
var self = this;
this.client = client;
this.port = (client.params.s5bPort == null) ? 8010 : client.params.s5bPort;
this.host = (client.params.s5bHost == null) ? "proxy." + client.params.host : client.params.s5bHost;
this.bytestreams = parent;
this.client = this.bytestreams.client;
this.port = (this.client.params.s5bPort == null) ? 8010 : this.client.params.s5bPort;
this.host = (this.client.params.s5bHost == null) ? "proxy." + this.client.params.host : this.client.params.s5bHost;
this._clients = {};
this._handlers = {};
this.tcpSocket = net.createServer(function(stream) {self.handleConnection(stream);});
@ -54,27 +55,6 @@ BytestreamServer.prototype.handleConnection = function(stream) {
stream.on("end", this._clients[stream.remoteAddress].onEnd);
};
BytestreamServer.prototype.iq = function(bytestream, query, to, id) {
var self = this;
var jabber = self.client;
if (typeof to === "undefined") {
var to = bytestream.to;
}
if (typeof id === "undefined") {
var id = bytestream.idIq;
}
var params = {
type: "set",
id: id,
to: to
};
jabber.xmpp.send(new xmpp.Element('iq', params).cnode(query).tree());
};
var BytestreamHandler = function(self, parent) {
self.parent = parent;
self.iqId = null;
@ -102,16 +82,6 @@ var BytestreamHandler = function(self, parent) {
&& data[data.length - 1] === 0x00 && data[data.length - 2] === 0x00) {
var reqHash = data.toString("ascii", 5, 45);
if ((reqHash in self.parent._handlers) === true) {
var buff = [0x05,0x00,0x00,0x03]; // Request header
buff.push(reqHash.length); // Announce data length
// Push our reqHash in the buffer
reqHash.split("").forEach(function(val) {
buff.push(val.charCodeAt(0));
});
// DST.PORT is two bytes
buff.push(0x00, 0x00);
// Add iq handler for receiving the target's ack
self.iqId = self.parent._handlers[reqHash].id;
self.parent.client._iqCallback[self.iqId] = {};
@ -130,7 +100,7 @@ var BytestreamHandler = function(self, parent) {
console.log(error);
};
self.write(new Buffer(buff));
self.write(self.bytestreams.buildHashResponse(reqHash, true));
}
}
};

@ -1,19 +1,21 @@
// TODO: add timeouts to the file offers
// TODO: Check if user is online first/do proper disco
var sys = require("sys"),
net = require("net"),
XMLNS = require("./xmlns.js"),
xmpp = require("node-xmpp"),
colors = require("colors"),
events = require("events"),
hash = require("../../../node-hash/lib/hash");
var Bytestream = function(server, file, to, idIq) {
var Bytestream = function(parent, file, to, idIq) {
var bytestream = this;
this.server = server;
this.client = server.client;
this.idIq = idIq;
this.parent = parent;
this.server = parent.server || null;
this.client = parent.client;
this.idIq = idIq || (new Date().getTime());
this.to = to;
this.sidId = this.idIq + "_" + (new Date().getTime());
this.sidHash = hash.sha1(this.sidId + this.client.jid.toString() + this.to);
this.sidId = "s5b_"+ this.idIq +"_"+ (new Date().getTime());
this.sidHash = parent.buildHash(this.sidId, this.client.jid, this.to);
this.file = file;
/*
// FIXME
@ -29,8 +31,6 @@ var Bytestream = function(server, file, to, idIq) {
this.sendInitiate();
};
sys.inherits(Bytestream, events.EventEmitter);
exports.Bytestream = Bytestream;
Bytestream.prototype.sendInitiate = function() {
@ -49,7 +49,7 @@ Bytestream.prototype.sendInitiate = function() {
var error = error.getChild("error");
if (typeof error.getChild("forbidden") !== "undefined") {
self.client.message(self.to, "Ok, maybe some other time!");
jabber.message(self.to, "Ok, maybe some other time!");
declined = true;
}
}
@ -62,9 +62,15 @@ Bytestream.prototype.sendInitiate = function() {
};
// Build and send the data negotiation request
self.server.iq(self,
new xmpp.Element
("si", {xmlns: XMLNS.SI, profile: XMLNS.SI_PROFILE, id: self.sidId, "mime-type": self.file.mimeType})
var params = {
type: "set",
id: self.idIq,
to: self.to
};
var xmlIQ =
new xmpp.Element('iq', params)
.c("si", {xmlns: XMLNS.SI, profile: XMLNS.SI_PROFILE, id: self.sidId, "mime-type": self.file.mimeType})
.c("file", {xmlns: XMLNS.SI_PROFILE, size: self.file.length, name: self.file.name})
.c("desc").t((typeof self.file.desc !== "undefined" ? self.file.desc : "")).up()
.c("range").up()
@ -80,8 +86,10 @@ Bytestream.prototype.sendInitiate = function() {
.up()
.up()
.up()
.tree()
);
.up()
.tree();
jabber.xmpp.send(xmlIQ);
};
Bytestream.prototype.sendFile = function() {
@ -96,11 +104,11 @@ Bytestream.prototype.sendFile = function() {
&& typeof stanza.getChild("query").getChild("streamhost-used") !== "undefined") {
var usedStream = stanza.getChild("query").getChild("streamhost-used").attrs.jid.toString();
// We connected through the local S5B
if (usedStream === self.client.jid.toString()) {
if (usedStream === jabber.jid.toString()) {
// Nothing here really, we only got this after the session is ready and this has been overwritten
// Connected through the server's S5B proxy
} else {
self.sendProxy(usedStream);
self.sendProxy(usedStream, jabber.params.proxyPort);
}
}
};
@ -111,67 +119,49 @@ Bytestream.prototype.sendFile = function() {
delete jabber._iqCallback[self.idIq];
};
self.server.iq(self,
new xmpp.Element
('query', {xmlns: XMLNS.BYTESTREAMS, sid: self.sidId})
// S5B Direct
.c("streamhost", {jid: jabber.jid.toString(), host: self.server.host, port: self.server.port}).up()
// S5B Proxy
.c("streamhost", {jid: self.client.params.proxyJid, host: self.client.params.proxyHost, port: self.client.params.proxyPort}).up()
.tree()
);
self.server.addHandler(self.idIq, self.sidHash, self.file.data);
var params = {
type: "set",
id: self.idIq,
to: self.to
};
Bytestream.prototype.sendProxy = function(streamProxy) {
var self = this;
var jabber = this.client;
if (self.server !== null || ("proxyHost" in self.client.params) === true) {
var xmlIQ = new xmpp.Element('iq', params).c('query', {xmlns: XMLNS.BYTESTREAMS, sid: self.sidId});
var client = net.createConnection(7777, streamProxy);
client.addListener("error", function(error) {
console.log("Error with S5B proxy connection for "+ self.to +" "+ client.remoteAddress);
console.log(error);
});
client.addListener("connect", function() {
var connected = false;
client.write(new Buffer([0x05,0x01,0x00])); // CONNECT
client.addListener("data", function(data) {
if (data[0] !== 0x05) {
return;
// S5B Direct
if (self.server !== null) {
self.server.addHandler(self.idIq, self.sidHash, self.file.data);
xmlIQ.c("streamhost", {jid: jabber.jid.toString(), host: self.server.host, port: self.server.port}).up();
}
// Ack
if (connected === false && data.length === 0x02 && data[1] === 0x00) {
var buff = [0x05,0x01,0x0,0x03]; // Request header
buff.push(self.sidHash.length); // Announce data length
// Push our sidHash in the buffer
self.sidHash.split("").forEach(function(val) {
buff.push(val.charCodeAt(0));
});
// S5B Proxy
if (("proxyHost" in self.client.params) === true) {
var proxyParams = {
jid: self.client.params.proxyJid,
host: self.client.params.proxyHost,
port: self.client.params.proxyPort
};
// DST.PORT is two bytes
buff.push(0x00, 0x00);
xmlIQ.c("streamhost", proxyParams).up();
}
client.write(new Buffer(buff));
jabber.xmpp.send(xmlIQ.up().tree());
} else {
console.log("S5B Error: no available transports");
}
};
connected = true;
} else if (connected === true && data.length == 47 && data[1] === 0x00) {
// Request Activate
var reqHash = data.toString("ascii", 5, 5 + self.sidHash.length);
Bytestream.prototype.sendProxy = function(streamProxy, streamPort) {
var self = this;
var jabber = this.client;
if (reqHash === self.sidHash) {
var iqId = "S5B_"+ (new Date().getTime()).toString();
var cbAcknowledged = function(client, streamHost, sid) {
var iqId = "s5b_"+ (new Date().getTime()).toString();
// Register activation callback
jabber._iqCallback[iqId] = {};
jabber._iqCallback[iqId].success = function(stanza) {
if (stanza.attrs.from.toString() === streamProxy
&& stanza.attrs.type.toString() === "result") {
// Send data! Finally!
@ -181,7 +171,7 @@ Bytestream.prototype.sendProxy = function(streamProxy) {
client.write(self.file.data);
}
delete jabber._iqCallback[self.iqId];
delete jabber._iqCallback[iqId];
}
};
@ -189,26 +179,32 @@ Bytestream.prototype.sendProxy = function(streamProxy) {
console.log("Activation error");
console.log(error.toString());
delete jabber._iqCallback[self.idIq];
delete jabber._iqCallback[iqId];
};
var params = {
type: "set",
id: iqId,
to: streamProxy
};
self.server.iq(self,
new xmpp.Element('query', {xmlns: XMLNS.BYTESTREAMS, sid: self.sidId})
var xmlIQ =
new xmpp.Element('iq', params)
.c('query', {xmlns: XMLNS.BYTESTREAMS, sid: self.sidId})
.c("activate").t(self.to)
.up()
.tree(),
streamProxy,
iqId
);
}
} else {
console.log(data);
}
});
.up()
.tree();
client.addListener("close", function(data) {
sys.puts("Disconnected from server");
});
});
jabber.xmpp.send(xmlIQ);
};
var cbFailure = function(error) {
console.log("Error with S5B proxy connection");
console.log(error);
};
var streamHosts = [{host: streamProxy, port: streamPort}];
self.parent.createS5BClient(self.sidHash, streamHosts, self.sidId, cbAcknowledged, undefined, cbFailure);
};

@ -0,0 +1,336 @@
// TODO: check if mode udp and refuse connection
var sys = require("sys"),
net = require("net"),
XMLNS = require("./xmlns.js"),
xmpp = require("node-xmpp"),
colors = require("colors"),
events = require("events"),
Bytestream = require("./bytestream").Bytestream,
BytestreamServer = require("./bytestream-server").BytestreamServer,
hash = require("../../../node-hash/lib/hash");
var Bytestreams = function(client, params) {
var self = this;
this.client = client;
this.incomingHandlers = [];
this.incomingFiles = {};
this.defaultParams = {};
this.params = params || this.defaultParams;
if (("server" in this.params) === true && this.params.server === true) {
this.server = new BytestreamServer(this);
}
// Register IQ handlers
// Stream negociate start
// TODO Send declined if there are no handlers
this.client.registerIqHandler(XMLNS.CLIENT, function(stanza) {
if (("xmlns:stream" in stanza.attrs) === true && stanza.attrs["xmlns:stream"] === XMLNS.STREAMS
&& stanza.getChild("si") !== undefined && stanza.getChild("si").getChild("file") !== undefined
&& stanza.getChild("si").getChild("feature") !== undefined
&& stanza.getChild("si").getChild("feature").getChild("x") !== undefined) {
if (self.incomingHandlers.length > 0) {
self.streamNegociate(stanza);
} else {
self.sendDecline(stanza);
}
}
});
// Stream negociate s5b
this.client.registerIqHandler(XMLNS.BYTESTREAMS, function(stanza) {
if (stanza.getChild("query") !== undefined
&& ("xmlns:stream" in stanza.attrs) === true && stanza.attrs["xmlns:stream"] === XMLNS.STREAMS
&& (stanza.getChild("query").attrs.sid in self.incomingFiles) === true) {
if (self.incomingHandlers.length > 0 && Object.keys(self.incomingFiles).length > 0) {
self.s5bNegociate(stanza);
} else {
self.sendError(stanza);
}
}
});
};
exports.Bytestreams = Bytestreams;
Bytestreams.prototype.s5bCommands = {};
Bytestreams.prototype.s5bCommands.connect = new Buffer([0x05,0x01,0x00]);
Bytestreams.prototype.registerIncomingHandler = function(cb) {
this.incomingHandlers.push(cb);
};
// Static
Bytestreams.prototype.buildHash = function(sid, from, to) {
return hash.sha1(sid + from.toString() + to.toString());
};
// Static
Bytestreams.prototype.buildHashResponse = function(hash, server) {
var cmdByte = ( (typeof server === "undefined" || server === true) ? 0x01 : 0x00 );
var buff = [0x05, cmdByte, 0x00, 0x03]; // Request header
buff.push(hash.length); // Announce data length
// Push our reqHash in the buffer
hash.split("").forEach(function(val) {
buff.push(val.charCodeAt(0));
});
// DST.PORT is two bytes
buff.push(0x00, 0x00);
return new Buffer(buff);
};
// Static
Bytestreams.prototype.hashMatchData = function(hash, data) {
var matches = false;
if (data.length == 47 && data[1] === 0x00) {
var reqHash = data.toString("ascii", 5, hash.length + 5);
if (reqHash === hash) {
matches = true;
}
}
return matches;
};
// Static
Bytestreams.prototype.checkBytestreamSupport = function(stanza) {
var options = stanza.getChild("si").getChild("feature").getChild("x").getChild("field");
var supported = false;
for (var ii = 0, len = options.children.length; ii < len; ii++) {
if (typeof options.children[ii] === "object"
&& options.children[ii].getChildText("value") === XMLNS.BYTESTREAMS) {
supported = true;
break;
}
}
return supported;
};
Bytestreams.prototype.sendFile = function(file, to, idIq) {
return new Bytestream(this, file, to, idIq);
};
Bytestreams.prototype.streamSendResponse = function(stanza, fileSID) {
if (this.checkBytestreamSupport(stanza) === true) {
this.client.xmpp.send(
new xmpp.Element('iq', {type: "result", to: stanza.attrs.from.toString(), id: stanza.attrs.id})
.c("si", {xmlns: XMLNS.SI})
.c("feature", {xmlns: XMLNS.FEATURE_NEG})
.c("x", {xmlns: XMLNS.DATA, type: "submit"})
.c("field", {"var": "stream-method"})
.c("value").t(XMLNS.BYTESTREAMS)
.up()
.up()
.up()
.up()
.up()
.tree()
);
} else {
delete this.incomingFiles[fileSID];
this.client.xmpp.send(
new xmpp.Element('iq', {type: "error", to: stanza.attrs.from.toString(), id: stanza.attrs.id})
.c("error", {code: 400, type: "cancel"})
.c("bad-request", {xmlns: XMLNS.STANZAS}).up()
.c("no-valid-streams", {xmlns: XMLNS.SI}).up()
.up()
.up()
.tree()
);
}
};
Bytestreams.prototype.streamNegociate = function(stanza) {
var fileStanza = stanza.getChild("si").getChild("file").attrs;
fileStanza.mime = stanza.getChild("si").attrs["mime-type"] || null;
var fileSID = stanza.getChild("si").attrs.id;
var fileWanted = false;
for (ii in this.incomingHandlers) {
var fileHandler = this.incomingHandlers[ii](fileSID, fileStanza, stanza);
if (typeof fileHandler === "function") {
if ((fileSID in this.incomingFiles) === false) {
this.incomingFiles[fileSID] = fileStanza;
this.incomingFiles[fileSID].cb = [];
}
this.incomingFiles[fileSID].cb.push(fileHandler);
fileWanted = true;
}
}
if (fileWanted === true) {
this.streamSendResponse(stanza, fileSID);
} else {
this.sendDecline(stanza);
}
};
Bytestreams.prototype.s5bNegociate = function(stanza) {
var self = this;
var fileSID = stanza.getChild("query").attrs.sid;
var streamHosts = [];
var streamJIDs = {};
for (var ii = 0, len = stanza.getChild("query").children.length; ii < len; ii++) {
if (typeof stanza.getChild("query").children[ii] === "object") {
if (stanza.getChild("query").children[ii].name === "fast") {
// Not handled
} else if (stanza.getChild("query").children[ii].name === "streamhost") {
var streamHost = stanza.getChild("query").children[ii].attrs;
if (stanza.getChild("query").children[ii].getChild("proxy")) {
streamHost.proxy = true;
}
if ((streamHost.jid in streamJIDs) !== true) {
streamJIDs[streamHost.jid] = true;
streamHosts.push(streamHost);
}
}
}
}
if (streamHosts.length === 0) {
this.sendError(stanza);
} else {
this.prepareS5BConnection(stanza, fileSID, streamHosts);
}
};
Bytestreams.prototype.sendError = function(stanza) {
this.client.xmpp.send(
new xmpp.Element("iq", {to: stanza.attrs.from, id: stanza.attrs.id, type: "error"})
.c("error", {type: "auth"})
.c("not-acceptable", {xmlns: XMLNS.STANZAS})
.up()
.up()
.tree()
);
};
Bytestreams.prototype.sendDecline = function(stanza) {
this.client.xmpp.send(
new xmpp.Element("iq", {to: stanza.attrs.from, id: stanza.attrs.id, type: "error"})
.c("error", {type: "cancel", code: 403})
.c("forbidden", {xmlns: XMLNS.STANZAS}).up()
.c("text", {xmlns: XMLNS.STANZAS}).t("Offer Declined").up()
.up()
.up()
.tree()
);
};
Bytestreams.prototype.prepareS5BConnection = function(stanza, fileSID, streamHosts) {
var self = this;
self.incomingFiles[fileSID].sid = fileSID;
self.incomingFiles[fileSID].hash = this.buildHash(fileSID, stanza.attrs.from, this.client.jid.toString());
var cbFailure = function(error, sid) {
delete self.incomingFiles[sid];
self.client.xmpp.send(
new xmpp.Element("iq", {to: stanza.attrs.from, from: stanza.attrs.to, id: stanza.attrs.id, type: "error"})
.c("error", {type: "cancel"})
.c("item-not-found", {xmlns: XMLNS.STANZAS})
.up()
.up()
.tree()
);
};
var cbAcknowledged = function(client, streamHost, sid) {
self.client.xmpp.send(
new xmpp.Element("iq", {id: stanza.attrs.id, to: stanza.attrs.from, type: "result"})
.c("query", {xmlns: XMLNS.BYTESTREAMS, sid: fileSID})
.c("streamhost-used", {jid: streamHost.jid})
.up()
.tree()
);
};
var cbSuccess = function(sid, data) {
for (ii in self.incomingFiles[sid].cb) {
self.incomingFiles[sid].cb[ii](self.incomingFiles[sid], data);
}
delete self.incomingFiles[sid];
};
// TODO iterate all the possible streamhost and support proxy server negociation
self.createS5BClient(self.incomingFiles[fileSID].hash, streamHosts, fileSID, cbAcknowledged, cbSuccess, cbFailure, self.incomingFiles[fileSID].size);
};
// Static
Bytestreams.prototype.createS5BClient = function(hash, streamHosts, sid, cbAcknowledged, cbSuccess, cbFailure, fileLen, ii) {
var iiStream = ii || 0;
var client = net.createConnection(streamHosts[iiStream].port, streamHosts[iiStream].host.replace("127.0.0.1", "10.0.0.174"));
client.addListener("error", function(error) {
if (++iiStream < streamHosts.length) {
Bytestreams.prototype.createS5BClient(hash, streamHosts, sid, cbAcknowledged, cbSuccess, cbFailure, fileLen, iiStream);
} else {
cbFailure(error, sid);
}
});
client.addListener("connect", function() {
var connected = false;
var acknowledged = false;
var done = false;
if (typeof fileLen !== "undefined") {
var bufferData = null;
}
client.write(Bytestreams.prototype.s5bCommands.connect);
client.addListener("end", function() {
done = true;
if (typeof cbSuccess === "function") {
cbSuccess(sid, bufferData);
client.end();
}
});
client.addListener("data", function(data) {
// Ack
if (acknowledged === false && data.length === 0x02 && data[1] === 0x00) {
client.write(Bytestreams.prototype.buildHashResponse(hash));
acknowledged = true;
} else if (connected === false && acknowledged === true) {
if (Bytestreams.prototype.hashMatchData(hash, data) === true) {
connected = true;
cbAcknowledged(client, streamHosts[iiStream], sid);
} else {
cbFailure("hash", data);
}
} else if (typeof fileLen !== "undefined" && connected === true && done !== true) {
var newBuff = new Buffer((bufferData === null ? 0 : bufferData.length) + data.length);
if (bufferData !== null) {
bufferData.copy(newBuff, 0, 0);
}
data.copy(newBuff, (bufferData === null ? 0 : bufferData.length), 0);
bufferData = newBuff;
}
});
});
};

@ -1,14 +1,18 @@
var sys = require('sys'),
util = require('util'),
colors = require('colors'),
var sys = require("sys"),
util = require("util"),
colors = require("colors"),
net = require("net"),
XMLNS = require("./xmlns.js"),
xmpp = require("node-xmpp"),
BasicClient = require("./basic-client").BasicClient,
dateFormat = require("../../../date.js").dateFormat,
xmpp = require('node-xmpp'),
BasicClient = require('./basic-client').BasicClient;
hash = require("../../../node-hash/lib/hash");
var Client = function(params, callback) {
var jabber = this;
var params = params;
var xmpp = this.xmpp;
this.roster = {};
this.presences = {};
@ -21,24 +25,24 @@ var Client = function(params, callback) {
}
else
{
jabber.emit('binded', this);
jabber.emit("binded", this);
}
});
});
this.registerIqHandler('http://jabber.org/protocol/disco#info', function(stanza) {
this.registerIqHandler("http://jabber.org/protocol/disco#info", function(stanza) {
jabber.sendDisco(stanza);
});
this.registerIqHandler('jabber:iq:last', function(stanza) {
this.registerIqHandler("jabber:iq:last", function(stanza) {
jabber.sendLast(stanza);
});
this.registerIqHandler('urn:xmpp:time', function(stanza) {
this.registerIqHandler("urn:xmpp:time", function(stanza) {
jabber.sendTime(stanza);
});
this.registerIqHandler('jabber:iq:version', function(stanza) {
this.registerIqHandler("jabber:iq:version", function(stanza) {
if (typeof jabber.cbVersion === "function") {
jabber.resultIq(stanza, jabber.cbVersion(stanza));
} else {
@ -106,11 +110,22 @@ Client.prototype.sendDisco = function(stanza) {
.c('feature', {'var': 'http://jabber.org/protocol/disco#info'}).up()
.c('feature', {'var': 'http://jabber.org/protocol/disco#items'}).up()
.c('feature', {'var': 'http://jabber.org/protocol/muc'}).up()
.c('feature', {'var': XMLNS.TIME}).up()
.c('feature', {'var': XMLNS.IQ_LAST}).up()
.c('feature', {'var': XMLNS.VERSION}).up()
.c('feature', {'var': XMLNS.BYTESTREAMS}).up()
.c('feature', {'var': XMLNS.SI}).up()
.c('feature', {'var': XMLNS.SI_PROFILE}).up()
.c('identity', {
category: 'conference',
type: 'text',
name: 'Play-Specific Chatrooms'
}).up()
.c('identity', {
category: 'client',
type: 'bot',
name: this.jid.resource
}).up()
.tree()
);
};

@ -14,6 +14,8 @@ exports.CLIENT = "jabber:client";
exports.ROASTER = "jabber:iq:roster";
exports.IQ_LAST = "jabber:iq:last";
exports.DATA = "jabber:x:data";
exports.VERSION = "jabber:iq:version";
exports.TIME = "jabber:iq:time";
exports.VCARD = "vcard-temp";
exports.VCARD_UPDATE = "vcard-temp:x:update";
@ -22,3 +24,4 @@ exports.TLS = "urn:ietf:params:xml:ns:xmpp-tls";
exports.SASL = "urn:ietf:params:xml:ns:xmpp-sasl";
exports.BIND = "urn:ietf:params:xml:ns:xmpp-bind";
exports.SESSION = "urn:ietf:params:xml:ns:xmpp-session";
exports.STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas";
Loading…
Cancel
Save