source: code/trunk/upstream.go@ 737

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

Mark ACCOUNT_REQUIRED error as permanent connection failure

There's no point in retrying to connect in this case.

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