Bug 1601102 - Support the echo-message capability for IRC. r=freaktechnik
authorPatrick Cloke <clokep@gmail.com>
Wed, 04 Dec 2019 12:21:21 -0500
changeset 82246 792c378e19bcedb1a164a6621d0e734f7236a88d
parent 82245 2b0257c0f794ce26c10658674b961f8a4c0bb883
child 82247 2cfb1d544ba0442fce04865f94804fd79ddeb30e
push id9940
push usergeoff@darktrojan.net
push dateWed, 04 Dec 2019 22:28:38 +0000
treeherdertry-comm-central@3c37719f7f34 [default view] [failures only]
reviewersfreaktechnik
bugs1601102
Bug 1601102 - Support the echo-message capability for IRC. r=freaktechnik
chat/protocols/irc/irc.js
chat/protocols/irc/ircBase.jsm
chat/protocols/irc/ircEchoMessage.jsm
chat/protocols/irc/ircMultiPrefix.jsm
chat/protocols/irc/ircSASL.jsm
chat/protocols/irc/ircServerTime.jsm
chat/protocols/irc/moz.build
--- a/chat/protocols/irc/irc.js
+++ b/chat/protocols/irc/irc.js
@@ -312,19 +312,22 @@ var GenericIRCConversation = {
       this.writeMessage(
         this._account._currentServerName,
         _("error.sendMessageFailed"),
         { error: true, system: true }
       );
       return;
     }
 
-    // Since the server doesn't send us a message back, just assume the
-    // message was received and immediately show it.
-    this.writeMessage(this._account._nickname, aMessage, { outgoing: true });
+    // By default the server doesn't send the message back, but this can be
+    // enabled with the echo-message capability. If this is not enabled, just
+    // assume the message was received and immediately show it.
+    if (!this._account._activeCAPs.has("echo-message")) {
+      this.writeMessage(this._account._nickname, aMessage, { outgoing: true });
+    }
 
     this._pendingMessage = true;
   },
   // IRC doesn't support typing notifications, but it does have a maximum
   // message length.
   sendTyping(aString) {
     let longestLineLength = Math.max.apply(
       null,
@@ -2284,16 +2287,17 @@ function ircProtocol() {
   ChromeUtils.import("resource:///modules/ircBase.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircISUPPORT.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircCAP.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircCTCP.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircDCC.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircServices.jsm", tempScope);
 
   // Extra features.
+  ChromeUtils.import("resource:///modules/ircEchoMessage.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircMultiPrefix.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircNonStandard.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircSASL.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircServerTime.jsm", tempScope);
   ChromeUtils.import("resource:///modules/ircWatchMonitor.jsm", tempScope);
 
   // Register default IRC handlers (IRC base, CTCP).
   ircHandlers.registerHandler(tempScope.ircBase);
@@ -2307,16 +2311,17 @@ function ircProtocol() {
   ircHandlers.registerCTCPHandler(tempScope.ctcpBase);
   ircHandlers.registerCTCPHandler(tempScope.ctcpDCC);
   // Register default IRC Services handlers (IRC Services base).
   ircHandlers.registerServicesHandler(tempScope.servicesBase);
   // Register default CAP handlers for base features (CAP basics).
   ircHandlers.registerCAPHandler(tempScope.capNotify);
 
   // Register extra features.
+  ircHandlers.registerCAPHandler(tempScope.capEchoMessage);
   ircHandlers.registerISUPPORTHandler(tempScope.isupportNAMESX);
   ircHandlers.registerCAPHandler(tempScope.capMultiPrefix);
   ircHandlers.registerHandler(tempScope.ircNonStandard);
   ircHandlers.registerHandler(tempScope.ircWATCH);
   ircHandlers.registerISUPPORTHandler(tempScope.isupportWATCH);
   ircHandlers.registerHandler(tempScope.ircMONITOR);
   ircHandlers.registerISUPPORTHandler(tempScope.isupportMONITOR);
   ircHandlers.registerHandler(tempScope.ircSASL);
--- a/chat/protocols/irc/ircBase.jsm
+++ b/chat/protocols/irc/ircBase.jsm
@@ -27,17 +27,28 @@ var { ircHandlers } = ChromeUtils.import
 var {
   _,
   ctcpFormatToText,
   conversationErrorMessage,
   kListRefreshInterval,
 } = ChromeUtils.import("resource:///modules/ircUtils.jsm");
 
 function privmsg(aAccount, aMessage, aIsNotification) {
-  let params = { incoming: true, tags: aMessage.tags };
+  let params = { tags: aMessage.tags };
+  // If the echo-message capability is enabled and the message is from our nick,
+  // mark it as outgoing. Otherwise, the message is incoming.
+  if (
+    aAccount._activeCAPs.has("echo-message") &&
+    aAccount.normalizeNick(aMessage.origin) ==
+      aAccount.normalizeNick(aAccount._nickname)
+  ) {
+    params.outgoing = true;
+  } else {
+    params.incoming = true;
+  }
   if (aIsNotification) {
     params.notification = true;
   }
   aAccount
     .getConversation(
       aAccount.isMUCName(aMessage.params[0])
         ? aMessage.params[0]
         : aMessage.origin
new file mode 100644
--- /dev/null
+++ b/chat/protocols/irc/ircEchoMessage.jsm
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * This implements the echo-message capability for IRC.
+ *   https://ircv3.net/specs/extensions/echo-message-3.2
+ *
+ * When enabled, displaying of a sent messages is disabled (until it is received
+ * by the server and sent back to the sender). This helps to ensure the ordering
+ * of messages is consistent for all participants in a channel and also helps
+ * signify whether a message was properly sent to a channel during disconnect.
+ */
+
+this.EXPORTED_SYMBOLS = ["capEchoMessage"];
+
+const { ircHandlers } = ChromeUtils.import(
+  "resource:///modules/ircHandlers.jsm"
+);
+
+var capEchoMessage = {
+  name: "echo-message CAP",
+  priority: ircHandlers.DEFAULT_PRIORITY,
+  isEnabled: () => true,
+
+  commands: {
+    "echo-message": function(aMessage) {
+      if (
+        aMessage.cap.subcommand === "LS" ||
+        aMessage.cap.subcommand === "NEW"
+      ) {
+        this.addCAP("echo-message");
+        this.sendMessage("CAP", ["REQ", "echo-message"]);
+      } else if (
+        aMessage.cap.subcommand === "ACK" ||
+        aMessage.cap.subcommand === "NAK"
+      ) {
+        this.removeCAP("echo-message");
+      } else {
+        return false;
+      }
+      return true;
+    },
+  },
+};
--- a/chat/protocols/irc/ircMultiPrefix.jsm
+++ b/chat/protocols/irc/ircMultiPrefix.jsm
@@ -46,17 +46,17 @@ var capMultiPrefix = {
       // Request to use multi-prefix if it is supported.
       if (
         aMessage.cap.subcommand === "LS" ||
         aMessage.cap.subcommand === "NEW"
       ) {
         this.addCAP("multi-prefix");
         this.sendMessage("CAP", ["REQ", "multi-prefix"]);
       } else if (
-        aMessage.cap.subcommand == "ACK" ||
+        aMessage.cap.subcommand === "ACK" ||
         aMessage.cap.subcommand === "NAK"
       ) {
         this.removeCAP("multi-prefix");
       } else {
         return false;
       }
       return true;
     },
--- a/chat/protocols/irc/ircSASL.jsm
+++ b/chat/protocols/irc/ircSASL.jsm
@@ -164,20 +164,20 @@ var capSASL = {
           // We only support the plain authentication mechanism for now, abort if it's not available.
           if (!mechanisms.includes("PLAIN")) {
             return true;
           }
         }
         // If it supports SASL, let the server know we're requiring SASL.
         this.addCAP("sasl");
         this.sendMessage("CAP", ["REQ", "sasl"]);
-      } else if (aMessage.cap.subcommand == "ACK") {
+      } else if (aMessage.cap.subcommand === "ACK") {
         // The server acknowledges our choice to use SASL, send the first
         // message.
         this.sendMessage("AUTHENTICATE", "PLAIN");
-      } else if (aMessage.cap.subcommand == "NAK") {
+      } else if (aMessage.cap.subcommand === "NAK") {
         this.removeCAP("sasl");
       }
 
       return true;
     },
   },
 };
--- a/chat/protocols/irc/ircServerTime.jsm
+++ b/chat/protocols/irc/ircServerTime.jsm
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*
  * This implements server-time for IRC.
- *   http://ircv3.net/specs/extensions/server-time-3.2.html
+ *   https://ircv3.net/specs/extensions/server-time-3.2.html
  */
 
 this.EXPORTED_SYMBOLS = ["capServerTime", "tagServerTime"];
 
 const { ircHandlers } = ChromeUtils.import(
   "resource:///modules/ircHandlers.jsm"
 );
 
@@ -47,17 +47,17 @@ var capServerTime = {
     "server-time": function(aMessage) {
       if (
         aMessage.cap.subcommand === "LS" ||
         aMessage.cap.subcommand === "NEW"
       ) {
         this.addCAP("server-time");
         this.sendMessage("CAP", ["REQ", "server-time"]);
       } else if (
-        aMessage.cap.subcommand == "ACK" ||
+        aMessage.cap.subcommand === "ACK" ||
         aMessage.cap.subcommand === "NAK"
       ) {
         this.removeCAP("server-time");
       } else {
         return false;
       }
       return true;
     },
@@ -66,17 +66,17 @@ var capServerTime = {
       if (
         (aMessage.cap.subcommand === "LS" ||
           aMessage.cap.subcommand === "NEW") &&
         !this._availableCAPs.has("server-time")
       ) {
         this.addCAP("znc.in/server-time-iso");
         this.sendMessage("CAP", ["REQ", "znc.in/server-time-iso"]);
       } else if (
-        aMessage.cap.subcommand == "ACK" ||
+        aMessage.cap.subcommand === "ACK" ||
         aMessage.cap.subcommand === "NAK"
       ) {
         this.removeCAP("znc.in/server-time-iso");
       } else {
         return false;
       }
       return true;
     },
--- a/chat/protocols/irc/moz.build
+++ b/chat/protocols/irc/moz.build
@@ -11,16 +11,17 @@ EXTRA_COMPONENTS += [
 ]
 
 EXTRA_JS_MODULES += [
     'ircBase.jsm',
     'ircCAP.jsm',
     'ircCommands.jsm',
     'ircCTCP.jsm',
     'ircDCC.jsm',
+    'ircEchoMessage.jsm',
     'ircHandlers.jsm',
     'ircISUPPORT.jsm',
     'ircMultiPrefix.jsm',
     'ircNonStandard.jsm',
     'ircSASL.jsm',
     'ircServerTime.jsm',
     'ircServices.jsm',
     'ircUtils.jsm',