source: code/trunk/upstream.go@ 472

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

Don't update downstream caps in upstream RPL_WELCOME handler

Prior to being registered, upstreamConn.handleMessage doesn't run
in the user goroutine, it runs in a goroutine specific to the
network. Thus we shouldn't access any user data structure from
there.

downstreamConn.updateSupportedCaps is already called from the
eventUpstreamConnected handler in user.run, the call being removed
was unnecessary.

Closes: https://todo.sr.ht/~emersion/soju/108

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