source: code/trunk/upstream.go@ 602

Last change on this file since 602 was 568, checked in by contact, 4 years ago

Add per-user realname setting

This allows users to set a default realname used if the per-network
realname isn't set.

A new "user update" command is introduced and can be extended to edit
other user properties and other users in the future.

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