source: code/trunk/upstream.go@ 675

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

Fix upstream USER command when both username and nick are empty

File size: 47.0 KB
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 = GetUsername(&uc.user.User, &uc.network.Network)
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.