source: code/trunk/downstream.go@ 159

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

Add KICK support

Downstream and upstream message handling are slightly different because
downstreams can send KICK messages with multiple channels or users,
while upstreams can only send KICK messages with one channel and one
user (according to the RFC).

File size: 28.0 KB
RevLine 
[98]1package soju
[13]2
3import (
[91]4 "crypto/tls"
[112]5 "encoding/base64"
[13]6 "fmt"
7 "io"
8 "net"
[108]9 "strconv"
[39]10 "strings"
[105]11 "sync"
[91]12 "time"
[13]13
[112]14 "github.com/emersion/go-sasl"
[85]15 "golang.org/x/crypto/bcrypt"
[13]16 "gopkg.in/irc.v3"
17)
18
19type ircError struct {
20 Message *irc.Message
21}
22
[85]23func (err ircError) Error() string {
24 return err.Message.String()
25}
26
[13]27func newUnknownCommandError(cmd string) ircError {
28 return ircError{&irc.Message{
29 Command: irc.ERR_UNKNOWNCOMMAND,
30 Params: []string{
31 "*",
32 cmd,
33 "Unknown command",
34 },
35 }}
36}
37
38func newNeedMoreParamsError(cmd string) ircError {
39 return ircError{&irc.Message{
40 Command: irc.ERR_NEEDMOREPARAMS,
41 Params: []string{
42 "*",
43 cmd,
44 "Not enough parameters",
45 },
46 }}
47}
48
[85]49var errAuthFailed = ircError{&irc.Message{
50 Command: irc.ERR_PASSWDMISMATCH,
51 Params: []string{"*", "Invalid username or password"},
52}}
[13]53
[104]54type ringMessage struct {
[69]55 consumer *RingConsumer
56 upstreamConn *upstreamConn
57}
58
[13]59type downstreamConn struct {
[154]60 id uint64
[69]61 net net.Conn
62 irc *irc.Conn
63 srv *Server
64 logger Logger
[102]65 outgoing chan *irc.Message
[104]66 ringMessages chan ringMessage
[69]67 closed chan struct{}
[22]68
[100]69 registered bool
70 user *user
71 nick string
72 username string
73 rawUsername string
74 realname string
[141]75 hostname string
[100]76 password string // empty after authentication
77 network *network // can be nil
[105]78
[108]79 negociatingCaps bool
80 capVersion int
81 caps map[string]bool
82
[112]83 saslServer sasl.Server
84
[105]85 lock sync.Mutex
86 ourMessages map[*irc.Message]struct{}
[13]87}
88
[154]89func newDownstreamConn(srv *Server, netConn net.Conn, id uint64) *downstreamConn {
[55]90 dc := &downstreamConn{
[154]91 id: id,
[69]92 net: netConn,
93 irc: irc.NewConn(netConn),
94 srv: srv,
95 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
[102]96 outgoing: make(chan *irc.Message, 64),
[104]97 ringMessages: make(chan ringMessage),
[69]98 closed: make(chan struct{}),
[108]99 caps: make(map[string]bool),
[105]100 ourMessages: make(map[*irc.Message]struct{}),
[22]101 }
[141]102 dc.hostname = netConn.RemoteAddr().String()
103 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
104 dc.hostname = host
105 }
[26]106
107 go func() {
[56]108 if err := dc.writeMessages(); err != nil {
109 dc.logger.Printf("failed to write message: %v", err)
[26]110 }
[55]111 if err := dc.net.Close(); err != nil {
112 dc.logger.Printf("failed to close connection: %v", err)
[45]113 } else {
[55]114 dc.logger.Printf("connection closed")
[45]115 }
[26]116 }()
117
[130]118 dc.logger.Printf("new connection")
[55]119 return dc
[22]120}
121
[55]122func (dc *downstreamConn) prefix() *irc.Prefix {
[27]123 return &irc.Prefix{
[55]124 Name: dc.nick,
125 User: dc.username,
[141]126 Host: dc.hostname,
[27]127 }
128}
129
[90]130func (dc *downstreamConn) forEachNetwork(f func(*network)) {
131 if dc.network != nil {
132 f(dc.network)
133 } else {
134 dc.user.forEachNetwork(f)
135 }
136}
137
[73]138func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
139 dc.user.forEachUpstream(func(uc *upstreamConn) {
[77]140 if dc.network != nil && uc.network != dc.network {
[73]141 return
142 }
143 f(uc)
144 })
145}
146
[89]147// upstream returns the upstream connection, if any. If there are zero or if
148// there are multiple upstream connections, it returns nil.
149func (dc *downstreamConn) upstream() *upstreamConn {
150 if dc.network == nil {
151 return nil
152 }
[136]153 return dc.network.upstream()
[89]154}
155
[129]156func (dc *downstreamConn) marshalEntity(uc *upstreamConn, entity string) string {
157 if uc.isChannel(entity) {
158 return dc.marshalChannel(uc, entity)
[119]159 }
[129]160 return dc.marshalNick(uc, entity)
[119]161}
162
163func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
[130]164 if dc.network != nil {
[119]165 return name
166 }
167 return name + "/" + uc.network.GetName()
168}
169
[127]170func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
[89]171 if uc := dc.upstream(); uc != nil {
172 return uc, name, nil
173 }
174
[127]175 var conn *upstreamConn
[119]176 if i := strings.LastIndexByte(name, '/'); i >= 0 {
[127]177 network := name[i+1:]
[119]178 name = name[:i]
179
180 dc.forEachUpstream(func(uc *upstreamConn) {
181 if network != uc.network.GetName() {
182 return
183 }
184 conn = uc
185 })
186 }
187
[127]188 if conn == nil {
[73]189 return nil, "", ircError{&irc.Message{
190 Command: irc.ERR_NOSUCHCHANNEL,
191 Params: []string{name, "No such channel"},
192 }}
[69]193 }
[127]194 return conn, name, nil
[69]195}
196
197func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
198 if nick == uc.nick {
199 return dc.nick
200 }
[130]201 if dc.network != nil {
[119]202 return nick
203 }
204 return nick + "/" + uc.network.GetName()
[69]205}
206
207func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
208 if prefix.Name == uc.nick {
209 return dc.prefix()
210 }
[130]211 if dc.network != nil {
[119]212 return prefix
213 }
214 return &irc.Prefix{
215 Name: prefix.Name + "/" + uc.network.GetName(),
216 User: prefix.User,
217 Host: prefix.Host,
218 }
[69]219}
220
[57]221func (dc *downstreamConn) isClosed() bool {
222 select {
223 case <-dc.closed:
224 return true
225 default:
226 return false
227 }
228}
229
[103]230func (dc *downstreamConn) readMessages(ch chan<- downstreamIncomingMessage) error {
[22]231 for {
[55]232 msg, err := dc.irc.ReadMessage()
[22]233 if err == io.EOF {
234 break
235 } else if err != nil {
236 return fmt.Errorf("failed to read IRC command: %v", err)
237 }
238
[64]239 if dc.srv.Debug {
240 dc.logger.Printf("received: %v", msg)
241 }
242
[103]243 ch <- downstreamIncomingMessage{msg, dc}
[22]244 }
245
[45]246 return nil
[22]247}
248
[56]249func (dc *downstreamConn) writeMessages() error {
[57]250 for {
251 var err error
252 var closed bool
253 select {
[102]254 case msg := <-dc.outgoing:
[64]255 if dc.srv.Debug {
256 dc.logger.Printf("sent: %v", msg)
257 }
[57]258 err = dc.irc.WriteMessage(msg)
[104]259 case ringMessage := <-dc.ringMessages:
260 consumer, uc := ringMessage.consumer, ringMessage.upstreamConn
[57]261 for {
262 msg := consumer.Peek()
263 if msg == nil {
264 break
265 }
[105]266
267 dc.lock.Lock()
268 _, ours := dc.ourMessages[msg]
269 delete(dc.ourMessages, msg)
270 dc.lock.Unlock()
271 if ours {
272 // The message comes from our connection, don't echo it
273 // back
[113]274 consumer.Consume()
[105]275 continue
276 }
277
[69]278 msg = msg.Copy()
279 switch msg.Command {
280 case "PRIVMSG":
[119]281 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
282 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
[69]283 default:
284 panic("expected to consume a PRIVMSG message")
285 }
[64]286 if dc.srv.Debug {
287 dc.logger.Printf("sent: %v", msg)
288 }
[57]289 err = dc.irc.WriteMessage(msg)
290 if err != nil {
291 break
292 }
293 consumer.Consume()
294 }
295 case <-dc.closed:
296 closed = true
297 }
298 if err != nil {
[56]299 return err
300 }
[57]301 if closed {
302 break
303 }
[56]304 }
305 return nil
306}
307
[55]308func (dc *downstreamConn) Close() error {
[57]309 if dc.isClosed() {
[26]310 return fmt.Errorf("downstream connection already closed")
311 }
[40]312
[55]313 if u := dc.user; u != nil {
[137]314 u.removeDownstream(dc)
[13]315 }
[40]316
[57]317 close(dc.closed)
[45]318 return nil
[13]319}
320
[55]321func (dc *downstreamConn) SendMessage(msg *irc.Message) {
[102]322 dc.outgoing <- msg
[54]323}
324
[55]325func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]326 switch msg.Command {
[28]327 case "QUIT":
[55]328 return dc.Close()
[13]329 default:
[55]330 if dc.registered {
331 return dc.handleMessageRegistered(msg)
[13]332 } else {
[55]333 return dc.handleMessageUnregistered(msg)
[13]334 }
335 }
336}
337
[55]338func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]339 switch msg.Command {
340 case "NICK":
[117]341 var nick string
342 if err := parseMessageParams(msg, &nick); err != nil {
[43]343 return err
[13]344 }
[117]345 if nick == serviceNick {
346 return ircError{&irc.Message{
347 Command: irc.ERR_NICKNAMEINUSE,
348 Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
349 }}
350 }
351 dc.nick = nick
[13]352 case "USER":
[117]353 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
[43]354 return err
[13]355 }
[85]356 case "PASS":
357 if err := parseMessageParams(msg, &dc.password); err != nil {
358 return err
359 }
[108]360 case "CAP":
361 var subCmd string
362 if err := parseMessageParams(msg, &subCmd); err != nil {
363 return err
364 }
365 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
366 return err
367 }
[112]368 case "AUTHENTICATE":
369 if !dc.caps["sasl"] {
370 return ircError{&irc.Message{
[125]371 Command: irc.ERR_SASLFAIL,
[112]372 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
373 }}
374 }
375 if len(msg.Params) == 0 {
376 return ircError{&irc.Message{
[125]377 Command: irc.ERR_SASLFAIL,
[112]378 Params: []string{"*", "Missing AUTHENTICATE argument"},
379 }}
380 }
381 if dc.nick == "" {
382 return ircError{&irc.Message{
[125]383 Command: irc.ERR_SASLFAIL,
[112]384 Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
385 }}
386 }
387
388 var resp []byte
389 if dc.saslServer == nil {
390 mech := strings.ToUpper(msg.Params[0])
391 switch mech {
392 case "PLAIN":
393 dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
394 return dc.authenticate(username, password)
395 }))
396 default:
397 return ircError{&irc.Message{
[125]398 Command: irc.ERR_SASLFAIL,
[112]399 Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
400 }}
401 }
402 } else if msg.Params[0] == "*" {
403 dc.saslServer = nil
404 return ircError{&irc.Message{
[125]405 Command: irc.ERR_SASLABORTED,
[112]406 Params: []string{"*", "SASL authentication aborted"},
407 }}
408 } else if msg.Params[0] == "+" {
409 resp = nil
410 } else {
411 // TODO: multi-line messages
412 var err error
413 resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
414 if err != nil {
415 dc.saslServer = nil
416 return ircError{&irc.Message{
[125]417 Command: irc.ERR_SASLFAIL,
[112]418 Params: []string{"*", "Invalid base64-encoded response"},
419 }}
420 }
421 }
422
423 challenge, done, err := dc.saslServer.Next(resp)
424 if err != nil {
425 dc.saslServer = nil
426 if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
427 return ircError{&irc.Message{
[125]428 Command: irc.ERR_SASLFAIL,
[112]429 Params: []string{"*", ircErr.Message.Params[1]},
430 }}
431 }
432 dc.SendMessage(&irc.Message{
433 Prefix: dc.srv.prefix(),
[125]434 Command: irc.ERR_SASLFAIL,
[112]435 Params: []string{"*", "SASL error"},
436 })
437 return fmt.Errorf("SASL authentication failed: %v", err)
438 } else if done {
439 dc.saslServer = nil
440 dc.SendMessage(&irc.Message{
441 Prefix: dc.srv.prefix(),
[125]442 Command: irc.RPL_LOGGEDIN,
[112]443 Params: []string{dc.nick, dc.nick, dc.user.Username, "You are now logged in"},
444 })
445 dc.SendMessage(&irc.Message{
446 Prefix: dc.srv.prefix(),
[125]447 Command: irc.RPL_SASLSUCCESS,
[112]448 Params: []string{dc.nick, "SASL authentication successful"},
449 })
450 } else {
451 challengeStr := "+"
[135]452 if len(challenge) > 0 {
[112]453 challengeStr = base64.StdEncoding.EncodeToString(challenge)
454 }
455
456 // TODO: multi-line messages
457 dc.SendMessage(&irc.Message{
458 Prefix: dc.srv.prefix(),
459 Command: "AUTHENTICATE",
460 Params: []string{challengeStr},
461 })
462 }
[13]463 default:
[55]464 dc.logger.Printf("unhandled message: %v", msg)
[13]465 return newUnknownCommandError(msg.Command)
466 }
[108]467 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
[55]468 return dc.register()
[13]469 }
470 return nil
471}
472
[108]473func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
[111]474 cmd = strings.ToUpper(cmd)
475
[108]476 replyTo := dc.nick
477 if !dc.registered {
478 replyTo = "*"
479 }
480
481 switch cmd {
482 case "LS":
483 if len(args) > 0 {
484 var err error
485 if dc.capVersion, err = strconv.Atoi(args[0]); err != nil {
486 return err
487 }
488 }
489
490 var caps []string
[112]491 if dc.capVersion >= 302 {
[108]492 caps = append(caps, "sasl=PLAIN")
493 } else {
494 caps = append(caps, "sasl")
[112]495 }
[108]496
497 // TODO: multi-line replies
498 dc.SendMessage(&irc.Message{
499 Prefix: dc.srv.prefix(),
500 Command: "CAP",
501 Params: []string{replyTo, "LS", strings.Join(caps, " ")},
502 })
503
504 if !dc.registered {
505 dc.negociatingCaps = true
506 }
507 case "LIST":
508 var caps []string
509 for name := range dc.caps {
510 caps = append(caps, name)
511 }
512
513 // TODO: multi-line replies
514 dc.SendMessage(&irc.Message{
515 Prefix: dc.srv.prefix(),
516 Command: "CAP",
517 Params: []string{replyTo, "LIST", strings.Join(caps, " ")},
518 })
519 case "REQ":
520 if len(args) == 0 {
521 return ircError{&irc.Message{
522 Command: err_invalidcapcmd,
523 Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"},
524 }}
525 }
526
527 caps := strings.Fields(args[0])
528 ack := true
529 for _, name := range caps {
530 name = strings.ToLower(name)
531 enable := !strings.HasPrefix(name, "-")
532 if !enable {
533 name = strings.TrimPrefix(name, "-")
534 }
535
536 enabled := dc.caps[name]
537 if enable == enabled {
538 continue
539 }
540
541 switch name {
[112]542 case "sasl":
543 dc.caps[name] = enable
[108]544 default:
545 ack = false
546 }
547 }
548
549 reply := "NAK"
550 if ack {
551 reply = "ACK"
552 }
553 dc.SendMessage(&irc.Message{
554 Prefix: dc.srv.prefix(),
555 Command: "CAP",
556 Params: []string{replyTo, reply, args[0]},
557 })
558 case "END":
559 dc.negociatingCaps = false
560 default:
561 return ircError{&irc.Message{
562 Command: err_invalidcapcmd,
563 Params: []string{replyTo, cmd, "Unknown CAP command"},
564 }}
565 }
566 return nil
567}
568
[91]569func sanityCheckServer(addr string) error {
570 dialer := net.Dialer{Timeout: 30 * time.Second}
571 conn, err := tls.DialWithDialer(&dialer, "tcp", addr, nil)
572 if err != nil {
573 return err
574 }
575 return conn.Close()
576}
577
[112]578func unmarshalUsername(rawUsername string) (username, network string) {
579 username = rawUsername
[73]580 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
[112]581 network = username[i+1:]
[73]582 }
583 if i := strings.IndexAny(username, "/@"); i >= 0 {
584 username = username[:i]
585 }
[112]586 return username, network
587}
[73]588
[112]589func (dc *downstreamConn) setNetwork(networkName string) error {
590 if networkName == "" {
591 return nil
592 }
[85]593
[112]594 network := dc.user.getNetwork(networkName)
595 if network == nil {
596 addr := networkName
597 if !strings.ContainsRune(addr, ':') {
598 addr = addr + ":6697"
599 }
600
601 dc.logger.Printf("trying to connect to new network %q", addr)
602 if err := sanityCheckServer(addr); err != nil {
603 dc.logger.Printf("failed to connect to %q: %v", addr, err)
604 return ircError{&irc.Message{
605 Command: irc.ERR_PASSWDMISMATCH,
606 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", networkName)},
607 }}
608 }
609
610 dc.logger.Printf("auto-saving network %q", networkName)
611 var err error
[120]612 network, err = dc.user.createNetwork(&Network{
613 Addr: networkName,
614 Nick: dc.nick,
615 })
[112]616 if err != nil {
617 return err
618 }
619 }
620
621 dc.network = network
622 return nil
623}
624
625func (dc *downstreamConn) authenticate(username, password string) error {
626 username, networkName := unmarshalUsername(username)
627
[73]628 u := dc.srv.getUser(username)
[38]629 if u == nil {
[85]630 dc.logger.Printf("failed authentication for %q: unknown username", username)
631 return errAuthFailed
[37]632 }
633
[85]634 err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
635 if err != nil {
636 dc.logger.Printf("failed authentication for %q: %v", username, err)
637 return errAuthFailed
638 }
639
[112]640 dc.user = u
[91]641
[112]642 return dc.setNetwork(networkName)
643}
[91]644
[112]645func (dc *downstreamConn) register() error {
646 password := dc.password
647 dc.password = ""
648 if dc.user == nil {
649 if err := dc.authenticate(dc.rawUsername, password); err != nil {
650 return err
[73]651 }
[112]652 } else if dc.network == nil {
653 _, networkName := unmarshalUsername(dc.rawUsername)
654 if err := dc.setNetwork(networkName); err != nil {
655 return err
656 }
[73]657 }
658
[55]659 dc.registered = true
[112]660 dc.username = dc.user.Username
[130]661 dc.logger.Printf("registration complete for user %q", dc.username)
[13]662
[137]663 firstDownstream := dc.user.addDownstream(dc)
[40]664
[55]665 dc.SendMessage(&irc.Message{
666 Prefix: dc.srv.prefix(),
[13]667 Command: irc.RPL_WELCOME,
[98]668 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
[54]669 })
[55]670 dc.SendMessage(&irc.Message{
671 Prefix: dc.srv.prefix(),
[13]672 Command: irc.RPL_YOURHOST,
[55]673 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]674 })
[55]675 dc.SendMessage(&irc.Message{
676 Prefix: dc.srv.prefix(),
[13]677 Command: irc.RPL_CREATED,
[55]678 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]679 })
[55]680 dc.SendMessage(&irc.Message{
681 Prefix: dc.srv.prefix(),
[13]682 Command: irc.RPL_MYINFO,
[98]683 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
[54]684 })
[93]685 // TODO: RPL_ISUPPORT
[55]686 dc.SendMessage(&irc.Message{
687 Prefix: dc.srv.prefix(),
[13]688 Command: irc.ERR_NOMOTD,
[55]689 Params: []string{dc.nick, "No MOTD"},
[54]690 })
[13]691
[73]692 dc.forEachUpstream(func(uc *upstreamConn) {
[30]693 for _, ch := range uc.channels {
694 if ch.complete {
[132]695 dc.SendMessage(&irc.Message{
696 Prefix: dc.prefix(),
697 Command: "JOIN",
698 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
699 })
700
[55]701 forwardChannel(dc, ch)
[30]702 }
703 }
[143]704 })
[50]705
[143]706 dc.forEachNetwork(func(net *network) {
[144]707 // TODO: need to take dc.network into account when deciding whether or
708 // not to load history
709 dc.runNetwork(net, firstDownstream)
710 })
[57]711
[144]712 return nil
713}
714
715// runNetwork starts listening for messages coming from the network's ring
716// buffer.
717//
718// It panics if the network is not suitable for the downstream connection.
719func (dc *downstreamConn) runNetwork(net *network, loadHistory bool) {
720 if dc.network != nil && net != dc.network {
721 panic("network not suitable for downstream connection")
722 }
723
724 historyName := dc.rawUsername
725
726 var seqPtr *uint64
727 if loadHistory {
728 net.lock.Lock()
729 seq, ok := net.history[historyName]
730 net.lock.Unlock()
731 if ok {
732 seqPtr = &seq
[50]733 }
[144]734 }
[57]735
[144]736 consumer, ch := net.ring.NewConsumer(seqPtr)
737 go func() {
738 for {
739 var closed bool
740 select {
741 case <-ch:
742 uc := net.upstream()
743 if uc == nil {
744 dc.logger.Printf("ignoring messages for upstream %q: upstream is disconnected", net.Addr)
[57]745 break
746 }
[144]747 dc.ringMessages <- ringMessage{consumer, uc}
748 case <-dc.closed:
749 closed = true
[57]750 }
[144]751 if closed {
752 break
753 }
754 }
[57]755
[144]756 seq := consumer.Close()
[57]757
[144]758 // TODO: need to take dc.network into account here
759 dc.user.lock.Lock()
760 lastDownstream := len(dc.user.downstreamConns) == 0
761 dc.user.lock.Unlock()
[57]762
[144]763 if lastDownstream {
764 net.lock.Lock()
765 net.history[historyName] = seq
766 net.lock.Unlock()
767 }
768 }()
[13]769}
770
[103]771func (dc *downstreamConn) runUntilRegistered() error {
772 for !dc.registered {
773 msg, err := dc.irc.ReadMessage()
[106]774 if err != nil {
[103]775 return fmt.Errorf("failed to read IRC command: %v", err)
776 }
777
[110]778 if dc.srv.Debug {
779 dc.logger.Printf("received: %v", msg)
780 }
781
[103]782 err = dc.handleMessage(msg)
783 if ircErr, ok := err.(ircError); ok {
784 ircErr.Message.Prefix = dc.srv.prefix()
785 dc.SendMessage(ircErr.Message)
786 } else if err != nil {
787 return fmt.Errorf("failed to handle IRC command %q: %v", msg, err)
788 }
789 }
790
791 return nil
792}
793
[55]794func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]795 switch msg.Command {
[111]796 case "CAP":
797 var subCmd string
798 if err := parseMessageParams(msg, &subCmd); err != nil {
799 return err
800 }
801 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
802 return err
803 }
[107]804 case "PING":
805 dc.SendMessage(&irc.Message{
806 Prefix: dc.srv.prefix(),
807 Command: "PONG",
808 Params: msg.Params,
809 })
810 return nil
[42]811 case "USER":
[13]812 return ircError{&irc.Message{
813 Command: irc.ERR_ALREADYREGISTERED,
[55]814 Params: []string{dc.nick, "You may not reregister"},
[13]815 }}
[42]816 case "NICK":
[90]817 var nick string
818 if err := parseMessageParams(msg, &nick); err != nil {
819 return err
820 }
821
822 var err error
823 dc.forEachNetwork(func(n *network) {
824 if err != nil {
825 return
826 }
827 n.Nick = nick
828 err = dc.srv.db.StoreNetwork(dc.user.Username, &n.Network)
829 })
830 if err != nil {
831 return err
832 }
833
[73]834 dc.forEachUpstream(func(uc *upstreamConn) {
[60]835 uc.SendMessage(msg)
[42]836 })
[146]837 case "JOIN":
838 var namesStr string
839 if err := parseMessageParams(msg, &namesStr); err != nil {
[48]840 return err
841 }
842
[146]843 var keys []string
844 if len(msg.Params) > 1 {
845 keys = strings.Split(msg.Params[1], ",")
846 }
847
848 for i, name := range strings.Split(namesStr, ",") {
[145]849 uc, upstreamName, err := dc.unmarshalEntity(name)
850 if err != nil {
[158]851 return err
[145]852 }
[48]853
[146]854 var key string
855 if len(keys) > i {
856 key = keys[i]
857 }
858
859 params := []string{upstreamName}
860 if key != "" {
861 params = append(params, key)
862 }
[145]863 uc.SendMessage(&irc.Message{
[146]864 Command: "JOIN",
865 Params: params,
[145]866 })
[89]867
[146]868 err = dc.srv.db.StoreChannel(uc.network.ID, &Channel{
869 Name: upstreamName,
870 Key: key,
871 })
872 if err != nil {
873 dc.logger.Printf("failed to create channel %q in DB: %v", upstreamName, err)
[89]874 }
875 }
[146]876 case "PART":
877 var namesStr string
878 if err := parseMessageParams(msg, &namesStr); err != nil {
879 return err
880 }
881
882 var reason string
883 if len(msg.Params) > 1 {
884 reason = msg.Params[1]
885 }
886
887 for _, name := range strings.Split(namesStr, ",") {
888 uc, upstreamName, err := dc.unmarshalEntity(name)
889 if err != nil {
[158]890 return err
[146]891 }
892
893 params := []string{upstreamName}
894 if reason != "" {
895 params = append(params, reason)
896 }
897 uc.SendMessage(&irc.Message{
898 Command: "PART",
899 Params: params,
900 })
901
902 if err := dc.srv.db.DeleteChannel(uc.network.ID, upstreamName); err != nil {
903 dc.logger.Printf("failed to delete channel %q in DB: %v", upstreamName, err)
904 }
905 }
[159]906 case "KICK":
907 var channelStr, userStr string
908 if err := parseMessageParams(msg, &channelStr, &userStr); err != nil {
909 return err
910 }
911
912 channels := strings.Split(channelStr, ",")
913 users := strings.Split(userStr, ",")
914
915 var reason string
916 if len(msg.Params) > 2 {
917 reason = msg.Params[2]
918 }
919
920 if len(channels) != 1 && len(channels) != len(users) {
921 return ircError{&irc.Message{
922 Command: irc.ERR_BADCHANMASK,
923 Params: []string{dc.nick, channelStr, "Bad channel mask"},
924 }}
925 }
926
927 for i, user := range users {
928 var channel string
929 if len(channels) == 1 {
930 channel = channels[0]
931 } else {
932 channel = channels[i]
933 }
934
935 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
936 if err != nil {
937 return err
938 }
939
940 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
941 if err != nil {
942 return err
943 }
944
945 if ucChannel != ucUser {
946 return ircError{&irc.Message{
947 Command: irc.ERR_USERNOTINCHANNEL,
948 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
949 }}
950 }
951 uc := ucChannel
952
953 params := []string{upstreamChannel, upstreamUser}
954 if reason != "" {
955 params = append(params, reason)
956 }
957 uc.SendMessage(&irc.Message{
958 Command: "KICK",
959 Params: params,
960 })
961 }
[69]962 case "MODE":
[46]963 var name string
964 if err := parseMessageParams(msg, &name); err != nil {
965 return err
966 }
967
968 var modeStr string
969 if len(msg.Params) > 1 {
970 modeStr = msg.Params[1]
971 }
972
[139]973 if name == dc.nick {
[46]974 if modeStr != "" {
[73]975 dc.forEachUpstream(func(uc *upstreamConn) {
[69]976 uc.SendMessage(&irc.Message{
977 Command: "MODE",
978 Params: []string{uc.nick, modeStr},
979 })
[46]980 })
981 } else {
[55]982 dc.SendMessage(&irc.Message{
983 Prefix: dc.srv.prefix(),
[46]984 Command: irc.RPL_UMODEIS,
[129]985 Params: []string{dc.nick, ""}, // TODO
[54]986 })
[46]987 }
[139]988 return nil
[46]989 }
[139]990
991 uc, upstreamName, err := dc.unmarshalEntity(name)
992 if err != nil {
993 return err
994 }
995
996 if !uc.isChannel(upstreamName) {
997 return ircError{&irc.Message{
998 Command: irc.ERR_USERSDONTMATCH,
999 Params: []string{dc.nick, "Cannot change mode for other users"},
1000 }}
1001 }
1002
1003 if modeStr != "" {
1004 params := []string{upstreamName, modeStr}
1005 params = append(params, msg.Params[2:]...)
1006 uc.SendMessage(&irc.Message{
1007 Command: "MODE",
1008 Params: params,
1009 })
1010 } else {
1011 ch, ok := uc.channels[upstreamName]
1012 if !ok {
1013 return ircError{&irc.Message{
1014 Command: irc.ERR_NOSUCHCHANNEL,
1015 Params: []string{dc.nick, name, "No such channel"},
1016 }}
1017 }
1018
1019 if ch.modes == nil {
1020 // we haven't received the initial RPL_CHANNELMODEIS yet
1021 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS
1022 return nil
1023 }
1024
1025 modeStr, modeParams := ch.modes.Format()
1026 params := []string{dc.nick, name, modeStr}
1027 params = append(params, modeParams...)
1028
1029 dc.SendMessage(&irc.Message{
1030 Prefix: dc.srv.prefix(),
1031 Command: irc.RPL_CHANNELMODEIS,
1032 Params: params,
1033 })
1034 }
[140]1035 case "NAMES":
1036 if len(msg.Params) == 0 {
1037 dc.SendMessage(&irc.Message{
1038 Prefix: dc.srv.prefix(),
1039 Command: irc.RPL_ENDOFNAMES,
1040 Params: []string{dc.nick, "*", "End of /NAMES list"},
1041 })
1042 return nil
1043 }
1044
1045 channels := strings.Split(msg.Params[0], ",")
1046 for _, channel := range channels {
1047 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1048 if err != nil {
1049 return err
1050 }
1051
1052 ch, ok := uc.channels[upstreamChannel]
1053 if ok {
1054 sendNames(dc, ch)
1055 } else {
1056 // NAMES on a channel we have not joined, ask upstream
[156]1057 uc.SendMessageLabeled(dc, &irc.Message{
[140]1058 Command: "NAMES",
1059 Params: []string{upstreamChannel},
1060 })
1061 }
1062 }
[127]1063 case "WHO":
1064 if len(msg.Params) == 0 {
1065 // TODO: support WHO without parameters
1066 dc.SendMessage(&irc.Message{
1067 Prefix: dc.srv.prefix(),
1068 Command: irc.RPL_ENDOFWHO,
[140]1069 Params: []string{dc.nick, "*", "End of /WHO list"},
[127]1070 })
1071 return nil
1072 }
1073
1074 // TODO: support WHO masks
1075 entity := msg.Params[0]
1076
[142]1077 if entity == dc.nick {
1078 // TODO: support AWAY (H/G) in self WHO reply
1079 dc.SendMessage(&irc.Message{
1080 Prefix: dc.srv.prefix(),
1081 Command: irc.RPL_WHOREPLY,
1082 Params: []string{dc.nick, "*", dc.username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
1083 })
1084 dc.SendMessage(&irc.Message{
1085 Prefix: dc.srv.prefix(),
1086 Command: irc.RPL_ENDOFWHO,
1087 Params: []string{dc.nick, dc.nick, "End of /WHO list"},
1088 })
1089 return nil
1090 }
1091
[127]1092 uc, upstreamName, err := dc.unmarshalEntity(entity)
1093 if err != nil {
1094 return err
1095 }
1096
1097 var params []string
1098 if len(msg.Params) == 2 {
1099 params = []string{upstreamName, msg.Params[1]}
1100 } else {
1101 params = []string{upstreamName}
1102 }
1103
[156]1104 uc.SendMessageLabeled(dc, &irc.Message{
[127]1105 Command: "WHO",
1106 Params: params,
1107 })
[128]1108 case "WHOIS":
1109 if len(msg.Params) == 0 {
1110 return ircError{&irc.Message{
1111 Command: irc.ERR_NONICKNAMEGIVEN,
1112 Params: []string{dc.nick, "No nickname given"},
1113 }}
1114 }
1115
1116 var target, mask string
1117 if len(msg.Params) == 1 {
1118 target = ""
1119 mask = msg.Params[0]
1120 } else {
1121 target = msg.Params[0]
1122 mask = msg.Params[1]
1123 }
1124 // TODO: support multiple WHOIS users
1125 if i := strings.IndexByte(mask, ','); i >= 0 {
1126 mask = mask[:i]
1127 }
1128
[142]1129 if mask == dc.nick {
1130 dc.SendMessage(&irc.Message{
1131 Prefix: dc.srv.prefix(),
1132 Command: irc.RPL_WHOISUSER,
1133 Params: []string{dc.nick, dc.nick, dc.username, dc.hostname, "*", dc.realname},
1134 })
1135 dc.SendMessage(&irc.Message{
1136 Prefix: dc.srv.prefix(),
1137 Command: irc.RPL_WHOISSERVER,
1138 Params: []string{dc.nick, dc.nick, dc.srv.Hostname, "soju"},
1139 })
1140 dc.SendMessage(&irc.Message{
1141 Prefix: dc.srv.prefix(),
1142 Command: irc.RPL_ENDOFWHOIS,
1143 Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
1144 })
1145 return nil
1146 }
1147
[128]1148 // TODO: support WHOIS masks
1149 uc, upstreamNick, err := dc.unmarshalEntity(mask)
1150 if err != nil {
1151 return err
1152 }
1153
1154 var params []string
1155 if target != "" {
1156 params = []string{target, upstreamNick}
1157 } else {
1158 params = []string{upstreamNick}
1159 }
1160
[156]1161 uc.SendMessageLabeled(dc, &irc.Message{
[128]1162 Command: "WHOIS",
1163 Params: params,
1164 })
[58]1165 case "PRIVMSG":
1166 var targetsStr, text string
1167 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1168 return err
1169 }
1170
1171 for _, name := range strings.Split(targetsStr, ",") {
[117]1172 if name == serviceNick {
1173 handleServicePRIVMSG(dc, text)
1174 continue
1175 }
1176
[127]1177 uc, upstreamName, err := dc.unmarshalEntity(name)
[58]1178 if err != nil {
1179 return err
1180 }
1181
[95]1182 if upstreamName == "NickServ" {
1183 dc.handleNickServPRIVMSG(uc, text)
1184 }
1185
[69]1186 uc.SendMessage(&irc.Message{
[58]1187 Command: "PRIVMSG",
[69]1188 Params: []string{upstreamName, text},
[60]1189 })
[105]1190
[113]1191 echoMsg := &irc.Message{
1192 Prefix: &irc.Prefix{
1193 Name: uc.nick,
1194 User: uc.username,
1195 },
[114]1196 Command: "PRIVMSG",
[113]1197 Params: []string{upstreamName, text},
1198 }
[105]1199 dc.lock.Lock()
[113]1200 dc.ourMessages[echoMsg] = struct{}{}
[105]1201 dc.lock.Unlock()
1202
[143]1203 uc.network.ring.Produce(echoMsg)
[58]1204 }
[13]1205 default:
[55]1206 dc.logger.Printf("unhandled message: %v", msg)
[13]1207 return newUnknownCommandError(msg.Command)
1208 }
[42]1209 return nil
[13]1210}
[95]1211
1212func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1213 username, password, ok := parseNickServCredentials(text, uc.nick)
1214 if !ok {
1215 return
1216 }
1217
1218 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1219 n := uc.network
1220 n.SASL.Mechanism = "PLAIN"
1221 n.SASL.Plain.Username = username
1222 n.SASL.Plain.Password = password
1223 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1224 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1225 }
1226}
1227
1228func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1229 fields := strings.Fields(text)
1230 if len(fields) < 2 {
1231 return "", "", false
1232 }
1233 cmd := strings.ToUpper(fields[0])
1234 params := fields[1:]
1235 switch cmd {
1236 case "REGISTER":
1237 username = nick
1238 password = params[0]
1239 case "IDENTIFY":
1240 if len(params) == 1 {
1241 username = nick
1242 } else {
1243 username = params[0]
1244 }
1245 password = params[1]
1246 }
1247 return username, password, true
1248}
Note: See TracBrowser for help on using the repository browser.