source: code/trunk/upstream.go@ 681

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

Remove support for mixed multi-upstream LIST

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

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