Bug 955606 - IRC ping does not properly send a timestamp, r=aleth,florian.
--- a/chat/locales/en-US/irc.properties
+++ b/chat/locales/en-US/irc.properties
@@ -28,21 +28,16 @@ options.server=Server
options.port=Port
options.ssl=Use SSL
options.encoding=Character Set
options.quitMessage=Quit message
options.partMessage=Part message
options.showServerTab=Show messages from the server
options.alternateNicks=Alternate nicks
-# LOCALIZATION NOTE (ctcp.ping): Semi-colon list of plural forms.
-# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
-# %1$S is the nickname of the user who was pinged.
-# #2 is the delay (in seconds).
-ctcp.ping=Ping reply from %1$S in #2 second.;Ping reply from %1$S in #2 seconds.
# LOCALIZATION NOTE (ctcp.version):
# %1$S is the nickname of the user whose version was requested.
# %2$S is the version response from the client.
ctcp.version=%1$S is using "%2$S".
# LOCALIZATION NOTE (ctcp.time):
# %1$S is the nickname of the user whose time was requested.
# %2$S is the time response.
ctcp.time=The time for %1$S is %2$S.
@@ -139,16 +134,22 @@ message.unknownNick=%S is an unknown nic
# channel key (password).
message.channelKeyAdded=%1$S changed the channel password to %2$S.
message.channelKeyRemoved=%S removed the channel password.
# This will be followed by a list of ban masks.
message.banMasks=Users connected from the following locations are banned from %S:
message.noBanMasks=There are no banned locations for %S.
message.banMaskAdded=Users connected from locations matching %1$S have been banned by %2$S.
message.banMaskRemoved=Users connected from locations matching %1$S are no longer banned by %2$S.
+# LOCALIZATION NOTE (message.ping): Semi-colon list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# %1$S is the nickname of the user or the server that was pinged.
+# #2 is the delay (in milliseconds).
+message.ping=Ping reply from %1$S in #2 millisecond.;Ping reply from %1$S in #2 milliseconds.
+
# LOCALIZATION NOTE (error.*):
# These are shown as error messages in the server tab.
# %S is the channel name.
error.noChannel=There is no channel: %S.
error.tooManyChannels=Cannot join %S; you've joined too many channels.
# %1$S is your new nick, %2$S is the kill message from the server.
error.nickCollision=Nick already in use, changing nick to %1$S [%2$S].
--- a/chat/protocols/irc/irc.js
+++ b/chat/protocols/irc/irc.js
@@ -6,16 +6,19 @@ const {classes: Cc, interfaces: Ci, resu
Cu.import("resource:///modules/imXPCOMUtils.jsm");
Cu.import("resource:///modules/imServices.jsm");
Cu.import("resource:///modules/ircUtils.jsm");
Cu.import("resource:///modules/ircHandlers.jsm");
Cu.import("resource:///modules/jsProtoHelper.jsm");
Cu.import("resource:///modules/socket.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+ "resource://gre/modules/PluralForm.jsm");
+
/*
* Parses a raw IRC message into an object (see section 2.3 of RFC 2812). This
* returns an object with the following fields:
* rawMessage The initial message string received without any processing.
* command A string that is the command or response code.
* params An array of strings for the parameters. The last parameter is
* stripped of its : prefix.
* If the message is from a user:
@@ -631,18 +634,20 @@ ircSocket.prototype = {
// Although RFCs 1459 and 2812 explicitly say that \r\n is the message
// separator, some networks (euIRC) only send \n.
delimiter: /\r?\n/,
connectTimeout: 60, // Failure to connect after 1 minute
readWriteTimeout: 300, // Failure when no data for 5 minutes
_converter: null,
sendPing: function() {
- // Send a ping using the current timestamp as a payload.
- this._account.sendMessage("PING", Date.now());
+ // Send a ping using the current timestamp as a payload prefixed with
+ // an underscore to signify this was an "automatic" PING (used to avoid
+ // socket timeouts).
+ this._account.sendMessage("PING", "_" + Date.now());
},
_initCharsetConverter: function() {
this._converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
try {
this._converter.charset = this._account._encoding;
} catch (e) {
@@ -1208,16 +1213,44 @@ ircAccount.prototype = {
return true;
}
this.LOG(aOldNick + " is already in use, trying " + newNick);
this.sendMessage("NICK", newNick); // Nick message.
return true;
},
+ handlePingReply: function(aSource, aPongTime) {
+ // Received PING response, display to the user.
+ let sentTime = new Date(parseInt(aPongTime, 10));
+
+ // The received timestamp is invalid.
+ if (isNaN(sentTime)) {
+ this.WARN(aMessage.servername +
+ " returned an invalid timestamp from a PING: " + aPongTime);
+ return false;
+ }
+
+ // Find the delay in milliseconds.
+ let delay = Date.now() - sentTime;
+
+ // If the delay is negative or greater than 1 minute, something is
+ // feeding us a crazy value. Don't display this to the user.
+ if (delay < 0 || 60 * 1000 < delay) {
+ this.WARN(aMessage.servername +
+ " returned an invalid delay from a PING: " + delay);
+ return false;
+ }
+
+ let msg = PluralForm.get(delay, _("message.ping", aSource))
+ .replace("#2", delay);
+ this.getConversation(aSource).writeMessage(aSource, msg, {system: true});
+ return true;
+ },
+
countBytes: function(aStr) {
// Assume that if it's not UTF-8 then each character is 1 byte.
if (this._encoding != "UTF-8")
return aStr.length;
// Count the number of bytes in a UTF-8 encoded string.
function charCodeToByteCount(c) {
// UTF-8 stores:
--- a/chat/protocols/irc/ircBase.jsm
+++ b/chat/protocols/irc/ircBase.jsm
@@ -271,19 +271,26 @@ var ircBase = {
"PING": function(aMessage) {
// PING <server1> [ <server2> ]
// Keep the connection alive.
this.sendMessage("PONG", aMessage.params[0]);
return true;
},
"PONG": function(aMessage) {
// PONG <server> [ <server2> ]
+ let pongTime = aMessage.params[1];
+
// Ping to keep the connection alive.
- this._socket.cancelDisconnectTimer();
- return true;
+ if (pongTime.startsWith("_")) {
+ this._socket.cancelDisconnectTimer();
+ return true;
+ }
+ // Otherwise, the ping was from a user command.
+ else
+ return this.handlePingReply(aMessage.servername, pongTime);
},
"PRIVMSG": function(aMessage) {
// PRIVMSG <msgtarget> <text to be sent>
// Display message in conversation
return privmsg(this, aMessage);
},
"QUIT": function(aMessage) {
// QUIT [ < Quit Message> ]
--- a/chat/protocols/irc/ircCTCP.jsm
+++ b/chat/protocols/irc/ircCTCP.jsm
@@ -12,21 +12,16 @@ const EXPORTED_SYMBOLS = ["ircCTCP", "ct
const Cu = Components.utils;
Cu.import("resource:///modules/imServices.jsm");
Cu.import("resource:///modules/imXPCOMUtils.jsm");
Cu.import("resource:///modules/ircHandlers.jsm");
Cu.import("resource:///modules/ircUtils.jsm");
-XPCOMUtils.defineLazyGetter(this, "PluralForm", function() {
- Cu.import("resource://gre/modules/PluralForm.jsm");
- return PluralForm;
-});
-
// Split into a CTCP message which is a single command and a single parameter:
// <command> " " <parameter>
// The high level dequote is to unescape \001 in the message content.
function CTCPMessage(aMessage, aRawCTCPMessage) {
let message = aMessage;
message.ctcp = {};
message.ctcp.rawMessage = aRawCTCPMessage;
@@ -142,46 +137,27 @@ var ctcpBase = {
// Returns the user's full name, and idle time.
"FINGER": function(aMessage) false,
// Dynamic master index of what a client knows.
"CLIENTINFO": function(aMessage) false,
// Used to measure the delay of the IRC network between clients.
"PING": function(aMessage) {
+ // PING timestamp
if (aMessage.command == "PRIVMSG") {
- // PING timestamp
// Received PING request, send PING response.
this.LOG("Received PING request from " + aMessage.nickname +
". Sending PING response: \"" + aMessage.ctcp.param + "\".");
this.sendCTCPMessage("PING", aMessage.ctcp.param, aMessage.nickname,
- true);
+ true);
+ return true;
}
- else {
- // PING timestamp
- // Received PING response, display to the user.
- let sentTime = new Date(aMessage.ctcp.param);
-
- // The received timestamp is invalid
- if (isNaN(sentTime)) {
- this.WARN(aMessage.nickname +
- " returned an invalid timestamp from a CTCP PING: " +
- aMessage.ctcp.param);
- return false;
- }
-
- // Find the delay in seconds.
- let delay = (Date.now() - sentTime) / 1000;
-
- let message = PluralForm.get(delay, _("ctcp.ping", aMessage.nickname))
- .replace("#2", delay);
- this.getConversation(aMessage.nickname)
- .writeMessage(aMessage.nickname, message, {system: true});
- }
- return true;
+ else
+ return this.handlePingReply(aMessage.nickname, aMessage.ctcp.param);
},
// An encryption protocol between clients without any known reference.
"SED": function(aMessage) false,
// Where to obtain a copy of a client.
"SOURCE": function(aMessage) false,
--- a/chat/protocols/irc/ircCommands.jsm
+++ b/chat/protocols/irc/ircCommands.jsm
@@ -332,19 +332,24 @@ var commands = [
getConv(aConv).part(aMsg);
return true;
}
},
{
name: "ping",
get helpString() _("command.ping", "ping"),
run: function(aMsg, aConv) {
- if (!aMsg || !aMsg.trim().length)
- return false;
- ctcpCommand(aConv, aMsg, "PING");
+ // Send a ping to the entered nick using the current time (in
+ // milliseconds) as the param. If no nick is entered, ping the
+ // server.
+ if (aMsg && aMsg.trim().length)
+ ctcpCommand(aConv, aMsg, "PING", Date.now());
+ else
+ getAccount(aConv).sendMessage("PING", Date.now());
+
return true;
}
},
{
name: "query",
get helpString() _("command.msg", "query"),
run: messageCommand
},