source: code/trunk/upstream.go@ 825

Last change on this file since 825 was 804, checked in by koizumi.aoi, 2 years ago

Drunk as I like

Signed-off-by: Aoi K <koizumi.aoi@…>

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