diff --git a/irc/client.go b/irc/client.go index f28153db..3d920910 100644 --- a/irc/client.go +++ b/irc/client.go @@ -580,14 +580,14 @@ func (session *Session) playResume() { timestamp := session.resumeDetails.Timestamp gap := lastDiscarded.Sub(timestamp) - session.resumeDetails.HistoryIncomplete = gap > 0 + session.resumeDetails.HistoryIncomplete = gap > 0 || timestamp.IsZero() gapSeconds := int(gap.Seconds()) + 1 // round up to avoid confusion details := client.Details() oldNickmask := details.nickMask client.SetRawHostname(session.rawHostname) hostname := client.Hostname() // may be a vhost - timestampString := session.resumeDetails.Timestamp.Format(IRCv3TimestampFormat) + timestampString := timestamp.Format(IRCv3TimestampFormat) // send quit/resume messages to friends for friend := range friends { @@ -596,23 +596,29 @@ func (session *Session) playResume() { } for _, fSession := range friend.Sessions() { if fSession.capabilities.Has(caps.Resume) { - if session.resumeDetails.HistoryIncomplete { + if !session.resumeDetails.HistoryIncomplete { + fSession.Send(nil, oldNickmask, "RESUMED", hostname, "ok") + } else if session.resumeDetails.HistoryIncomplete && !timestamp.IsZero() { fSession.Send(nil, oldNickmask, "RESUMED", hostname, timestampString) } else { fSession.Send(nil, oldNickmask, "RESUMED", hostname) } } else { - if session.resumeDetails.HistoryIncomplete { - fSession.Send(nil, oldNickmask, "QUIT", fmt.Sprintf(friend.t("Client reconnected (up to %d seconds of history lost)"), gapSeconds)) - } else { + if !session.resumeDetails.HistoryIncomplete { fSession.Send(nil, oldNickmask, "QUIT", fmt.Sprintf(friend.t("Client reconnected"))) + } else if session.resumeDetails.HistoryIncomplete && !timestamp.IsZero() { + fSession.Send(nil, oldNickmask, "QUIT", fmt.Sprintf(friend.t("Client reconnected (up to %d seconds of message history lost)"), gapSeconds)) + } else { + fSession.Send(nil, oldNickmask, "QUIT", fmt.Sprintf(friend.t("Client reconnected (message history may have been lost)"))) } } } } - if session.resumeDetails.HistoryIncomplete { + if session.resumeDetails.HistoryIncomplete && !timestamp.IsZero() { session.Send(nil, client.server.name, "WARN", "RESUME", "HISTORY_LOST", fmt.Sprintf(client.t("Resume may have lost up to %d seconds of history"), gapSeconds)) + } else { + session.Send(nil, client.server.name, "WARN", "RESUME", "HISTORY_LOST", client.t("Resume may have lost some message history")) } session.Send(nil, client.server.name, "RESUME", "SUCCESS", details.nick) diff --git a/irc/commands.go b/irc/commands.go index 2b221621..f5049948 100644 --- a/irc/commands.go +++ b/irc/commands.go @@ -232,7 +232,7 @@ func init() { "RESUME": { handler: resumeHandler, usablePreReg: true, - minParams: 2, + minParams: 1, }, "SAJOIN": { handler: sajoinHandler, diff --git a/irc/handlers.go b/irc/handlers.go index 98b77c8d..e6cf61ce 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -2359,24 +2359,27 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re return false } -// RESUME +// RESUME [timestamp] func resumeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool { + details := ResumeDetails{ + PresentedToken: msg.Params[0], + } + if client.registered { rb.Add(nil, server.name, "FAIL", "RESUME", "REGISTRATION_IS_COMPLETED", client.t("Cannot resume connection, connection registration has already been completed")) return false } - ts, err := time.Parse(IRCv3TimestampFormat, msg.Params[1]) - if err != nil { - rb.Add(nil, server.name, "FAIL", "RESUME", "INVALID_TIMESTAMP", client.t("Cannot resume connection, timestamp is not valid")) - return false - } - - rb.session.resumeDetails = &ResumeDetails{ - PresentedToken: msg.Params[0], - Timestamp: ts, + if 1 < len(msg.Params) { + ts, err := time.Parse(IRCv3TimestampFormat, msg.Params[1]) + if err == nil { + details.Timestamp = ts + } else { + rb.Add(nil, server.name, "WARN", "RESUME", "HISTORY_LOST", client.t("Timestamp is not in 2006-01-02T15:04:05.999Z format, ignoring it")) + } } + rb.session.resumeDetails = &details return false }