commit c652dad
Hubert Hirtz
·
2021-04-27 08:10:56 +0000 UTC
parent cede6d5
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
1 files changed,
+44,
-1
+44,
-1
1@@ -11,6 +11,7 @@ import (
2 "strings"
3 "sync/atomic"
4 "time"
5+ "unicode/utf8"
6 )
7
8 const writeDeadline = 10 * time.Second
9@@ -177,6 +178,7 @@ type Session struct {
10 // ISUPPORT features
11 casemap func(string) string
12 chantypes string
13+ linelen int
14
15 users map[string]*User // known users.
16 channels map[string]Channel // joined channels.
17@@ -206,6 +208,7 @@ func NewSession(conn net.Conn, params SessionParams) (*Session, error) {
18 enabledCaps: map[string]struct{}{},
19 casemap: CasemapRFC1459,
20 chantypes: "#&",
21+ linelen: 512,
22 users: map[string]*User{},
23 channels: map[string]Channel{},
24 chBatches: map[string]HistoryEvent{},
25@@ -400,12 +403,47 @@ func (s *Session) quit(act actionQuit) (err error) {
26 return
27 }
28
29+func splitChunks(s string, chunkLen int) (chunks []string) {
30+ if chunkLen <= 0 {
31+ return []string{s}
32+ }
33+ for chunkLen < len(s) {
34+ i := chunkLen
35+ min := chunkLen - utf8.UTFMax
36+ for min <= i && !utf8.RuneStart(s[i]) {
37+ i--
38+ }
39+ chunks = append(chunks, s[:i])
40+ s = s[i:]
41+ }
42+ if len(s) != 0 {
43+ chunks = append(chunks, s)
44+ }
45+ return
46+}
47+
48 func (s *Session) PrivMsg(target, content string) {
49 s.acts <- actionPrivMsg{target, content}
50 }
51
52 func (s *Session) privMsg(act actionPrivMsg) (err error) {
53- err = s.send("PRIVMSG %s :%s\r\n", act.Target, act.Content)
54+ hostLen := len(s.host)
55+ if hostLen == 0 {
56+ hostLen = len("255.255.255.255")
57+ }
58+ maxMessageLen := s.linelen -
59+ len(":!@ PRIVMSG :\r\n") -
60+ len(s.nick) -
61+ len(s.user) -
62+ hostLen -
63+ len(act.Target)
64+ chunks := splitChunks(act.Content, maxMessageLen)
65+ for _, chunk := range chunks {
66+ err = s.send("PRIVMSG %s :%s\r\n", act.Target, chunk)
67+ if err != nil {
68+ return
69+ }
70+ }
71 target := s.Casemap(act.Target)
72 delete(s.typingStamps, target)
73 return
74@@ -1084,6 +1122,11 @@ func (s *Session) updateFeatures(features []string) {
75 }
76 case "CHANTYPES":
77 s.chantypes = value
78+ case "LINELEN":
79+ linelen, err := strconv.Atoi(value)
80+ if err == nil && linelen != 0 {
81+ s.linelen = linelen
82+ }
83 }
84 }
85 }