diff --git a/go.mod b/go.mod index 294f7eb5..2141c4bb 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/go-sql-driver/mysql v1.5.0 github.com/go-test/deep v1.0.6 // indirect github.com/gorilla/websocket v1.4.2 - github.com/goshuirc/irc-go v0.0.0-20210311004346-ea7a188a73fe + github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef github.com/onsi/ginkgo v1.12.0 // indirect github.com/onsi/gomega v1.9.0 // indirect github.com/oragono/confusables v0.0.0-20201108231250-4ab98ab61fb1 diff --git a/go.sum b/go.sum index 9a2e3bcb..3d0d6ced 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ github.com/goshuirc/irc-go v0.0.0-20210304031553-cf78e9176f96 h1:sihI3HsrJWyS4Mt github.com/goshuirc/irc-go v0.0.0-20210304031553-cf78e9176f96/go.mod h1:q/JhvvKLmif3y9q8MDQM+gRCnjEKnu5ClF298TTXJug= github.com/goshuirc/irc-go v0.0.0-20210311004346-ea7a188a73fe h1:5UsPgeXJBkFgJK3Ml0nj6ljasjd26xiUxALnDJHmipE= github.com/goshuirc/irc-go v0.0.0-20210311004346-ea7a188a73fe/go.mod h1:q/JhvvKLmif3y9q8MDQM+gRCnjEKnu5ClF298TTXJug= +github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef h1:07e6GcSuNh1BoZJigrvaJSpe2PsYJgkYETOuGKpM2co= +github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef/go.mod h1:q/JhvvKLmif3y9q8MDQM+gRCnjEKnu5ClF298TTXJug= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= diff --git a/irc/channel.go b/irc/channel.go index 21326977..4b341e3e 100644 --- a/irc/channel.go +++ b/irc/channel.go @@ -13,6 +13,8 @@ import ( "sync" + "github.com/goshuirc/irc-go/ircutils" + "github.com/oragono/oragono/irc/caps" "github.com/oragono/oragono/irc/history" "github.com/oragono/oragono/irc/modes" @@ -1259,10 +1261,7 @@ func (channel *Channel) SetTopic(client *Client, topic string, rb *ResponseBuffe return } - topicLimit := client.server.Config().Limits.TopicLen - if len(topic) > topicLimit { - topic = topic[:topicLimit] - } + topic = ircutils.TruncateUTF8Safe(topic, client.server.Config().Limits.TopicLen) channel.stateMutex.Lock() chname := channel.name @@ -1520,10 +1519,7 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb return } - kicklimit := channel.server.Config().Limits.KickLen - if len(comment) > kicklimit { - comment = comment[:kicklimit] - } + comment = ircutils.TruncateUTF8Safe(comment, channel.server.Config().Limits.KickLen) message := utils.MakeMessage(comment) details := client.Details() diff --git a/irc/handlers.go b/irc/handlers.go index b910e1f1..95a99a1a 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -22,6 +22,9 @@ import ( "github.com/goshuirc/irc-go/ircfmt" "github.com/goshuirc/irc-go/ircmsg" + "github.com/goshuirc/irc-go/ircutils" + "golang.org/x/crypto/bcrypt" + "github.com/oragono/oragono/irc/caps" "github.com/oragono/oragono/irc/custime" "github.com/oragono/oragono/irc/flatip" @@ -30,7 +33,6 @@ import ( "github.com/oragono/oragono/irc/modes" "github.com/oragono/oragono/irc/sno" "github.com/oragono/oragono/irc/utils" - "golang.org/x/crypto/bcrypt" ) // helper function to parse ACC callbacks, e.g., mailto:person@example.com, tel:16505551234 @@ -346,10 +348,7 @@ func awayHandler(server *Server, client *Client, msg ircmsg.Message, rb *Respons if len(msg.Params) > 0 { isAway = true awayMessage = msg.Params[0] - awayLen := server.Config().Limits.AwayLen - if len(awayMessage) > awayLen { - awayMessage = awayMessage[:awayLen] - } + awayMessage = ircutils.TruncateUTF8Safe(awayMessage, server.Config().Limits.AwayLen) } rb.session.SetAway(awayMessage) diff --git a/vendor/github.com/goshuirc/irc-go/ircutils/doc.go b/vendor/github.com/goshuirc/irc-go/ircutils/doc.go new file mode 100644 index 00000000..b963a1a8 --- /dev/null +++ b/vendor/github.com/goshuirc/irc-go/ircutils/doc.go @@ -0,0 +1,9 @@ +// written by Daniel Oaks +// released under the ISC license + +/* +Package ircutils provides small, useful utility functions and classes. + +This package is in an alpha stage. +*/ +package ircutils diff --git a/vendor/github.com/goshuirc/irc-go/ircutils/hostnames.go b/vendor/github.com/goshuirc/irc-go/ircutils/hostnames.go new file mode 100644 index 00000000..fb12d8ac --- /dev/null +++ b/vendor/github.com/goshuirc/irc-go/ircutils/hostnames.go @@ -0,0 +1,41 @@ +// written by Daniel Oaks +// released under the ISC license + +package ircutils + +import "strings" + +var allowedHostnameChars = "abcdefghijklmnopqrstuvwxyz1234567890-." + +// HostnameIsValid provides a way for servers to check whether a looked-up client +// hostname is valid (see InspIRCd #1033 for why this is required). +// +// This function shouldn't be called by clients since they don't need to validate +// hostnames for IRC use, just by servers that need to confirm hostnames of incoming +// clients. +// +// In addition to this function, servers should impose their own limits on max +// hostname length -- this function limits it to 200 but most servers will probably +// want to make it smaller than that. +func HostnameIsValid(hostname string) bool { + // IRC hostnames specifically require a period, rough limit of 200 chars + if !strings.Contains(hostname, ".") || len(hostname) < 1 || len(hostname) > 200 { + return false + } + + // ensure each part of hostname is valid + for _, part := range strings.Split(hostname, ".") { + if len(part) < 1 || len(part) > 63 || strings.HasPrefix(part, "-") || strings.HasSuffix(part, "-") { + return false + } + } + + // ensure all chars of hostname are valid + for _, char := range strings.Split(strings.ToLower(hostname), "") { + if !strings.Contains(allowedHostnameChars, char) { + return false + } + } + + return true +} diff --git a/vendor/github.com/goshuirc/irc-go/ircutils/unicode.go b/vendor/github.com/goshuirc/irc-go/ircutils/unicode.go new file mode 100644 index 00000000..14f628e3 --- /dev/null +++ b/vendor/github.com/goshuirc/irc-go/ircutils/unicode.go @@ -0,0 +1,25 @@ +// Copyright (c) 2021 Shivaram Lingamneni +// Released under the MIT License + +package ircutils + +import ( + "unicode/utf8" +) + +// truncate a message, taking care not to make valid UTF8 into invalid UTF8 +func TruncateUTF8Safe(message string, byteLimit int) (result string) { + if len(message) <= byteLimit { + return message + } + message = message[:byteLimit] + for i := 0; i < (utf8.UTFMax - 1); i++ { + r, n := utf8.DecodeLastRuneInString(message) + if r == utf8.RuneError && n <= 1 { + message = message[:len(message)-1] + } else { + break + } + } + return message +} diff --git a/vendor/github.com/goshuirc/irc-go/ircutils/userhost.go b/vendor/github.com/goshuirc/irc-go/ircutils/userhost.go new file mode 100644 index 00000000..53b0d75c --- /dev/null +++ b/vendor/github.com/goshuirc/irc-go/ircutils/userhost.go @@ -0,0 +1,56 @@ +// written by Daniel Oaks +// released under the ISC license + +package ircutils + +import "strings" + +// UserHost holds a username+host combination +type UserHost struct { + Nick string + User string + Host string +} + +// ParseUserhost takes a userhost string and returns a UserHost instance. +func ParseUserhost(userhost string) UserHost { + var uh UserHost + + if len(userhost) == 0 { + return uh + } + + if strings.Contains(userhost, "!") { + usersplit := strings.SplitN(userhost, "!", 2) + var rest string + if len(usersplit) == 2 { + uh.Nick = usersplit[0] + rest = usersplit[1] + } else { + rest = usersplit[0] + } + + hostsplit := strings.SplitN(rest, "@", 2) + if len(hostsplit) == 2 { + uh.User = hostsplit[0] + uh.Host = hostsplit[1] + } else { + uh.User = hostsplit[0] + } + } else { + hostsplit := strings.SplitN(userhost, "@", 2) + if len(hostsplit) == 2 { + uh.Nick = hostsplit[0] + uh.Host = hostsplit[1] + } else { + uh.User = hostsplit[0] + } + } + + return uh +} + +// // Canonical returns the canonical string representation of the userhost. +// func (uh *UserHost) Canonical() string { +// return "" +// } diff --git a/vendor/modules.txt b/vendor/modules.txt index f9504979..d7fd49d2 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -21,11 +21,12 @@ github.com/go-sql-driver/mysql # github.com/gorilla/websocket v1.4.2 => github.com/oragono/websocket v1.4.2-oragono1 ## explicit github.com/gorilla/websocket -# github.com/goshuirc/irc-go v0.0.0-20210311004346-ea7a188a73fe +# github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef ## explicit github.com/goshuirc/irc-go/ircfmt github.com/goshuirc/irc-go/ircmsg github.com/goshuirc/irc-go/ircreader +github.com/goshuirc/irc-go/ircutils # github.com/onsi/ginkgo v1.12.0 ## explicit # github.com/onsi/gomega v1.9.0