source: code/trunk/upstream.go@ 666

Last change on this file since 666 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
Line 
1package soju
2
3import (
4 "context"
5 "crypto"
6 "crypto/sha256"
7 "crypto/tls"
8 "crypto/x509"
9 "encoding/base64"
10 "errors"
11 "fmt"
12 "io"
13 "net"
14 "strconv"
15 "strings"
16 "time"
17
18 "github.com/emersion/go-sasl"
19 "gopkg.in/irc.v3"
20)
21
22// permanentUpstreamCaps is the static list of upstream capabilities always
23// requested when supported.
24var permanentUpstreamCaps = map[string]bool{
25 "account-tag": true,
26 "away-notify": true,
27 "batch": true,
28 "extended-join": true,
29 "invite-notify": true,
30 "labeled-response": true,
31 "message-tags": true,
32 "multi-prefix": true,
33 "server-time": true,
34 "setname": true,
35}
36
37type registrationError string
38
39func (err registrationError) Error() string {
40 return fmt.Sprintf("registration error: %v", string(err))
41}
42
43type upstreamChannel struct {
44 Name string
45 conn *upstreamConn
46 Topic string
47 TopicWho *irc.Prefix
48 TopicTime time.Time
49 Status channelStatus
50 modes channelModes
51 creationTime string
52 Members membershipsCasemapMap
53 complete bool
54 detachTimer *time.Timer
55}
56
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
75type upstreamConn struct {
76 conn
77
78 network *network
79 user *user
80
81 serverName string
82 availableUserModes string
83 availableChannelModes map[byte]channelModeType
84 availableChannelTypes string
85 availableMemberships []membership
86 isupport map[string]*string
87
88 registered bool
89 nick string
90 nickCM string
91 username string
92 realname string
93 modes userModes
94 channels upstreamChannelCasemapMap
95 supportedCaps map[string]string
96 caps map[string]bool
97 batches map[string]batch
98 away bool
99 account string
100 nextLabelID uint64
101
102 saslClient sasl.Client
103 saslStarted bool
104
105 casemapIsSet bool
106
107 // set of LIST commands in progress, per downstream
108 pendingLISTDownstreamSet map[uint64]struct{}
109
110 gotMotd bool
111}
112
113func connectToUpstream(network *network) (*upstreamConn, error) {
114 logger := &prefixLogger{network.user.logger, fmt.Sprintf("upstream %q: ", network.GetName())}
115
116 dialer := net.Dialer{Timeout: connectTimeout}
117
118 u, err := network.URL()
119 if err != nil {
120 return nil, err
121 }
122
123 var netConn net.Conn
124 switch u.Scheme {
125 case "ircs":
126 addr := u.Host
127 host, _, err := net.SplitHostPort(u.Host)
128 if err != nil {
129 host = u.Host
130 addr = u.Host + ":6697"
131 }
132
133 logger.Printf("connecting to TLS server at address %q", addr)
134
135 tlsConfig := &tls.Config{ServerName: host, NextProtos: []string{"irc"}}
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 }
147 tlsConfig.Certificates = []tls.Certificate{
148 {
149 Certificate: [][]byte{network.SASL.External.CertBlob},
150 PrivateKey: key.(crypto.PrivateKey),
151 },
152 }
153 logger.Printf("using TLS client certificate %x", sha256.Sum256(network.SASL.External.CertBlob))
154 }
155
156 netConn, err = dialer.Dial("tcp", addr)
157 if err != nil {
158 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
159 }
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)
165 case "irc+insecure":
166 addr := u.Host
167 if _, _, err := net.SplitHostPort(addr); err != nil {
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)
173 if err != nil {
174 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
175 }
176 case "irc+unix", "unix":
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 }
182 default:
183 return nil, fmt.Errorf("failed to dial %q: unknown scheme: %v", network.Addr, u.Scheme)
184 }
185
186 options := connOptions{
187 Logger: logger,
188 RateLimitDelay: upstreamMessageDelay,
189 RateLimitBurst: upstreamMessageBurst,
190 }
191
192 uc := &upstreamConn{
193 conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options),
194 network: network,
195 user: network.user,
196 channels: upstreamChannelCasemapMap{newCasemapMap(0)},
197 supportedCaps: make(map[string]string),
198 caps: make(map[string]bool),
199 batches: make(map[string]batch),
200 availableChannelTypes: stdChannelTypes,
201 availableChannelModes: stdChannelModes,
202 availableMemberships: stdMemberships,
203 isupport: make(map[string]*string),
204 pendingLISTDownstreamSet: make(map[uint64]struct{}),
205 }
206 return uc, nil
207}
208
209func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
210 uc.network.forEachDownstream(f)
211}
212
213func (uc *upstreamConn) forEachDownstreamByID(id uint64, f func(*downstreamConn)) {
214 uc.forEachDownstream(func(dc *downstreamConn) {
215 if id != 0 && id != dc.id {
216 return
217 }
218 f(dc)
219 })
220}
221
222func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
223 ch := uc.channels.Value(name)
224 if ch == nil {
225 return nil, fmt.Errorf("unknown channel %q", name)
226 }
227 return ch, nil
228}
229
230func (uc *upstreamConn) isChannel(entity string) bool {
231 return strings.ContainsRune(uc.availableChannelTypes, rune(entity[0]))
232}
233
234func (uc *upstreamConn) isOurNick(nick string) bool {
235 return uc.nickCM == uc.network.casemap(nick)
236}
237
238func (uc *upstreamConn) getPendingLIST() *pendingLIST {
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
248func (uc *upstreamConn) endPendingLISTs(all bool) (found bool) {
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) {
271 uc.trySendLIST(pl.downstreamID)
272 })
273 return
274 }
275 }
276 return
277}
278
279func (uc *upstreamConn) trySendLIST(downstreamID uint64) {
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
307func (uc *upstreamConn) parseMembershipPrefix(s string) (ms *memberships, nick string) {
308 memberships := make(memberships, 0, 4)
309 i := 0
310 for _, m := range uc.availableMemberships {
311 if i >= len(s) {
312 break
313 }
314 if s[i] == m.Prefix {
315 memberships = append(memberships, m)
316 i++
317 }
318 }
319 return &memberships, s[i:]
320}
321
322func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
323 var label string
324 if l, ok := msg.GetTag("label"); ok {
325 label = l
326 delete(msg.Tags, "label")
327 }
328
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
336 if label == "" {
337 label = msgBatch.Label
338 }
339 delete(msg.Tags, "batch")
340 }
341
342 var downstreamID uint64 = 0
343 if label != "" {
344 var labelOffset uint64
345 n, err := fmt.Sscanf(label, "sd-%d-%d", &downstreamID, &labelOffset)
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
354 if _, ok := msg.Tags["time"]; !ok {
355 msg.Tags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
356 }
357
358 switch msg.Command {
359 case "PING":
360 uc.SendMessage(&irc.Message{
361 Command: "PONG",
362 Params: msg.Params,
363 })
364 return nil
365 case "NOTICE", "PRIVMSG", "TAGMSG":
366 if msg.Prefix == nil {
367 return fmt.Errorf("expected a prefix")
368 }
369
370 var entity, text string
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 }
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
390 if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message
391 uc.produce("", msg, nil)
392 } else { // regular user message
393 target := entity
394 if uc.isOurNick(target) {
395 target = msg.Prefix.Name
396 }
397
398 ch := uc.network.channels.Value(target)
399 if ch != nil && msg.Command != "TAGMSG" {
400 if ch.Detached {
401 uc.handleDetachedMessage(ch, msg)
402 }
403
404 highlight := uc.network.isHighlight(msg)
405 if ch.DetachOn == FilterMessage || ch.DetachOn == FilterDefault || (ch.DetachOn == FilterHighlight && highlight) {
406 uc.updateChannelAutoDetach(target)
407 }
408 }
409
410 uc.produce(target, msg, nil)
411 }
412 case "CAP":
413 var subCmd string
414 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
415 return err
416 }
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 }
424 caps := subParams[len(subParams)-1]
425 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
426
427 uc.handleSupportedCaps(caps)
428
429 if more {
430 break // wait to receive all capabilities
431 }
432
433 uc.requestCaps()
434
435 if uc.requestSASL() {
436 break // we'll send CAP END after authentication is completed
437 }
438
439 uc.SendMessage(&irc.Message{
440 Command: "CAP",
441 Params: []string{"END"},
442 })
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
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 }
482 default:
483 uc.logger.Printf("unhandled message: %v", msg)
484 }
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 = "+"
531 if len(resp) != 0 {
532 respStr = base64.StdEncoding.EncodeToString(resp)
533 }
534
535 uc.SendMessage(&irc.Message{
536 Command: "AUTHENTICATE",
537 Params: []string{respStr},
538 })
539 case irc.RPL_LOGGEDIN:
540 if err := parseMessageParams(msg, nil, nil, &uc.account); err != nil {
541 return err
542 }
543 uc.logger.Printf("logged in with account %q", uc.account)
544 case irc.RPL_LOGGEDOUT:
545 uc.account = ""
546 uc.logger.Printf("logged out")
547 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
548 var info string
549 if err := parseMessageParams(msg, nil, &info); err != nil {
550 return err
551 }
552 switch msg.Command {
553 case irc.ERR_NICKLOCKED:
554 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
555 case irc.ERR_SASLFAIL:
556 uc.logger.Printf("SASL authentication failed: %v", info)
557 case irc.ERR_SASLTOOLONG:
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 })
568 case irc.RPL_WELCOME:
569 uc.registered = true
570 uc.logger.Printf("connection registered")
571
572 if uc.network.channels.Len() > 0 {
573 var channels, keys []string
574 for _, entry := range uc.network.channels.innerMap {
575 ch := entry.value.(*Channel)
576 channels = append(channels, ch.Name)
577 keys = append(keys, ch.Key)
578 }
579
580 for _, msg := range join(channels, keys) {
581 uc.SendMessage(msg)
582 }
583 }
584 case irc.RPL_MYINFO:
585 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
586 return err
587 }
588 case irc.RPL_ISUPPORT:
589 if err := parseMessageParams(msg, nil, nil); err != nil {
590 return err
591 }
592
593 var downstreamIsupport []string
594 for _, token := range msg.Params[1 : len(msg.Params)-1] {
595 parameter := token
596 var negate, hasValue bool
597 var value string
598 if strings.HasPrefix(token, "-") {
599 negate = true
600 token = token[1:]
601 } else if i := strings.IndexByte(token, '='); i >= 0 {
602 parameter = token[:i]
603 value = token[i+1:]
604 hasValue = true
605 }
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
615 var err error
616 switch parameter {
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
625 case "CHANMODES":
626 if !negate {
627 err = uc.handleChanModes(value)
628 } else {
629 uc.availableChannelModes = stdChannelModes
630 }
631 case "CHANTYPES":
632 if !negate {
633 uc.availableChannelTypes = value
634 } else {
635 uc.availableChannelTypes = stdChannelTypes
636 }
637 case "PREFIX":
638 if !negate {
639 err = uc.handleMemberships(value)
640 } else {
641 uc.availableMemberships = stdMemberships
642 }
643 }
644 if err != nil {
645 return err
646 }
647
648 if passthroughIsupport[parameter] {
649 downstreamIsupport = append(downstreamIsupport, token)
650 }
651 }
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 })
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 }
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
678 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
679 dc.SendMessage(&irc.Message{
680 Prefix: uc.srv.prefix(),
681 Command: msg.Command,
682 Params: msg.Params,
683 })
684 })
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 }
700 label := label
701 if label == "" && msgBatch != nil {
702 label = msgBatch.Label
703 }
704 uc.batches[tag] = batch{
705 Type: batchType,
706 Params: msg.Params[2:],
707 Outer: msgBatch,
708 Label: label,
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 }
719 case "NICK":
720 if msg.Prefix == nil {
721 return fmt.Errorf("expected a prefix")
722 }
723
724 var newNick string
725 if err := parseMessageParams(msg, &newNick); err != nil {
726 return err
727 }
728
729 me := false
730 if uc.isOurNick(msg.Prefix.Name) {
731 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
732 me = true
733 uc.nick = newNick
734 uc.nickCM = uc.network.casemap(uc.nick)
735 }
736
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)
743 uc.appendLog(ch.Name, msg)
744 }
745 }
746
747 if !me {
748 uc.forEachDownstream(func(dc *downstreamConn) {
749 dc.SendMessage(dc.marshalMessage(msg, uc.network))
750 })
751 } else {
752 uc.forEachDownstream(func(dc *downstreamConn) {
753 dc.updateNick()
754 })
755 }
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 }
780 case "JOIN":
781 if msg.Prefix == nil {
782 return fmt.Errorf("expected a prefix")
783 }
784
785 var channels string
786 if err := parseMessageParams(msg, &channels); err != nil {
787 return err
788 }
789
790 for _, ch := range strings.Split(channels, ",") {
791 if uc.isOurNick(msg.Prefix.Name) {
792 uc.logger.Printf("joined channel %q", ch)
793 members := membershipsCasemapMap{newCasemapMap(0)}
794 members.casemap = uc.network.casemap
795 uc.channels.SetValue(ch, &upstreamChannel{
796 Name: ch,
797 conn: uc,
798 Members: members,
799 })
800 uc.updateChannelAutoDetach(ch)
801
802 uc.SendMessage(&irc.Message{
803 Command: "MODE",
804 Params: []string{ch},
805 })
806 } else {
807 ch, err := uc.getChannel(ch)
808 if err != nil {
809 return err
810 }
811 ch.Members.SetValue(msg.Prefix.Name, &memberships{})
812 }
813
814 chMsg := msg.Copy()
815 chMsg.Params[0] = ch
816 uc.produce(ch, chMsg, nil)
817 }
818 case "PART":
819 if msg.Prefix == nil {
820 return fmt.Errorf("expected a prefix")
821 }
822
823 var channels string
824 if err := parseMessageParams(msg, &channels); err != nil {
825 return err
826 }
827
828 for _, ch := range strings.Split(channels, ",") {
829 if uc.isOurNick(msg.Prefix.Name) {
830 uc.logger.Printf("parted channel %q", ch)
831 uch := uc.channels.Value(ch)
832 if uch != nil {
833 uc.channels.Delete(ch)
834 uch.updateAutoDetach(0)
835 }
836 } else {
837 ch, err := uc.getChannel(ch)
838 if err != nil {
839 return err
840 }
841 ch.Members.Delete(msg.Prefix.Name)
842 }
843
844 chMsg := msg.Copy()
845 chMsg.Params[0] = ch
846 uc.produce(ch, chMsg, nil)
847 }
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
858 if uc.isOurNick(user) {
859 uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
860 uc.channels.Delete(channel)
861 } else {
862 ch, err := uc.getChannel(channel)
863 if err != nil {
864 return err
865 }
866 ch.Members.Delete(user)
867 }
868
869 uc.produce(channel, msg, nil)
870 case "QUIT":
871 if msg.Prefix == nil {
872 return fmt.Errorf("expected a prefix")
873 }
874
875 if uc.isOurNick(msg.Prefix.Name) {
876 uc.logger.Printf("quit")
877 }
878
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)
883
884 uc.appendLog(ch.Name, msg)
885 }
886 }
887
888 if msg.Prefix.Name != uc.nick {
889 uc.forEachDownstream(func(dc *downstreamConn) {
890 dc.SendMessage(dc.marshalMessage(msg, uc.network))
891 })
892 }
893 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
894 var name, topic string
895 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
896 return err
897 }
898 ch, err := uc.getChannel(name)
899 if err != nil {
900 return err
901 }
902 if msg.Command == irc.RPL_TOPIC {
903 ch.Topic = topic
904 } else {
905 ch.Topic = ""
906 }
907 case "TOPIC":
908 if msg.Prefix == nil {
909 return fmt.Errorf("expected a prefix")
910 }
911
912 var name string
913 if err := parseMessageParams(msg, &name); err != nil {
914 return err
915 }
916 ch, err := uc.getChannel(name)
917 if err != nil {
918 return err
919 }
920 if len(msg.Params) > 1 {
921 ch.Topic = msg.Params[1]
922 ch.TopicWho = msg.Prefix.Copy()
923 ch.TopicTime = time.Now() // TODO use msg.Tags["time"]
924 } else {
925 ch.Topic = ""
926 }
927 uc.produce(ch.Name, msg, nil)
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 }
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 })
950 } else { // channel mode change
951 ch, err := uc.getChannel(name)
952 if err != nil {
953 return err
954 }
955
956 needMarshaling, err := applyChannelModes(ch, modeStr, msg.Params[2:])
957 if err != nil {
958 return err
959 }
960
961 uc.appendLog(ch.Name, msg)
962
963 c := uc.network.channels.Value(name)
964 if c == nil || !c.Detached {
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 }
975 }
976
977 dc.SendMessage(&irc.Message{
978 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
979 Command: "MODE",
980 Params: params,
981 })
982 })
983 }
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 }
998
999 uc.forEachDownstream(func(dc *downstreamConn) {
1000 if dc.upstream() == nil {
1001 return
1002 }
1003
1004 dc.SendMessage(msg)
1005 })
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)
1023 if _, err := applyChannelModes(ch, modeStr, msg.Params[3:]); err != nil {
1024 return err
1025 }
1026 if firstMode {
1027 c := uc.network.channels.Value(channel)
1028 if c == nil || !c.Detached {
1029 modeStr, modeParams := ch.modes.Format()
1030
1031 uc.forEachDownstream(func(dc *downstreamConn) {
1032 params := []string{dc.nick, dc.marshalEntity(uc.network, channel), modeStr}
1033 params = append(params, modeParams...)
1034
1035 dc.SendMessage(&irc.Message{
1036 Prefix: dc.srv.prefix(),
1037 Command: irc.RPL_CHANNELMODEIS,
1038 Params: params,
1039 })
1040 })
1041 }
1042 }
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,
1061 Params: []string{dc.nick, dc.marshalEntity(uc.network, ch.Name), creationTime},
1062 })
1063 })
1064 }
1065 case rpl_topicwhotime:
1066 var name, who, timeStr string
1067 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
1068 return err
1069 }
1070 ch, err := uc.getChannel(name)
1071 if err != nil {
1072 return err
1073 }
1074 firstTopicWhoTime := ch.TopicWho == nil
1075 ch.TopicWho = irc.ParsePrefix(who)
1076 sec, err := strconv.ParseInt(timeStr, 10, 64)
1077 if err != nil {
1078 return fmt.Errorf("failed to parse topic time: %v", err)
1079 }
1080 ch.TopicTime = time.Unix(sec, 0)
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 }
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
1102 pl := uc.getPendingLIST()
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,
1111 Params: []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
1112 })
1113 })
1114 case irc.RPL_LISTEND:
1115 ok := uc.endPendingLISTs(false)
1116 if !ok {
1117 return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
1118 }
1119 case irc.RPL_NAMREPLY:
1120 var name, statusStr, members string
1121 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
1122 return err
1123 }
1124
1125 ch := uc.channels.Value(name)
1126 if ch == nil {
1127 // NAMES on a channel we have not joined, forward to downstream
1128 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1129 channel := dc.marshalEntity(uc.network, name)
1130 members := splitSpace(members)
1131 for i, member := range members {
1132 memberships, nick := uc.parseMembershipPrefix(member)
1133 members[i] = memberships.Format(dc) + dc.marshalEntity(uc.network, nick)
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
1144 }
1145
1146 status, err := parseChannelStatus(statusStr)
1147 if err != nil {
1148 return err
1149 }
1150 ch.Status = status
1151
1152 for _, s := range splitSpace(members) {
1153 memberships, nick := uc.parseMembershipPrefix(s)
1154 ch.Members.SetValue(nick, memberships)
1155 }
1156 case irc.RPL_ENDOFNAMES:
1157 var name string
1158 if err := parseMessageParams(msg, nil, &name); err != nil {
1159 return err
1160 }
1161
1162 ch := uc.channels.Value(name)
1163 if ch == nil {
1164 // NAMES on a channel we have not joined, forward to downstream
1165 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1166 channel := dc.marshalEntity(uc.network, name)
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
1175 }
1176
1177 if ch.complete {
1178 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
1179 }
1180 ch.complete = true
1181
1182 c := uc.network.channels.Value(name)
1183 if c == nil || !c.Detached {
1184 uc.forEachDownstream(func(dc *downstreamConn) {
1185 forwardChannel(dc, ch)
1186 })
1187 }
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
1207 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1208 channel := channel
1209 if channel != "*" {
1210 channel = dc.marshalEntity(uc.network, channel)
1211 }
1212 nick := dc.marshalEntity(uc.network, nick)
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
1225 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1226 name := name
1227 if name != "*" {
1228 // TODO: support WHO masks
1229 name = dc.marshalEntity(uc.network, name)
1230 }
1231 dc.SendMessage(&irc.Message{
1232 Prefix: dc.srv.prefix(),
1233 Command: irc.RPL_ENDOFWHO,
1234 Params: []string{dc.nick, name, "End of /WHO list"},
1235 })
1236 })
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
1243 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1244 nick := dc.marshalEntity(uc.network, nick)
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
1257 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1258 nick := dc.marshalEntity(uc.network, nick)
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
1271 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1272 nick := dc.marshalEntity(uc.network, nick)
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
1285 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1286 nick := dc.marshalEntity(uc.network, nick)
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 }
1300 channels := splitSpace(channelList)
1301
1302 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1303 nick := dc.marshalEntity(uc.network, nick)
1304 channelList := make([]string, len(channels))
1305 for i, channel := range channels {
1306 prefix, channel := uc.parseMembershipPrefix(channel)
1307 channel = dc.marshalEntity(uc.network, channel)
1308 channelList[i] = prefix.Format(dc) + channel
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
1323 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1324 nick := dc.marshalEntity(uc.network, nick)
1325 dc.SendMessage(&irc.Message{
1326 Prefix: dc.srv.prefix(),
1327 Command: irc.RPL_ENDOFWHOIS,
1328 Params: []string{dc.nick, nick, "End of /WHOIS list"},
1329 })
1330 })
1331 case "INVITE":
1332 var nick, channel string
1333 if err := parseMessageParams(msg, &nick, &channel); err != nil {
1334 return err
1335 }
1336
1337 weAreInvited := uc.isOurNick(nick)
1338
1339 uc.forEachDownstream(func(dc *downstreamConn) {
1340 if !weAreInvited && !dc.caps["invite-notify"] {
1341 return
1342 }
1343 dc.SendMessage(&irc.Message{
1344 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1345 Command: "INVITE",
1346 Params: []string{dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1347 })
1348 })
1349 case irc.RPL_INVITING:
1350 var nick, channel string
1351 if err := parseMessageParams(msg, nil, &nick, &channel); err != nil {
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,
1359 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1360 })
1361 })
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
1368 uc.forEachDownstream(func(dc *downstreamConn) {
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 })
1375 case "AWAY", "ACCOUNT":
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),
1383 Command: msg.Command,
1384 Params: msg.Params,
1385 })
1386 })
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 })
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
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},
1447 })
1448 })
1449 case "ACK":
1450 // Ignore
1451 case irc.RPL_NOWAWAY, irc.RPL_UNAWAY:
1452 // Ignore
1453 case irc.RPL_YOURHOST, irc.RPL_CREATED:
1454 // Ignore
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 })
1460 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
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
1466 case irc.RPL_MOTDSTART, irc.RPL_MOTD:
1467 // Ignore these messages if they're part of the initial registration
1468 // message burst. Forward them if the user explicitly asked for them.
1469 if !uc.gotMotd {
1470 return nil
1471 }
1472
1473 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1474 dc.SendMessage(&irc.Message{
1475 Prefix: uc.srv.prefix(),
1476 Command: msg.Command,
1477 Params: msg.Params,
1478 })
1479 })
1480 case irc.RPL_LISTSTART:
1481 // Ignore
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)
1488 case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKNAMEINUSE, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP:
1489 if !uc.registered {
1490 text := msg.Params[len(msg.Params)-1]
1491 return registrationError(text)
1492 }
1493 fallthrough
1494 default:
1495 uc.logger.Printf("unhandled message: %v", msg)
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
1505 }
1506 }
1507 dc.SendMessage(&irc.Message{
1508 Prefix: uc.srv.prefix(),
1509 Command: msg.Command,
1510 Params: params,
1511 })
1512 })
1513 }
1514 return nil
1515}
1516
1517func (uc *upstreamConn) handleDetachedMessage(ch *Channel, msg *irc.Message) {
1518 if uc.network.detachedMessageNeedsRelay(ch, msg) {
1519 uc.forEachDownstream(func(dc *downstreamConn) {
1520 dc.relayDetachedMessage(uc.network, msg)
1521 })
1522 }
1523 if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && uc.network.isHighlight(msg)) {
1524 uc.network.attach(ch)
1525 if err := uc.srv.db.StoreChannel(context.TODO(), uc.network.ID, ch); err != nil {
1526 uc.logger.Printf("failed to update channel %q: %v", ch.Name, err)
1527 }
1528 }
1529}
1530
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
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
1586 for c := range permanentUpstreamCaps {
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
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)
1647 case "EXTERNAL":
1648 uc.logger.Printf("starting SASL EXTERNAL authentication")
1649 uc.saslClient = sasl.NewExternalClient("")
1650 default:
1651 return fmt.Errorf("unsupported SASL mechanism %q", name)
1652 }
1653
1654 uc.SendMessage(&irc.Message{
1655 Command: "AUTHENTICATE",
1656 Params: []string{auth.Mechanism},
1657 })
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)
1663 }
1664 return nil
1665}
1666
1667func splitSpace(s string) []string {
1668 return strings.FieldsFunc(s, func(r rune) bool {
1669 return r == ' '
1670 })
1671}
1672
1673func (uc *upstreamConn) register() {
1674 uc.nick = GetNick(&uc.user.User, &uc.network.Network)
1675 uc.nickCM = uc.network.casemap(uc.nick)
1676 uc.username = uc.network.GetUsername()
1677 uc.realname = GetRealname(&uc.user.User, &uc.network.Network)
1678
1679 uc.SendMessage(&irc.Message{
1680 Command: "CAP",
1681 Params: []string{"LS", "302"},
1682 })
1683
1684 if uc.network.Pass != "" {
1685 uc.SendMessage(&irc.Message{
1686 Command: "PASS",
1687 Params: []string{uc.network.Pass},
1688 })
1689 }
1690
1691 uc.SendMessage(&irc.Message{
1692 Command: "NICK",
1693 Params: []string{uc.nick},
1694 })
1695 uc.SendMessage(&irc.Message{
1696 Command: "USER",
1697 Params: []string{uc.username, "0", "*", uc.realname},
1698 })
1699}
1700
1701func (uc *upstreamConn) runUntilRegistered() error {
1702 for !uc.registered {
1703 msg, err := uc.ReadMessage()
1704 if err != nil {
1705 return fmt.Errorf("failed to read message: %v", err)
1706 }
1707
1708 if err := uc.handleMessage(msg); err != nil {
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 }
1715 }
1716 }
1717
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
1727 return nil
1728}
1729
1730func (uc *upstreamConn) readMessages(ch chan<- event) error {
1731 for {
1732 msg, err := uc.ReadMessage()
1733 if errors.Is(err, io.EOF) {
1734 break
1735 } else if err != nil {
1736 return fmt.Errorf("failed to read IRC command: %v", err)
1737 }
1738
1739 ch <- eventUpstreamMessage{msg, uc}
1740 }
1741
1742 return nil
1743}
1744
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
1754func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
1755 if uc.caps["labeled-response"] {
1756 if msg.Tags == nil {
1757 msg.Tags = make(map[string]irc.TagValue)
1758 }
1759 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", downstreamID, uc.nextLabelID))
1760 uc.nextLabelID++
1761 }
1762 uc.SendMessage(msg)
1763}
1764
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) {
1770 if uc.user.msgStore == nil {
1771 return ""
1772 }
1773
1774 // Don't store messages with a server mask target
1775 if strings.HasPrefix(entity, "$") {
1776 return ""
1777 }
1778
1779 entityCM := uc.network.casemap(entity)
1780 if entityCM == "nickserv" {
1781 // The messages sent/received from NickServ may contain
1782 // security-related information (like passwords). Don't store these.
1783 return ""
1784 }
1785
1786 if !uc.network.delivered.HasTarget(entity) {
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.
1790 lastID, err := uc.user.msgStore.LastMsgID(&uc.network.Network, entityCM, time.Now())
1791 if err != nil {
1792 uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
1793 return ""
1794 }
1795
1796 uc.network.delivered.ForEachClient(func(clientName string) {
1797 uc.network.delivered.StoreID(entity, clientName, lastID)
1798 })
1799 }
1800
1801 msgID, err := uc.user.msgStore.Append(&uc.network.Network, entityCM, msg)
1802 if err != nil {
1803 uc.logger.Printf("failed to log message: %v", err)
1804 return ""
1805 }
1806
1807 return msgID
1808}
1809
1810// produce appends a message to the logs and forwards it to connected downstream
1811// connections.
1812//
1813// If origin is not nil and origin doesn't support echo-message, the message is
1814// forwarded to all connections except origin.
1815func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstreamConn) {
1816 var msgID string
1817 if target != "" {
1818 msgID = uc.appendLog(target, msg)
1819 }
1820
1821 // Don't forward messages if it's a detached channel
1822 ch := uc.network.channels.Value(target)
1823 detached := ch != nil && ch.Detached
1824
1825 uc.forEachDownstream(func(dc *downstreamConn) {
1826 if !detached && (dc != origin || dc.caps["echo-message"]) {
1827 dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID)
1828 } else {
1829 dc.advanceMessageWithID(msg, msgID)
1830 }
1831 })
1832}
1833
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}
1854
1855func (uc *upstreamConn) updateChannelAutoDetach(name string) {
1856 uch := uc.channels.Value(name)
1857 if uch == nil {
1858 return
1859 }
1860 ch := uc.network.channels.Value(name)
1861 if ch == nil || ch.Detached {
1862 return
1863 }
1864 uch.updateAutoDetach(ch.DetachAfter)
1865}
Note: See TracBrowser for help on using the repository browser.