source: code/trunk/upstream.go@ 677

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

Fix upstream USER command when both username and nick are empty

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)
[674]1676 uc.username = GetUsername(&uc.user.User, &uc.network.Network)
[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.