source: code/trunk/downstream.go@ 113

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

Fix echo PRIVMSG messages

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