source: code/trunk/upstream.go@ 722

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

Use RPL_LOGGEDIN/OUT to mirror upstream status

This will allow clients to properly show/hide UI to login and
register.

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