source: code/trunk/downstream.go@ 183

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

Add downstreamConn.clientName

File size: 31.6 KB
Line 
1package soju
2
3import (
4 "crypto/tls"
5 "encoding/base64"
6 "fmt"
7 "io"
8 "net"
9 "strconv"
10 "strings"
11 "sync"
12 "time"
13
14 "github.com/emersion/go-sasl"
15 "golang.org/x/crypto/bcrypt"
16 "gopkg.in/irc.v3"
17)
18
19type ircError struct {
20 Message *irc.Message
21}
22
23func (err ircError) Error() string {
24 return err.Message.String()
25}
26
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
49var errAuthFailed = ircError{&irc.Message{
50 Command: irc.ERR_PASSWDMISMATCH,
51 Params: []string{"*", "Invalid username or password"},
52}}
53
54type ringMessage struct {
55 consumer *RingConsumer
56 upstreamConn *upstreamConn
57}
58
59type downstreamConn struct {
60 id uint64
61 net net.Conn
62 irc *irc.Conn
63 srv *Server
64 logger Logger
65 outgoing chan *irc.Message
66 ringMessages chan ringMessage
67 closed chan struct{}
68
69 registered bool
70 user *user
71 nick string
72 username string
73 rawUsername string
74 networkName string
75 clientName string
76 realname string
77 hostname string
78 password string // empty after authentication
79 network *network // can be nil
80
81 negociatingCaps bool
82 capVersion int
83 caps map[string]bool
84
85 saslServer sasl.Server
86
87 lock sync.Mutex
88 ourMessages map[*irc.Message]struct{}
89}
90
91func newDownstreamConn(srv *Server, netConn net.Conn, id uint64) *downstreamConn {
92 dc := &downstreamConn{
93 id: id,
94 net: netConn,
95 irc: irc.NewConn(netConn),
96 srv: srv,
97 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
98 outgoing: make(chan *irc.Message, 64),
99 ringMessages: make(chan ringMessage),
100 closed: make(chan struct{}),
101 caps: make(map[string]bool),
102 ourMessages: make(map[*irc.Message]struct{}),
103 }
104 dc.hostname = netConn.RemoteAddr().String()
105 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
106 dc.hostname = host
107 }
108
109 go func() {
110 if err := dc.writeMessages(); err != nil {
111 dc.logger.Printf("failed to write message: %v", err)
112 }
113 if err := dc.net.Close(); err != nil {
114 dc.logger.Printf("failed to close connection: %v", err)
115 } else {
116 dc.logger.Printf("connection closed")
117 }
118 }()
119
120 dc.logger.Printf("new connection")
121 return dc
122}
123
124func (dc *downstreamConn) prefix() *irc.Prefix {
125 return &irc.Prefix{
126 Name: dc.nick,
127 User: dc.username,
128 Host: dc.hostname,
129 }
130}
131
132func (dc *downstreamConn) forEachNetwork(f func(*network)) {
133 if dc.network != nil {
134 f(dc.network)
135 } else {
136 dc.user.forEachNetwork(f)
137 }
138}
139
140func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
141 dc.user.forEachUpstream(func(uc *upstreamConn) {
142 if dc.network != nil && uc.network != dc.network {
143 return
144 }
145 f(uc)
146 })
147}
148
149// upstream returns the upstream connection, if any. If there are zero or if
150// there are multiple upstream connections, it returns nil.
151func (dc *downstreamConn) upstream() *upstreamConn {
152 if dc.network == nil {
153 return nil
154 }
155 return dc.network.upstream()
156}
157
158func (dc *downstreamConn) marshalEntity(uc *upstreamConn, entity string) string {
159 if uc.isChannel(entity) {
160 return dc.marshalChannel(uc, entity)
161 }
162 return dc.marshalNick(uc, entity)
163}
164
165func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
166 if dc.network != nil {
167 return name
168 }
169 return name + "/" + uc.network.GetName()
170}
171
172func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
173 if uc := dc.upstream(); uc != nil {
174 return uc, name, nil
175 }
176
177 var conn *upstreamConn
178 if i := strings.LastIndexByte(name, '/'); i >= 0 {
179 network := name[i+1:]
180 name = name[:i]
181
182 dc.forEachUpstream(func(uc *upstreamConn) {
183 if network != uc.network.GetName() {
184 return
185 }
186 conn = uc
187 })
188 }
189
190 if conn == nil {
191 return nil, "", ircError{&irc.Message{
192 Command: irc.ERR_NOSUCHCHANNEL,
193 Params: []string{name, "No such channel"},
194 }}
195 }
196 return conn, name, nil
197}
198
199func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
200 if nick == uc.nick {
201 return dc.nick
202 }
203 if dc.network != nil {
204 return nick
205 }
206 return nick + "/" + uc.network.GetName()
207}
208
209func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
210 if prefix.Name == uc.nick {
211 return dc.prefix()
212 }
213 if dc.network != nil {
214 return prefix
215 }
216 return &irc.Prefix{
217 Name: prefix.Name + "/" + uc.network.GetName(),
218 User: prefix.User,
219 Host: prefix.Host,
220 }
221}
222
223func (dc *downstreamConn) isClosed() bool {
224 select {
225 case <-dc.closed:
226 return true
227 default:
228 return false
229 }
230}
231
232func (dc *downstreamConn) readMessages(ch chan<- event) error {
233 for {
234 msg, err := dc.irc.ReadMessage()
235 if err == io.EOF {
236 break
237 } else if err != nil {
238 return fmt.Errorf("failed to read IRC command: %v", err)
239 }
240
241 if dc.srv.Debug {
242 dc.logger.Printf("received: %v", msg)
243 }
244
245 ch <- eventDownstreamMessage{msg, dc}
246 }
247
248 return nil
249}
250
251func (dc *downstreamConn) writeMessages() error {
252 for {
253 var err error
254 var closed bool
255 select {
256 case msg := <-dc.outgoing:
257 if dc.srv.Debug {
258 dc.logger.Printf("sent: %v", msg)
259 }
260 err = dc.irc.WriteMessage(msg)
261 case ringMessage := <-dc.ringMessages:
262 consumer, uc := ringMessage.consumer, ringMessage.upstreamConn
263 for {
264 msg := consumer.Peek()
265 if msg == nil {
266 break
267 }
268
269 dc.lock.Lock()
270 _, ours := dc.ourMessages[msg]
271 delete(dc.ourMessages, msg)
272 dc.lock.Unlock()
273 if ours {
274 // The message comes from our connection, don't echo it
275 // back
276 consumer.Consume()
277 continue
278 }
279
280 msg = msg.Copy()
281 switch msg.Command {
282 case "PRIVMSG":
283 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
284 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
285 default:
286 panic("expected to consume a PRIVMSG message")
287 }
288 if dc.srv.Debug {
289 dc.logger.Printf("sent: %v", msg)
290 }
291 err = dc.irc.WriteMessage(msg)
292 if err != nil {
293 break
294 }
295 consumer.Consume()
296 }
297 case <-dc.closed:
298 closed = true
299 }
300 if err != nil {
301 return err
302 }
303 if closed {
304 break
305 }
306 }
307 return nil
308}
309
310// Close closes the connection. It is safe to call from any goroutine.
311func (dc *downstreamConn) Close() error {
312 if dc.isClosed() {
313 return fmt.Errorf("downstream connection already closed")
314 }
315 close(dc.closed)
316 return nil
317}
318
319// SendMessage queues a new outgoing message. It is safe to call from any
320// goroutine.
321func (dc *downstreamConn) SendMessage(msg *irc.Message) {
322 dc.outgoing <- msg
323}
324
325func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
326 switch msg.Command {
327 case "QUIT":
328 return dc.Close()
329 default:
330 if dc.registered {
331 return dc.handleMessageRegistered(msg)
332 } else {
333 return dc.handleMessageUnregistered(msg)
334 }
335 }
336}
337
338func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
339 switch msg.Command {
340 case "NICK":
341 var nick string
342 if err := parseMessageParams(msg, &nick); err != nil {
343 return err
344 }
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
352 case "USER":
353 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
354 return err
355 }
356 case "PASS":
357 if err := parseMessageParams(msg, &dc.password); err != nil {
358 return err
359 }
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 }
368 case "AUTHENTICATE":
369 if !dc.caps["sasl"] {
370 return ircError{&irc.Message{
371 Command: irc.ERR_SASLFAIL,
372 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
373 }}
374 }
375 if len(msg.Params) == 0 {
376 return ircError{&irc.Message{
377 Command: irc.ERR_SASLFAIL,
378 Params: []string{"*", "Missing AUTHENTICATE argument"},
379 }}
380 }
381 if dc.nick == "" {
382 return ircError{&irc.Message{
383 Command: irc.ERR_SASLFAIL,
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{
398 Command: irc.ERR_SASLFAIL,
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{
405 Command: irc.ERR_SASLABORTED,
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{
417 Command: irc.ERR_SASLFAIL,
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{
428 Command: irc.ERR_SASLFAIL,
429 Params: []string{"*", ircErr.Message.Params[1]},
430 }}
431 }
432 dc.SendMessage(&irc.Message{
433 Prefix: dc.srv.prefix(),
434 Command: irc.ERR_SASLFAIL,
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(),
442 Command: irc.RPL_LOGGEDIN,
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(),
447 Command: irc.RPL_SASLSUCCESS,
448 Params: []string{dc.nick, "SASL authentication successful"},
449 })
450 } else {
451 challengeStr := "+"
452 if len(challenge) > 0 {
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 }
463 default:
464 dc.logger.Printf("unhandled message: %v", msg)
465 return newUnknownCommandError(msg.Command)
466 }
467 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
468 return dc.register()
469 }
470 return nil
471}
472
473func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
474 cmd = strings.ToUpper(cmd)
475
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
491 if dc.capVersion >= 302 {
492 caps = append(caps, "sasl=PLAIN")
493 } else {
494 caps = append(caps, "sasl")
495 }
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 {
542 case "sasl":
543 dc.caps[name] = enable
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
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
578func unmarshalUsername(rawUsername string) (username, client, network string) {
579 username = rawUsername
580
581 i := strings.IndexAny(username, "/@")
582 j := strings.LastIndexAny(username, "/@")
583 if i >= 0 {
584 username = rawUsername[:i]
585 }
586 if j >= 0 {
587 network = rawUsername[j+1:]
588 }
589 if i >= 0 && j >= 0 && i < j {
590 client = rawUsername[i+1 : j]
591 }
592
593 return username, client, network
594}
595
596func (dc *downstreamConn) authenticate(username, password string) error {
597 username, clientName, networkName := unmarshalUsername(username)
598
599 u, err := dc.srv.db.GetUser(username)
600 if err != nil {
601 dc.logger.Printf("failed authentication for %q: %v", username, err)
602 return errAuthFailed
603 }
604
605 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
606 if err != nil {
607 dc.logger.Printf("failed authentication for %q: %v", username, err)
608 return errAuthFailed
609 }
610
611 dc.user = dc.srv.getUser(username)
612 if dc.user == nil {
613 dc.logger.Printf("failed authentication for %q: user not active", username)
614 return errAuthFailed
615 }
616 dc.clientName = clientName
617 dc.networkName = networkName
618 return nil
619}
620
621func (dc *downstreamConn) register() error {
622 if dc.registered {
623 return fmt.Errorf("tried to register twice")
624 }
625
626 password := dc.password
627 dc.password = ""
628 if dc.user == nil {
629 if err := dc.authenticate(dc.rawUsername, password); err != nil {
630 return err
631 }
632 }
633
634 if dc.clientName == "" && dc.networkName == "" {
635 _, dc.clientName, dc.networkName = unmarshalUsername(dc.rawUsername)
636 }
637
638 dc.registered = true
639 dc.username = dc.user.Username
640 dc.logger.Printf("registration complete for user %q", dc.username)
641 return nil
642}
643
644func (dc *downstreamConn) loadNetwork() error {
645 if dc.networkName == "" {
646 return nil
647 }
648
649 network := dc.user.getNetwork(dc.networkName)
650 if network == nil {
651 addr := dc.networkName
652 if !strings.ContainsRune(addr, ':') {
653 addr = addr + ":6697"
654 }
655
656 dc.logger.Printf("trying to connect to new network %q", addr)
657 if err := sanityCheckServer(addr); err != nil {
658 dc.logger.Printf("failed to connect to %q: %v", addr, err)
659 return ircError{&irc.Message{
660 Command: irc.ERR_PASSWDMISMATCH,
661 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", dc.networkName)},
662 }}
663 }
664
665 dc.logger.Printf("auto-saving network %q", dc.networkName)
666 var err error
667 network, err = dc.user.createNetwork(&Network{
668 Addr: dc.networkName,
669 Nick: dc.nick,
670 })
671 if err != nil {
672 return err
673 }
674 }
675
676 dc.network = network
677 return nil
678}
679
680func (dc *downstreamConn) welcome() error {
681 if dc.user == nil || !dc.registered {
682 panic("tried to welcome an unregistered connection")
683 }
684
685 // TODO: doing this might take some time. We should do it in dc.register
686 // instead, but we'll potentially be adding a new network and this must be
687 // done in the user goroutine.
688 if err := dc.loadNetwork(); err != nil {
689 return err
690 }
691
692 firstDownstream := len(dc.user.downstreamConns) == 0
693
694 dc.SendMessage(&irc.Message{
695 Prefix: dc.srv.prefix(),
696 Command: irc.RPL_WELCOME,
697 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
698 })
699 dc.SendMessage(&irc.Message{
700 Prefix: dc.srv.prefix(),
701 Command: irc.RPL_YOURHOST,
702 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
703 })
704 dc.SendMessage(&irc.Message{
705 Prefix: dc.srv.prefix(),
706 Command: irc.RPL_CREATED,
707 Params: []string{dc.nick, "Who cares when the server was created?"},
708 })
709 dc.SendMessage(&irc.Message{
710 Prefix: dc.srv.prefix(),
711 Command: irc.RPL_MYINFO,
712 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
713 })
714 // TODO: RPL_ISUPPORT
715 dc.SendMessage(&irc.Message{
716 Prefix: dc.srv.prefix(),
717 Command: irc.ERR_NOMOTD,
718 Params: []string{dc.nick, "No MOTD"},
719 })
720
721 dc.forEachUpstream(func(uc *upstreamConn) {
722 for _, ch := range uc.channels {
723 if ch.complete {
724 dc.SendMessage(&irc.Message{
725 Prefix: dc.prefix(),
726 Command: "JOIN",
727 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
728 })
729
730 forwardChannel(dc, ch)
731 }
732 }
733 })
734
735 dc.forEachNetwork(func(net *network) {
736 // TODO: need to take dc.network into account when deciding whether or
737 // not to load history
738 dc.runNetwork(net, firstDownstream)
739 })
740
741 return nil
742}
743
744// runNetwork starts listening for messages coming from the network's ring
745// buffer.
746//
747// It panics if the network is not suitable for the downstream connection.
748func (dc *downstreamConn) runNetwork(net *network, loadHistory bool) {
749 if dc.network != nil && net != dc.network {
750 panic("network not suitable for downstream connection")
751 }
752
753 historyName := dc.rawUsername
754
755 var seqPtr *uint64
756 if loadHistory {
757 net.lock.Lock()
758 seq, ok := net.history[historyName]
759 net.lock.Unlock()
760 if ok {
761 seqPtr = &seq
762 }
763 }
764
765 consumer, ch := net.ring.NewConsumer(seqPtr)
766 go func() {
767 for {
768 var closed bool
769 select {
770 case <-ch:
771 uc := net.upstream()
772 if uc == nil {
773 dc.logger.Printf("ignoring messages for upstream %q: upstream is disconnected", net.Addr)
774 break
775 }
776 dc.ringMessages <- ringMessage{consumer, uc}
777 case <-dc.closed:
778 closed = true
779 }
780 if closed {
781 break
782 }
783 }
784
785 seq := consumer.Close()
786
787 net.lock.Lock()
788 net.history[historyName] = seq
789 net.lock.Unlock()
790 }()
791}
792
793func (dc *downstreamConn) runUntilRegistered() error {
794 for !dc.registered {
795 msg, err := dc.irc.ReadMessage()
796 if err != nil {
797 return fmt.Errorf("failed to read IRC command: %v", err)
798 }
799
800 if dc.srv.Debug {
801 dc.logger.Printf("received: %v", msg)
802 }
803
804 err = dc.handleMessage(msg)
805 if ircErr, ok := err.(ircError); ok {
806 ircErr.Message.Prefix = dc.srv.prefix()
807 dc.SendMessage(ircErr.Message)
808 } else if err != nil {
809 return fmt.Errorf("failed to handle IRC command %q: %v", msg, err)
810 }
811 }
812
813 return nil
814}
815
816func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
817 switch msg.Command {
818 case "CAP":
819 var subCmd string
820 if err := parseMessageParams(msg, &subCmd); err != nil {
821 return err
822 }
823 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
824 return err
825 }
826 case "PING":
827 dc.SendMessage(&irc.Message{
828 Prefix: dc.srv.prefix(),
829 Command: "PONG",
830 Params: msg.Params,
831 })
832 return nil
833 case "USER":
834 return ircError{&irc.Message{
835 Command: irc.ERR_ALREADYREGISTERED,
836 Params: []string{dc.nick, "You may not reregister"},
837 }}
838 case "NICK":
839 var nick string
840 if err := parseMessageParams(msg, &nick); err != nil {
841 return err
842 }
843
844 var err error
845 dc.forEachNetwork(func(n *network) {
846 if err != nil {
847 return
848 }
849 n.Nick = nick
850 err = dc.srv.db.StoreNetwork(dc.user.Username, &n.Network)
851 })
852 if err != nil {
853 return err
854 }
855
856 dc.forEachUpstream(func(uc *upstreamConn) {
857 uc.SendMessage(msg)
858 })
859 case "JOIN":
860 var namesStr string
861 if err := parseMessageParams(msg, &namesStr); err != nil {
862 return err
863 }
864
865 var keys []string
866 if len(msg.Params) > 1 {
867 keys = strings.Split(msg.Params[1], ",")
868 }
869
870 for i, name := range strings.Split(namesStr, ",") {
871 uc, upstreamName, err := dc.unmarshalEntity(name)
872 if err != nil {
873 return err
874 }
875
876 var key string
877 if len(keys) > i {
878 key = keys[i]
879 }
880
881 params := []string{upstreamName}
882 if key != "" {
883 params = append(params, key)
884 }
885 uc.SendMessage(&irc.Message{
886 Command: "JOIN",
887 Params: params,
888 })
889
890 err = dc.srv.db.StoreChannel(uc.network.ID, &Channel{
891 Name: upstreamName,
892 Key: key,
893 })
894 if err != nil {
895 dc.logger.Printf("failed to create channel %q in DB: %v", upstreamName, err)
896 }
897 }
898 case "PART":
899 var namesStr string
900 if err := parseMessageParams(msg, &namesStr); err != nil {
901 return err
902 }
903
904 var reason string
905 if len(msg.Params) > 1 {
906 reason = msg.Params[1]
907 }
908
909 for _, name := range strings.Split(namesStr, ",") {
910 uc, upstreamName, err := dc.unmarshalEntity(name)
911 if err != nil {
912 return err
913 }
914
915 params := []string{upstreamName}
916 if reason != "" {
917 params = append(params, reason)
918 }
919 uc.SendMessage(&irc.Message{
920 Command: "PART",
921 Params: params,
922 })
923
924 if err := dc.srv.db.DeleteChannel(uc.network.ID, upstreamName); err != nil {
925 dc.logger.Printf("failed to delete channel %q in DB: %v", upstreamName, err)
926 }
927 }
928 case "KICK":
929 var channelStr, userStr string
930 if err := parseMessageParams(msg, &channelStr, &userStr); err != nil {
931 return err
932 }
933
934 channels := strings.Split(channelStr, ",")
935 users := strings.Split(userStr, ",")
936
937 var reason string
938 if len(msg.Params) > 2 {
939 reason = msg.Params[2]
940 }
941
942 if len(channels) != 1 && len(channels) != len(users) {
943 return ircError{&irc.Message{
944 Command: irc.ERR_BADCHANMASK,
945 Params: []string{dc.nick, channelStr, "Bad channel mask"},
946 }}
947 }
948
949 for i, user := range users {
950 var channel string
951 if len(channels) == 1 {
952 channel = channels[0]
953 } else {
954 channel = channels[i]
955 }
956
957 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
958 if err != nil {
959 return err
960 }
961
962 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
963 if err != nil {
964 return err
965 }
966
967 if ucChannel != ucUser {
968 return ircError{&irc.Message{
969 Command: irc.ERR_USERNOTINCHANNEL,
970 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
971 }}
972 }
973 uc := ucChannel
974
975 params := []string{upstreamChannel, upstreamUser}
976 if reason != "" {
977 params = append(params, reason)
978 }
979 uc.SendMessage(&irc.Message{
980 Command: "KICK",
981 Params: params,
982 })
983 }
984 case "MODE":
985 var name string
986 if err := parseMessageParams(msg, &name); err != nil {
987 return err
988 }
989
990 var modeStr string
991 if len(msg.Params) > 1 {
992 modeStr = msg.Params[1]
993 }
994
995 if name == dc.nick {
996 if modeStr != "" {
997 dc.forEachUpstream(func(uc *upstreamConn) {
998 uc.SendMessage(&irc.Message{
999 Command: "MODE",
1000 Params: []string{uc.nick, modeStr},
1001 })
1002 })
1003 } else {
1004 dc.SendMessage(&irc.Message{
1005 Prefix: dc.srv.prefix(),
1006 Command: irc.RPL_UMODEIS,
1007 Params: []string{dc.nick, ""}, // TODO
1008 })
1009 }
1010 return nil
1011 }
1012
1013 uc, upstreamName, err := dc.unmarshalEntity(name)
1014 if err != nil {
1015 return err
1016 }
1017
1018 if !uc.isChannel(upstreamName) {
1019 return ircError{&irc.Message{
1020 Command: irc.ERR_USERSDONTMATCH,
1021 Params: []string{dc.nick, "Cannot change mode for other users"},
1022 }}
1023 }
1024
1025 if modeStr != "" {
1026 params := []string{upstreamName, modeStr}
1027 params = append(params, msg.Params[2:]...)
1028 uc.SendMessage(&irc.Message{
1029 Command: "MODE",
1030 Params: params,
1031 })
1032 } else {
1033 ch, ok := uc.channels[upstreamName]
1034 if !ok {
1035 return ircError{&irc.Message{
1036 Command: irc.ERR_NOSUCHCHANNEL,
1037 Params: []string{dc.nick, name, "No such channel"},
1038 }}
1039 }
1040
1041 if ch.modes == nil {
1042 // we haven't received the initial RPL_CHANNELMODEIS yet
1043 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS
1044 return nil
1045 }
1046
1047 modeStr, modeParams := ch.modes.Format()
1048 params := []string{dc.nick, name, modeStr}
1049 params = append(params, modeParams...)
1050
1051 dc.SendMessage(&irc.Message{
1052 Prefix: dc.srv.prefix(),
1053 Command: irc.RPL_CHANNELMODEIS,
1054 Params: params,
1055 })
1056 if ch.creationTime != "" {
1057 dc.SendMessage(&irc.Message{
1058 Prefix: dc.srv.prefix(),
1059 Command: rpl_creationtime,
1060 Params: []string{dc.nick, name, ch.creationTime},
1061 })
1062 }
1063 }
1064 case "TOPIC":
1065 var channel string
1066 if err := parseMessageParams(msg, &channel); err != nil {
1067 return err
1068 }
1069
1070 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1071 if err != nil {
1072 return err
1073 }
1074
1075 if len(msg.Params) > 1 { // setting topic
1076 topic := msg.Params[1]
1077 uc.SendMessage(&irc.Message{
1078 Command: "TOPIC",
1079 Params: []string{upstreamChannel, topic},
1080 })
1081 } else { // getting topic
1082 ch, ok := uc.channels[upstreamChannel]
1083 if !ok {
1084 return ircError{&irc.Message{
1085 Command: irc.ERR_NOSUCHCHANNEL,
1086 Params: []string{dc.nick, upstreamChannel, "No such channel"},
1087 }}
1088 }
1089 sendTopic(dc, ch)
1090 }
1091 case "LIST":
1092 // TODO: support ELIST when supported by all upstreams
1093
1094 pl := pendingLIST{
1095 downstreamID: dc.id,
1096 pendingCommands: make(map[int64]*irc.Message),
1097 }
1098 var upstreamChannels map[int64][]string
1099 if len(msg.Params) > 0 {
1100 upstreamChannels = make(map[int64][]string)
1101 channels := strings.Split(msg.Params[0], ",")
1102 for _, channel := range channels {
1103 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1104 if err != nil {
1105 return err
1106 }
1107 upstreamChannels[uc.network.ID] = append(upstreamChannels[uc.network.ID], upstreamChannel)
1108 }
1109 }
1110
1111 dc.user.pendingLISTs = append(dc.user.pendingLISTs, pl)
1112 dc.forEachUpstream(func(uc *upstreamConn) {
1113 var params []string
1114 if upstreamChannels != nil {
1115 if channels, ok := upstreamChannels[uc.network.ID]; ok {
1116 params = []string{strings.Join(channels, ",")}
1117 } else {
1118 return
1119 }
1120 }
1121 pl.pendingCommands[uc.network.ID] = &irc.Message{
1122 Command: "LIST",
1123 Params: params,
1124 }
1125 uc.trySendLIST(dc.id)
1126 })
1127 case "NAMES":
1128 if len(msg.Params) == 0 {
1129 dc.SendMessage(&irc.Message{
1130 Prefix: dc.srv.prefix(),
1131 Command: irc.RPL_ENDOFNAMES,
1132 Params: []string{dc.nick, "*", "End of /NAMES list"},
1133 })
1134 return nil
1135 }
1136
1137 channels := strings.Split(msg.Params[0], ",")
1138 for _, channel := range channels {
1139 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1140 if err != nil {
1141 return err
1142 }
1143
1144 ch, ok := uc.channels[upstreamChannel]
1145 if ok {
1146 sendNames(dc, ch)
1147 } else {
1148 // NAMES on a channel we have not joined, ask upstream
1149 uc.SendMessageLabeled(dc.id, &irc.Message{
1150 Command: "NAMES",
1151 Params: []string{upstreamChannel},
1152 })
1153 }
1154 }
1155 case "WHO":
1156 if len(msg.Params) == 0 {
1157 // TODO: support WHO without parameters
1158 dc.SendMessage(&irc.Message{
1159 Prefix: dc.srv.prefix(),
1160 Command: irc.RPL_ENDOFWHO,
1161 Params: []string{dc.nick, "*", "End of /WHO list"},
1162 })
1163 return nil
1164 }
1165
1166 // TODO: support WHO masks
1167 entity := msg.Params[0]
1168
1169 if entity == dc.nick {
1170 // TODO: support AWAY (H/G) in self WHO reply
1171 dc.SendMessage(&irc.Message{
1172 Prefix: dc.srv.prefix(),
1173 Command: irc.RPL_WHOREPLY,
1174 Params: []string{dc.nick, "*", dc.username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
1175 })
1176 dc.SendMessage(&irc.Message{
1177 Prefix: dc.srv.prefix(),
1178 Command: irc.RPL_ENDOFWHO,
1179 Params: []string{dc.nick, dc.nick, "End of /WHO list"},
1180 })
1181 return nil
1182 }
1183
1184 uc, upstreamName, err := dc.unmarshalEntity(entity)
1185 if err != nil {
1186 return err
1187 }
1188
1189 var params []string
1190 if len(msg.Params) == 2 {
1191 params = []string{upstreamName, msg.Params[1]}
1192 } else {
1193 params = []string{upstreamName}
1194 }
1195
1196 uc.SendMessageLabeled(dc.id, &irc.Message{
1197 Command: "WHO",
1198 Params: params,
1199 })
1200 case "WHOIS":
1201 if len(msg.Params) == 0 {
1202 return ircError{&irc.Message{
1203 Command: irc.ERR_NONICKNAMEGIVEN,
1204 Params: []string{dc.nick, "No nickname given"},
1205 }}
1206 }
1207
1208 var target, mask string
1209 if len(msg.Params) == 1 {
1210 target = ""
1211 mask = msg.Params[0]
1212 } else {
1213 target = msg.Params[0]
1214 mask = msg.Params[1]
1215 }
1216 // TODO: support multiple WHOIS users
1217 if i := strings.IndexByte(mask, ','); i >= 0 {
1218 mask = mask[:i]
1219 }
1220
1221 if mask == dc.nick {
1222 dc.SendMessage(&irc.Message{
1223 Prefix: dc.srv.prefix(),
1224 Command: irc.RPL_WHOISUSER,
1225 Params: []string{dc.nick, dc.nick, dc.username, dc.hostname, "*", dc.realname},
1226 })
1227 dc.SendMessage(&irc.Message{
1228 Prefix: dc.srv.prefix(),
1229 Command: irc.RPL_WHOISSERVER,
1230 Params: []string{dc.nick, dc.nick, dc.srv.Hostname, "soju"},
1231 })
1232 dc.SendMessage(&irc.Message{
1233 Prefix: dc.srv.prefix(),
1234 Command: irc.RPL_ENDOFWHOIS,
1235 Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
1236 })
1237 return nil
1238 }
1239
1240 // TODO: support WHOIS masks
1241 uc, upstreamNick, err := dc.unmarshalEntity(mask)
1242 if err != nil {
1243 return err
1244 }
1245
1246 var params []string
1247 if target != "" {
1248 params = []string{target, upstreamNick}
1249 } else {
1250 params = []string{upstreamNick}
1251 }
1252
1253 uc.SendMessageLabeled(dc.id, &irc.Message{
1254 Command: "WHOIS",
1255 Params: params,
1256 })
1257 case "PRIVMSG":
1258 var targetsStr, text string
1259 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1260 return err
1261 }
1262
1263 for _, name := range strings.Split(targetsStr, ",") {
1264 if name == serviceNick {
1265 handleServicePRIVMSG(dc, text)
1266 continue
1267 }
1268
1269 uc, upstreamName, err := dc.unmarshalEntity(name)
1270 if err != nil {
1271 return err
1272 }
1273
1274 if upstreamName == "NickServ" {
1275 dc.handleNickServPRIVMSG(uc, text)
1276 }
1277
1278 uc.SendMessage(&irc.Message{
1279 Command: "PRIVMSG",
1280 Params: []string{upstreamName, text},
1281 })
1282
1283 echoMsg := &irc.Message{
1284 Prefix: &irc.Prefix{
1285 Name: uc.nick,
1286 User: uc.username,
1287 },
1288 Command: "PRIVMSG",
1289 Params: []string{upstreamName, text},
1290 }
1291 dc.lock.Lock()
1292 dc.ourMessages[echoMsg] = struct{}{}
1293 dc.lock.Unlock()
1294
1295 uc.network.ring.Produce(echoMsg)
1296 }
1297 case "NOTICE":
1298 var targetsStr, text string
1299 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1300 return err
1301 }
1302
1303 for _, name := range strings.Split(targetsStr, ",") {
1304 uc, upstreamName, err := dc.unmarshalEntity(name)
1305 if err != nil {
1306 return err
1307 }
1308
1309 uc.SendMessage(&irc.Message{
1310 Command: "NOTICE",
1311 Params: []string{upstreamName, text},
1312 })
1313 }
1314 case "INVITE":
1315 var user, channel string
1316 if err := parseMessageParams(msg, &user, &channel); err != nil {
1317 return err
1318 }
1319
1320 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
1321 if err != nil {
1322 return err
1323 }
1324
1325 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
1326 if err != nil {
1327 return err
1328 }
1329
1330 if ucChannel != ucUser {
1331 return ircError{&irc.Message{
1332 Command: irc.ERR_USERNOTINCHANNEL,
1333 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
1334 }}
1335 }
1336 uc := ucChannel
1337
1338 uc.SendMessageLabeled(dc.id, &irc.Message{
1339 Command: "INVITE",
1340 Params: []string{upstreamUser, upstreamChannel},
1341 })
1342 default:
1343 dc.logger.Printf("unhandled message: %v", msg)
1344 return newUnknownCommandError(msg.Command)
1345 }
1346 return nil
1347}
1348
1349func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1350 username, password, ok := parseNickServCredentials(text, uc.nick)
1351 if !ok {
1352 return
1353 }
1354
1355 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1356 n := uc.network
1357 n.SASL.Mechanism = "PLAIN"
1358 n.SASL.Plain.Username = username
1359 n.SASL.Plain.Password = password
1360 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1361 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1362 }
1363}
1364
1365func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1366 fields := strings.Fields(text)
1367 if len(fields) < 2 {
1368 return "", "", false
1369 }
1370 cmd := strings.ToUpper(fields[0])
1371 params := fields[1:]
1372 switch cmd {
1373 case "REGISTER":
1374 username = nick
1375 password = params[0]
1376 case "IDENTIFY":
1377 if len(params) == 1 {
1378 username = nick
1379 password = params[0]
1380 } else {
1381 username = params[0]
1382 password = params[1]
1383 }
1384 case "SET":
1385 if len(params) == 2 && strings.EqualFold(params[0], "PASSWORD") {
1386 username = nick
1387 password = params[1]
1388 }
1389 }
1390 return username, password, true
1391}
Note: See TracBrowser for help on using the repository browser.