mirror of
https://github.com/ergochat/ergo.git
synced 2024-12-22 18:52:41 +01:00
Merge pull request #442 from slingamn/message_tags.5
upgrade message-tags to non-draft version
This commit is contained in:
commit
baa7e5af0b
4
Gopkg.lock
generated
4
Gopkg.lock
generated
@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:6bcd7bcd5e14cc9552fbf83b2f77f24935c0c502d009c9825b6c212c3f8eb967"
|
digest = "1:e6ed6eaa63211bb90847d8c5f11d7412e56c96b5befb7402ee7a7a8ad02700ec"
|
||||||
name = "github.com/goshuirc/irc-go"
|
name = "github.com/goshuirc/irc-go"
|
||||||
packages = [
|
packages = [
|
||||||
"ircfmt",
|
"ircfmt",
|
||||||
@ -35,7 +35,7 @@
|
|||||||
"ircmsg",
|
"ircmsg",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "cf199aea7186fd960d0ed5abbf579bb0f9d890d1"
|
revision = "ca74bf6a176d2d1dce6f28f99901a2d48d8da2bd"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
digest = "1:c658e84ad3916da105a761660dcaeb01e63416c8ec7bc62256a9b411a05fcd67"
|
||||||
|
@ -83,15 +83,15 @@ CAPDEFS = [
|
|||||||
),
|
),
|
||||||
CapDef(
|
CapDef(
|
||||||
identifier="MaxLine",
|
identifier="MaxLine",
|
||||||
name="oragono.io/maxline",
|
name="oragono.io/maxline-2",
|
||||||
url="https://oragono.io/maxline",
|
url="https://oragono.io/maxline-2",
|
||||||
standard="Oragono-specific",
|
standard="Oragono-specific",
|
||||||
),
|
),
|
||||||
CapDef(
|
CapDef(
|
||||||
identifier="MessageTags",
|
identifier="MessageTags",
|
||||||
name="draft/message-tags-0.2",
|
name="message-tags",
|
||||||
url="https://ircv3.net/specs/core/message-tags-3.3.html",
|
url="https://ircv3.net/specs/extensions/message-tags.html",
|
||||||
standard="draft IRCv3",
|
standard="IRCv3",
|
||||||
),
|
),
|
||||||
CapDef(
|
CapDef(
|
||||||
identifier="MultiPrefix",
|
identifier="MultiPrefix",
|
||||||
|
@ -57,12 +57,12 @@ const (
|
|||||||
// https://gist.github.com/DanielOaks/8126122f74b26012a3de37db80e4e0c6
|
// https://gist.github.com/DanielOaks/8126122f74b26012a3de37db80e4e0c6
|
||||||
Languages Capability = iota
|
Languages Capability = iota
|
||||||
|
|
||||||
// MaxLine is the Oragono-specific capability named "oragono.io/maxline":
|
// MaxLine is the Oragono-specific capability named "oragono.io/maxline-2":
|
||||||
// https://oragono.io/maxline
|
// https://oragono.io/maxline-2
|
||||||
MaxLine Capability = iota
|
MaxLine Capability = iota
|
||||||
|
|
||||||
// MessageTags is the draft IRCv3 capability named "draft/message-tags-0.2":
|
// MessageTags is the IRCv3 capability named "message-tags":
|
||||||
// https://ircv3.net/specs/core/message-tags-3.3.html
|
// https://ircv3.net/specs/extensions/message-tags.html
|
||||||
MessageTags Capability = iota
|
MessageTags Capability = iota
|
||||||
|
|
||||||
// MultiPrefix is the IRCv3 capability named "multi-prefix":
|
// MultiPrefix is the IRCv3 capability named "multi-prefix":
|
||||||
@ -112,8 +112,8 @@ var (
|
|||||||
"invite-notify",
|
"invite-notify",
|
||||||
"draft/labeled-response",
|
"draft/labeled-response",
|
||||||
"draft/languages",
|
"draft/languages",
|
||||||
"oragono.io/maxline",
|
"oragono.io/maxline-2",
|
||||||
"draft/message-tags-0.2",
|
"message-tags",
|
||||||
"multi-prefix",
|
"multi-prefix",
|
||||||
"draft/rename",
|
"draft/rename",
|
||||||
"draft/resume-0.3",
|
"draft/resume-0.3",
|
||||||
|
135
irc/channel.go
135
irc/channel.go
@ -14,7 +14,6 @@ import (
|
|||||||
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/goshuirc/irc-go/ircmsg"
|
|
||||||
"github.com/oragono/oragono/irc/caps"
|
"github.com/oragono/oragono/irc/caps"
|
||||||
"github.com/oragono/oragono/irc/history"
|
"github.com/oragono/oragono/irc/history"
|
||||||
"github.com/oragono/oragono/irc/modes"
|
"github.com/oragono/oragono/irc/modes"
|
||||||
@ -425,11 +424,13 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
|
|||||||
|
|
||||||
channel.regenerateMembersCache()
|
channel.regenerateMembersCache()
|
||||||
|
|
||||||
|
message := utils.SplitMessage{}
|
||||||
|
message.Msgid = details.realname
|
||||||
channel.history.Add(history.Item{
|
channel.history.Add(history.Item{
|
||||||
Type: history.Join,
|
Type: history.Join,
|
||||||
Nick: details.nickMask,
|
Nick: details.nickMask,
|
||||||
AccountName: details.accountName,
|
AccountName: details.accountName,
|
||||||
Msgid: details.realname,
|
Message: message,
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -603,16 +604,17 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
|
|||||||
serverTime := client.capabilities.Has(caps.ServerTime)
|
serverTime := client.capabilities.Has(caps.ServerTime)
|
||||||
|
|
||||||
for _, item := range items {
|
for _, item := range items {
|
||||||
var tags Tags
|
var tags map[string]string
|
||||||
if serverTime {
|
if serverTime {
|
||||||
tags = ensureTag(tags, "time", item.Time.Format(IRCv3TimestampFormat))
|
tags = map[string]string{"time": item.Time.Format(IRCv3TimestampFormat)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(#437) support history.Tagmsg
|
||||||
switch item.Type {
|
switch item.Type {
|
||||||
case history.Privmsg:
|
case history.Privmsg:
|
||||||
rb.AddSplitMessageFromClient(item.Msgid, item.Nick, item.AccountName, tags, "PRIVMSG", chname, item.Message)
|
rb.AddSplitMessageFromClient(item.Nick, item.AccountName, tags, "PRIVMSG", chname, item.Message)
|
||||||
case history.Notice:
|
case history.Notice:
|
||||||
rb.AddSplitMessageFromClient(item.Msgid, item.Nick, item.AccountName, tags, "NOTICE", chname, item.Message)
|
rb.AddSplitMessageFromClient(item.Nick, item.AccountName, tags, "NOTICE", chname, item.Message)
|
||||||
case history.Join:
|
case history.Join:
|
||||||
nick := stripMaskFromNick(item.Nick)
|
nick := stripMaskFromNick(item.Nick)
|
||||||
var message string
|
var message string
|
||||||
@ -624,16 +626,16 @@ func (channel *Channel) replayHistoryItems(rb *ResponseBuffer, items []history.I
|
|||||||
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
||||||
case history.Part:
|
case history.Part:
|
||||||
nick := stripMaskFromNick(item.Nick)
|
nick := stripMaskFromNick(item.Nick)
|
||||||
message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Original)
|
message := fmt.Sprintf(client.t("%[1]s left the channel (%[2]s)"), nick, item.Message.Message)
|
||||||
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
||||||
case history.Quit:
|
case history.Quit:
|
||||||
nick := stripMaskFromNick(item.Nick)
|
nick := stripMaskFromNick(item.Nick)
|
||||||
message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Original)
|
message := fmt.Sprintf(client.t("%[1]s quit (%[2]s)"), nick, item.Message.Message)
|
||||||
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
||||||
case history.Kick:
|
case history.Kick:
|
||||||
nick := stripMaskFromNick(item.Nick)
|
nick := stripMaskFromNick(item.Nick)
|
||||||
// XXX Msgid is the kick target
|
// XXX Msgid is the kick target
|
||||||
message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Msgid, item.Message.Original)
|
message := fmt.Sprintf(client.t("%[1]s kicked %[2]s (%[3]s)"), nick, item.Message.Msgid, item.Message.Message)
|
||||||
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
rb.Add(tags, "HistServ", "PRIVMSG", chname, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -717,13 +719,20 @@ func (channel *Channel) CanSpeak(client *Client) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagMsg sends a tag message to everyone in this channel who can accept them.
|
func (channel *Channel) SendSplitMessage(command string, minPrefix *modes.Mode, clientOnlyTags map[string]string, client *Client, message utils.SplitMessage, rb *ResponseBuffer) {
|
||||||
func (channel *Channel) TagMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, rb *ResponseBuffer) {
|
var histType history.ItemType
|
||||||
channel.sendMessage(msgid, "TAGMSG", []caps.Capability{caps.MessageTags}, minPrefix, clientOnlyTags, client, nil, rb)
|
switch command {
|
||||||
}
|
case "PRIVMSG":
|
||||||
|
histType = history.Privmsg
|
||||||
|
case "NOTICE":
|
||||||
|
histType = history.Notice
|
||||||
|
case "TAGMSG":
|
||||||
|
histType = history.Tagmsg
|
||||||
|
default:
|
||||||
|
channel.server.logger.Error("internal", "unrecognized Channel.SendSplitMessage command", command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// sendMessage sends a given message to everyone on this channel.
|
|
||||||
func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capability, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *string, rb *ResponseBuffer) {
|
|
||||||
if !channel.CanSpeak(client) {
|
if !channel.CanSpeak(client) {
|
||||||
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
||||||
return
|
return
|
||||||
@ -736,85 +745,16 @@ func (channel *Channel) sendMessage(msgid, cmd string, requiredCaps []caps.Capab
|
|||||||
}
|
}
|
||||||
// send echo-message
|
// send echo-message
|
||||||
if client.capabilities.Has(caps.EchoMessage) {
|
if client.capabilities.Has(caps.EchoMessage) {
|
||||||
var messageTagsToUse *map[string]ircmsg.TagValue
|
var tagsToUse map[string]string
|
||||||
if client.capabilities.Has(caps.MessageTags) {
|
|
||||||
messageTagsToUse = clientOnlyTags
|
|
||||||
}
|
|
||||||
|
|
||||||
nickMaskString := client.NickMaskString()
|
|
||||||
accountName := client.AccountName()
|
|
||||||
if message == nil {
|
|
||||||
rb.AddFromClient(msgid, nickMaskString, accountName, messageTagsToUse, cmd, channel.name)
|
|
||||||
} else {
|
|
||||||
rb.AddFromClient(msgid, nickMaskString, accountName, messageTagsToUse, cmd, channel.name, *message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, member := range channel.Members() {
|
|
||||||
if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
|
|
||||||
// STATUSMSG
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// echo-message is handled above, so skip sending the msg to the user themselves as well
|
|
||||||
if member == client {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
canReceive := true
|
|
||||||
for _, capName := range requiredCaps {
|
|
||||||
if !member.capabilities.Has(capName) {
|
|
||||||
canReceive = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !canReceive {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var messageTagsToUse *map[string]ircmsg.TagValue
|
|
||||||
if member.capabilities.Has(caps.MessageTags) {
|
|
||||||
messageTagsToUse = clientOnlyTags
|
|
||||||
}
|
|
||||||
|
|
||||||
if message == nil {
|
|
||||||
member.SendFromClient(msgid, client, messageTagsToUse, cmd, channel.name)
|
|
||||||
} else {
|
|
||||||
member.SendFromClient(msgid, client, messageTagsToUse, cmd, channel.name, *message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SplitPrivMsg sends a private message to everyone in this channel.
|
|
||||||
func (channel *Channel) SplitPrivMsg(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message utils.SplitMessage, rb *ResponseBuffer) {
|
|
||||||
channel.sendSplitMessage(msgid, "PRIVMSG", history.Privmsg, minPrefix, clientOnlyTags, client, &message, rb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SplitNotice sends a private message to everyone in this channel.
|
|
||||||
func (channel *Channel) SplitNotice(msgid string, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message utils.SplitMessage, rb *ResponseBuffer) {
|
|
||||||
channel.sendSplitMessage(msgid, "NOTICE", history.Notice, minPrefix, clientOnlyTags, client, &message, rb)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.ItemType, minPrefix *modes.Mode, clientOnlyTags *map[string]ircmsg.TagValue, client *Client, message *utils.SplitMessage, rb *ResponseBuffer) {
|
|
||||||
if !channel.CanSpeak(client) {
|
|
||||||
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// for STATUSMSG
|
|
||||||
var minPrefixMode modes.Mode
|
|
||||||
if minPrefix != nil {
|
|
||||||
minPrefixMode = *minPrefix
|
|
||||||
}
|
|
||||||
// send echo-message
|
|
||||||
if client.capabilities.Has(caps.EchoMessage) {
|
|
||||||
var tagsToUse *map[string]ircmsg.TagValue
|
|
||||||
if client.capabilities.Has(caps.MessageTags) {
|
if client.capabilities.Has(caps.MessageTags) {
|
||||||
tagsToUse = clientOnlyTags
|
tagsToUse = clientOnlyTags
|
||||||
}
|
}
|
||||||
nickMaskString := client.NickMaskString()
|
nickMaskString := client.NickMaskString()
|
||||||
accountName := client.AccountName()
|
accountName := client.AccountName()
|
||||||
if message == nil {
|
if command == "TAGMSG" && client.capabilities.Has(caps.MessageTags) {
|
||||||
rb.AddFromClient(msgid, nickMaskString, accountName, tagsToUse, cmd, channel.name)
|
rb.AddFromClient(message.Msgid, nickMaskString, accountName, tagsToUse, command, channel.name)
|
||||||
} else {
|
} else {
|
||||||
rb.AddSplitMessageFromClient(msgid, nickMaskString, accountName, tagsToUse, cmd, channel.name, *message)
|
rb.AddSplitMessageFromClient(nickMaskString, accountName, tagsToUse, command, channel.name, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -832,22 +772,23 @@ func (channel *Channel) sendSplitMessage(msgid, cmd string, histType history.Ite
|
|||||||
if member == client {
|
if member == client {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var tagsToUse *map[string]ircmsg.TagValue
|
var tagsToUse map[string]string
|
||||||
if member.capabilities.Has(caps.MessageTags) {
|
if member.capabilities.Has(caps.MessageTags) {
|
||||||
tagsToUse = clientOnlyTags
|
tagsToUse = clientOnlyTags
|
||||||
|
} else if command == "TAGMSG" {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if message == nil {
|
if command == "TAGMSG" {
|
||||||
member.sendFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name)
|
member.sendFromClientInternal(false, now, message.Msgid, nickmask, account, tagsToUse, command, channel.name)
|
||||||
} else {
|
} else {
|
||||||
member.sendSplitMsgFromClientInternal(false, now, msgid, nickmask, account, tagsToUse, cmd, channel.name, *message)
|
member.sendSplitMsgFromClientInternal(false, now, nickmask, account, tagsToUse, command, channel.name, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.history.Add(history.Item{
|
channel.history.Add(history.Item{
|
||||||
Type: histType,
|
Type: histType,
|
||||||
Msgid: msgid,
|
Message: message,
|
||||||
Message: *message,
|
|
||||||
Nick: nickmask,
|
Nick: nickmask,
|
||||||
AccountName: account,
|
AccountName: account,
|
||||||
Time: now,
|
Time: now,
|
||||||
@ -980,12 +921,14 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
|
|||||||
member.Send(nil, clientMask, "KICK", channel.name, targetNick, comment)
|
member.Send(nil, clientMask, "KICK", channel.name, targetNick, comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message := utils.SplitMessage{}
|
||||||
|
message.Message = comment
|
||||||
|
message.Msgid = targetNick // XXX abuse this field
|
||||||
channel.history.Add(history.Item{
|
channel.history.Add(history.Item{
|
||||||
Type: history.Kick,
|
Type: history.Kick,
|
||||||
Nick: clientMask,
|
Nick: clientMask,
|
||||||
Message: utils.MakeSplitMessage(comment, true),
|
|
||||||
AccountName: target.AccountName(),
|
AccountName: target.AccountName(),
|
||||||
Msgid: targetNick, // XXX abuse this field
|
Message: message,
|
||||||
})
|
})
|
||||||
|
|
||||||
channel.Quit(target)
|
channel.Quit(target)
|
||||||
|
132
irc/client.go
132
irc/client.go
@ -62,14 +62,13 @@ type Client struct {
|
|||||||
hasQuit bool
|
hasQuit bool
|
||||||
hops int
|
hops int
|
||||||
hostname string
|
hostname string
|
||||||
idletimer *IdleTimer
|
idletimer IdleTimer
|
||||||
invitedTo map[string]bool
|
invitedTo map[string]bool
|
||||||
isDestroyed bool
|
isDestroyed bool
|
||||||
isTor bool
|
isTor bool
|
||||||
isQuitting bool
|
isQuitting bool
|
||||||
languages []string
|
languages []string
|
||||||
loginThrottle connection_limits.GenericThrottle
|
loginThrottle connection_limits.GenericThrottle
|
||||||
maxlenTags uint32
|
|
||||||
maxlenRest uint32
|
maxlenRest uint32
|
||||||
nick string
|
nick string
|
||||||
nickCasefolded string
|
nickCasefolded string
|
||||||
@ -122,8 +121,9 @@ type ClientDetails struct {
|
|||||||
func RunNewClient(server *Server, conn clientConn) {
|
func RunNewClient(server *Server, conn clientConn) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
config := server.Config()
|
config := server.Config()
|
||||||
fullLineLenLimit := config.Limits.LineLen.Tags + config.Limits.LineLen.Rest
|
fullLineLenLimit := ircmsg.MaxlenTagsFromClient + config.Limits.LineLen.Rest
|
||||||
socket := NewSocket(conn.Conn, fullLineLenLimit*2, config.Server.MaxSendQBytes)
|
// give them 1k of grace over the limit:
|
||||||
|
socket := NewSocket(conn.Conn, fullLineLenLimit+1024, config.Server.MaxSendQBytes)
|
||||||
client := &Client{
|
client := &Client{
|
||||||
atime: now,
|
atime: now,
|
||||||
capabilities: caps.NewSet(),
|
capabilities: caps.NewSet(),
|
||||||
@ -246,30 +246,21 @@ func (client *Client) IPString() string {
|
|||||||
// command goroutine
|
// command goroutine
|
||||||
//
|
//
|
||||||
|
|
||||||
func (client *Client) recomputeMaxlens() (int, int) {
|
func (client *Client) recomputeMaxlens() int {
|
||||||
maxlenTags := 512
|
|
||||||
maxlenRest := 512
|
maxlenRest := 512
|
||||||
if client.capabilities.Has(caps.MessageTags) {
|
|
||||||
maxlenTags = 4096
|
|
||||||
}
|
|
||||||
if client.capabilities.Has(caps.MaxLine) {
|
if client.capabilities.Has(caps.MaxLine) {
|
||||||
limits := client.server.Limits()
|
maxlenRest = client.server.Limits().LineLen.Rest
|
||||||
if limits.LineLen.Tags > maxlenTags {
|
|
||||||
maxlenTags = limits.LineLen.Tags
|
|
||||||
}
|
|
||||||
maxlenRest = limits.LineLen.Rest
|
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.StoreUint32(&client.maxlenTags, uint32(maxlenTags))
|
|
||||||
atomic.StoreUint32(&client.maxlenRest, uint32(maxlenRest))
|
atomic.StoreUint32(&client.maxlenRest, uint32(maxlenRest))
|
||||||
|
|
||||||
return maxlenTags, maxlenRest
|
return maxlenRest
|
||||||
}
|
}
|
||||||
|
|
||||||
// allow these negotiated length limits to be read without locks; this is a convenience
|
// allow these negotiated length limits to be read without locks; this is a convenience
|
||||||
// so that Client.Send doesn't have to acquire any Client locks
|
// so that Client.Send doesn't have to acquire any Client locks
|
||||||
func (client *Client) maxlens() (int, int) {
|
func (client *Client) MaxlenRest() int {
|
||||||
return int(atomic.LoadUint32(&client.maxlenTags)), int(atomic.LoadUint32(&client.maxlenRest))
|
return int(atomic.LoadUint32(&client.maxlenRest))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) run() {
|
func (client *Client) run() {
|
||||||
@ -292,8 +283,7 @@ func (client *Client) run() {
|
|||||||
client.destroy(false)
|
client.destroy(false)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
client.idletimer = NewIdleTimer(client)
|
client.idletimer.Initialize(client)
|
||||||
client.idletimer.Start()
|
|
||||||
|
|
||||||
client.nickTimer.Initialize(client)
|
client.nickTimer.Initialize(client)
|
||||||
|
|
||||||
@ -302,7 +292,7 @@ func (client *Client) run() {
|
|||||||
firstLine := true
|
firstLine := true
|
||||||
|
|
||||||
for {
|
for {
|
||||||
maxlenTags, maxlenRest := client.recomputeMaxlens()
|
maxlenRest := client.recomputeMaxlens()
|
||||||
|
|
||||||
line, err = client.socket.Read()
|
line, err = client.socket.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -331,9 +321,12 @@ func (client *Client) run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
|
msg, err = ircmsg.ParseLineStrict(line, true, maxlenRest)
|
||||||
if err == ircmsg.ErrorLineIsEmpty {
|
if err == ircmsg.ErrorLineIsEmpty {
|
||||||
continue
|
continue
|
||||||
|
} else if err == ircmsg.ErrorLineTooLong {
|
||||||
|
client.Send(nil, client.server.name, ERR_INPUTTOOLONG, client.nick, client.t("Input line too long"))
|
||||||
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
client.Quit(client.t("Received malformed line"))
|
client.Quit(client.t("Received malformed line"))
|
||||||
break
|
break
|
||||||
@ -539,11 +532,11 @@ func (client *Client) replayPrivmsgHistory(rb *ResponseBuffer, items []history.I
|
|||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var tags Tags
|
var tags map[string]string
|
||||||
if serverTime {
|
if serverTime {
|
||||||
tags = ensureTag(tags, "time", item.Time.Format(IRCv3TimestampFormat))
|
tags = map[string]string{"time": item.Time.Format(IRCv3TimestampFormat)}
|
||||||
}
|
}
|
||||||
rb.AddSplitMessageFromClient(item.Msgid, item.Nick, item.AccountName, tags, command, nick, item.Message)
|
rb.AddSplitMessageFromClient(item.Nick, item.AccountName, tags, command, nick, item.Message)
|
||||||
}
|
}
|
||||||
if !complete {
|
if !complete {
|
||||||
rb.Add(nil, "HistServ", "NOTICE", nick, client.t("Some additional message history may have been lost"))
|
rb.Add(nil, "HistServ", "NOTICE", nick, client.t("Some additional message history may have been lost"))
|
||||||
@ -841,17 +834,18 @@ func (client *Client) Quit(message string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var quitLine string
|
var finalData []byte
|
||||||
// #364: don't send QUIT lines to unregistered clients
|
// #364: don't send QUIT lines to unregistered clients
|
||||||
if registered {
|
if registered {
|
||||||
quitMsg := ircmsg.MakeMessage(nil, prefix, "QUIT", message)
|
quitMsg := ircmsg.MakeMessage(nil, prefix, "QUIT", message)
|
||||||
quitLine, _ = quitMsg.Line()
|
finalData, _ = quitMsg.LineBytesStrict(false, 512)
|
||||||
}
|
}
|
||||||
|
|
||||||
errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message)
|
errorMsg := ircmsg.MakeMessage(nil, "", "ERROR", message)
|
||||||
errorLine, _ := errorMsg.Line()
|
errorMsgBytes, _ := errorMsg.LineBytesStrict(false, 512)
|
||||||
|
finalData = append(finalData, errorMsgBytes...)
|
||||||
|
|
||||||
client.socket.SetFinalData(quitLine + errorLine)
|
client.socket.SetFinalData(finalData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy gets rid of a client, removes them from server lists etc.
|
// destroy gets rid of a client, removes them from server lists etc.
|
||||||
@ -962,50 +956,45 @@ func (client *Client) destroy(beingResumed bool) {
|
|||||||
|
|
||||||
// SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
|
// SendSplitMsgFromClient sends an IRC PRIVMSG/NOTICE coming from a specific client.
|
||||||
// Adds account-tag to the line as well.
|
// Adds account-tag to the line as well.
|
||||||
func (client *Client) SendSplitMsgFromClient(msgid string, from *Client, tags Tags, command, target string, message utils.SplitMessage) {
|
func (client *Client) SendSplitMsgFromClient(from *Client, tags map[string]string, command, target string, message utils.SplitMessage) {
|
||||||
client.sendSplitMsgFromClientInternal(false, time.Time{}, msgid, from.NickMaskString(), from.AccountName(), tags, command, target, message)
|
client.sendSplitMsgFromClientInternal(false, time.Time{}, from.NickMaskString(), from.AccountName(), tags, command, target, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) sendSplitMsgFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags Tags, command, target string, message utils.SplitMessage) {
|
func (client *Client) sendSplitMsgFromClientInternal(blocking bool, serverTime time.Time, nickmask, accountName string, tags map[string]string, command, target string, message utils.SplitMessage) {
|
||||||
if client.capabilities.Has(caps.MaxLine) || message.Wrapped == nil {
|
if client.capabilities.Has(caps.MaxLine) || message.Wrapped == nil {
|
||||||
client.sendFromClientInternal(blocking, serverTime, msgid, nickmask, accountName, tags, command, target, message.Original)
|
client.sendFromClientInternal(blocking, serverTime, message.Msgid, nickmask, accountName, tags, command, target, message.Message)
|
||||||
} else {
|
} else {
|
||||||
for _, str := range message.Wrapped {
|
for _, messagePair := range message.Wrapped {
|
||||||
client.sendFromClientInternal(blocking, serverTime, msgid, nickmask, accountName, tags, command, target, str)
|
client.sendFromClientInternal(blocking, serverTime, messagePair.Msgid, nickmask, accountName, tags, command, target, messagePair.Message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendFromClient sends an IRC line coming from a specific client.
|
// SendFromClient sends an IRC line coming from a specific client.
|
||||||
// Adds account-tag to the line as well.
|
// Adds account-tag to the line as well.
|
||||||
func (client *Client) SendFromClient(msgid string, from *Client, tags Tags, command string, params ...string) error {
|
func (client *Client) SendFromClient(msgid string, from *Client, tags map[string]string, command string, params ...string) error {
|
||||||
return client.sendFromClientInternal(false, time.Time{}, msgid, from.NickMaskString(), from.AccountName(), tags, command, params...)
|
return client.sendFromClientInternal(false, time.Time{}, msgid, from.NickMaskString(), from.AccountName(), tags, command, params...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper to add a tag to `tags` (or create a new tag set if the current one is nil)
|
// this is SendFromClient, but directly exposing nickmask and accountName,
|
||||||
func ensureTag(tags Tags, tagName, tagValue string) (result Tags) {
|
// for things like history replay and CHGHOST where they no longer (necessarily)
|
||||||
if tags == nil {
|
// correspond to the current state of a client
|
||||||
result = ircmsg.MakeTags(tagName, tagValue)
|
func (client *Client) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags map[string]string, command string, params ...string) error {
|
||||||
} else {
|
msg := ircmsg.MakeMessage(tags, nickmask, command, params...)
|
||||||
result = tags
|
|
||||||
(*tags)[tagName] = ircmsg.MakeTagValue(tagValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX this is a hack where we allow overriding the client's nickmask
|
|
||||||
// this is to support CHGHOST, which requires that we send the *original* nickmask with the response
|
|
||||||
func (client *Client) sendFromClientInternal(blocking bool, serverTime time.Time, msgid string, nickmask, accountName string, tags Tags, command string, params ...string) error {
|
|
||||||
// attach account-tag
|
// attach account-tag
|
||||||
if client.capabilities.Has(caps.AccountTag) && accountName != "*" {
|
if client.capabilities.Has(caps.AccountTag) && accountName != "*" {
|
||||||
tags = ensureTag(tags, "account", accountName)
|
msg.SetTag("account", accountName)
|
||||||
}
|
}
|
||||||
// attach message-id
|
// attach message-id
|
||||||
if len(msgid) > 0 && client.capabilities.Has(caps.MessageTags) {
|
if msgid != "" && client.capabilities.Has(caps.MessageTags) {
|
||||||
tags = ensureTag(tags, "draft/msgid", msgid)
|
msg.SetTag("draft/msgid", msgid)
|
||||||
|
}
|
||||||
|
// attach server-time
|
||||||
|
if client.capabilities.Has(caps.ServerTime) {
|
||||||
|
msg.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.sendInternal(blocking, serverTime, tags, nickmask, command, params...)
|
return client.SendRawMessage(msg, blocking)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -1025,24 +1014,24 @@ var (
|
|||||||
func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) error {
|
func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) error {
|
||||||
// use dumb hack to force the last param to be a trailing param if required
|
// use dumb hack to force the last param to be a trailing param if required
|
||||||
var usedTrailingHack bool
|
var usedTrailingHack bool
|
||||||
if commandsThatMustUseTrailing[strings.ToUpper(message.Command)] && len(message.Params) > 0 {
|
if commandsThatMustUseTrailing[message.Command] && len(message.Params) > 0 {
|
||||||
lastParam := message.Params[len(message.Params)-1]
|
lastParam := message.Params[len(message.Params)-1]
|
||||||
// to force trailing, we ensure the final param contains a space
|
// to force trailing, we ensure the final param contains a space
|
||||||
if !strings.Contains(lastParam, " ") {
|
if strings.IndexByte(lastParam, ' ') == -1 {
|
||||||
message.Params[len(message.Params)-1] = lastParam + " "
|
message.Params[len(message.Params)-1] = lastParam + " "
|
||||||
usedTrailingHack = true
|
usedTrailingHack = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// assemble message
|
// assemble message
|
||||||
maxlenTags, maxlenRest := client.maxlens()
|
maxlenRest := client.MaxlenRest()
|
||||||
line, err := message.LineMaxLenBytes(maxlenTags, maxlenRest)
|
line, err := message.LineBytesStrict(false, maxlenRest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack())
|
logline := fmt.Sprintf("Error assembling message for sending: %v\n%s", err, debug.Stack())
|
||||||
client.server.logger.Error("internal", logline)
|
client.server.logger.Error("internal", logline)
|
||||||
|
|
||||||
message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
|
message = ircmsg.MakeMessage(nil, client.server.name, ERR_UNKNOWNERROR, "*", "Error assembling message for sending")
|
||||||
line, _ := message.LineBytes()
|
line, _ := message.LineBytesStrict(false, 0)
|
||||||
|
|
||||||
if blocking {
|
if blocking {
|
||||||
client.socket.BlockingWrite(line)
|
client.socket.BlockingWrite(line)
|
||||||
@ -1054,7 +1043,7 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) e
|
|||||||
|
|
||||||
// if we used the trailing hack, we need to strip the final space we appended earlier on
|
// if we used the trailing hack, we need to strip the final space we appended earlier on
|
||||||
if usedTrailingHack {
|
if usedTrailingHack {
|
||||||
copy(line[len(line)-3:], []byte{'\r', '\n'})
|
copy(line[len(line)-3:], "\r\n")
|
||||||
line = line[:len(line)-1]
|
line = line[:len(line)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1070,24 +1059,13 @@ func (client *Client) SendRawMessage(message ircmsg.IrcMessage, blocking bool) e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) sendInternal(blocking bool, serverTime time.Time, tags Tags, prefix string, command string, params ...string) error {
|
|
||||||
// attach server time
|
|
||||||
if client.capabilities.Has(caps.ServerTime) {
|
|
||||||
if serverTime.IsZero() {
|
|
||||||
serverTime = time.Now()
|
|
||||||
}
|
|
||||||
tags = ensureTag(tags, "time", serverTime.UTC().Format(IRCv3TimestampFormat))
|
|
||||||
}
|
|
||||||
|
|
||||||
// send out the message
|
|
||||||
message := ircmsg.MakeMessage(tags, prefix, command, params...)
|
|
||||||
client.SendRawMessage(message, blocking)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send sends an IRC line to the client.
|
// Send sends an IRC line to the client.
|
||||||
func (client *Client) Send(tags Tags, prefix string, command string, params ...string) error {
|
func (client *Client) Send(tags map[string]string, prefix string, command string, params ...string) error {
|
||||||
return client.sendInternal(false, time.Time{}, tags, prefix, command, params...)
|
msg := ircmsg.MakeMessage(tags, prefix, command, params...)
|
||||||
|
if client.capabilities.Has(caps.ServerTime) && !msg.HasTag("time") {
|
||||||
|
msg.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
|
||||||
|
}
|
||||||
|
return client.SendRawMessage(msg, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notice sends the client a notice from the server.
|
// Notice sends the client a notice from the server.
|
||||||
|
@ -202,7 +202,6 @@ type OperConfig struct {
|
|||||||
|
|
||||||
// LineLenConfig controls line lengths.
|
// LineLenConfig controls line lengths.
|
||||||
type LineLenLimits struct {
|
type LineLenLimits struct {
|
||||||
Tags int
|
|
||||||
Rest int
|
Rest int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -553,9 +552,6 @@ func LoadConfig(filename string) (config *Config, err error) {
|
|||||||
}
|
}
|
||||||
config.Server.WebIRC = newWebIRC
|
config.Server.WebIRC = newWebIRC
|
||||||
// process limits
|
// process limits
|
||||||
if config.Limits.LineLen.Tags < 512 {
|
|
||||||
config.Limits.LineLen.Tags = 512
|
|
||||||
}
|
|
||||||
if config.Limits.LineLen.Rest < 512 {
|
if config.Limits.LineLen.Rest < 512 {
|
||||||
config.Limits.LineLen.Rest = 512
|
config.Limits.LineLen.Rest = 512
|
||||||
}
|
}
|
||||||
|
@ -1874,7 +1874,7 @@ func nickHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
|
|||||||
|
|
||||||
// NOTICE <target>{,<target>} <message>
|
// NOTICE <target>{,<target>} <message>
|
||||||
func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
||||||
clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
|
clientOnlyTags := msg.ClientOnlyTags()
|
||||||
targets := strings.Split(msg.Params[0], ",")
|
targets := strings.Split(msg.Params[0], ",")
|
||||||
message := msg.Params[1]
|
message := msg.Params[1]
|
||||||
|
|
||||||
@ -1883,7 +1883,6 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// split privmsg
|
|
||||||
splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
|
splitMsg := utils.MakeSplitMessage(message, !client.capabilities.Has(caps.MaxLine))
|
||||||
|
|
||||||
for i, targetString := range targets {
|
for i, targetString := range targets {
|
||||||
@ -1905,8 +1904,7 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
|||||||
// errors silently ignored with NOTICE as per RFC
|
// errors silently ignored with NOTICE as per RFC
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msgid := server.generateMessageID()
|
channel.SendSplitMessage("NOTICE", lowestPrefix, clientOnlyTags, client, splitMsg, rb)
|
||||||
channel.SplitNotice(msgid, lowestPrefix, clientOnlyTags, client, splitMsg, rb)
|
|
||||||
} else {
|
} else {
|
||||||
target, err := CasefoldName(targetString)
|
target, err := CasefoldName(targetString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1926,23 +1924,21 @@ func noticeHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
|||||||
if !user.capabilities.Has(caps.MessageTags) {
|
if !user.capabilities.Has(caps.MessageTags) {
|
||||||
clientOnlyTags = nil
|
clientOnlyTags = nil
|
||||||
}
|
}
|
||||||
msgid := server.generateMessageID()
|
|
||||||
// restrict messages appropriately when +R is set
|
// restrict messages appropriately when +R is set
|
||||||
// intentionally make the sending user think the message went through fine
|
// intentionally make the sending user think the message went through fine
|
||||||
allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
|
allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
|
||||||
allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
|
allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
|
||||||
if allowedPlusR && allowedTor {
|
if allowedPlusR && allowedTor {
|
||||||
user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
|
user.SendSplitMsgFromClient(client, clientOnlyTags, "NOTICE", user.nick, splitMsg)
|
||||||
}
|
}
|
||||||
nickMaskString := client.NickMaskString()
|
nickMaskString := client.NickMaskString()
|
||||||
accountName := client.AccountName()
|
accountName := client.AccountName()
|
||||||
if client.capabilities.Has(caps.EchoMessage) {
|
if client.capabilities.Has(caps.EchoMessage) {
|
||||||
rb.AddSplitMessageFromClient(msgid, nickMaskString, accountName, clientOnlyTags, "NOTICE", user.nick, splitMsg)
|
rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "NOTICE", user.nick, splitMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
user.history.Add(history.Item{
|
user.history.Add(history.Item{
|
||||||
Type: history.Notice,
|
Type: history.Notice,
|
||||||
Msgid: msgid,
|
|
||||||
Message: splitMsg,
|
Message: splitMsg,
|
||||||
Nick: nickMaskString,
|
Nick: nickMaskString,
|
||||||
AccountName: accountName,
|
AccountName: accountName,
|
||||||
@ -2096,7 +2092,7 @@ func isRestrictedCTCPMessage(message string) bool {
|
|||||||
|
|
||||||
// PRIVMSG <target>{,<target>} <message>
|
// PRIVMSG <target>{,<target>} <message>
|
||||||
func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
||||||
clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
|
clientOnlyTags := msg.ClientOnlyTags()
|
||||||
targets := strings.Split(msg.Params[0], ",")
|
targets := strings.Split(msg.Params[0], ",")
|
||||||
message := msg.Params[1]
|
message := msg.Params[1]
|
||||||
|
|
||||||
@ -2133,8 +2129,7 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
|
|||||||
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msgid := server.generateMessageID()
|
channel.SendSplitMessage("PRIVMSG", lowestPrefix, clientOnlyTags, client, splitMsg, rb)
|
||||||
channel.SplitPrivMsg(msgid, lowestPrefix, clientOnlyTags, client, splitMsg, rb)
|
|
||||||
} else {
|
} else {
|
||||||
target, err = CasefoldName(targetString)
|
target, err = CasefoldName(targetString)
|
||||||
if service, isService := OragonoServices[target]; isService {
|
if service, isService := OragonoServices[target]; isService {
|
||||||
@ -2151,18 +2146,17 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
|
|||||||
if !user.capabilities.Has(caps.MessageTags) {
|
if !user.capabilities.Has(caps.MessageTags) {
|
||||||
clientOnlyTags = nil
|
clientOnlyTags = nil
|
||||||
}
|
}
|
||||||
msgid := server.generateMessageID()
|
|
||||||
// restrict messages appropriately when +R is set
|
// restrict messages appropriately when +R is set
|
||||||
// intentionally make the sending user think the message went through fine
|
// intentionally make the sending user think the message went through fine
|
||||||
allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
|
allowedPlusR := !user.HasMode(modes.RegisteredOnly) || client.LoggedIntoAccount()
|
||||||
allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
|
allowedTor := !user.isTor || !isRestrictedCTCPMessage(message)
|
||||||
if allowedPlusR && allowedTor {
|
if allowedPlusR && allowedTor {
|
||||||
user.SendSplitMsgFromClient(msgid, client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
|
user.SendSplitMsgFromClient(client, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
|
||||||
}
|
}
|
||||||
nickMaskString := client.NickMaskString()
|
nickMaskString := client.NickMaskString()
|
||||||
accountName := client.AccountName()
|
accountName := client.AccountName()
|
||||||
if client.capabilities.Has(caps.EchoMessage) {
|
if client.capabilities.Has(caps.EchoMessage) {
|
||||||
rb.AddSplitMessageFromClient(msgid, nickMaskString, accountName, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
|
rb.AddSplitMessageFromClient(nickMaskString, accountName, clientOnlyTags, "PRIVMSG", user.nick, splitMsg)
|
||||||
}
|
}
|
||||||
if user.HasMode(modes.Away) {
|
if user.HasMode(modes.Away) {
|
||||||
//TODO(dan): possibly implement cooldown of away notifications to users
|
//TODO(dan): possibly implement cooldown of away notifications to users
|
||||||
@ -2171,7 +2165,6 @@ func privmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
|
|||||||
|
|
||||||
user.history.Add(history.Item{
|
user.history.Add(history.Item{
|
||||||
Type: history.Privmsg,
|
Type: history.Privmsg,
|
||||||
Msgid: msgid,
|
|
||||||
Message: splitMsg,
|
Message: splitMsg,
|
||||||
Nick: nickMaskString,
|
Nick: nickMaskString,
|
||||||
AccountName: accountName,
|
AccountName: accountName,
|
||||||
@ -2353,7 +2346,7 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *R
|
|||||||
|
|
||||||
// TAGMSG <target>{,<target>}
|
// TAGMSG <target>{,<target>}
|
||||||
func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool {
|
||||||
clientOnlyTags := utils.GetClientOnlyTags(msg.Tags)
|
clientOnlyTags := msg.ClientOnlyTags()
|
||||||
// no client-only tags, so we can drop it
|
// no client-only tags, so we can drop it
|
||||||
if clientOnlyTags == nil {
|
if clientOnlyTags == nil {
|
||||||
return false
|
return false
|
||||||
@ -2362,6 +2355,7 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
|||||||
targets := strings.Split(msg.Params[0], ",")
|
targets := strings.Split(msg.Params[0], ",")
|
||||||
|
|
||||||
cnick := client.Nick()
|
cnick := client.Nick()
|
||||||
|
message := utils.MakeSplitMessage("", true) // assign consistent message ID
|
||||||
for i, targetString := range targets {
|
for i, targetString := range targets {
|
||||||
// max of four targets per privmsg
|
// max of four targets per privmsg
|
||||||
if i > maxTargets-1 {
|
if i > maxTargets-1 {
|
||||||
@ -2386,9 +2380,7 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
|||||||
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
rb.Add(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, client.t("Cannot send to channel"))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msgid := server.generateMessageID()
|
channel.SendSplitMessage("TAGMSG", lowestPrefix, clientOnlyTags, client, message, rb)
|
||||||
|
|
||||||
channel.TagMsg(msgid, lowestPrefix, clientOnlyTags, client, rb)
|
|
||||||
} else {
|
} else {
|
||||||
target, err = CasefoldName(targetString)
|
target, err = CasefoldName(targetString)
|
||||||
user := server.clients.Get(target)
|
user := server.clients.Get(target)
|
||||||
@ -2398,19 +2390,19 @@ func tagmsgHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Re
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msgid := server.generateMessageID()
|
|
||||||
|
|
||||||
// end user can't receive tagmsgs
|
// end user can't receive tagmsgs
|
||||||
if !user.capabilities.Has(caps.MessageTags) {
|
if !user.capabilities.Has(caps.MessageTags) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
user.SendFromClient(msgid, client, clientOnlyTags, "TAGMSG", user.nick)
|
unick := user.Nick()
|
||||||
|
user.SendSplitMsgFromClient(client, clientOnlyTags, "TAGMSG", unick, message)
|
||||||
if client.capabilities.Has(caps.EchoMessage) {
|
if client.capabilities.Has(caps.EchoMessage) {
|
||||||
rb.AddFromClient(msgid, client.NickMaskString(), client.AccountName(), clientOnlyTags, "TAGMSG", user.nick)
|
rb.AddSplitMessageFromClient(client.NickMaskString(), client.AccountName(), clientOnlyTags, "TAGMSG", unick, message)
|
||||||
}
|
}
|
||||||
if user.HasMode(modes.Away) {
|
if user.HasMode(modes.Away) {
|
||||||
//TODO(dan): possibly implement cooldown of away notifications to users
|
//TODO(dan): possibly implement cooldown of away notifications to users
|
||||||
rb.Add(nil, server.name, RPL_AWAY, cnick, user.Nick(), user.AwayMessage())
|
rb.Add(nil, server.name, RPL_AWAY, cnick, unick, user.AwayMessage())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ const (
|
|||||||
Kick
|
Kick
|
||||||
Quit
|
Quit
|
||||||
Mode
|
Mode
|
||||||
|
Tagmsg
|
||||||
)
|
)
|
||||||
|
|
||||||
// Item represents an event (e.g., a PRIVMSG or a JOIN) and its associated data
|
// Item represents an event (e.g., a PRIVMSG or a JOIN) and its associated data
|
||||||
@ -33,14 +34,13 @@ type Item struct {
|
|||||||
AccountName string
|
AccountName string
|
||||||
Message utils.SplitMessage
|
Message utils.SplitMessage
|
||||||
// for non-privmsg items, we may stuff some other data in here
|
// for non-privmsg items, we may stuff some other data in here
|
||||||
Msgid string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasMsgid tests whether a message has the message id `msgid`.
|
// HasMsgid tests whether a message has the message id `msgid`.
|
||||||
func (item *Item) HasMsgid(msgid string) bool {
|
func (item *Item) HasMsgid(msgid string) bool {
|
||||||
// XXX we stuff other data in the Msgid field sometimes,
|
// XXX we stuff other data in the Msgid field sometimes,
|
||||||
// don't match it by accident
|
// don't match it by accident
|
||||||
return (item.Type == Privmsg || item.Type == Notice) && item.Msgid == msgid
|
return (item.Type == Privmsg || item.Type == Notice) && item.Message.Msgid == msgid
|
||||||
}
|
}
|
||||||
|
|
||||||
type Predicate func(item Item) (matches bool)
|
type Predicate func(item Item) (matches bool)
|
||||||
|
@ -54,14 +54,17 @@ type IdleTimer struct {
|
|||||||
timer *time.Timer
|
timer *time.Timer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIdleTimer sets up a new IdleTimer using constant timeouts.
|
// Initialize sets up an IdleTimer and starts counting idle time;
|
||||||
func NewIdleTimer(client *Client) *IdleTimer {
|
// if there is no activity from the client, it will eventually be stopped.
|
||||||
it := IdleTimer{
|
func (it *IdleTimer) Initialize(client *Client) {
|
||||||
registerTimeout: RegisterTimeout,
|
it.client = client
|
||||||
client: client,
|
it.registerTimeout = RegisterTimeout
|
||||||
}
|
|
||||||
it.idleTimeout, it.quitTimeout = it.recomputeDurations()
|
it.idleTimeout, it.quitTimeout = it.recomputeDurations()
|
||||||
return &it
|
|
||||||
|
it.Lock()
|
||||||
|
defer it.Unlock()
|
||||||
|
it.state = TimerUnregistered
|
||||||
|
it.resetTimeout()
|
||||||
}
|
}
|
||||||
|
|
||||||
// recomputeDurations recomputes the idle and quit durations, given the client's caps.
|
// recomputeDurations recomputes the idle and quit durations, given the client's caps.
|
||||||
@ -82,15 +85,6 @@ func (it *IdleTimer) recomputeDurations() (idleTimeout, quitTimeout time.Duratio
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts counting idle time; if there is no activity from the client,
|
|
||||||
// it will eventually be stopped.
|
|
||||||
func (it *IdleTimer) Start() {
|
|
||||||
it.Lock()
|
|
||||||
defer it.Unlock()
|
|
||||||
it.state = TimerUnregistered
|
|
||||||
it.resetTimeout()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *IdleTimer) Touch() {
|
func (it *IdleTimer) Touch() {
|
||||||
idleTimeout, quitTimeout := it.recomputeDurations()
|
idleTimeout, quitTimeout := it.recomputeDurations()
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ func (lm *Manager) Translators() []string {
|
|||||||
tlist = append(tlist, fmt.Sprintf("%s (%s): %s", info.Name, info.Code, info.Contributors))
|
tlist = append(tlist, fmt.Sprintf("%s (%s): %s", info.Name, info.Code, info.Contributors))
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(tlist)
|
tlist.Sort()
|
||||||
return tlist
|
return tlist
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +228,7 @@ func (lm *Manager) Translate(languages []string, originalString string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lm *Manager) CapValue() string {
|
func (lm *Manager) CapValue() string {
|
||||||
langCodes := make([]string, len(lm.Languages)+1)
|
langCodes := make(sort.StringSlice, len(lm.Languages)+1)
|
||||||
langCodes[0] = strconv.Itoa(len(lm.Languages))
|
langCodes[0] = strconv.Itoa(len(lm.Languages))
|
||||||
i := 1
|
i := 1
|
||||||
for _, info := range lm.Languages {
|
for _, info := range lm.Languages {
|
||||||
@ -239,5 +239,6 @@ func (lm *Manager) CapValue() string {
|
|||||||
langCodes[i] = codeToken
|
langCodes[i] = codeToken
|
||||||
i += 1
|
i += 1
|
||||||
}
|
}
|
||||||
|
langCodes.Sort()
|
||||||
return strings.Join(langCodes, ",")
|
return strings.Join(langCodes, ",")
|
||||||
}
|
}
|
||||||
|
@ -119,6 +119,7 @@ const (
|
|||||||
ERR_NOTOPLEVEL = "413"
|
ERR_NOTOPLEVEL = "413"
|
||||||
ERR_WILDTOPLEVEL = "414"
|
ERR_WILDTOPLEVEL = "414"
|
||||||
ERR_BADMASK = "415"
|
ERR_BADMASK = "415"
|
||||||
|
ERR_INPUTTOOLONG = "417"
|
||||||
ERR_UNKNOWNCOMMAND = "421"
|
ERR_UNKNOWNCOMMAND = "421"
|
||||||
ERR_NOMOTD = "422"
|
ERR_NOMOTD = "422"
|
||||||
ERR_NOADMININFO = "423"
|
ERR_NOADMININFO = "423"
|
||||||
|
@ -32,7 +32,8 @@ type ResponseBuffer struct {
|
|||||||
|
|
||||||
// GetLabel returns the label from the given message.
|
// GetLabel returns the label from the given message.
|
||||||
func GetLabel(msg ircmsg.IrcMessage) string {
|
func GetLabel(msg ircmsg.IrcMessage) string {
|
||||||
return msg.Tags[caps.LabelTagName].Value
|
_, value := msg.GetTag(caps.LabelTagName)
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewResponseBuffer returns a new ResponseBuffer.
|
// NewResponseBuffer returns a new ResponseBuffer.
|
||||||
@ -42,8 +43,7 @@ func NewResponseBuffer(target *Client) *ResponseBuffer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a standard new message to our queue.
|
func (rb *ResponseBuffer) AddMessage(msg ircmsg.IrcMessage) {
|
||||||
func (rb *ResponseBuffer) Add(tags *map[string]ircmsg.TagValue, prefix string, command string, params ...string) {
|
|
||||||
if rb.finalized {
|
if rb.finalized {
|
||||||
rb.target.server.logger.Error("internal", "message added to finalized ResponseBuffer, undefined behavior")
|
rb.target.server.logger.Error("internal", "message added to finalized ResponseBuffer, undefined behavior")
|
||||||
debug.PrintStack()
|
debug.PrintStack()
|
||||||
@ -52,33 +52,38 @@ func (rb *ResponseBuffer) Add(tags *map[string]ircmsg.TagValue, prefix string, c
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
message := ircmsg.MakeMessage(tags, prefix, command, params...)
|
rb.messages = append(rb.messages, msg)
|
||||||
rb.messages = append(rb.messages, message)
|
}
|
||||||
|
|
||||||
|
// Add adds a standard new message to our queue.
|
||||||
|
func (rb *ResponseBuffer) Add(tags map[string]string, prefix string, command string, params ...string) {
|
||||||
|
rb.AddMessage(ircmsg.MakeMessage(tags, prefix, command, params...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFromClient adds a new message from a specific client to our queue.
|
// AddFromClient adds a new message from a specific client to our queue.
|
||||||
func (rb *ResponseBuffer) AddFromClient(msgid string, fromNickMask string, fromAccount string, tags *map[string]ircmsg.TagValue, command string, params ...string) {
|
func (rb *ResponseBuffer) AddFromClient(msgid string, fromNickMask string, fromAccount string, tags map[string]string, command string, params ...string) {
|
||||||
|
msg := ircmsg.MakeMessage(nil, fromNickMask, command, params...)
|
||||||
|
msg.UpdateTags(tags)
|
||||||
|
|
||||||
// attach account-tag
|
// attach account-tag
|
||||||
if rb.target.capabilities.Has(caps.AccountTag) {
|
if rb.target.capabilities.Has(caps.AccountTag) && fromAccount != "*" {
|
||||||
if fromAccount != "*" {
|
msg.SetTag("account", fromAccount)
|
||||||
tags = ensureTag(tags, "account", fromAccount)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// attach message-id
|
// attach message-id
|
||||||
if len(msgid) > 0 && rb.target.capabilities.Has(caps.MessageTags) {
|
if len(msgid) > 0 && rb.target.capabilities.Has(caps.MessageTags) {
|
||||||
tags = ensureTag(tags, "draft/msgid", msgid)
|
msg.SetTag("draft/msgid", msgid)
|
||||||
}
|
}
|
||||||
|
|
||||||
rb.Add(tags, fromNickMask, command, params...)
|
rb.AddMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSplitMessageFromClient adds a new split message from a specific client to our queue.
|
// AddSplitMessageFromClient adds a new split message from a specific client to our queue.
|
||||||
func (rb *ResponseBuffer) AddSplitMessageFromClient(msgid string, fromNickMask string, fromAccount string, tags *map[string]ircmsg.TagValue, command string, target string, message utils.SplitMessage) {
|
func (rb *ResponseBuffer) AddSplitMessageFromClient(fromNickMask string, fromAccount string, tags map[string]string, command string, target string, message utils.SplitMessage) {
|
||||||
if rb.target.capabilities.Has(caps.MaxLine) || message.Wrapped == nil {
|
if rb.target.capabilities.Has(caps.MaxLine) || message.Wrapped == nil {
|
||||||
rb.AddFromClient(msgid, fromNickMask, fromAccount, tags, command, target, message.Original)
|
rb.AddFromClient(message.Msgid, fromNickMask, fromAccount, tags, command, target, message.Message)
|
||||||
} else {
|
} else {
|
||||||
for _, str := range message.Wrapped {
|
for _, messagePair := range message.Wrapped {
|
||||||
rb.AddFromClient(msgid, fromNickMask, fromAccount, tags, command, target, str)
|
rb.AddFromClient(messagePair.Msgid, fromNickMask, fromAccount, tags, command, target, messagePair.Message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,7 +108,7 @@ func (rb *ResponseBuffer) sendBatchStart(batchType string, blocking bool) {
|
|||||||
|
|
||||||
message := ircmsg.MakeMessage(nil, rb.target.server.name, "BATCH", "+"+rb.batchID, batchType)
|
message := ircmsg.MakeMessage(nil, rb.target.server.name, "BATCH", "+"+rb.batchID, batchType)
|
||||||
if rb.Label != "" {
|
if rb.Label != "" {
|
||||||
message.Tags[caps.LabelTagName] = ircmsg.MakeTagValue(rb.Label)
|
message.SetTag(caps.LabelTagName, rb.Label)
|
||||||
}
|
}
|
||||||
rb.target.SendRawMessage(message, blocking)
|
rb.target.SendRawMessage(message, blocking)
|
||||||
}
|
}
|
||||||
@ -149,7 +154,7 @@ func (rb *ResponseBuffer) flushInternal(final bool, blocking bool) error {
|
|||||||
|
|
||||||
// if label but no batch, add label to first message
|
// if label but no batch, add label to first message
|
||||||
if useLabel && !useBatch && len(rb.messages) == 1 && rb.batchID == "" {
|
if useLabel && !useBatch && len(rb.messages) == 1 && rb.batchID == "" {
|
||||||
rb.messages[0].Tags[caps.LabelTagName] = ircmsg.MakeTagValue(rb.Label)
|
rb.messages[0].SetTag(caps.LabelTagName, rb.Label)
|
||||||
} else if useBatch {
|
} else if useBatch {
|
||||||
rb.sendBatchStart(defaultBatchType, blocking)
|
rb.sendBatchStart(defaultBatchType, blocking)
|
||||||
}
|
}
|
||||||
@ -157,16 +162,13 @@ func (rb *ResponseBuffer) flushInternal(final bool, blocking bool) error {
|
|||||||
// send each message out
|
// send each message out
|
||||||
for _, message := range rb.messages {
|
for _, message := range rb.messages {
|
||||||
// attach server-time if needed
|
// attach server-time if needed
|
||||||
if rb.target.capabilities.Has(caps.ServerTime) {
|
if rb.target.capabilities.Has(caps.ServerTime) && !message.HasTag("time") {
|
||||||
if !message.Tags["time"].HasValue {
|
message.SetTag("time", time.Now().UTC().Format(IRCv3TimestampFormat))
|
||||||
t := time.Now().UTC().Format(IRCv3TimestampFormat)
|
|
||||||
message.Tags["time"] = ircmsg.MakeTagValue(t)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// attach batch ID
|
// attach batch ID
|
||||||
if rb.batchID != "" {
|
if rb.batchID != "" {
|
||||||
message.Tags["batch"] = ircmsg.MakeTagValue(rb.batchID)
|
message.SetTag("batch", rb.batchID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// send message out
|
// send message out
|
||||||
|
@ -387,11 +387,6 @@ func (server *Server) createListener(addr string, tlsConfig *tls.Config, isTor b
|
|||||||
return &wrapper, nil
|
return &wrapper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// generateMessageID returns a network-unique message ID.
|
|
||||||
func (server *Server) generateMessageID() string {
|
|
||||||
return utils.GenerateSecretToken()
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// server functionality
|
// server functionality
|
||||||
//
|
//
|
||||||
@ -623,7 +618,7 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
|
|||||||
} else {
|
} else {
|
||||||
// enforce configs that can't be changed after launch:
|
// enforce configs that can't be changed after launch:
|
||||||
currentLimits := server.Limits()
|
currentLimits := server.Limits()
|
||||||
if currentLimits.LineLen.Tags != config.Limits.LineLen.Tags || currentLimits.LineLen.Rest != config.Limits.LineLen.Rest {
|
if currentLimits.LineLen.Rest != config.Limits.LineLen.Rest {
|
||||||
return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted")
|
return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted")
|
||||||
} else if server.name != config.Server.Name {
|
} else if server.name != config.Server.Name {
|
||||||
return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted")
|
return fmt.Errorf("Server name cannot be changed after launching the server, rehash aborted")
|
||||||
@ -703,9 +698,9 @@ func (server *Server) applyConfig(config *Config, initial bool) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MaxLine
|
// MaxLine
|
||||||
if config.Limits.LineLen.Tags != 512 || config.Limits.LineLen.Rest != 512 {
|
if config.Limits.LineLen.Rest != 512 {
|
||||||
SupportedCapabilities.Enable(caps.MaxLine)
|
SupportedCapabilities.Enable(caps.MaxLine)
|
||||||
value := fmt.Sprintf("%d,%d", config.Limits.LineLen.Tags, config.Limits.LineLen.Rest)
|
value := fmt.Sprintf("%d", config.Limits.LineLen.Rest)
|
||||||
CapValues.Set(caps.MaxLine, value)
|
CapValues.Set(caps.MaxLine, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ import (
|
|||||||
var (
|
var (
|
||||||
handshakeTimeout, _ = time.ParseDuration("5s")
|
handshakeTimeout, _ = time.ParseDuration("5s")
|
||||||
errSendQExceeded = errors.New("SendQ exceeded")
|
errSendQExceeded = errors.New("SendQ exceeded")
|
||||||
|
|
||||||
|
sendQExceededMessage = []byte("\r\nERROR :SendQ Exceeded\r\n")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Socket represents an IRC socket.
|
// Socket represents an IRC socket.
|
||||||
@ -38,7 +40,7 @@ type Socket struct {
|
|||||||
totalLength int
|
totalLength int
|
||||||
closed bool
|
closed bool
|
||||||
sendQExceeded bool
|
sendQExceeded bool
|
||||||
finalData string // what to send when we die
|
finalData []byte // what to send when we die
|
||||||
finalized bool
|
finalized bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +198,7 @@ func (socket *Socket) wakeWriter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetFinalData sets the final data to send when the SocketWriter closes.
|
// SetFinalData sets the final data to send when the SocketWriter closes.
|
||||||
func (socket *Socket) SetFinalData(data string) {
|
func (socket *Socket) SetFinalData(data []byte) {
|
||||||
socket.Lock()
|
socket.Lock()
|
||||||
defer socket.Unlock()
|
defer socket.Unlock()
|
||||||
socket.finalData = data
|
socket.finalData = data
|
||||||
@ -271,7 +273,7 @@ func (socket *Socket) finalize() {
|
|||||||
socket.finalized = true
|
socket.finalized = true
|
||||||
finalData := socket.finalData
|
finalData := socket.finalData
|
||||||
if socket.sendQExceeded {
|
if socket.sendQExceeded {
|
||||||
finalData = "\r\nERROR :SendQ Exceeded\r\n"
|
finalData = sendQExceededMessage
|
||||||
}
|
}
|
||||||
socket.Unlock()
|
socket.Unlock()
|
||||||
|
|
||||||
@ -279,8 +281,8 @@ func (socket *Socket) finalize() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if finalData != "" {
|
if len(finalData) != 0 {
|
||||||
socket.conn.Write([]byte(finalData))
|
socket.conn.Write(finalData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// close the connection
|
// close the connection
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import "github.com/oragono/oragono/irc/modes"
|
import "github.com/oragono/oragono/irc/modes"
|
||||||
import "github.com/goshuirc/irc-go/ircmsg"
|
|
||||||
|
|
||||||
// ClientSet is a set of clients.
|
// ClientSet is a set of clients.
|
||||||
type ClientSet map[*Client]bool
|
type ClientSet map[*Client]bool
|
||||||
@ -57,5 +56,3 @@ func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
|
|||||||
|
|
||||||
// ChannelSet is a set of channels.
|
// ChannelSet is a set of channels.
|
||||||
type ChannelSet map[*Channel]bool
|
type ChannelSet map[*Channel]bool
|
||||||
|
|
||||||
type Tags *map[string]ircmsg.TagValue
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
|
|
||||||
// released under the MIT license
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import "github.com/goshuirc/irc-go/ircmsg"
|
|
||||||
|
|
||||||
// GetClientOnlyTags takes a tag map and returns a map containing just the client-only tags from it.
|
|
||||||
func GetClientOnlyTags(tags map[string]ircmsg.TagValue) *map[string]ircmsg.TagValue {
|
|
||||||
if len(tags) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
clientOnlyTags := make(map[string]ircmsg.TagValue)
|
|
||||||
|
|
||||||
for name, value := range tags {
|
|
||||||
if len(name) > 1 && name[0] == '+' {
|
|
||||||
clientOnlyTags[name] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(clientOnlyTags) < 1 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &clientOnlyTags
|
|
||||||
}
|
|
@ -50,17 +50,32 @@ func WordWrap(text string, lineWidth int) []string {
|
|||||||
return lines
|
return lines
|
||||||
}
|
}
|
||||||
|
|
||||||
// SplitMessage represents a message that's been split for sending.
|
type MessagePair struct {
|
||||||
type SplitMessage struct {
|
Message string
|
||||||
Original string
|
Msgid string
|
||||||
Wrapped []string // if this is nil, Original didn't need wrapping and can be sent to anyone
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func MakeSplitMessage(original string, origIs512 bool) (result SplitMessage) {
|
// SplitMessage represents a message that's been split for sending.
|
||||||
result.Original = original
|
type SplitMessage struct {
|
||||||
|
MessagePair
|
||||||
|
Wrapped []MessagePair // if this is nil, `Message` didn't need wrapping and can be sent to anyone
|
||||||
|
}
|
||||||
|
|
||||||
if !origIs512 {
|
const defaultLineWidth = 400
|
||||||
result.Wrapped = WordWrap(original, 400)
|
|
||||||
|
func MakeSplitMessage(original string, origIs512 bool) (result SplitMessage) {
|
||||||
|
result.Message = original
|
||||||
|
result.Msgid = GenerateSecretToken()
|
||||||
|
|
||||||
|
if !origIs512 && defaultLineWidth < len(original) {
|
||||||
|
wrapped := WordWrap(original, defaultLineWidth)
|
||||||
|
result.Wrapped = make([]MessagePair, len(wrapped))
|
||||||
|
for i, wrappedMessage := range wrapped {
|
||||||
|
result.Wrapped[i] = MessagePair{
|
||||||
|
Message: wrappedMessage,
|
||||||
|
Msgid: GenerateSecretToken(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -487,10 +487,8 @@ limits:
|
|||||||
# maximum length of IRC lines
|
# maximum length of IRC lines
|
||||||
# this should generally be 1024-2048, and will only apply when negotiated by clients
|
# this should generally be 1024-2048, and will only apply when negotiated by clients
|
||||||
linelen:
|
linelen:
|
||||||
# tags section
|
# ratified version of the message-tags cap fixes the max tag length at 8191 bytes
|
||||||
tags: 2048
|
# configurable length for the rest of the message:
|
||||||
|
|
||||||
# rest of the message
|
|
||||||
rest: 2048
|
rest: 2048
|
||||||
|
|
||||||
# fakelag: prevents clients from spamming commands too rapidly
|
# fakelag: prevents clients from spamming commands too rapidly
|
||||||
|
2
vendor
2
vendor
@ -1 +1 @@
|
|||||||
Subproject commit 72043bab39044196e57bff1fe5e59c9ec81e59f3
|
Subproject commit 8ddbb531841add50f8b7aff8fe00bef311448aaa
|
Loading…
Reference in New Issue
Block a user