source: code/trunk/upstream.go@ 407

Last change on this file since 407 was 407, checked in by contact, 5 years ago

Introduce internal message IDs

For now, these can be used as cursors in the logs. Future patches will
introduce functions that perform log queries with message IDs.

The IDs are state-less tokens containing all the required information to
refer to an on-disk log line: network name, entity name, date and byte
offset. The byte offset doesn't need to point to the first byte of the
line, any byte will do (note, this makes it so message IDs aren't
necessarily unique, we may want to change that in the future).

These internal message IDs are not exposed to clients because we don't
support upstream message IDs yet.

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