source: code/trunk/downstream.go@ 128

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

Add WHOIS support

File size: 23.4 KB
RevLine 
[98]1package soju
[13]2
3import (
[91]4 "crypto/tls"
[112]5 "encoding/base64"
[13]6 "fmt"
7 "io"
8 "net"
[108]9 "strconv"
[39]10 "strings"
[105]11 "sync"
[91]12 "time"
[13]13
[112]14 "github.com/emersion/go-sasl"
[85]15 "golang.org/x/crypto/bcrypt"
[13]16 "gopkg.in/irc.v3"
17)
18
19type ircError struct {
20 Message *irc.Message
21}
22
[85]23func (err ircError) Error() string {
24 return err.Message.String()
25}
26
[13]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
[85]49var errAuthFailed = ircError{&irc.Message{
50 Command: irc.ERR_PASSWDMISMATCH,
51 Params: []string{"*", "Invalid username or password"},
52}}
[13]53
[104]54type ringMessage struct {
[69]55 consumer *RingConsumer
56 upstreamConn *upstreamConn
57}
58
[13]59type downstreamConn struct {
[69]60 net net.Conn
61 irc *irc.Conn
62 srv *Server
63 logger Logger
[102]64 outgoing chan *irc.Message
[104]65 ringMessages chan ringMessage
[69]66 closed chan struct{}
[22]67
[100]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
[105]76
[108]77 negociatingCaps bool
78 capVersion int
79 caps map[string]bool
80
[112]81 saslServer sasl.Server
82
[105]83 lock sync.Mutex
84 ourMessages map[*irc.Message]struct{}
[13]85}
86
[22]87func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
[55]88 dc := &downstreamConn{
[69]89 net: netConn,
90 irc: irc.NewConn(netConn),
91 srv: srv,
92 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
[102]93 outgoing: make(chan *irc.Message, 64),
[104]94 ringMessages: make(chan ringMessage),
[69]95 closed: make(chan struct{}),
[108]96 caps: make(map[string]bool),
[105]97 ourMessages: make(map[*irc.Message]struct{}),
[22]98 }
[26]99
100 go func() {
[56]101 if err := dc.writeMessages(); err != nil {
102 dc.logger.Printf("failed to write message: %v", err)
[26]103 }
[55]104 if err := dc.net.Close(); err != nil {
105 dc.logger.Printf("failed to close connection: %v", err)
[45]106 } else {
[55]107 dc.logger.Printf("connection closed")
[45]108 }
[26]109 }()
110
[55]111 return dc
[22]112}
113
[55]114func (dc *downstreamConn) prefix() *irc.Prefix {
[27]115 return &irc.Prefix{
[55]116 Name: dc.nick,
117 User: dc.username,
[27]118 // TODO: fill the host?
119 }
120}
121
[90]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
[73]130func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
131 dc.user.forEachUpstream(func(uc *upstreamConn) {
[77]132 if dc.network != nil && uc.network != dc.network {
[73]133 return
134 }
135 f(uc)
136 })
137}
138
[89]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
[119]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
[127]172func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
[89]173 if uc := dc.upstream(); uc != nil {
174 return uc, name, nil
175 }
176
[127]177 var conn *upstreamConn
[119]178 if i := strings.LastIndexByte(name, '/'); i >= 0 {
[127]179 network := name[i+1:]
[119]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
[127]190 if conn == nil {
[73]191 return nil, "", ircError{&irc.Message{
192 Command: irc.ERR_NOSUCHCHANNEL,
193 Params: []string{name, "No such channel"},
194 }}
[69]195 }
[127]196 return conn, name, nil
[69]197}
198
199func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
200 if nick == uc.nick {
201 return dc.nick
202 }
[119]203 if dc.upstream() != nil {
204 return nick
205 }
206 return nick + "/" + uc.network.GetName()
[69]207}
208
209func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
210 if prefix.Name == uc.nick {
211 return dc.prefix()
212 }
[119]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 }
[69]221}
222
[57]223func (dc *downstreamConn) isClosed() bool {
224 select {
225 case <-dc.closed:
226 return true
227 default:
228 return false
229 }
230}
231
[103]232func (dc *downstreamConn) readMessages(ch chan<- downstreamIncomingMessage) error {
[55]233 dc.logger.Printf("new connection")
[22]234
235 for {
[55]236 msg, err := dc.irc.ReadMessage()
[22]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
[64]243 if dc.srv.Debug {
244 dc.logger.Printf("received: %v", msg)
245 }
246
[103]247 ch <- downstreamIncomingMessage{msg, dc}
[22]248 }
249
[45]250 return nil
[22]251}
252
[56]253func (dc *downstreamConn) writeMessages() error {
[57]254 for {
255 var err error
256 var closed bool
257 select {
[102]258 case msg := <-dc.outgoing:
[64]259 if dc.srv.Debug {
260 dc.logger.Printf("sent: %v", msg)
261 }
[57]262 err = dc.irc.WriteMessage(msg)
[104]263 case ringMessage := <-dc.ringMessages:
264 consumer, uc := ringMessage.consumer, ringMessage.upstreamConn
[57]265 for {
266 msg := consumer.Peek()
267 if msg == nil {
268 break
269 }
[105]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
[113]278 consumer.Consume()
[105]279 continue
280 }
281
[69]282 msg = msg.Copy()
283 switch msg.Command {
284 case "PRIVMSG":
[119]285 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
286 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
[69]287 default:
288 panic("expected to consume a PRIVMSG message")
289 }
[64]290 if dc.srv.Debug {
291 dc.logger.Printf("sent: %v", msg)
292 }
[57]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 {
[56]303 return err
304 }
[57]305 if closed {
306 break
307 }
[56]308 }
309 return nil
310}
311
[55]312func (dc *downstreamConn) Close() error {
[57]313 if dc.isClosed() {
[26]314 return fmt.Errorf("downstream connection already closed")
315 }
[40]316
[55]317 if u := dc.user; u != nil {
[40]318 u.lock.Lock()
319 for i := range u.downstreamConns {
[55]320 if u.downstreamConns[i] == dc {
[40]321 u.downstreamConns = append(u.downstreamConns[:i], u.downstreamConns[i+1:]...)
[63]322 break
[40]323 }
324 }
325 u.lock.Unlock()
[13]326 }
[40]327
[57]328 close(dc.closed)
[45]329 return nil
[13]330}
331
[55]332func (dc *downstreamConn) SendMessage(msg *irc.Message) {
[102]333 dc.outgoing <- msg
[54]334}
335
[55]336func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]337 switch msg.Command {
[28]338 case "QUIT":
[55]339 return dc.Close()
[13]340 default:
[55]341 if dc.registered {
342 return dc.handleMessageRegistered(msg)
[13]343 } else {
[55]344 return dc.handleMessageUnregistered(msg)
[13]345 }
346 }
347}
348
[55]349func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]350 switch msg.Command {
351 case "NICK":
[117]352 var nick string
353 if err := parseMessageParams(msg, &nick); err != nil {
[43]354 return err
[13]355 }
[117]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
[13]363 case "USER":
[117]364 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
[43]365 return err
[13]366 }
[85]367 case "PASS":
368 if err := parseMessageParams(msg, &dc.password); err != nil {
369 return err
370 }
[108]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 }
[112]379 case "AUTHENTICATE":
380 if !dc.caps["sasl"] {
381 return ircError{&irc.Message{
[125]382 Command: irc.ERR_SASLFAIL,
[112]383 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
384 }}
385 }
386 if len(msg.Params) == 0 {
387 return ircError{&irc.Message{
[125]388 Command: irc.ERR_SASLFAIL,
[112]389 Params: []string{"*", "Missing AUTHENTICATE argument"},
390 }}
391 }
392 if dc.nick == "" {
393 return ircError{&irc.Message{
[125]394 Command: irc.ERR_SASLFAIL,
[112]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{
[125]409 Command: irc.ERR_SASLFAIL,
[112]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{
[125]416 Command: irc.ERR_SASLABORTED,
[112]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{
[125]428 Command: irc.ERR_SASLFAIL,
[112]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{
[125]439 Command: irc.ERR_SASLFAIL,
[112]440 Params: []string{"*", ircErr.Message.Params[1]},
441 }}
442 }
443 dc.SendMessage(&irc.Message{
444 Prefix: dc.srv.prefix(),
[125]445 Command: irc.ERR_SASLFAIL,
[112]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(),
[125]453 Command: irc.RPL_LOGGEDIN,
[112]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(),
[125]458 Command: irc.RPL_SASLSUCCESS,
[112]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 }
[13]474 default:
[55]475 dc.logger.Printf("unhandled message: %v", msg)
[13]476 return newUnknownCommandError(msg.Command)
477 }
[108]478 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
[55]479 return dc.register()
[13]480 }
481 return nil
482}
483
[108]484func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
[111]485 cmd = strings.ToUpper(cmd)
486
[108]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
[112]502 if dc.capVersion >= 302 {
[108]503 caps = append(caps, "sasl=PLAIN")
504 } else {
505 caps = append(caps, "sasl")
[112]506 }
[108]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 {
[112]553 case "sasl":
554 dc.caps[name] = enable
[108]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
[91]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
[112]589func unmarshalUsername(rawUsername string) (username, network string) {
590 username = rawUsername
[73]591 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
[112]592 network = username[i+1:]
[73]593 }
594 if i := strings.IndexAny(username, "/@"); i >= 0 {
595 username = username[:i]
596 }
[112]597 return username, network
598}
[73]599
[112]600func (dc *downstreamConn) setNetwork(networkName string) error {
601 if networkName == "" {
602 return nil
603 }
[85]604
[112]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
[120]623 network, err = dc.user.createNetwork(&Network{
624 Addr: networkName,
625 Nick: dc.nick,
626 })
[112]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
[73]639 u := dc.srv.getUser(username)
[38]640 if u == nil {
[85]641 dc.logger.Printf("failed authentication for %q: unknown username", username)
642 return errAuthFailed
[37]643 }
644
[85]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
[112]651 dc.user = u
[91]652
[112]653 return dc.setNetwork(networkName)
654}
[91]655
[112]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
[73]662 }
[112]663 } else if dc.network == nil {
664 _, networkName := unmarshalUsername(dc.rawUsername)
665 if err := dc.setNetwork(networkName); err != nil {
666 return err
667 }
[73]668 }
669
[55]670 dc.registered = true
[112]671 dc.username = dc.user.Username
[13]672
[112]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()
[40]677
[55]678 dc.SendMessage(&irc.Message{
679 Prefix: dc.srv.prefix(),
[13]680 Command: irc.RPL_WELCOME,
[98]681 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
[54]682 })
[55]683 dc.SendMessage(&irc.Message{
684 Prefix: dc.srv.prefix(),
[13]685 Command: irc.RPL_YOURHOST,
[55]686 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]687 })
[55]688 dc.SendMessage(&irc.Message{
689 Prefix: dc.srv.prefix(),
[13]690 Command: irc.RPL_CREATED,
[55]691 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]692 })
[55]693 dc.SendMessage(&irc.Message{
694 Prefix: dc.srv.prefix(),
[13]695 Command: irc.RPL_MYINFO,
[98]696 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
[54]697 })
[93]698 // TODO: RPL_ISUPPORT
[55]699 dc.SendMessage(&irc.Message{
700 Prefix: dc.srv.prefix(),
[13]701 Command: irc.ERR_NOMOTD,
[55]702 Params: []string{dc.nick, "No MOTD"},
[54]703 })
[13]704
[73]705 dc.forEachUpstream(func(uc *upstreamConn) {
[30]706 for _, ch := range uc.channels {
707 if ch.complete {
[55]708 forwardChannel(dc, ch)
[30]709 }
710 }
[50]711
[124]712 historyName := dc.rawUsername
[57]713
714 var seqPtr *uint64
715 if firstDownstream {
[109]716 uc.lock.Lock()
[57]717 seq, ok := uc.history[historyName]
[109]718 uc.lock.Unlock()
[57]719 if ok {
720 seqPtr = &seq
[50]721 }
722 }
[57]723
[59]724 consumer, ch := uc.ring.NewConsumer(seqPtr)
[57]725 go func() {
726 for {
727 var closed bool
728 select {
729 case <-ch:
[104]730 dc.ringMessages <- ringMessage{consumer, uc}
[57]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 {
[109]746 uc.lock.Lock()
[57]747 uc.history[historyName] = seq
[109]748 uc.lock.Unlock()
[57]749 }
750 }()
[39]751 })
[50]752
[13]753 return nil
754}
755
[103]756func (dc *downstreamConn) runUntilRegistered() error {
757 for !dc.registered {
758 msg, err := dc.irc.ReadMessage()
[106]759 if err != nil {
[103]760 return fmt.Errorf("failed to read IRC command: %v", err)
761 }
762
[110]763 if dc.srv.Debug {
764 dc.logger.Printf("received: %v", msg)
765 }
766
[103]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
[55]779func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]780 switch msg.Command {
[111]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 }
[107]789 case "PING":
790 dc.SendMessage(&irc.Message{
791 Prefix: dc.srv.prefix(),
792 Command: "PONG",
793 Params: msg.Params,
794 })
795 return nil
[42]796 case "USER":
[13]797 return ircError{&irc.Message{
798 Command: irc.ERR_ALREADYREGISTERED,
[55]799 Params: []string{dc.nick, "You may not reregister"},
[13]800 }}
[42]801 case "NICK":
[90]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
[73]819 dc.forEachUpstream(func(uc *upstreamConn) {
[60]820 uc.SendMessage(msg)
[42]821 })
[69]822 case "JOIN", "PART":
[48]823 var name string
824 if err := parseMessageParams(msg, &name); err != nil {
825 return err
826 }
827
[127]828 uc, upstreamName, err := dc.unmarshalEntity(name)
[69]829 if err != nil {
830 return ircError{&irc.Message{
831 Command: irc.ERR_NOSUCHCHANNEL,
832 Params: []string{name, err.Error()},
833 }}
[48]834 }
835
[69]836 uc.SendMessage(&irc.Message{
837 Command: msg.Command,
838 Params: []string{upstreamName},
839 })
[89]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 }
[69]854 case "MODE":
855 if msg.Prefix == nil {
856 return fmt.Errorf("missing prefix")
[49]857 }
858
[46]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 {
[127]870 uc, upstreamName, err := dc.unmarshalEntity(name)
[46]871 if err != nil {
872 return err
873 }
874
875 if modeStr != "" {
[69]876 uc.SendMessage(&irc.Message{
877 Command: "MODE",
878 Params: []string{upstreamName, modeStr},
879 })
[46]880 } else {
[69]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
[55]889 dc.SendMessage(&irc.Message{
890 Prefix: dc.srv.prefix(),
[46]891 Command: irc.RPL_CHANNELMODEIS,
[69]892 Params: []string{name, string(ch.modes)},
[54]893 })
[46]894 }
895 } else {
[55]896 if name != dc.nick {
[46]897 return ircError{&irc.Message{
898 Command: irc.ERR_USERSDONTMATCH,
[55]899 Params: []string{dc.nick, "Cannot change mode for other users"},
[46]900 }}
901 }
902
903 if modeStr != "" {
[73]904 dc.forEachUpstream(func(uc *upstreamConn) {
[69]905 uc.SendMessage(&irc.Message{
906 Command: "MODE",
907 Params: []string{uc.nick, modeStr},
908 })
[46]909 })
910 } else {
[55]911 dc.SendMessage(&irc.Message{
912 Prefix: dc.srv.prefix(),
[46]913 Command: irc.RPL_UMODEIS,
914 Params: []string{""}, // TODO
[54]915 })
[46]916 }
917 }
[127]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 })
[128]948 case "WHOIS":
949 if len(msg.Params) == 0 {
950 return ircError{&irc.Message{
951 Command: irc.ERR_NONICKNAMEGIVEN,
952 Params: []string{dc.nick, "No nickname given"},
953 }}
954 }
955
956 var target, mask string
957 if len(msg.Params) == 1 {
958 target = ""
959 mask = msg.Params[0]
960 } else {
961 target = msg.Params[0]
962 mask = msg.Params[1]
963 }
964 // TODO: support multiple WHOIS users
965 if i := strings.IndexByte(mask, ','); i >= 0 {
966 mask = mask[:i]
967 }
968
969 // TODO: support WHOIS masks
970 uc, upstreamNick, err := dc.unmarshalEntity(mask)
971 if err != nil {
972 return err
973 }
974
975 var params []string
976 if target != "" {
977 params = []string{target, upstreamNick}
978 } else {
979 params = []string{upstreamNick}
980 }
981
982 uc.SendMessage(&irc.Message{
983 Command: "WHOIS",
984 Params: params,
985 })
[58]986 case "PRIVMSG":
987 var targetsStr, text string
988 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
989 return err
990 }
991
992 for _, name := range strings.Split(targetsStr, ",") {
[117]993 if name == serviceNick {
994 handleServicePRIVMSG(dc, text)
995 continue
996 }
997
[127]998 uc, upstreamName, err := dc.unmarshalEntity(name)
[58]999 if err != nil {
1000 return err
1001 }
1002
[95]1003 if upstreamName == "NickServ" {
1004 dc.handleNickServPRIVMSG(uc, text)
1005 }
1006
[69]1007 uc.SendMessage(&irc.Message{
[58]1008 Command: "PRIVMSG",
[69]1009 Params: []string{upstreamName, text},
[60]1010 })
[105]1011
[113]1012 echoMsg := &irc.Message{
1013 Prefix: &irc.Prefix{
1014 Name: uc.nick,
1015 User: uc.username,
1016 },
[114]1017 Command: "PRIVMSG",
[113]1018 Params: []string{upstreamName, text},
1019 }
[105]1020 dc.lock.Lock()
[113]1021 dc.ourMessages[echoMsg] = struct{}{}
[105]1022 dc.lock.Unlock()
1023
[113]1024 uc.ring.Produce(echoMsg)
[58]1025 }
[13]1026 default:
[55]1027 dc.logger.Printf("unhandled message: %v", msg)
[13]1028 return newUnknownCommandError(msg.Command)
1029 }
[42]1030 return nil
[13]1031}
[95]1032
1033func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1034 username, password, ok := parseNickServCredentials(text, uc.nick)
1035 if !ok {
1036 return
1037 }
1038
1039 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1040 n := uc.network
1041 n.SASL.Mechanism = "PLAIN"
1042 n.SASL.Plain.Username = username
1043 n.SASL.Plain.Password = password
1044 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1045 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1046 }
1047}
1048
1049func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1050 fields := strings.Fields(text)
1051 if len(fields) < 2 {
1052 return "", "", false
1053 }
1054 cmd := strings.ToUpper(fields[0])
1055 params := fields[1:]
1056 switch cmd {
1057 case "REGISTER":
1058 username = nick
1059 password = params[0]
1060 case "IDENTIFY":
1061 if len(params) == 1 {
1062 username = nick
1063 } else {
1064 username = params[0]
1065 }
1066 password = params[1]
1067 }
1068 return username, password, true
1069}
Note: See TracBrowser for help on using the repository browser.