source: code/trunk/upstream.go@ 567

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

Allow admins to broadcast message to all bouncer users

Typically done via:

/notice $<bouncer> <message>

Or, for a connection not bound to a specific network:

/notice $* <message>

The message is broadcast as BouncerServ, because that's the only
user that can be trusted to belong to the bouncer by users. Any
other prefix would conflict with the upstream network.

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