source: code/trunk/upstream.go@ 721

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

Add missing account-notify to permanentUpstreamCaps

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