source: code/trunk/downstream.go@ 531

Last change on this file since 531 was 529, checked in by philipk, 4 years ago

Directly return self-messages to user in multi-upstream mode

File size: 48.3 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
48func newChatHistoryError(subcommand string, target string) ircError {
49 return ircError{&irc.Message{
50 Command: "FAIL",
51 Params: []string{"CHATHISTORY", "MESSAGE_ERROR", subcommand, target, "Messages could not be retrieved"},
52 }}
53}
54
55var errAuthFailed = ircError{&irc.Message{
56 Command: irc.ERR_PASSWDMISMATCH,
57 Params: []string{"*", "Invalid username or password"},
58}}
59
60// ' ' and ':' break the IRC message wire format, '@' and '!' break prefixes,
61// '*' and '?' break masks
62const illegalNickChars = " :@!*?"
63
64// permanentDownstreamCaps is the list of always-supported downstream
65// capabilities.
66var permanentDownstreamCaps = map[string]string{
67 "batch": "",
68 "cap-notify": "",
69 "echo-message": "",
70 "invite-notify": "",
71 "message-tags": "",
72 "sasl": "PLAIN",
73 "server-time": "",
74}
75
76// needAllDownstreamCaps is the list of downstream capabilities that
77// require support from all upstreams to be enabled
78var needAllDownstreamCaps = map[string]string{
79 "away-notify": "",
80 "extended-join": "",
81 "multi-prefix": "",
82}
83
84// passthroughIsupport is the set of ISUPPORT tokens that are directly passed
85// through from the upstream server to downstream clients.
86//
87// This is only effective in single-upstream mode.
88var passthroughIsupport = map[string]bool{
89 "AWAYLEN": true,
90 "BOT": true,
91 "CHANLIMIT": true,
92 "CHANMODES": true,
93 "CHANNELLEN": true,
94 "CHANTYPES": true,
95 "EXCEPTS": true,
96 "EXTBAN": true,
97 "HOSTLEN": true,
98 "INVEX": true,
99 "KICKLEN": true,
100 "MAXLIST": true,
101 "MAXTARGETS": true,
102 "MODES": true,
103 "NETWORK": true,
104 "NICKLEN": true,
105 "PREFIX": true,
106 "SAFELIST": true,
107 "TARGMAX": true,
108 "TOPICLEN": true,
109 "USERLEN": true,
110}
111
112type downstreamConn struct {
113 conn
114
115 id uint64
116
117 registered bool
118 user *user
119 nick string
120 nickCM string
121 rawUsername string
122 networkName string
123 clientName string
124 realname string
125 hostname string
126 password string // empty after authentication
127 network *network // can be nil
128
129 negociatingCaps bool
130 capVersion int
131 supportedCaps map[string]string
132 caps map[string]bool
133
134 saslServer sasl.Server
135}
136
137func newDownstreamConn(srv *Server, ic ircConn, id uint64) *downstreamConn {
138 remoteAddr := ic.RemoteAddr().String()
139 logger := &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", remoteAddr)}
140 options := connOptions{Logger: logger}
141 dc := &downstreamConn{
142 conn: *newConn(srv, ic, &options),
143 id: id,
144 supportedCaps: make(map[string]string),
145 caps: make(map[string]bool),
146 }
147 dc.hostname = remoteAddr
148 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
149 dc.hostname = host
150 }
151 for k, v := range permanentDownstreamCaps {
152 dc.supportedCaps[k] = v
153 }
154 if srv.LogPath != "" {
155 dc.supportedCaps["draft/chathistory"] = ""
156 }
157 return dc
158}
159
160func (dc *downstreamConn) prefix() *irc.Prefix {
161 return &irc.Prefix{
162 Name: dc.nick,
163 User: dc.user.Username,
164 Host: dc.hostname,
165 }
166}
167
168func (dc *downstreamConn) forEachNetwork(f func(*network)) {
169 if dc.network != nil {
170 f(dc.network)
171 } else {
172 dc.user.forEachNetwork(f)
173 }
174}
175
176func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
177 dc.user.forEachUpstream(func(uc *upstreamConn) {
178 if dc.network != nil && uc.network != dc.network {
179 return
180 }
181 f(uc)
182 })
183}
184
185// upstream returns the upstream connection, if any. If there are zero or if
186// there are multiple upstream connections, it returns nil.
187func (dc *downstreamConn) upstream() *upstreamConn {
188 if dc.network == nil {
189 return nil
190 }
191 return dc.network.conn
192}
193
194func isOurNick(net *network, nick string) bool {
195 // TODO: this doesn't account for nick changes
196 if net.conn != nil {
197 return net.casemap(nick) == net.conn.nickCM
198 }
199 // We're not currently connected to the upstream connection, so we don't
200 // know whether this name is our nickname. Best-effort: use the network's
201 // configured nickname and hope it was the one being used when we were
202 // connected.
203 return net.casemap(nick) == net.casemap(net.Nick)
204}
205
206// marshalEntity converts an upstream entity name (ie. channel or nick) into a
207// downstream entity name.
208//
209// This involves adding a "/<network>" suffix if the entity isn't the current
210// user.
211func (dc *downstreamConn) marshalEntity(net *network, name string) string {
212 if isOurNick(net, name) {
213 return dc.nick
214 }
215 name = partialCasemap(net.casemap, name)
216 if dc.network != nil {
217 if dc.network != net {
218 panic("soju: tried to marshal an entity for another network")
219 }
220 return name
221 }
222 return name + "/" + net.GetName()
223}
224
225func (dc *downstreamConn) marshalUserPrefix(net *network, prefix *irc.Prefix) *irc.Prefix {
226 if isOurNick(net, prefix.Name) {
227 return dc.prefix()
228 }
229 prefix.Name = partialCasemap(net.casemap, prefix.Name)
230 if dc.network != nil {
231 if dc.network != net {
232 panic("soju: tried to marshal a user prefix for another network")
233 }
234 return prefix
235 }
236 return &irc.Prefix{
237 Name: prefix.Name + "/" + net.GetName(),
238 User: prefix.User,
239 Host: prefix.Host,
240 }
241}
242
243// unmarshalEntity converts a downstream entity name (ie. channel or nick) into
244// an upstream entity name.
245//
246// This involves removing the "/<network>" suffix.
247func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
248 if uc := dc.upstream(); uc != nil {
249 return uc, name, nil
250 }
251 if dc.network != nil {
252 return nil, "", ircError{&irc.Message{
253 Command: irc.ERR_NOSUCHCHANNEL,
254 Params: []string{name, "Disconnected from upstream network"},
255 }}
256 }
257
258 var conn *upstreamConn
259 if i := strings.LastIndexByte(name, '/'); i >= 0 {
260 network := name[i+1:]
261 name = name[:i]
262
263 dc.forEachUpstream(func(uc *upstreamConn) {
264 if network != uc.network.GetName() {
265 return
266 }
267 conn = uc
268 })
269 }
270
271 if conn == nil {
272 return nil, "", ircError{&irc.Message{
273 Command: irc.ERR_NOSUCHCHANNEL,
274 Params: []string{name, "Missing network suffix in channel name"},
275 }}
276 }
277 return conn, name, nil
278}
279
280func (dc *downstreamConn) unmarshalText(uc *upstreamConn, text string) string {
281 if dc.upstream() != nil {
282 return text
283 }
284 // TODO: smarter parsing that ignores URLs
285 return strings.ReplaceAll(text, "/"+uc.network.GetName(), "")
286}
287
288func (dc *downstreamConn) readMessages(ch chan<- event) error {
289 for {
290 msg, err := dc.ReadMessage()
291 if err == io.EOF {
292 break
293 } else if err != nil {
294 return fmt.Errorf("failed to read IRC command: %v", err)
295 }
296
297 ch <- eventDownstreamMessage{msg, dc}
298 }
299
300 return nil
301}
302
303// SendMessage sends an outgoing message.
304//
305// This can only called from the user goroutine.
306func (dc *downstreamConn) SendMessage(msg *irc.Message) {
307 if !dc.caps["message-tags"] {
308 if msg.Command == "TAGMSG" {
309 return
310 }
311 msg = msg.Copy()
312 for name := range msg.Tags {
313 supported := false
314 switch name {
315 case "time":
316 supported = dc.caps["server-time"]
317 }
318 if !supported {
319 delete(msg.Tags, name)
320 }
321 }
322 }
323 if msg.Command == "JOIN" && !dc.caps["extended-join"] {
324 msg.Params = msg.Params[:1]
325 }
326
327 dc.conn.SendMessage(msg)
328}
329
330// sendMessageWithID sends an outgoing message with the specified internal ID.
331func (dc *downstreamConn) sendMessageWithID(msg *irc.Message, id string) {
332 dc.SendMessage(msg)
333
334 if id == "" || !dc.messageSupportsHistory(msg) {
335 return
336 }
337
338 dc.sendPing(id)
339}
340
341// advanceMessageWithID advances history to the specified message ID without
342// sending a message. This is useful e.g. for self-messages when echo-message
343// isn't enabled.
344func (dc *downstreamConn) advanceMessageWithID(msg *irc.Message, id string) {
345 if id == "" || !dc.messageSupportsHistory(msg) {
346 return
347 }
348
349 dc.sendPing(id)
350}
351
352// ackMsgID acknowledges that a message has been received.
353func (dc *downstreamConn) ackMsgID(id string) {
354 netID, entity, err := parseMsgID(id, nil)
355 if err != nil {
356 dc.logger.Printf("failed to ACK message ID %q: %v", id, err)
357 return
358 }
359
360 network := dc.user.getNetworkByID(netID)
361 if network == nil {
362 return
363 }
364
365 network.delivered.StoreID(entity, dc.clientName, id)
366}
367
368func (dc *downstreamConn) sendPing(msgID string) {
369 token := "soju-msgid-" + msgID
370 dc.SendMessage(&irc.Message{
371 Command: "PING",
372 Params: []string{token},
373 })
374}
375
376func (dc *downstreamConn) handlePong(token string) {
377 if !strings.HasPrefix(token, "soju-msgid-") {
378 dc.logger.Printf("received unrecognized PONG token %q", token)
379 return
380 }
381 msgID := strings.TrimPrefix(token, "soju-msgid-")
382 dc.ackMsgID(msgID)
383}
384
385// marshalMessage re-formats a message coming from an upstream connection so
386// that it's suitable for being sent on this downstream connection. Only
387// messages that may appear in logs are supported, except MODE.
388func (dc *downstreamConn) marshalMessage(msg *irc.Message, net *network) *irc.Message {
389 msg = msg.Copy()
390 msg.Prefix = dc.marshalUserPrefix(net, msg.Prefix)
391
392 switch msg.Command {
393 case "PRIVMSG", "NOTICE", "TAGMSG":
394 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
395 case "NICK":
396 // Nick change for another user
397 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
398 case "JOIN", "PART":
399 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
400 case "KICK":
401 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
402 msg.Params[1] = dc.marshalEntity(net, msg.Params[1])
403 case "TOPIC":
404 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
405 case "QUIT":
406 // This space is intentionally left blank
407 default:
408 panic(fmt.Sprintf("unexpected %q message", msg.Command))
409 }
410
411 return msg
412}
413
414func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
415 switch msg.Command {
416 case "QUIT":
417 return dc.Close()
418 default:
419 if dc.registered {
420 return dc.handleMessageRegistered(msg)
421 } else {
422 return dc.handleMessageUnregistered(msg)
423 }
424 }
425}
426
427func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
428 switch msg.Command {
429 case "NICK":
430 var nick string
431 if err := parseMessageParams(msg, &nick); err != nil {
432 return err
433 }
434 if strings.ContainsAny(nick, illegalNickChars) {
435 return ircError{&irc.Message{
436 Command: irc.ERR_ERRONEUSNICKNAME,
437 Params: []string{dc.nick, nick, "contains illegal characters"},
438 }}
439 }
440 nickCM := casemapASCII(nick)
441 if nickCM == serviceNickCM {
442 return ircError{&irc.Message{
443 Command: irc.ERR_NICKNAMEINUSE,
444 Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
445 }}
446 }
447 dc.nick = nick
448 dc.nickCM = nickCM
449 case "USER":
450 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
451 return err
452 }
453 case "PASS":
454 if err := parseMessageParams(msg, &dc.password); err != nil {
455 return err
456 }
457 case "CAP":
458 var subCmd string
459 if err := parseMessageParams(msg, &subCmd); err != nil {
460 return err
461 }
462 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
463 return err
464 }
465 case "AUTHENTICATE":
466 if !dc.caps["sasl"] {
467 return ircError{&irc.Message{
468 Command: irc.ERR_SASLFAIL,
469 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
470 }}
471 }
472 if len(msg.Params) == 0 {
473 return ircError{&irc.Message{
474 Command: irc.ERR_SASLFAIL,
475 Params: []string{"*", "Missing AUTHENTICATE argument"},
476 }}
477 }
478 if dc.nick == "" {
479 return ircError{&irc.Message{
480 Command: irc.ERR_SASLFAIL,
481 Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
482 }}
483 }
484
485 var resp []byte
486 if dc.saslServer == nil {
487 mech := strings.ToUpper(msg.Params[0])
488 switch mech {
489 case "PLAIN":
490 dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
491 return dc.authenticate(username, password)
492 }))
493 default:
494 return ircError{&irc.Message{
495 Command: irc.ERR_SASLFAIL,
496 Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
497 }}
498 }
499 } else if msg.Params[0] == "*" {
500 dc.saslServer = nil
501 return ircError{&irc.Message{
502 Command: irc.ERR_SASLABORTED,
503 Params: []string{"*", "SASL authentication aborted"},
504 }}
505 } else if msg.Params[0] == "+" {
506 resp = nil
507 } else {
508 // TODO: multi-line messages
509 var err error
510 resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
511 if err != nil {
512 dc.saslServer = nil
513 return ircError{&irc.Message{
514 Command: irc.ERR_SASLFAIL,
515 Params: []string{"*", "Invalid base64-encoded response"},
516 }}
517 }
518 }
519
520 challenge, done, err := dc.saslServer.Next(resp)
521 if err != nil {
522 dc.saslServer = nil
523 if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
524 return ircError{&irc.Message{
525 Command: irc.ERR_SASLFAIL,
526 Params: []string{"*", ircErr.Message.Params[1]},
527 }}
528 }
529 dc.SendMessage(&irc.Message{
530 Prefix: dc.srv.prefix(),
531 Command: irc.ERR_SASLFAIL,
532 Params: []string{"*", "SASL error"},
533 })
534 return fmt.Errorf("SASL authentication failed: %v", err)
535 } else if done {
536 dc.saslServer = nil
537 dc.SendMessage(&irc.Message{
538 Prefix: dc.srv.prefix(),
539 Command: irc.RPL_LOGGEDIN,
540 Params: []string{dc.nick, dc.prefix().String(), dc.user.Username, "You are now logged in"},
541 })
542 dc.SendMessage(&irc.Message{
543 Prefix: dc.srv.prefix(),
544 Command: irc.RPL_SASLSUCCESS,
545 Params: []string{dc.nick, "SASL authentication successful"},
546 })
547 } else {
548 challengeStr := "+"
549 if len(challenge) > 0 {
550 challengeStr = base64.StdEncoding.EncodeToString(challenge)
551 }
552
553 // TODO: multi-line messages
554 dc.SendMessage(&irc.Message{
555 Prefix: dc.srv.prefix(),
556 Command: "AUTHENTICATE",
557 Params: []string{challengeStr},
558 })
559 }
560 default:
561 dc.logger.Printf("unhandled message: %v", msg)
562 return newUnknownCommandError(msg.Command)
563 }
564 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
565 return dc.register()
566 }
567 return nil
568}
569
570func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
571 cmd = strings.ToUpper(cmd)
572
573 replyTo := dc.nick
574 if !dc.registered {
575 replyTo = "*"
576 }
577
578 switch cmd {
579 case "LS":
580 if len(args) > 0 {
581 var err error
582 if dc.capVersion, err = strconv.Atoi(args[0]); err != nil {
583 return err
584 }
585 }
586 if !dc.registered && dc.capVersion >= 302 {
587 // Let downstream show everything it supports, and trim
588 // down the available capabilities when upstreams are
589 // known.
590 for k, v := range needAllDownstreamCaps {
591 dc.supportedCaps[k] = v
592 }
593 }
594
595 caps := make([]string, 0, len(dc.supportedCaps))
596 for k, v := range dc.supportedCaps {
597 if dc.capVersion >= 302 && v != "" {
598 caps = append(caps, k+"="+v)
599 } else {
600 caps = append(caps, k)
601 }
602 }
603
604 // TODO: multi-line replies
605 dc.SendMessage(&irc.Message{
606 Prefix: dc.srv.prefix(),
607 Command: "CAP",
608 Params: []string{replyTo, "LS", strings.Join(caps, " ")},
609 })
610
611 if dc.capVersion >= 302 {
612 // CAP version 302 implicitly enables cap-notify
613 dc.caps["cap-notify"] = true
614 }
615
616 if !dc.registered {
617 dc.negociatingCaps = true
618 }
619 case "LIST":
620 var caps []string
621 for name, enabled := range dc.caps {
622 if enabled {
623 caps = append(caps, name)
624 }
625 }
626
627 // TODO: multi-line replies
628 dc.SendMessage(&irc.Message{
629 Prefix: dc.srv.prefix(),
630 Command: "CAP",
631 Params: []string{replyTo, "LIST", strings.Join(caps, " ")},
632 })
633 case "REQ":
634 if len(args) == 0 {
635 return ircError{&irc.Message{
636 Command: err_invalidcapcmd,
637 Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"},
638 }}
639 }
640
641 // TODO: atomically ack/nak the whole capability set
642 caps := strings.Fields(args[0])
643 ack := true
644 for _, name := range caps {
645 name = strings.ToLower(name)
646 enable := !strings.HasPrefix(name, "-")
647 if !enable {
648 name = strings.TrimPrefix(name, "-")
649 }
650
651 if enable == dc.caps[name] {
652 continue
653 }
654
655 _, ok := dc.supportedCaps[name]
656 if !ok {
657 ack = false
658 break
659 }
660
661 if name == "cap-notify" && dc.capVersion >= 302 && !enable {
662 // cap-notify cannot be disabled with CAP version 302
663 ack = false
664 break
665 }
666
667 dc.caps[name] = enable
668 }
669
670 reply := "NAK"
671 if ack {
672 reply = "ACK"
673 }
674 dc.SendMessage(&irc.Message{
675 Prefix: dc.srv.prefix(),
676 Command: "CAP",
677 Params: []string{replyTo, reply, args[0]},
678 })
679 case "END":
680 dc.negociatingCaps = false
681 default:
682 return ircError{&irc.Message{
683 Command: err_invalidcapcmd,
684 Params: []string{replyTo, cmd, "Unknown CAP command"},
685 }}
686 }
687 return nil
688}
689
690func (dc *downstreamConn) setSupportedCap(name, value string) {
691 prevValue, hasPrev := dc.supportedCaps[name]
692 changed := !hasPrev || prevValue != value
693 dc.supportedCaps[name] = value
694
695 if !dc.caps["cap-notify"] || !changed {
696 return
697 }
698
699 replyTo := dc.nick
700 if !dc.registered {
701 replyTo = "*"
702 }
703
704 cap := name
705 if value != "" && dc.capVersion >= 302 {
706 cap = name + "=" + value
707 }
708
709 dc.SendMessage(&irc.Message{
710 Prefix: dc.srv.prefix(),
711 Command: "CAP",
712 Params: []string{replyTo, "NEW", cap},
713 })
714}
715
716func (dc *downstreamConn) unsetSupportedCap(name string) {
717 _, hasPrev := dc.supportedCaps[name]
718 delete(dc.supportedCaps, name)
719 delete(dc.caps, name)
720
721 if !dc.caps["cap-notify"] || !hasPrev {
722 return
723 }
724
725 replyTo := dc.nick
726 if !dc.registered {
727 replyTo = "*"
728 }
729
730 dc.SendMessage(&irc.Message{
731 Prefix: dc.srv.prefix(),
732 Command: "CAP",
733 Params: []string{replyTo, "DEL", name},
734 })
735}
736
737func (dc *downstreamConn) updateSupportedCaps() {
738 supportedCaps := make(map[string]bool)
739 for cap := range needAllDownstreamCaps {
740 supportedCaps[cap] = true
741 }
742 dc.forEachUpstream(func(uc *upstreamConn) {
743 for cap, supported := range supportedCaps {
744 supportedCaps[cap] = supported && uc.caps[cap]
745 }
746 })
747
748 for cap, supported := range supportedCaps {
749 if supported {
750 dc.setSupportedCap(cap, needAllDownstreamCaps[cap])
751 } else {
752 dc.unsetSupportedCap(cap)
753 }
754 }
755}
756
757func (dc *downstreamConn) updateNick() {
758 if uc := dc.upstream(); uc != nil && uc.nick != dc.nick {
759 dc.SendMessage(&irc.Message{
760 Prefix: dc.prefix(),
761 Command: "NICK",
762 Params: []string{uc.nick},
763 })
764 dc.nick = uc.nick
765 dc.nickCM = casemapASCII(dc.nick)
766 }
767}
768
769func sanityCheckServer(addr string) error {
770 dialer := net.Dialer{Timeout: 30 * time.Second}
771 conn, err := tls.DialWithDialer(&dialer, "tcp", addr, nil)
772 if err != nil {
773 return err
774 }
775 return conn.Close()
776}
777
778func unmarshalUsername(rawUsername string) (username, client, network string) {
779 username = rawUsername
780
781 i := strings.IndexAny(username, "/@")
782 j := strings.LastIndexAny(username, "/@")
783 if i >= 0 {
784 username = rawUsername[:i]
785 }
786 if j >= 0 {
787 if rawUsername[j] == '@' {
788 client = rawUsername[j+1:]
789 } else {
790 network = rawUsername[j+1:]
791 }
792 }
793 if i >= 0 && j >= 0 && i < j {
794 if rawUsername[i] == '@' {
795 client = rawUsername[i+1 : j]
796 } else {
797 network = rawUsername[i+1 : j]
798 }
799 }
800
801 return username, client, network
802}
803
804func (dc *downstreamConn) authenticate(username, password string) error {
805 username, clientName, networkName := unmarshalUsername(username)
806
807 u, err := dc.srv.db.GetUser(username)
808 if err != nil {
809 dc.logger.Printf("failed authentication for %q: user not found: %v", username, err)
810 return errAuthFailed
811 }
812
813 // Password auth disabled
814 if u.Password == "" {
815 return errAuthFailed
816 }
817
818 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
819 if err != nil {
820 dc.logger.Printf("failed authentication for %q: wrong password: %v", username, err)
821 return errAuthFailed
822 }
823
824 dc.user = dc.srv.getUser(username)
825 if dc.user == nil {
826 dc.logger.Printf("failed authentication for %q: user not active", username)
827 return errAuthFailed
828 }
829 dc.clientName = clientName
830 dc.networkName = networkName
831 return nil
832}
833
834func (dc *downstreamConn) register() error {
835 if dc.registered {
836 return fmt.Errorf("tried to register twice")
837 }
838
839 password := dc.password
840 dc.password = ""
841 if dc.user == nil {
842 if err := dc.authenticate(dc.rawUsername, password); err != nil {
843 return err
844 }
845 }
846
847 if dc.clientName == "" && dc.networkName == "" {
848 _, dc.clientName, dc.networkName = unmarshalUsername(dc.rawUsername)
849 }
850
851 dc.registered = true
852 dc.logger.Printf("registration complete for user %q", dc.user.Username)
853 return nil
854}
855
856func (dc *downstreamConn) loadNetwork() error {
857 if dc.networkName == "" {
858 return nil
859 }
860
861 network := dc.user.getNetwork(dc.networkName)
862 if network == nil {
863 addr := dc.networkName
864 if !strings.ContainsRune(addr, ':') {
865 addr = addr + ":6697"
866 }
867
868 dc.logger.Printf("trying to connect to new network %q", addr)
869 if err := sanityCheckServer(addr); err != nil {
870 dc.logger.Printf("failed to connect to %q: %v", addr, err)
871 return ircError{&irc.Message{
872 Command: irc.ERR_PASSWDMISMATCH,
873 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", dc.networkName)},
874 }}
875 }
876
877 // Some clients only allow specifying the nickname (and use the
878 // nickname as a username too). Strip the network name from the
879 // nickname when auto-saving networks.
880 nick, _, _ := unmarshalUsername(dc.nick)
881
882 dc.logger.Printf("auto-saving network %q", dc.networkName)
883 var err error
884 network, err = dc.user.createNetwork(&Network{
885 Addr: dc.networkName,
886 Nick: nick,
887 })
888 if err != nil {
889 return err
890 }
891 }
892
893 dc.network = network
894 return nil
895}
896
897func (dc *downstreamConn) welcome() error {
898 if dc.user == nil || !dc.registered {
899 panic("tried to welcome an unregistered connection")
900 }
901
902 // TODO: doing this might take some time. We should do it in dc.register
903 // instead, but we'll potentially be adding a new network and this must be
904 // done in the user goroutine.
905 if err := dc.loadNetwork(); err != nil {
906 return err
907 }
908
909 isupport := []string{
910 fmt.Sprintf("CHATHISTORY=%v", dc.srv.HistoryLimit),
911 "CASEMAPPING=ascii",
912 }
913
914 if uc := dc.upstream(); uc != nil {
915 for k := range passthroughIsupport {
916 v, ok := uc.isupport[k]
917 if !ok {
918 continue
919 }
920 if v != nil {
921 isupport = append(isupport, fmt.Sprintf("%v=%v", k, *v))
922 } else {
923 isupport = append(isupport, k)
924 }
925 }
926 }
927
928 dc.SendMessage(&irc.Message{
929 Prefix: dc.srv.prefix(),
930 Command: irc.RPL_WELCOME,
931 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
932 })
933 dc.SendMessage(&irc.Message{
934 Prefix: dc.srv.prefix(),
935 Command: irc.RPL_YOURHOST,
936 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
937 })
938 dc.SendMessage(&irc.Message{
939 Prefix: dc.srv.prefix(),
940 Command: irc.RPL_CREATED,
941 Params: []string{dc.nick, "Who cares when the server was created?"},
942 })
943 dc.SendMessage(&irc.Message{
944 Prefix: dc.srv.prefix(),
945 Command: irc.RPL_MYINFO,
946 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
947 })
948 for _, msg := range generateIsupport(dc.srv.prefix(), dc.nick, isupport) {
949 dc.SendMessage(msg)
950 }
951 dc.SendMessage(&irc.Message{
952 Prefix: dc.srv.prefix(),
953 Command: irc.ERR_NOMOTD,
954 Params: []string{dc.nick, "No MOTD"},
955 })
956
957 dc.updateNick()
958 dc.updateSupportedCaps()
959
960 dc.forEachUpstream(func(uc *upstreamConn) {
961 for _, entry := range uc.channels.innerMap {
962 ch := entry.value.(*upstreamChannel)
963 if !ch.complete {
964 continue
965 }
966 record := uc.network.channels.Value(ch.Name)
967 if record != nil && record.Detached {
968 continue
969 }
970
971 dc.SendMessage(&irc.Message{
972 Prefix: dc.prefix(),
973 Command: "JOIN",
974 Params: []string{dc.marshalEntity(ch.conn.network, ch.Name)},
975 })
976
977 forwardChannel(dc, ch)
978 }
979 })
980
981 dc.forEachNetwork(func(net *network) {
982 if dc.caps["draft/chathistory"] || dc.user.msgStore == nil {
983 return
984 }
985
986 // Only send history if we're the first connected client with that name
987 // for the network
988 firstClient := true
989 dc.user.forEachDownstream(func(c *downstreamConn) {
990 if c != dc && c.clientName == dc.clientName && c.network == dc.network {
991 firstClient = false
992 }
993 })
994 if firstClient {
995 net.delivered.ForEachTarget(func(target string) {
996 lastDelivered := net.delivered.LoadID(target, dc.clientName)
997 if lastDelivered == "" {
998 return
999 }
1000
1001 dc.sendTargetBacklog(net, target, lastDelivered)
1002
1003 // Fast-forward history to last message
1004 targetCM := net.casemap(target)
1005 lastID, err := dc.user.msgStore.LastMsgID(net, targetCM, time.Now())
1006 if err != nil {
1007 dc.logger.Printf("failed to get last message ID: %v", err)
1008 return
1009 }
1010 net.delivered.StoreID(target, dc.clientName, lastID)
1011 })
1012 }
1013 })
1014
1015 return nil
1016}
1017
1018// messageSupportsHistory checks whether the provided message can be sent as
1019// part of an history batch.
1020func (dc *downstreamConn) messageSupportsHistory(msg *irc.Message) bool {
1021 // Don't replay all messages, because that would mess up client
1022 // state. For instance we just sent the list of users, sending
1023 // PART messages for one of these users would be incorrect.
1024 // TODO: add support for draft/event-playback
1025 switch msg.Command {
1026 case "PRIVMSG", "NOTICE":
1027 return true
1028 }
1029 return false
1030}
1031
1032func (dc *downstreamConn) sendTargetBacklog(net *network, target, msgID string) {
1033 if dc.caps["draft/chathistory"] || dc.user.msgStore == nil {
1034 return
1035 }
1036
1037 ch := net.channels.Value(target)
1038
1039 limit := 4000
1040 targetCM := net.casemap(target)
1041 history, err := dc.user.msgStore.LoadLatestID(net, targetCM, msgID, limit)
1042 if err != nil {
1043 dc.logger.Printf("failed to send backlog for %q: %v", target, err)
1044 return
1045 }
1046
1047 batchRef := "history"
1048 if dc.caps["batch"] {
1049 dc.SendMessage(&irc.Message{
1050 Prefix: dc.srv.prefix(),
1051 Command: "BATCH",
1052 Params: []string{"+" + batchRef, "chathistory", dc.marshalEntity(net, target)},
1053 })
1054 }
1055
1056 for _, msg := range history {
1057 if !dc.messageSupportsHistory(msg) {
1058 continue
1059 }
1060
1061 if ch != nil && ch.Detached {
1062 if net.detachedMessageNeedsRelay(ch, msg) {
1063 dc.relayDetachedMessage(net, msg)
1064 }
1065 } else {
1066 if dc.caps["batch"] {
1067 msg.Tags["batch"] = irc.TagValue(batchRef)
1068 }
1069 dc.SendMessage(dc.marshalMessage(msg, net))
1070 }
1071 }
1072
1073 if dc.caps["batch"] {
1074 dc.SendMessage(&irc.Message{
1075 Prefix: dc.srv.prefix(),
1076 Command: "BATCH",
1077 Params: []string{"-" + batchRef},
1078 })
1079 }
1080}
1081
1082func (dc *downstreamConn) relayDetachedMessage(net *network, msg *irc.Message) {
1083 if msg.Command != "PRIVMSG" && msg.Command != "NOTICE" {
1084 return
1085 }
1086
1087 sender := msg.Prefix.Name
1088 target, text := msg.Params[0], msg.Params[1]
1089 if net.isHighlight(msg) {
1090 sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(net, target), sender, text))
1091 } else {
1092 sendServiceNOTICE(dc, fmt.Sprintf("message in %v: <%v> %v", dc.marshalEntity(net, target), sender, text))
1093 }
1094}
1095
1096func (dc *downstreamConn) runUntilRegistered() error {
1097 for !dc.registered {
1098 msg, err := dc.ReadMessage()
1099 if err != nil {
1100 return fmt.Errorf("failed to read IRC command: %v", err)
1101 }
1102
1103 err = dc.handleMessage(msg)
1104 if ircErr, ok := err.(ircError); ok {
1105 ircErr.Message.Prefix = dc.srv.prefix()
1106 dc.SendMessage(ircErr.Message)
1107 } else if err != nil {
1108 return fmt.Errorf("failed to handle IRC command %q: %v", msg, err)
1109 }
1110 }
1111
1112 return nil
1113}
1114
1115func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
1116 switch msg.Command {
1117 case "CAP":
1118 var subCmd string
1119 if err := parseMessageParams(msg, &subCmd); err != nil {
1120 return err
1121 }
1122 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
1123 return err
1124 }
1125 case "PING":
1126 var source, destination string
1127 if err := parseMessageParams(msg, &source); err != nil {
1128 return err
1129 }
1130 if len(msg.Params) > 1 {
1131 destination = msg.Params[1]
1132 }
1133 if destination != "" && destination != dc.srv.Hostname {
1134 return ircError{&irc.Message{
1135 Command: irc.ERR_NOSUCHSERVER,
1136 Params: []string{dc.nick, destination, "No such server"},
1137 }}
1138 }
1139 dc.SendMessage(&irc.Message{
1140 Prefix: dc.srv.prefix(),
1141 Command: "PONG",
1142 Params: []string{dc.srv.Hostname, source},
1143 })
1144 return nil
1145 case "PONG":
1146 if len(msg.Params) == 0 {
1147 return newNeedMoreParamsError(msg.Command)
1148 }
1149 token := msg.Params[len(msg.Params)-1]
1150 dc.handlePong(token)
1151 case "USER":
1152 return ircError{&irc.Message{
1153 Command: irc.ERR_ALREADYREGISTERED,
1154 Params: []string{dc.nick, "You may not reregister"},
1155 }}
1156 case "NICK":
1157 var rawNick string
1158 if err := parseMessageParams(msg, &rawNick); err != nil {
1159 return err
1160 }
1161
1162 nick := rawNick
1163 var upstream *upstreamConn
1164 if dc.upstream() == nil {
1165 uc, unmarshaledNick, err := dc.unmarshalEntity(nick)
1166 if err == nil { // NICK nick/network: NICK only on a specific upstream
1167 upstream = uc
1168 nick = unmarshaledNick
1169 }
1170 }
1171
1172 if strings.ContainsAny(nick, illegalNickChars) {
1173 return ircError{&irc.Message{
1174 Command: irc.ERR_ERRONEUSNICKNAME,
1175 Params: []string{dc.nick, rawNick, "contains illegal characters"},
1176 }}
1177 }
1178 if casemapASCII(nick) == serviceNickCM {
1179 return ircError{&irc.Message{
1180 Command: irc.ERR_NICKNAMEINUSE,
1181 Params: []string{dc.nick, rawNick, "Nickname reserved for bouncer service"},
1182 }}
1183 }
1184
1185 var err error
1186 dc.forEachNetwork(func(n *network) {
1187 if err != nil || (upstream != nil && upstream.network != n) {
1188 return
1189 }
1190 n.Nick = nick
1191 err = dc.srv.db.StoreNetwork(dc.user.ID, &n.Network)
1192 })
1193 if err != nil {
1194 return err
1195 }
1196
1197 dc.forEachUpstream(func(uc *upstreamConn) {
1198 if upstream != nil && upstream != uc {
1199 return
1200 }
1201 uc.SendMessageLabeled(dc.id, &irc.Message{
1202 Command: "NICK",
1203 Params: []string{nick},
1204 })
1205 })
1206
1207 if dc.upstream() == nil && upstream == nil && dc.nick != nick {
1208 dc.SendMessage(&irc.Message{
1209 Prefix: dc.prefix(),
1210 Command: "NICK",
1211 Params: []string{nick},
1212 })
1213 dc.nick = nick
1214 dc.nickCM = casemapASCII(dc.nick)
1215 }
1216 case "JOIN":
1217 var namesStr string
1218 if err := parseMessageParams(msg, &namesStr); err != nil {
1219 return err
1220 }
1221
1222 var keys []string
1223 if len(msg.Params) > 1 {
1224 keys = strings.Split(msg.Params[1], ",")
1225 }
1226
1227 for i, name := range strings.Split(namesStr, ",") {
1228 uc, upstreamName, err := dc.unmarshalEntity(name)
1229 if err != nil {
1230 return err
1231 }
1232
1233 var key string
1234 if len(keys) > i {
1235 key = keys[i]
1236 }
1237
1238 params := []string{upstreamName}
1239 if key != "" {
1240 params = append(params, key)
1241 }
1242 uc.SendMessageLabeled(dc.id, &irc.Message{
1243 Command: "JOIN",
1244 Params: params,
1245 })
1246
1247 ch := uc.network.channels.Value(upstreamName)
1248 if ch != nil {
1249 // Don't clear the channel key if there's one set
1250 // TODO: add a way to unset the channel key
1251 if key != "" {
1252 ch.Key = key
1253 }
1254 uc.network.attach(ch)
1255 } else {
1256 ch = &Channel{
1257 Name: upstreamName,
1258 Key: key,
1259 }
1260 uc.network.channels.SetValue(upstreamName, ch)
1261 }
1262 if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
1263 dc.logger.Printf("failed to create or update channel %q: %v", upstreamName, err)
1264 }
1265 }
1266 case "PART":
1267 var namesStr string
1268 if err := parseMessageParams(msg, &namesStr); err != nil {
1269 return err
1270 }
1271
1272 var reason string
1273 if len(msg.Params) > 1 {
1274 reason = msg.Params[1]
1275 }
1276
1277 for _, name := range strings.Split(namesStr, ",") {
1278 uc, upstreamName, err := dc.unmarshalEntity(name)
1279 if err != nil {
1280 return err
1281 }
1282
1283 if strings.EqualFold(reason, "detach") {
1284 ch := uc.network.channels.Value(upstreamName)
1285 if ch != nil {
1286 uc.network.detach(ch)
1287 } else {
1288 ch = &Channel{
1289 Name: name,
1290 Detached: true,
1291 }
1292 uc.network.channels.SetValue(upstreamName, ch)
1293 }
1294 if err := dc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
1295 dc.logger.Printf("failed to create or update channel %q: %v", upstreamName, err)
1296 }
1297 } else {
1298 params := []string{upstreamName}
1299 if reason != "" {
1300 params = append(params, reason)
1301 }
1302 uc.SendMessageLabeled(dc.id, &irc.Message{
1303 Command: "PART",
1304 Params: params,
1305 })
1306
1307 if err := uc.network.deleteChannel(upstreamName); err != nil {
1308 dc.logger.Printf("failed to delete channel %q: %v", upstreamName, err)
1309 }
1310 }
1311 }
1312 case "KICK":
1313 var channelStr, userStr string
1314 if err := parseMessageParams(msg, &channelStr, &userStr); err != nil {
1315 return err
1316 }
1317
1318 channels := strings.Split(channelStr, ",")
1319 users := strings.Split(userStr, ",")
1320
1321 var reason string
1322 if len(msg.Params) > 2 {
1323 reason = msg.Params[2]
1324 }
1325
1326 if len(channels) != 1 && len(channels) != len(users) {
1327 return ircError{&irc.Message{
1328 Command: irc.ERR_BADCHANMASK,
1329 Params: []string{dc.nick, channelStr, "Bad channel mask"},
1330 }}
1331 }
1332
1333 for i, user := range users {
1334 var channel string
1335 if len(channels) == 1 {
1336 channel = channels[0]
1337 } else {
1338 channel = channels[i]
1339 }
1340
1341 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
1342 if err != nil {
1343 return err
1344 }
1345
1346 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
1347 if err != nil {
1348 return err
1349 }
1350
1351 if ucChannel != ucUser {
1352 return ircError{&irc.Message{
1353 Command: irc.ERR_USERNOTINCHANNEL,
1354 Params: []string{dc.nick, user, channel, "They are on another network"},
1355 }}
1356 }
1357 uc := ucChannel
1358
1359 params := []string{upstreamChannel, upstreamUser}
1360 if reason != "" {
1361 params = append(params, reason)
1362 }
1363 uc.SendMessageLabeled(dc.id, &irc.Message{
1364 Command: "KICK",
1365 Params: params,
1366 })
1367 }
1368 case "MODE":
1369 var name string
1370 if err := parseMessageParams(msg, &name); err != nil {
1371 return err
1372 }
1373
1374 var modeStr string
1375 if len(msg.Params) > 1 {
1376 modeStr = msg.Params[1]
1377 }
1378
1379 if casemapASCII(name) == dc.nickCM {
1380 if modeStr != "" {
1381 dc.forEachUpstream(func(uc *upstreamConn) {
1382 uc.SendMessageLabeled(dc.id, &irc.Message{
1383 Command: "MODE",
1384 Params: []string{uc.nick, modeStr},
1385 })
1386 })
1387 } else {
1388 // TODO: only do this in multi-upstream mode
1389 dc.SendMessage(&irc.Message{
1390 Prefix: dc.srv.prefix(),
1391 Command: irc.RPL_UMODEIS,
1392 Params: []string{dc.nick, ""}, // TODO
1393 })
1394 }
1395 return nil
1396 }
1397
1398 uc, upstreamName, err := dc.unmarshalEntity(name)
1399 if err != nil {
1400 return err
1401 }
1402
1403 if !uc.isChannel(upstreamName) {
1404 return ircError{&irc.Message{
1405 Command: irc.ERR_USERSDONTMATCH,
1406 Params: []string{dc.nick, "Cannot change mode for other users"},
1407 }}
1408 }
1409
1410 if modeStr != "" {
1411 params := []string{upstreamName, modeStr}
1412 params = append(params, msg.Params[2:]...)
1413 uc.SendMessageLabeled(dc.id, &irc.Message{
1414 Command: "MODE",
1415 Params: params,
1416 })
1417 } else {
1418 ch := uc.channels.Value(upstreamName)
1419 if ch == nil {
1420 return ircError{&irc.Message{
1421 Command: irc.ERR_NOSUCHCHANNEL,
1422 Params: []string{dc.nick, name, "No such channel"},
1423 }}
1424 }
1425
1426 if ch.modes == nil {
1427 // we haven't received the initial RPL_CHANNELMODEIS yet
1428 // ignore the request, we will broadcast the modes later when we receive RPL_CHANNELMODEIS
1429 return nil
1430 }
1431
1432 modeStr, modeParams := ch.modes.Format()
1433 params := []string{dc.nick, name, modeStr}
1434 params = append(params, modeParams...)
1435
1436 dc.SendMessage(&irc.Message{
1437 Prefix: dc.srv.prefix(),
1438 Command: irc.RPL_CHANNELMODEIS,
1439 Params: params,
1440 })
1441 if ch.creationTime != "" {
1442 dc.SendMessage(&irc.Message{
1443 Prefix: dc.srv.prefix(),
1444 Command: rpl_creationtime,
1445 Params: []string{dc.nick, name, ch.creationTime},
1446 })
1447 }
1448 }
1449 case "TOPIC":
1450 var channel string
1451 if err := parseMessageParams(msg, &channel); err != nil {
1452 return err
1453 }
1454
1455 uc, upstreamName, err := dc.unmarshalEntity(channel)
1456 if err != nil {
1457 return err
1458 }
1459
1460 if len(msg.Params) > 1 { // setting topic
1461 topic := msg.Params[1]
1462 uc.SendMessageLabeled(dc.id, &irc.Message{
1463 Command: "TOPIC",
1464 Params: []string{upstreamName, topic},
1465 })
1466 } else { // getting topic
1467 ch := uc.channels.Value(upstreamName)
1468 if ch == nil {
1469 return ircError{&irc.Message{
1470 Command: irc.ERR_NOSUCHCHANNEL,
1471 Params: []string{dc.nick, upstreamName, "No such channel"},
1472 }}
1473 }
1474 sendTopic(dc, ch)
1475 }
1476 case "LIST":
1477 // TODO: support ELIST when supported by all upstreams
1478
1479 pl := pendingLIST{
1480 downstreamID: dc.id,
1481 pendingCommands: make(map[int64]*irc.Message),
1482 }
1483 var upstream *upstreamConn
1484 var upstreamChannels map[int64][]string
1485 if len(msg.Params) > 0 {
1486 uc, upstreamMask, err := dc.unmarshalEntity(msg.Params[0])
1487 if err == nil && upstreamMask == "*" { // LIST */network: send LIST only to one network
1488 upstream = uc
1489 } else {
1490 upstreamChannels = make(map[int64][]string)
1491 channels := strings.Split(msg.Params[0], ",")
1492 for _, channel := range channels {
1493 uc, upstreamChannel, err := dc.unmarshalEntity(channel)
1494 if err != nil {
1495 return err
1496 }
1497 upstreamChannels[uc.network.ID] = append(upstreamChannels[uc.network.ID], upstreamChannel)
1498 }
1499 }
1500 }
1501
1502 dc.user.pendingLISTs = append(dc.user.pendingLISTs, pl)
1503 dc.forEachUpstream(func(uc *upstreamConn) {
1504 if upstream != nil && upstream != uc {
1505 return
1506 }
1507 var params []string
1508 if upstreamChannels != nil {
1509 if channels, ok := upstreamChannels[uc.network.ID]; ok {
1510 params = []string{strings.Join(channels, ",")}
1511 } else {
1512 return
1513 }
1514 }
1515 pl.pendingCommands[uc.network.ID] = &irc.Message{
1516 Command: "LIST",
1517 Params: params,
1518 }
1519 uc.trySendLIST(dc.id)
1520 })
1521 case "NAMES":
1522 if len(msg.Params) == 0 {
1523 dc.SendMessage(&irc.Message{
1524 Prefix: dc.srv.prefix(),
1525 Command: irc.RPL_ENDOFNAMES,
1526 Params: []string{dc.nick, "*", "End of /NAMES list"},
1527 })
1528 return nil
1529 }
1530
1531 channels := strings.Split(msg.Params[0], ",")
1532 for _, channel := range channels {
1533 uc, upstreamName, err := dc.unmarshalEntity(channel)
1534 if err != nil {
1535 return err
1536 }
1537
1538 ch := uc.channels.Value(upstreamName)
1539 if ch != nil {
1540 sendNames(dc, ch)
1541 } else {
1542 // NAMES on a channel we have not joined, ask upstream
1543 uc.SendMessageLabeled(dc.id, &irc.Message{
1544 Command: "NAMES",
1545 Params: []string{upstreamName},
1546 })
1547 }
1548 }
1549 case "WHO":
1550 if len(msg.Params) == 0 {
1551 // TODO: support WHO without parameters
1552 dc.SendMessage(&irc.Message{
1553 Prefix: dc.srv.prefix(),
1554 Command: irc.RPL_ENDOFWHO,
1555 Params: []string{dc.nick, "*", "End of /WHO list"},
1556 })
1557 return nil
1558 }
1559
1560 // TODO: support WHO masks
1561 entity := msg.Params[0]
1562 entityCM := casemapASCII(entity)
1563
1564 if dc.network == nil && entityCM == dc.nickCM {
1565 // TODO: support AWAY (H/G) in self WHO reply
1566 dc.SendMessage(&irc.Message{
1567 Prefix: dc.srv.prefix(),
1568 Command: irc.RPL_WHOREPLY,
1569 Params: []string{dc.nick, "*", dc.user.Username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
1570 })
1571 dc.SendMessage(&irc.Message{
1572 Prefix: dc.srv.prefix(),
1573 Command: irc.RPL_ENDOFWHO,
1574 Params: []string{dc.nick, dc.nick, "End of /WHO list"},
1575 })
1576 return nil
1577 }
1578 if entityCM == serviceNickCM {
1579 dc.SendMessage(&irc.Message{
1580 Prefix: dc.srv.prefix(),
1581 Command: irc.RPL_WHOREPLY,
1582 Params: []string{serviceNick, "*", servicePrefix.User, servicePrefix.Host, dc.srv.Hostname, serviceNick, "H", "0 " + serviceRealname},
1583 })
1584 dc.SendMessage(&irc.Message{
1585 Prefix: dc.srv.prefix(),
1586 Command: irc.RPL_ENDOFWHO,
1587 Params: []string{dc.nick, serviceNick, "End of /WHO list"},
1588 })
1589 return nil
1590 }
1591
1592 uc, upstreamName, err := dc.unmarshalEntity(entity)
1593 if err != nil {
1594 return err
1595 }
1596
1597 var params []string
1598 if len(msg.Params) == 2 {
1599 params = []string{upstreamName, msg.Params[1]}
1600 } else {
1601 params = []string{upstreamName}
1602 }
1603
1604 uc.SendMessageLabeled(dc.id, &irc.Message{
1605 Command: "WHO",
1606 Params: params,
1607 })
1608 case "WHOIS":
1609 if len(msg.Params) == 0 {
1610 return ircError{&irc.Message{
1611 Command: irc.ERR_NONICKNAMEGIVEN,
1612 Params: []string{dc.nick, "No nickname given"},
1613 }}
1614 }
1615
1616 var target, mask string
1617 if len(msg.Params) == 1 {
1618 target = ""
1619 mask = msg.Params[0]
1620 } else {
1621 target = msg.Params[0]
1622 mask = msg.Params[1]
1623 }
1624 // TODO: support multiple WHOIS users
1625 if i := strings.IndexByte(mask, ','); i >= 0 {
1626 mask = mask[:i]
1627 }
1628
1629 if dc.network == nil && casemapASCII(mask) == dc.nickCM {
1630 dc.SendMessage(&irc.Message{
1631 Prefix: dc.srv.prefix(),
1632 Command: irc.RPL_WHOISUSER,
1633 Params: []string{dc.nick, dc.nick, dc.user.Username, dc.hostname, "*", dc.realname},
1634 })
1635 dc.SendMessage(&irc.Message{
1636 Prefix: dc.srv.prefix(),
1637 Command: irc.RPL_WHOISSERVER,
1638 Params: []string{dc.nick, dc.nick, dc.srv.Hostname, "soju"},
1639 })
1640 dc.SendMessage(&irc.Message{
1641 Prefix: dc.srv.prefix(),
1642 Command: irc.RPL_ENDOFWHOIS,
1643 Params: []string{dc.nick, dc.nick, "End of /WHOIS list"},
1644 })
1645 return nil
1646 }
1647
1648 // TODO: support WHOIS masks
1649 uc, upstreamNick, err := dc.unmarshalEntity(mask)
1650 if err != nil {
1651 return err
1652 }
1653
1654 var params []string
1655 if target != "" {
1656 if target == mask { // WHOIS nick nick
1657 params = []string{upstreamNick, upstreamNick}
1658 } else {
1659 params = []string{target, upstreamNick}
1660 }
1661 } else {
1662 params = []string{upstreamNick}
1663 }
1664
1665 uc.SendMessageLabeled(dc.id, &irc.Message{
1666 Command: "WHOIS",
1667 Params: params,
1668 })
1669 case "PRIVMSG":
1670 var targetsStr, text string
1671 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1672 return err
1673 }
1674 tags := copyClientTags(msg.Tags)
1675
1676 for _, name := range strings.Split(targetsStr, ",") {
1677 if dc.network == nil && casemapASCII(name) == dc.nickCM {
1678 dc.SendMessage(msg)
1679 continue
1680 }
1681
1682 if casemapASCII(name) == serviceNickCM {
1683 if dc.caps["echo-message"] {
1684 echoTags := tags.Copy()
1685 echoTags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
1686 dc.SendMessage(&irc.Message{
1687 Tags: echoTags,
1688 Prefix: dc.prefix(),
1689 Command: "PRIVMSG",
1690 Params: []string{name, text},
1691 })
1692 }
1693 handleServicePRIVMSG(dc, text)
1694 continue
1695 }
1696
1697 uc, upstreamName, err := dc.unmarshalEntity(name)
1698 if err != nil {
1699 return err
1700 }
1701
1702 if uc.network.casemap(upstreamName) == "nickserv" {
1703 dc.handleNickServPRIVMSG(uc, text)
1704 }
1705
1706 unmarshaledText := text
1707 if uc.isChannel(upstreamName) {
1708 unmarshaledText = dc.unmarshalText(uc, text)
1709 }
1710 uc.SendMessageLabeled(dc.id, &irc.Message{
1711 Tags: tags,
1712 Command: "PRIVMSG",
1713 Params: []string{upstreamName, unmarshaledText},
1714 })
1715
1716 echoTags := tags.Copy()
1717 echoTags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
1718 echoMsg := &irc.Message{
1719 Tags: echoTags,
1720 Prefix: &irc.Prefix{
1721 Name: uc.nick,
1722 User: uc.username,
1723 },
1724 Command: "PRIVMSG",
1725 Params: []string{upstreamName, text},
1726 }
1727 uc.produce(upstreamName, echoMsg, dc)
1728
1729 uc.updateChannelAutoDetach(upstreamName)
1730 }
1731 case "NOTICE":
1732 var targetsStr, text string
1733 if err := parseMessageParams(msg, &targetsStr, &text); err != nil {
1734 return err
1735 }
1736 tags := copyClientTags(msg.Tags)
1737
1738 for _, name := range strings.Split(targetsStr, ",") {
1739 uc, upstreamName, err := dc.unmarshalEntity(name)
1740 if err != nil {
1741 return err
1742 }
1743
1744 unmarshaledText := text
1745 if uc.isChannel(upstreamName) {
1746 unmarshaledText = dc.unmarshalText(uc, text)
1747 }
1748 uc.SendMessageLabeled(dc.id, &irc.Message{
1749 Tags: tags,
1750 Command: "NOTICE",
1751 Params: []string{upstreamName, unmarshaledText},
1752 })
1753
1754 uc.updateChannelAutoDetach(upstreamName)
1755 }
1756 case "TAGMSG":
1757 var targetsStr string
1758 if err := parseMessageParams(msg, &targetsStr); err != nil {
1759 return err
1760 }
1761 tags := copyClientTags(msg.Tags)
1762
1763 for _, name := range strings.Split(targetsStr, ",") {
1764 uc, upstreamName, err := dc.unmarshalEntity(name)
1765 if err != nil {
1766 return err
1767 }
1768 if _, ok := uc.caps["message-tags"]; !ok {
1769 continue
1770 }
1771
1772 uc.SendMessageLabeled(dc.id, &irc.Message{
1773 Tags: tags,
1774 Command: "TAGMSG",
1775 Params: []string{upstreamName},
1776 })
1777
1778 uc.updateChannelAutoDetach(upstreamName)
1779 }
1780 case "INVITE":
1781 var user, channel string
1782 if err := parseMessageParams(msg, &user, &channel); err != nil {
1783 return err
1784 }
1785
1786 ucChannel, upstreamChannel, err := dc.unmarshalEntity(channel)
1787 if err != nil {
1788 return err
1789 }
1790
1791 ucUser, upstreamUser, err := dc.unmarshalEntity(user)
1792 if err != nil {
1793 return err
1794 }
1795
1796 if ucChannel != ucUser {
1797 return ircError{&irc.Message{
1798 Command: irc.ERR_USERNOTINCHANNEL,
1799 Params: []string{dc.nick, user, channel, "They are on another network"},
1800 }}
1801 }
1802 uc := ucChannel
1803
1804 uc.SendMessageLabeled(dc.id, &irc.Message{
1805 Command: "INVITE",
1806 Params: []string{upstreamUser, upstreamChannel},
1807 })
1808 case "CHATHISTORY":
1809 var subcommand string
1810 if err := parseMessageParams(msg, &subcommand); err != nil {
1811 return err
1812 }
1813 var target, limitStr string
1814 var boundsStr [2]string
1815 switch subcommand {
1816 case "AFTER", "BEFORE":
1817 if err := parseMessageParams(msg, nil, &target, &boundsStr[0], &limitStr); err != nil {
1818 return err
1819 }
1820 case "BETWEEN":
1821 if err := parseMessageParams(msg, nil, &target, &boundsStr[0], &boundsStr[1], &limitStr); err != nil {
1822 return err
1823 }
1824 default:
1825 // TODO: support LATEST, AROUND
1826 return ircError{&irc.Message{
1827 Command: "FAIL",
1828 Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, "Unknown command"},
1829 }}
1830 }
1831
1832 store, ok := dc.user.msgStore.(chatHistoryMessageStore)
1833 if !ok {
1834 return ircError{&irc.Message{
1835 Command: irc.ERR_UNKNOWNCOMMAND,
1836 Params: []string{dc.nick, "CHATHISTORY", "Unknown command"},
1837 }}
1838 }
1839
1840 uc, entity, err := dc.unmarshalEntity(target)
1841 if err != nil {
1842 return err
1843 }
1844 entity = uc.network.casemap(entity)
1845
1846 // TODO: support msgid criteria
1847 var bounds [2]time.Time
1848 bounds[0] = parseChatHistoryBound(boundsStr[0])
1849 if bounds[0].IsZero() {
1850 return ircError{&irc.Message{
1851 Command: "FAIL",
1852 Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, boundsStr[0], "Invalid first bound"},
1853 }}
1854 }
1855
1856 if boundsStr[1] != "" {
1857 bounds[1] = parseChatHistoryBound(boundsStr[1])
1858 if bounds[1].IsZero() {
1859 return ircError{&irc.Message{
1860 Command: "FAIL",
1861 Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, boundsStr[1], "Invalid second bound"},
1862 }}
1863 }
1864 }
1865
1866 limit, err := strconv.Atoi(limitStr)
1867 if err != nil || limit < 0 || limit > dc.srv.HistoryLimit {
1868 return ircError{&irc.Message{
1869 Command: "FAIL",
1870 Params: []string{"CHATHISTORY", "INVALID_PARAMS", subcommand, limitStr, "Invalid limit"},
1871 }}
1872 }
1873
1874 var history []*irc.Message
1875 switch subcommand {
1876 case "BEFORE":
1877 history, err = store.LoadBeforeTime(uc.network, entity, bounds[0], time.Time{}, limit)
1878 case "AFTER":
1879 history, err = store.LoadAfterTime(uc.network, entity, bounds[0], time.Now(), limit)
1880 case "BETWEEN":
1881 if bounds[0].Before(bounds[1]) {
1882 history, err = store.LoadAfterTime(uc.network, entity, bounds[0], bounds[1], limit)
1883 } else {
1884 history, err = store.LoadBeforeTime(uc.network, entity, bounds[0], bounds[1], limit)
1885 }
1886 }
1887 if err != nil {
1888 dc.logger.Printf("failed fetching %q messages for chathistory: %v", target, err)
1889 return newChatHistoryError(subcommand, target)
1890 }
1891
1892 batchRef := "history"
1893 dc.SendMessage(&irc.Message{
1894 Prefix: dc.srv.prefix(),
1895 Command: "BATCH",
1896 Params: []string{"+" + batchRef, "chathistory", target},
1897 })
1898
1899 for _, msg := range history {
1900 msg.Tags["batch"] = irc.TagValue(batchRef)
1901 dc.SendMessage(dc.marshalMessage(msg, uc.network))
1902 }
1903
1904 dc.SendMessage(&irc.Message{
1905 Prefix: dc.srv.prefix(),
1906 Command: "BATCH",
1907 Params: []string{"-" + batchRef},
1908 })
1909 default:
1910 dc.logger.Printf("unhandled message: %v", msg)
1911 return newUnknownCommandError(msg.Command)
1912 }
1913 return nil
1914}
1915
1916func (dc *downstreamConn) handleNickServPRIVMSG(uc *upstreamConn, text string) {
1917 username, password, ok := parseNickServCredentials(text, uc.nick)
1918 if !ok {
1919 return
1920 }
1921
1922 // User may have e.g. EXTERNAL mechanism configured. We do not want to
1923 // automatically erase the key pair or any other credentials.
1924 if uc.network.SASL.Mechanism != "" && uc.network.SASL.Mechanism != "PLAIN" {
1925 return
1926 }
1927
1928 dc.logger.Printf("auto-saving NickServ credentials with username %q", username)
1929 n := uc.network
1930 n.SASL.Mechanism = "PLAIN"
1931 n.SASL.Plain.Username = username
1932 n.SASL.Plain.Password = password
1933 if err := dc.srv.db.StoreNetwork(dc.user.ID, &n.Network); err != nil {
1934 dc.logger.Printf("failed to save NickServ credentials: %v", err)
1935 }
1936}
1937
1938func parseNickServCredentials(text, nick string) (username, password string, ok bool) {
1939 fields := strings.Fields(text)
1940 if len(fields) < 2 {
1941 return "", "", false
1942 }
1943 cmd := strings.ToUpper(fields[0])
1944 params := fields[1:]
1945 switch cmd {
1946 case "REGISTER":
1947 username = nick
1948 password = params[0]
1949 case "IDENTIFY":
1950 if len(params) == 1 {
1951 username = nick
1952 password = params[0]
1953 } else {
1954 username = params[0]
1955 password = params[1]
1956 }
1957 case "SET":
1958 if len(params) == 2 && strings.EqualFold(params[0], "PASSWORD") {
1959 username = nick
1960 password = params[1]
1961 }
1962 default:
1963 return "", "", false
1964 }
1965 return username, password, true
1966}
Note: See TracBrowser for help on using the repository browser.