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
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 realname string
75 hostname string
76 password string // empty after authentication
77 network *network // can be nil
78
79 negociatingCaps bool
80 capVersion int
81 caps map[string]bool
82
83 saslServer sasl.Server
84
85 lock sync.Mutex
86 ourMessages map[*irc.Message]struct{}
87}
88
89func newDownstreamConn(srv *Server, netConn net.Conn, id uint64) *downstreamConn {
90 dc := &downstreamConn{
91 id: id,
92 net: netConn,
93 irc: irc.NewConn(netConn),
94 srv: srv,
95 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
96 outgoing: make(chan *irc.Message, 64),
97 ringMessages: make(chan ringMessage),
98 closed: make(chan struct{}),
99 caps: make(map[string]bool),
100 ourMessages: make(map[*irc.Message]struct{}),
101 }
102 dc.hostname = netConn.RemoteAddr().String()
103 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
104 dc.hostname = host
105 }
106
107 go func() {
108 if err := dc.writeMessages(); err != nil {
109 dc.logger.Printf("failed to write message: %v", err)
110 }
111 if err := dc.net.Close(); err != nil {
112 dc.logger.Printf("failed to close connection: %v", err)
113 } else {
114 dc.logger.Printf("connection closed")
115 }
116 }()
117
118 dc.logger.Printf("new connection")
119 return dc
120}
121
122func (dc *downstreamConn) prefix() *irc.Prefix {
123 return &irc.Prefix{
124 Name: dc.nick,
125 User: dc.username,
126 Host: dc.hostname,
127 }
128}
129
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
138func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
139 dc.user.forEachUpstream(func(uc *upstreamConn) {
140 if dc.network != nil && uc.network != dc.network {
141 return
142 }
143 f(uc)
144 })
145}
146
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 }
153 return dc.network.upstream()
154}
155
156func (dc *downstreamConn) marshalEntity(uc *upstreamConn, entity string) string {
157 if uc.isChannel(entity) {
158 return dc.marshalChannel(uc, entity)
159 }
160 return dc.marshalNick(uc, entity)
161}
162
163func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
164 if dc.network != nil {
165 return name
166 }
167 return name + "/" + uc.network.GetName()
168}
169
170func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
171 if uc := dc.upstream(); uc != nil {
172 return uc, name, nil
173 }
174
175 var conn *upstreamConn
176 if i := strings.LastIndexByte(name, '/'); i >= 0 {
177 network := name[i+1:]
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
188 if conn == nil {
189 return nil, "", ircError{&irc.Message{
190 Command: irc.ERR_NOSUCHCHANNEL,
191 Params: []string{name, "No such channel"},
192 }}
193 }
194 return conn, name, nil
195}
196
197func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
198 if nick == uc.nick {
199 return dc.nick
200 }
201 if dc.network != nil {
202 return nick
203 }
204 return nick + "/" + uc.network.GetName()
205}
206
207func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
208 if prefix.Name == uc.nick {
209 return dc.prefix()
210 }
211 if dc.network != nil {
212 return prefix
213 }
214 return &irc.Prefix{
215 Name: prefix.Name + "/" + uc.network.GetName(),
216 User: prefix.User,
217 Host: prefix.Host,
218 }
219}
220
221func (dc *downstreamConn) isClosed() bool {
222 select {
223 case <-dc.closed:
224 return true
225 default:
226 return false
227 }
228}
229
230func (dc *downstreamConn) readMessages(ch chan<- event) error {
231 for {
232 msg, err := dc.irc.ReadMessage()
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
239 if dc.srv.Debug {
240 dc.logger.Printf("received: %v", msg)
241 }
242
243 ch <- eventDownstreamMessage{msg, dc}
244 }
245
246 return nil
247}
248
249func (dc *downstreamConn) writeMessages() error {
250 for {
251 var err error
252 var closed bool
253 select {
254 case msg := <-dc.outgoing:
255 if dc.srv.Debug {
256 dc.logger.Printf("sent: %v", msg)
257 }
258 err = dc.irc.WriteMessage(msg)
259 case ringMessage := <-dc.ringMessages:
260 consumer, uc := ringMessage.consumer, ringMessage.upstreamConn
261 for {
262 msg := consumer.Peek()
263 if msg == nil {
264 break
265 }
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
274 consumer.Consume()
275 continue
276 }
277
278 msg = msg.Copy()
279 switch msg.Command {
280 case "PRIVMSG":
281 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
282 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
283 default:
284 panic("expected to consume a PRIVMSG message")
285 }
286 if dc.srv.Debug {
287 dc.logger.Printf("sent: %v", msg)
288 }
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 {
299 return err
300 }
301 if closed {
302 break
303 }
304 }
305 return nil
306}
307
308func (dc *downstreamConn) Close() error {
309 if dc.isClosed() {
310 return fmt.Errorf("downstream connection already closed")
311 }
312
313 close(dc.closed)
314 return nil
315}
316
317func (dc *downstreamConn) SendMessage(msg *irc.Message) {
318 dc.outgoing <- msg
319}
320
321func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
322 switch msg.Command {
323 case "QUIT":
324 return dc.Close()
325 default:
326 if dc.registered {
327 return dc.handleMessageRegistered(msg)
328 } else {
329 return dc.handleMessageUnregistered(msg)
330 }
331 }
332}
333
334func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
335 switch msg.Command {
336 case "NICK":
337 var nick string
338 if err := parseMessageParams(msg, &nick); err != nil {
339 return err
340 }
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
348 case "USER":
349 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
350 return err
351 }
352 case "PASS":
353 if err := parseMessageParams(msg, &dc.password); err != nil {
354 return err
355 }
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 }
364 case "AUTHENTICATE":
365 if !dc.caps["sasl"] {
366 return ircError{&irc.Message{
367 Command: irc.ERR_SASLFAIL,
368 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
369 }}
370 }
371 if len(msg.Params) == 0 {
372 return ircError{&irc.Message{
373 Command: irc.ERR_SASLFAIL,
374 Params: []string{"*", "Missing AUTHENTICATE argument"},
375 }}
376 }
377 if dc.nick == "" {
378 return ircError{&irc.Message{
379 Command: irc.ERR_SASLFAIL,
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{
394 Command: irc.ERR_SASLFAIL,
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{
401 Command: irc.ERR_SASLABORTED,
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{
413 Command: irc.ERR_SASLFAIL,
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{
424 Command: irc.ERR_SASLFAIL,
425 Params: []string{"*", ircErr.Message.Params[1]},
426 }}
427 }
428 dc.SendMessage(&irc.Message{
429 Prefix: dc.srv.prefix(),
430 Command: irc.ERR_SASLFAIL,
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(),
438 Command: irc.RPL_LOGGEDIN,
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(),
443 Command: irc.RPL_SASLSUCCESS,
444 Params: []string{dc.nick, "SASL authentication successful"},
445 })
446 } else {
447 challengeStr := "+"
448 if len(challenge) > 0 {
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 }
459 default:
460 dc.logger.Printf("unhandled message: %v", msg)
461 return newUnknownCommandError(msg.Command)
462 }
463 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
464 return dc.register()
465 }
466 return nil
467}
468
469func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
470 cmd = strings.ToUpper(cmd)
471
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
487 if dc.capVersion >= 302 {
488 caps = append(caps, "sasl=PLAIN")
489 } else {
490 caps = append(caps, "sasl")
491 }
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 {
538 case "sasl":
539 dc.caps[name] = enable
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
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
574func unmarshalUsername(rawUsername string) (username, network string) {
575 username = rawUsername
576 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
577 network = username[i+1:]
578 }
579 if i := strings.IndexAny(username, "/@"); i >= 0 {
580 username = username[:i]
581 }
582 return username, network
583}
584
585func (dc *downstreamConn) setNetwork(networkName string) error {
586 if networkName == "" {
587 return nil
588 }
589
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
608 network, err = dc.user.createNetwork(&Network{
609 Addr: networkName,
610 Nick: dc.nick,
611 })
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
624 u := dc.srv.getUser(username)
625 if u == nil {
626 dc.logger.Printf("failed authentication for %q: unknown username", username)
627 return errAuthFailed
628 }
629
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
636 dc.user = u
637
638 return dc.setNetwork(networkName)
639}
640
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
647 }
648 } else if dc.network == nil {
649 _, networkName := unmarshalUsername(dc.rawUsername)
650 if err := dc.setNetwork(networkName); err != nil {
651 return err
652 }
653 }
654
655 dc.registered = true
656 dc.username = dc.user.Username
657 dc.logger.Printf("registration complete for user %q", dc.username)
658
659 dc.user.lock.Lock()
660 firstDownstream := len(dc.user.downstreamConns) == 0
661 dc.user.lock.Unlock()
662
663 dc.SendMessage(&irc.Message{
664 Prefix: dc.srv.prefix(),
665 Command: irc.RPL_WELCOME,
666 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
667 })
668 dc.SendMessage(&irc.Message{
669 Prefix: dc.srv.prefix(),
670 Command: irc.RPL_YOURHOST,
671 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
672 })
673 dc.SendMessage(&irc.Message{
674 Prefix: dc.srv.prefix(),
675 Command: irc.RPL_CREATED,
676 Params: []string{dc.nick, "Who cares when the server was created?"},
677 })
678 dc.SendMessage(&irc.Message{
679 Prefix: dc.srv.prefix(),
680 Command: irc.RPL_MYINFO,
681 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
682 })
683 // TODO: RPL_ISUPPORT
684 dc.SendMessage(&irc.Message{
685 Prefix: dc.srv.prefix(),
686 Command: irc.ERR_NOMOTD,
687 Params: []string{dc.nick, "No MOTD"},
688 })
689
690 dc.forEachUpstream(func(uc *upstreamConn) {
691 for _, ch := range uc.channels {
692 if ch.complete {
693 dc.SendMessage(&irc.Message{
694 Prefix: dc.prefix(),
695 Command: "JOIN",
696 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
697 })
698
699 forwardChannel(dc, ch)
700 }
701 }
702 })
703
704 dc.forEachNetwork(func(net *network) {
705 // TODO: need to take dc.network into account when deciding whether or
706 // not to load history
707 dc.runNetwork(net, firstDownstream)
708 })
709
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
731 }
732 }
733
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)
743 break
744 }
745 dc.ringMessages <- ringMessage{consumer, uc}
746 case <-dc.closed:
747 closed = true
748 }
749 if closed {
750 break
751 }
752 }
753
754 seq := consumer.Close()
755
756 net.lock.Lock()
757 net.history[historyName] = seq
758 net.lock.Unlock()
759 }()
760}
761
762func (dc *downstreamConn) runUntilRegistered() error {
763 for !dc.registered {
764 msg, err := dc.irc.ReadMessage()
765 if err != nil {
766 return fmt.Errorf("failed to read IRC command: %v", err)
767 }
768
769 if dc.srv.Debug {
770 dc.logger.Printf("received: %v", msg)
771 }
772
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
785func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
786 switch msg.Command {
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 }
795 case "PING":
796 dc.SendMessage(&irc.Message{
797 Prefix: dc.srv.prefix(),
798 Command: "PONG",
799 Params: msg.Params,
800 })
801 return nil
802 case "USER":
803 return ircError{&irc.Message{
804 Command: irc.ERR_ALREADYREGISTERED,
805 Params: []string{dc.nick, "You may not reregister"},
806 }}
807 case "NICK":
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
825 dc.forEachUpstream(func(uc *upstreamConn) {
826 uc.SendMessage(msg)
827 })
828 case "JOIN":
829 var namesStr string
830 if err := parseMessageParams(msg, &namesStr); err != nil {
831 return err
832 }
833
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, ",") {
840 uc, upstreamName, err := dc.unmarshalEntity(name)
841 if err != nil {
842 return err
843 }
844
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 }
854 uc.SendMessage(&irc.Message{
855 Command: "JOIN",
856 Params: params,
857 })
858
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)
865 }
866 }
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 {
881 return err
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 }
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 }
953 case "MODE":
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
964 if name == dc.nick {
965 if modeStr != "" {
966 dc.forEachUpstream(func(uc *upstreamConn) {
967 uc.SendMessage(&irc.Message{
968 Command: "MODE",
969 Params: []string{uc.nick, modeStr},
970 })
971 })
972 } else {
973 dc.SendMessage(&irc.Message{
974 Prefix: dc.srv.prefix(),
975 Command: irc.RPL_UMODEIS,
976 Params: []string{dc.nick, ""}, // TODO
977 })
978 }
979 return nil
980 }
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 })
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 }
1032 }
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 }
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
1082 uc.SendMessageLabeled(dc, &irc.Message{
1083 Command: "NAMES",
1084 Params: []string{upstreamChannel},
1085 })
1086 }
1087 }
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,
1094 Params: []string{dc.nick, "*", "End of /WHO list"},
1095 })
1096 return nil
1097 }
1098
1099 // TODO: support WHO masks
1100 entity := msg.Params[0]
1101
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
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
1129 uc.SendMessageLabeled(dc, &irc.Message{
1130 Command: "WHO",
1131 Params: params,
1132 })
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
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
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
1186 uc.SendMessageLabeled(dc, &irc.Message{
1187 Command: "WHOIS",
1188 Params: params,
1189 })
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, ",") {
1197 if name == serviceNick {
1198 handleServicePRIVMSG(dc, text)
1199 continue
1200 }
1201
1202 uc, upstreamName, err := dc.unmarshalEntity(name)
1203 if err != nil {
1204 return err
1205 }
1206
1207 if upstreamName == "NickServ" {
1208 dc.handleNickServPRIVMSG(uc, text)
1209 }
1210
1211 uc.SendMessage(&irc.Message{
1212 Command: "PRIVMSG",
1213 Params: []string{upstreamName, text},
1214 })
1215
1216 echoMsg := &irc.Message{
1217 Prefix: &irc.Prefix{
1218 Name: uc.nick,
1219 User: uc.username,
1220 },
1221 Command: "PRIVMSG",
1222 Params: []string{upstreamName, text},
1223 }
1224 dc.lock.Lock()
1225 dc.ourMessages[echoMsg] = struct{}{}
1226 dc.lock.Unlock()
1227
1228 uc.network.ring.Produce(echoMsg)
1229 }
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 }
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 })
1275 default:
1276 dc.logger.Printf("unhandled message: %v", msg)
1277 return newUnknownCommandError(msg.Command)
1278 }
1279 return nil
1280}
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.