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
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
[130]111 dc.logger.Printf("new connection")
[55]112 return dc
[22]113}
114
[55]115func (dc *downstreamConn) prefix() *irc.Prefix {
[27]116 return &irc.Prefix{
[55]117 Name: dc.nick,
118 User: dc.username,
[27]119 // TODO: fill the host?
120 }
121}
122
[90]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
[73]131func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
132 dc.user.forEachUpstream(func(uc *upstreamConn) {
[77]133 if dc.network != nil && uc.network != dc.network {
[73]134 return
135 }
136 f(uc)
137 })
138}
139
[89]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 }
[136]146 return dc.network.upstream()
[89]147}
148
[129]149func (dc *downstreamConn) marshalEntity(uc *upstreamConn, entity string) string {
150 if uc.isChannel(entity) {
151 return dc.marshalChannel(uc, entity)
[119]152 }
[129]153 return dc.marshalNick(uc, entity)
[119]154}
155
156func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
[130]157 if dc.network != nil {
[119]158 return name
159 }
160 return name + "/" + uc.network.GetName()
161}
162
[127]163func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
[89]164 if uc := dc.upstream(); uc != nil {
165 return uc, name, nil
166 }
167
[127]168 var conn *upstreamConn
[119]169 if i := strings.LastIndexByte(name, '/'); i >= 0 {
[127]170 network := name[i+1:]
[119]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
[127]181 if conn == nil {
[73]182 return nil, "", ircError{&irc.Message{
183 Command: irc.ERR_NOSUCHCHANNEL,
184 Params: []string{name, "No such channel"},
185 }}
[69]186 }
[127]187 return conn, name, nil
[69]188}
189
190func (dc *downstreamConn) marshalNick(uc *upstreamConn, nick string) string {
191 if nick == uc.nick {
192 return dc.nick
193 }
[130]194 if dc.network != nil {
[119]195 return nick
196 }
197 return nick + "/" + uc.network.GetName()
[69]198}
199
200func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
201 if prefix.Name == uc.nick {
202 return dc.prefix()
203 }
[130]204 if dc.network != nil {
[119]205 return prefix
206 }
207 return &irc.Prefix{
208 Name: prefix.Name + "/" + uc.network.GetName(),
209 User: prefix.User,
210 Host: prefix.Host,
211 }
[69]212}
213
[57]214func (dc *downstreamConn) isClosed() bool {
215 select {
216 case <-dc.closed:
217 return true
218 default:
219 return false
220 }
221}
222
[103]223func (dc *downstreamConn) readMessages(ch chan<- downstreamIncomingMessage) error {
[22]224 for {
[55]225 msg, err := dc.irc.ReadMessage()
[22]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
[64]232 if dc.srv.Debug {
233 dc.logger.Printf("received: %v", msg)
234 }
235
[103]236 ch <- downstreamIncomingMessage{msg, dc}
[22]237 }
238
[45]239 return nil
[22]240}
241
[56]242func (dc *downstreamConn) writeMessages() error {
[57]243 for {
244 var err error
245 var closed bool
246 select {
[102]247 case msg := <-dc.outgoing:
[64]248 if dc.srv.Debug {
249 dc.logger.Printf("sent: %v", msg)
250 }
[57]251 err = dc.irc.WriteMessage(msg)
[104]252 case ringMessage := <-dc.ringMessages:
253 consumer, uc := ringMessage.consumer, ringMessage.upstreamConn
[57]254 for {
255 msg := consumer.Peek()
256 if msg == nil {
257 break
258 }
[105]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
[113]267 consumer.Consume()
[105]268 continue
269 }
270
[69]271 msg = msg.Copy()
272 switch msg.Command {
273 case "PRIVMSG":
[119]274 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
275 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
[69]276 default:
277 panic("expected to consume a PRIVMSG message")
278 }
[64]279 if dc.srv.Debug {
280 dc.logger.Printf("sent: %v", msg)
281 }
[57]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 {
[56]292 return err
293 }
[57]294 if closed {
295 break
296 }
[56]297 }
298 return nil
299}
300
[55]301func (dc *downstreamConn) Close() error {
[57]302 if dc.isClosed() {
[26]303 return fmt.Errorf("downstream connection already closed")
304 }
[40]305
[55]306 if u := dc.user; u != nil {
[137]307 u.removeDownstream(dc)
[13]308 }
[40]309
[57]310 close(dc.closed)
[45]311 return nil
[13]312}
313
[55]314func (dc *downstreamConn) SendMessage(msg *irc.Message) {
[102]315 dc.outgoing <- msg
[54]316}
317
[55]318func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]319 switch msg.Command {
[28]320 case "QUIT":
[55]321 return dc.Close()
[13]322 default:
[55]323 if dc.registered {
324 return dc.handleMessageRegistered(msg)
[13]325 } else {
[55]326 return dc.handleMessageUnregistered(msg)
[13]327 }
328 }
329}
330
[55]331func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]332 switch msg.Command {
333 case "NICK":
[117]334 var nick string
335 if err := parseMessageParams(msg, &nick); err != nil {
[43]336 return err
[13]337 }
[117]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
[13]345 case "USER":
[117]346 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
[43]347 return err
[13]348 }
[85]349 case "PASS":
350 if err := parseMessageParams(msg, &dc.password); err != nil {
351 return err
352 }
[108]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 }
[112]361 case "AUTHENTICATE":
362 if !dc.caps["sasl"] {
363 return ircError{&irc.Message{
[125]364 Command: irc.ERR_SASLFAIL,
[112]365 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
366 }}
367 }
368 if len(msg.Params) == 0 {
369 return ircError{&irc.Message{
[125]370 Command: irc.ERR_SASLFAIL,
[112]371 Params: []string{"*", "Missing AUTHENTICATE argument"},
372 }}
373 }
374 if dc.nick == "" {
375 return ircError{&irc.Message{
[125]376 Command: irc.ERR_SASLFAIL,
[112]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{
[125]391 Command: irc.ERR_SASLFAIL,
[112]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{
[125]398 Command: irc.ERR_SASLABORTED,
[112]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{
[125]410 Command: irc.ERR_SASLFAIL,
[112]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{
[125]421 Command: irc.ERR_SASLFAIL,
[112]422 Params: []string{"*", ircErr.Message.Params[1]},
423 }}
424 }
425 dc.SendMessage(&irc.Message{
426 Prefix: dc.srv.prefix(),
[125]427 Command: irc.ERR_SASLFAIL,
[112]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(),
[125]435 Command: irc.RPL_LOGGEDIN,
[112]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(),
[125]440 Command: irc.RPL_SASLSUCCESS,
[112]441 Params: []string{dc.nick, "SASL authentication successful"},
442 })
443 } else {
444 challengeStr := "+"
[135]445 if len(challenge) > 0 {
[112]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 }
[13]456 default:
[55]457 dc.logger.Printf("unhandled message: %v", msg)
[13]458 return newUnknownCommandError(msg.Command)
459 }
[108]460 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
[55]461 return dc.register()
[13]462 }
463 return nil
464}
465
[108]466func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
[111]467 cmd = strings.ToUpper(cmd)
468
[108]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
[112]484 if dc.capVersion >= 302 {
[108]485 caps = append(caps, "sasl=PLAIN")
486 } else {
487 caps = append(caps, "sasl")
[112]488 }
[108]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 {
[112]535 case "sasl":
536 dc.caps[name] = enable
[108]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
[91]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
[112]571func unmarshalUsername(rawUsername string) (username, network string) {
572 username = rawUsername
[73]573 if i := strings.LastIndexAny(username, "/@"); i >= 0 {
[112]574 network = username[i+1:]
[73]575 }
576 if i := strings.IndexAny(username, "/@"); i >= 0 {
577 username = username[:i]
578 }
[112]579 return username, network
580}
[73]581
[112]582func (dc *downstreamConn) setNetwork(networkName string) error {
583 if networkName == "" {
584 return nil
585 }
[85]586
[112]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
[120]605 network, err = dc.user.createNetwork(&Network{
606 Addr: networkName,
607 Nick: dc.nick,
608 })
[112]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
[73]621 u := dc.srv.getUser(username)
[38]622 if u == nil {
[85]623 dc.logger.Printf("failed authentication for %q: unknown username", username)
624 return errAuthFailed
[37]625 }
626
[85]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
[112]633 dc.user = u
[91]634
[112]635 return dc.setNetwork(networkName)
636}
[91]637
[112]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
[73]644 }
[112]645 } else if dc.network == nil {
646 _, networkName := unmarshalUsername(dc.rawUsername)
647 if err := dc.setNetwork(networkName); err != nil {
648 return err
649 }
[73]650 }
651
[55]652 dc.registered = true
[112]653 dc.username = dc.user.Username
[130]654 dc.logger.Printf("registration complete for user %q", dc.username)
[13]655
[137]656 firstDownstream := dc.user.addDownstream(dc)
[40]657
[55]658 dc.SendMessage(&irc.Message{
659 Prefix: dc.srv.prefix(),
[13]660 Command: irc.RPL_WELCOME,
[98]661 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
[54]662 })
[55]663 dc.SendMessage(&irc.Message{
664 Prefix: dc.srv.prefix(),
[13]665 Command: irc.RPL_YOURHOST,
[55]666 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]667 })
[55]668 dc.SendMessage(&irc.Message{
669 Prefix: dc.srv.prefix(),
[13]670 Command: irc.RPL_CREATED,
[55]671 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]672 })
[55]673 dc.SendMessage(&irc.Message{
674 Prefix: dc.srv.prefix(),
[13]675 Command: irc.RPL_MYINFO,
[98]676 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
[54]677 })
[93]678 // TODO: RPL_ISUPPORT
[55]679 dc.SendMessage(&irc.Message{
680 Prefix: dc.srv.prefix(),
[13]681 Command: irc.ERR_NOMOTD,
[55]682 Params: []string{dc.nick, "No MOTD"},
[54]683 })
[13]684
[73]685 dc.forEachUpstream(func(uc *upstreamConn) {
[30]686 for _, ch := range uc.channels {
687 if ch.complete {
[132]688 dc.SendMessage(&irc.Message{
689 Prefix: dc.prefix(),
690 Command: "JOIN",
691 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
692 })
693
[55]694 forwardChannel(dc, ch)
[30]695 }
696 }
[50]697
[124]698 historyName := dc.rawUsername
[57]699
[137]700 // TODO: need to take dc.network into account here
[57]701 var seqPtr *uint64
702 if firstDownstream {
[131]703 uc.network.lock.Lock()
704 seq, ok := uc.network.history[historyName]
705 uc.network.lock.Unlock()
[57]706 if ok {
707 seqPtr = &seq
[50]708 }
709 }
[57]710
[137]711 // TODO: we need to create a consumer when adding networks on-the-fly
[59]712 consumer, ch := uc.ring.NewConsumer(seqPtr)
[57]713 go func() {
714 for {
715 var closed bool
716 select {
717 case <-ch:
[104]718 dc.ringMessages <- ringMessage{consumer, uc}
[57]719 case <-dc.closed:
720 closed = true
721 }
722 if closed {
723 break
724 }
725 }
726
727 seq := consumer.Close()
728
[137]729 // TODO: need to take dc.network into account here
[57]730 dc.user.lock.Lock()
731 lastDownstream := len(dc.user.downstreamConns) == 0
732 dc.user.lock.Unlock()
733
734 if lastDownstream {
[131]735 uc.network.lock.Lock()
736 uc.network.history[historyName] = seq
737 uc.network.lock.Unlock()
[57]738 }
739 }()
[39]740 })
[50]741
[13]742 return nil
743}
744
[103]745func (dc *downstreamConn) runUntilRegistered() error {
746 for !dc.registered {
747 msg, err := dc.irc.ReadMessage()
[106]748 if err != nil {
[103]749 return fmt.Errorf("failed to read IRC command: %v", err)
750 }
751
[110]752 if dc.srv.Debug {
753 dc.logger.Printf("received: %v", msg)
754 }
755
[103]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
[55]768func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]769 switch msg.Command {
[111]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 }
[107]778 case "PING":
779 dc.SendMessage(&irc.Message{
780 Prefix: dc.srv.prefix(),
781 Command: "PONG",
782 Params: msg.Params,
783 })
784 return nil
[42]785 case "USER":
[13]786 return ircError{&irc.Message{
787 Command: irc.ERR_ALREADYREGISTERED,
[55]788 Params: []string{dc.nick, "You may not reregister"},
[13]789 }}
[42]790 case "NICK":
[90]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
[73]808 dc.forEachUpstream(func(uc *upstreamConn) {
[60]809 uc.SendMessage(msg)
[42]810 })
[69]811 case "JOIN", "PART":
[48]812 var name string
813 if err := parseMessageParams(msg, &name); err != nil {
814 return err
815 }
816
[127]817 uc, upstreamName, err := dc.unmarshalEntity(name)
[69]818 if err != nil {
819 return ircError{&irc.Message{
820 Command: irc.ERR_NOSUCHCHANNEL,
821 Params: []string{name, err.Error()},
822 }}
[48]823 }
824
[69]825 uc.SendMessage(&irc.Message{
826 Command: msg.Command,
827 Params: []string{upstreamName},
828 })
[89]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 }
[69]843 case "MODE":
[46]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
[139]854 if name == dc.nick {
[46]855 if modeStr != "" {
[73]856 dc.forEachUpstream(func(uc *upstreamConn) {
[69]857 uc.SendMessage(&irc.Message{
858 Command: "MODE",
859 Params: []string{uc.nick, modeStr},
860 })
[46]861 })
862 } else {
[55]863 dc.SendMessage(&irc.Message{
864 Prefix: dc.srv.prefix(),
[46]865 Command: irc.RPL_UMODEIS,
[129]866 Params: []string{dc.nick, ""}, // TODO
[54]867 })
[46]868 }
[139]869 return nil
[46]870 }
[139]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 }
[127]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 })
[128]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 })
[58]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, ",") {
[117]991 if name == serviceNick {
992 handleServicePRIVMSG(dc, text)
993 continue
994 }
995
[127]996 uc, upstreamName, err := dc.unmarshalEntity(name)
[58]997 if err != nil {
998 return err
999 }
1000
[95]1001 if upstreamName == "NickServ" {
1002 dc.handleNickServPRIVMSG(uc, text)
1003 }
1004
[69]1005 uc.SendMessage(&irc.Message{
[58]1006 Command: "PRIVMSG",
[69]1007 Params: []string{upstreamName, text},
[60]1008 })
[105]1009
[113]1010 echoMsg := &irc.Message{
1011 Prefix: &irc.Prefix{
1012 Name: uc.nick,
1013 User: uc.username,
1014 },
[114]1015 Command: "PRIVMSG",
[113]1016 Params: []string{upstreamName, text},
1017 }
[105]1018 dc.lock.Lock()
[113]1019 dc.ourMessages[echoMsg] = struct{}{}
[105]1020 dc.lock.Unlock()
1021
[113]1022 uc.ring.Produce(echoMsg)
[58]1023 }
[13]1024 default:
[55]1025 dc.logger.Printf("unhandled message: %v", msg)
[13]1026 return newUnknownCommandError(msg.Command)
1027 }
[42]1028 return nil
[13]1029}
[95]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.