source: code/trunk/upstream.go@ 736

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

Don't retry connecting on permanent failure

Closes: https://todo.sr.ht/~emersion/soju/164

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