source: code/trunk/downstream.go@ 124

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

Fix history name

dc.username contains the raw account username, not the username supplied
by the client.

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