source: code/trunk/upstream.go@ 454

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

if true return true else return false

Trivial improvement

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