source: code/trunk/upstream.go@ 176

Last change on this file since 176 was 176, checked in by delthas, 5 years ago

Make upstream.SendMessageLabeled use an uint64 id

This commit is preparatory work for code that will call
SendMessageLabeled with a direct downstream id rather than a
downstreamConnection pointer.

File size: 29.9 KB
RevLine 
[98]1package soju
[13]2
3import (
4 "crypto/tls"
[95]5 "encoding/base64"
[155]6 "errors"
[13]7 "fmt"
8 "io"
9 "net"
[19]10 "strconv"
[17]11 "strings"
[19]12 "time"
[13]13
[95]14 "github.com/emersion/go-sasl"
[13]15 "gopkg.in/irc.v3"
16)
17
[19]18type upstreamChannel struct {
[162]19 Name string
20 conn *upstreamConn
21 Topic string
22 TopicWho string
23 TopicTime time.Time
24 Status channelStatus
25 modes channelModes
26 creationTime string
27 Members map[string]*membership
28 complete bool
[19]29}
30
[13]31type upstreamConn struct {
[77]32 network *network
[21]33 logger Logger
[19]34 net net.Conn
35 irc *irc.Conn
36 srv *Server
[37]37 user *user
[102]38 outgoing chan<- *irc.Message
[175]39 closed chan struct{}
[16]40
41 serverName string
42 availableUserModes string
[139]43 availableChannelModes map[byte]channelModeType
44 availableChannelTypes string
45 availableMemberships []membership
[19]46
47 registered bool
[42]48 nick string
[77]49 username string
50 realname string
[139]51 modes userModes
[19]52 channels map[string]*upstreamChannel
[92]53 caps map[string]string
[153]54 batches map[string]batch
[95]55
[155]56 tagsSupported bool
57 labelsSupported bool
[161]58 nextLabelID uint64
[152]59
[95]60 saslClient sasl.Client
61 saslStarted bool
[13]62}
63
[77]64func connectToUpstream(network *network) (*upstreamConn, error) {
65 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
[33]66
[77]67 addr := network.Addr
68 if !strings.ContainsRune(addr, ':') {
69 addr = addr + ":6697"
70 }
71
72 logger.Printf("connecting to TLS server at address %q", addr)
73 netConn, err := tls.Dial("tcp", addr, nil)
[33]74 if err != nil {
[77]75 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
[33]76 }
77
[67]78 setKeepAlive(netConn)
79
[102]80 outgoing := make(chan *irc.Message, 64)
[55]81 uc := &upstreamConn{
[139]82 network: network,
83 logger: logger,
84 net: netConn,
85 irc: irc.NewConn(netConn),
86 srv: network.user.srv,
87 user: network.user,
88 outgoing: outgoing,
89 channels: make(map[string]*upstreamChannel),
90 caps: make(map[string]string),
[153]91 batches: make(map[string]batch),
[139]92 availableChannelTypes: stdChannelTypes,
93 availableChannelModes: stdChannelModes,
94 availableMemberships: stdMemberships,
[33]95 }
96
97 go func() {
[175]98 for {
99 var closed bool
100 select {
101 case msg := <-outgoing:
102 if uc.srv.Debug {
103 uc.logger.Printf("sent: %v", msg)
104 }
105 if err := uc.irc.WriteMessage(msg); err != nil {
106 uc.logger.Printf("failed to write message: %v", err)
107 }
108 case <-uc.closed:
109 closed = true
[64]110 }
[175]111 if closed {
112 break
[33]113 }
114 }
[55]115 if err := uc.net.Close(); err != nil {
116 uc.logger.Printf("failed to close connection: %v", err)
[45]117 } else {
[55]118 uc.logger.Printf("connection closed")
[45]119 }
[33]120 }()
121
[55]122 return uc, nil
[33]123}
124
[175]125func (uc *upstreamConn) isClosed() bool {
126 select {
127 case <-uc.closed:
128 return true
129 default:
130 return false
131 }
132}
133
[55]134func (uc *upstreamConn) Close() error {
[175]135 if uc.isClosed() {
[33]136 return fmt.Errorf("upstream connection already closed")
137 }
[175]138 close(uc.closed)
[33]139 return nil
140}
141
[73]142func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
143 uc.user.forEachDownstream(func(dc *downstreamConn) {
[77]144 if dc.network != nil && dc.network != uc.network {
[73]145 return
146 }
147 f(dc)
148 })
149}
150
[161]151func (uc *upstreamConn) forEachDownstreamByID(id uint64, f func(*downstreamConn)) {
[155]152 uc.forEachDownstream(func(dc *downstreamConn) {
153 if id != 0 && id != dc.id {
154 return
155 }
156 f(dc)
157 })
158}
159
[55]160func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
161 ch, ok := uc.channels[name]
[19]162 if !ok {
163 return nil, fmt.Errorf("unknown channel %q", name)
164 }
165 return ch, nil
166}
167
[129]168func (uc *upstreamConn) isChannel(entity string) bool {
[139]169 if i := strings.IndexByte(uc.availableChannelTypes, entity[0]); i >= 0 {
170 return true
[129]171 }
172 return false
173}
174
[139]175func (uc *upstreamConn) parseMembershipPrefix(s string) (membership *membership, nick string) {
176 for _, m := range uc.availableMemberships {
177 if m.Prefix == s[0] {
178 return &m, s[1:]
179 }
180 }
181 return nil, s
182}
183
[55]184func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
[155]185 var label string
186 if l, ok := msg.GetTag("label"); ok {
187 label = l
188 }
189
[153]190 var msgBatch *batch
191 if batchName, ok := msg.GetTag("batch"); ok {
192 b, ok := uc.batches[batchName]
193 if !ok {
194 return fmt.Errorf("unexpected batch reference: batch was not defined: %q", batchName)
195 }
196 msgBatch = &b
[155]197 if label == "" {
198 label = msgBatch.Label
199 }
[153]200 }
201
[161]202 var downstreamID uint64 = 0
[155]203 if label != "" {
204 var labelOffset uint64
[161]205 n, err := fmt.Sscanf(label, "sd-%d-%d", &downstreamID, &labelOffset)
[155]206 if err == nil && n < 2 {
207 err = errors.New("not enough arguments")
208 }
209 if err != nil {
210 return fmt.Errorf("unexpected message label: invalid downstream reference for label %q: %v", label, err)
211 }
212 }
213
[13]214 switch msg.Command {
215 case "PING":
[60]216 uc.SendMessage(&irc.Message{
[13]217 Command: "PONG",
[68]218 Params: msg.Params,
[60]219 })
[33]220 return nil
[18]221 case "NOTICE":
[55]222 uc.logger.Print(msg)
[97]223
[171]224 if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message
225 uc.forEachDownstream(func(dc *downstreamConn) {
226 dc.SendMessage(msg)
227 })
228 } else { // regular user NOTICE
229 var nick, text string
230 if err := parseMessageParams(msg, &nick, &text); err != nil {
231 return err
232 }
233
234 uc.forEachDownstream(func(dc *downstreamConn) {
235 dc.SendMessage(&irc.Message{
236 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
237 Command: "NOTICE",
238 Params: []string{dc.marshalEntity(uc, nick), text},
239 })
240 })
241 }
[92]242 case "CAP":
[95]243 var subCmd string
244 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
245 return err
[92]246 }
[95]247 subCmd = strings.ToUpper(subCmd)
248 subParams := msg.Params[2:]
249 switch subCmd {
250 case "LS":
251 if len(subParams) < 1 {
252 return newNeedMoreParamsError(msg.Command)
253 }
254 caps := strings.Fields(subParams[len(subParams)-1])
255 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
[92]256
[95]257 for _, s := range caps {
258 kv := strings.SplitN(s, "=", 2)
259 k := strings.ToLower(kv[0])
260 var v string
261 if len(kv) == 2 {
262 v = kv[1]
263 }
264 uc.caps[k] = v
[92]265 }
266
[95]267 if more {
268 break // wait to receive all capabilities
269 }
270
[152]271 requestCaps := make([]string, 0, 16)
[155]272 for _, c := range []string{"message-tags", "batch", "labeled-response"} {
[152]273 if _, ok := uc.caps[c]; ok {
274 requestCaps = append(requestCaps, c)
275 }
276 }
277
[95]278 if uc.requestSASL() {
[152]279 requestCaps = append(requestCaps, "sasl")
280 }
281
282 if len(requestCaps) > 0 {
[95]283 uc.SendMessage(&irc.Message{
284 Command: "CAP",
[152]285 Params: []string{"REQ", strings.Join(requestCaps, " ")},
[95]286 })
[152]287 }
288
289 if uc.requestSASL() {
[95]290 break // we'll send CAP END after authentication is completed
291 }
292
[92]293 uc.SendMessage(&irc.Message{
294 Command: "CAP",
295 Params: []string{"END"},
296 })
[95]297 case "ACK", "NAK":
298 if len(subParams) < 1 {
299 return newNeedMoreParamsError(msg.Command)
300 }
301 caps := strings.Fields(subParams[0])
302
303 for _, name := range caps {
304 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
305 return err
306 }
307 }
308
309 if uc.saslClient == nil {
310 uc.SendMessage(&irc.Message{
311 Command: "CAP",
312 Params: []string{"END"},
313 })
314 }
315 default:
316 uc.logger.Printf("unhandled message: %v", msg)
[92]317 }
[95]318 case "AUTHENTICATE":
319 if uc.saslClient == nil {
320 return fmt.Errorf("received unexpected AUTHENTICATE message")
321 }
322
323 // TODO: if a challenge is 400 bytes long, buffer it
324 var challengeStr string
325 if err := parseMessageParams(msg, &challengeStr); err != nil {
326 uc.SendMessage(&irc.Message{
327 Command: "AUTHENTICATE",
328 Params: []string{"*"},
329 })
330 return err
331 }
332
333 var challenge []byte
334 if challengeStr != "+" {
335 var err error
336 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
337 if err != nil {
338 uc.SendMessage(&irc.Message{
339 Command: "AUTHENTICATE",
340 Params: []string{"*"},
341 })
342 return err
343 }
344 }
345
346 var resp []byte
347 var err error
348 if !uc.saslStarted {
349 _, resp, err = uc.saslClient.Start()
350 uc.saslStarted = true
351 } else {
352 resp, err = uc.saslClient.Next(challenge)
353 }
354 if err != nil {
355 uc.SendMessage(&irc.Message{
356 Command: "AUTHENTICATE",
357 Params: []string{"*"},
358 })
359 return err
360 }
361
362 // TODO: send response in multiple chunks if >= 400 bytes
363 var respStr = "+"
364 if resp != nil {
365 respStr = base64.StdEncoding.EncodeToString(resp)
366 }
367
368 uc.SendMessage(&irc.Message{
369 Command: "AUTHENTICATE",
370 Params: []string{respStr},
371 })
[125]372 case irc.RPL_LOGGEDIN:
[95]373 var account string
374 if err := parseMessageParams(msg, nil, nil, &account); err != nil {
375 return err
376 }
377 uc.logger.Printf("logged in with account %q", account)
[125]378 case irc.RPL_LOGGEDOUT:
[95]379 uc.logger.Printf("logged out")
[125]380 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
[95]381 var info string
382 if err := parseMessageParams(msg, nil, &info); err != nil {
383 return err
384 }
385 switch msg.Command {
[125]386 case irc.ERR_NICKLOCKED:
[95]387 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
[125]388 case irc.ERR_SASLFAIL:
[95]389 uc.logger.Printf("SASL authentication failed: %v", info)
[125]390 case irc.ERR_SASLTOOLONG:
[95]391 uc.logger.Printf("SASL message too long: %v", info)
392 }
393
394 uc.saslClient = nil
395 uc.saslStarted = false
396
397 uc.SendMessage(&irc.Message{
398 Command: "CAP",
399 Params: []string{"END"},
400 })
[14]401 case irc.RPL_WELCOME:
[55]402 uc.registered = true
403 uc.logger.Printf("connection registered")
[19]404
[77]405 channels, err := uc.srv.db.ListChannels(uc.network.ID)
406 if err != nil {
407 uc.logger.Printf("failed to list channels from database: %v", err)
408 break
409 }
410
411 for _, ch := range channels {
[146]412 params := []string{ch.Name}
413 if ch.Key != "" {
414 params = append(params, ch.Key)
415 }
[60]416 uc.SendMessage(&irc.Message{
[19]417 Command: "JOIN",
[146]418 Params: params,
[60]419 })
[19]420 }
[16]421 case irc.RPL_MYINFO:
[139]422 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
[43]423 return err
[16]424 }
[139]425 case irc.RPL_ISUPPORT:
426 if err := parseMessageParams(msg, nil, nil); err != nil {
427 return err
[16]428 }
[139]429 for _, token := range msg.Params[1 : len(msg.Params)-1] {
430 negate := false
431 parameter := token
432 value := ""
433 if strings.HasPrefix(token, "-") {
434 negate = true
435 token = token[1:]
436 } else {
437 if i := strings.IndexByte(token, '='); i >= 0 {
438 parameter = token[:i]
439 value = token[i+1:]
440 }
441 }
442 if !negate {
443 switch parameter {
444 case "CHANMODES":
445 parts := strings.SplitN(value, ",", 5)
446 if len(parts) < 4 {
447 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", value)
448 }
449 modes := make(map[byte]channelModeType)
450 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
451 for j := 0; j < len(parts[i]); j++ {
452 mode := parts[i][j]
453 modes[mode] = mt
454 }
455 }
456 uc.availableChannelModes = modes
457 case "CHANTYPES":
458 uc.availableChannelTypes = value
459 case "PREFIX":
460 if value == "" {
461 uc.availableMemberships = nil
462 } else {
463 if value[0] != '(' {
464 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
465 }
466 sep := strings.IndexByte(value, ')')
467 if sep < 0 || len(value) != sep*2 {
468 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
469 }
470 memberships := make([]membership, len(value)/2-1)
471 for i := range memberships {
472 memberships[i] = membership{
473 Mode: value[i+1],
474 Prefix: value[sep+i+1],
475 }
476 }
477 uc.availableMemberships = memberships
478 }
479 }
480 } else {
481 // TODO: handle ISUPPORT negations
482 }
483 }
[153]484 case "BATCH":
485 var tag string
486 if err := parseMessageParams(msg, &tag); err != nil {
487 return err
488 }
489
490 if strings.HasPrefix(tag, "+") {
491 tag = tag[1:]
492 if _, ok := uc.batches[tag]; ok {
493 return fmt.Errorf("unexpected BATCH reference tag: batch was already defined: %q", tag)
494 }
495 var batchType string
496 if err := parseMessageParams(msg, nil, &batchType); err != nil {
497 return err
498 }
[155]499 label := label
500 if label == "" && msgBatch != nil {
501 label = msgBatch.Label
502 }
[153]503 uc.batches[tag] = batch{
504 Type: batchType,
505 Params: msg.Params[2:],
506 Outer: msgBatch,
[155]507 Label: label,
[153]508 }
509 } else if strings.HasPrefix(tag, "-") {
510 tag = tag[1:]
511 if _, ok := uc.batches[tag]; !ok {
512 return fmt.Errorf("unknown BATCH reference tag: %q", tag)
513 }
514 delete(uc.batches, tag)
515 } else {
516 return fmt.Errorf("unexpected BATCH reference tag: missing +/- prefix: %q", tag)
517 }
[42]518 case "NICK":
[83]519 if msg.Prefix == nil {
520 return fmt.Errorf("expected a prefix")
521 }
522
[43]523 var newNick string
524 if err := parseMessageParams(msg, &newNick); err != nil {
525 return err
[42]526 }
527
[55]528 if msg.Prefix.Name == uc.nick {
529 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
530 uc.nick = newNick
[42]531 }
532
[55]533 for _, ch := range uc.channels {
[42]534 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
535 delete(ch.Members, msg.Prefix.Name)
536 ch.Members[newNick] = membership
537 }
538 }
[82]539
540 if msg.Prefix.Name != uc.nick {
541 uc.forEachDownstream(func(dc *downstreamConn) {
542 dc.SendMessage(&irc.Message{
543 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
544 Command: "NICK",
545 Params: []string{newNick},
546 })
547 })
548 }
[69]549 case "JOIN":
550 if msg.Prefix == nil {
551 return fmt.Errorf("expected a prefix")
552 }
[42]553
[43]554 var channels string
555 if err := parseMessageParams(msg, &channels); err != nil {
556 return err
[19]557 }
[34]558
[43]559 for _, ch := range strings.Split(channels, ",") {
[55]560 if msg.Prefix.Name == uc.nick {
561 uc.logger.Printf("joined channel %q", ch)
562 uc.channels[ch] = &upstreamChannel{
[34]563 Name: ch,
[55]564 conn: uc,
[139]565 Members: make(map[string]*membership),
[34]566 }
[139]567
568 uc.SendMessage(&irc.Message{
569 Command: "MODE",
570 Params: []string{ch},
571 })
[34]572 } else {
[55]573 ch, err := uc.getChannel(ch)
[34]574 if err != nil {
575 return err
576 }
[139]577 ch.Members[msg.Prefix.Name] = nil
[19]578 }
[69]579
[73]580 uc.forEachDownstream(func(dc *downstreamConn) {
[69]581 dc.SendMessage(&irc.Message{
582 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
583 Command: "JOIN",
584 Params: []string{dc.marshalChannel(uc, ch)},
585 })
586 })
[19]587 }
[69]588 case "PART":
589 if msg.Prefix == nil {
590 return fmt.Errorf("expected a prefix")
591 }
[34]592
[43]593 var channels string
594 if err := parseMessageParams(msg, &channels); err != nil {
595 return err
[34]596 }
597
[43]598 for _, ch := range strings.Split(channels, ",") {
[55]599 if msg.Prefix.Name == uc.nick {
600 uc.logger.Printf("parted channel %q", ch)
601 delete(uc.channels, ch)
[34]602 } else {
[55]603 ch, err := uc.getChannel(ch)
[34]604 if err != nil {
605 return err
606 }
607 delete(ch.Members, msg.Prefix.Name)
608 }
[69]609
[73]610 uc.forEachDownstream(func(dc *downstreamConn) {
[69]611 dc.SendMessage(&irc.Message{
612 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
613 Command: "PART",
614 Params: []string{dc.marshalChannel(uc, ch)},
615 })
616 })
[34]617 }
[159]618 case "KICK":
619 if msg.Prefix == nil {
620 return fmt.Errorf("expected a prefix")
621 }
622
623 var channel, user string
624 if err := parseMessageParams(msg, &channel, &user); err != nil {
625 return err
626 }
627
628 var reason string
629 if len(msg.Params) > 2 {
630 reason = msg.Params[1]
631 }
632
633 if user == uc.nick {
634 uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
635 delete(uc.channels, channel)
636 } else {
637 ch, err := uc.getChannel(channel)
638 if err != nil {
639 return err
640 }
641 delete(ch.Members, user)
642 }
643
644 uc.forEachDownstream(func(dc *downstreamConn) {
645 params := []string{dc.marshalChannel(uc, channel), dc.marshalNick(uc, user)}
646 if reason != "" {
647 params = append(params, reason)
648 }
649 dc.SendMessage(&irc.Message{
650 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
651 Command: "KICK",
652 Params: params,
653 })
654 })
[83]655 case "QUIT":
656 if msg.Prefix == nil {
657 return fmt.Errorf("expected a prefix")
658 }
659
660 if msg.Prefix.Name == uc.nick {
661 uc.logger.Printf("quit")
662 }
663
664 for _, ch := range uc.channels {
665 delete(ch.Members, msg.Prefix.Name)
666 }
667
668 if msg.Prefix.Name != uc.nick {
669 uc.forEachDownstream(func(dc *downstreamConn) {
670 dc.SendMessage(&irc.Message{
671 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
672 Command: "QUIT",
673 Params: msg.Params,
674 })
675 })
676 }
[19]677 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
[43]678 var name, topic string
679 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
680 return err
[19]681 }
[55]682 ch, err := uc.getChannel(name)
[19]683 if err != nil {
684 return err
685 }
686 if msg.Command == irc.RPL_TOPIC {
[43]687 ch.Topic = topic
[19]688 } else {
689 ch.Topic = ""
690 }
691 case "TOPIC":
[43]692 var name string
[74]693 if err := parseMessageParams(msg, &name); err != nil {
[43]694 return err
[19]695 }
[55]696 ch, err := uc.getChannel(name)
[19]697 if err != nil {
698 return err
699 }
700 if len(msg.Params) > 1 {
701 ch.Topic = msg.Params[1]
702 } else {
703 ch.Topic = ""
704 }
[74]705 uc.forEachDownstream(func(dc *downstreamConn) {
706 params := []string{dc.marshalChannel(uc, name)}
707 if ch.Topic != "" {
708 params = append(params, ch.Topic)
709 }
710 dc.SendMessage(&irc.Message{
711 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
712 Command: "TOPIC",
713 Params: params,
714 })
715 })
[139]716 case "MODE":
717 var name, modeStr string
718 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
719 return err
720 }
721
722 if !uc.isChannel(name) { // user mode change
723 if name != uc.nick {
724 return fmt.Errorf("received MODE message for unknown nick %q", name)
725 }
726 return uc.modes.Apply(modeStr)
727 // TODO: notify downstreams about user mode change?
728 } else { // channel mode change
729 ch, err := uc.getChannel(name)
730 if err != nil {
731 return err
732 }
733
734 if ch.modes != nil {
735 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[2:]...); err != nil {
736 return err
737 }
738 }
739
740 uc.forEachDownstream(func(dc *downstreamConn) {
741 params := []string{dc.marshalChannel(uc, name), modeStr}
742 params = append(params, msg.Params[2:]...)
743
744 dc.SendMessage(&irc.Message{
745 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
746 Command: "MODE",
747 Params: params,
748 })
749 })
750 }
751 case irc.RPL_UMODEIS:
752 if err := parseMessageParams(msg, nil); err != nil {
753 return err
754 }
755 modeStr := ""
756 if len(msg.Params) > 1 {
757 modeStr = msg.Params[1]
758 }
759
760 uc.modes = ""
761 if err := uc.modes.Apply(modeStr); err != nil {
762 return err
763 }
764 // TODO: send RPL_UMODEIS to downstream connections when applicable
765 case irc.RPL_CHANNELMODEIS:
766 var channel string
767 if err := parseMessageParams(msg, nil, &channel); err != nil {
768 return err
769 }
770 modeStr := ""
771 if len(msg.Params) > 2 {
772 modeStr = msg.Params[2]
773 }
774
775 ch, err := uc.getChannel(channel)
776 if err != nil {
777 return err
778 }
779
780 firstMode := ch.modes == nil
781 ch.modes = make(map[byte]string)
782 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[3:]...); err != nil {
783 return err
784 }
785 if firstMode {
786 modeStr, modeParams := ch.modes.Format()
787
788 uc.forEachDownstream(func(dc *downstreamConn) {
789 params := []string{dc.nick, dc.marshalChannel(uc, channel), modeStr}
790 params = append(params, modeParams...)
791
792 dc.SendMessage(&irc.Message{
793 Prefix: dc.srv.prefix(),
794 Command: irc.RPL_CHANNELMODEIS,
795 Params: params,
796 })
797 })
798 }
[162]799 case rpl_creationtime:
800 var channel, creationTime string
801 if err := parseMessageParams(msg, nil, &channel, &creationTime); err != nil {
802 return err
803 }
804
805 ch, err := uc.getChannel(channel)
806 if err != nil {
807 return err
808 }
809
810 firstCreationTime := ch.creationTime == ""
811 ch.creationTime = creationTime
812 if firstCreationTime {
813 uc.forEachDownstream(func(dc *downstreamConn) {
814 dc.SendMessage(&irc.Message{
815 Prefix: dc.srv.prefix(),
816 Command: rpl_creationtime,
817 Params: []string{dc.nick, channel, creationTime},
818 })
819 })
820 }
[19]821 case rpl_topicwhotime:
[43]822 var name, who, timeStr string
823 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
824 return err
[19]825 }
[55]826 ch, err := uc.getChannel(name)
[19]827 if err != nil {
828 return err
829 }
[43]830 ch.TopicWho = who
831 sec, err := strconv.ParseInt(timeStr, 10, 64)
[19]832 if err != nil {
833 return fmt.Errorf("failed to parse topic time: %v", err)
834 }
835 ch.TopicTime = time.Unix(sec, 0)
836 case irc.RPL_NAMREPLY:
[43]837 var name, statusStr, members string
838 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
839 return err
[19]840 }
[140]841
842 ch, ok := uc.channels[name]
843 if !ok {
844 // NAMES on a channel we have not joined, forward to downstream
[161]845 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[140]846 channel := dc.marshalChannel(uc, name)
[174]847 members := splitSpace(members)
[140]848 for i, member := range members {
849 membership, nick := uc.parseMembershipPrefix(member)
850 members[i] = membership.String() + dc.marshalNick(uc, nick)
851 }
852 memberStr := strings.Join(members, " ")
853
854 dc.SendMessage(&irc.Message{
855 Prefix: dc.srv.prefix(),
856 Command: irc.RPL_NAMREPLY,
857 Params: []string{dc.nick, statusStr, channel, memberStr},
858 })
859 })
860 return nil
[19]861 }
862
[43]863 status, err := parseChannelStatus(statusStr)
[19]864 if err != nil {
865 return err
866 }
867 ch.Status = status
868
[174]869 for _, s := range splitSpace(members) {
[139]870 membership, nick := uc.parseMembershipPrefix(s)
[19]871 ch.Members[nick] = membership
872 }
873 case irc.RPL_ENDOFNAMES:
[43]874 var name string
875 if err := parseMessageParams(msg, nil, &name); err != nil {
876 return err
[25]877 }
[140]878
879 ch, ok := uc.channels[name]
880 if !ok {
881 // NAMES on a channel we have not joined, forward to downstream
[161]882 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[140]883 channel := dc.marshalChannel(uc, name)
884
885 dc.SendMessage(&irc.Message{
886 Prefix: dc.srv.prefix(),
887 Command: irc.RPL_ENDOFNAMES,
888 Params: []string{dc.nick, channel, "End of /NAMES list"},
889 })
890 })
891 return nil
[25]892 }
893
[34]894 if ch.complete {
895 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
896 }
[25]897 ch.complete = true
[27]898
[73]899 uc.forEachDownstream(func(dc *downstreamConn) {
[27]900 forwardChannel(dc, ch)
[40]901 })
[127]902 case irc.RPL_WHOREPLY:
903 var channel, username, host, server, nick, mode, trailing string
904 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
905 return err
906 }
907
908 parts := strings.SplitN(trailing, " ", 2)
909 if len(parts) != 2 {
910 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
911 }
912 realname := parts[1]
913 hops, err := strconv.Atoi(parts[0])
914 if err != nil {
915 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
916 }
917 hops++
918
919 trailing = strconv.Itoa(hops) + " " + realname
920
[161]921 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[127]922 channel := channel
923 if channel != "*" {
924 channel = dc.marshalChannel(uc, channel)
925 }
926 nick := dc.marshalNick(uc, nick)
927 dc.SendMessage(&irc.Message{
928 Prefix: dc.srv.prefix(),
929 Command: irc.RPL_WHOREPLY,
930 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
931 })
932 })
933 case irc.RPL_ENDOFWHO:
934 var name string
935 if err := parseMessageParams(msg, nil, &name); err != nil {
936 return err
937 }
938
[161]939 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[127]940 name := name
941 if name != "*" {
942 // TODO: support WHO masks
943 name = dc.marshalEntity(uc, name)
944 }
945 dc.SendMessage(&irc.Message{
946 Prefix: dc.srv.prefix(),
947 Command: irc.RPL_ENDOFWHO,
[142]948 Params: []string{dc.nick, name, "End of /WHO list"},
[127]949 })
950 })
[128]951 case irc.RPL_WHOISUSER:
952 var nick, username, host, realname string
953 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
954 return err
955 }
956
[161]957 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[128]958 nick := dc.marshalNick(uc, nick)
959 dc.SendMessage(&irc.Message{
960 Prefix: dc.srv.prefix(),
961 Command: irc.RPL_WHOISUSER,
962 Params: []string{dc.nick, nick, username, host, "*", realname},
963 })
964 })
965 case irc.RPL_WHOISSERVER:
966 var nick, server, serverInfo string
967 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
968 return err
969 }
970
[161]971 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[128]972 nick := dc.marshalNick(uc, nick)
973 dc.SendMessage(&irc.Message{
974 Prefix: dc.srv.prefix(),
975 Command: irc.RPL_WHOISSERVER,
976 Params: []string{dc.nick, nick, server, serverInfo},
977 })
978 })
979 case irc.RPL_WHOISOPERATOR:
980 var nick string
981 if err := parseMessageParams(msg, nil, &nick); err != nil {
982 return err
983 }
984
[161]985 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[128]986 nick := dc.marshalNick(uc, nick)
987 dc.SendMessage(&irc.Message{
988 Prefix: dc.srv.prefix(),
989 Command: irc.RPL_WHOISOPERATOR,
990 Params: []string{dc.nick, nick, "is an IRC operator"},
991 })
992 })
993 case irc.RPL_WHOISIDLE:
994 var nick string
995 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
996 return err
997 }
998
[161]999 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[128]1000 nick := dc.marshalNick(uc, nick)
1001 params := []string{dc.nick, nick}
1002 params = append(params, msg.Params[2:]...)
1003 dc.SendMessage(&irc.Message{
1004 Prefix: dc.srv.prefix(),
1005 Command: irc.RPL_WHOISIDLE,
1006 Params: params,
1007 })
1008 })
1009 case irc.RPL_WHOISCHANNELS:
1010 var nick, channelList string
1011 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
1012 return err
1013 }
[174]1014 channels := splitSpace(channelList)
[128]1015
[161]1016 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[128]1017 nick := dc.marshalNick(uc, nick)
1018 channelList := make([]string, len(channels))
1019 for i, channel := range channels {
[139]1020 prefix, channel := uc.parseMembershipPrefix(channel)
[128]1021 channel = dc.marshalChannel(uc, channel)
1022 channelList[i] = prefix.String() + channel
1023 }
1024 channels := strings.Join(channelList, " ")
1025 dc.SendMessage(&irc.Message{
1026 Prefix: dc.srv.prefix(),
1027 Command: irc.RPL_WHOISCHANNELS,
1028 Params: []string{dc.nick, nick, channels},
1029 })
1030 })
1031 case irc.RPL_ENDOFWHOIS:
1032 var nick string
1033 if err := parseMessageParams(msg, nil, &nick); err != nil {
1034 return err
1035 }
1036
[161]1037 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
[128]1038 nick := dc.marshalNick(uc, nick)
1039 dc.SendMessage(&irc.Message{
1040 Prefix: dc.srv.prefix(),
1041 Command: irc.RPL_ENDOFWHOIS,
[142]1042 Params: []string{dc.nick, nick, "End of /WHOIS list"},
[128]1043 })
1044 })
[36]1045 case "PRIVMSG":
[117]1046 if msg.Prefix == nil {
1047 return fmt.Errorf("expected a prefix")
1048 }
1049
1050 var nick string
1051 if err := parseMessageParams(msg, &nick, nil); err != nil {
[69]1052 return err
1053 }
[117]1054
1055 if msg.Prefix.Name == serviceNick {
1056 uc.logger.Printf("skipping PRIVMSG from soju's service: %v", msg)
1057 break
1058 }
1059 if nick == serviceNick {
1060 uc.logger.Printf("skipping PRIVMSG to soju's service: %v", msg)
1061 break
1062 }
1063
[143]1064 uc.network.ring.Produce(msg)
[115]1065 case "INVITE":
1066 var nick string
1067 var channel string
1068 if err := parseMessageParams(msg, &nick, &channel); err != nil {
1069 return err
1070 }
1071
1072 uc.forEachDownstream(func(dc *downstreamConn) {
1073 dc.SendMessage(&irc.Message{
1074 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
1075 Command: "INVITE",
1076 Params: []string{dc.marshalNick(uc, nick), dc.marshalChannel(uc, channel)},
1077 })
1078 })
[163]1079 case irc.RPL_INVITING:
1080 var nick string
1081 var channel string
1082 if err := parseMessageParams(msg, &nick, &channel); err != nil {
1083 return err
1084 }
1085
1086 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1087 dc.SendMessage(&irc.Message{
1088 Prefix: dc.srv.prefix(),
1089 Command: irc.RPL_INVITING,
1090 Params: []string{dc.nick, dc.marshalNick(uc, nick), dc.marshalChannel(uc, channel)},
1091 })
1092 })
[152]1093 case "TAGMSG":
1094 // TODO: relay to downstream connections that accept message-tags
[155]1095 case "ACK":
1096 // Ignore
[16]1097 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]1098 // Ignore
1099 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
1100 // Ignore
1101 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
1102 // Ignore
1103 case rpl_localusers, rpl_globalusers:
1104 // Ignore
[96]1105 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
[14]1106 // Ignore
[13]1107 default:
[95]1108 uc.logger.Printf("unhandled message: %v", msg)
[13]1109 }
[14]1110 return nil
[13]1111}
1112
[174]1113func splitSpace(s string) []string {
1114 return strings.FieldsFunc(s, func(r rune) bool {
1115 return r == ' '
1116 })
1117}
1118
[55]1119func (uc *upstreamConn) register() {
[77]1120 uc.nick = uc.network.Nick
1121 uc.username = uc.network.Username
1122 if uc.username == "" {
1123 uc.username = uc.nick
1124 }
1125 uc.realname = uc.network.Realname
1126 if uc.realname == "" {
1127 uc.realname = uc.nick
1128 }
1129
[60]1130 uc.SendMessage(&irc.Message{
[92]1131 Command: "CAP",
1132 Params: []string{"LS", "302"},
1133 })
1134
[93]1135 if uc.network.Pass != "" {
1136 uc.SendMessage(&irc.Message{
1137 Command: "PASS",
1138 Params: []string{uc.network.Pass},
1139 })
1140 }
1141
[92]1142 uc.SendMessage(&irc.Message{
[13]1143 Command: "NICK",
[69]1144 Params: []string{uc.nick},
[60]1145 })
1146 uc.SendMessage(&irc.Message{
[13]1147 Command: "USER",
[77]1148 Params: []string{uc.username, "0", "*", uc.realname},
[60]1149 })
[44]1150}
[13]1151
[95]1152func (uc *upstreamConn) requestSASL() bool {
1153 if uc.network.SASL.Mechanism == "" {
1154 return false
1155 }
1156
1157 v, ok := uc.caps["sasl"]
1158 if !ok {
1159 return false
1160 }
1161 if v != "" {
1162 mechanisms := strings.Split(v, ",")
1163 found := false
1164 for _, mech := range mechanisms {
1165 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
1166 found = true
1167 break
1168 }
1169 }
1170 if !found {
1171 return false
1172 }
1173 }
1174
1175 return true
1176}
1177
1178func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
1179 auth := &uc.network.SASL
1180 switch name {
1181 case "sasl":
1182 if !ok {
1183 uc.logger.Printf("server refused to acknowledge the SASL capability")
1184 return nil
1185 }
1186
1187 switch auth.Mechanism {
1188 case "PLAIN":
1189 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
1190 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
1191 default:
1192 return fmt.Errorf("unsupported SASL mechanism %q", name)
1193 }
1194
1195 uc.SendMessage(&irc.Message{
1196 Command: "AUTHENTICATE",
1197 Params: []string{auth.Mechanism},
1198 })
[152]1199 case "message-tags":
1200 uc.tagsSupported = ok
[155]1201 case "labeled-response":
1202 uc.labelsSupported = ok
[95]1203 }
1204 return nil
1205}
1206
[165]1207func (uc *upstreamConn) readMessages(ch chan<- event) error {
[13]1208 for {
[55]1209 msg, err := uc.irc.ReadMessage()
[13]1210 if err == io.EOF {
1211 break
1212 } else if err != nil {
1213 return fmt.Errorf("failed to read IRC command: %v", err)
1214 }
1215
[64]1216 if uc.srv.Debug {
1217 uc.logger.Printf("received: %v", msg)
1218 }
1219
[165]1220 ch <- eventUpstreamMessage{msg, uc}
[13]1221 }
1222
[45]1223 return nil
[13]1224}
[60]1225
1226func (uc *upstreamConn) SendMessage(msg *irc.Message) {
[102]1227 uc.outgoing <- msg
[60]1228}
[155]1229
[176]1230func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
[155]1231 if uc.labelsSupported {
1232 if msg.Tags == nil {
1233 msg.Tags = make(map[string]irc.TagValue)
1234 }
[176]1235 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", downstreamID, uc.nextLabelID))
[161]1236 uc.nextLabelID++
[155]1237 }
1238 uc.SendMessage(msg)
1239}
Note: See TracBrowser for help on using the repository browser.