source: code/trunk/downstream.go@ 172

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

Nuke user.lock

Split user.register into two functions, one to make sure the user is
authenticated, the other to send our current state. This allows to get
rid of data races by doing the second part in the user goroutine.

Closes: https://todo.sr.ht/~emersion/soju/22

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