diff --git a/chat/locales/en-US/xmpp.properties b/chat/locales/en-US/xmpp.properties index 76d649b4f5..5cd285df59 100644 --- a/chat/locales/en-US/xmpp.properties +++ b/chat/locales/en-US/xmpp.properties @@ -54,6 +54,7 @@ conversation.error.creationFailedNotAllowed=Access restricted: You are not allow # is not found. # %S is the name of MUC room. conversation.error.joinFailedRemoteServerNotFound=Could not join the room %S as the server the room is hosted on could not be reached. +conversation.error.changeTopicFailedNotAuthorized=You are not authorized to set the topic of this room. # This is displayed in a conversation as an error message when the user sends # a message to a room that he is not in. # %1$S is the name of MUC room. @@ -127,3 +128,4 @@ odnoklassniki.usernameHint=Profile ID # These are the help messages for each command. command.join=join [<room@server>][/<nick>] [<password>]: Join a room, optionally providing a different nickname, or the room password. command.part=part [<message>]: Leave the current room with an optional message. +command.topic=%S [<new topic>]: Set this room's topic. diff --git a/chat/protocols/xmpp/xmpp-commands.jsm b/chat/protocols/xmpp/xmpp-commands.jsm index 05606b9c0c..2467d34359 100644 --- a/chat/protocols/xmpp/xmpp-commands.jsm +++ b/chat/protocols/xmpp/xmpp-commands.jsm @@ -62,5 +62,16 @@ var commands = [ conv.part(aMsg); return true; } + }, + { + name: "topic", + get helpString() _("command.topic", "topic"), + usageContext: Ci.imICommand.CMD_CONTEXT_CHAT, + run: function(aMsg, aConv) { + let conv = getConv(aConv); + if (!conv.left) + conv.topic = aMsg; + return true; + } } ]; diff --git a/chat/protocols/xmpp/xmpp.jsm b/chat/protocols/xmpp/xmpp.jsm index e81f2dfe96..b42e9c3f31 100644 --- a/chat/protocols/xmpp/xmpp.jsm +++ b/chat/protocols/xmpp/xmpp.jsm @@ -106,6 +106,25 @@ const XMPPMUCConversationPrototype = { _targetResource: "", + get topic() this._topic, + set topic(aTopic) { + let notAuthorized = (aError) => { + // XEP-0045 (8.1): Unauthorized subject change. + let message = _("conversation.error.changeTopicFailedNotAuthorized"); + this.writeMessage(this.name, message, {system: true, error: true}); + return true; + }; + let errorHandler = this._account.handleErrors({forbidden: notAuthorized, + notAcceptable: notAuthorized, + itemNotFound: notAuthorized}); + + // XEP-0045 (8.1): Modifying the room subject. + let subject = Stanza.node("subject", null, null, aTopic.trim()); + let s = Stanza.message(this.name, null, null,{type: "groupchat"}, subject); + this._account.sendStanza(s, errorHandler); + }, + get topicSettable() true, + /* Called when the user enters a chat message */ sendMsg: function (aMsg) { let notInRoom = (aError) => { @@ -228,6 +247,8 @@ const XMPPMUCConversationPrototype = { // XEP-0045: Room exists and joined successfully. this.left = false; this.joining = false; + // TODO (Bug 1172350): Implement Service Discovery Extensions (XEP-0128) to obtain + // configuration of this room. } if (!this._participants.get(nick)) { @@ -1218,7 +1239,8 @@ const XMPPAccountPrototype = { /* Called when a message stanza is received */ onMessageStanza: function(aStanza) { - let norm = this.normalize(aStanza.attributes["from"]); + let from = aStanza.attributes["from"]; + let norm = this.normalize(from); let type = aStanza.attributes["type"]; let body; @@ -1236,6 +1258,22 @@ const XMPPAccountPrototype = { body = TXTToHTML(b.innerText); } } + + let subject = aStanza.getElement(["subject"]); + if (subject) { + // XEP-0045 (7.2.16): Check for a subject element in the stanza and update + // the topic if it exists. + // We are breaking the spec because only a message that contains a + // but no element shall be considered a subject change + // for MUC, but we ignore that to be compatible with ejabberd versions + // before 15.06. + let muc = this._mucs.get(norm); + let nick = this._parseJID(from).resource; + // TODO There can be multiple subject elements with different xml:lang + // attributes. + muc.setTopic(subject.innerText, nick); + } + if (body) { let date; let delay = aStanza.getElement(["delay"]); @@ -1252,15 +1290,6 @@ const XMPPAccountPrototype = { return; } let muc = this._mucs.get(norm); - - // Check for a subject element in the stanza and update the topic if - // it exists. - let s = aStanza.getElement(["subject"]); - // TODO There can be multiple subject elements with different xml:lang - // attributes. - if (s) - muc.setTopic(s.innerText); - muc.incomingMessage(body, aStanza, date); return; }