source: code/trunk/downstream.go@ 257

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

Unify downstreamConn.marshal{Entity,Nick,Channel}

We don't actually need to check if the entity is a channel.

File size: 31.5 KB
Line 
1package soju
2
3import (
4 "crypto/tls"
5 "encoding/base64"
6 "fmt"
7 "io"
8 "net"
9 "strconv"
10 "strings"
11 "time"
12
13 "github.com/emersion/go-sasl"
14 "golang.org/x/crypto/bcrypt"
15 "gopkg.in/irc.v3"
16)
17
18type ircError struct {
19 Message *irc.Message
20}
21
22func (err ircError) Error() string {
23 return err.Message.String()
24}
25
26func newUnknownCommandError(cmd string) ircError {
27 return ircError{&irc.Message{
28 Command: irc.ERR_UNKNOWNCOMMAND,
29 Params: []string{
30 "*",
31 cmd,
32 "Unknown command",
33 },
34 }}
35}
36
37func newNeedMoreParamsError(cmd string) ircError {
38 return ircError{&irc.Message{
39 Command: irc.ERR_NEEDMOREPARAMS,
40 Params: []string{
41 "*",
42 cmd,
43 "Not enough parameters",
44 },
45 }}
46}
47
48var errAuthFailed = ircError{&irc.Message{
49 Command: irc.ERR_PASSWDMISMATCH,
50 Params: []string{"*", "Invalid username or password"},
51}}
52
53type downstreamConn struct {
54 conn
55
56 id uint64
57
58 registered bool
59 user *user
60 nick string
61 rawUsername string
62 networkName string
63 clientName string
64 realname string
65 hostname string
66 password string // empty after authentication
67 network *network // can be nil
68
69 negociatingCaps bool
70 capVersion int
71 caps map[string]bool
72
73 saslServer sasl.Server
74}
75
76func newDownstreamConn(srv *Server, netConn net.Conn, id uint64) *downstreamConn {
77 logger := &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())}
78 dc := &downstreamConn{
79 conn: *newConn(srv, netConn, logger),
80 id: id,
81 caps: make(map[string]bool),
82 }
83 dc.hostname = netConn.RemoteAddr().String()
84 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
85 dc.hostname = host
86 }
87 return dc
88}
89
90func (dc *downstreamConn) prefix() *irc.Prefix {
91 return &irc.Prefix{
92 Name: dc.nick,
93 User: dc.user.Username,
94 Host: dc.hostname,
95 }
96}
97
98func (dc *downstreamConn) forEachNetwork(f func(*network)) {
99 if dc.network != nil {
100 f(dc.network)
101 } else {
102 dc.user.forEachNetwork(f)
103 }
104}
105
106func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
107 dc.user.forEachUpstream(func(uc *upstreamConn) {
108 if dc.network != nil && uc.network != dc.network {
109 return
110 }
111 f(uc)
112 })
113}
114
115// upstream returns the upstream connection, if any. If there are zero or if
116// there are multiple upstream connections, it returns nil.
117func (dc *downstreamConn) upstream() *upstreamConn {
118 if dc.network == nil {
119 return nil
120 }
121 return dc.network.upstream()
122}
123
124// marshalEntity converts an upstream entity name (ie. channel or nick) into a
125// downstream entity name.
126//
127// This involves adding a "/<network>" suffix if the entity isn't the current
128// user.
129func (dc *downstreamConn) marshalEntity(uc *upstreamConn, name string) string {
130 if dc.network != nil {
131 return name
132 }
133 if name == uc.nick {
134 return dc.nick
135 }
136 return name + "/" + uc.network.GetName()
137}
138
139func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
140 return dc.marshalEntity(uc, name)
141}
142
143func (dc *downstreamConn) marshalNick(uc *upstreamConn, name string) string {
144 return dc.marshalEntity(uc, name)
145}
146
147func (dc *downstreamConn) marshalUserPrefix(uc *upstreamConn, prefix *irc.Prefix) *irc.Prefix {
148 if prefix.Name == uc.nick {
149 return dc.prefix()
150 }
151 if dc.network != nil {
152 return prefix
153 }
154 return &irc.Prefix{
155 Name: prefix.Name + "/" + uc.network.GetName(),
156 User: prefix.User,
157 Host: prefix.Host,
158 }
159}
160
161// unmarshalEntity converts a downstream entity name (ie. channel or nick) into
162// an upstream entity name.
163//
164// This involves removing the "/<network>" suffix.
165func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
166 if uc := dc.upstream(); uc != nil {
167 return uc, name, nil
168 }
169
170 var conn *upstreamConn
171 if i := strings.LastIndexByte(name, '/'); i >= 0 {
172 network := name[i+1:]
173 name = name[:i]
174
175 dc.forEachUpstream(func(uc *upstreamConn) {
176 if network != uc.network.GetName() {
177 return
178 }
179 conn = uc
180 })
181 }
182
183 if conn == nil {
184 return nil, "", ircError{&irc.Message{
185 Command: irc.ERR_NOSUCHCHANNEL,
186 Params: []string{name, "No such channel"},
187 }}
188 }
189 return conn, name, nil
190}
191
192func (dc *downstreamConn) readMessages(ch chan<- event) error {
193 for {
194 msg, err := dc.ReadMessage()
195 if err == io.EOF {
196 break
197 } else if err != nil {
198 return fmt.Errorf("failed to read IRC command: %v", err)
199 }
200
201 ch <- eventDownstreamMessage{msg, dc}
202 }
203
204 return nil
205}
206
207// SendMessage sends an outgoing message.
208//
209// This can only called from the user goroutine.
210func (dc *downstreamConn) SendMessage(msg *irc.Message) {
211 if !dc.caps["message-tags"] {
212 msg = msg.Copy()
213 for name := range msg.Tags {
214 supported := false
215 switch name {
216 case "time":
217 supported = dc.caps["server-time"]
218 }
219 if !supported {
220 delete(msg.Tags, name)
221 }
222 }
223 }
224
225 dc.conn.SendMessage(msg)
226}
227
228// marshalMessage re-formats a message coming from an upstream connection so
229// that it's suitable for being sent on this downstream connection. Only
230// messages that may appear in logs are supported.
231func (dc *downstreamConn) marshalMessage(msg *irc.Message, uc *upstreamConn) *irc.Message {
232 msg = msg.Copy()
233 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
234
235 switch msg.Command {
236 case "PRIVMSG", "NOTICE":
237 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
238 case "NICK":
239 // Nick change for another user
240 msg.Params[0] = dc.marshalNick(uc, msg.Params[0])
241 case "JOIN", "PART":
242 msg.Params[0] = dc.marshalChannel(uc, msg.Params[0])
243 case "KICK":
244 msg.Params[0] = dc.marshalChannel(uc, msg.Params[0])
245 msg.Params[1] = dc.marshalNick(uc, msg.Params[1])
246 case "TOPIC":
247 msg.Params[0] = dc.marshalChannel(uc, msg.Params[0])
248 case "MODE":
249 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
250 case "QUIT":
251 // This space is intentinally left blank
252 default:
253 panic(fmt.Sprintf("unexpected %q message", msg.Command))
254 }
255
256 return msg
257}
258
259func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
260 switch msg.Command {
261 case "QUIT":
262 return dc.Close()
263 default:
264 if dc.registered {
265 return dc.handleMessageRegistered(msg)
266 } else {
267 return dc.handleMessageUnregistered(msg)
268 }
269 }
270}
271
272func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
273 switch msg.Command {
274 case "NICK":
275 var nick string
276 if err := parseMessageParams(msg, &nick); err != nil {
277 return err
278 }
279 if nick == serviceNick {
280 return ircError{&irc.Message{
281 Command: irc.ERR_NICKNAMEINUSE,
282 Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
283 }}
284 }
285 dc.nick = nick
286 case "USER":
287 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
288 return err
289 }
290 case "PASS":
291 if err := parseMessageParams(msg, &dc.password); err != nil {
292 return err
293 }
294 case "CAP":
295 var subCmd string
296 if err := parseMessageParams(msg, &subCmd); err != nil {
297 return err
298 }
299 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
300 return err
301 }
302 case "AUTHENTICATE":
303 if !dc.caps["sasl"] {
304 return ircError{&irc.Message{
305 Command: irc.ERR_SASLFAIL,
306 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
307 }}
308 }
309 if len(msg.Params) == 0 {
310 return ircError{&irc.Message{
311 Command: irc.ERR_SASLFAIL,
312 Params: []string{"*", "Missing AUTHENTICATE argument"},
313 }}
314 }
315 if dc.nick == "" {
316 return ircError{&irc.Message{
317 Command: irc.ERR_SASLFAIL,
318 Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
319 }}
320 }
321
322 var resp []byte
323 if dc.saslServer == nil {
324 mech := strings.ToUpper(msg.Params[0])
325 switch mech {
326 case "PLAIN":
327 dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
328 return dc.authenticate(username, password)
329 }))
330 default:
331 return ircError{&irc.Message{
332 Command: irc.ERR_SASLFAIL,
333 Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
334 }}
335 }
336 } else if msg.Params[0] == "*" {
337 dc.saslServer = nil
338 return ircError{&irc.Message{
339 Command: irc.ERR_SASLABORTED,
340 Params: []string{"*", "SASL authentication aborted"},
341 }}
342 } else if msg.Params[0] == "+" {
343 resp = nil
344 } else {
345 // TODO: multi-line messages
346 var err error
347 resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
348 if err != nil {
349 dc.saslServer = nil
350 return ircError{&irc.Message{
351 Command: irc.ERR_SASLFAIL,
352 Params: []string{"*", "Invalid base64-encoded response"},
353 }}
354 }
355 }
356
357 challenge, done, err := dc.saslServer.Next(resp)
358 if err != nil {
359 dc.saslServer = nil
360 if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
361 return ircError{&irc.Message{
362 Command: irc.ERR_SASLFAIL,
363 Params: []string{"*", ircErr.Message.Params[1]},
364 }}
365 }
366 dc.SendMessage(&irc.Message{
367 Prefix: dc.srv.prefix(),
368 Command: irc.ERR_SASLFAIL,
369 Params: []string{"*", "SASL error"},
370 })
371 return fmt.Errorf("SASL authentication failed: %v", err)
372 } else if done {
373 dc.saslServer = nil
374 dc.SendMessage(&irc.Message{
375 Prefix: dc.srv.prefix(),
376 Command: irc.RPL_LOGGEDIN,
377 Params: []string{dc.nick, dc.nick, dc.user.Username, "You are now logged in"},
378 })
379 dc.SendMessage(&irc.Message{
380 Prefix: dc.srv.prefix(),
381 Command: irc.RPL_SASLSUCCESS,
382 Params: []string{dc.nick, "SASL authentication successful"},
383 })
384 } else {
385 challengeStr := "+"
386 if len(challenge) > 0 {
387 challengeStr = base64.StdEncoding.EncodeToString(challenge)
388 }
389
390 // TODO: multi-line messages
391 dc.SendMessage(&irc.Message{
392 Prefix: dc.srv.prefix(),
393 Command: "AUTHENTICATE",
394 Params: []string{challengeStr},
395 })
396 }
397 default:
398 dc.logger.Printf("unhandled message: %v", msg)
399 return newUnknownCommandError(msg.Command)
400 }
401 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
402 return dc.register()
403 }
404 return nil
405}
406
407func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
408 cmd = strings.ToUpper(cmd)
409
410 replyTo := dc.nick
411 if !dc.registered {
412 replyTo = "*"
413 }
414
415 switch cmd {
416 case "LS":
417 if len(args) > 0 {
418 var err error
419 if dc.capVersion, err = strconv.Atoi(args[0]); err != nil {
420 return err
421 }
422 }
423
424 caps := []string{"message-tags", "server-time", "echo-message", "batch"}
425
426 if dc.capVersion >= 302 {
427 caps = append(caps, "sasl=PLAIN")
428 } else {
429 caps = append(caps, "sasl")
430 }
431
432 // TODO: multi-line replies
433 dc.SendMessage(&irc.Message{
434 Prefix: dc.srv.prefix(),
435 Command: "CAP",
436 Params: []string{replyTo, "LS", strings.Join(caps, " ")},
437 })
438
439 if !dc.registered {
440 dc.negociatingCaps = true
441 }
442 case "LIST":
443 var caps []string
444 for name := range dc.caps {
445 caps = append(caps, name)
446 }
447
448 // TODO: multi-line replies
449 dc.SendMessage(&irc.Message{
450 Prefix: dc.srv.prefix(),
451 Command: "CAP",
452 Params: []string{replyTo, "LIST", strings.Join(caps, " ")},
453 })
454 case "REQ":
455 if len(args) == 0 {
456 return ircError{&irc.Message{
457 Command: err_invalidcapcmd,
458 Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"},
459 }}
460 }
461
462 caps := strings.Fields(args[0])
463 ack := true
464 for _, name := range caps {
465 name = strings.ToLower(name)
466 enable := !strings.HasPrefix(name, "-")
467 if !enable {
468 name = strings.TrimPrefix(name, "-")
469 }
470
471 enabled := dc.caps[name]
472 if enable == enabled {
473 continue
474 }
475
476 switch name {
477 case "sasl", "message-tags", "server-time", "echo-message", "batch":
478 dc.caps[name] = enable
479 default:
480 ack = false
481 }
482 }
483
484 reply := "NAK"
485 if ack {
486 reply = "ACK"
487 }
488 dc.SendMessage(&irc.Message{
489 Prefix: dc.srv.prefix(),
490 Command: "CAP",
491 Params: []string{replyTo, reply, args[0]},
492 })
493 case "END":
494 dc.negociatingCaps = false
495 default:
496 return ircError{&irc.Message{
497 Command: err_invalidcapcmd,
498 Params: []string{replyTo, cmd, "Unknown CAP command"},
499 }}
500 }
501 return nil
502}
503
504func sanityCheckServer(addr string) error {
505 dialer := net.Dialer{Timeout: 30 * time.Second}
506 conn, err := tls.DialWithDialer(&dialer, "tcp", addr, nil)
507 if err != nil {
508 return err
509 }
510 return conn.Close()
511}
512
513func unmarshalUsername(rawUsername string) (username, client, network string) {
514 username = rawUsername
515
516 i := strings.IndexAny(username, "/@")
517 j := strings.LastIndexAny(username, "/@")
518 if i >= 0 {
519 username = rawUsername[:i]
520 }
521 if j >= 0 {
522 if rawUsername[j] == '@' {
523 client = rawUsername[j+1:]
524 } else {
525 network = rawUsername[j+1:]
526 }
527 }
528 if i >= 0 && j >= 0 && i < j {
529 if rawUsername[i] == '@' {
530 client = rawUsername[i+1 : j]
531 } else {
532 network = rawUsername[i+1 : j]
533 }
534 }
535
536 return username, client, network
537}
538
539func (dc *downstreamConn) authenticate(username, password string) error {
540 username, clientName, networkName := unmarshalUsername(username)
541
542 u, err := dc.srv.db.GetUser(username)
543 if err != nil {
544 dc.logger.Printf("failed authentication for %q: %v", username, err)
545 return errAuthFailed
546 }
547
548 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
549 if err != nil {
550 dc.logger.Printf("failed authentication for %q: %v", username, err)
551 return errAuthFailed
552 }
553
554 dc.user = dc.srv.getUser(username)
555 if dc.user == nil {
556 dc.logger.Printf("failed authentication for %q: user not active", username)
557 return errAuthFailed
558 }
559 dc.clientName = clientName
560 dc.networkName = networkName
561 return nil
562}
563
564func (dc *downstreamConn) register() error {
565 if dc.registered {
566 return fmt.Errorf("tried to register twice")
567 }
568
569 password := dc.password
570 dc.password = ""
571 if dc.user == nil {
572 if err := dc.authenticate(dc.rawUsername, password); err != nil {
573 return err
574 }
575 }
576
577 if dc.clientName == "" && dc.networkName == "" {
578 _, dc.clientName, dc.networkName = unmarshalUsername(dc.rawUsername)
579 }
580
581 dc.registered = true
582 dc.logger.Printf("registration complete for user %q", dc.user.Username)
583 return nil
584}
585
586func (dc *downstreamConn) loadNetwork() error {
587 if dc.networkName == "" {
588 return nil
589 }
590
591 network := dc.user.getNetwork(dc.networkName)
592 if network == nil {
593 addr := dc.networkName
594 if !strings.ContainsRune(addr, ':') {
595 addr = addr + ":6697"
596 }
597
598 dc.logger.Printf("trying to connect to new network %q", addr)
599 if err := sanityCheckServer(addr); err != nil {
600 dc.logger.Printf("failed to connect to %q: %v", addr, err)
601 return ircError{&irc.Message{
602 Command: irc.ERR_PASSWDMISMATCH,
603 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", dc.networkName)},
604 }}
605 }
606
607 dc.logger.Printf("auto-saving network %q", dc.networkName)
608 var err error
609 network, err = dc.user.createNetwork(&Network{
610 Addr: dc.networkName,
611 Nick: dc.nick,
612 })
613 if err != nil {
614 return err
615 }
616 }
617
618 dc.network = network
619 return nil
620}
621
622func (dc *downstreamConn) welcome() error {
623 if dc.user == nil || !dc.registered {
624 panic("tried to welcome an unregistered connection")
625 }
626
627 // TODO: doing this might take some time. We should do it in dc.register
628 // instead, but we'll potentially be adding a new network and this must be
629 // done in the user goroutine.
630 if err := dc.loadNetwork(); err != nil {
631 return err
632 }
633
634 dc.SendMessage(&irc.Message{
635 Prefix: dc.srv.prefix(),
636 Command: irc.RPL_WELCOME,
637 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
638 })
639 dc.SendMessage(&irc.Message{
640 Prefix: dc.srv.prefix(),
641 Command: irc.RPL_YOURHOST,
642 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
643 })
644 dc.SendMessage(&irc.Message{
645 Prefix: dc.srv.prefix(),
646 Command: irc.RPL_CREATED,
647 Params: []string{dc.nick, "Who cares when the server was created?"},
648 })
649 dc.SendMessage(&irc.Message{
650 Prefix: dc.srv.prefix(),
651 Command: irc.RPL_MYINFO,
652 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
653 })
654 // TODO: RPL_ISUPPORT
655 dc.SendMessage(&irc.Message{
656 Prefix: dc.srv.prefix(),
657 Command: irc.ERR_NOMOTD,
658 Params: []string{dc.nick, "No MOTD"},
659 })
660
661 dc.forEachUpstream(func(uc *upstreamConn) {
662 for _, ch := range uc.channels {
663 if ch.complete {
664 dc.SendMessage(&irc.Message{
665 Prefix: dc.prefix(),
666 Command: "JOIN",
667 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
668 })
669
670 forwardChannel(dc, ch)
671 }
672 }
673 })
674
675 dc.forEachNetwork(func(net *network) {
676 // Only send history if we're the first connected client with that name
677 // for the network
678 if _, ok := net.offlineClients[dc.clientName]; ok {
679 dc.sendNetworkHistory(net)
680 delete(net.offlineClients, dc.clientName)
681 }
682 })
683
684 return nil
685}
686
687func (dc *downstreamConn) sendNetworkHistory(net *network) {
688 for target, history := range net.history {
689 seq, ok := history.offlineClients[dc.clientName]
690 if !ok {
691 continue
692 }
693 delete(history.offlineClients, dc.clientName)
694
695 // If all clients have received history, no need to keep the
696 // ring buffer around
697 if len(history.offlineClients) == 0 {
698 delete(net.history, target)
699 }
700
701 consumer := history.ring.NewConsumer(seq)
702
703 // TODO: this means all history is lost when trying to send it while the
704 // upstream is disconnected. We need to store history differently so that
705 // we don't need access to upstreamConn to forward it to a downstream
706 // client.
707 uc := net.upstream()
708 if uc == nil {
709 dc.logger.Printf("ignoring messages for upstream %q: upstream is disconnected", net.Addr)
710 return
711 }
712
713 batchRef := "history"
714 if dc.caps["batch"] {
715 dc.SendMessage(&irc.Message{
716 Prefix: dc.srv.prefix(),
717 Command: "BATCH",
718 Params: []string{"+" + batchRef, "chathistory", dc.marshalEntity(uc, target)},
719 })
720 }
721
722 for {
723 msg := consumer.Consume()
724 if msg == nil {
725 break
726 }
727
728 // Don't replay all messages, because that would mess up client
729 // state. For instance we just sent the list of users, sending
730 // PART messages for one of these users would be incorrect.
731 ignore := true
732 switch msg.Command {
733 case "PRIVMSG", "NOTICE":
734 ignore = false
735 }
736 if ignore {
737 continue
738 }
739
740 if dc.caps["batch"] {
741 msg = msg.Copy()
742 msg.Tags["batch"] = irc.TagValue(batchRef)
743 }
744
745 dc.SendMessage(dc.marshalMessage(msg, uc))
746 }
747
748 if dc.caps["batch"] {
749 dc.SendMessage(&irc.Message{
750 Prefix: dc.srv.prefix(),
751 Command: "BATCH",
752 Params: []string{"-" + batchRef},
753 })
754 }
755 }
756}
757
758func (dc *downstreamConn) runUntilRegistered() error {
759 for !dc.registered {
760 msg, err := dc.ReadMessage()
761 if err != nil {
762 return fmt.Errorf("failed to read IRC command: %v", err)
763 }
764
765 err = dc.handleMessage(msg)
766 if ircErr, ok := err.(ircError); ok {
767 ircErr.Message.Prefix = dc.srv.prefix()
768 dc.SendMessage(ircErr.Message)
769 } else if err != nil {
770 return fmt.Errorf("failed to handle IRC command %q: %v", msg, err)
771 }
772 }
773
774 return nil
775}
776
777func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
778 switch msg.Command {
779 case "CAP":
780 var subCmd string
781 if err := parseMessageParams(msg, &subCmd); err != nil {
782 return err
783 }
784 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
785 return err
786 }
787 case "PING":
788 dc.SendMessage(&irc.Message{
789 Prefix: dc.srv.prefix(),
790 Command: "PONG",
791 Params: msg.Params,
792 })
793 return nil
794 case "USER":
795 return ircError{&irc.Message{
796 Command: irc.ERR_ALREADYREGISTERED,
797 Params: []string{dc.nick, "You may not reregister"},
798 }}
799 case "NICK":
800 var nick string
801 if err := parseMessageParams(msg, &nick); err != nil {
802 return err
803 }
804
805 var err error
806 dc.forEachNetwork(func(n *network) {
807 if err != nil {
808 return
809 }
810 n.Nick = nick
811 err = dc.srv.db.StoreNetwork(dc.user.Username, &n.Network)
812 })
813 if err != nil {
814 return err
815 }
816
817 dc.forEachUpstream(func(uc *upstreamConn) {
818 uc.SendMessage(msg)
819 })
820 case "JOIN":
821 var namesStr string
822 if err := parseMessageParams(msg, &namesStr); err != nil {
823 return err
824 }
825
826 var keys []string
827 if len(msg.Params) > 1 {
828 keys = strings.Split(msg.Params[1], ",")
829 }
830
831 for i, name := range strings.Split(namesStr, ",") {
832 uc, upstreamName, err := dc.unmarshalEntity(name)
833 if err != nil {
834 return err
835 }
836
837 var key string
838 if len(keys) > i {
839 key = keys[i]
840 }
841
842 params := []string{upstreamName}
843 if key != "" {
844 params = append(params, key)
845 }
846 uc.SendMessage(&irc.Message{
847 Command: "JOIN",
848 Params: params,
849 })
850
851 ch := &Channel{Name: upstreamName, Key: key}
852 if err := uc.network.createUpdateChannel(ch); err != nil {
853 dc.logger.Printf("failed to create or update channel %q: %v", upstreamName, err)
854 }
855 }
856 case "PART":
857 var namesStr string
858 if err := parseMessageParams(msg, &namesStr); err != nil {
859 return err
860 }
861
862 var reason string
863 if len(msg.Params) > 1 {
864 reason = msg.Params[1]
865 }
866
867 for _, name := range strings.Split(namesStr, ",") {
868 uc, upstreamName, err := dc.unmarshalEntity(name)
869 if err != nil {
870 return err
871 }
872
873 params := []string{upstreamName}
874 if reason != "" {
875 params = append(params, reason)
876 }
877 uc.SendMessage(&irc.Message{
878 Command: "PART",
879 Params: params,
880 })
881
882 if err := uc.network.deleteChannel(upstreamName); err != nil {
883 dc.logger.Printf("failed to delete channel %q: %v", upstreamName, err)
884 }
885 }
886 case "KICK":
887 var channelStr, userStr string
888 if err := parseMessageParams(msg, &channelStr, &userStr); err != nil {
889 return err
890 }
891
892 channels := strings.Split(channelStr, ",")
893 users := strings.Split(userStr, ",")
894
895 var reason string
896 if len(msg.Params) > 2 {
897 reason = msg.Params[2]
898 }
899
900 if len(channels) != 1 && len(channels) != len(users) {
901 return ircError{&irc.Message{
902 Command: irc.ERR_BADCHANMASK,
903 Params: []string{dc.nick, channelStr, "Bad channel mask"},
904 }}
905 }
906
907 for i, user := range users {
908 var channel string
909 if len(channels) == 1 {
910 channel = channels[0]
911 } else {
912 channel = channels[i]
913 }
914
915 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
916 if err != nil {
917 return err
918 }
919
920 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
921 if err != nil {
922 return err
923 }
924
925 if ucChannel != ucUser {
926 return ircError{&irc.Message{
927 Command: irc.ERR_USERNOTINCHANNEL,
928 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
929 }}
930 }
931 uc := ucChannel
932
933 params := []string{upstreamChannel, upstreamUser}
934 if reason != "" {
935 params = append(params, reason)
936 }
937 uc.SendMessage(&irc.Message{
938 Command: "KICK",
939 Params: params,
940 })
941 }
942 case "MODE":
943 var name string
944 if err := parseMessageParams(msg, &name); err != nil {
945 return err
946 }
947
948 var modeStr string
949 if len(msg.Params) > 1 {
950 modeStr = msg.Params[1]
951 }
952
953 if name == dc.nick {
954 if modeStr != "" {
955 dc.forEachUpstream(func(uc *upstreamConn) {
956 uc.SendMessage(&irc.Message{
957 Command: "MODE",
958 Params: []string{uc.nick, modeStr},
959 })
960 })
961 } else {
962 dc.SendMessage(&irc.Message{
963 Prefix: dc.srv.prefix(),
964 Command: irc.RPL_UMODEIS,
965 Params: []string{dc.nick, ""}, // TODO
966 })
967 }
968 return nil
969 }
970
971 uc, upstreamName, err := dc.unmarshalEntity(name)
972 if err != nil {
973 return err
974 }
975
976 if !uc.isChannel(upstreamName) {
977 return ircError{&irc.Message{
978 Command: irc.ERR_USERSDONTMATCH,
979 Params: []string{dc.nick, "Cannot change mode for other users"},
980 }}
981 }
982
983 if modeStr != "" {
984 params := []string{upstreamName, modeStr}
985 params = append(params, msg.Params[2:]...)
986 uc.SendMessage(&irc.Message{
987 Command: "MODE",
988 Params: params,
989 })
990 } else {
991 ch, ok := uc.channels[upstreamName]
992 if !ok {
993 return ircError{&irc.Message{
994 Command: irc.ERR_NOSUCHCHANNEL,
995 Params: []string{dc.nick, name, "No such channel"},
996 }}
997 }
998
999 if ch.modes == nil {
1000 // we haven't received the initial RPL_CHANNELMODEIS yet
1001 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS
1002 return nil
1003 }
1004
1005 modeStr, modeParams := ch.modes.Format()
1006 params := []string{dc.nick, name, modeStr}
1007 params = append(params, modeParams...)
1008
1009 dc.SendMessage(&irc.Message{
1010 Prefix: dc.srv.prefix(),
1011 Command: irc.RPL_CHANNELMODEIS,
1012 Params: params,
1013 })
1014 if ch.creationTime != "" {
1015 dc.SendMessage(&irc.Message{
1016 Prefix: dc.srv.prefix(),
1017 Command: rpl_creationtime,
1018 Params: []string{dc.nick, name, ch.creationTime},
1019 })
1020 }
1021 }
1022 case "TOPIC":
1023 var channel string
1024 if err := parseMessageParams(msg, &channel); err != nil {
1025 return err
1026 }
1027
1028 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1029 if err != nil {
1030 return err
1031 }
1032
1033 if len(msg.Params) > 1 { // setting topic
1034 topic := msg.Params[1]
1035 uc.SendMessage(&irc.Message{
1036 Command: "TOPIC",
1037 Params: []string{upstreamChannel, topic},
1038 })
1039 } else { // getting topic
1040 ch, ok := uc.channels[upstreamChannel]
1041 if !ok {
1042 return ircError{&irc.Message{
1043 Command: irc.ERR_NOSUCHCHANNEL,
1044 Params: []string{dc.nick, upstreamChannel, "No such channel"},
1045 }}
1046 }
1047 sendTopic(dc, ch)
1048 }
1049 case "LIST":
1050 // TODO: support ELIST when supported by all upstreams
1051
1052 pl := pendingLIST{
1053 downstreamID: dc.id,
1054 pendingCommands: make(map[int64]*irc.Message),
1055 }
1056 var upstreamChannels map[int64][]string
1057 if len(msg.Params) > 0 {
1058 upstreamChannels = make(map[int64][]string)
1059 channels := strings.Split(msg.Params[0], ",")
1060 for _, channel := range channels {
1061 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1062 if err != nil {
1063 return err
1064 }
1065 upstreamChannels[uc.network.ID] = append(upstreamChannels[uc.network.ID], upstreamChannel)
1066 }
1067 }
1068
1069 dc.user.pendingLISTs = append(dc.user.pendingLISTs, pl)
1070 dc.forEachUpstream(func(uc *upstreamConn) {
1071 var params []string
1072 if upstreamChannels != nil {
1073 if channels, ok := upstreamChannels[uc.network.ID]; ok {
1074 params = []string{strings.Join(channels, ",")}
1075 } else {
1076 return
1077 }
1078 }
1079 pl.pendingCommands[uc.network.ID] = &irc.Message{
1080 Command: "LIST",
1081 Params: params,
1082 }
1083 uc.trySendLIST(dc.id)
1084 })
1085 case "NAMES":
1086 if len(msg.Params) == 0 {
1087 dc.SendMessage(&irc.Message{
1088 Prefix: dc.srv.prefix(),
1089 Command: irc.RPL_ENDOFNAMES,
1090 Params: []string{dc.nick, "*", "End of /NAMES list"},
1091 })
1092 return nil
1093 }
1094
1095 channels := strings.Split(msg.Params[0], ",")
1096 for _, channel := range channels {
1097 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1098 if err != nil {
1099 return err
1100 }
1101
1102 ch, ok := uc.channels[upstreamChannel]
1103 if ok {
1104 sendNames(dc, ch)
1105 } else {
1106 // NAMES on a channel we have not joined, ask upstream
1107 uc.SendMessageLabeled(dc.id, &irc.Message{
1108 Command: "NAMES",
1109 Params: []string{upstreamChannel},
1110 })
1111 }
1112 }
1113 case "WHO":
1114 if len(msg.Params) == 0 {
1115 // TODO: support WHO without parameters
1116 dc.SendMessage(&irc.Message{
1117 Prefix: dc.srv.prefix(),
1118 Command: irc.RPL_ENDOFWHO,
1119 Params: []string{dc.nick, "*", "End of /WHO list"},
1120 })
1121 return nil
1122 }
1123
1124 // TODO: support WHO masks
1125 entity := msg.Params[0]
1126
1127 if entity == dc.nick {
1128 // TODO: support AWAY (H/G) in self WHO reply
1129 dc.SendMessage(&irc.Message{
1130 Prefix: dc.srv.prefix(),
1131 Command: irc.RPL_WHOREPLY,
1132 Params: []string{dc.nick, "*", dc.user.Username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
1133 })
1134 dc.SendMessage(&irc.Message{
1135 Prefix: dc.srv.prefix(),
1136 Command: irc.RPL_ENDOFWHO,
1137 Params: []string{dc.nick, dc.nick, "End of /WHO list"},
1138 })
1139 return nil
1140 }
1141
1142 uc, upstreamName, err := dc.unmarshalEntity(entity)
1143 if err != nil {
1144 return err
1145 }
1146
1147 var params []string
1148 if len(msg.Params) == 2 {
1149 params = []string{upstreamName, msg.Params[1]}
1150 } else {
1151 params = []string{upstreamName}
1152 }
1153
1154 uc.SendMessageLabeled(dc.id, &irc.Message{
1155 Command: "WHO",
1156 Params: params,
1157 })
1158 case "WHOIS":
1159 if len(msg.Params) == 0 {
1160 return ircError{&irc.Message{
1161 Command: irc.ERR_NONICKNAMEGIVEN,
1162 Params: []string{dc.nick, "No nickname given"},
1163 }}
1164 }
1165
1166 var target, mask string
1167 if len(msg.Params) == 1 {
1168 target = ""
1169 mask = msg.Params[0]
1170 } else {
1171 target = msg.Params[0]
1172 mask = msg.Params[1]
1173 }
1174 // TODO: support multiple WHOIS users
1175 if i := strings.IndexByte(mask, ','); i >= 0 {
1176 mask = mask[:i]
1177 }
1178
1179 if mask == dc.nick {
1180 dc.SendMessage(&irc.Message{
1181 Prefix: dc.srv.prefix(),
1182 Command: irc.RPL_WHOISUSER,
1183 Params: []string{dc.nick, dc.nick, dc.user.Username, dc.hostname, "*", dc.realname},
1184 })
1185 dc.SendMessage(&irc.Message{
1186 Prefix: dc.srv.prefix(),
1187 Command: irc.RPL_WHOISSERVER,
1188 Params: []string{dc.nick, dc.nick, dc.srv.Hostname, "soju"},
1189 })
1190 dc.SendMessage(&irc.Message{
1191 Prefix: dc.srv.prefix(),
1192 Command: irc.RPL_ENDOFWHOIS,
1193 Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
1194 })
1195 return nil
1196 }
1197
1198 // TODO: support WHOIS masks
1199 uc, upstreamNick, err := dc.unmarshalEntity(mask)
1200 if err != nil {
1201 return err
1202 }
1203
1204 var params []string
1205 if target != "" {
1206 params = []string{target, upstreamNick}
1207 } else {
1208 params = []string{upstreamNick}
1209 }
1210
1211 uc.SendMessageLabeled(dc.id, &irc.Message{
1212 Command: "WHOIS",
1213 Params: params,
1214 })
1215 case "PRIVMSG":
1216 var targetsStr, text string
1217 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1218 return err
1219 }
1220
1221 for _, name := range strings.Split(targetsStr, ",") {
1222 if name == serviceNick {
1223 handleServicePRIVMSG(dc, text)
1224 continue
1225 }
1226
1227 uc, upstreamName, err := dc.unmarshalEntity(name)
1228 if err != nil {
1229 return err
1230 }
1231
1232 if upstreamName == "NickServ" {
1233 dc.handleNickServPRIVMSG(uc, text)
1234 }
1235
1236 uc.SendMessage(&irc.Message{
1237 Command: "PRIVMSG",
1238 Params: []string{upstreamName, text},
1239 })
1240
1241 echoMsg := &irc.Message{
1242 Prefix: &irc.Prefix{
1243 Name: uc.nick,
1244 User: uc.username,
1245 },
1246 Command: "PRIVMSG",
1247 Params: []string{upstreamName, text},
1248 }
1249
1250 uc.produce(upstreamName, echoMsg, dc)
1251 }
1252 case "NOTICE":
1253 var targetsStr, text string
1254 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1255 return err
1256 }
1257
1258 for _, name := range strings.Split(targetsStr, ",") {
1259 uc, upstreamName, err := dc.unmarshalEntity(name)
1260 if err != nil {
1261 return err
1262 }
1263
1264 uc.SendMessage(&irc.Message{
1265 Command: "NOTICE",
1266 Params: []string{upstreamName, text},
1267 })
1268 }
1269 case "INVITE":
1270 var user, channel string
1271 if err := parseMessageParams(msg, &user, &channel); err != nil {
1272 return err
1273 }
1274
1275 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
1276 if err != nil {
1277 return err
1278 }
1279
1280 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
1281 if err != nil {
1282 return err
1283 }
1284
1285 if ucChannel != ucUser {
1286 return ircError{&irc.Message{
1287 Command: irc.ERR_USERNOTINCHANNEL,
1288 Params: []string{dc.nick, user, channel, "They aren't on that channel"},
1289 }}
1290 }
1291 uc := ucChannel
1292
1293 uc.SendMessageLabeled(dc.id, &irc.Message{
1294 Command: "INVITE",
1295 Params: []string{upstreamUser, upstreamChannel},
1296 })
1297 default:
1298 dc.logger.Printf("unhandled message: %v", msg)
1299 return newUnknownCommandError(msg.Command)
1300 }
1301 return nil
1302}
1303
1304func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1305 username, password, ok := parseNickServCredentials(text, uc.nick)
1306 if !ok {
1307 return
1308 }
1309
1310 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1311 n := uc.network
1312 n.SASL.Mechanism = "PLAIN"
1313 n.SASL.Plain.Username = username
1314 n.SASL.Plain.Password = password
1315 if err := dc.srv.db.StoreNetwork(dc.user.Username, &n.Network); err != nil {
1316 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1317 }
1318}
1319
1320func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1321 fields := strings.Fields(text)
1322 if len(fields) < 2 {
1323 return "", "", false
1324 }
1325 cmd := strings.ToUpper(fields[0])
1326 params := fields[1:]
1327 switch cmd {
1328 case "REGISTER":
1329 username = nick
1330 password = params[0]
1331 case "IDENTIFY":
1332 if len(params) == 1 {
1333 username = nick
1334 password = params[0]
1335 } else {
1336 username = params[0]
1337 password = params[1]
1338 }
1339 case "SET":
1340 if len(params) == 2 && strings.EqualFold(params[0], "PASSWORD") {
1341 username = nick
1342 password = params[1]
1343 }
1344 }
1345 return username, password, true
1346}
Note: See TracBrowser for help on using the repository browser.