source: code/trunk/downstream.go@ 167

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

Add eventDownstreamDisconnected

This should remove the need for protecting user.downstreamConns with a
mutex.

File size: 29.6 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
[165]230func (dc *downstreamConn) readMessages(ch chan<- event) 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
[165]243 ch <- eventDownstreamMessage{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
[57]313 close(dc.closed)
[45]314 return nil
[13]315}
316
[55]317func (dc *downstreamConn) SendMessage(msg *irc.Message) {
[102]318 dc.outgoing <- msg
[54]319}
320
[55]321func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]322 switch msg.Command {
[28]323 case "QUIT":
[55]324 return dc.Close()
[13]325 default:
[55]326 if dc.registered {
327 return dc.handleMessageRegistered(msg)
[13]328 } else {
[55]329 return dc.handleMessageUnregistered(msg)
[13]330 }
331 }
332}
333
[55]334func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]335 switch msg.Command {
336 case "NICK":
[117]337 var nick string
338 if err := parseMessageParams(msg, &nick); err != nil {
[43]339 return err
[13]340 }
[117]341 if nick == serviceNick {
342 return ircError{&irc.Message{
343 Command: irc.ERR_NICKNAMEINUSE,
344 Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
345 }}
346 }
347 dc.nick = nick
[13]348 case "USER":
[117]349 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
[43]350 return err
[13]351 }
[85]352 case "PASS":
353 if err := parseMessageParams(msg, &dc.password); err != nil {
354 return err
355 }
[108]356 case "CAP":
357 var subCmd string
358 if err := parseMessageParams(msg, &subCmd); err != nil {
359 return err
360 }
361 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
362 return err
363 }
[112]364 case "AUTHENTICATE":
365 if !dc.caps["sasl"] {
366 return ircError{&irc.Message{
[125]367 Command: irc.ERR_SASLFAIL,
[112]368 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
369 }}
370 }
371 if len(msg.Params) == 0 {
372 return ircError{&irc.Message{
[125]373 Command: irc.ERR_SASLFAIL,
[112]374 Params: []string{"*", "Missing AUTHENTICATE argument"},
375 }}
376 }
377 if dc.nick == "" {
378 return ircError{&irc.Message{
[125]379 Command: irc.ERR_SASLFAIL,
[112]380 Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
381 }}
382 }
383
384 var resp []byte
385 if dc.saslServer == nil {
386 mech := strings.ToUpper(msg.Params[0])
387 switch mech {
388 case "PLAIN":
389 dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
390 return dc.authenticate(username, password)
391 }))
392 default:
393 return ircError{&irc.Message{
[125]394 Command: irc.ERR_SASLFAIL,
[112]395 Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
396 }}
397 }
398 } else if msg.Params[0] == "*" {
399 dc.saslServer = nil
400 return ircError{&irc.Message{
[125]401 Command: irc.ERR_SASLABORTED,
[112]402 Params: []string{"*", "SASL authentication aborted"},
403 }}
404 } else if msg.Params[0] == "+" {
405 resp = nil
406 } else {
407 // TODO: multi-line messages
408 var err error
409 resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
410 if err != nil {
411 dc.saslServer = nil
412 return ircError{&irc.Message{
[125]413 Command: irc.ERR_SASLFAIL,
[112]414 Params: []string{"*", "Invalid base64-encoded response"},
415 }}
416 }
417 }
418
419 challenge, done, err := dc.saslServer.Next(resp)
420 if err != nil {
421 dc.saslServer = nil
422 if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
423 return ircError{&irc.Message{
[125]424 Command: irc.ERR_SASLFAIL,
[112]425 Params: []string{"*", ircErr.Message.Params[1]},
426 }}
427 }
428 dc.SendMessage(&irc.Message{
429 Prefix: dc.srv.prefix(),
[125]430 Command: irc.ERR_SASLFAIL,
[112]431 Params: []string{"*", "SASL error"},
432 })
433 return fmt.Errorf("SASL authentication failed: %v", err)
434 } else if done {
435 dc.saslServer = nil
436 dc.SendMessage(&irc.Message{
437 Prefix: dc.srv.prefix(),
[125]438 Command: irc.RPL_LOGGEDIN,
[112]439 Params: []string{dc.nick, dc.nick, dc.user.Username, "You are now logged in"},
440 })
441 dc.SendMessage(&irc.Message{
442 Prefix: dc.srv.prefix(),
[125]443 Command: irc.RPL_SASLSUCCESS,
[112]444 Params: []string{dc.nick, "SASL authentication successful"},
445 })
446 } else {
447 challengeStr := "+"
[135]448 if len(challenge) > 0 {
[112]449 challengeStr = base64.StdEncoding.EncodeToString(challenge)
450 }
451
452 // TODO: multi-line messages
453 dc.SendMessage(&irc.Message{
454 Prefix: dc.srv.prefix(),
455 Command: "AUTHENTICATE",
456 Params: []string{challengeStr},
457 })
458 }
[13]459 default:
[55]460 dc.logger.Printf("unhandled message: %v", msg)
[13]461 return newUnknownCommandError(msg.Command)
462 }
[108]463 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
[55]464 return dc.register()
[13]465 }
466 return nil
467}
468
[108]469func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
[111]470 cmd = strings.ToUpper(cmd)
471
[108]472 replyTo := dc.nick
473 if !dc.registered {
474 replyTo = "*"
475 }
476
477 switch cmd {
478 case "LS":
479 if len(args) > 0 {
480 var err error
481 if dc.capVersion, err = strconv.Atoi(args[0]); err != nil {
482 return err
483 }
484 }
485
486 var caps []string
[112]487 if dc.capVersion >= 302 {
[108]488 caps = append(caps, "sasl=PLAIN")
489 } else {
490 caps = append(caps, "sasl")
[112]491 }
[108]492
493 // TODO: multi-line replies
494 dc.SendMessage(&irc.Message{
495 Prefix: dc.srv.prefix(),
496 Command: "CAP",
497 Params: []string{replyTo, "LS", strings.Join(caps, " ")},
498 })
499
500 if !dc.registered {
501 dc.negociatingCaps = true
502 }
503 case "LIST":
504 var caps []string
505 for name := range dc.caps {
506 caps = append(caps, name)
507 }
508
509 // TODO: multi-line replies
510 dc.SendMessage(&irc.Message{
511 Prefix: dc.srv.prefix(),
512 Command: "CAP",
513 Params: []string{replyTo, "LIST", strings.Join(caps, " ")},
514 })
515 case "REQ":
516 if len(args) == 0 {
517 return ircError{&irc.Message{
518 Command: err_invalidcapcmd,
519 Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"},
520 }}
521 }
522
523 caps := strings.Fields(args[0])
524 ack := true
525 for _, name := range caps {
526 name = strings.ToLower(name)
527 enable := !strings.HasPrefix(name, "-")
528 if !enable {
529 name = strings.TrimPrefix(name, "-")
530 }
531
532 enabled := dc.caps[name]
533 if enable == enabled {
534 continue
535 }
536
537 switch name {
[112]538 case "sasl":
539 dc.caps[name] = enable
[108]540 default:
541 ack = false
542 }
543 }
544
545 reply := "NAK"
546 if ack {
547 reply = "ACK"
548 }
549 dc.SendMessage(&irc.Message{
550 Prefix: dc.srv.prefix(),
551 Command: "CAP",
552 Params: []string{replyTo, reply, args[0]},
553 })
554 case "END":
555 dc.negociatingCaps = false
556 default:
557 return ircError{&irc.Message{
558 Command: err_invalidcapcmd,
559 Params: []string{replyTo, cmd, "Unknown CAP command"},
560 }}
561 }
562 return nil
563}
564
[91]565func sanityCheckServer(addr string) error {
566 dialer := net.Dialer{Timeout: 30 * time.Second}
567 conn, err := tls.DialWithDialer(&dialer, "tcp", addr, nil)
568 if err != nil {
569 return err
570 }
571 return conn.Close()
572}
573
[112]574func unmarshalUsername(rawUsername string) (username, network string) {
575 username = rawUsername
[73]576 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
[112]577 network = username[i+1:]
[73]578 }
579 if i := strings.IndexAny(username, "/@"); i >= 0 {
580 username = username[:i]
581 }
[112]582 return username, network
583}
[73]584
[112]585func (dc *downstreamConn) setNetwork(networkName string) error {
586 if networkName == "" {
587 return nil
588 }
[85]589
[112]590 network := dc.user.getNetwork(networkName)
591 if network == nil {
592 addr := networkName
593 if !strings.ContainsRune(addr, ':') {
594 addr = addr + ":6697"
595 }
596
597 dc.logger.Printf("trying to connect to new network %q", addr)
598 if err := sanityCheckServer(addr); err != nil {
599 dc.logger.Printf("failed to connect to %q: %v", addr, err)
600 return ircError{&irc.Message{
601 Command: irc.ERR_PASSWDMISMATCH,
602 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", networkName)},
603 }}
604 }
605
606 dc.logger.Printf("auto-saving network %q", networkName)
607 var err error
[120]608 network, err = dc.user.createNetwork(&Network{
609 Addr: networkName,
610 Nick: dc.nick,
611 })
[112]612 if err != nil {
613 return err
614 }
615 }
616
617 dc.network = network
618 return nil
619}
620
621func (dc *downstreamConn) authenticate(username, password string) error {
622 username, networkName := unmarshalUsername(username)
623
[73]624 u := dc.srv.getUser(username)
[38]625 if u == nil {
[85]626 dc.logger.Printf("failed authentication for %q: unknown username", username)
627 return errAuthFailed
[37]628 }
629
[85]630 err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
631 if err != nil {
632 dc.logger.Printf("failed authentication for %q: %v", username, err)
633 return errAuthFailed
634 }
635
[112]636 dc.user = u
[91]637
[112]638 return dc.setNetwork(networkName)
639}
[91]640
[112]641func (dc *downstreamConn) register() error {
642 password := dc.password
643 dc.password = ""
644 if dc.user == nil {
645 if err := dc.authenticate(dc.rawUsername, password); err != nil {
646 return err
[73]647 }
[112]648 } else if dc.network == nil {
649 _, networkName := unmarshalUsername(dc.rawUsername)
650 if err := dc.setNetwork(networkName); err != nil {
651 return err
652 }
[73]653 }
654
[55]655 dc.registered = true
[112]656 dc.username = dc.user.Username
[130]657 dc.logger.Printf("registration complete for user %q", dc.username)
[13]658
[166]659 dc.user.lock.Lock()
660 firstDownstream := len(dc.user.downstreamConns) == 0
661 dc.user.lock.Unlock()
[40]662
[55]663 dc.SendMessage(&irc.Message{
664 Prefix: dc.srv.prefix(),
[13]665 Command: irc.RPL_WELCOME,
[98]666 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
[54]667 })
[55]668 dc.SendMessage(&irc.Message{
669 Prefix: dc.srv.prefix(),
[13]670 Command: irc.RPL_YOURHOST,
[55]671 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]672 })
[55]673 dc.SendMessage(&irc.Message{
674 Prefix: dc.srv.prefix(),
[13]675 Command: irc.RPL_CREATED,
[55]676 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]677 })
[55]678 dc.SendMessage(&irc.Message{
679 Prefix: dc.srv.prefix(),
[13]680 Command: irc.RPL_MYINFO,
[98]681 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
[54]682 })
[93]683 // TODO: RPL_ISUPPORT
[55]684 dc.SendMessage(&irc.Message{
685 Prefix: dc.srv.prefix(),
[13]686 Command: irc.ERR_NOMOTD,
[55]687 Params: []string{dc.nick, "No MOTD"},
[54]688 })
[13]689
[73]690 dc.forEachUpstream(func(uc *upstreamConn) {
[30]691 for _, ch := range uc.channels {
692 if ch.complete {
[132]693 dc.SendMessage(&irc.Message{
694 Prefix: dc.prefix(),
695 Command: "JOIN",
696 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
697 })
698
[55]699 forwardChannel(dc, ch)
[30]700 }
701 }
[143]702 })
[50]703
[143]704 dc.forEachNetwork(func(net *network) {
[144]705 // TODO: need to take dc.network into account when deciding whether or
706 // not to load history
707 dc.runNetwork(net, firstDownstream)
708 })
[57]709
[144]710 return nil
711}
712
713// runNetwork starts listening for messages coming from the network's ring
714// buffer.
715//
716// It panics if the network is not suitable for the downstream connection.
717func (dc *downstreamConn) runNetwork(net *network, loadHistory bool) {
718 if dc.network != nil && net != dc.network {
719 panic("network not suitable for downstream connection")
720 }
721
722 historyName := dc.rawUsername
723
724 var seqPtr *uint64
725 if loadHistory {
726 net.lock.Lock()
727 seq, ok := net.history[historyName]
728 net.lock.Unlock()
729 if ok {
730 seqPtr = &seq
[50]731 }
[144]732 }
[57]733
[144]734 consumer, ch := net.ring.NewConsumer(seqPtr)
735 go func() {
736 for {
737 var closed bool
738 select {
739 case <-ch:
740 uc := net.upstream()
741 if uc == nil {
742 dc.logger.Printf("ignoring messages for upstream %q: upstream is disconnected", net.Addr)
[57]743 break
744 }
[144]745 dc.ringMessages <- ringMessage{consumer, uc}
746 case <-dc.closed:
747 closed = true
[57]748 }
[144]749 if closed {
750 break
751 }
752 }
[57]753
[144]754 seq := consumer.Close()
[57]755
[167]756 net.lock.Lock()
757 net.history[historyName] = seq
758 net.lock.Unlock()
[144]759 }()
[13]760}
761
[103]762func (dc *downstreamConn) runUntilRegistered() error {
763 for !dc.registered {
764 msg, err := dc.irc.ReadMessage()
[106]765 if err != nil {
[103]766 return fmt.Errorf("failed to read IRC command: %v", err)
767 }
768
[110]769 if dc.srv.Debug {
770 dc.logger.Printf("received: %v", msg)
771 }
772
[103]773 err = dc.handleMessage(msg)
774 if ircErr, ok := err.(ircError); ok {
775 ircErr.Message.Prefix = dc.srv.prefix()
776 dc.SendMessage(ircErr.Message)
777 } else if err != nil {
778 return fmt.Errorf("failed to handle IRC command %q: %v", msg, err)
779 }
780 }
781
782 return nil
783}
784
[55]785func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]786 switch msg.Command {
[111]787 case "CAP":
788 var subCmd string
789 if err := parseMessageParams(msg, &subCmd); err != nil {
790 return err
791 }
792 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
793 return err
794 }
[107]795 case "PING":
796 dc.SendMessage(&irc.Message{
797 Prefix: dc.srv.prefix(),
798 Command: "PONG",
799 Params: msg.Params,
800 })
801 return nil
[42]802 case "USER":
[13]803 return ircError{&irc.Message{
804 Command: irc.ERR_ALREADYREGISTERED,
[55]805 Params: []string{dc.nick, "You may not reregister"},
[13]806 }}
[42]807 case "NICK":
[90]808 var nick string
809 if err := parseMessageParams(msg, &nick); err != nil {
810 return err
811 }
812
813 var err error
814 dc.forEachNetwork(func(n *network) {
815 if err != nil {
816 return
817 }
818 n.Nick = nick
819 err = dc.srv.db.StoreNetwork(dc.user.Username, &n.Network)
820 })
821 if err != nil {
822 return err
823 }
824
[73]825 dc.forEachUpstream(func(uc *upstreamConn) {
[60]826 uc.SendMessage(msg)
[42]827 })
[146]828 case "JOIN":
829 var namesStr string
830 if err := parseMessageParams(msg, &namesStr); err != nil {
[48]831 return err
832 }
833
[146]834 var keys []string
835 if len(msg.Params) > 1 {
836 keys = strings.Split(msg.Params[1], ",")
837 }
838
839 for i, name := range strings.Split(namesStr, ",") {
[145]840 uc, upstreamName, err := dc.unmarshalEntity(name)
841 if err != nil {
[158]842 return err
[145]843 }
[48]844
[146]845 var key string
846 if len(keys) > i {
847 key = keys[i]
848 }
849
850 params := []string{upstreamName}
851 if key != "" {
852 params = append(params, key)
853 }
[145]854 uc.SendMessage(&irc.Message{
[146]855 Command: "JOIN",
856 Params: params,
[145]857 })
[89]858
[146]859 err = dc.srv.db.StoreChannel(uc.network.ID, &Channel{
860 Name: upstreamName,
861 Key: key,
862 })
863 if err != nil {
864 dc.logger.Printf("failed to create channel %q in DB: %v", upstreamName, err)
[89]865 }
866 }
[146]867 case "PART":
868 var namesStr string
869 if err := parseMessageParams(msg, &namesStr); err != nil {
870 return err
871 }
872
873 var reason string
874 if len(msg.Params) > 1 {
875 reason = msg.Params[1]
876 }
877
878 for _, name := range strings.Split(namesStr, ",") {
879 uc, upstreamName, err := dc.unmarshalEntity(name)
880 if err != nil {
[158]881 return err
[146]882 }
883
884 params := []string{upstreamName}
885 if reason != "" {
886 params = append(params, reason)
887 }
888 uc.SendMessage(&irc.Message{
889 Command: "PART",
890 Params: params,
891 })
892
893 if err := dc.srv.db.DeleteChannel(uc.network.ID, upstreamName); err != nil {
894 dc.logger.Printf("failed to delete channel %q in DB: %v", upstreamName, err)
895 }
896 }
[159]897 case "KICK":
898 var channelStr, userStr string
899 if err := parseMessageParams(msg, &channelStr, &userStr); err != nil {
900 return err
901 }
902
903 channels := strings.Split(channelStr, ",")
904 users := strings.Split(userStr, ",")
905
906 var reason string
907 if len(msg.Params) > 2 {
908 reason = msg.Params[2]
909 }
910
911 if len(channels) != 1 && len(channels) != len(users) {
912 return ircError{&irc.Message{
913 Command: irc.ERR_BADCHANMASK,
914 Params: []string{dc.nick, channelStr, "Bad channel mask"},
915 }}
916 }
917
918 for i, user := range users {
919 var channel string
920 if len(channels) == 1 {
921 channel = channels[0]
922 } else {
923 channel = channels[i]
924 }
925
926 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
927 if err != nil {
928 return err
929 }
930
931 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
932 if err != nil {
933 return err
934 }
935
936 if ucChannel != ucUser {
937 return ircError{&irc.Message{
938 Command: irc.ERR_USERNOTINCHANNEL,
939 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
940 }}
941 }
942 uc := ucChannel
943
944 params := []string{upstreamChannel, upstreamUser}
945 if reason != "" {
946 params = append(params, reason)
947 }
948 uc.SendMessage(&irc.Message{
949 Command: "KICK",
950 Params: params,
951 })
952 }
[69]953 case "MODE":
[46]954 var name string
955 if err := parseMessageParams(msg, &name); err != nil {
956 return err
957 }
958
959 var modeStr string
960 if len(msg.Params) > 1 {
961 modeStr = msg.Params[1]
962 }
963
[139]964 if name == dc.nick {
[46]965 if modeStr != "" {
[73]966 dc.forEachUpstream(func(uc *upstreamConn) {
[69]967 uc.SendMessage(&irc.Message{
968 Command: "MODE",
969 Params: []string{uc.nick, modeStr},
970 })
[46]971 })
972 } else {
[55]973 dc.SendMessage(&irc.Message{
974 Prefix: dc.srv.prefix(),
[46]975 Command: irc.RPL_UMODEIS,
[129]976 Params: []string{dc.nick, ""}, // TODO
[54]977 })
[46]978 }
[139]979 return nil
[46]980 }
[139]981
982 uc, upstreamName, err := dc.unmarshalEntity(name)
983 if err != nil {
984 return err
985 }
986
987 if !uc.isChannel(upstreamName) {
988 return ircError{&irc.Message{
989 Command: irc.ERR_USERSDONTMATCH,
990 Params: []string{dc.nick, "Cannot change mode for other users"},
991 }}
992 }
993
994 if modeStr != "" {
995 params := []string{upstreamName, modeStr}
996 params = append(params, msg.Params[2:]...)
997 uc.SendMessage(&irc.Message{
998 Command: "MODE",
999 Params: params,
1000 })
1001 } else {
1002 ch, ok := uc.channels[upstreamName]
1003 if !ok {
1004 return ircError{&irc.Message{
1005 Command: irc.ERR_NOSUCHCHANNEL,
1006 Params: []string{dc.nick, name, "No such channel"},
1007 }}
1008 }
1009
1010 if ch.modes == nil {
1011 // we haven't received the initial RPL_CHANNELMODEIS yet
1012 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS
1013 return nil
1014 }
1015
1016 modeStr, modeParams := ch.modes.Format()
1017 params := []string{dc.nick, name, modeStr}
1018 params = append(params, modeParams...)
1019
1020 dc.SendMessage(&irc.Message{
1021 Prefix: dc.srv.prefix(),
1022 Command: irc.RPL_CHANNELMODEIS,
1023 Params: params,
1024 })
[162]1025 if ch.creationTime != "" {
1026 dc.SendMessage(&irc.Message{
1027 Prefix: dc.srv.prefix(),
1028 Command: rpl_creationtime,
1029 Params: []string{dc.nick, name, ch.creationTime},
1030 })
1031 }
[139]1032 }
[160]1033 case "TOPIC":
1034 var channel string
1035 if err := parseMessageParams(msg, &channel); err != nil {
1036 return err
1037 }
1038
1039 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1040 if err != nil {
1041 return err
1042 }
1043
1044 if len(msg.Params) > 1 { // setting topic
1045 topic := msg.Params[1]
1046 uc.SendMessage(&irc.Message{
1047 Command: "TOPIC",
1048 Params: []string{upstreamChannel, topic},
1049 })
1050 } else { // getting topic
1051 ch, ok := uc.channels[upstreamChannel]
1052 if !ok {
1053 return ircError{&irc.Message{
1054 Command: irc.ERR_NOSUCHCHANNEL,
1055 Params: []string{dc.nick, upstreamChannel, "No such channel"},
1056 }}
1057 }
1058 sendTopic(dc, ch)
1059 }
[140]1060 case "NAMES":
1061 if len(msg.Params) == 0 {
1062 dc.SendMessage(&irc.Message{
1063 Prefix: dc.srv.prefix(),
1064 Command: irc.RPL_ENDOFNAMES,
1065 Params: []string{dc.nick, "*", "End of /NAMES list"},
1066 })
1067 return nil
1068 }
1069
1070 channels := strings.Split(msg.Params[0], ",")
1071 for _, channel := range channels {
1072 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1073 if err != nil {
1074 return err
1075 }
1076
1077 ch, ok := uc.channels[upstreamChannel]
1078 if ok {
1079 sendNames(dc, ch)
1080 } else {
1081 // NAMES on a channel we have not joined, ask upstream
[156]1082 uc.SendMessageLabeled(dc, &irc.Message{
[140]1083 Command: "NAMES",
1084 Params: []string{upstreamChannel},
1085 })
1086 }
1087 }
[127]1088 case "WHO":
1089 if len(msg.Params) == 0 {
1090 // TODO: support WHO without parameters
1091 dc.SendMessage(&irc.Message{
1092 Prefix: dc.srv.prefix(),
1093 Command: irc.RPL_ENDOFWHO,
[140]1094 Params: []string{dc.nick, "*", "End of /WHO list"},
[127]1095 })
1096 return nil
1097 }
1098
1099 // TODO: support WHO masks
1100 entity := msg.Params[0]
1101
[142]1102 if entity == dc.nick {
1103 // TODO: support AWAY (H/G) in self WHO reply
1104 dc.SendMessage(&irc.Message{
1105 Prefix: dc.srv.prefix(),
1106 Command: irc.RPL_WHOREPLY,
1107 Params: []string{dc.nick, "*", dc.username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
1108 })
1109 dc.SendMessage(&irc.Message{
1110 Prefix: dc.srv.prefix(),
1111 Command: irc.RPL_ENDOFWHO,
1112 Params: []string{dc.nick, dc.nick, "End of /WHO list"},
1113 })
1114 return nil
1115 }
1116
[127]1117 uc, upstreamName, err := dc.unmarshalEntity(entity)
1118 if err != nil {
1119 return err
1120 }
1121
1122 var params []string
1123 if len(msg.Params) == 2 {
1124 params = []string{upstreamName, msg.Params[1]}
1125 } else {
1126 params = []string{upstreamName}
1127 }
1128
[156]1129 uc.SendMessageLabeled(dc, &irc.Message{
[127]1130 Command: "WHO",
1131 Params: params,
1132 })
[128]1133 case "WHOIS":
1134 if len(msg.Params) == 0 {
1135 return ircError{&irc.Message{
1136 Command: irc.ERR_NONICKNAMEGIVEN,
1137 Params: []string{dc.nick, "No nickname given"},
1138 }}
1139 }
1140
1141 var target, mask string
1142 if len(msg.Params) == 1 {
1143 target = ""
1144 mask = msg.Params[0]
1145 } else {
1146 target = msg.Params[0]
1147 mask = msg.Params[1]
1148 }
1149 // TODO: support multiple WHOIS users
1150 if i := strings.IndexByte(mask, ','); i >= 0 {
1151 mask = mask[:i]
1152 }
1153
[142]1154 if mask == dc.nick {
1155 dc.SendMessage(&irc.Message{
1156 Prefix: dc.srv.prefix(),
1157 Command: irc.RPL_WHOISUSER,
1158 Params: []string{dc.nick, dc.nick, dc.username, dc.hostname, "*", dc.realname},
1159 })
1160 dc.SendMessage(&irc.Message{
1161 Prefix: dc.srv.prefix(),
1162 Command: irc.RPL_WHOISSERVER,
1163 Params: []string{dc.nick, dc.nick, dc.srv.Hostname, "soju"},
1164 })
1165 dc.SendMessage(&irc.Message{
1166 Prefix: dc.srv.prefix(),
1167 Command: irc.RPL_ENDOFWHOIS,
1168 Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
1169 })
1170 return nil
1171 }
1172
[128]1173 // TODO: support WHOIS masks
1174 uc, upstreamNick, err := dc.unmarshalEntity(mask)
1175 if err != nil {
1176 return err
1177 }
1178
1179 var params []string
1180 if target != "" {
1181 params = []string{target, upstreamNick}
1182 } else {
1183 params = []string{upstreamNick}
1184 }
1185
[156]1186 uc.SendMessageLabeled(dc, &irc.Message{
[128]1187 Command: "WHOIS",
1188 Params: params,
1189 })
[58]1190 case "PRIVMSG":
1191 var targetsStr, text string
1192 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1193 return err
1194 }
1195
1196 for _, name := range strings.Split(targetsStr, ",") {
[117]1197 if name == serviceNick {
1198 handleServicePRIVMSG(dc, text)
1199 continue
1200 }
1201
[127]1202 uc, upstreamName, err := dc.unmarshalEntity(name)
[58]1203 if err != nil {
1204 return err
1205 }
1206
[95]1207 if upstreamName == "NickServ" {
1208 dc.handleNickServPRIVMSG(uc, text)
1209 }
1210
[69]1211 uc.SendMessage(&irc.Message{
[58]1212 Command: "PRIVMSG",
[69]1213 Params: []string{upstreamName, text},
[60]1214 })
[105]1215
[113]1216 echoMsg := &irc.Message{
1217 Prefix: &irc.Prefix{
1218 Name: uc.nick,
1219 User: uc.username,
1220 },
[114]1221 Command: "PRIVMSG",
[113]1222 Params: []string{upstreamName, text},
1223 }
[105]1224 dc.lock.Lock()
[113]1225 dc.ourMessages[echoMsg] = struct{}{}
[105]1226 dc.lock.Unlock()
1227
[143]1228 uc.network.ring.Produce(echoMsg)
[58]1229 }
[164]1230 case "NOTICE":
1231 var targetsStr, text string
1232 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1233 return err
1234 }
1235
1236 for _, name := range strings.Split(targetsStr, ",") {
1237 uc, upstreamName, err := dc.unmarshalEntity(name)
1238 if err != nil {
1239 return err
1240 }
1241
1242 uc.SendMessage(&irc.Message{
1243 Command: "NOTICE",
1244 Params: []string{upstreamName, text},
1245 })
1246 }
[163]1247 case "INVITE":
1248 var user, channel string
1249 if err := parseMessageParams(msg, &user, &channel); err != nil {
1250 return err
1251 }
1252
1253 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
1254 if err != nil {
1255 return err
1256 }
1257
1258 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
1259 if err != nil {
1260 return err
1261 }
1262
1263 if ucChannel != ucUser {
1264 return ircError{&irc.Message{
1265 Command: irc.ERR_USERNOTINCHANNEL,
1266 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
1267 }}
1268 }
1269 uc := ucChannel
1270
1271 uc.SendMessageLabeled(dc, &irc.Message{
1272 Command: "INVITE",
1273 Params: []string{upstreamUser, upstreamChannel},
1274 })
[13]1275 default:
[55]1276 dc.logger.Printf("unhandled message: %v", msg)
[13]1277 return newUnknownCommandError(msg.Command)
1278 }
[42]1279 return nil
[13]1280}
[95]1281
1282func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1283 username, password, ok := parseNickServCredentials(text, uc.nick)
1284 if !ok {
1285 return
1286 }
1287
1288 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1289 n := uc.network
1290 n.SASL.Mechanism = "PLAIN"
1291 n.SASL.Plain.Username = username
1292 n.SASL.Plain.Password = password
1293 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1294 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1295 }
1296}
1297
1298func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1299 fields := strings.Fields(text)
1300 if len(fields) < 2 {
1301 return "", "", false
1302 }
1303 cmd := strings.ToUpper(fields[0])
1304 params := fields[1:]
1305 switch cmd {
1306 case "REGISTER":
1307 username = nick
1308 password = params[0]
1309 case "IDENTIFY":
1310 if len(params) == 1 {
1311 username = nick
1312 } else {
1313 username = params[0]
1314 }
1315 password = params[1]
1316 }
1317 return username, password, true
1318}
Note: See TracBrowser for help on using the repository browser.