source: code/trunk/upstream.go@ 681

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

Remove support for mixed multi-upstream LIST

Multi-upstream connections can still send LIST commands with a
network suffix.

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