source: code/trunk/upstream.go@ 683

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

Add a queue for WHO commands

This has the following upsides:

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