diff --git a/CHANGELOG.md b/CHANGELOG.md index b336608e..b4eef445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ This release also updates the database, so be sure to run the `oragono upgradedb ### Added * Added ability to ban IP addresses and networks from the server with the `DLINE` and `UNDLINE` commands. * Added alpha REST API (intended primarily for use with a future web interface to manage accounts, DLINEs, etc). +* Added proposed IRCv3 capability [`maxline`](https://github.com/ircv3/ircv3-specifications/pull/281). ### Changed * Database upgraded to make handling accounts simpler. diff --git a/irc/capability.go b/irc/capability.go index 8236b408..3cab24c9 100644 --- a/irc/capability.go +++ b/irc/capability.go @@ -22,6 +22,7 @@ const ( EchoMessage Capability = "echo-message" ExtendedJoin Capability = "extended-join" InviteNotify Capability = "invite-notify" + MaxLine Capability = "draft/maxline" MessageTags Capability = "draft/message-tags" MultiPrefix Capability = "multi-prefix" SASL Capability = "sasl" @@ -39,8 +40,9 @@ var ( EchoMessage: true, ExtendedJoin: true, InviteNotify: true, - MessageTags: true, - MultiPrefix: true, + // MaxLine is set during server startup + MessageTags: true, + MultiPrefix: true, // SASL is set during server startup ServerTime: true, UserhostInNames: true, diff --git a/irc/client.go b/irc/client.go index 0f457b08..2e8a8c51 100644 --- a/irc/client.go +++ b/irc/client.go @@ -152,7 +152,14 @@ func (client *Client) run() { break } - msg, err = ircmsg.ParseLine(line) + var maxLen int + if client.capabilities[MaxLine] { + maxLen = maxLineLength + } else { + maxLen = 512 + } + + msg, err = ircmsg.ParseLineMaxLen(line, maxLen) if err != nil { client.Quit("received malformed line") break @@ -505,9 +512,16 @@ func (client *Client) Send(tags *map[string]ircmsg.TagValue, prefix string, comm } } + var maxLen int + if client.capabilities[MaxLine] { + maxLen = maxLineLength + } else { + maxLen = 512 + } + // send out the message message := ircmsg.MakeMessage(tags, prefix, command, params...) - line, err := message.Line() + line, err := message.LineMaxLen(maxLen) if err != nil { // try not to fail quietly - especially useful when running tests, as a note to dig deeper // log.Println("Error assembling message:") diff --git a/irc/config.go b/irc/config.go index 4bf7e679..f840e8b9 100644 --- a/irc/config.go +++ b/irc/config.go @@ -151,14 +151,15 @@ type Config struct { Opers map[string]*OperConfig Limits struct { - NickLen uint `yaml:"nicklen"` - ChannelLen uint `yaml:"channellen"` AwayLen uint `yaml:"awaylen"` + ChanListModes uint `yaml:"chan-list-modes"` + ChannelLen uint `yaml:"channellen"` KickLen uint `yaml:"kicklen"` + LineLen uint `yaml:"linelen"` + MonitorEntries uint `yaml:"monitor-entries"` + NickLen uint `yaml:"nicklen"` TopicLen uint `yaml:"topiclen"` WhowasEntries uint `yaml:"whowas-entries"` - MonitorEntries uint `yaml:"monitor-entries"` - ChanListModes uint `yaml:"chan-list-modes"` } } @@ -335,6 +336,9 @@ func LoadConfig(filename string) (config *Config, err error) { return nil, fmt.Errorf("Could not parse connection-throttle ban-duration: %s", err.Error()) } } + if config.Limits.LineLen < 512 { + return nil, errors.New("Line length must be 512 or greater") + } return config, nil } diff --git a/irc/constants.go b/irc/constants.go index 771878e5..354be513 100644 --- a/irc/constants.go +++ b/irc/constants.go @@ -16,6 +16,8 @@ var ( // Ver is the full version of Oragono, used in responses to clients. Ver = fmt.Sprintf("oragono-%s", SemVer) + // Used as the standard maximum line length unless overridden at runtime. + maxLineLength = 512 // maxLastArgLength is used to simply cap off the final argument when creating general messages where we need to select a limit. // for instance, in MONITOR lists, RPL_ISUPPORT lists, etc. maxLastArgLength = 400 diff --git a/irc/server.go b/irc/server.go index 0bc1887d..d9f7f649 100644 --- a/irc/server.go +++ b/irc/server.go @@ -46,6 +46,7 @@ type Limits struct { NickLen int TopicLen int ChanListModes int + LineLen int } // ListenerInterface represents an interface for a listener. @@ -146,6 +147,12 @@ func NewServer(configFilename string, config *Config) *Server { SupportedCapabilities[SASL] = true } + if config.Limits.LineLen > 512 { + maxLineLength = int(config.Limits.LineLen) + SupportedCapabilities[MaxLine] = true + CapValues[MaxLine] = strconv.Itoa(int(config.Limits.LineLen)) + } + operClasses, err := config.OperatorClasses() if err != nil { log.Fatal("Error loading oper classes:", err.Error()) @@ -184,6 +191,7 @@ func NewServer(configFilename string, config *Config) *Server { NickLen: int(config.Limits.NickLen), TopicLen: int(config.Limits.TopicLen), ChanListModes: int(config.Limits.ChanListModes), + LineLen: int(config.Limits.LineLen), }, listeners: make(map[string]ListenerInterface), monitoring: make(map[string][]Client), @@ -1099,6 +1107,11 @@ func (server *Server) rehash() error { return fmt.Errorf("Error rehashing config file config: %s", err.Error()) } + // line lengths cannot be changed after launching the server + if maxLineLength != int(config.Limits.LineLen) { + return fmt.Errorf("Maximum line length (linelen) cannot be changed after launching the server, rehash aborted") + } + // confirm connectionLimits are fine connectionLimits, err := NewConnectionLimits(config.Server.ConnectionLimits) if err != nil { diff --git a/oragono.yaml b/oragono.yaml index e862df92..2875eb4f 100644 --- a/oragono.yaml +++ b/oragono.yaml @@ -186,13 +186,13 @@ limits: channellen: 64 # awaylen is the maximum length of an away message - awaylen: 200 + awaylen: 500 # kicklen is the maximum length of a kick message - kicklen: 390 + kicklen: 1000 # topiclen is the maximum length of a channel topic - topiclen: 390 + topiclen: 1000 # maximum number of monitor entries a client can have monitor-entries: 100 @@ -202,3 +202,6 @@ limits: # maximum length of channel lists (beI modes) chan-list-modes: 60 + + # maximum length of IRC lines + linelen: 2048