source: code/trunk/upstream.go@ 443

Last change on this file since 443 was 443, checked in by hubert, 4 years ago

Don't forward batch tags

We don't want to have the batch tag when calling uc.produce, otherwise
downstream will end up with junk batch ids.

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