source: code/trunk/downstream.go@ 139

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

Add MODE arguments support

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