diff --git a/irc/client.go b/irc/client.go index 8465fa6b..a5cc0f8f 100644 --- a/irc/client.go +++ b/irc/client.go @@ -175,7 +175,7 @@ func (s *Session) EndMultilineBatch(label string) (batch MultilineBatch, err err } s.deferredFakelagCount = fakelagBill - if batch.label == "" || batch.label != label || batch.message.LenLines() == 0 { + if batch.label == "" || batch.label != label || !batch.message.ValidMultiline() { err = errInvalidMultilineBatch return } @@ -1357,9 +1357,14 @@ func (session *Session) sendSplitMsgFromClientInternal(blocking bool, nickmask, session.SendRawMessage(msg, blocking) } } else { - for i, messagePair := range message.Split { + msgidSent := false // send msgid on the first nonblank line + for _, messagePair := range message.Split { + if len(messagePair.Message) == 0 { + continue + } var msgid string - if i == 0 { + if !msgidSent { + msgidSent = true msgid = message.Msgid } session.sendFromClientInternal(blocking, message.Time, msgid, nickmask, accountName, tags, command, target, messagePair.Message) diff --git a/irc/handlers.go b/irc/handlers.go index 4427bdf9..c6745d95 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -1793,33 +1793,35 @@ func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp // helper to store a batched PRIVMSG in the session object func absorbBatchedMessage(server *Server, client *Client, msg ircmsg.IrcMessage, batchTag string, histType history.ItemType, rb *ResponseBuffer) { + var errorCode, errorMessage string + defer func() { + if errorCode != "" { + if histType != history.Notice { + rb.Add(nil, server.name, "FAIL", "BATCH", errorCode, errorMessage) + } + rb.session.EndMultilineBatch("") + } + }() + if batchTag != rb.session.batch.label { - if histType != history.Notice { - rb.Add(nil, server.name, "FAIL", "BATCH", "MULTILINE_INVALID", client.t("Incorrect batch tag sent")) - } - rb.session.EndMultilineBatch("") + errorCode, errorMessage = "MULTILINE_INVALID", client.t("Incorrect batch tag sent") return - } else if len(msg.Params) < 2 || msg.Params[1] == "" { - if histType != history.Notice { - rb.Add(nil, server.name, "FAIL", "BATCH", "MULTILINE_INVALID", client.t("Invalid multiline batch")) - } - rb.session.EndMultilineBatch("") + } else if len(msg.Params) < 2 { + errorCode, errorMessage = "MULTILINE_INVALID", client.t("Invalid multiline batch") return } rb.session.batch.command = msg.Command isConcat, _ := msg.GetTag(caps.MultilineConcatTag) + if isConcat && len(msg.Params[1]) == 0 { + errorCode, errorMessage = "MULTILINE_INVALID", client.t("Cannot send a blank line with the multiline concat tag") + return + } rb.session.batch.message.Append(msg.Params[1], isConcat) config := server.Config() if config.Limits.Multiline.MaxBytes < rb.session.batch.message.LenBytes() { - if histType != history.Notice { - rb.Add(nil, server.name, "FAIL", "BATCH", "MULTILINE_MAX_BYTES", strconv.Itoa(config.Limits.Multiline.MaxBytes)) - } - rb.session.EndMultilineBatch("") + errorCode, errorMessage = "MULTILINE_MAX_BYTES", strconv.Itoa(config.Limits.Multiline.MaxBytes) } else if config.Limits.Multiline.MaxLines != 0 && config.Limits.Multiline.MaxLines < rb.session.batch.message.LenLines() { - if histType != history.Notice { - rb.Add(nil, server.name, "FAIL", "BATCH", "MULTILINE_MAX_LINES", strconv.Itoa(config.Limits.Multiline.MaxLines)) - } - rb.session.EndMultilineBatch("") + errorCode, errorMessage = "MULTILINE_MAX_LINES", strconv.Itoa(config.Limits.Multiline.MaxLines) } } diff --git a/irc/utils/text.go b/irc/utils/text.go index 5d46b140..d77b88d4 100644 --- a/irc/utils/text.go +++ b/irc/utils/text.go @@ -73,10 +73,24 @@ func (sm *SplitMessage) LenBytes() (result int) { } for i := 0; i < len(sm.Split); i++ { result += len(sm.Split[i].Message) + // bill for the joining newline if necessary + if i != 0 && !sm.Split[i].Concat { + result += 1 + } } return } +func (sm *SplitMessage) ValidMultiline() bool { + // must contain at least one nonblank line + for i := 0; i < len(sm.Split); i++ { + if len(sm.Split[i].Message) != 0 { + return true + } + } + return false +} + func (sm *SplitMessage) IsRestrictedCTCPMessage() bool { if IsRestrictedCTCPMessage(sm.Message) { return true