Bug 842183 - Land in comm-central Instantbird's changes to chat/ - 5 - Bio 1748 - Add NAMESX support (to get all user modes of someone instead of just one), r=aleth.
authorPatrick Cloke <clokep@gmail.com>
Sat, 24 Nov 2012 22:21:15 -0500
changeset 14886 46fd0eac83e7de6f3cd1345393f7b9b01870d0d2
parent 14885 4d877bc3b994f21ede304a5b7e8e222e2492399a
child 14887 e5e235094adffac9a0f4711e15aafd3cab8e4bfa
push id867
push userbugzilla@standard8.plus.com
push dateMon, 01 Apr 2013 20:44:27 +0000
treeherdercomm-beta@797726b8d244 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaleth
bugs842183
Bug 842183 - Land in comm-central Instantbird's changes to chat/ - 5 - Bio 1748 - Add NAMESX support (to get all user modes of someone instead of just one), r=aleth.
chat/protocols/irc/Makefile.in
chat/protocols/irc/irc.js
chat/protocols/irc/ircISUPPORT.jsm
chat/protocols/irc/ircMultiPrefix.jsm
--- a/chat/protocols/irc/Makefile.in
+++ b/chat/protocols/irc/Makefile.in
@@ -17,16 +17,17 @@ EXTRA_COMPONENTS = \
 EXTRA_JS_MODULES = \
 		ircBase.jsm \
 		ircCAP.jsm \
 		ircCommands.jsm \
 		ircCTCP.jsm \
 		ircDCC.jsm \
 		ircHandlers.jsm \
 		ircISUPPORT.jsm \
+		ircMultiPrefix.jsm \
 		ircNonStandard.jsm \
 		ircSASL.jsm \
 		ircServices.jsm \
 		ircUtils.jsm \
 		ircWatchMonitor.jsm \
 		$(NULL)
 
 ifdef ENABLE_TESTS
--- a/chat/protocols/irc/irc.js
+++ b/chat/protocols/irc/irc.js
@@ -490,20 +490,22 @@ ircChannel.prototype = {
 copySharedBaseToPrototype(GenericIRCConversation, ircChannel.prototype);
 
 function ircParticipant(aName, aConv) {
   this._name = aName;
   this._conv = aConv;
   this._account = aConv._account;
   this._modes = [];
 
-  if (this._name[0] in this._account.userPrefixToModeMap) {
-    this._modes.push(this._account.userPrefixToModeMap[this._name[0]]);
-    this._name = this._name.slice(1);
-  }
+  // Handle multi-prefix modes.
+  let i;
+  for (i = 0; i < this._name.length &&
+              this._name[i] in this._account.userPrefixToModeMap; ++i)
+    this._modes.push(this._account.userPrefixToModeMap[this._name[i]]);
+  this._name = this._name.slice(i);
 }
 ircParticipant.prototype = {
   __proto__: GenericConvChatBuddyPrototype,
 
   setMode: function(aAddNewMode, aNewModes, aSetter) {
     _setMode.call(this, aAddNewMode, aNewModes);
 
     // Notify the UI of changes.
@@ -730,22 +732,26 @@ ircAccount.prototype = {
   // Member Status of RFC 2811.
   memberStatuses: ["a", "h", "o", "O", "q", "v", "!"],
   channelPrefixes: ["&", "#", "+", "!"], // 1.3 Channels
   channelRestrictionToModeMap: {"@": "s", "*": "p", "=": null}, // 353 RPL_NAMREPLY
 
   // Handle Scandanavian lower case (optionally remove status indicators).
   // See Section 2.2 of RFC 2812: the characters {}|^ are considered to be the
   // lower case equivalents of the characters []\~, respectively.
+  normalizeExpression: /[\x41-\x5E]/g,
   normalize: function(aStr, aPrefixes) {
     let str = aStr;
-    if (aPrefixes && aPrefixes.indexOf(aStr[0]) != -1)
-      str = str.slice(1);
 
-    return str.replace(/[\x41-\x5E]/g,
+    if (aPrefixes) {
+      while (aPrefixes.indexOf(str[0]) != -1)
+        str = str.slice(1);
+    }
+
+    return str.replace(this.normalizeExpression,
                        function(c) String.fromCharCode(c.charCodeAt(0) + 0x20));
   },
 
   isMUCName: function(aStr) {
     return (this.channelPrefixes.indexOf(aStr[0]) != -1);
   },
 
   // Tell the server about status changes. IRC is only away or not away;
@@ -1391,16 +1397,17 @@ function ircProtocol() {
   Cu.import("resource:///modules/ircBase.jsm", tempScope);
   Cu.import("resource:///modules/ircISUPPORT.jsm", tempScope);
   Cu.import("resource:///modules/ircCAP.jsm", tempScope);
   Cu.import("resource:///modules/ircCTCP.jsm", tempScope);
   Cu.import("resource:///modules/ircDCC.jsm", tempScope);
   Cu.import("resource:///modules/ircServices.jsm", tempScope);
 
   // Extra features.
+  Cu.import("resource:///modules/ircMultiPrefix.jsm", tempScope);
   Cu.import("resource:///modules/ircNonStandard.jsm", tempScope);
   Cu.import("resource:///modules/ircSASL.jsm", tempScope);
   Cu.import("resource:///modules/ircWatchMonitor.jsm", tempScope);
 
   // Register default IRC handlers (IRC base, CTCP).
   ircHandlers.registerHandler(tempScope.ircBase);
   ircHandlers.registerHandler(tempScope.ircISUPPORT);
   ircHandlers.registerHandler(tempScope.ircCAP);
@@ -1410,16 +1417,18 @@ function ircProtocol() {
   ircHandlers.registerISUPPORTHandler(tempScope.isupportBase);
   // Register default CTCP handlers (CTCP base, DCC).
   ircHandlers.registerCTCPHandler(tempScope.ctcpBase);
   ircHandlers.registerCTCPHandler(tempScope.ctcpDCC);
   // Register default IRC Services handlers (IRC Services base).
   ircHandlers.registerServicesHandler(tempScope.servicesBase);
 
   // Register extra features.
+  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);
   ircHandlers.registerCAPHandler(tempScope.capSASL);
 }
--- a/chat/protocols/irc/ircISUPPORT.jsm
+++ b/chat/protocols/irc/ircISUPPORT.jsm
@@ -67,29 +67,19 @@ var ircISUPPORT = {
 
 function setSimpleNumber(aAccount, aField, aMessage, aDefaultValue) {
   let value =
     aMessage.isupport.value ? new Number(aMessage.isupport.value) : null;
   aAccount[aField] = (value && !isNaN(value)) ? value : aDefaultValue;
   return true;
 }
 
-// Generates a function that will set the ASCII range of aStart-aEnd as the
-// uppercase of (aStart-aEnd) + 0x20.
-function generateNormalize(aStart, aEnd) {
-  const exp = new RegExp("[\\x" + aStart.toString(16) + "-\\x" +
-                         aEnd.toString(16) + "]", "g");
-  return function(aStr, aPrefixes) {
-    let str = aStr;
-    if (aPrefixes && aPrefixes.indexOf(aStr[0]) != -1)
-      str = str.slice(1);
-    return str.replace(exp,
-                       function(c) String.fromCharCode(c.charCodeAt(0) + 0x20));
-  };
-}
+// Generates an expression to search for the ASCII range of a-b.
+function generateNormalize(a, b)
+  new RegExp("[\\x" + a.toString(16) + "-\\x" + b.toString(16) + "]", "g");
 
 var isupportBase = {
   name: "ISUPPORT",
   priority: ircHandlers.DEFAULT_PRIORITY,
   isEnabled: function() true,
 
   commands: {
     "CASEMAPPING": function(aMessage) {
@@ -101,27 +91,27 @@ var isupportBase = {
       let value = aMessage.isupport.useDefault ?
         "rfc1493" : aMessage.isupport.value;
 
       // Set the normalize function of the account to use the proper case
       // mapping.
       if (value == "ascii") {
         // The ASCII characters 97 to 122 (decimal) are the lower-case
         // characters of ASCII 65 to 90 (decimal).
-        this.normalize = generateNormalize(65, 90);
+        this.normalizeExpression = generateNormalize(65, 90);
       }
       else if (value == "rfc1493") {
         // The ASCII characters 97 to 126 (decimal) are the lower-case
         // characters of ASCII 65 to 94 (decimal).
-        this.normalize = generateNormalize(65, 94);
+        this.normalizeExpression = generateNormalize(65, 94);
       }
       else if (value == "strict-rfc1459") {
         // The ASCII characters 97 to 125 (decimal) are the lower-case
         // characters of ASCII 65 to 93 (decimal).
-        this.normalize = generateNormalize(65, 93);
+        this.normalizeExpression = generateNormalize(65, 93);
       }
       return true;
     },
     "CHANLIMIT": function(aMessage) {
       // CHANLIMIT=<prefix>:<number>[,<prefix>:<number>]*
       // Note that each <prefix> can actually contain multiple prefixes, this
       // means the sum of those prefixes is given.
       this.maxChannels = {};
new file mode 100644
--- /dev/null
+++ b/chat/protocols/irc/ircMultiPrefix.jsm
@@ -0,0 +1,56 @@
+/* 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 contains an implementation of the multi-prefix IRC extension. This fixes
+ * a protocol level bug where the following can happen:
+ *   foo MODE +h
+ *   foo MODE +o
+ *   bar JOINs the channel (and receives @foo)
+ *   foo MODE -o
+ * foo knows that it has mode +h, but bar does not know foo has +h set.
+ *
+ *   http://wiki.inspircd.org/Modules/2.1/namesx
+ *   http://ircv3.atheme.org/extensions/multi-prefix-3.1
+ */
+
+const EXPORTED_SYMBOLS = ["isupportNAMESX", "capMultiPrefix"];
+
+Components.utils.import("resource:///modules/ircHandlers.jsm");
+
+var isupportNAMESX = {
+  name: "ISUPPORT NAMESX",
+  // Slightly above default ISUPPORT priority.
+  priority: ircHandlers.DEFAULT_PRIORITY + 10,
+  isEnabled: function() true,
+
+  commands: {
+    "NAMESX": function(aMessage) {
+      this.sendMessage("PROTOCTL", "NAMESX");
+      return true;
+    }
+  }
+};
+
+var capMultiPrefix = {
+  name: "CAP multi-prefix",
+  // Slightly above default ISUPPORT priority.
+  priority: ircHandlers.HIGH_PRIORITY,
+  isEnabled: function() true,
+
+  commands: {
+    "multi-prefix": function(aMessage) {
+      // Request to use multi-prefix if it is supported.
+      if (aMessage.cap.subcommand == "LS") {
+        this.addCAP("multi-prefix");
+        this.sendMessage("CAP", ["REQ", "multi-prefix"]);
+      }
+      else if (aMessage.cap.subcommand == "ACK")
+        this.removeCAP("multi-prefix");
+      else
+        return false;
+      return true;
+    }
+  }
+};