source: code/trunk/upstream.go@ 381

Last change on this file since 381 was 381, checked in by contact, 5 years ago

Don't perform TLS handshake in connectToUpstream

This defers TLS handshake until the first read or write operation. This
allows the upcoming identd server to register the connection before the
TLS handshake is complete, and is necessary because some IRC servers
send an ident request before that.

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