source: code/trunk/upstream.go@ 560

Last change on this file since 560 was 559, checked in by contact, 4 years ago

Add support for account-tag

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