Index: dojo-release-1.3.2-src/dojox/xmpp/ChatService.js =================================================================== --- dojo-release-1.3.2-src.orig/dojox/xmpp/ChatService.js 2010-01-04 11:09:30.000000000 +0530 +++ dojo-release-1.3.2-src/dojox/xmpp/ChatService.js 2010-01-04 11:09:32.000000000 +0530 @@ -63,7 +63,7 @@ }, - sendMessage: function(msg){ + sendMessage: function(msg, type){ if (!this.uid){ //console.log("ChatService::sendMessage() - Contact Id is null, need to invite to chat"); return; @@ -71,11 +71,16 @@ if ((!msg.body || msg.body=="") && !msg.xhtml){return;} + var msgType = "chat"; + if(type){ + msgType = type; + } + var req = { xmlns: "jabber:client", to: this.uid, from: this.session.jid + "/" + this.session.resource, - type: "chat" + type: msgType } var message = new dojox.string.Builder(dojox.xmpp.util.createElement("message",req,false)); @@ -93,9 +98,9 @@ } } */ - if (message.subject && message.subject != ""){ + if (msg.subject){ message.append(dojox.xmpp.util.createElement("subject",{},false)); - message.append(message.subject); + message.append(msg.subject); message.append(""); } message.append(bodyPlainTag); Index: dojo-release-1.3.2-src/dojox/xmpp/MUCManager.js =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ dojo-release-1.3.2-src/dojox/xmpp/MUCManager.js 2010-01-27 19:26:26.000000000 +0530 @@ -0,0 +1,521 @@ +dojo.provide("dojox.xmpp.MUCManager"); +dojo.require("dojox.xmpp.MUCService"); +dojo.require("dojox.xmpp._MUCAdminService"); + +dojox.xmpp.muc = { + MUC_NS: "http://jabber.org/protocol/muc", + MUC_USER_NS: "http://jabber.org/protocol/muc#user", + MUC_ADMIN_NS: "http://jabber.org/protocol/muc#admin", + MUC_OWNER_NS: "http://jabber.org/protocol/muc#owner", + ROOM_USER_NS: "x-roomuser-item", + ROOM_NAME_NS: "muc#roomconfig_roomname", + ROOM_DESC_NS: "muc#roomconfig_roomdesc", + PERSIST_ROOM_NS: "muc#roomconfig_persistentroom", + MOD_ROOM_NS: "muc#roomconfig_moderatedroom", + MEMBERS_ONLY_NS: "muc#roomconfig_membersonly", + PUB_ROOM_NS: "muc#roomconfig_public", + DIST_ROOM_NS: "muc#roomconfig_distributedroom", + ROOM_LANG_NS: "muc#roomconfig_lang", + ROOM_EMAIL_NS: "muc#roomconfig_email", + CHANGE_SUBJ_NS: "muc#roomconfig_changesubject", + ALLOW_INVITES_NS: "muc#roomconfig_allowinvites", + ROOM_PWD_PROTECT_NS: "muc#roomconfig_passwordprotectedroom", + ROOM_PWD_NS: "muc#roomconfig_roomsecret", + CUSTOM_ERR_CODES: { + ROOM_CREATION_FAILED_NAME_CONFLICT:402, + ROOM_CREATION_FAILED: 400, + SUCCESS:200, + ROOM_DESTROY_FAILED:400 + }, + AFFILIATIONS : { + OWNER: "owner", + ADMIN: "admin", + MEMBER: "member", + OUTCAST: "outcast", + NONE: "none" + }, + ROLES : { + MODERATOR: "moderator", + NONE: "none", + PARTICIPANT: "participant", + VISITOR: "visitor" + } +} + +dojo.declare("dojox.xmpp.MUCManager",'',{ + xmppSession : null, + mucService : "", //identitiy of mucService + + constructor: function(/* xmppSession */ session){ + this.xmppSession = session; + }, + + _setMUCService : function(service){ + // summary: + // Sets the identity of the muc-service. + // Called by xmppSession after it discovers + // the capabilities of the server + // + // service: + // string - id of the muc component + // + if(!this.mucService && service){ + this.mucService = service; + //console.log("setting MUC service jid as: "+this.mucService); + } + }, + + _setXMPPSession : function(session){ + this.xmppSession = session; + }, + + getMUCInstance : function(room){ + if(this.xmppSession == null){ + console.error("MUCManager::getMUCInstance()::No xmppSession set"); + return; + } + var instances = this.xmppSession.chatRegister; + for(var i=0; i"); + var deferred = new dojo.Deferred(); + var def = this.xmppSession.dispatchPacket(req,"iq", req.id); + def.addCallback(this, function(msg){ + var nick = ""; + if ((msg.getAttribute('type')=='result') && msg.hasChildNodes()){ + var qnode = msg.getElementsByTagName('query')[0]; + for (var i=0;i"); + var def = this.xmppSession.dispatchPacket(req.toString(),"iq",props.id); + def.addCallback(this, function(msg){ + if ((msg.getAttribute('type')=='result') && msg.hasChildNodes()){ + var qnode = msg.getElementsByTagName('query')[0]; + var itemNode, roomInstance; + for (var i=0;i= 0) this.xmppSession.chatRegister.splice(ind,1); + }, + + _handlePresence : function(msg){ + if(this.xmppSession == null){ + console.error("MUCManager::handlePresence()::No xmppSession set"); + return; + } + var room = this.xmppSession.getBareJid(msg.getAttribute('from')); + var mucInstance = this.getMUCInstance(room); + // response from createRoom presence request + if(!mucInstance && this.roomInCreateMode){ + mucInstance = this.roomInCreateMode; + mucInstance._initRoster(msg); + mucInstance._handleCreateRoom(msg); + return; + } + if(!mucInstance){ + //console.log("MUCManager::handlePresence()::No MUCService instance present for "+room); + return; + } + if(!mucInstance.rosterRetrieved){ + mucInstance._initRoster(msg); + }else { + mucInstance._updateRoster(msg); + } + }, + + declineInvitation: function(invitation){ + // summary: + // Sends a message stanza to decline an invitation + // + // room: + // the dojox.xmpp.MUCInvitation object + // + // returns: + // + if(!invitation.from || !invitation.room){ + console.error("MUCManager::declineInvitation()::Invalid invitation object"); + } + var to = invitation.from; + var room = invitation.room; + var reason = invitation.reason; + + var m = { + to:room, + from:this.xmppSession.jid + "/" + this.xmppSession.resource + }; + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("message", m, false)); + req.append(dojox.xmpp.util.createElement("x",{ + xmlns:dojox.xmpp.muc.MUC_USER_NS + },false)); + req.append(dojox.xmpp.util.createElement("decline",{ + to:to + },false)); + req.append(dojox.xmpp.util.createElement("reason",{},false)); + req.append(dojox.xmpp.util.stripHtml(reason)); + req.append(""); + req.append(""); + var def = this.xmppSession.dispatchPacket(req.toString()); + }, + + _handleMUCSimpleMessage : function(msg){ + var room = this.xmppSession.getBareJid(msg.getAttribute('from')); + if(msg.getElementsByTagName("invite")[0]){ + this._processInvite(msg); + }else if(msg.getElementsByTagName("decline")[0]){ + this._processDecline(msg); + } + }, + + _processInvite : function(msg){ + var room = msg.getAttribute('from'); + var inNode = msg.getElementsByTagName("invite")[0]; + if(!inNode || !inNode.getAttribute('from')) return; + var from = this.xmppSession.getBareJid(inNode.getAttribute('from')); + var rNode = inNode.getElementsByTagName("reason")[0]; + var reason = ""; + if(rNode) reason = rNode.textContent; + var invite = new dojox.xmpp.MUCInvitation(); + invite.from = from; + invite.room = room; + invite.reason = reason; + this.onInvitation(invite); + }, + + _processDecline : function(msg){ + var room = msg.getAttribute('from'); + var inNode = msg.getElementsByTagName("decline")[0]; + if(!inNode || !inNode.getAttribute('from')) return; + var from = this.xmppSession.getBareJid(inNode.getAttribute('from')); + var rNode = inNode.getElementsByTagName("reason")[0]; + var reason = ""; + if(rNode) reason = rNode.textContent; + var invite = new dojox.xmpp.MUCInvitation(); + invite.from = from; + invite.room = room; + invite.reason = reason; + var instance = this.getMUCInstance(room); + if(instance){ + instance.onInvitationDeclined(invite); + } + }, + + createRoom : function(nickname, roomCfg){ + // summary: + // Creates a reserved room as specified in section 10.1.3 of xep-0045 + // Outline of steps to create a room: + // 1. Check if the room exists + // 2. Send presence to the room + // 3. Send IQ with MUC owner NS + // 4. Fill the configuration form and send to server + // If, at any point there is a failure or in case of success + // an event is fired to mark the end of creation process + // Response code is set in the roomCfg object + // + // nickname: + // Nickname to be used when creating this room + // mucService: + // dojox.xmpp.MUCService object for this room + // roomCfg: + // dojox.xmpp.MUCRoomConfig object for this room + // + // associated event: + // this.onCreateRoom + // + // returns: + // void + var roomId = roomCfg.roomId; + var admSrvc = new dojox.xmpp._MUCAdminService(this.xmppSession); + var mucService = new dojox.xmpp.MUCService(roomId, nickname); + mucService.setSession(this.xmppSession); + admSrvc.setMUCManager(this); + this.roomInCreateMode = mucService; + var def = admSrvc.createRoom(mucService, roomCfg); + def.addBoth(this, "_onCreateRoom"); + return def; + }, + + createInstantRoom : function(nickname, roomCfg){ + // summary: + // Creates an instant room as specified in section 10.1.2 + // of XEP-0045. Requests for a unique id from the server + // and uses this as the name of the room. + // nickname: + // Nickname to be used when creating this room + // mucService: + // dojox.xmpp.MUCService object for this room + // roomCfg: + // dojox.xmpp.MUCRoomConfig object for this room + // + // associated event: + // this.onCreateRoom + // + // returns: + // void + var roomId = roomCfg.roomId; + var mucService = new dojox.xmpp.MUCService(roomId, nickname); + mucService.setSession(this.xmppSession); + var admSrvc = new dojox.xmpp._MUCAdminService(this.xmppSession); + admSrvc.setMUCManager(this); + this.roomInCreateMode = mucService; + var def = admSrvc.createInstantRoom(mucService, roomCfg); + def.addBoth(this, "_onCreateRoom"); + return def; + }, + + getUniqueName: function() { + // summary: + // Sends iq stanza to request for a unique name as specified in + // section 10.1.4 of xep-0045 + // + // returns: + // deferred + var props={ + id: this.xmppSession.getNextIqId(), + from: this.xmppSession.jid + '/' + this.xmppSession.resource, + type: "get", + to: this.mucService + } + var xmlns = "http://jabber.org/protocol/muc#unique"; + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("iq",props,false)); + req.append(dojox.xmpp.util.createElement("unique",{ + xmlns:xmlns + },true)); + req.append(""); + var deferred = new dojo.Deferred(); + var def = this.xmppSession.dispatchPacket(req.toString(),"iq",props.id); + def.addCallback(this, function(msg){ + var uniqueId = ""; + if(msg.getAttribute('type') == 'result'){ + uniqueId = msg.getElementsByTagName('unique')[0].firstChild.nodeValue; + deferred.callback(uniqueId); + }else { + deferred.errback(); + } + }); + return deferred; + }, + + destroyRoom : function(room /*the fqdn of room*/, reason /* why? */) { + // summary: + // Sends iq stanza to destroy a room + // + // room: + // the fqdn of the room + // reason: + // optional string - reason to destroy this room + // + // + // returns: + // deferred + var admSrvc = new dojox.xmpp._MUCAdminService(this.xmppSession); + admSrvc.setMUCManager(this); + return admSrvc.destroyRoom(room, reason); + }, + + bookmarkRooms : function(rooms) { + // summary: + // Use XML storage to store the given list of rooms. + // The rooms are stored under the "storage:bookmarks" namespace + // rooms: + // Array of dojox.xmpp.MUCRoom objects + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("storage",{xmlns:"storage:bookmarks"},false)); + var room; + for(var i=0;i"); + } + req.append(""); + } + req.append(""); + var deferred = new dojo.Deferred(); + var def = this.xmppSession.storeXML(req); + def.addCallback(this, function(msg){ + if(msg.getAttribute('type') == 'result'){ + deferred.callback(); + }else { + deferred.errback(); + } + }); + return deferred; + }, + + retrieveBookmarks : function() { + // summary: + // Retrieve list of rooms stored under the "storage:bookmarks" namespace + // + // returns: + // deferred + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("storage",{xmlns:"storage:bookmarks"},true)); + var deferred = new dojo.Deferred(); + var def = this.xmppSession.retrieveXML(req); + def.addCallback(this, function(msg){ + if(msg.getAttribute('type') == 'error'){ + deferred.errback(); + }else { + var confNodes = msg.getElementsByTagName('conference'); + var conf; + var room, rooms = []; + for(var i=0;i= 0){ + return true; + } + } + } + return false; + }, + + onRetrieveRooms : function(mucRooms){}, + + onRegisterChatInstance : function(instance, message){ + //console.log("MUCManager::onRegisterChatInstance: for instance "+instance.uid); + }, + + onInvitation : function(/* dojox.xmpp.MUCInvitation object */invitation){}, /* somebody sent me an invitation */ + + _onCreateRoom : function() { /* Room Created */ + //console.log("Room creation complete "+roomCfg.roomId); + this.roomInCreateMode = null; + } +}); \ No newline at end of file Index: dojo-release-1.3.2-src/dojox/xmpp/MUCService.js =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ dojo-release-1.3.2-src/dojox/xmpp/MUCService.js 2010-01-28 18:27:40.000000000 +0530 @@ -0,0 +1,408 @@ +dojo.provide("dojox.xmpp.MUCService"); + +dojox.xmpp.MUCOccupant = function(){ + this.nickName = "", + this.affiliation = "", + this.role = "", + this.jid = "", + this.presence = { + show:"", + type:"", + status: "" + } +} + +dojox.xmpp.MUCRoom = function(){ + this.jid = "", + this.name = "", + this.nick = "" +} + +dojox.xmpp.MUCInvitation = function(){ + this.from = ""; + this.room = ""; + this.reason = ""; +} + +dojo.declare("dojox.xmpp.MUCService",[dojox.xmpp.ChatService],{ + + nickname : "", + rosterRetrieved: false /* flag to mark retrieval of room roster */, + roster: [] /* associative array of Occupant objects identified by nickname */, + self: null, + _hasJoined: false, /* has the joinRoom been called? */ + + constructor: function(room, nick){ + // summary: + // creates a new instance of MUCService for this room + // and uses the provided nickname + // + // room: + // fqdn of the room + // nick: + // nickname for this room + this.uid = room; + this.nickname = nick; + this.roster = []; + //console.log("MUCService instance for "+ room +" created with nick "+nick); + }, + + joinRoom: function(extraAttrs){ + // summary: + // sends a presence stanza to join this room + // + // extraAttrs: + // Extra information to be sent as part of the presence stanza + // They are + // history: + // name:as specified in section 7.1.16 - Managing Discussion History + // of http://xmpp.org/extensions/xep-0045.html + // value: datatype as specified in the section above + // For eg: history: { maxchars: "0" } + // + + var room = this.uid; + if(this._hasJoined){ // already joined, do not fire again + this.onInitRoster(this.roster); // fire event for continuity + return; + } + this.roster = []; + this.rosterRetrieved = false; + if(!room || room==''){ + throw new Error("MUCService::joinRoom()::room is NULL"); + } + + var p = { + to:room + "/" + this.nickname, + from:this.session.jid + '/' + this.session.resource + } + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("presence",p,false)); + req.append(dojox.xmpp.util.createElement("x",{ + xmlns:dojox.xmpp.muc.MUC_NS + },false)); + if(extraAttrs && extraAttrs.history){ + req.append(dojox.xmpp.util.createElement("history", extraAttrs.history ,true)); + } + req.append("") + req.append(""); + var def = this.session.dispatchPacket(req.toString()); + this._hasJoined = true; + }, + + leaveRoom: function(){ + + var room = this.uid; + var p = { + to:room + "/" + this.nickname, + from:this.session.jid + '/' + this.session.resource, + type: "unavailable" + } + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("presence",p,false)); + req.append(dojox.xmpp.util.createElement("x",{ + xmlns:dojox.xmpp.muc.MUC_NS + },true)); + req.append(""); + var def = this.session.dispatchPacket(req.toString()); + }, + + invite: function(/* userid or array of userids */to, /*Reason*/message){ + // summary: + // send an iq stanza to this room to invite the + // given user with the provided message + // + // to: + // fully qualified userid or array of userids of the invitee + // message: + // String - message to be sent as part of the invitation + var m = { + to:this.uid, + from:this.session.jid + "/" + this.session.resource + }; + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("message", m, false)); + req.append(dojox.xmpp.util.createElement("x",{ + xmlns:dojox.xmpp.muc.MUC_USER_NS + },false)); + if(dojo.isArray(to)){ + dojo.forEach(to, function(item){ + req.append(dojox.xmpp.util.createElement("invite",{ + to:item + },false)); + req.append(dojox.xmpp.util.createElement("reason",{},false)); + req.append(dojox.xmpp.util.stripHtml(message)); + req.append(""); + }); + }else { + req.append(dojox.xmpp.util.createElement("invite",{ + to:to + },false)); + req.append(dojox.xmpp.util.createElement("reason",{},false)); + req.append(dojox.xmpp.util.stripHtml(message)); + req.append(""); + } + req.append(""); + req.append(""); + var def = this.session.dispatchPacket(req.toString()); + }, + + removeFromRoster: function(occ){ + if(!this.roster || !occ) return; + delete this.roster[occ.nickName]; + }, + + setNickName: function(nick){ + this.nickname = nick; + }, + + _initRoster: function(msg){ + // summary: + // Called to create the room-roster when + // a user joins a conference. A flag is set to + // mark the completion of init. All further + // updated are handled by updateRoster + // + // msg: + // the xmpp response + if(this.rosterRetrieved){ + console.error("MUCService::initRoster::Illegal state: initRoster called after initialization for room "+this.uid); + return; + } + if(msg.getAttribute('type') == 'error'){ + this._processPresenceError(msg); + return; + } + var roomjid = msg.getAttribute('from'); + var occNN = this.session.getResourceFromJid(roomjid);//occupant nick-name; + //console.log("MUCService for room "+this.uid+"::initRoster::Processing presence for: "+occNN); + this.roster[occNN] = this._processPresenceParams(msg); + if(this._contains(this._getStatusCode(msg), '110') || this.nickname == occNN){ // presence of self - marking end of room roster + this.self = this.roster[occNN]; + if(!this.rosterRetrieved){ + this.onInitRoster(this.roster); + this.rosterRetrieved = true; + } + } + if(this._contains(this._getStatusCode(msg), '210')){ // presence of self - nick rewritten by service + this.nickname = occNN; + this.onMyNickNameModified(occNN); + } + }, + + _updateRoster: function(msg){ + // summary: + // Handle presence updates to this room + // after initializing the roster + // + // msg: + // the xmpp response + if(msg.getAttribute('type') == 'error'){ + this._processPresenceError(msg); + return; + } + var roomjid = msg.getAttribute('from'); + var occNN = this.session.getResourceFromJid(roomjid); + var origOcc = this.roster[occNN]; + var occ; + if(origOcc){ + occ = dojo.clone(origOcc); + } + //console.log("MUCService for room "+this.uid+"::updateRoster::Processing presence for: "+occNN); + this.roster[occNN] = this._processPresenceParams(msg, occ);; + if(occNN == this.nickname){ // self presence changed + this.self = this.roster[occNN]; + if(this._contains(this._getStatusCode(msg), '307')){ // I am kicked! + this.onKicked(); + return; + } + if(this._contains(this._getStatusCode(msg), '301')){ // I am banned! + this.onBanned(); + return; + } + this.onMyPresenceChanged(); + return; + } + if(this._contains(this._getStatusCode(msg), '303')){// occupant's nick-name changed + this.onNickNameChanged(occNN); + return; + } + this.onUpdateRoster(/*nickname*/occNN, /*original state*/origOcc); + }, + + getSelfOccupancy: function(){ + // summary: + // Returns the Occupant object for self + // + return this.self; + }, + + hasVoice: function() { + // summary: + // Determines if self can speak or not in this room + // + if(!this.self) return false; + var role = this.self.role; + if(role == dojox.xmpp.muc.ROLES.NONE || role == dojox.xmpp.muc.ROLES.VISITOR) + return false; + return true; + }, + + isOwner: function() { + // summary: + // Determines if self is the owner of this room + // + if(!this.self) return false; + var affiliation = this.self.affiliation; + if(affiliation == dojox.xmpp.muc.AFFILIATIONS.OWNER) + return true; + return false; + }, + + kick: function(nickname, reason) { + // summary: + // Kicks an occupant out of this room + // + // nickname: + // string nickname of the occupant to be kicked + // reason: + // optional string reason to kick the occupant + var props={ + id: this.session.getNextIqId(), + from: this.session.jid + '/' + this.session.resource, + type: "set", + to: this.uid + } + var props1 = { + xmlns:dojox.xmpp.muc.MUC_ADMIN_NS + } + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("iq",props,false)); + req.append(new dojox.string.Builder(dojox.xmpp.util.createElement("query",props1,false))); + req.append(new dojox.string.Builder(dojox.xmpp.util.createElement("item",{nick:nickname, role:dojox.xmpp.muc.ROLES.NONE}, false))); + if(reason){ + req.append(""); + req.append(dojox.xmpp.util.stripHtml(reason)); + req.append(""); + } + req.append(""); + var deferred = new dojo.Deferred(); + var def = this.session.dispatchPacket(req.toString(),"iq",props.id); + def.addCallback(this, function(msg){ + if(msg.getAttribute('type') == 'result'){ + deferred.callback(); + }else { + deferred.errback(); + } + }); + return deferred; + }, + _handleCreateRoom: function(msg){ + // summary: + // Called by MUCManager.createRoom + // as part of the call to create a room + // msg: + // the xmpp response + if(msg.getAttribute('type') == 'error'){ + this._processPresenceError(msg); + return; + }else { + this.onRoomCreate(this.uid); + } + }, + + _processPresenceParams: function(msg, occ){ + var roomjid = msg.getAttribute('from'); + var occNN = this.session.getResourceFromJid(roomjid); + if(!occ) + occ = new dojox.xmpp.MUCOccupant(); + occ.nickName = occNN; + if(msg.getAttribute('type')) occ.presence.type = msg.getAttribute('type'); + for (var i=0; i"); + var def = this.xmppSession.dispatchPacket(req.toString(),"iq",props.id); + return def; + }, + + + createRoom: function(mucService, roomCfg){ + // summary: + // Creates a reserved room as specified in section 10.1.3 of xep-0045 + // Outline of steps to create a room: + // 1. Check if the room exists + // 2. Send presence to the room + // 3. Send IQ with MUC owner NS + // 4. Fill the configuration form and send to server + // If, at any point there is a failure or in case of success + // an event is fired to mark the end of creation process + // Response code is set in the roomCfg object + // + // nickname: + // Nickname to be used when creating this room + // mucService: + // dojox.xmpp.MUCService object for this room + // roomCfg: + // dojox.xmpp.MUCRoomConfig object for this room + // + // associated event: + // MUCManager.onCreateRoom + // + // returns: + // void + var def = this.isRoomPresent(roomCfg); + var self = this; + var connects = []; + var deferred = new dojo.Deferred(); + def.addCallback(this, function(msg){ + var isPresent; + // if room-name is not present continue with room creation steps + if(msg.getAttribute('type') == 'error'){ + isPresent = false; + }else { + isPresent = true; + } + if(!isPresent){ + var c1 = dojo.connect(mucService,"onRoomCreate", function(){ + // first step successful.... continue + var props={ + id: self.xmppSession.getNextIqId(), + from: self.xmppSession.jid + '/' + self.xmppSession.resource, + type: "get", + to: roomCfg.roomId + } + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("iq",props,false)); + req.append(dojox.xmpp.util.createElement("query",{ + xmlns:dojox.xmpp.muc.MUC_OWNER_NS + },true)); + req.append(""); + self._doDisconnects(connects); + var def = self.xmppSession.dispatchPacket(req.toString(),"iq",props.id); + def.addCallback(self, function(msg){ + var def1 = self._processCreateConfig(msg, roomCfg); + def1.addCallback(function(roomCfg){ + var resp = {}; + resp.mucService = mucService; + resp.roomCfg = roomCfg; + deferred.callback(resp); + }).addErrback(function(errCode){ + deferred.errback(errCode); + }); + }); + }); + connects.push(c1); + var c2 = dojo.connect(mucService,"_processPresenceError", function(msg){ + // failed, notify end of creation + roomCfg.responseCode = dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED; + self._doDisconnects(connects); + //self.mgr.onCreateRoom(roomCfg); + deferred.errback(dojox.xmpp.muc.CUSTOM_ERR_CODES.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED); + }); + connects.push(c2); + // send presence to the new room-name to start room creation + mucService.joinRoom(); + }else { + // notify that creation process is over + roomCfg.responseCode = dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED_NAME_CONFLICT; + //this.mgr.onCreateRoom(roomCfg); + deferred.errback(dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED_NAME_CONFLICT); + } + }); + return deferred; + }, + + createInstantRoom: function(mucService, roomCfg){ + // summary: + // Creates an instant room as specified in section 10.1.2 + // of XEP-0045. Requests for a unique id from the server + // and uses this as the name of the room. + // nickname: + // Nickname to be used when creating this room + // mucService: + // dojox.xmpp.MUCService object for this room + // roomCfg: + // dojox.xmpp.MUCRoomConfig object for this room + // + // associated event: + // MUCManager.onCreateRoom + // + // returns: + // void + var roomId = roomCfg.roomId; + var self = this; + var connects = []; + var deferred = new dojo.Deferred(); + var c1 = dojo.connect(mucService,"onRoomCreate", function(){ + // first step successful.... continue + var props={ + id: self.xmppSession.getNextIqId(), + from: self.xmppSession.jid + '/' + self.xmppSession.resource, + type: "set", + to: roomId + } + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("iq",props,false)); + req.append(dojox.xmpp.util.createElement("query",{ + xmlns:dojox.xmpp.muc.MUC_OWNER_NS + },false)); + req.append(dojox.xmpp.util.createElement("x",{ + xmlns:"jabber:x:data", type:"submit" + },true)); + req.append(""); + req.append(""); + var def = self.xmppSession.dispatchPacket(req.toString(),"iq",props.id); + def.addCallback(self, function(msg){ + if(msg.getAttribute('type') == 'result'){ + var resp = {}; + resp.mucService = mucService; + resp.roomCfg = roomCfg; + deferred.callback(resp); + }else { + //roomCfg.responseCode = dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED; + deferred.errback(dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED); + } + self._doDisconnects(connects); + //this.mgr.onCreateRoom(roomCfg, mucService); + }); + }); + connects.push(c1); + var c2 = dojo.connect(mucService,"_processPresenceError", function(msg){ + // failed, notify end of creation + //roomCfg.responseCode = dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED; + self._doDisconnects(connects); + deferred.errback(dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED); + //self.mgr.onCreateRoom(roomCfg); + }); + connects.push(c2); + // send presence to the new room-name to start room creation + mucService.joinRoom(); + return deferred; + }, + + destroyRoom: function(room/* roomname */, reason /* message */){ + var props={ + id: this.xmppSession.getNextIqId(), + from: this.xmppSession.jid + '/' + this.xmppSession.resource, + type: "set", + to: room + } + var props1 = { + xmlns:dojox.xmpp.muc.MUC_OWNER_NS + } + var req = new dojox.string.Builder(dojox.xmpp.util.createElement("iq",props,false)); + req.append(new dojox.string.Builder(dojox.xmpp.util.createElement("query",props1,false))); + req.append(new dojox.string.Builder(dojox.xmpp.util.createElement("destroy",{jid:props.from},false))); + if(reason){ + req.append(dojox.xmpp.util.createElement("reason",{},false)); + req.append(dojox.xmpp.util.stripHtml(reason)); + req.append(""); + } + req.append(""); + var deferred = new dojo.Deferred(); + var def = this.xmppSession.dispatchPacket(req.toString(),"iq",props.id); + def.addCallback(this, function(msg){ + if(msg.getAttribute('type') == 'result'){ + deferred.callback(room); + }else { + deferred.errback(); + } + }); + return deferred; + + }, + + _processCreateConfig: function(msg, roomCfg){ + // process the create-room config here + // fill the form and send to server + this._setValueIntoCfgForm(dojox.xmpp.muc.ROOM_NAME_NS, roomCfg.roomName); + this._setValueIntoCfgForm(dojox.xmpp.muc.ROOM_DESC_NS, roomCfg.roomDesc); + this._setValueIntoCfgForm(dojox.xmpp.muc.PERSIST_ROOM_NS, roomCfg.isPersistent); + this._setValueIntoCfgForm(dojox.xmpp.muc.MOD_ROOM_NS, roomCfg.isModerated); + this._setValueIntoCfgForm(dojox.xmpp.muc.MEMBERS_ONLY_NS, roomCfg.isMembersOnly); + this._setValueIntoCfgForm(dojox.xmpp.muc.PUB_ROOM_NS, roomCfg.isPublic); + this._setValueIntoCfgForm(dojox.xmpp.muc.DIST_ROOM_NS, roomCfg.isDistributed); + this._setValueIntoCfgForm(dojox.xmpp.muc.ROOM_EMAIL_NS, roomCfg.email); + this._setValueIntoCfgForm(dojox.xmpp.muc.CHANGE_SUBJ_NS, roomCfg.allowChangeSubject); + this._setValueIntoCfgForm(dojox.xmpp.muc.ROOM_LANG_NS, roomCfg.lang); + this._setValueIntoCfgForm(dojox.xmpp.muc.ALLOW_INVITES_NS, roomCfg.allowInvites); + this._setValueIntoCfgForm(dojox.xmpp.muc.ROOM_PWD_PROTECT_NS, roomCfg.isPasswordProtected); + this._setValueIntoCfgForm(dojox.xmpp.muc.ROOM_PWD_NS, roomCfg.password); + + var deferred = new dojo.Deferred(); + + var xNodes = msg.getElementsByTagName('x'); + var xNode = null; + for(var i=0; i"); + req.append(queryN); + req.append(""); + var def = this.xmppSession.dispatchPacket(req,"iq",props.id); + // reset the map + this.cfgVals = []; + def.addCallback(this, function(resp){ + if(resp.getAttribute('type') == 'result'){ + //roomCfg.responseCode = dojox.xmpp.muc.CUSTOM_ERR_CODES.SUCCESS; + deferred.callback(roomCfg); + }else { + //roomCfg.responseCode = dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED; + deferred.errback(dojox.xmpp.muc.CUSTOM_ERR_CODES.ROOM_CREATION_FAILED); + } + //this.mgr.onCreateRoom(roomCfg); + }); + return deferred; + }, + + _setValueIntoCfgForm: function(/* namespaceId */nsId, /* value to set */val){ + if(val === false) val = "0"; + if(val === true) val = "1"; + if(!val) return; + this.cfgVals[nsId] = val; + }, + + _setValueIntoCfgForm1: function(/* The config form */xNode){ + + var node, val; + //var xNode = dojo.query("x[xmlns='jabber:x:data']", msg)[0]; + for(var i=0;i - - - xmpp test page - - - - - + + + - - - - - - - - - -
-
- XMPP Test Client -
- Status: - -
-
- -
- -
-
-
-
-
- + if(action == 'Join'){ + joinMUC(room); + } else if(action == 'Leave'){ + leaveRoom(room, true); + } else if(action == 'Invite'){ + inviteToMUC(room); + } else if(action == 'Destroy'){ + destroyRoom(room); + } + } + + function kick(nick, room){ + var mucInst = mucManager.getMUCInstance(room); + var def = mucInst.kick(nick); + def.addCallback(function(){ + console.log("successfully kicked user "+nick); + }).errback(function(){ + console.log("failed to kicked user "); + }); + //this.session.kick(nick); + } + + function isMUCEnabled(){ + if(!mucManager) dijit.byId("confDialog").show(); + else dijit.byId("confDialog").hide(); + return (mucManager != null) + } + + + + + + + + + + +
+
+
+
+
+ XMPP Test Client +
+ Status: + +
+
+ +
+ +
+
+
+
+
+
+ +
+
+ XEP-124 Test MUC Client + + + +
+ Create +
+
+ Instant Room +
+
+ Reserved Room +
+
+
+
+
+
+ +
+
+
+ + Index: dojo-release-1.3.2-src/dojox/xmpp/widget/MUCSession.js =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ dojo-release-1.3.2-src/dojox/xmpp/widget/MUCSession.js 2010-01-04 11:09:32.000000000 +0530 @@ -0,0 +1,104 @@ +dojo.provide("dojox.xmpp.widget.MUCSession"); + +dojo.require("dojo.date"); +dojo.require("dojo.date.locale"); +dojo.require("dojo.date.stamp"); + +dojo.declare("dojox.xmpp.widget.MUCSession", + [dijit.layout.LayoutContainer, dijit._Templated], + { + templatePath: dojo.moduleUrl("dojox.xmpp.widget", "templates/MUCSession.html"), + enableSubWidgets: true, + widgetsInTemplate: true, + + widgetType: "MUCSession", + room: null, + instance: null, + session: null, //xmppSession + mucManager: null, + titlePane: null, + connects : [], + + postCreate: function(){ + + }, + + joinRoom: function() { + this.instance.joinRoom(); + }, + + displayMessage: function(message) { + //console.log("displayMessage", this, message); + if(message) { + var nick = this.session.getResourceFromJid(message.from); + var name = (nick == this.instance.nickname) ? "me" : nick; + var delay=""; + if(message.delay) + delay = "("+dojo.date.locale.format(dojo.date.stamp.fromISOString(message.delay))+")"; + this.messages.domNode.innerHTML += "" + name + delay +": " + message.body + "
"; + this.goToLastMessage(); + } + + }, + + goToLastMessage: function() { + this.messages.domNode.scrollTop = this.messages.domNode.scrollHeight; + }, + + onKeyPress: function(e){ + var key = e.keyCode || e.charCode; + if ((key == dojo.keys.ENTER) && (this.chatInput.value != "")){ + this.instance.sendMessage({ + body: this.chatInput.value + }, "groupchat"); + this.chatInput.value = ""; + } + }, + + createRoster: function(roster) { + //console.log("Widget::createRoster called for "+this.room); + var rosterDiv = document.createElement("div"); + this.mucRoster.appendChild(rosterDiv); + var buffer = new dojox.string.Builder(); + var rItem; + for(var item in roster){ + rItem = roster[item]; + console.log("Roster item->"+rItem.nickName); + buffer.append('
'+ + '
'+ + '
'+rItem.nickName+'
'); + if(this.instance.isOwner() && rItem != this.instance.getSelfOccupancy()){ + buffer.append('
'); + } + buffer.append('
'); + } + rosterDiv.innerHTML = buffer.toString(); + this.titlePane = new dijit.TitlePane({ + title: "Participants" + }, rosterDiv); + }, + + updateRoster: function(nick, occupant) { + var newOcc = this.instance.roster[nick]; + if(nick == this.instance.nickname) return; + if(newOcc && newOcc.presence.type == 'unavailable'){ // occupant leaves room + dojo.query('div[imContactId="'+nick+'"]', this.mucRoster).forEach(function(buddy) { + buddy.parentNode.removeChild(buddy); + }); + this.instance.removeFromRoster(newOcc); + this.messages.domNode.innerHTML += "" + nick + " left this room
"; + }else if(occupant == null){ //when a new user joins occupant object will be null + var buffer = new dojox.string.Builder(); + buffer = buffer.append(this.titlePane.containerNode.innerHTML); + buffer.append('
'+ + '
'+ + '
'+newOcc.nickName+'
'); + if(this.instance.isOwner()){ + buffer.append('
'); + } + buffer.append('
'); + this.titlePane.containerNode.innerHTML = buffer.toString(); + this.messages.domNode.innerHTML += "" + newOcc.nickName + " joined this room
"; + } + } + }); \ No newline at end of file Index: dojo-release-1.3.2-src/dojox/xmpp/widget/templates/ChatSession.html =================================================================== --- dojo-release-1.3.2-src.orig/dojox/xmpp/widget/templates/ChatSession.html 2010-01-04 11:09:30.000000000 +0530 +++ dojo-release-1.3.2-src/dojox/xmpp/widget/templates/ChatSession.html 2010-01-04 11:09:32.000000000 +0530 @@ -1,5 +1,5 @@
-
+
-
+
\ No newline at end of file Index: dojo-release-1.3.2-src/dojox/xmpp/widget/templates/MUCSession.html =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ dojo-release-1.3.2-src/dojox/xmpp/widget/templates/MUCSession.html 2010-01-04 11:09:32.000000000 +0530 @@ -0,0 +1,13 @@ +
+
+
+
+
+
+ +
+
+ +
+ +
\ No newline at end of file Index: dojo-release-1.3.2-src/dojox/xmpp/xmppSession.js =================================================================== --- dojo-release-1.3.2-src.orig/dojox/xmpp/xmppSession.js 2010-01-04 11:09:30.000000000 +0530 +++ dojo-release-1.3.2-src/dojox/xmpp/xmppSession.js 2010-01-28 18:23:04.000000000 +0530 @@ -5,6 +5,7 @@ dojo.require("dojox.xmpp.PresenceService"); dojo.require("dojox.xmpp.UserService"); dojo.require("dojox.xmpp.ChatService"); +dojo.require("dojox.xmpp.MUCManager"); dojo.require("dojox.xmpp.sasl"); dojox.xmpp.xmpp = { @@ -14,7 +15,8 @@ SASL_NS: 'urn:ietf:params:xml:ns:xmpp-sasl', BIND_NS: 'urn:ietf:params:xml:ns:xmpp-bind', BODY_NS: "http://jabber.org/protocol/httpbind", - + MUC_NS: "http://jabber.org/protocol/muc", + XHTML_BODY_NS: "http://www.w3.org/1999/xhtml", XHTML_IM_NS: "http://jabber.org/protocol/xhtml-im", @@ -23,6 +25,8 @@ ACTIVE: "Active", TERMINATE: "Terminate", LOGIN_FAILURE: "LoginFailure", + DELAY_NS:"urn:xmpp:delay", + IQ_PRIVATE_NS: "jabber:iq:private", INVALID_ID: -1, NO_ID: 0, @@ -52,7 +56,7 @@ dojo.mixin(this, props); } - this.session = new dojox.xmpp.TransportSession(props); + this.session = new dojox.xmpp.TransportSession(props); dojo.connect(this.session, "onReady", this, "onTransportReady"); dojo.connect(this.session, "onTerminate", this, "onTransportTerminate"); dojo.connect(this.session, "onProcessProtocolResponse", this, "processProtocolResponse"); @@ -65,7 +69,8 @@ chatRegister: [], _iqId: Math.round(Math.random() * 1000000000), services: [], - + mucManager: null, + open: function(user, password, resource){ if (!user) { @@ -93,7 +98,7 @@ close: function(){ this.state = dojox.xmpp.xmpp.TERMINATE; - this.session.close(dojox.xmpp.util.createElement("presence",{type:"unavailable",xmlns:dojox.xmpp.xmpp.CLIENT_NS},true)); + this.session.close(dojox.xmpp.util.createElement("presence",{type:"unavailable",xmlns:dojox.xmpp.xmpp.CLIENT_NS},true)); }, processProtocolResponse: function(msg){ @@ -114,23 +119,24 @@ //console.log("default action?", msg.getAttribute('xmlns')); if(msg.getAttribute('xmlns')==dojox.xmpp.xmpp.SASL_NS){ this.saslHandler(msg); - } + } } }, - //HANDLERS + //HANDLERS messageHandler: function(msg){ //console.log("xmppSession::messageHandler() ",msg); switch(msg.getAttribute('type')){ + case "groupchat": case "chat": this.chatHandler(msg); break; case "normal": default: - this.simpleMessageHandler(msg); + this.simpleMessageHandler(msg); } - + }, iqHandler: function(msg){ @@ -146,6 +152,10 @@ presenceHandler: function(msg){ //console.log("xmppSession::presenceHandler()"); + if(this.mucManager && this.mucManager._isMUC(msg)){ // if its from a MUC service, delegate and return + this.mucManager._handlePresence(msg); + return; + } switch(msg.getAttribute('type')){ case 'subscribe': //console.log("PresenceHandler: ", msg.getAttribute('from')); @@ -154,7 +164,7 @@ case 'subscribed': case 'unsubscribed': break; - case 'error': + case 'error': this.processXmppError(msg); //console.log("xmppService::presenceHandler() Error"); break; @@ -185,13 +195,13 @@ hasBindFeature = true; // } break; - } + } } } //console.log("Has connected/bind?", this.state, hasBindFeature, authMechanisms); if (this.state == dojox.xmpp.xmpp.CONNECTED && hasBindFeature){ for(var i=0; i-1 && chatState){ var chat = this.chatRegister[found]; @@ -293,10 +307,10 @@ if (chat.firstMessage){ if (chatState == dojox.xmpp.chat.ACTIVE_STATE) { chat.useChatState = (chatState != null) ? true : false; - chat.firstMessage = false; + chat.firstMessage = false; } } - } + } if ((!message.body || message.body=="") && !message.xhtml) {return;} @@ -317,6 +331,9 @@ simpleMessageHandler: function(msg){ //console.log("xmppSession::simpleMessageHandler() ", msg); + if(this.mucManager && this.mucManager._isMUC(msg)){ + this.mucManager._handleMUCSimpleMessage(msg); + } }, registerChatInstance: function(chatInstance, message){ @@ -325,7 +342,14 @@ this.onRegisterChatInstance(chatInstance, message); chatInstance.recieveMessage(message,true); }, - + + registerMUCInstance: function(mucInstance, message){ + mucInstance.setSession(this); + this.chatRegister.push(mucInstance); + this.mucManager.onRegisterChatInstance(mucInstance, message); + mucInstance.recieveMessage(message,true); + }, + iqSetHandler: function(msg){ if (msg.hasChildNodes()){ var fn = msg.firstChild; @@ -333,7 +357,7 @@ case 'query': if(fn.getAttribute('xmlns') == "jabber:iq:roster"){ this.rosterSetHandler(fn); - this.sendIqResult(msg.getAttribute('id'), msg.getAttribute('from')); + this.sendIqResult(msg.getAttribute('id'), msg.getAttribute('from')); } break; default: @@ -357,7 +381,7 @@ //console.log("xmppSession::rosterSetHandler()", arguments); for (var i=0; i 0 || re.name @@ -635,7 +659,7 @@ if (this.state != state){ if (this["on"+state]){ this["on"+state](state, this.state, message); - } + } this.state=state; } }, @@ -682,19 +706,22 @@ request.append(""); var def = this.dispatchPacket(request.toString(),"iq",req.id); def.addCallback(this, function(info) { - if ((info.getAttribute('type')=='result')&&(info.hasChildNodes())){ - var query = info.getElementsByTagName('query')[0]; - if (query.getAttribute('xmlns')=="http://jabber.org/protocol/disco#info"){ - for (var i=0;i"); + request.append(""); + var def = this.dispatchPacket(request.toString(),"iq",req.id); + return def; + }, + + /** + * Implementation of retrieve part of XEP-0049 (Private XML Storage) + */ + retrieveXML: function(xmlNode){ + var req={ + id: this.getNextIqId(), + "xml:lang": this.lang, + type: 'get' + } + var request = new dojox.string.Builder(dojox.xmpp.util.createElement("iq",req,false)); + request.append(dojox.xmpp.util.createElement('query',{xmlns:dojox.xmpp.xmpp.IQ_PRIVATE_NS},false)); + request.append(xmlNode.toString()); + request.append(""); + request.append(""); + var def = this.dispatchPacket(request.toString(),"iq",req.id); + return def; + }, + // EVENTS - onLogin: function(){ - ////console.log("xmppSession::onLogin()"); + onLogin: function(){ + ////console.log("xmppSession::onLogin()"); this.retrieveRoster(); this.discoverServices(); }, @@ -747,7 +819,7 @@ onBindResource: function(msg){ //console.log("xmppSession::onBindResource() ", msg); - + if (msg.getAttribute('type')=='result'){ //console.log("xmppSession::onBindResource() Got Result Message"); if ((msg.hasChildNodes()) && (msg.firstChild.nodeName=="bind")){ @@ -763,8 +835,8 @@ }else{ //console.log("xmppService::onBindResource() No Bind Element Found"); } - - this.onLogin(); + + this.onLogin(); }else if(msg.getAttribute('type')=='error'){ //console.log("xmppSession::onBindResource() Bind Error ", msg); var err = this.processXmppError(msg); @@ -789,18 +861,18 @@ this.roster[i] = this.createRosterEntry(query.childNodes[i]); } } - } + } }else if(msg.getAttribute('type')=="error"){ - //console.log("xmppService::storeRoster() Error recieved on roster get"); + //console.log("xmppService::storeRoster() Error recieved on roster get"); } ////console.log("Roster: ", this.roster); this.setState(dojox.xmpp.xmpp.ACTIVE); this.onRosterUpdated(); - return msg; + return msg; }, - + onRosterUpdated: function() {}, onSubscriptionRequest: function(req){}, @@ -841,6 +913,8 @@ onRosterRemoved: function(ri){}, onRosterChanged: function(ri, previousCopy){}, + onMUCServiceReady: function(mucManager){} /* MUC service found, mucManager=null if not found */, + //Utilities processXmppError: function(msg){ @@ -849,7 +923,7 @@ stanzaType: msg.nodeName, id: msg.getAttribute('id') } - + for (var i=0; i