source: code/trunk/upstream.go@ 650

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

Unify away-notify and account-notify handling

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", "ACCOUNT":
1375 if msg.Prefix == nil {
1376 return fmt.Errorf("expected a prefix")
1377 }
1378
1379 uc.forEachDownstream(func(dc *downstreamConn) {
1380 dc.SendMessage(&irc.Message{
1381 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1382 Command: msg.Command,
1383 Params: msg.Params,
1384 })
1385 })
1386 case irc.RPL_BANLIST, irc.RPL_INVITELIST, irc.RPL_EXCEPTLIST:
1387 var channel, mask string
1388 if err := parseMessageParams(msg, nil, &channel, &mask); err != nil {
1389 return err
1390 }
1391 var addNick, addTime string
1392 if len(msg.Params) >= 5 {
1393 addNick = msg.Params[3]
1394 addTime = msg.Params[4]
1395 }
1396
1397 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1398 channel := dc.marshalEntity(uc.network, channel)
1399
1400 var params []string
1401 if addNick != "" && addTime != "" {
1402 addNick := dc.marshalEntity(uc.network, addNick)
1403 params = []string{dc.nick, channel, mask, addNick, addTime}
1404 } else {
1405 params = []string{dc.nick, channel, mask}
1406 }
1407
1408 dc.SendMessage(&irc.Message{
1409 Prefix: dc.srv.prefix(),
1410 Command: msg.Command,
1411 Params: params,
1412 })
1413 })
1414 case irc.RPL_ENDOFBANLIST, irc.RPL_ENDOFINVITELIST, irc.RPL_ENDOFEXCEPTLIST:
1415 var channel, trailing string
1416 if err := parseMessageParams(msg, nil, &channel, &trailing); err != nil {
1417 return err
1418 }
1419
1420 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1421 upstreamChannel := dc.marshalEntity(uc.network, channel)
1422 dc.SendMessage(&irc.Message{
1423 Prefix: dc.srv.prefix(),
1424 Command: msg.Command,
1425 Params: []string{dc.nick, upstreamChannel, trailing},
1426 })
1427 })
1428 case irc.ERR_UNKNOWNCOMMAND, irc.RPL_TRYAGAIN:
1429 var command, reason string
1430 if err := parseMessageParams(msg, nil, &command, &reason); err != nil {
1431 return err
1432 }
1433
1434 if command == "LIST" {
1435 ok := uc.endPendingLISTs(false)
1436 if !ok {
1437 return fmt.Errorf("unexpected response for LIST: %q: no matching pending LIST", msg.Command)
1438 }
1439 }
1440
1441 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1442 dc.SendMessage(&irc.Message{
1443 Prefix: uc.srv.prefix(),
1444 Command: msg.Command,
1445 Params: []string{dc.nick, command, reason},
1446 })
1447 })
1448 case "ACK":
1449 // Ignore
1450 case irc.RPL_NOWAWAY, irc.RPL_UNAWAY:
1451 // Ignore
1452 case irc.RPL_YOURHOST, irc.RPL_CREATED:
1453 // Ignore
1454 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
1455 fallthrough
1456 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
1457 fallthrough
1458 case rpl_localusers, rpl_globalusers:
1459 fallthrough
1460 case irc.RPL_MOTDSTART, irc.RPL_MOTD:
1461 // Ignore these messages if they're part of the initial registration
1462 // message burst. Forward them if the user explicitly asked for them.
1463 if !uc.gotMotd {
1464 return nil
1465 }
1466
1467 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1468 dc.SendMessage(&irc.Message{
1469 Prefix: uc.srv.prefix(),
1470 Command: msg.Command,
1471 Params: msg.Params,
1472 })
1473 })
1474 case irc.RPL_LISTSTART:
1475 // Ignore
1476 case "ERROR":
1477 var text string
1478 if err := parseMessageParams(msg, &text); err != nil {
1479 return err
1480 }
1481 return fmt.Errorf("fatal server error: %v", text)
1482 case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKNAMEINUSE, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP:
1483 if !uc.registered {
1484 text := msg.Params[len(msg.Params)-1]
1485 return registrationError(text)
1486 }
1487 fallthrough
1488 default:
1489 uc.logger.Printf("unhandled message: %v", msg)
1490
1491 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1492 // best effort marshaling for unknown messages, replies and errors:
1493 // most numerics start with the user nick, marshal it if that's the case
1494 // otherwise, conservately keep the params without marshaling
1495 params := msg.Params
1496 if _, err := strconv.Atoi(msg.Command); err == nil { // numeric
1497 if len(msg.Params) > 0 && isOurNick(uc.network, msg.Params[0]) {
1498 params[0] = dc.nick
1499 }
1500 }
1501 dc.SendMessage(&irc.Message{
1502 Prefix: uc.srv.prefix(),
1503 Command: msg.Command,
1504 Params: params,
1505 })
1506 })
1507 }
1508 return nil
1509}
1510
1511func (uc *upstreamConn) handleDetachedMessage(ch *Channel, msg *irc.Message) {
1512 if uc.network.detachedMessageNeedsRelay(ch, msg) {
1513 uc.forEachDownstream(func(dc *downstreamConn) {
1514 dc.relayDetachedMessage(uc.network, msg)
1515 })
1516 }
1517 if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && uc.network.isHighlight(msg)) {
1518 uc.network.attach(ch)
1519 if err := uc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
1520 uc.logger.Printf("failed to update channel %q: %v", ch.Name, err)
1521 }
1522 }
1523}
1524
1525func (uc *upstreamConn) handleChanModes(s string) error {
1526 parts := strings.SplitN(s, ",", 5)
1527 if len(parts) < 4 {
1528 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", s)
1529 }
1530 modes := make(map[byte]channelModeType)
1531 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
1532 for j := 0; j < len(parts[i]); j++ {
1533 mode := parts[i][j]
1534 modes[mode] = mt
1535 }
1536 }
1537 uc.availableChannelModes = modes
1538 return nil
1539}
1540
1541func (uc *upstreamConn) handleMemberships(s string) error {
1542 if s == "" {
1543 uc.availableMemberships = nil
1544 return nil
1545 }
1546
1547 if s[0] != '(' {
1548 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1549 }
1550 sep := strings.IndexByte(s, ')')
1551 if sep < 0 || len(s) != sep*2 {
1552 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1553 }
1554 memberships := make([]membership, len(s)/2-1)
1555 for i := range memberships {
1556 memberships[i] = membership{
1557 Mode: s[i+1],
1558 Prefix: s[sep+i+1],
1559 }
1560 }
1561 uc.availableMemberships = memberships
1562 return nil
1563}
1564
1565func (uc *upstreamConn) handleSupportedCaps(capsStr string) {
1566 caps := strings.Fields(capsStr)
1567 for _, s := range caps {
1568 kv := strings.SplitN(s, "=", 2)
1569 k := strings.ToLower(kv[0])
1570 var v string
1571 if len(kv) == 2 {
1572 v = kv[1]
1573 }
1574 uc.supportedCaps[k] = v
1575 }
1576}
1577
1578func (uc *upstreamConn) requestCaps() {
1579 var requestCaps []string
1580 for c := range permanentUpstreamCaps {
1581 if _, ok := uc.supportedCaps[c]; ok && !uc.caps[c] {
1582 requestCaps = append(requestCaps, c)
1583 }
1584 }
1585
1586 if uc.requestSASL() && !uc.caps["sasl"] {
1587 requestCaps = append(requestCaps, "sasl")
1588 }
1589
1590 if len(requestCaps) == 0 {
1591 return
1592 }
1593
1594 uc.SendMessage(&irc.Message{
1595 Command: "CAP",
1596 Params: []string{"REQ", strings.Join(requestCaps, " ")},
1597 })
1598}
1599
1600func (uc *upstreamConn) requestSASL() bool {
1601 if uc.network.SASL.Mechanism == "" {
1602 return false
1603 }
1604
1605 v, ok := uc.supportedCaps["sasl"]
1606 if !ok {
1607 return false
1608 }
1609 if v != "" {
1610 mechanisms := strings.Split(v, ",")
1611 found := false
1612 for _, mech := range mechanisms {
1613 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
1614 found = true
1615 break
1616 }
1617 }
1618 if !found {
1619 return false
1620 }
1621 }
1622
1623 return true
1624}
1625
1626func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
1627 uc.caps[name] = ok
1628
1629 switch name {
1630 case "sasl":
1631 if !ok {
1632 uc.logger.Printf("server refused to acknowledge the SASL capability")
1633 return nil
1634 }
1635
1636 auth := &uc.network.SASL
1637 switch auth.Mechanism {
1638 case "PLAIN":
1639 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
1640 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
1641 case "EXTERNAL":
1642 uc.logger.Printf("starting SASL EXTERNAL authentication")
1643 uc.saslClient = sasl.NewExternalClient("")
1644 default:
1645 return fmt.Errorf("unsupported SASL mechanism %q", name)
1646 }
1647
1648 uc.SendMessage(&irc.Message{
1649 Command: "AUTHENTICATE",
1650 Params: []string{auth.Mechanism},
1651 })
1652 default:
1653 if permanentUpstreamCaps[name] {
1654 break
1655 }
1656 uc.logger.Printf("received CAP ACK/NAK for a cap we don't support: %v", name)
1657 }
1658 return nil
1659}
1660
1661func splitSpace(s string) []string {
1662 return strings.FieldsFunc(s, func(r rune) bool {
1663 return r == ' '
1664 })
1665}
1666
1667func (uc *upstreamConn) register() {
1668 uc.nick = uc.network.Nick
1669 uc.nickCM = uc.network.casemap(uc.nick)
1670 uc.username = uc.network.GetUsername()
1671 uc.realname = GetRealname(&uc.user.User, &uc.network.Network)
1672
1673 uc.SendMessage(&irc.Message{
1674 Command: "CAP",
1675 Params: []string{"LS", "302"},
1676 })
1677
1678 if uc.network.Pass != "" {
1679 uc.SendMessage(&irc.Message{
1680 Command: "PASS",
1681 Params: []string{uc.network.Pass},
1682 })
1683 }
1684
1685 uc.SendMessage(&irc.Message{
1686 Command: "NICK",
1687 Params: []string{uc.nick},
1688 })
1689 uc.SendMessage(&irc.Message{
1690 Command: "USER",
1691 Params: []string{uc.username, "0", "*", uc.realname},
1692 })
1693}
1694
1695func (uc *upstreamConn) runUntilRegistered() error {
1696 for !uc.registered {
1697 msg, err := uc.ReadMessage()
1698 if err != nil {
1699 return fmt.Errorf("failed to read message: %v", err)
1700 }
1701
1702 if err := uc.handleMessage(msg); err != nil {
1703 if _, ok := err.(registrationError); ok {
1704 return err
1705 } else {
1706 msg.Tags = nil // prevent message tags from cluttering logs
1707 return fmt.Errorf("failed to handle message %q: %v", msg, err)
1708 }
1709 }
1710 }
1711
1712 for _, command := range uc.network.ConnectCommands {
1713 m, err := irc.ParseMessage(command)
1714 if err != nil {
1715 uc.logger.Printf("failed to parse connect command %q: %v", command, err)
1716 } else {
1717 uc.SendMessage(m)
1718 }
1719 }
1720
1721 return nil
1722}
1723
1724func (uc *upstreamConn) readMessages(ch chan<- event) error {
1725 for {
1726 msg, err := uc.ReadMessage()
1727 if err == io.EOF {
1728 break
1729 } else if err != nil {
1730 return fmt.Errorf("failed to read IRC command: %v", err)
1731 }
1732
1733 ch <- eventUpstreamMessage{msg, uc}
1734 }
1735
1736 return nil
1737}
1738
1739func (uc *upstreamConn) SendMessage(msg *irc.Message) {
1740 if !uc.caps["message-tags"] {
1741 msg = msg.Copy()
1742 msg.Tags = nil
1743 }
1744
1745 uc.conn.SendMessage(msg)
1746}
1747
1748func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
1749 if uc.caps["labeled-response"] {
1750 if msg.Tags == nil {
1751 msg.Tags = make(map[string]irc.TagValue)
1752 }
1753 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", downstreamID, uc.nextLabelID))
1754 uc.nextLabelID++
1755 }
1756 uc.SendMessage(msg)
1757}
1758
1759// appendLog appends a message to the log file.
1760//
1761// The internal message ID is returned. If the message isn't recorded in the
1762// log file, an empty string is returned.
1763func (uc *upstreamConn) appendLog(entity string, msg *irc.Message) (msgID string) {
1764 if uc.user.msgStore == nil {
1765 return ""
1766 }
1767
1768 // Don't store messages with a server mask target
1769 if strings.HasPrefix(entity, "$") {
1770 return ""
1771 }
1772
1773 entityCM := uc.network.casemap(entity)
1774 if entityCM == "nickserv" {
1775 // The messages sent/received from NickServ may contain
1776 // security-related information (like passwords). Don't store these.
1777 return ""
1778 }
1779
1780 if !uc.network.delivered.HasTarget(entity) {
1781 // This is the first message we receive from this target. Save the last
1782 // message ID in delivery receipts, so that we can send the new message
1783 // in the backlog if an offline client reconnects.
1784 lastID, err := uc.user.msgStore.LastMsgID(uc.network, entityCM, time.Now())
1785 if err != nil {
1786 uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
1787 return ""
1788 }
1789
1790 uc.network.delivered.ForEachClient(func(clientName string) {
1791 uc.network.delivered.StoreID(entity, clientName, lastID)
1792 })
1793 }
1794
1795 msgID, err := uc.user.msgStore.Append(uc.network, entityCM, msg)
1796 if err != nil {
1797 uc.logger.Printf("failed to log message: %v", err)
1798 return ""
1799 }
1800
1801 return msgID
1802}
1803
1804// produce appends a message to the logs and forwards it to connected downstream
1805// connections.
1806//
1807// If origin is not nil and origin doesn't support echo-message, the message is
1808// forwarded to all connections except origin.
1809func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstreamConn) {
1810 var msgID string
1811 if target != "" {
1812 msgID = uc.appendLog(target, msg)
1813 }
1814
1815 // Don't forward messages if it's a detached channel
1816 ch := uc.network.channels.Value(target)
1817 detached := ch != nil && ch.Detached
1818
1819 uc.forEachDownstream(func(dc *downstreamConn) {
1820 if !detached && (dc != origin || dc.caps["echo-message"]) {
1821 dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID)
1822 } else {
1823 dc.advanceMessageWithID(msg, msgID)
1824 }
1825 })
1826}
1827
1828func (uc *upstreamConn) updateAway() {
1829 away := true
1830 uc.forEachDownstream(func(*downstreamConn) {
1831 away = false
1832 })
1833 if away == uc.away {
1834 return
1835 }
1836 if away {
1837 uc.SendMessage(&irc.Message{
1838 Command: "AWAY",
1839 Params: []string{"Auto away"},
1840 })
1841 } else {
1842 uc.SendMessage(&irc.Message{
1843 Command: "AWAY",
1844 })
1845 }
1846 uc.away = away
1847}
1848
1849func (uc *upstreamConn) updateChannelAutoDetach(name string) {
1850 uch := uc.channels.Value(name)
1851 if uch == nil {
1852 return
1853 }
1854 ch := uc.network.channels.Value(name)
1855 if ch == nil || ch.Detached {
1856 return
1857 }
1858 uch.updateAutoDetach(ch.DetachAfter)
1859}
Note: See TracBrowser for help on using the repository browser.