source: code/trunk/upstream.go@ 731

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

Add support for draft/account-registration proxying

This adds support for the draft/account-registration extension [1].
This allows downstreams to register on upstream networks.

[1]: https://ircv3.net/specs/extensions/account-registration

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