diff --git a/lib/xmpp-client.js b/lib/xmpp-client.js index 614117c..2773035 100644 --- a/lib/xmpp-client.js +++ b/lib/xmpp-client.js @@ -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'); \ No newline at end of file diff --git a/lib/xmpp-client/basic-client.js b/lib/xmpp-client/basic-client.js index 2540cdc..7f7b45a 100644 --- a/lib/xmpp-client/basic-client.js +++ b/lib/xmpp-client/basic-client.js @@ -5,6 +5,7 @@ var sys = require('sys'), xmpp = require('node-xmpp'), + util = require('util'), colors = require('colors'), events = require('events'); @@ -73,10 +74,18 @@ var BasicClient = function(params, callback) { jabber.emit('iq:unknow', stanza); } } else { - jabber.emit('iq:unknow', stanza); + 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); diff --git a/lib/xmpp-client/bytestream-server.js b/lib/xmpp-client/bytestream-server.js index 40f461d..f1a3a6f 100644 --- a/lib/xmpp-client/bytestream-server.js +++ b/lib/xmpp-client/bytestream-server.js @@ -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)); } } }; diff --git a/lib/xmpp-client/bytestream.js b/lib/xmpp-client/bytestream.js index 19bcd1b..d670b3d 100644 --- a/lib/xmpp-client/bytestream.js +++ b/lib/xmpp-client/bytestream.js @@ -1,19 +1,21 @@ -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"); +// 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"), + 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,104 +119,92 @@ 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; - - 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; + 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}); - client.write(new Buffer([0x05,0x01,0x00])); // CONNECT + // 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(); + } - client.addListener("data", function(data) { - if (data[0] !== 0x05) { - return; - } + // 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 + }; - // 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)); - }); - - // DST.PORT is two bytes - buff.push(0x00, 0x00); - - client.write(new Buffer(buff)); - - connected = true; - } else if (connected === true && data.length == 47 && data[1] === 0x00) { - // Request Activate - var reqHash = data.toString("ascii", 5, 5 + self.sidHash.length); + xmlIQ.c("streamhost", proxyParams).up(); + } + + jabber.xmpp.send(xmlIQ.up().tree()); + } else { + console.log("S5B Error: no available transports"); + } +}; + +Bytestream.prototype.sendProxy = function(streamProxy, streamPort) { + var self = this; + var jabber = this.client; + + 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 (reqHash === self.sidHash) { - var iqId = "S5B_"+ (new Date().getTime()).toString(); + if (stanza.attrs.from.toString() === streamProxy + && stanza.attrs.type.toString() === "result") { + // Send data! Finally! + if (typeof self.file.data === "function") { + self.file.data(client); + } else { + client.write(self.file.data); + } - // 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! - if (typeof self.file.data === "function") { - self.file.data(client); - } else { - client.write(self.file.data); - } - - delete jabber._iqCallback[self.iqId]; - } - }; - - jabber._iqCallback[iqId].error = function(error) { - console.log("Activation error"); - console.log(error.toString()); - - delete jabber._iqCallback[self.idIq]; - }; - - self.server.iq(self, - new xmpp.Element('query', {xmlns: XMLNS.BYTESTREAMS, sid: self.sidId}) - .c("activate").t(self.to) - .up() - .tree(), - streamProxy, - iqId - ); + delete jabber._iqCallback[iqId]; } - } else { - console.log(data); - } - }); + }; + + jabber._iqCallback[iqId].error = function(error) { + console.log("Activation error"); + console.log(error.toString()); + + delete jabber._iqCallback[iqId]; + }; + + var params = { + type: "set", + id: iqId, + to: streamProxy + }; + + var xmlIQ = + new xmpp.Element('iq', params) + .c('query', {xmlns: XMLNS.BYTESTREAMS, sid: self.sidId}) + .c("activate").t(self.to) + .up() + .up() + .tree(); + + jabber.xmpp.send(xmlIQ); + }; + + var cbFailure = function(error) { + console.log("Error with S5B proxy connection"); + console.log(error); + }; - client.addListener("close", function(data) { - sys.puts("Disconnected from server"); - }); - }); + var streamHosts = [{host: streamProxy, port: streamPort}]; + + self.parent.createS5BClient(self.sidHash, streamHosts, self.sidId, cbAcknowledged, undefined, cbFailure); }; - diff --git a/lib/xmpp-client/bytestreams.js b/lib/xmpp-client/bytestreams.js new file mode 100644 index 0000000..691f33b --- /dev/null +++ b/lib/xmpp-client/bytestreams.js @@ -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; + } + }); + }); +}; diff --git a/lib/xmpp-client/client.js b/lib/xmpp-client/client.js index e453356..35747d7 100644 --- a/lib/xmpp-client/client.js +++ b/lib/xmpp-client/client.js @@ -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() ); }; diff --git a/lib/xmpp-client/xmlns.js b/lib/xmpp-client/xmlns.js index 5c68897..6eb173f 100644 --- a/lib/xmpp-client/xmlns.js +++ b/lib/xmpp-client/xmlns.js @@ -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"; @@ -21,4 +23,5 @@ exports.VCARD_UPDATE = "vcard-temp:x:update"; 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"; \ No newline at end of file +exports.SESSION = "urn:ietf:params:xml:ns:xmpp-session"; +exports.STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas"; \ No newline at end of file