source: code/trunk/downstream.go@ 112

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

Add downstream SASL support

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