source: code/trunk/upstream.go@ 496

Last change on this file since 496 was 489, checked in by contact, 4 years ago

Save delivery receipts in DB

This avoids loosing history on restart for clients that don't
support chathistory.

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

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