source: code/trunk/upstream.go@ 162

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

Add upstream RPL_CREATIONTIME support

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