diff options
author | Hubert Hirtz <hubert@hirtz.pm> | 2021-04-27 10:10:56 +0200 |
---|---|---|
committer | Hubert Hirtz <hubert@hirtz.pm> | 2021-04-27 10:10:56 +0200 |
commit | c652dad312d1de75a0661e057d46d8cc0e9b9918 (patch) | |
tree | 99a08f30c7cbe9ee0b0be7a928a47cbb63e520ad /irc | |
parent | Split the "feature" map into specific attributes (diff) |
Split long messages before sending them
avoids silent truncations.
Also consider the LINELEN ISUPPORT token:
- https://defs.ircdocs.horse/defs/isupport.html
- http://rabbit.dereferenced.org/~nenolod/ircv3-harmony/id-wpitcock-ircv3-core.html#rfc.section.3.8.2.1.17
Diffstat (limited to 'irc')
-rw-r--r-- | irc/states.go | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/irc/states.go b/irc/states.go index 2441d5c..50bf4a6 100644 --- a/irc/states.go +++ b/irc/states.go @@ -11,6 +11,7 @@ import ( "strings" "sync/atomic" "time" + "unicode/utf8" ) const writeDeadline = 10 * time.Second @@ -177,6 +178,7 @@ type Session struct { // ISUPPORT features casemap func(string) string chantypes string + linelen int users map[string]*User // known users. channels map[string]Channel // joined channels. @@ -206,6 +208,7 @@ func NewSession(conn net.Conn, params SessionParams) (*Session, error) { enabledCaps: map[string]struct{}{}, casemap: CasemapRFC1459, chantypes: "#&", + linelen: 512, users: map[string]*User{}, channels: map[string]Channel{}, chBatches: map[string]HistoryEvent{}, @@ -400,12 +403,47 @@ func (s *Session) quit(act actionQuit) (err error) { return } +func splitChunks(s string, chunkLen int) (chunks []string) { + if chunkLen <= 0 { + return []string{s} + } + for chunkLen < len(s) { + i := chunkLen + min := chunkLen - utf8.UTFMax + for min <= i && !utf8.RuneStart(s[i]) { + i-- + } + chunks = append(chunks, s[:i]) + s = s[i:] + } + if len(s) != 0 { + chunks = append(chunks, s) + } + return +} + func (s *Session) PrivMsg(target, content string) { s.acts <- actionPrivMsg{target, content} } func (s *Session) privMsg(act actionPrivMsg) (err error) { - err = s.send("PRIVMSG %s :%s\r\n", act.Target, act.Content) + hostLen := len(s.host) + if hostLen == 0 { + hostLen = len("255.255.255.255") + } + maxMessageLen := s.linelen - + len(":!@ PRIVMSG :\r\n") - + len(s.nick) - + len(s.user) - + hostLen - + len(act.Target) + chunks := splitChunks(act.Content, maxMessageLen) + for _, chunk := range chunks { + err = s.send("PRIVMSG %s :%s\r\n", act.Target, chunk) + if err != nil { + return + } + } target := s.Casemap(act.Target) delete(s.typingStamps, target) return @@ -1084,6 +1122,11 @@ func (s *Session) updateFeatures(features []string) { } case "CHANTYPES": s.chantypes = value + case "LINELEN": + linelen, err := strconv.Atoi(value) + if err == nil && linelen != 0 { + s.linelen = linelen + } } } } |