source: code/trunk/upstream.go@ 684

Last change on this file since 684 was 684, checked in by contact, 4 years ago

Add support for MONITOR

Add support for MONITOR in single-upstream mode.

Each downstream has its own set of monitored targets. These sets
are merged together to compute the MONITOR commands to send to
upstream.

Each upstream has a set of monitored targets accepted by the server
alongside with their status (online/offline). This is used to
directly send replies to downstreams adding a target another
downstream has already added, and send MONITOR S[TATUS] replies.

Co-authored-by: delthas <delthas@…>

File size: 50.5 KB
Line 
1package soju
2
3import (
4 "context"
5 "crypto"
6 "crypto/sha256"
7 "crypto/tls"
8 "crypto/x509"
9 "encoding/base64"
10 "errors"
11 "fmt"
12 "io"
13 "net"
14 "strconv"
15 "strings"
16 "time"
17
18 "github.com/emersion/go-sasl"
19 "gopkg.in/irc.v3"
20)
21
22// permanentUpstreamCaps is the static list of upstream capabilities always
23// requested when supported.
24var permanentUpstreamCaps = map[string]bool{
25 "account-tag": true,
26 "away-notify": true,
27 "batch": true,
28 "extended-join": true,
29 "invite-notify": true,
30 "labeled-response": true,
31 "message-tags": true,
32 "multi-prefix": true,
33 "server-time": true,
34 "setname": true,
35}
36
37type registrationError string
38
39func (err registrationError) Error() string {
40 return fmt.Sprintf("registration error: %v", string(err))
41}
42
43type upstreamChannel struct {
44 Name string
45 conn *upstreamConn
46 Topic string
47 TopicWho *irc.Prefix
48 TopicTime time.Time
49 Status channelStatus
50 modes channelModes
51 creationTime string
52 Members membershipsCasemapMap
53 complete bool
54 detachTimer *time.Timer
55}
56
57func (uc *upstreamChannel) updateAutoDetach(dur time.Duration) {
58 if uc.detachTimer != nil {
59 uc.detachTimer.Stop()
60 uc.detachTimer = nil
61 }
62
63 if dur == 0 {
64 return
65 }
66
67 uc.detachTimer = time.AfterFunc(dur, func() {
68 uc.conn.network.user.events <- eventChannelDetach{
69 uc: uc.conn,
70 name: uc.Name,
71 }
72 })
73}
74
75type pendingUpstreamCommand struct {
76 downstreamID uint64
77 msg *irc.Message
78}
79
80type upstreamConn struct {
81 conn
82
83 network *network
84 user *user
85
86 serverName string
87 availableUserModes string
88 availableChannelModes map[byte]channelModeType
89 availableChannelTypes string
90 availableMemberships []membership
91 isupport map[string]*string
92
93 registered bool
94 nick string
95 nickCM string
96 username string
97 realname string
98 modes userModes
99 channels upstreamChannelCasemapMap
100 supportedCaps map[string]string
101 caps map[string]bool
102 batches map[string]batch
103 away bool
104 account string
105 nextLabelID uint64
106 monitored monitorCasemapMap
107
108 saslClient sasl.Client
109 saslStarted bool
110
111 casemapIsSet bool
112
113 // Queue of commands in progress, indexed by type. The first entry has been
114 // sent to the server and is awaiting reply. The following entries have not
115 // been sent yet.
116 pendingCmds map[string][]pendingUpstreamCommand
117
118 gotMotd bool
119}
120
121func connectToUpstream(network *network) (*upstreamConn, error) {
122 logger := &prefixLogger{network.user.logger, fmt.Sprintf("upstream %q: ", network.GetName())}
123
124 dialer := net.Dialer{Timeout: connectTimeout}
125
126 u, err := network.URL()
127 if err != nil {
128 return nil, err
129 }
130
131 var netConn net.Conn
132 switch u.Scheme {
133 case "ircs":
134 addr := u.Host
135 host, _, err := net.SplitHostPort(u.Host)
136 if err != nil {
137 host = u.Host
138 addr = u.Host + ":6697"
139 }
140
141 logger.Printf("connecting to TLS server at address %q", addr)
142
143 tlsConfig := &tls.Config{ServerName: host, NextProtos: []string{"irc"}}
144 if network.SASL.Mechanism == "EXTERNAL" {
145 if network.SASL.External.CertBlob == nil {
146 return nil, fmt.Errorf("missing certificate for authentication")
147 }
148 if network.SASL.External.PrivKeyBlob == nil {
149 return nil, fmt.Errorf("missing private key for authentication")
150 }
151 key, err := x509.ParsePKCS8PrivateKey(network.SASL.External.PrivKeyBlob)
152 if err != nil {
153 return nil, fmt.Errorf("failed to parse private key: %v", err)
154 }
155 tlsConfig.Certificates = []tls.Certificate{
156 {
157 Certificate: [][]byte{network.SASL.External.CertBlob},
158 PrivateKey: key.(crypto.PrivateKey),
159 },
160 }
161 logger.Printf("using TLS client certificate %x", sha256.Sum256(network.SASL.External.CertBlob))
162 }
163
164 netConn, err = dialer.Dial("tcp", addr)
165 if err != nil {
166 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
167 }
168
169 // Don't do the TLS handshake immediately, because we need to register
170 // the new connection with identd ASAP. See:
171 // https://todo.sr.ht/~emersion/soju/69#event-41859
172 netConn = tls.Client(netConn, tlsConfig)
173 case "irc+insecure":
174 addr := u.Host
175 if _, _, err := net.SplitHostPort(addr); err != nil {
176 addr = addr + ":6667"
177 }
178
179 logger.Printf("connecting to plain-text server at address %q", addr)
180 netConn, err = dialer.Dial("tcp", addr)
181 if err != nil {
182 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
183 }
184 case "irc+unix", "unix":
185 logger.Printf("connecting to Unix socket at path %q", u.Path)
186 netConn, err = dialer.Dial("unix", u.Path)
187 if err != nil {
188 return nil, fmt.Errorf("failed to connect to Unix socket %q: %v", u.Path, err)
189 }
190 default:
191 return nil, fmt.Errorf("failed to dial %q: unknown scheme: %v", network.Addr, u.Scheme)
192 }
193
194 options := connOptions{
195 Logger: logger,
196 RateLimitDelay: upstreamMessageDelay,
197 RateLimitBurst: upstreamMessageBurst,
198 }
199
200 uc := &upstreamConn{
201 conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options),
202 network: network,
203 user: network.user,
204 channels: upstreamChannelCasemapMap{newCasemapMap(0)},
205 supportedCaps: make(map[string]string),
206 caps: make(map[string]bool),
207 batches: make(map[string]batch),
208 availableChannelTypes: stdChannelTypes,
209 availableChannelModes: stdChannelModes,
210 availableMemberships: stdMemberships,
211 isupport: make(map[string]*string),
212 pendingCmds: make(map[string][]pendingUpstreamCommand),
213 monitored: monitorCasemapMap{newCasemapMap(0)},
214 }
215 return uc, nil
216}
217
218func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
219 uc.network.forEachDownstream(f)
220}
221
222func (uc *upstreamConn) forEachDownstreamByID(id uint64, f func(*downstreamConn)) {
223 uc.forEachDownstream(func(dc *downstreamConn) {
224 if id != 0 && id != dc.id {
225 return
226 }
227 f(dc)
228 })
229}
230
231func (uc *upstreamConn) downstreamByID(id uint64) *downstreamConn {
232 for _, dc := range uc.user.downstreamConns {
233 if dc.id == id {
234 return dc
235 }
236 }
237 return nil
238}
239
240func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
241 ch := uc.channels.Value(name)
242 if ch == nil {
243 return nil, fmt.Errorf("unknown channel %q", name)
244 }
245 return ch, nil
246}
247
248func (uc *upstreamConn) isChannel(entity string) bool {
249 return strings.ContainsRune(uc.availableChannelTypes, rune(entity[0]))
250}
251
252func (uc *upstreamConn) isOurNick(nick string) bool {
253 return uc.nickCM == uc.network.casemap(nick)
254}
255
256func (uc *upstreamConn) endPendingCommands() {
257 for _, l := range uc.pendingCmds {
258 for _, pendingCmd := range l {
259 dc := uc.downstreamByID(pendingCmd.downstreamID)
260 if dc == nil {
261 continue
262 }
263
264 switch pendingCmd.msg.Command {
265 case "LIST":
266 dc.SendMessage(&irc.Message{
267 Prefix: dc.srv.prefix(),
268 Command: irc.RPL_LISTEND,
269 Params: []string{dc.nick, "End of /LIST"},
270 })
271 case "WHO":
272 mask := "*"
273 if len(pendingCmd.msg.Params) > 0 {
274 mask = pendingCmd.msg.Params[0]
275 }
276 dc.SendMessage(&irc.Message{
277 Prefix: dc.srv.prefix(),
278 Command: irc.RPL_ENDOFWHO,
279 Params: []string{dc.nick, mask, "End of /WHO"},
280 })
281 default:
282 panic(fmt.Errorf("Unsupported pending command %q", pendingCmd.msg.Command))
283 }
284 }
285 }
286
287 uc.pendingCmds = make(map[string][]pendingUpstreamCommand)
288}
289
290func (uc *upstreamConn) sendNextPendingCommand(cmd string) {
291 if len(uc.pendingCmds[cmd]) == 0 {
292 return
293 }
294 uc.SendMessage(uc.pendingCmds[cmd][0].msg)
295}
296
297func (uc *upstreamConn) enqueueCommand(dc *downstreamConn, msg *irc.Message) {
298 switch msg.Command {
299 case "LIST", "WHO":
300 // Supported
301 default:
302 panic(fmt.Errorf("Unsupported pending command %q", msg.Command))
303 }
304
305 uc.pendingCmds[msg.Command] = append(uc.pendingCmds[msg.Command], pendingUpstreamCommand{
306 downstreamID: dc.id,
307 msg: msg,
308 })
309
310 if len(uc.pendingCmds[msg.Command]) == 1 {
311 uc.sendNextPendingCommand(msg.Command)
312 }
313}
314
315func (uc *upstreamConn) currentPendingCommand(cmd string) (*downstreamConn, *irc.Message) {
316 if len(uc.pendingCmds[cmd]) == 0 {
317 return nil, nil
318 }
319
320 pendingCmd := uc.pendingCmds[cmd][0]
321 return uc.downstreamByID(pendingCmd.downstreamID), pendingCmd.msg
322}
323
324func (uc *upstreamConn) dequeueCommand(cmd string) (*downstreamConn, *irc.Message) {
325 dc, msg := uc.currentPendingCommand(cmd)
326
327 if len(uc.pendingCmds[cmd]) > 0 {
328 copy(uc.pendingCmds[cmd], uc.pendingCmds[cmd][1:])
329 uc.pendingCmds[cmd] = uc.pendingCmds[cmd][:len(uc.pendingCmds[cmd])-1]
330 }
331
332 uc.sendNextPendingCommand(cmd)
333
334 return dc, msg
335}
336
337func (uc *upstreamConn) parseMembershipPrefix(s string) (ms *memberships, nick string) {
338 memberships := make(memberships, 0, 4)
339 i := 0
340 for _, m := range uc.availableMemberships {
341 if i >= len(s) {
342 break
343 }
344 if s[i] == m.Prefix {
345 memberships = append(memberships, m)
346 i++
347 }
348 }
349 return &memberships, s[i:]
350}
351
352func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
353 var label string
354 if l, ok := msg.GetTag("label"); ok {
355 label = l
356 delete(msg.Tags, "label")
357 }
358
359 var msgBatch *batch
360 if batchName, ok := msg.GetTag("batch"); ok {
361 b, ok := uc.batches[batchName]
362 if !ok {
363 return fmt.Errorf("unexpected batch reference: batch was not defined: %q", batchName)
364 }
365 msgBatch = &b
366 if label == "" {
367 label = msgBatch.Label
368 }
369 delete(msg.Tags, "batch")
370 }
371
372 var downstreamID uint64 = 0
373 if label != "" {
374 var labelOffset uint64
375 n, err := fmt.Sscanf(label, "sd-%d-%d", &downstreamID, &labelOffset)
376 if err == nil && n < 2 {
377 err = errors.New("not enough arguments")
378 }
379 if err != nil {
380 return fmt.Errorf("unexpected message label: invalid downstream reference for label %q: %v", label, err)
381 }
382 }
383
384 if _, ok := msg.Tags["time"]; !ok {
385 msg.Tags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
386 }
387
388 switch msg.Command {
389 case "PING":
390 uc.SendMessage(&irc.Message{
391 Command: "PONG",
392 Params: msg.Params,
393 })
394 return nil
395 case "NOTICE", "PRIVMSG", "TAGMSG":
396 if msg.Prefix == nil {
397 return fmt.Errorf("expected a prefix")
398 }
399
400 var entity, text string
401 if msg.Command != "TAGMSG" {
402 if err := parseMessageParams(msg, &entity, &text); err != nil {
403 return err
404 }
405 } else {
406 if err := parseMessageParams(msg, &entity); err != nil {
407 return err
408 }
409 }
410
411 if msg.Prefix.Name == serviceNick {
412 uc.logger.Printf("skipping %v from soju's service: %v", msg.Command, msg)
413 break
414 }
415 if entity == serviceNick {
416 uc.logger.Printf("skipping %v to soju's service: %v", msg.Command, msg)
417 break
418 }
419
420 if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message
421 uc.produce("", msg, nil)
422 } else { // regular user message
423 target := entity
424 if uc.isOurNick(target) {
425 target = msg.Prefix.Name
426 }
427
428 ch := uc.network.channels.Value(target)
429 if ch != nil && msg.Command != "TAGMSG" {
430 if ch.Detached {
431 uc.handleDetachedMessage(ch, msg)
432 }
433
434 highlight := uc.network.isHighlight(msg)
435 if ch.DetachOn == FilterMessage || ch.DetachOn == FilterDefault || (ch.DetachOn == FilterHighlight && highlight) {
436 uc.updateChannelAutoDetach(target)
437 }
438 }
439
440 uc.produce(target, msg, nil)
441 }
442 case "CAP":
443 var subCmd string
444 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
445 return err
446 }
447 subCmd = strings.ToUpper(subCmd)
448 subParams := msg.Params[2:]
449 switch subCmd {
450 case "LS":
451 if len(subParams) < 1 {
452 return newNeedMoreParamsError(msg.Command)
453 }
454 caps := subParams[len(subParams)-1]
455 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
456
457 uc.handleSupportedCaps(caps)
458
459 if more {
460 break // wait to receive all capabilities
461 }
462
463 uc.requestCaps()
464
465 if uc.requestSASL() {
466 break // we'll send CAP END after authentication is completed
467 }
468
469 uc.SendMessage(&irc.Message{
470 Command: "CAP",
471 Params: []string{"END"},
472 })
473 case "ACK", "NAK":
474 if len(subParams) < 1 {
475 return newNeedMoreParamsError(msg.Command)
476 }
477 caps := strings.Fields(subParams[0])
478
479 for _, name := range caps {
480 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
481 return err
482 }
483 }
484
485 if uc.registered {
486 uc.forEachDownstream(func(dc *downstreamConn) {
487 dc.updateSupportedCaps()
488 })
489 }
490 case "NEW":
491 if len(subParams) < 1 {
492 return newNeedMoreParamsError(msg.Command)
493 }
494 uc.handleSupportedCaps(subParams[0])
495 uc.requestCaps()
496 case "DEL":
497 if len(subParams) < 1 {
498 return newNeedMoreParamsError(msg.Command)
499 }
500 caps := strings.Fields(subParams[0])
501
502 for _, c := range caps {
503 delete(uc.supportedCaps, c)
504 delete(uc.caps, c)
505 }
506
507 if uc.registered {
508 uc.forEachDownstream(func(dc *downstreamConn) {
509 dc.updateSupportedCaps()
510 })
511 }
512 default:
513 uc.logger.Printf("unhandled message: %v", msg)
514 }
515 case "AUTHENTICATE":
516 if uc.saslClient == nil {
517 return fmt.Errorf("received unexpected AUTHENTICATE message")
518 }
519
520 // TODO: if a challenge is 400 bytes long, buffer it
521 var challengeStr string
522 if err := parseMessageParams(msg, &challengeStr); err != nil {
523 uc.SendMessage(&irc.Message{
524 Command: "AUTHENTICATE",
525 Params: []string{"*"},
526 })
527 return err
528 }
529
530 var challenge []byte
531 if challengeStr != "+" {
532 var err error
533 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
534 if err != nil {
535 uc.SendMessage(&irc.Message{
536 Command: "AUTHENTICATE",
537 Params: []string{"*"},
538 })
539 return err
540 }
541 }
542
543 var resp []byte
544 var err error
545 if !uc.saslStarted {
546 _, resp, err = uc.saslClient.Start()
547 uc.saslStarted = true
548 } else {
549 resp, err = uc.saslClient.Next(challenge)
550 }
551 if err != nil {
552 uc.SendMessage(&irc.Message{
553 Command: "AUTHENTICATE",
554 Params: []string{"*"},
555 })
556 return err
557 }
558
559 // TODO: send response in multiple chunks if >= 400 bytes
560 var respStr = "+"
561 if len(resp) != 0 {
562 respStr = base64.StdEncoding.EncodeToString(resp)
563 }
564
565 uc.SendMessage(&irc.Message{
566 Command: "AUTHENTICATE",
567 Params: []string{respStr},
568 })
569 case irc.RPL_LOGGEDIN:
570 if err := parseMessageParams(msg, nil, nil, &uc.account); err != nil {
571 return err
572 }
573 uc.logger.Printf("logged in with account %q", uc.account)
574 case irc.RPL_LOGGEDOUT:
575 uc.account = ""
576 uc.logger.Printf("logged out")
577 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
578 var info string
579 if err := parseMessageParams(msg, nil, &info); err != nil {
580 return err
581 }
582 switch msg.Command {
583 case irc.ERR_NICKLOCKED:
584 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
585 case irc.ERR_SASLFAIL:
586 uc.logger.Printf("SASL authentication failed: %v", info)
587 case irc.ERR_SASLTOOLONG:
588 uc.logger.Printf("SASL message too long: %v", info)
589 }
590
591 uc.saslClient = nil
592 uc.saslStarted = false
593
594 uc.SendMessage(&irc.Message{
595 Command: "CAP",
596 Params: []string{"END"},
597 })
598 case irc.RPL_WELCOME:
599 uc.registered = true
600 uc.logger.Printf("connection registered")
601
602 if uc.network.channels.Len() > 0 {
603 var channels, keys []string
604 for _, entry := range uc.network.channels.innerMap {
605 ch := entry.value.(*Channel)
606 channels = append(channels, ch.Name)
607 keys = append(keys, ch.Key)
608 }
609
610 for _, msg := range join(channels, keys) {
611 uc.SendMessage(msg)
612 }
613 }
614 case irc.RPL_MYINFO:
615 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
616 return err
617 }
618 case irc.RPL_ISUPPORT:
619 if err := parseMessageParams(msg, nil, nil); err != nil {
620 return err
621 }
622
623 var downstreamIsupport []string
624 for _, token := range msg.Params[1 : len(msg.Params)-1] {
625 parameter := token
626 var negate, hasValue bool
627 var value string
628 if strings.HasPrefix(token, "-") {
629 negate = true
630 token = token[1:]
631 } else if i := strings.IndexByte(token, '='); i >= 0 {
632 parameter = token[:i]
633 value = token[i+1:]
634 hasValue = true
635 }
636
637 if hasValue {
638 uc.isupport[parameter] = &value
639 } else if !negate {
640 uc.isupport[parameter] = nil
641 } else {
642 delete(uc.isupport, parameter)
643 }
644
645 var err error
646 switch parameter {
647 case "CASEMAPPING":
648 casemap, ok := parseCasemappingToken(value)
649 if !ok {
650 casemap = casemapRFC1459
651 }
652 uc.network.updateCasemapping(casemap)
653 uc.nickCM = uc.network.casemap(uc.nick)
654 uc.casemapIsSet = true
655 case "CHANMODES":
656 if !negate {
657 err = uc.handleChanModes(value)
658 } else {
659 uc.availableChannelModes = stdChannelModes
660 }
661 case "CHANTYPES":
662 if !negate {
663 uc.availableChannelTypes = value
664 } else {
665 uc.availableChannelTypes = stdChannelTypes
666 }
667 case "PREFIX":
668 if !negate {
669 err = uc.handleMemberships(value)
670 } else {
671 uc.availableMemberships = stdMemberships
672 }
673 }
674 if err != nil {
675 return err
676 }
677
678 if passthroughIsupport[parameter] {
679 downstreamIsupport = append(downstreamIsupport, token)
680 }
681 }
682
683 uc.forEachDownstream(func(dc *downstreamConn) {
684 if dc.network == nil {
685 return
686 }
687 msgs := generateIsupport(dc.srv.prefix(), dc.nick, downstreamIsupport)
688 for _, msg := range msgs {
689 dc.SendMessage(msg)
690 }
691 })
692 case irc.ERR_NOMOTD, irc.RPL_ENDOFMOTD:
693 if !uc.casemapIsSet {
694 // upstream did not send any CASEMAPPING token, thus
695 // we assume it implements the old RFCs with rfc1459.
696 uc.casemapIsSet = true
697 uc.network.updateCasemapping(casemapRFC1459)
698 uc.nickCM = uc.network.casemap(uc.nick)
699 }
700
701 if !uc.gotMotd {
702 // Ignore the initial MOTD upon connection, but forward
703 // subsequent MOTD messages downstream
704 uc.gotMotd = true
705 return nil
706 }
707
708 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
709 dc.SendMessage(&irc.Message{
710 Prefix: uc.srv.prefix(),
711 Command: msg.Command,
712 Params: msg.Params,
713 })
714 })
715 case "BATCH":
716 var tag string
717 if err := parseMessageParams(msg, &tag); err != nil {
718 return err
719 }
720
721 if strings.HasPrefix(tag, "+") {
722 tag = tag[1:]
723 if _, ok := uc.batches[tag]; ok {
724 return fmt.Errorf("unexpected BATCH reference tag: batch was already defined: %q", tag)
725 }
726 var batchType string
727 if err := parseMessageParams(msg, nil, &batchType); err != nil {
728 return err
729 }
730 label := label
731 if label == "" && msgBatch != nil {
732 label = msgBatch.Label
733 }
734 uc.batches[tag] = batch{
735 Type: batchType,
736 Params: msg.Params[2:],
737 Outer: msgBatch,
738 Label: label,
739 }
740 } else if strings.HasPrefix(tag, "-") {
741 tag = tag[1:]
742 if _, ok := uc.batches[tag]; !ok {
743 return fmt.Errorf("unknown BATCH reference tag: %q", tag)
744 }
745 delete(uc.batches, tag)
746 } else {
747 return fmt.Errorf("unexpected BATCH reference tag: missing +/- prefix: %q", tag)
748 }
749 case "NICK":
750 if msg.Prefix == nil {
751 return fmt.Errorf("expected a prefix")
752 }
753
754 var newNick string
755 if err := parseMessageParams(msg, &newNick); err != nil {
756 return err
757 }
758
759 me := false
760 if uc.isOurNick(msg.Prefix.Name) {
761 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
762 me = true
763 uc.nick = newNick
764 uc.nickCM = uc.network.casemap(uc.nick)
765 }
766
767 for _, entry := range uc.channels.innerMap {
768 ch := entry.value.(*upstreamChannel)
769 memberships := ch.Members.Value(msg.Prefix.Name)
770 if memberships != nil {
771 ch.Members.Delete(msg.Prefix.Name)
772 ch.Members.SetValue(newNick, memberships)
773 uc.appendLog(ch.Name, msg)
774 }
775 }
776
777 if !me {
778 uc.forEachDownstream(func(dc *downstreamConn) {
779 dc.SendMessage(dc.marshalMessage(msg, uc.network))
780 })
781 } else {
782 uc.forEachDownstream(func(dc *downstreamConn) {
783 dc.updateNick()
784 })
785 }
786 case "SETNAME":
787 if msg.Prefix == nil {
788 return fmt.Errorf("expected a prefix")
789 }
790
791 var newRealname string
792 if err := parseMessageParams(msg, &newRealname); err != nil {
793 return err
794 }
795
796 // TODO: consider appending this message to logs
797
798 if uc.isOurNick(msg.Prefix.Name) {
799 uc.logger.Printf("changed realname from %q to %q", uc.realname, newRealname)
800 uc.realname = newRealname
801
802 uc.forEachDownstream(func(dc *downstreamConn) {
803 dc.updateRealname()
804 })
805 } else {
806 uc.forEachDownstream(func(dc *downstreamConn) {
807 dc.SendMessage(dc.marshalMessage(msg, uc.network))
808 })
809 }
810 case "JOIN":
811 if msg.Prefix == nil {
812 return fmt.Errorf("expected a prefix")
813 }
814
815 var channels string
816 if err := parseMessageParams(msg, &channels); err != nil {
817 return err
818 }
819
820 for _, ch := range strings.Split(channels, ",") {
821 if uc.isOurNick(msg.Prefix.Name) {
822 uc.logger.Printf("joined channel %q", ch)
823 members := membershipsCasemapMap{newCasemapMap(0)}
824 members.casemap = uc.network.casemap
825 uc.channels.SetValue(ch, &upstreamChannel{
826 Name: ch,
827 conn: uc,
828 Members: members,
829 })
830 uc.updateChannelAutoDetach(ch)
831
832 uc.SendMessage(&irc.Message{
833 Command: "MODE",
834 Params: []string{ch},
835 })
836 } else {
837 ch, err := uc.getChannel(ch)
838 if err != nil {
839 return err
840 }
841 ch.Members.SetValue(msg.Prefix.Name, &memberships{})
842 }
843
844 chMsg := msg.Copy()
845 chMsg.Params[0] = ch
846 uc.produce(ch, chMsg, nil)
847 }
848 case "PART":
849 if msg.Prefix == nil {
850 return fmt.Errorf("expected a prefix")
851 }
852
853 var channels string
854 if err := parseMessageParams(msg, &channels); err != nil {
855 return err
856 }
857
858 for _, ch := range strings.Split(channels, ",") {
859 if uc.isOurNick(msg.Prefix.Name) {
860 uc.logger.Printf("parted channel %q", ch)
861 uch := uc.channels.Value(ch)
862 if uch != nil {
863 uc.channels.Delete(ch)
864 uch.updateAutoDetach(0)
865 }
866 } else {
867 ch, err := uc.getChannel(ch)
868 if err != nil {
869 return err
870 }
871 ch.Members.Delete(msg.Prefix.Name)
872 }
873
874 chMsg := msg.Copy()
875 chMsg.Params[0] = ch
876 uc.produce(ch, chMsg, nil)
877 }
878 case "KICK":
879 if msg.Prefix == nil {
880 return fmt.Errorf("expected a prefix")
881 }
882
883 var channel, user string
884 if err := parseMessageParams(msg, &channel, &user); err != nil {
885 return err
886 }
887
888 if uc.isOurNick(user) {
889 uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
890 uc.channels.Delete(channel)
891 } else {
892 ch, err := uc.getChannel(channel)
893 if err != nil {
894 return err
895 }
896 ch.Members.Delete(user)
897 }
898
899 uc.produce(channel, msg, nil)
900 case "QUIT":
901 if msg.Prefix == nil {
902 return fmt.Errorf("expected a prefix")
903 }
904
905 if uc.isOurNick(msg.Prefix.Name) {
906 uc.logger.Printf("quit")
907 }
908
909 for _, entry := range uc.channels.innerMap {
910 ch := entry.value.(*upstreamChannel)
911 if ch.Members.Has(msg.Prefix.Name) {
912 ch.Members.Delete(msg.Prefix.Name)
913
914 uc.appendLog(ch.Name, msg)
915 }
916 }
917
918 if msg.Prefix.Name != uc.nick {
919 uc.forEachDownstream(func(dc *downstreamConn) {
920 dc.SendMessage(dc.marshalMessage(msg, uc.network))
921 })
922 }
923 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
924 var name, topic string
925 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
926 return err
927 }
928 ch, err := uc.getChannel(name)
929 if err != nil {
930 return err
931 }
932 if msg.Command == irc.RPL_TOPIC {
933 ch.Topic = topic
934 } else {
935 ch.Topic = ""
936 }
937 case "TOPIC":
938 if msg.Prefix == nil {
939 return fmt.Errorf("expected a prefix")
940 }
941
942 var name string
943 if err := parseMessageParams(msg, &name); err != nil {
944 return err
945 }
946 ch, err := uc.getChannel(name)
947 if err != nil {
948 return err
949 }
950 if len(msg.Params) > 1 {
951 ch.Topic = msg.Params[1]
952 ch.TopicWho = msg.Prefix.Copy()
953 ch.TopicTime = time.Now() // TODO use msg.Tags["time"]
954 } else {
955 ch.Topic = ""
956 }
957 uc.produce(ch.Name, msg, nil)
958 case "MODE":
959 var name, modeStr string
960 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
961 return err
962 }
963
964 if !uc.isChannel(name) { // user mode change
965 if name != uc.nick {
966 return fmt.Errorf("received MODE message for unknown nick %q", name)
967 }
968
969 if err := uc.modes.Apply(modeStr); err != nil {
970 return err
971 }
972
973 uc.forEachDownstream(func(dc *downstreamConn) {
974 if dc.upstream() == nil {
975 return
976 }
977
978 dc.SendMessage(msg)
979 })
980 } else { // channel mode change
981 ch, err := uc.getChannel(name)
982 if err != nil {
983 return err
984 }
985
986 needMarshaling, err := applyChannelModes(ch, modeStr, msg.Params[2:])
987 if err != nil {
988 return err
989 }
990
991 uc.appendLog(ch.Name, msg)
992
993 c := uc.network.channels.Value(name)
994 if c == nil || !c.Detached {
995 uc.forEachDownstream(func(dc *downstreamConn) {
996 params := make([]string, len(msg.Params))
997 params[0] = dc.marshalEntity(uc.network, name)
998 params[1] = modeStr
999
1000 copy(params[2:], msg.Params[2:])
1001 for i, modeParam := range params[2:] {
1002 if _, ok := needMarshaling[i]; ok {
1003 params[2+i] = dc.marshalEntity(uc.network, modeParam)
1004 }
1005 }
1006
1007 dc.SendMessage(&irc.Message{
1008 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1009 Command: "MODE",
1010 Params: params,
1011 })
1012 })
1013 }
1014 }
1015 case irc.RPL_UMODEIS:
1016 if err := parseMessageParams(msg, nil); err != nil {
1017 return err
1018 }
1019 modeStr := ""
1020 if len(msg.Params) > 1 {
1021 modeStr = msg.Params[1]
1022 }
1023
1024 uc.modes = ""
1025 if err := uc.modes.Apply(modeStr); err != nil {
1026 return err
1027 }
1028
1029 uc.forEachDownstream(func(dc *downstreamConn) {
1030 if dc.upstream() == nil {
1031 return
1032 }
1033
1034 dc.SendMessage(msg)
1035 })
1036 case irc.RPL_CHANNELMODEIS:
1037 var channel string
1038 if err := parseMessageParams(msg, nil, &channel); err != nil {
1039 return err
1040 }
1041 modeStr := ""
1042 if len(msg.Params) > 2 {
1043 modeStr = msg.Params[2]
1044 }
1045
1046 ch, err := uc.getChannel(channel)
1047 if err != nil {
1048 return err
1049 }
1050
1051 firstMode := ch.modes == nil
1052 ch.modes = make(map[byte]string)
1053 if _, err := applyChannelModes(ch, modeStr, msg.Params[3:]); err != nil {
1054 return err
1055 }
1056 if firstMode {
1057 c := uc.network.channels.Value(channel)
1058 if c == nil || !c.Detached {
1059 modeStr, modeParams := ch.modes.Format()
1060
1061 uc.forEachDownstream(func(dc *downstreamConn) {
1062 params := []string{dc.nick, dc.marshalEntity(uc.network, channel), modeStr}
1063 params = append(params, modeParams...)
1064
1065 dc.SendMessage(&irc.Message{
1066 Prefix: dc.srv.prefix(),
1067 Command: irc.RPL_CHANNELMODEIS,
1068 Params: params,
1069 })
1070 })
1071 }
1072 }
1073 case rpl_creationtime:
1074 var channel, creationTime string
1075 if err := parseMessageParams(msg, nil, &channel, &creationTime); err != nil {
1076 return err
1077 }
1078
1079 ch, err := uc.getChannel(channel)
1080 if err != nil {
1081 return err
1082 }
1083
1084 firstCreationTime := ch.creationTime == ""
1085 ch.creationTime = creationTime
1086 if firstCreationTime {
1087 uc.forEachDownstream(func(dc *downstreamConn) {
1088 dc.SendMessage(&irc.Message{
1089 Prefix: dc.srv.prefix(),
1090 Command: rpl_creationtime,
1091 Params: []string{dc.nick, dc.marshalEntity(uc.network, ch.Name), creationTime},
1092 })
1093 })
1094 }
1095 case rpl_topicwhotime:
1096 var name, who, timeStr string
1097 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
1098 return err
1099 }
1100 ch, err := uc.getChannel(name)
1101 if err != nil {
1102 return err
1103 }
1104 firstTopicWhoTime := ch.TopicWho == nil
1105 ch.TopicWho = irc.ParsePrefix(who)
1106 sec, err := strconv.ParseInt(timeStr, 10, 64)
1107 if err != nil {
1108 return fmt.Errorf("failed to parse topic time: %v", err)
1109 }
1110 ch.TopicTime = time.Unix(sec, 0)
1111 if firstTopicWhoTime {
1112 uc.forEachDownstream(func(dc *downstreamConn) {
1113 topicWho := dc.marshalUserPrefix(uc.network, ch.TopicWho)
1114 dc.SendMessage(&irc.Message{
1115 Prefix: dc.srv.prefix(),
1116 Command: rpl_topicwhotime,
1117 Params: []string{
1118 dc.nick,
1119 dc.marshalEntity(uc.network, ch.Name),
1120 topicWho.String(),
1121 timeStr,
1122 },
1123 })
1124 })
1125 }
1126 case irc.RPL_LIST:
1127 var channel, clients, topic string
1128 if err := parseMessageParams(msg, nil, &channel, &clients, &topic); err != nil {
1129 return err
1130 }
1131
1132 dc, cmd := uc.currentPendingCommand("LIST")
1133 if cmd == nil {
1134 return fmt.Errorf("unexpected RPL_LIST: no matching pending LIST")
1135 } else if dc == nil {
1136 return nil
1137 }
1138
1139 dc.SendMessage(&irc.Message{
1140 Prefix: dc.srv.prefix(),
1141 Command: irc.RPL_LIST,
1142 Params: []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
1143 })
1144 case irc.RPL_LISTEND:
1145 dc, cmd := uc.dequeueCommand("LIST")
1146 if cmd == nil {
1147 return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
1148 } else if dc == nil {
1149 return nil
1150 }
1151
1152 dc.SendMessage(&irc.Message{
1153 Prefix: dc.srv.prefix(),
1154 Command: irc.RPL_LISTEND,
1155 Params: []string{dc.nick, "End of /LIST"},
1156 })
1157 case irc.RPL_NAMREPLY:
1158 var name, statusStr, members string
1159 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
1160 return err
1161 }
1162
1163 ch := uc.channels.Value(name)
1164 if ch == nil {
1165 // NAMES on a channel we have not joined, forward to downstream
1166 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1167 channel := dc.marshalEntity(uc.network, name)
1168 members := splitSpace(members)
1169 for i, member := range members {
1170 memberships, nick := uc.parseMembershipPrefix(member)
1171 members[i] = memberships.Format(dc) + dc.marshalEntity(uc.network, nick)
1172 }
1173 memberStr := strings.Join(members, " ")
1174
1175 dc.SendMessage(&irc.Message{
1176 Prefix: dc.srv.prefix(),
1177 Command: irc.RPL_NAMREPLY,
1178 Params: []string{dc.nick, statusStr, channel, memberStr},
1179 })
1180 })
1181 return nil
1182 }
1183
1184 status, err := parseChannelStatus(statusStr)
1185 if err != nil {
1186 return err
1187 }
1188 ch.Status = status
1189
1190 for _, s := range splitSpace(members) {
1191 memberships, nick := uc.parseMembershipPrefix(s)
1192 ch.Members.SetValue(nick, memberships)
1193 }
1194 case irc.RPL_ENDOFNAMES:
1195 var name string
1196 if err := parseMessageParams(msg, nil, &name); err != nil {
1197 return err
1198 }
1199
1200 ch := uc.channels.Value(name)
1201 if ch == nil {
1202 // NAMES on a channel we have not joined, forward to downstream
1203 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1204 channel := dc.marshalEntity(uc.network, name)
1205
1206 dc.SendMessage(&irc.Message{
1207 Prefix: dc.srv.prefix(),
1208 Command: irc.RPL_ENDOFNAMES,
1209 Params: []string{dc.nick, channel, "End of /NAMES list"},
1210 })
1211 })
1212 return nil
1213 }
1214
1215 if ch.complete {
1216 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
1217 }
1218 ch.complete = true
1219
1220 c := uc.network.channels.Value(name)
1221 if c == nil || !c.Detached {
1222 uc.forEachDownstream(func(dc *downstreamConn) {
1223 forwardChannel(dc, ch)
1224 })
1225 }
1226 case irc.RPL_WHOREPLY:
1227 var channel, username, host, server, nick, mode, trailing string
1228 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
1229 return err
1230 }
1231
1232 dc, cmd := uc.currentPendingCommand("WHO")
1233 if cmd == nil {
1234 return fmt.Errorf("unexpected RPL_WHOREPLY: no matching pending WHO")
1235 } else if dc == nil {
1236 return nil
1237 }
1238
1239 parts := strings.SplitN(trailing, " ", 2)
1240 if len(parts) != 2 {
1241 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
1242 }
1243 realname := parts[1]
1244 hops, err := strconv.Atoi(parts[0])
1245 if err != nil {
1246 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
1247 }
1248 hops++
1249
1250 trailing = strconv.Itoa(hops) + " " + realname
1251
1252 if channel != "*" {
1253 channel = dc.marshalEntity(uc.network, channel)
1254 }
1255 nick = dc.marshalEntity(uc.network, nick)
1256 dc.SendMessage(&irc.Message{
1257 Prefix: dc.srv.prefix(),
1258 Command: irc.RPL_WHOREPLY,
1259 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
1260 })
1261 case rpl_whospcrpl:
1262 dc, cmd := uc.currentPendingCommand("WHO")
1263 if cmd == nil {
1264 return fmt.Errorf("unexpected RPL_WHOSPCRPL: no matching pending WHO")
1265 } else if dc == nil {
1266 return nil
1267 }
1268
1269 // Only supported in single-upstream mode, so forward as-is
1270 dc.SendMessage(msg)
1271 case irc.RPL_ENDOFWHO:
1272 var name string
1273 if err := parseMessageParams(msg, nil, &name); err != nil {
1274 return err
1275 }
1276
1277 dc, cmd := uc.dequeueCommand("WHO")
1278 if cmd == nil {
1279 return fmt.Errorf("unexpected RPL_ENDOFWHO: no matching pending WHO")
1280 } else if dc == nil {
1281 return nil
1282 }
1283
1284 mask := "*"
1285 if len(cmd.Params) > 0 {
1286 mask = cmd.Params[0]
1287 }
1288 dc.SendMessage(&irc.Message{
1289 Prefix: dc.srv.prefix(),
1290 Command: irc.RPL_ENDOFWHO,
1291 Params: []string{dc.nick, mask, "End of /WHO list"},
1292 })
1293 case irc.RPL_WHOISUSER:
1294 var nick, username, host, realname string
1295 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
1296 return err
1297 }
1298
1299 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1300 nick := dc.marshalEntity(uc.network, nick)
1301 dc.SendMessage(&irc.Message{
1302 Prefix: dc.srv.prefix(),
1303 Command: irc.RPL_WHOISUSER,
1304 Params: []string{dc.nick, nick, username, host, "*", realname},
1305 })
1306 })
1307 case irc.RPL_WHOISSERVER:
1308 var nick, server, serverInfo string
1309 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
1310 return err
1311 }
1312
1313 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1314 nick := dc.marshalEntity(uc.network, nick)
1315 dc.SendMessage(&irc.Message{
1316 Prefix: dc.srv.prefix(),
1317 Command: irc.RPL_WHOISSERVER,
1318 Params: []string{dc.nick, nick, server, serverInfo},
1319 })
1320 })
1321 case irc.RPL_WHOISOPERATOR:
1322 var nick string
1323 if err := parseMessageParams(msg, nil, &nick); err != nil {
1324 return err
1325 }
1326
1327 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1328 nick := dc.marshalEntity(uc.network, nick)
1329 dc.SendMessage(&irc.Message{
1330 Prefix: dc.srv.prefix(),
1331 Command: irc.RPL_WHOISOPERATOR,
1332 Params: []string{dc.nick, nick, "is an IRC operator"},
1333 })
1334 })
1335 case irc.RPL_WHOISIDLE:
1336 var nick string
1337 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
1338 return err
1339 }
1340
1341 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1342 nick := dc.marshalEntity(uc.network, nick)
1343 params := []string{dc.nick, nick}
1344 params = append(params, msg.Params[2:]...)
1345 dc.SendMessage(&irc.Message{
1346 Prefix: dc.srv.prefix(),
1347 Command: irc.RPL_WHOISIDLE,
1348 Params: params,
1349 })
1350 })
1351 case irc.RPL_WHOISCHANNELS:
1352 var nick, channelList string
1353 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
1354 return err
1355 }
1356 channels := splitSpace(channelList)
1357
1358 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1359 nick := dc.marshalEntity(uc.network, nick)
1360 channelList := make([]string, len(channels))
1361 for i, channel := range channels {
1362 prefix, channel := uc.parseMembershipPrefix(channel)
1363 channel = dc.marshalEntity(uc.network, channel)
1364 channelList[i] = prefix.Format(dc) + channel
1365 }
1366 channels := strings.Join(channelList, " ")
1367 dc.SendMessage(&irc.Message{
1368 Prefix: dc.srv.prefix(),
1369 Command: irc.RPL_WHOISCHANNELS,
1370 Params: []string{dc.nick, nick, channels},
1371 })
1372 })
1373 case irc.RPL_ENDOFWHOIS:
1374 var nick string
1375 if err := parseMessageParams(msg, nil, &nick); err != nil {
1376 return err
1377 }
1378
1379 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1380 nick := dc.marshalEntity(uc.network, nick)
1381 dc.SendMessage(&irc.Message{
1382 Prefix: dc.srv.prefix(),
1383 Command: irc.RPL_ENDOFWHOIS,
1384 Params: []string{dc.nick, nick, "End of /WHOIS list"},
1385 })
1386 })
1387 case "INVITE":
1388 var nick, channel string
1389 if err := parseMessageParams(msg, &nick, &channel); err != nil {
1390 return err
1391 }
1392
1393 weAreInvited := uc.isOurNick(nick)
1394
1395 uc.forEachDownstream(func(dc *downstreamConn) {
1396 if !weAreInvited && !dc.caps["invite-notify"] {
1397 return
1398 }
1399 dc.SendMessage(&irc.Message{
1400 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1401 Command: "INVITE",
1402 Params: []string{dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1403 })
1404 })
1405 case irc.RPL_INVITING:
1406 var nick, channel string
1407 if err := parseMessageParams(msg, nil, &nick, &channel); err != nil {
1408 return err
1409 }
1410
1411 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1412 dc.SendMessage(&irc.Message{
1413 Prefix: dc.srv.prefix(),
1414 Command: irc.RPL_INVITING,
1415 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1416 })
1417 })
1418 case irc.RPL_MONONLINE, irc.RPL_MONOFFLINE:
1419 var targetsStr string
1420 if err := parseMessageParams(msg, nil, &targetsStr); err != nil {
1421 return err
1422 }
1423 targets := strings.Split(targetsStr, ",")
1424
1425 online := msg.Command == irc.RPL_MONONLINE
1426 for _, target := range targets {
1427 prefix := irc.ParsePrefix(target)
1428 uc.monitored.SetValue(prefix.Name, online)
1429 }
1430
1431 uc.forEachDownstream(func(dc *downstreamConn) {
1432 for _, target := range targets {
1433 prefix := irc.ParsePrefix(target)
1434 if dc.monitored.Has(prefix.Name) {
1435 dc.SendMessage(&irc.Message{
1436 Prefix: dc.srv.prefix(),
1437 Command: msg.Command,
1438 Params: []string{dc.nick, target},
1439 })
1440 }
1441 }
1442 })
1443 case irc.ERR_MONLISTFULL:
1444 var limit, targetsStr string
1445 if err := parseMessageParams(msg, nil, &limit, &targetsStr); err != nil {
1446 return err
1447 }
1448
1449 targets := strings.Split(targetsStr, ",")
1450 uc.forEachDownstream(func(dc *downstreamConn) {
1451 for _, target := range targets {
1452 if dc.monitored.Has(target) {
1453 dc.SendMessage(&irc.Message{
1454 Prefix: dc.srv.prefix(),
1455 Command: msg.Command,
1456 Params: []string{dc.nick, limit, target},
1457 })
1458 }
1459 }
1460 })
1461 case irc.RPL_AWAY:
1462 var nick, reason string
1463 if err := parseMessageParams(msg, nil, &nick, &reason); err != nil {
1464 return err
1465 }
1466
1467 uc.forEachDownstream(func(dc *downstreamConn) {
1468 dc.SendMessage(&irc.Message{
1469 Prefix: dc.srv.prefix(),
1470 Command: irc.RPL_AWAY,
1471 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), reason},
1472 })
1473 })
1474 case "AWAY", "ACCOUNT":
1475 if msg.Prefix == nil {
1476 return fmt.Errorf("expected a prefix")
1477 }
1478
1479 uc.forEachDownstream(func(dc *downstreamConn) {
1480 dc.SendMessage(&irc.Message{
1481 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1482 Command: msg.Command,
1483 Params: msg.Params,
1484 })
1485 })
1486 case irc.RPL_BANLIST, irc.RPL_INVITELIST, irc.RPL_EXCEPTLIST:
1487 var channel, mask string
1488 if err := parseMessageParams(msg, nil, &channel, &mask); err != nil {
1489 return err
1490 }
1491 var addNick, addTime string
1492 if len(msg.Params) >= 5 {
1493 addNick = msg.Params[3]
1494 addTime = msg.Params[4]
1495 }
1496
1497 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1498 channel := dc.marshalEntity(uc.network, channel)
1499
1500 var params []string
1501 if addNick != "" && addTime != "" {
1502 addNick := dc.marshalEntity(uc.network, addNick)
1503 params = []string{dc.nick, channel, mask, addNick, addTime}
1504 } else {
1505 params = []string{dc.nick, channel, mask}
1506 }
1507
1508 dc.SendMessage(&irc.Message{
1509 Prefix: dc.srv.prefix(),
1510 Command: msg.Command,
1511 Params: params,
1512 })
1513 })
1514 case irc.RPL_ENDOFBANLIST, irc.RPL_ENDOFINVITELIST, irc.RPL_ENDOFEXCEPTLIST:
1515 var channel, trailing string
1516 if err := parseMessageParams(msg, nil, &channel, &trailing); err != nil {
1517 return err
1518 }
1519
1520 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1521 upstreamChannel := dc.marshalEntity(uc.network, channel)
1522 dc.SendMessage(&irc.Message{
1523 Prefix: dc.srv.prefix(),
1524 Command: msg.Command,
1525 Params: []string{dc.nick, upstreamChannel, trailing},
1526 })
1527 })
1528 case irc.ERR_UNKNOWNCOMMAND, irc.RPL_TRYAGAIN:
1529 var command, reason string
1530 if err := parseMessageParams(msg, nil, &command, &reason); err != nil {
1531 return err
1532 }
1533
1534 if command == "LIST" || command == "WHO" {
1535 dc, _ := uc.dequeueCommand(command)
1536 if dc != nil && downstreamID == 0 {
1537 downstreamID = dc.id
1538 }
1539 }
1540
1541 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1542 dc.SendMessage(&irc.Message{
1543 Prefix: uc.srv.prefix(),
1544 Command: msg.Command,
1545 Params: []string{dc.nick, command, reason},
1546 })
1547 })
1548 case "ACK":
1549 // Ignore
1550 case irc.RPL_NOWAWAY, irc.RPL_UNAWAY:
1551 // Ignore
1552 case irc.RPL_YOURHOST, irc.RPL_CREATED:
1553 // Ignore
1554 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
1555 fallthrough
1556 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
1557 fallthrough
1558 case rpl_localusers, rpl_globalusers:
1559 fallthrough
1560 case irc.RPL_MOTDSTART, irc.RPL_MOTD:
1561 // Ignore these messages if they're part of the initial registration
1562 // message burst. Forward them if the user explicitly asked for them.
1563 if !uc.gotMotd {
1564 return nil
1565 }
1566
1567 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1568 dc.SendMessage(&irc.Message{
1569 Prefix: uc.srv.prefix(),
1570 Command: msg.Command,
1571 Params: msg.Params,
1572 })
1573 })
1574 case irc.RPL_LISTSTART:
1575 // Ignore
1576 case "ERROR":
1577 var text string
1578 if err := parseMessageParams(msg, &text); err != nil {
1579 return err
1580 }
1581 return fmt.Errorf("fatal server error: %v", text)
1582 case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKNAMEINUSE, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP:
1583 if !uc.registered {
1584 text := msg.Params[len(msg.Params)-1]
1585 return registrationError(text)
1586 }
1587 fallthrough
1588 default:
1589 uc.logger.Printf("unhandled message: %v", msg)
1590
1591 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1592 // best effort marshaling for unknown messages, replies and errors:
1593 // most numerics start with the user nick, marshal it if that's the case
1594 // otherwise, conservately keep the params without marshaling
1595 params := msg.Params
1596 if _, err := strconv.Atoi(msg.Command); err == nil { // numeric
1597 if len(msg.Params) > 0 && isOurNick(uc.network, msg.Params[0]) {
1598 params[0] = dc.nick
1599 }
1600 }
1601 dc.SendMessage(&irc.Message{
1602 Prefix: uc.srv.prefix(),
1603 Command: msg.Command,
1604 Params: params,
1605 })
1606 })
1607 }
1608 return nil
1609}
1610
1611func (uc *upstreamConn) handleDetachedMessage(ch *Channel, msg *irc.Message) {
1612 if uc.network.detachedMessageNeedsRelay(ch, msg) {
1613 uc.forEachDownstream(func(dc *downstreamConn) {
1614 dc.relayDetachedMessage(uc.network, msg)
1615 })
1616 }
1617 if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && uc.network.isHighlight(msg)) {
1618 uc.network.attach(ch)
1619 if err := uc.srv.db.StoreChannel(context.TODO(), uc.network.ID, ch); err != nil {
1620 uc.logger.Printf("failed to update channel %q: %v", ch.Name, err)
1621 }
1622 }
1623}
1624
1625func (uc *upstreamConn) handleChanModes(s string) error {
1626 parts := strings.SplitN(s, ",", 5)
1627 if len(parts) < 4 {
1628 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", s)
1629 }
1630 modes := make(map[byte]channelModeType)
1631 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
1632 for j := 0; j < len(parts[i]); j++ {
1633 mode := parts[i][j]
1634 modes[mode] = mt
1635 }
1636 }
1637 uc.availableChannelModes = modes
1638 return nil
1639}
1640
1641func (uc *upstreamConn) handleMemberships(s string) error {
1642 if s == "" {
1643 uc.availableMemberships = nil
1644 return nil
1645 }
1646
1647 if s[0] != '(' {
1648 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1649 }
1650 sep := strings.IndexByte(s, ')')
1651 if sep < 0 || len(s) != sep*2 {
1652 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1653 }
1654 memberships := make([]membership, len(s)/2-1)
1655 for i := range memberships {
1656 memberships[i] = membership{
1657 Mode: s[i+1],
1658 Prefix: s[sep+i+1],
1659 }
1660 }
1661 uc.availableMemberships = memberships
1662 return nil
1663}
1664
1665func (uc *upstreamConn) handleSupportedCaps(capsStr string) {
1666 caps := strings.Fields(capsStr)
1667 for _, s := range caps {
1668 kv := strings.SplitN(s, "=", 2)
1669 k := strings.ToLower(kv[0])
1670 var v string
1671 if len(kv) == 2 {
1672 v = kv[1]
1673 }
1674 uc.supportedCaps[k] = v
1675 }
1676}
1677
1678func (uc *upstreamConn) requestCaps() {
1679 var requestCaps []string
1680 for c := range permanentUpstreamCaps {
1681 if _, ok := uc.supportedCaps[c]; ok && !uc.caps[c] {
1682 requestCaps = append(requestCaps, c)
1683 }
1684 }
1685
1686 if uc.requestSASL() && !uc.caps["sasl"] {
1687 requestCaps = append(requestCaps, "sasl")
1688 }
1689
1690 if len(requestCaps) == 0 {
1691 return
1692 }
1693
1694 uc.SendMessage(&irc.Message{
1695 Command: "CAP",
1696 Params: []string{"REQ", strings.Join(requestCaps, " ")},
1697 })
1698}
1699
1700func (uc *upstreamConn) requestSASL() bool {
1701 if uc.network.SASL.Mechanism == "" {
1702 return false
1703 }
1704
1705 v, ok := uc.supportedCaps["sasl"]
1706 if !ok {
1707 return false
1708 }
1709 if v != "" {
1710 mechanisms := strings.Split(v, ",")
1711 found := false
1712 for _, mech := range mechanisms {
1713 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
1714 found = true
1715 break
1716 }
1717 }
1718 if !found {
1719 return false
1720 }
1721 }
1722
1723 return true
1724}
1725
1726func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
1727 uc.caps[name] = ok
1728
1729 switch name {
1730 case "sasl":
1731 if !ok {
1732 uc.logger.Printf("server refused to acknowledge the SASL capability")
1733 return nil
1734 }
1735
1736 auth := &uc.network.SASL
1737 switch auth.Mechanism {
1738 case "PLAIN":
1739 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
1740 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
1741 case "EXTERNAL":
1742 uc.logger.Printf("starting SASL EXTERNAL authentication")
1743 uc.saslClient = sasl.NewExternalClient("")
1744 default:
1745 return fmt.Errorf("unsupported SASL mechanism %q", name)
1746 }
1747
1748 uc.SendMessage(&irc.Message{
1749 Command: "AUTHENTICATE",
1750 Params: []string{auth.Mechanism},
1751 })
1752 default:
1753 if permanentUpstreamCaps[name] {
1754 break
1755 }
1756 uc.logger.Printf("received CAP ACK/NAK for a cap we don't support: %v", name)
1757 }
1758 return nil
1759}
1760
1761func splitSpace(s string) []string {
1762 return strings.FieldsFunc(s, func(r rune) bool {
1763 return r == ' '
1764 })
1765}
1766
1767func (uc *upstreamConn) register() {
1768 uc.nick = GetNick(&uc.user.User, &uc.network.Network)
1769 uc.nickCM = uc.network.casemap(uc.nick)
1770 uc.username = GetUsername(&uc.user.User, &uc.network.Network)
1771 uc.realname = GetRealname(&uc.user.User, &uc.network.Network)
1772
1773 uc.SendMessage(&irc.Message{
1774 Command: "CAP",
1775 Params: []string{"LS", "302"},
1776 })
1777
1778 if uc.network.Pass != "" {
1779 uc.SendMessage(&irc.Message{
1780 Command: "PASS",
1781 Params: []string{uc.network.Pass},
1782 })
1783 }
1784
1785 uc.SendMessage(&irc.Message{
1786 Command: "NICK",
1787 Params: []string{uc.nick},
1788 })
1789 uc.SendMessage(&irc.Message{
1790 Command: "USER",
1791 Params: []string{uc.username, "0", "*", uc.realname},
1792 })
1793}
1794
1795func (uc *upstreamConn) runUntilRegistered() error {
1796 for !uc.registered {
1797 msg, err := uc.ReadMessage()
1798 if err != nil {
1799 return fmt.Errorf("failed to read message: %v", err)
1800 }
1801
1802 if err := uc.handleMessage(msg); err != nil {
1803 if _, ok := err.(registrationError); ok {
1804 return err
1805 } else {
1806 msg.Tags = nil // prevent message tags from cluttering logs
1807 return fmt.Errorf("failed to handle message %q: %v", msg, err)
1808 }
1809 }
1810 }
1811
1812 for _, command := range uc.network.ConnectCommands {
1813 m, err := irc.ParseMessage(command)
1814 if err != nil {
1815 uc.logger.Printf("failed to parse connect command %q: %v", command, err)
1816 } else {
1817 uc.SendMessage(m)
1818 }
1819 }
1820
1821 return nil
1822}
1823
1824func (uc *upstreamConn) readMessages(ch chan<- event) error {
1825 for {
1826 msg, err := uc.ReadMessage()
1827 if errors.Is(err, io.EOF) {
1828 break
1829 } else if err != nil {
1830 return fmt.Errorf("failed to read IRC command: %v", err)
1831 }
1832
1833 ch <- eventUpstreamMessage{msg, uc}
1834 }
1835
1836 return nil
1837}
1838
1839func (uc *upstreamConn) SendMessage(msg *irc.Message) {
1840 if !uc.caps["message-tags"] {
1841 msg = msg.Copy()
1842 msg.Tags = nil
1843 }
1844
1845 uc.conn.SendMessage(msg)
1846}
1847
1848func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
1849 if uc.caps["labeled-response"] {
1850 if msg.Tags == nil {
1851 msg.Tags = make(map[string]irc.TagValue)
1852 }
1853 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", downstreamID, uc.nextLabelID))
1854 uc.nextLabelID++
1855 }
1856 uc.SendMessage(msg)
1857}
1858
1859// appendLog appends a message to the log file.
1860//
1861// The internal message ID is returned. If the message isn't recorded in the
1862// log file, an empty string is returned.
1863func (uc *upstreamConn) appendLog(entity string, msg *irc.Message) (msgID string) {
1864 if uc.user.msgStore == nil {
1865 return ""
1866 }
1867
1868 // Don't store messages with a server mask target
1869 if strings.HasPrefix(entity, "$") {
1870 return ""
1871 }
1872
1873 entityCM := uc.network.casemap(entity)
1874 if entityCM == "nickserv" {
1875 // The messages sent/received from NickServ may contain
1876 // security-related information (like passwords). Don't store these.
1877 return ""
1878 }
1879
1880 if !uc.network.delivered.HasTarget(entity) {
1881 // This is the first message we receive from this target. Save the last
1882 // message ID in delivery receipts, so that we can send the new message
1883 // in the backlog if an offline client reconnects.
1884 lastID, err := uc.user.msgStore.LastMsgID(&uc.network.Network, entityCM, time.Now())
1885 if err != nil {
1886 uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
1887 return ""
1888 }
1889
1890 uc.network.delivered.ForEachClient(func(clientName string) {
1891 uc.network.delivered.StoreID(entity, clientName, lastID)
1892 })
1893 }
1894
1895 msgID, err := uc.user.msgStore.Append(&uc.network.Network, entityCM, msg)
1896 if err != nil {
1897 uc.logger.Printf("failed to log message: %v", err)
1898 return ""
1899 }
1900
1901 return msgID
1902}
1903
1904// produce appends a message to the logs and forwards it to connected downstream
1905// connections.
1906//
1907// If origin is not nil and origin doesn't support echo-message, the message is
1908// forwarded to all connections except origin.
1909func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstreamConn) {
1910 var msgID string
1911 if target != "" {
1912 msgID = uc.appendLog(target, msg)
1913 }
1914
1915 // Don't forward messages if it's a detached channel
1916 ch := uc.network.channels.Value(target)
1917 detached := ch != nil && ch.Detached
1918
1919 uc.forEachDownstream(func(dc *downstreamConn) {
1920 if !detached && (dc != origin || dc.caps["echo-message"]) {
1921 dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID)
1922 } else {
1923 dc.advanceMessageWithID(msg, msgID)
1924 }
1925 })
1926}
1927
1928func (uc *upstreamConn) updateAway() {
1929 away := true
1930 uc.forEachDownstream(func(*downstreamConn) {
1931 away = false
1932 })
1933 if away == uc.away {
1934 return
1935 }
1936 if away {
1937 uc.SendMessage(&irc.Message{
1938 Command: "AWAY",
1939 Params: []string{"Auto away"},
1940 })
1941 } else {
1942 uc.SendMessage(&irc.Message{
1943 Command: "AWAY",
1944 })
1945 }
1946 uc.away = away
1947}
1948
1949func (uc *upstreamConn) updateChannelAutoDetach(name string) {
1950 uch := uc.channels.Value(name)
1951 if uch == nil {
1952 return
1953 }
1954 ch := uc.network.channels.Value(name)
1955 if ch == nil || ch.Detached {
1956 return
1957 }
1958 uch.updateAutoDetach(ch.DetachAfter)
1959}
1960
1961func (uc *upstreamConn) updateMonitor() {
1962 add := make(map[string]struct{})
1963 var addList []string
1964 seen := make(map[string]struct{})
1965 uc.forEachDownstream(func(dc *downstreamConn) {
1966 for targetCM := range dc.monitored.innerMap {
1967 if !uc.monitored.Has(targetCM) {
1968 if _, ok := add[targetCM]; !ok {
1969 addList = append(addList, targetCM)
1970 }
1971 add[targetCM] = struct{}{}
1972 } else {
1973 seen[targetCM] = struct{}{}
1974 }
1975 }
1976 })
1977
1978 removeAll := true
1979 var removeList []string
1980 for targetCM, entry := range uc.monitored.innerMap {
1981 if _, ok := seen[targetCM]; ok {
1982 removeAll = false
1983 } else {
1984 removeList = append(removeList, entry.originalKey)
1985 }
1986 }
1987
1988 // TODO: better handle the case where len(uc.monitored) + len(addList)
1989 // exceeds the limit, probably by immediately sending ERR_MONLISTFULL?
1990
1991 if removeAll && len(addList) == 0 && len(removeList) > 0 {
1992 // Optimization when the last MONITOR-aware downstream disconnects
1993 uc.SendMessage(&irc.Message{
1994 Command: "MONITOR",
1995 Params: []string{"C"},
1996 })
1997 } else {
1998 msgs := generateMonitor("-", removeList)
1999 msgs = append(msgs, generateMonitor("+", addList)...)
2000 for _, msg := range msgs {
2001 uc.SendMessage(msg)
2002 }
2003 }
2004
2005 for _, target := range removeList {
2006 uc.monitored.Delete(target)
2007 }
2008}
Note: See TracBrowser for help on using the repository browser.