source: code/trunk/downstream.go@ 127

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

Add WHO support

File size: 22.7 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 net net.Conn
61 irc *irc.Conn
62 srv *Server
63 logger Logger
64 outgoing chan *irc.Message
65 ringMessages chan ringMessage
66 closed chan struct{}
67
68 registered bool
69 user *user
70 nick string
71 username string
72 rawUsername string
73 realname string
74 password string // empty after authentication
75 network *network // can be nil
76
77 negociatingCaps bool
78 capVersion int
79 caps map[string]bool
80
81 saslServer sasl.Server
82
83 lock sync.Mutex
84 ourMessages map[*irc.Message]struct{}
85}
86
87func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
88 dc := &downstreamConn{
89 net: netConn,
90 irc: irc.NewConn(netConn),
91 srv: srv,
92 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
93 outgoing: make(chan *irc.Message, 64),
94 ringMessages: make(chan ringMessage),
95 closed: make(chan struct{}),
96 caps: make(map[string]bool),
97 ourMessages: make(map[*irc.Message]struct{}),
98 }
99
100 go func() {
101 if err := dc.writeMessages(); err != nil {
102 dc.logger.Printf("failed to write message: %v", err)
103 }
104 if err := dc.net.Close(); err != nil {
105 dc.logger.Printf("failed to close connection: %v", err)
106 } else {
107 dc.logger.Printf("connection closed")
108 }
109 }()
110
111 return dc
112}
113
114func (dc *downstreamConn) prefix() *irc.Prefix {
115 return &irc.Prefix{
116 Name: dc.nick,
117 User: dc.username,
118 // TODO: fill the host?
119 }
120}
121
122func (dc *downstreamConn) forEachNetwork(f func(*network)) {
123 if dc.network != nil {
124 f(dc.network)
125 } else {
126 dc.user.forEachNetwork(f)
127 }
128}
129
130func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
131 dc.user.forEachUpstream(func(uc *upstreamConn) {
132 if dc.network != nil && uc.network != dc.network {
133 return
134 }
135 f(uc)
136 })
137}
138
139// upstream returns the upstream connection, if any. If there are zero or if
140// there are multiple upstream connections, it returns nil.
141func (dc *downstreamConn) upstream() *upstreamConn {
142 if dc.network == nil {
143 return nil
144 }
145
146 var upstream *upstreamConn
147 dc.forEachUpstream(func(uc *upstreamConn) {
148 upstream = uc
149 })
150 return upstream
151}
152
153func (dc *downstreamConn) marshalEntity(uc *upstreamConn, name string) string {
154 for _, r := range name {
155 switch r {
156 // TODO: support upstream ISUPPORT channel prefixes
157 case '#', '&', '+', '!':
158 return dc.marshalChannel(uc, name)
159 }
160 break
161 }
162 return dc.marshalNick(uc, name)
163}
164
165func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
166 if dc.upstream() != 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.upstream() != 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.upstream() != 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<- downstreamIncomingMessage) error {
233 dc.logger.Printf("new connection")
234
235 for {
236 msg, err := dc.irc.ReadMessage()
237 if err == io.EOF {
238 break
239 } else if err != nil {
240 return fmt.Errorf("failed to read IRC command: %v", err)
241 }
242
243 if dc.srv.Debug {
244 dc.logger.Printf("received: %v", msg)
245 }
246
247 ch <- downstreamIncomingMessage{msg, dc}
248 }
249
250 return nil
251}
252
253func (dc *downstreamConn) writeMessages() error {
254 for {
255 var err error
256 var closed bool
257 select {
258 case msg := <-dc.outgoing:
259 if dc.srv.Debug {
260 dc.logger.Printf("sent: %v", msg)
261 }
262 err = dc.irc.WriteMessage(msg)
263 case ringMessage := <-dc.ringMessages:
264 consumer, uc := ringMessage.consumer, ringMessage.upstreamConn
265 for {
266 msg := consumer.Peek()
267 if msg == nil {
268 break
269 }
270
271 dc.lock.Lock()
272 _, ours := dc.ourMessages[msg]
273 delete(dc.ourMessages, msg)
274 dc.lock.Unlock()
275 if ours {
276 // The message comes from our connection, don't echo it
277 // back
278 consumer.Consume()
279 continue
280 }
281
282 msg = msg.Copy()
283 switch msg.Command {
284 case "PRIVMSG":
285 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
286 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
287 default:
288 panic("expected to consume a PRIVMSG message")
289 }
290 if dc.srv.Debug {
291 dc.logger.Printf("sent: %v", msg)
292 }
293 err = dc.irc.WriteMessage(msg)
294 if err != nil {
295 break
296 }
297 consumer.Consume()
298 }
299 case <-dc.closed:
300 closed = true
301 }
302 if err != nil {
303 return err
304 }
305 if closed {
306 break
307 }
308 }
309 return nil
310}
311
312func (dc *downstreamConn) Close() error {
313 if dc.isClosed() {
314 return fmt.Errorf("downstream connection already closed")
315 }
316
317 if u := dc.user; u != nil {
318 u.lock.Lock()
319 for i := range u.downstreamConns {
320 if u.downstreamConns[i] == dc {
321 u.downstreamConns = append(u.downstreamConns[:i], u.downstreamConns[i+1:]...)
322 break
323 }
324 }
325 u.lock.Unlock()
326 }
327
328 close(dc.closed)
329 return nil
330}
331
332func (dc *downstreamConn) SendMessage(msg *irc.Message) {
333 dc.outgoing <- msg
334}
335
336func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
337 switch msg.Command {
338 case "QUIT":
339 return dc.Close()
340 default:
341 if dc.registered {
342 return dc.handleMessageRegistered(msg)
343 } else {
344 return dc.handleMessageUnregistered(msg)
345 }
346 }
347}
348
349func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
350 switch msg.Command {
351 case "NICK":
352 var nick string
353 if err := parseMessageParams(msg, &nick); err != nil {
354 return err
355 }
356 if nick == serviceNick {
357 return ircError{&irc.Message{
358 Command: irc.ERR_NICKNAMEINUSE,
359 Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
360 }}
361 }
362 dc.nick = nick
363 case "USER":
364 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
365 return err
366 }
367 case "PASS":
368 if err := parseMessageParams(msg, &dc.password); err != nil {
369 return err
370 }
371 case "CAP":
372 var subCmd string
373 if err := parseMessageParams(msg, &subCmd); err != nil {
374 return err
375 }
376 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
377 return err
378 }
379 case "AUTHENTICATE":
380 if !dc.caps["sasl"] {
381 return ircError{&irc.Message{
382 Command: irc.ERR_SASLFAIL,
383 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
384 }}
385 }
386 if len(msg.Params) == 0 {
387 return ircError{&irc.Message{
388 Command: irc.ERR_SASLFAIL,
389 Params: []string{"*", "Missing AUTHENTICATE argument"},
390 }}
391 }
392 if dc.nick == "" {
393 return ircError{&irc.Message{
394 Command: irc.ERR_SASLFAIL,
395 Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
396 }}
397 }
398
399 var resp []byte
400 if dc.saslServer == nil {
401 mech := strings.ToUpper(msg.Params[0])
402 switch mech {
403 case "PLAIN":
404 dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
405 return dc.authenticate(username, password)
406 }))
407 default:
408 return ircError{&irc.Message{
409 Command: irc.ERR_SASLFAIL,
410 Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
411 }}
412 }
413 } else if msg.Params[0] == "*" {
414 dc.saslServer = nil
415 return ircError{&irc.Message{
416 Command: irc.ERR_SASLABORTED,
417 Params: []string{"*", "SASL authentication aborted"},
418 }}
419 } else if msg.Params[0] == "+" {
420 resp = nil
421 } else {
422 // TODO: multi-line messages
423 var err error
424 resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
425 if err != nil {
426 dc.saslServer = nil
427 return ircError{&irc.Message{
428 Command: irc.ERR_SASLFAIL,
429 Params: []string{"*", "Invalid base64-encoded response"},
430 }}
431 }
432 }
433
434 challenge, done, err := dc.saslServer.Next(resp)
435 if err != nil {
436 dc.saslServer = nil
437 if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
438 return ircError{&irc.Message{
439 Command: irc.ERR_SASLFAIL,
440 Params: []string{"*", ircErr.Message.Params[1]},
441 }}
442 }
443 dc.SendMessage(&irc.Message{
444 Prefix: dc.srv.prefix(),
445 Command: irc.ERR_SASLFAIL,
446 Params: []string{"*", "SASL error"},
447 })
448 return fmt.Errorf("SASL authentication failed: %v", err)
449 } else if done {
450 dc.saslServer = nil
451 dc.SendMessage(&irc.Message{
452 Prefix: dc.srv.prefix(),
453 Command: irc.RPL_LOGGEDIN,
454 Params: []string{dc.nick, dc.nick, dc.user.Username, "You are now logged in"},
455 })
456 dc.SendMessage(&irc.Message{
457 Prefix: dc.srv.prefix(),
458 Command: irc.RPL_SASLSUCCESS,
459 Params: []string{dc.nick, "SASL authentication successful"},
460 })
461 } else {
462 challengeStr := "+"
463 if challenge != nil {
464 challengeStr = base64.StdEncoding.EncodeToString(challenge)
465 }
466
467 // TODO: multi-line messages
468 dc.SendMessage(&irc.Message{
469 Prefix: dc.srv.prefix(),
470 Command: "AUTHENTICATE",
471 Params: []string{challengeStr},
472 })
473 }
474 default:
475 dc.logger.Printf("unhandled message: %v", msg)
476 return newUnknownCommandError(msg.Command)
477 }
478 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
479 return dc.register()
480 }
481 return nil
482}
483
484func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
485 cmd = strings.ToUpper(cmd)
486
487 replyTo := dc.nick
488 if !dc.registered {
489 replyTo = "*"
490 }
491
492 switch cmd {
493 case "LS":
494 if len(args) > 0 {
495 var err error
496 if dc.capVersion, err = strconv.Atoi(args[0]); err != nil {
497 return err
498 }
499 }
500
501 var caps []string
502 if dc.capVersion >= 302 {
503 caps = append(caps, "sasl=PLAIN")
504 } else {
505 caps = append(caps, "sasl")
506 }
507
508 // TODO: multi-line replies
509 dc.SendMessage(&irc.Message{
510 Prefix: dc.srv.prefix(),
511 Command: "CAP",
512 Params: []string{replyTo, "LS", strings.Join(caps, " ")},
513 })
514
515 if !dc.registered {
516 dc.negociatingCaps = true
517 }
518 case "LIST":
519 var caps []string
520 for name := range dc.caps {
521 caps = append(caps, name)
522 }
523
524 // TODO: multi-line replies
525 dc.SendMessage(&irc.Message{
526 Prefix: dc.srv.prefix(),
527 Command: "CAP",
528 Params: []string{replyTo, "LIST", strings.Join(caps, " ")},
529 })
530 case "REQ":
531 if len(args) == 0 {
532 return ircError{&irc.Message{
533 Command: err_invalidcapcmd,
534 Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"},
535 }}
536 }
537
538 caps := strings.Fields(args[0])
539 ack := true
540 for _, name := range caps {
541 name = strings.ToLower(name)
542 enable := !strings.HasPrefix(name, "-")
543 if !enable {
544 name = strings.TrimPrefix(name, "-")
545 }
546
547 enabled := dc.caps[name]
548 if enable == enabled {
549 continue
550 }
551
552 switch name {
553 case "sasl":
554 dc.caps[name] = enable
555 default:
556 ack = false
557 }
558 }
559
560 reply := "NAK"
561 if ack {
562 reply = "ACK"
563 }
564 dc.SendMessage(&irc.Message{
565 Prefix: dc.srv.prefix(),
566 Command: "CAP",
567 Params: []string{replyTo, reply, args[0]},
568 })
569 case "END":
570 dc.negociatingCaps = false
571 default:
572 return ircError{&irc.Message{
573 Command: err_invalidcapcmd,
574 Params: []string{replyTo, cmd, "Unknown CAP command"},
575 }}
576 }
577 return nil
578}
579
580func sanityCheckServer(addr string) error {
581 dialer := net.Dialer{Timeout: 30 * time.Second}
582 conn, err := tls.DialWithDialer(&dialer, "tcp", addr, nil)
583 if err != nil {
584 return err
585 }
586 return conn.Close()
587}
588
589func unmarshalUsername(rawUsername string) (username, network string) {
590 username = rawUsername
591 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
592 network = username[i+1:]
593 }
594 if i := strings.IndexAny(username, "/@"); i >= 0 {
595 username = username[:i]
596 }
597 return username, network
598}
599
600func (dc *downstreamConn) setNetwork(networkName string) error {
601 if networkName == "" {
602 return nil
603 }
604
605 network := dc.user.getNetwork(networkName)
606 if network == nil {
607 addr := networkName
608 if !strings.ContainsRune(addr, ':') {
609 addr = addr + ":6697"
610 }
611
612 dc.logger.Printf("trying to connect to new network %q", addr)
613 if err := sanityCheckServer(addr); err != nil {
614 dc.logger.Printf("failed to connect to %q: %v", addr, err)
615 return ircError{&irc.Message{
616 Command: irc.ERR_PASSWDMISMATCH,
617 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", networkName)},
618 }}
619 }
620
621 dc.logger.Printf("auto-saving network %q", networkName)
622 var err error
623 network, err = dc.user.createNetwork(&Network{
624 Addr: networkName,
625 Nick: dc.nick,
626 })
627 if err != nil {
628 return err
629 }
630 }
631
632 dc.network = network
633 return nil
634}
635
636func (dc *downstreamConn) authenticate(username, password string) error {
637 username, networkName := unmarshalUsername(username)
638
639 u := dc.srv.getUser(username)
640 if u == nil {
641 dc.logger.Printf("failed authentication for %q: unknown username", username)
642 return errAuthFailed
643 }
644
645 err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
646 if err != nil {
647 dc.logger.Printf("failed authentication for %q: %v", username, err)
648 return errAuthFailed
649 }
650
651 dc.user = u
652
653 return dc.setNetwork(networkName)
654}
655
656func (dc *downstreamConn) register() error {
657 password := dc.password
658 dc.password = ""
659 if dc.user == nil {
660 if err := dc.authenticate(dc.rawUsername, password); err != nil {
661 return err
662 }
663 } else if dc.network == nil {
664 _, networkName := unmarshalUsername(dc.rawUsername)
665 if err := dc.setNetwork(networkName); err != nil {
666 return err
667 }
668 }
669
670 dc.registered = true
671 dc.username = dc.user.Username
672
673 dc.user.lock.Lock()
674 firstDownstream := len(dc.user.downstreamConns) == 0
675 dc.user.downstreamConns = append(dc.user.downstreamConns, dc)
676 dc.user.lock.Unlock()
677
678 dc.SendMessage(&irc.Message{
679 Prefix: dc.srv.prefix(),
680 Command: irc.RPL_WELCOME,
681 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
682 })
683 dc.SendMessage(&irc.Message{
684 Prefix: dc.srv.prefix(),
685 Command: irc.RPL_YOURHOST,
686 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
687 })
688 dc.SendMessage(&irc.Message{
689 Prefix: dc.srv.prefix(),
690 Command: irc.RPL_CREATED,
691 Params: []string{dc.nick, "Who cares when the server was created?"},
692 })
693 dc.SendMessage(&irc.Message{
694 Prefix: dc.srv.prefix(),
695 Command: irc.RPL_MYINFO,
696 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
697 })
698 // TODO: RPL_ISUPPORT
699 dc.SendMessage(&irc.Message{
700 Prefix: dc.srv.prefix(),
701 Command: irc.ERR_NOMOTD,
702 Params: []string{dc.nick, "No MOTD"},
703 })
704
705 dc.forEachUpstream(func(uc *upstreamConn) {
706 for _, ch := range uc.channels {
707 if ch.complete {
708 forwardChannel(dc, ch)
709 }
710 }
711
712 historyName := dc.rawUsername
713
714 var seqPtr *uint64
715 if firstDownstream {
716 uc.lock.Lock()
717 seq, ok := uc.history[historyName]
718 uc.lock.Unlock()
719 if ok {
720 seqPtr = &seq
721 }
722 }
723
724 consumer, ch := uc.ring.NewConsumer(seqPtr)
725 go func() {
726 for {
727 var closed bool
728 select {
729 case <-ch:
730 dc.ringMessages <- ringMessage{consumer, uc}
731 case <-dc.closed:
732 closed = true
733 }
734 if closed {
735 break
736 }
737 }
738
739 seq := consumer.Close()
740
741 dc.user.lock.Lock()
742 lastDownstream := len(dc.user.downstreamConns) == 0
743 dc.user.lock.Unlock()
744
745 if lastDownstream {
746 uc.lock.Lock()
747 uc.history[historyName] = seq
748 uc.lock.Unlock()
749 }
750 }()
751 })
752
753 return nil
754}
755
756func (dc *downstreamConn) runUntilRegistered() error {
757 for !dc.registered {
758 msg, err := dc.irc.ReadMessage()
759 if err != nil {
760 return fmt.Errorf("failed to read IRC command: %v", err)
761 }
762
763 if dc.srv.Debug {
764 dc.logger.Printf("received: %v", msg)
765 }
766
767 err = dc.handleMessage(msg)
768 if ircErr, ok := err.(ircError); ok {
769 ircErr.Message.Prefix = dc.srv.prefix()
770 dc.SendMessage(ircErr.Message)
771 } else if err != nil {
772 return fmt.Errorf("failed to handle IRC command %q: %v", msg, err)
773 }
774 }
775
776 return nil
777}
778
779func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
780 switch msg.Command {
781 case "CAP":
782 var subCmd string
783 if err := parseMessageParams(msg, &subCmd); err != nil {
784 return err
785 }
786 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
787 return err
788 }
789 case "PING":
790 dc.SendMessage(&irc.Message{
791 Prefix: dc.srv.prefix(),
792 Command: "PONG",
793 Params: msg.Params,
794 })
795 return nil
796 case "USER":
797 return ircError{&irc.Message{
798 Command: irc.ERR_ALREADYREGISTERED,
799 Params: []string{dc.nick, "You may not reregister"},
800 }}
801 case "NICK":
802 var nick string
803 if err := parseMessageParams(msg, &nick); err != nil {
804 return err
805 }
806
807 var err error
808 dc.forEachNetwork(func(n *network) {
809 if err != nil {
810 return
811 }
812 n.Nick = nick
813 err = dc.srv.db.StoreNetwork(dc.user.Username, &n.Network)
814 })
815 if err != nil {
816 return err
817 }
818
819 dc.forEachUpstream(func(uc *upstreamConn) {
820 uc.SendMessage(msg)
821 })
822 case "JOIN", "PART":
823 var name string
824 if err := parseMessageParams(msg, &name); err != nil {
825 return err
826 }
827
828 uc, upstreamName, err := dc.unmarshalEntity(name)
829 if err != nil {
830 return ircError{&irc.Message{
831 Command: irc.ERR_NOSUCHCHANNEL,
832 Params: []string{name, err.Error()},
833 }}
834 }
835
836 uc.SendMessage(&irc.Message{
837 Command: msg.Command,
838 Params: []string{upstreamName},
839 })
840
841 switch msg.Command {
842 case "JOIN":
843 err := dc.srv.db.StoreChannel(uc.network.ID, &Channel{
844 Name: upstreamName,
845 })
846 if err != nil {
847 dc.logger.Printf("failed to create channel %q in DB: %v", upstreamName, err)
848 }
849 case "PART":
850 if err := dc.srv.db.DeleteChannel(uc.network.ID, upstreamName); err != nil {
851 dc.logger.Printf("failed to delete channel %q in DB: %v", upstreamName, err)
852 }
853 }
854 case "MODE":
855 if msg.Prefix == nil {
856 return fmt.Errorf("missing prefix")
857 }
858
859 var name string
860 if err := parseMessageParams(msg, &name); err != nil {
861 return err
862 }
863
864 var modeStr string
865 if len(msg.Params) > 1 {
866 modeStr = msg.Params[1]
867 }
868
869 if msg.Prefix.Name != name {
870 uc, upstreamName, err := dc.unmarshalEntity(name)
871 if err != nil {
872 return err
873 }
874
875 if modeStr != "" {
876 uc.SendMessage(&irc.Message{
877 Command: "MODE",
878 Params: []string{upstreamName, modeStr},
879 })
880 } else {
881 ch, ok := uc.channels[upstreamName]
882 if !ok {
883 return ircError{&irc.Message{
884 Command: irc.ERR_NOSUCHCHANNEL,
885 Params: []string{name, "No such channel"},
886 }}
887 }
888
889 dc.SendMessage(&irc.Message{
890 Prefix: dc.srv.prefix(),
891 Command: irc.RPL_CHANNELMODEIS,
892 Params: []string{name, string(ch.modes)},
893 })
894 }
895 } else {
896 if name != dc.nick {
897 return ircError{&irc.Message{
898 Command: irc.ERR_USERSDONTMATCH,
899 Params: []string{dc.nick, "Cannot change mode for other users"},
900 }}
901 }
902
903 if modeStr != "" {
904 dc.forEachUpstream(func(uc *upstreamConn) {
905 uc.SendMessage(&irc.Message{
906 Command: "MODE",
907 Params: []string{uc.nick, modeStr},
908 })
909 })
910 } else {
911 dc.SendMessage(&irc.Message{
912 Prefix: dc.srv.prefix(),
913 Command: irc.RPL_UMODEIS,
914 Params: []string{""}, // TODO
915 })
916 }
917 }
918 case "WHO":
919 if len(msg.Params) == 0 {
920 // TODO: support WHO without parameters
921 dc.SendMessage(&irc.Message{
922 Prefix: dc.srv.prefix(),
923 Command: irc.RPL_ENDOFWHO,
924 Params: []string{dc.nick, "*", "End of /WHO list."},
925 })
926 return nil
927 }
928
929 // TODO: support WHO masks
930 entity := msg.Params[0]
931
932 uc, upstreamName, err := dc.unmarshalEntity(entity)
933 if err != nil {
934 return err
935 }
936
937 var params []string
938 if len(msg.Params) == 2 {
939 params = []string{upstreamName, msg.Params[1]}
940 } else {
941 params = []string{upstreamName}
942 }
943
944 uc.SendMessage(&irc.Message{
945 Command: "WHO",
946 Params: params,
947 })
948 case "PRIVMSG":
949 var targetsStr, text string
950 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
951 return err
952 }
953
954 for _, name := range strings.Split(targetsStr, ",") {
955 if name == serviceNick {
956 handleServicePRIVMSG(dc, text)
957 continue
958 }
959
960 uc, upstreamName, err := dc.unmarshalEntity(name)
961 if err != nil {
962 return err
963 }
964
965 if upstreamName == "NickServ" {
966 dc.handleNickServPRIVMSG(uc, text)
967 }
968
969 uc.SendMessage(&irc.Message{
970 Command: "PRIVMSG",
971 Params: []string{upstreamName, text},
972 })
973
974 echoMsg := &irc.Message{
975 Prefix: &irc.Prefix{
976 Name: uc.nick,
977 User: uc.username,
978 },
979 Command: "PRIVMSG",
980 Params: []string{upstreamName, text},
981 }
982 dc.lock.Lock()
983 dc.ourMessages[echoMsg] = struct{}{}
984 dc.lock.Unlock()
985
986 uc.ring.Produce(echoMsg)
987 }
988 default:
989 dc.logger.Printf("unhandled message: %v", msg)
990 return newUnknownCommandError(msg.Command)
991 }
992 return nil
993}
994
995func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
996 username, password, ok := parseNickServCredentials(text, uc.nick)
997 if !ok {
998 return
999 }
1000
1001 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1002 n := uc.network
1003 n.SASL.Mechanism = "PLAIN"
1004 n.SASL.Plain.Username = username
1005 n.SASL.Plain.Password = password
1006 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1007 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1008 }
1009}
1010
1011func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1012 fields := strings.Fields(text)
1013 if len(fields) < 2 {
1014 return "", "", false
1015 }
1016 cmd := strings.ToUpper(fields[0])
1017 params := fields[1:]
1018 switch cmd {
1019 case "REGISTER":
1020 username = nick
1021 password = params[0]
1022 case "IDENTIFY":
1023 if len(params) == 1 {
1024 username = nick
1025 } else {
1026 username = params[0]
1027 }
1028 password = params[1]
1029 }
1030 return username, password, true
1031}
Note: See TracBrowser for help on using the repository browser.