source: code/trunk/upstream.go@ 494

Last change on this file since 494 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
RevLine 
[98]1package soju
[13]2
3import (
[307]4 "crypto"
5 "crypto/sha256"
[13]6 "crypto/tls"
[307]7 "crypto/x509"
[95]8 "encoding/base64"
[155]9 "errors"
[13]10 "fmt"
11 "io"
12 "net"
[19]13 "strconv"
[17]14 "strings"
[19]15 "time"
[288]16 "unicode"
17 "unicode/utf8"
[13]18
[95]19 "github.com/emersion/go-sasl"
[13]20 "gopkg.in/irc.v3"
21)
22
[282]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,
[419]28 "extended-join": true,
[448]29 "invite-notify": true,
[282]30 "labeled-response": true,
31 "message-tags": true,
[292]32 "multi-prefix": true,
[282]33 "server-time": true,
34}
35
[399]36type registrationError string
37
38func (err registrationError) Error() string {
39 return fmt.Sprintf("registration error: %v", string(err))
40}
41
[19]42type upstreamChannel struct {
[162]43 Name string
44 conn *upstreamConn
45 Topic string
[405]46 TopicWho *irc.Prefix
[162]47 TopicTime time.Time
48 Status channelStatus
49 modes channelModes
50 creationTime string
[478]51 Members membershipsCasemapMap
[162]52 complete bool
[435]53 detachTimer *time.Timer
[19]54}
55
[435]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
[13]74type upstreamConn struct {
[210]75 conn
[16]76
[210]77 network *network
78 user *user
79
[16]80 serverName string
81 availableUserModes string
[139]82 availableChannelModes map[byte]channelModeType
83 availableChannelTypes string
84 availableMemberships []membership
[460]85 isupport map[string]*string
[19]86
[277]87 registered bool
88 nick string
[478]89 nickCM string
[277]90 username string
91 realname string
92 modes userModes
[478]93 channels upstreamChannelCasemapMap
[277]94 supportedCaps map[string]string
[278]95 caps map[string]bool
[277]96 batches map[string]batch
97 away bool
[278]98 nextLabelID uint64
[95]99
100 saslClient sasl.Client
101 saslStarted bool
[177]102
[478]103 casemapIsSet bool
104
[177]105 // set of LIST commands in progress, per downstream
106 pendingLISTDownstreamSet map[uint64]struct{}
[13]107}
108
[77]109func connectToUpstream(network *network) (*upstreamConn, error) {
110 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
[33]111
[352]112 dialer := net.Dialer{Timeout: connectTimeout}
[269]113
[457]114 u, err := network.URL()
[352]115 if err != nil {
[457]116 return nil, err
[352]117 }
[206]118
[269]119 var netConn net.Conn
[352]120 switch u.Scheme {
[269]121 case "ircs":
[352]122 addr := u.Host
[381]123 host, _, err := net.SplitHostPort(u.Host)
124 if err != nil {
125 host = u.Host
126 addr = u.Host + ":6697"
[269]127 }
128
129 logger.Printf("connecting to TLS server at address %q", addr)
[307]130
[455]131 tlsConfig := &tls.Config{ServerName: host, NextProtos: []string{"irc"}}
[307]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 }
[381]143 tlsConfig.Certificates = []tls.Certificate{
144 {
145 Certificate: [][]byte{network.SASL.External.CertBlob},
146 PrivateKey: key.(crypto.PrivateKey),
[307]147 },
148 }
149 logger.Printf("using TLS client certificate %x", sha256.Sum256(network.SASL.External.CertBlob))
150 }
151
[381]152 netConn, err = dialer.Dial("tcp", addr)
[352]153 if err != nil {
154 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
155 }
[381]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)
[270]161 case "irc+insecure":
[352]162 addr := u.Host
[351]163 if _, _, err := net.SplitHostPort(addr); err != nil {
[270]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)
[352]169 if err != nil {
170 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
171 }
[369]172 case "irc+unix", "unix":
[353]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 }
[269]178 default:
[352]179 return nil, fmt.Errorf("failed to dial %q: unknown scheme: %v", network.Addr, u.Scheme)
[269]180 }
[33]181
[398]182 options := connOptions{
[402]183 Logger: logger,
[398]184 RateLimitDelay: upstreamMessageDelay,
185 RateLimitBurst: upstreamMessageBurst,
186 }
187
[55]188 uc := &upstreamConn{
[398]189 conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options),
[177]190 network: network,
191 user: network.user,
[478]192 channels: upstreamChannelCasemapMap{newCasemapMap(0)},
[277]193 supportedCaps: make(map[string]string),
[278]194 caps: make(map[string]bool),
[177]195 batches: make(map[string]batch),
196 availableChannelTypes: stdChannelTypes,
197 availableChannelModes: stdChannelModes,
198 availableMemberships: stdMemberships,
[460]199 isupport: make(map[string]*string),
[177]200 pendingLISTDownstreamSet: make(map[uint64]struct{}),
[33]201 }
[55]202 return uc, nil
[33]203}
204
[73]205func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
[218]206 uc.network.forEachDownstream(f)
[73]207}
208
[161]209func (uc *upstreamConn) forEachDownstreamByID(id uint64, f func(*downstreamConn)) {
[155]210 uc.forEachDownstream(func(dc *downstreamConn) {
211 if id != 0 && id != dc.id {
212 return
213 }
214 f(dc)
215 })
216}
217
[55]218func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
[478]219 ch := uc.channels.Value(name)
220 if ch == nil {
[19]221 return nil, fmt.Errorf("unknown channel %q", name)
222 }
223 return ch, nil
224}
225
[129]226func (uc *upstreamConn) isChannel(entity string) bool {
[454]227 return strings.ContainsRune(uc.availableChannelTypes, rune(entity[0]))
[129]228}
229
[478]230func (uc *upstreamConn) isOurNick(nick string) bool {
231 return uc.nickCM == uc.network.casemap(nick)
232}
233
[181]234func (uc *upstreamConn) getPendingLIST() *pendingLIST {
[177]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
[181]244func (uc *upstreamConn) endPendingLISTs(all bool) (found bool) {
[177]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) {
[181]267 uc.trySendLIST(pl.downstreamID)
[177]268 })
269 return
270 }
271 }
272 return
273}
274
[181]275func (uc *upstreamConn) trySendLIST(downstreamID uint64) {
[177]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
[292]303func (uc *upstreamConn) parseMembershipPrefix(s string) (ms *memberships, nick string) {
304 memberships := make(memberships, 0, 4)
305 i := 0
[139]306 for _, m := range uc.availableMemberships {
[292]307 if i >= len(s) {
308 break
[139]309 }
[292]310 if s[i] == m.Prefix {
311 memberships = append(memberships, m)
312 i++
313 }
[139]314 }
[292]315 return &memberships, s[i:]
[139]316}
317
[288]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
[55]352func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
[155]353 var label string
354 if l, ok := msg.GetTag("label"); ok {
355 label = l
356 }
357
[153]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
[155]365 if label == "" {
366 label = msgBatch.Label
367 }
[443]368 delete(msg.Tags, "batch")
[153]369 }
370
[161]371 var downstreamID uint64 = 0
[155]372 if label != "" {
373 var labelOffset uint64
[161]374 n, err := fmt.Sscanf(label, "sd-%d-%d", &downstreamID, &labelOffset)
[155]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
[216]383 if _, ok := msg.Tags["time"]; !ok {
[240]384 msg.Tags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
[216]385 }
386
[13]387 switch msg.Command {
388 case "PING":
[60]389 uc.SendMessage(&irc.Message{
[13]390 Command: "PONG",
[68]391 Params: msg.Params,
[60]392 })
[33]393 return nil
[303]394 case "NOTICE", "PRIVMSG", "TAGMSG":
[273]395 if msg.Prefix == nil {
396 return fmt.Errorf("expected a prefix")
397 }
398
[286]399 var entity, text string
[303]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 }
[286]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
[171]419 if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message
[239]420 uc.produce("", msg, nil)
[303]421 } else { // regular user message
[217]422 target := entity
[478]423 if uc.isOurNick(target) {
[178]424 target = msg.Prefix.Name
425 }
[287]426
[478]427 ch := uc.network.channels.Value(target)
428 if ch != nil {
[435]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 }
[287]437 }
[435]438
439 uc.produce(target, msg, nil)
[171]440 }
[92]441 case "CAP":
[95]442 var subCmd string
443 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
444 return err
[92]445 }
[95]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 }
[281]453 caps := subParams[len(subParams)-1]
[95]454 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
[92]455
[281]456 uc.handleSupportedCaps(caps)
[92]457
[95]458 if more {
459 break // wait to receive all capabilities
460 }
461
[281]462 uc.requestCaps()
[152]463
[95]464 if uc.requestSASL() {
465 break // we'll send CAP END after authentication is completed
466 }
467
[92]468 uc.SendMessage(&irc.Message{
469 Command: "CAP",
470 Params: []string{"END"},
471 })
[95]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
[281]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 }
[95]511 default:
512 uc.logger.Printf("unhandled message: %v", msg)
[92]513 }
[95]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 = "+"
[318]560 if len(resp) != 0 {
[95]561 respStr = base64.StdEncoding.EncodeToString(resp)
562 }
563
564 uc.SendMessage(&irc.Message{
565 Command: "AUTHENTICATE",
566 Params: []string{respStr},
567 })
[125]568 case irc.RPL_LOGGEDIN:
[95]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)
[125]574 case irc.RPL_LOGGEDOUT:
[95]575 uc.logger.Printf("logged out")
[125]576 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
[95]577 var info string
578 if err := parseMessageParams(msg, nil, &info); err != nil {
579 return err
580 }
581 switch msg.Command {
[125]582 case irc.ERR_NICKLOCKED:
[95]583 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
[125]584 case irc.ERR_SASLFAIL:
[95]585 uc.logger.Printf("SASL authentication failed: %v", info)
[125]586 case irc.ERR_SASLTOOLONG:
[95]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 })
[14]597 case irc.RPL_WELCOME:
[55]598 uc.registered = true
599 uc.logger.Printf("connection registered")
[19]600
[478]601 if uc.network.channels.Len() > 0 {
[350]602 var channels, keys []string
[478]603 for _, entry := range uc.network.channels.innerMap {
604 ch := entry.value.(*Channel)
[350]605 channels = append(channels, ch.Name)
[310]606 keys = append(keys, ch.Key)
607 }
[350]608
609 for _, msg := range join(channels, keys) {
610 uc.SendMessage(msg)
611 }
[19]612 }
[16]613 case irc.RPL_MYINFO:
[139]614 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
[43]615 return err
[16]616 }
[139]617 case irc.RPL_ISUPPORT:
618 if err := parseMessageParams(msg, nil, nil); err != nil {
619 return err
[16]620 }
[463]621
622 var downstreamIsupport []string
[139]623 for _, token := range msg.Params[1 : len(msg.Params)-1] {
624 parameter := token
[460]625 var negate, hasValue bool
626 var value string
[139]627 if strings.HasPrefix(token, "-") {
628 negate = true
629 token = token[1:]
[459]630 } else if i := strings.IndexByte(token, '='); i >= 0 {
631 parameter = token[:i]
632 value = token[i+1:]
[460]633 hasValue = true
[139]634 }
[460]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
[462]644 var err error
645 switch parameter {
[478]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
[462]654 case "CHANMODES":
655 if !negate {
656 err = uc.handleChanModes(value)
657 } else {
658 uc.availableChannelModes = stdChannelModes
659 }
660 case "CHANTYPES":
661 if !negate {
[139]662 uc.availableChannelTypes = value
[462]663 } else {
664 uc.availableChannelTypes = stdChannelTypes
[139]665 }
[462]666 case "PREFIX":
667 if !negate {
668 err = uc.handleMemberships(value)
669 } else {
670 uc.availableMemberships = stdMemberships
671 }
[139]672 }
[462]673 if err != nil {
674 return err
675 }
[463]676
677 if passthroughIsupport[parameter] {
678 downstreamIsupport = append(downstreamIsupport, token)
679 }
[139]680 }
[463]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 })
[478]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 }
[153]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 }
[155]714 label := label
715 if label == "" && msgBatch != nil {
716 label = msgBatch.Label
717 }
[153]718 uc.batches[tag] = batch{
719 Type: batchType,
720 Params: msg.Params[2:],
721 Outer: msgBatch,
[155]722 Label: label,
[153]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 }
[42]733 case "NICK":
[83]734 if msg.Prefix == nil {
735 return fmt.Errorf("expected a prefix")
736 }
737
[43]738 var newNick string
739 if err := parseMessageParams(msg, &newNick); err != nil {
740 return err
[42]741 }
742
[244]743 me := false
[478]744 if uc.isOurNick(msg.Prefix.Name) {
[55]745 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
[244]746 me = true
[55]747 uc.nick = newNick
[478]748 uc.nickCM = uc.network.casemap(uc.nick)
[42]749 }
750
[478]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)
[215]757 uc.appendLog(ch.Name, msg)
[42]758 }
759 }
[82]760
[244]761 if !me {
[82]762 uc.forEachDownstream(func(dc *downstreamConn) {
[261]763 dc.SendMessage(dc.marshalMessage(msg, uc.network))
[82]764 })
[296]765 } else {
766 uc.forEachDownstream(func(dc *downstreamConn) {
767 dc.updateNick()
768 })
[82]769 }
[69]770 case "JOIN":
771 if msg.Prefix == nil {
772 return fmt.Errorf("expected a prefix")
773 }
[42]774
[43]775 var channels string
776 if err := parseMessageParams(msg, &channels); err != nil {
777 return err
[19]778 }
[34]779
[43]780 for _, ch := range strings.Split(channels, ",") {
[478]781 if uc.isOurNick(msg.Prefix.Name) {
[55]782 uc.logger.Printf("joined channel %q", ch)
[478]783 members := membershipsCasemapMap{newCasemapMap(0)}
784 members.casemap = uc.network.casemap
785 uc.channels.SetValue(ch, &upstreamChannel{
[34]786 Name: ch,
[55]787 conn: uc,
[478]788 Members: members,
789 })
[435]790 uc.updateChannelAutoDetach(ch)
[139]791
792 uc.SendMessage(&irc.Message{
793 Command: "MODE",
794 Params: []string{ch},
795 })
[34]796 } else {
[55]797 ch, err := uc.getChannel(ch)
[34]798 if err != nil {
799 return err
800 }
[478]801 ch.Members.SetValue(msg.Prefix.Name, &memberships{})
[19]802 }
[69]803
[245]804 chMsg := msg.Copy()
805 chMsg.Params[0] = ch
806 uc.produce(ch, chMsg, nil)
[19]807 }
[69]808 case "PART":
809 if msg.Prefix == nil {
810 return fmt.Errorf("expected a prefix")
811 }
[34]812
[43]813 var channels string
814 if err := parseMessageParams(msg, &channels); err != nil {
815 return err
[34]816 }
817
[43]818 for _, ch := range strings.Split(channels, ",") {
[478]819 if uc.isOurNick(msg.Prefix.Name) {
[55]820 uc.logger.Printf("parted channel %q", ch)
[478]821 uch := uc.channels.Value(ch)
822 if uch != nil {
823 uc.channels.Delete(ch)
[435]824 uch.updateAutoDetach(0)
825 }
[34]826 } else {
[55]827 ch, err := uc.getChannel(ch)
[34]828 if err != nil {
829 return err
830 }
[478]831 ch.Members.Delete(msg.Prefix.Name)
[34]832 }
[69]833
[245]834 chMsg := msg.Copy()
835 chMsg.Params[0] = ch
836 uc.produce(ch, chMsg, nil)
[34]837 }
[159]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
[478]848 if uc.isOurNick(user) {
[159]849 uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
[478]850 uc.channels.Delete(channel)
[159]851 } else {
852 ch, err := uc.getChannel(channel)
853 if err != nil {
854 return err
855 }
[478]856 ch.Members.Delete(user)
[159]857 }
858
[245]859 uc.produce(channel, msg, nil)
[83]860 case "QUIT":
861 if msg.Prefix == nil {
862 return fmt.Errorf("expected a prefix")
863 }
864
[478]865 if uc.isOurNick(msg.Prefix.Name) {
[83]866 uc.logger.Printf("quit")
867 }
868
[478]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)
[178]873
[215]874 uc.appendLog(ch.Name, msg)
[178]875 }
[83]876 }
877
878 if msg.Prefix.Name != uc.nick {
879 uc.forEachDownstream(func(dc *downstreamConn) {
[261]880 dc.SendMessage(dc.marshalMessage(msg, uc.network))
[83]881 })
882 }
[19]883 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
[43]884 var name, topic string
885 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
886 return err
[19]887 }
[55]888 ch, err := uc.getChannel(name)
[19]889 if err != nil {
890 return err
891 }
892 if msg.Command == irc.RPL_TOPIC {
[43]893 ch.Topic = topic
[19]894 } else {
895 ch.Topic = ""
896 }
897 case "TOPIC":
[405]898 if msg.Prefix == nil {
899 return fmt.Errorf("expected a prefix")
900 }
901
[43]902 var name string
[74]903 if err := parseMessageParams(msg, &name); err != nil {
[43]904 return err
[19]905 }
[55]906 ch, err := uc.getChannel(name)
[19]907 if err != nil {
908 return err
909 }
910 if len(msg.Params) > 1 {
911 ch.Topic = msg.Params[1]
[405]912 ch.TopicWho = msg.Prefix.Copy()
913 ch.TopicTime = time.Now() // TODO use msg.Tags["time"]
[19]914 } else {
915 ch.Topic = ""
916 }
[245]917 uc.produce(ch.Name, msg, nil)
[139]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
[293]936 needMarshaling, err := applyChannelModes(ch, modeStr, msg.Params[2:])
937 if err != nil {
938 return err
[139]939 }
940
[293]941 uc.appendLog(ch.Name, msg)
942
[478]943 c := uc.network.channels.Value(name)
944 if c == nil || !c.Detached {
[338]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 }
[293]955 }
956
[338]957 dc.SendMessage(&irc.Message{
958 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
959 Command: "MODE",
960 Params: params,
961 })
[293]962 })
[338]963 }
[139]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)
[293]996 if _, err := applyChannelModes(ch, modeStr, msg.Params[3:]); err != nil {
[139]997 return err
998 }
999 if firstMode {
[478]1000 c := uc.network.channels.Value(channel)
1001 if c == nil || !c.Detached {
[338]1002 modeStr, modeParams := ch.modes.Format()
[139]1003
[338]1004 uc.forEachDownstream(func(dc *downstreamConn) {
1005 params := []string{dc.nick, dc.marshalEntity(uc.network, channel), modeStr}
1006 params = append(params, modeParams...)
[139]1007
[338]1008 dc.SendMessage(&irc.Message{
1009 Prefix: dc.srv.prefix(),
1010 Command: irc.RPL_CHANNELMODEIS,
1011 Params: params,
1012 })
[139]1013 })
[338]1014 }
[139]1015 }
[162]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,
[403]1034 Params: []string{dc.nick, dc.marshalEntity(uc.network, ch.Name), creationTime},
[162]1035 })
1036 })
1037 }
[19]1038 case rpl_topicwhotime:
[43]1039 var name, who, timeStr string
1040 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
1041 return err
[19]1042 }
[55]1043 ch, err := uc.getChannel(name)
[19]1044 if err != nil {
1045 return err
1046 }
[405]1047 firstTopicWhoTime := ch.TopicWho == nil
1048 ch.TopicWho = irc.ParsePrefix(who)
[43]1049 sec, err := strconv.ParseInt(timeStr, 10, 64)
[19]1050 if err != nil {
1051 return fmt.Errorf("failed to parse topic time: %v", err)
1052 }
1053 ch.TopicTime = time.Unix(sec, 0)
[405]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 }
[177]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
[181]1075 pl := uc.getPendingLIST()
[177]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,
[260]1084 Params: []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
[177]1085 })
1086 })
1087 case irc.RPL_LISTEND:
[181]1088 ok := uc.endPendingLISTs(false)
[177]1089 if !ok {
1090 return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
1091 }
[19]1092 case irc.RPL_NAMREPLY:
[43]1093 var name, statusStr, members string
1094 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
1095 return err
[19]1096 }
[140]1097
[478]1098 ch := uc.channels.Value(name)
1099 if ch == nil {
[140]1100 // NAMES on a channel we have not joined, forward to downstream
[161]1101 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1102 channel := dc.marshalEntity(uc.network, name)
[174]1103 members := splitSpace(members)
[140]1104 for i, member := range members {
[292]1105 memberships, nick := uc.parseMembershipPrefix(member)
1106 members[i] = memberships.Format(dc) + dc.marshalEntity(uc.network, nick)
[140]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
[19]1117 }
1118
[43]1119 status, err := parseChannelStatus(statusStr)
[19]1120 if err != nil {
1121 return err
1122 }
1123 ch.Status = status
1124
[174]1125 for _, s := range splitSpace(members) {
[292]1126 memberships, nick := uc.parseMembershipPrefix(s)
[478]1127 ch.Members.SetValue(nick, memberships)
[19]1128 }
1129 case irc.RPL_ENDOFNAMES:
[43]1130 var name string
1131 if err := parseMessageParams(msg, nil, &name); err != nil {
1132 return err
[25]1133 }
[140]1134
[478]1135 ch := uc.channels.Value(name)
1136 if ch == nil {
[140]1137 // NAMES on a channel we have not joined, forward to downstream
[161]1138 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1139 channel := dc.marshalEntity(uc.network, name)
[140]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
[25]1148 }
1149
[34]1150 if ch.complete {
1151 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
1152 }
[25]1153 ch.complete = true
[27]1154
[478]1155 c := uc.network.channels.Value(name)
1156 if c == nil || !c.Detached {
[338]1157 uc.forEachDownstream(func(dc *downstreamConn) {
1158 forwardChannel(dc, ch)
1159 })
1160 }
[127]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
[161]1180 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[127]1181 channel := channel
1182 if channel != "*" {
[260]1183 channel = dc.marshalEntity(uc.network, channel)
[127]1184 }
[260]1185 nick := dc.marshalEntity(uc.network, nick)
[127]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
[161]1198 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[127]1199 name := name
1200 if name != "*" {
1201 // TODO: support WHO masks
[260]1202 name = dc.marshalEntity(uc.network, name)
[127]1203 }
1204 dc.SendMessage(&irc.Message{
1205 Prefix: dc.srv.prefix(),
1206 Command: irc.RPL_ENDOFWHO,
[142]1207 Params: []string{dc.nick, name, "End of /WHO list"},
[127]1208 })
1209 })
[128]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
[161]1216 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1217 nick := dc.marshalEntity(uc.network, nick)
[128]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
[161]1230 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1231 nick := dc.marshalEntity(uc.network, nick)
[128]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
[161]1244 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1245 nick := dc.marshalEntity(uc.network, nick)
[128]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
[161]1258 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1259 nick := dc.marshalEntity(uc.network, nick)
[128]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 }
[174]1273 channels := splitSpace(channelList)
[128]1274
[161]1275 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1276 nick := dc.marshalEntity(uc.network, nick)
[128]1277 channelList := make([]string, len(channels))
1278 for i, channel := range channels {
[139]1279 prefix, channel := uc.parseMembershipPrefix(channel)
[260]1280 channel = dc.marshalEntity(uc.network, channel)
[292]1281 channelList[i] = prefix.Format(dc) + channel
[128]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
[161]1296 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[260]1297 nick := dc.marshalEntity(uc.network, nick)
[128]1298 dc.SendMessage(&irc.Message{
1299 Prefix: dc.srv.prefix(),
1300 Command: irc.RPL_ENDOFWHOIS,
[142]1301 Params: []string{dc.nick, nick, "End of /WHOIS list"},
[128]1302 })
1303 })
[115]1304 case "INVITE":
[273]1305 var nick, channel string
[115]1306 if err := parseMessageParams(msg, &nick, &channel); err != nil {
1307 return err
1308 }
1309
[478]1310 weAreInvited := uc.isOurNick(nick)
[448]1311
[115]1312 uc.forEachDownstream(func(dc *downstreamConn) {
[448]1313 if !weAreInvited && !dc.caps["invite-notify"] {
1314 return
1315 }
[115]1316 dc.SendMessage(&irc.Message{
[260]1317 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
[115]1318 Command: "INVITE",
[260]1319 Params: []string{dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
[115]1320 })
1321 })
[163]1322 case irc.RPL_INVITING:
[273]1323 var nick, channel string
[304]1324 if err := parseMessageParams(msg, nil, &nick, &channel); err != nil {
[163]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,
[260]1332 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
[163]1333 })
1334 })
[272]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
[274]1341 uc.forEachDownstream(func(dc *downstreamConn) {
[272]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 })
[276]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 })
[300]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 })
[302]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
[355]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},
[302]1423 })
[355]1424 })
[155]1425 case "ACK":
1426 // Ignore
[198]1427 case irc.RPL_NOWAWAY, irc.RPL_UNAWAY:
1428 // Ignore
[16]1429 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]1430 // Ignore
1431 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
1432 // Ignore
[478]1433 case irc.RPL_MOTDSTART, irc.RPL_MOTD:
[14]1434 // Ignore
[177]1435 case irc.RPL_LISTSTART:
1436 // Ignore
[14]1437 case rpl_localusers, rpl_globalusers:
1438 // Ignore
[96]1439 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
[14]1440 // Ignore
[390]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)
[389]1447 case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKNAMEINUSE, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP:
[342]1448 if !uc.registered {
[399]1449 text := msg.Params[len(msg.Params)-1]
1450 return registrationError(text)
[342]1451 }
1452 fallthrough
[13]1453 default:
[95]1454 uc.logger.Printf("unhandled message: %v", msg)
[355]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
[302]1464 }
[355]1465 }
1466 dc.SendMessage(&irc.Message{
1467 Prefix: uc.srv.prefix(),
1468 Command: msg.Command,
1469 Params: params,
[302]1470 })
[355]1471 })
[13]1472 }
[14]1473 return nil
[13]1474}
1475
[435]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
[458]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
[281]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
[282]1550 for c := range permanentUpstreamCaps {
[281]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
[282]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)
[307]1611 case "EXTERNAL":
1612 uc.logger.Printf("starting SASL EXTERNAL authentication")
1613 uc.saslClient = sasl.NewExternalClient("")
[282]1614 default:
1615 return fmt.Errorf("unsupported SASL mechanism %q", name)
1616 }
1617
[281]1618 uc.SendMessage(&irc.Message{
[282]1619 Command: "AUTHENTICATE",
1620 Params: []string{auth.Mechanism},
[281]1621 })
[282]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)
[281]1627 }
[282]1628 return nil
[281]1629}
1630
[174]1631func splitSpace(s string) []string {
1632 return strings.FieldsFunc(s, func(r rune) bool {
1633 return r == ' '
1634 })
1635}
1636
[55]1637func (uc *upstreamConn) register() {
[77]1638 uc.nick = uc.network.Nick
[478]1639 uc.nickCM = uc.network.casemap(uc.nick)
[457]1640 uc.username = uc.network.GetUsername()
1641 uc.realname = uc.network.GetRealname()
[77]1642
[60]1643 uc.SendMessage(&irc.Message{
[92]1644 Command: "CAP",
1645 Params: []string{"LS", "302"},
1646 })
1647
[93]1648 if uc.network.Pass != "" {
1649 uc.SendMessage(&irc.Message{
1650 Command: "PASS",
1651 Params: []string{uc.network.Pass},
1652 })
1653 }
1654
[92]1655 uc.SendMessage(&irc.Message{
[13]1656 Command: "NICK",
[69]1657 Params: []string{uc.nick},
[60]1658 })
1659 uc.SendMessage(&irc.Message{
[13]1660 Command: "USER",
[77]1661 Params: []string{uc.username, "0", "*", uc.realname},
[60]1662 })
[44]1663}
[13]1664
[197]1665func (uc *upstreamConn) runUntilRegistered() error {
1666 for !uc.registered {
[212]1667 msg, err := uc.ReadMessage()
[197]1668 if err != nil {
1669 return fmt.Errorf("failed to read message: %v", err)
1670 }
1671
1672 if err := uc.handleMessage(msg); err != nil {
[399]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 }
[197]1679 }
1680 }
1681
[263]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
[197]1691 return nil
1692}
1693
[165]1694func (uc *upstreamConn) readMessages(ch chan<- event) error {
[13]1695 for {
[210]1696 msg, err := uc.ReadMessage()
[13]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
[165]1703 ch <- eventUpstreamMessage{msg, uc}
[13]1704 }
1705
[45]1706 return nil
[13]1707}
[60]1708
[303]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
[176]1718func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
[278]1719 if uc.caps["labeled-response"] {
[155]1720 if msg.Tags == nil {
1721 msg.Tags = make(map[string]irc.TagValue)
1722 }
[176]1723 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", downstreamID, uc.nextLabelID))
[161]1724 uc.nextLabelID++
[155]1725 }
1726 uc.SendMessage(msg)
1727}
[178]1728
[428]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) {
[423]1734 if uc.user.msgStore == nil {
[428]1735 return ""
[178]1736 }
[486]1737
1738 entityCM := uc.network.casemap(entity)
1739 if entityCM == "nickserv" {
[468]1740 // The messages sent/received from NickServ may contain
1741 // security-related information (like passwords). Don't store these.
1742 return ""
1743 }
[215]1744
[485]1745 if !uc.network.delivered.HasTarget(entity) {
[482]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.
[478]1749 lastID, err := uc.user.msgStore.LastMsgID(uc.network, entityCM, time.Now())
[409]1750 if err != nil {
1751 uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
[428]1752 return ""
[409]1753 }
1754
[489]1755 uc.network.delivered.ForEachClient(func(clientName string) {
[485]1756 uc.network.delivered.StoreID(entity, clientName, lastID)
[489]1757 })
[253]1758 }
1759
[478]1760 msgID, err := uc.user.msgStore.Append(uc.network, entityCM, msg)
[409]1761 if err != nil {
1762 uc.logger.Printf("failed to log message: %v", err)
[428]1763 return ""
[409]1764 }
[406]1765
[428]1766 return msgID
[253]1767}
1768
[409]1769// produce appends a message to the logs and forwards it to connected downstream
1770// connections.
[245]1771//
1772// If origin is not nil and origin doesn't support echo-message, the message is
1773// forwarded to all connections except origin.
[239]1774func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstreamConn) {
[428]1775 var msgID string
[239]1776 if target != "" {
[428]1777 msgID = uc.appendLog(target, msg)
[239]1778 }
1779
[284]1780 // Don't forward messages if it's a detached channel
[478]1781 ch := uc.network.channels.Value(target)
1782 if ch != nil && ch.Detached {
[284]1783 return
1784 }
1785
[227]1786 uc.forEachDownstream(func(dc *downstreamConn) {
[238]1787 if dc != origin || dc.caps["echo-message"] {
[428]1788 dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID)
1789 } else {
1790 dc.advanceMessageWithID(msg, msgID)
[238]1791 }
[227]1792 })
[226]1793}
1794
[198]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}
[435]1815
1816func (uc *upstreamConn) updateChannelAutoDetach(name string) {
[478]1817 uch := uc.channels.Value(name)
1818 if uch == nil {
1819 return
[435]1820 }
[478]1821 ch := uc.network.channels.Value(name)
1822 if ch == nil || ch.Detached {
1823 return
1824 }
1825 uch.updateAutoDetach(ch.DetachAfter)
[435]1826}
Note: See TracBrowser for help on using the repository browser.