source: code/trunk/upstream.go@ 501

Last change on this file since 501 was 499, checked in by contact, 4 years ago

Relay detached channel backlog as BouncerServ NOTICE if necessary

Instead of ignoring detached channels wehn replaying backlog,
process them as usual and relay messages as BouncerServ NOTICEs
if necessary. Advance the delivery receipts as if the channel was
attached.

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

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