source: code/trunk/upstream.go@ 672

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

msgstore: take Network as arg instead of network

The message stores don't need to access the internal network
struct, they just need network metadata such as ID and name.

This can ease moving message stores into a separate package in the
future.

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