source: code/trunk/downstream.go@ 265

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

Fix typo

File size: 31.5 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"
[91]11 "time"
[13]12
[112]13 "github.com/emersion/go-sasl"
[85]14 "golang.org/x/crypto/bcrypt"
[13]15 "gopkg.in/irc.v3"
16)
17
18type ircError struct {
19 Message *irc.Message
20}
21
[85]22func (err ircError) Error() string {
23 return err.Message.String()
24}
25
[13]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
[85]48var errAuthFailed = ircError{&irc.Message{
49 Command: irc.ERR_PASSWDMISMATCH,
50 Params: []string{"*", "Invalid username or password"},
51}}
[13]52
53type downstreamConn struct {
[210]54 conn
[22]55
[210]56 id uint64
57
[100]58 registered bool
59 user *user
60 nick string
61 rawUsername string
[168]62 networkName string
[183]63 clientName string
[100]64 realname string
[141]65 hostname string
[100]66 password string // empty after authentication
67 network *network // can be nil
[105]68
[108]69 negociatingCaps bool
70 capVersion int
[236]71 caps map[string]bool
[108]72
[112]73 saslServer sasl.Server
[13]74}
75
[154]76func newDownstreamConn(srv *Server, netConn net.Conn, id uint64) *downstreamConn {
[210]77 logger := &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())}
[55]78 dc := &downstreamConn{
[236]79 conn: *newConn(srv, netConn, logger),
80 id: id,
81 caps: make(map[string]bool),
[22]82 }
[141]83 dc.hostname = netConn.RemoteAddr().String()
84 if host, _, err := net.SplitHostPort(dc.hostname); err == nil {
85 dc.hostname = host
86 }
[55]87 return dc
[22]88}
89
[55]90func (dc *downstreamConn) prefix() *irc.Prefix {
[27]91 return &irc.Prefix{
[55]92 Name: dc.nick,
[184]93 User: dc.user.Username,
[141]94 Host: dc.hostname,
[27]95 }
96}
97
[90]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
[73]106func (dc *downstreamConn) forEachUpstream(f func(*upstreamConn)) {
107 dc.user.forEachUpstream(func(uc *upstreamConn) {
[77]108 if dc.network != nil && uc.network != dc.network {
[73]109 return
110 }
111 f(uc)
112 })
113}
114
[89]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 }
[136]121 return dc.network.upstream()
[89]122}
123
[260]124func isOurNick(net *network, nick string) bool {
125 // TODO: this doesn't account for nick changes
126 if net.conn != nil {
127 return nick == net.conn.nick
128 }
129 // We're not currently connected to the upstream connection, so we don't
130 // know whether this name is our nickname. Best-effort: use the network's
131 // configured nickname and hope it was the one being used when we were
132 // connected.
133 return nick == net.Nick
134}
135
[249]136// marshalEntity converts an upstream entity name (ie. channel or nick) into a
137// downstream entity name.
138//
139// This involves adding a "/<network>" suffix if the entity isn't the current
140// user.
[260]141func (dc *downstreamConn) marshalEntity(net *network, name string) string {
[257]142 if dc.network != nil {
[260]143 if dc.network != net {
[258]144 panic("soju: tried to marshal an entity for another network")
145 }
[257]146 return name
[119]147 }
[260]148 if isOurNick(net, name) {
[257]149 return dc.nick
150 }
[260]151 return name + "/" + net.GetName()
[119]152}
153
[260]154func (dc *downstreamConn) marshalUserPrefix(net *network, prefix *irc.Prefix) *irc.Prefix {
155 if isOurNick(net, prefix.Name) {
[257]156 return dc.prefix()
157 }
[130]158 if dc.network != nil {
[260]159 if dc.network != net {
[258]160 panic("soju: tried to marshal a user prefix for another network")
161 }
[257]162 return prefix
[119]163 }
[257]164 return &irc.Prefix{
[260]165 Name: prefix.Name + "/" + net.GetName(),
[257]166 User: prefix.User,
167 Host: prefix.Host,
168 }
[119]169}
170
[249]171// unmarshalEntity converts a downstream entity name (ie. channel or nick) into
172// an upstream entity name.
173//
174// This involves removing the "/<network>" suffix.
[127]175func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
[89]176 if uc := dc.upstream(); uc != nil {
177 return uc, name, nil
178 }
179
[127]180 var conn *upstreamConn
[119]181 if i := strings.LastIndexByte(name, '/'); i >= 0 {
[127]182 network := name[i+1:]
[119]183 name = name[:i]
184
185 dc.forEachUpstream(func(uc *upstreamConn) {
186 if network != uc.network.GetName() {
187 return
188 }
189 conn = uc
190 })
191 }
192
[127]193 if conn == nil {
[73]194 return nil, "", ircError{&irc.Message{
195 Command: irc.ERR_NOSUCHCHANNEL,
196 Params: []string{name, "No such channel"},
197 }}
[69]198 }
[127]199 return conn, name, nil
[69]200}
201
[165]202func (dc *downstreamConn) readMessages(ch chan<- event) error {
[22]203 for {
[210]204 msg, err := dc.ReadMessage()
[22]205 if err == io.EOF {
206 break
207 } else if err != nil {
208 return fmt.Errorf("failed to read IRC command: %v", err)
209 }
210
[165]211 ch <- eventDownstreamMessage{msg, dc}
[22]212 }
213
[45]214 return nil
[22]215}
216
[230]217// SendMessage sends an outgoing message.
218//
219// This can only called from the user goroutine.
[55]220func (dc *downstreamConn) SendMessage(msg *irc.Message) {
[230]221 if !dc.caps["message-tags"] {
[216]222 msg = msg.Copy()
223 for name := range msg.Tags {
224 supported := false
225 switch name {
226 case "time":
[230]227 supported = dc.caps["server-time"]
[216]228 }
229 if !supported {
230 delete(msg.Tags, name)
231 }
232 }
233 }
234
[210]235 dc.conn.SendMessage(msg)
[54]236}
237
[245]238// marshalMessage re-formats a message coming from an upstream connection so
239// that it's suitable for being sent on this downstream connection. Only
240// messages that may appear in logs are supported.
[261]241func (dc *downstreamConn) marshalMessage(msg *irc.Message, net *network) *irc.Message {
[227]242 msg = msg.Copy()
[261]243 msg.Prefix = dc.marshalUserPrefix(net, msg.Prefix)
[245]244
[227]245 switch msg.Command {
246 case "PRIVMSG", "NOTICE":
[261]247 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
[245]248 case "NICK":
249 // Nick change for another user
[261]250 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
[245]251 case "JOIN", "PART":
[261]252 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
[245]253 case "KICK":
[261]254 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
255 msg.Params[1] = dc.marshalEntity(net, msg.Params[1])
[245]256 case "TOPIC":
[261]257 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
[245]258 case "MODE":
[261]259 msg.Params[0] = dc.marshalEntity(net, msg.Params[0])
[245]260 case "QUIT":
[262]261 // This space is intentionally left blank
[227]262 default:
263 panic(fmt.Sprintf("unexpected %q message", msg.Command))
264 }
265
[245]266 return msg
[227]267}
268
[55]269func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]270 switch msg.Command {
[28]271 case "QUIT":
[55]272 return dc.Close()
[13]273 default:
[55]274 if dc.registered {
275 return dc.handleMessageRegistered(msg)
[13]276 } else {
[55]277 return dc.handleMessageUnregistered(msg)
[13]278 }
279 }
280}
281
[55]282func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]283 switch msg.Command {
284 case "NICK":
[117]285 var nick string
286 if err := parseMessageParams(msg, &nick); err != nil {
[43]287 return err
[13]288 }
[117]289 if nick == serviceNick {
290 return ircError{&irc.Message{
291 Command: irc.ERR_NICKNAMEINUSE,
292 Params: []string{dc.nick, nick, "Nickname reserved for bouncer service"},
293 }}
294 }
295 dc.nick = nick
[13]296 case "USER":
[117]297 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
[43]298 return err
[13]299 }
[85]300 case "PASS":
301 if err := parseMessageParams(msg, &dc.password); err != nil {
302 return err
303 }
[108]304 case "CAP":
305 var subCmd string
306 if err := parseMessageParams(msg, &subCmd); err != nil {
307 return err
308 }
309 if err := dc.handleCapCommand(subCmd, msg.Params[1:]); err != nil {
310 return err
311 }
[112]312 case "AUTHENTICATE":
[230]313 if !dc.caps["sasl"] {
[112]314 return ircError{&irc.Message{
[125]315 Command: irc.ERR_SASLFAIL,
[112]316 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
317 }}
318 }
319 if len(msg.Params) == 0 {
320 return ircError{&irc.Message{
[125]321 Command: irc.ERR_SASLFAIL,
[112]322 Params: []string{"*", "Missing AUTHENTICATE argument"},
323 }}
324 }
325 if dc.nick == "" {
326 return ircError{&irc.Message{
[125]327 Command: irc.ERR_SASLFAIL,
[112]328 Params: []string{"*", "Expected NICK command before AUTHENTICATE"},
329 }}
330 }
331
332 var resp []byte
333 if dc.saslServer == nil {
334 mech := strings.ToUpper(msg.Params[0])
335 switch mech {
336 case "PLAIN":
337 dc.saslServer = sasl.NewPlainServer(sasl.PlainAuthenticator(func(identity, username, password string) error {
338 return dc.authenticate(username, password)
339 }))
340 default:
341 return ircError{&irc.Message{
[125]342 Command: irc.ERR_SASLFAIL,
[112]343 Params: []string{"*", fmt.Sprintf("Unsupported SASL mechanism %q", mech)},
344 }}
345 }
346 } else if msg.Params[0] == "*" {
347 dc.saslServer = nil
348 return ircError{&irc.Message{
[125]349 Command: irc.ERR_SASLABORTED,
[112]350 Params: []string{"*", "SASL authentication aborted"},
351 }}
352 } else if msg.Params[0] == "+" {
353 resp = nil
354 } else {
355 // TODO: multi-line messages
356 var err error
357 resp, err = base64.StdEncoding.DecodeString(msg.Params[0])
358 if err != nil {
359 dc.saslServer = nil
360 return ircError{&irc.Message{
[125]361 Command: irc.ERR_SASLFAIL,
[112]362 Params: []string{"*", "Invalid base64-encoded response"},
363 }}
364 }
365 }
366
367 challenge, done, err := dc.saslServer.Next(resp)
368 if err != nil {
369 dc.saslServer = nil
370 if ircErr, ok := err.(ircError); ok && ircErr.Message.Command == irc.ERR_PASSWDMISMATCH {
371 return ircError{&irc.Message{
[125]372 Command: irc.ERR_SASLFAIL,
[112]373 Params: []string{"*", ircErr.Message.Params[1]},
374 }}
375 }
376 dc.SendMessage(&irc.Message{
377 Prefix: dc.srv.prefix(),
[125]378 Command: irc.ERR_SASLFAIL,
[112]379 Params: []string{"*", "SASL error"},
380 })
381 return fmt.Errorf("SASL authentication failed: %v", err)
382 } else if done {
383 dc.saslServer = nil
384 dc.SendMessage(&irc.Message{
385 Prefix: dc.srv.prefix(),
[125]386 Command: irc.RPL_LOGGEDIN,
[112]387 Params: []string{dc.nick, dc.nick, dc.user.Username, "You are now logged in"},
388 })
389 dc.SendMessage(&irc.Message{
390 Prefix: dc.srv.prefix(),
[125]391 Command: irc.RPL_SASLSUCCESS,
[112]392 Params: []string{dc.nick, "SASL authentication successful"},
393 })
394 } else {
395 challengeStr := "+"
[135]396 if len(challenge) > 0 {
[112]397 challengeStr = base64.StdEncoding.EncodeToString(challenge)
398 }
399
400 // TODO: multi-line messages
401 dc.SendMessage(&irc.Message{
402 Prefix: dc.srv.prefix(),
403 Command: "AUTHENTICATE",
404 Params: []string{challengeStr},
405 })
406 }
[13]407 default:
[55]408 dc.logger.Printf("unhandled message: %v", msg)
[13]409 return newUnknownCommandError(msg.Command)
410 }
[108]411 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
[55]412 return dc.register()
[13]413 }
414 return nil
415}
416
[108]417func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
[111]418 cmd = strings.ToUpper(cmd)
419
[108]420 replyTo := dc.nick
421 if !dc.registered {
422 replyTo = "*"
423 }
424
425 switch cmd {
426 case "LS":
427 if len(args) > 0 {
428 var err error
429 if dc.capVersion, err = strconv.Atoi(args[0]); err != nil {
430 return err
431 }
432 }
433
[256]434 caps := []string{"message-tags", "server-time", "echo-message", "batch"}
[194]435
[112]436 if dc.capVersion >= 302 {
[108]437 caps = append(caps, "sasl=PLAIN")
438 } else {
439 caps = append(caps, "sasl")
[112]440 }
[108]441
442 // TODO: multi-line replies
443 dc.SendMessage(&irc.Message{
444 Prefix: dc.srv.prefix(),
445 Command: "CAP",
446 Params: []string{replyTo, "LS", strings.Join(caps, " ")},
447 })
448
449 if !dc.registered {
450 dc.negociatingCaps = true
451 }
452 case "LIST":
453 var caps []string
454 for name := range dc.caps {
455 caps = append(caps, name)
456 }
457
458 // TODO: multi-line replies
459 dc.SendMessage(&irc.Message{
460 Prefix: dc.srv.prefix(),
461 Command: "CAP",
462 Params: []string{replyTo, "LIST", strings.Join(caps, " ")},
463 })
464 case "REQ":
465 if len(args) == 0 {
466 return ircError{&irc.Message{
467 Command: err_invalidcapcmd,
468 Params: []string{replyTo, cmd, "Missing argument in CAP REQ command"},
469 }}
470 }
471
472 caps := strings.Fields(args[0])
473 ack := true
474 for _, name := range caps {
475 name = strings.ToLower(name)
476 enable := !strings.HasPrefix(name, "-")
477 if !enable {
478 name = strings.TrimPrefix(name, "-")
479 }
480
481 enabled := dc.caps[name]
482 if enable == enabled {
483 continue
484 }
485
486 switch name {
[256]487 case "sasl", "message-tags", "server-time", "echo-message", "batch":
[112]488 dc.caps[name] = enable
[108]489 default:
490 ack = false
491 }
492 }
493
494 reply := "NAK"
495 if ack {
496 reply = "ACK"
497 }
498 dc.SendMessage(&irc.Message{
499 Prefix: dc.srv.prefix(),
500 Command: "CAP",
501 Params: []string{replyTo, reply, args[0]},
502 })
503 case "END":
504 dc.negociatingCaps = false
505 default:
506 return ircError{&irc.Message{
507 Command: err_invalidcapcmd,
508 Params: []string{replyTo, cmd, "Unknown CAP command"},
509 }}
510 }
511 return nil
512}
513
[91]514func sanityCheckServer(addr string) error {
515 dialer := net.Dialer{Timeout: 30 * time.Second}
516 conn, err := tls.DialWithDialer(&dialer, "tcp", addr, nil)
517 if err != nil {
518 return err
519 }
520 return conn.Close()
521}
522
[183]523func unmarshalUsername(rawUsername string) (username, client, network string) {
[112]524 username = rawUsername
[183]525
526 i := strings.IndexAny(username, "/@")
527 j := strings.LastIndexAny(username, "/@")
528 if i >= 0 {
529 username = rawUsername[:i]
[73]530 }
[183]531 if j >= 0 {
[190]532 if rawUsername[j] == '@' {
533 client = rawUsername[j+1:]
534 } else {
535 network = rawUsername[j+1:]
536 }
[73]537 }
[183]538 if i >= 0 && j >= 0 && i < j {
[190]539 if rawUsername[i] == '@' {
540 client = rawUsername[i+1 : j]
541 } else {
542 network = rawUsername[i+1 : j]
543 }
[183]544 }
545
546 return username, client, network
[112]547}
[73]548
[168]549func (dc *downstreamConn) authenticate(username, password string) error {
[183]550 username, clientName, networkName := unmarshalUsername(username)
[168]551
[173]552 u, err := dc.srv.db.GetUser(username)
553 if err != nil {
554 dc.logger.Printf("failed authentication for %q: %v", username, err)
[168]555 return errAuthFailed
556 }
557
[173]558 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
[168]559 if err != nil {
560 dc.logger.Printf("failed authentication for %q: %v", username, err)
561 return errAuthFailed
562 }
563
[173]564 dc.user = dc.srv.getUser(username)
565 if dc.user == nil {
566 dc.logger.Printf("failed authentication for %q: user not active", username)
567 return errAuthFailed
568 }
[183]569 dc.clientName = clientName
[168]570 dc.networkName = networkName
571 return nil
572}
573
574func (dc *downstreamConn) register() error {
575 if dc.registered {
576 return fmt.Errorf("tried to register twice")
577 }
578
579 password := dc.password
580 dc.password = ""
581 if dc.user == nil {
582 if err := dc.authenticate(dc.rawUsername, password); err != nil {
583 return err
584 }
585 }
586
[183]587 if dc.clientName == "" && dc.networkName == "" {
588 _, dc.clientName, dc.networkName = unmarshalUsername(dc.rawUsername)
[168]589 }
590
591 dc.registered = true
[184]592 dc.logger.Printf("registration complete for user %q", dc.user.Username)
[168]593 return nil
594}
595
596func (dc *downstreamConn) loadNetwork() error {
597 if dc.networkName == "" {
[112]598 return nil
599 }
[85]600
[168]601 network := dc.user.getNetwork(dc.networkName)
[112]602 if network == nil {
[168]603 addr := dc.networkName
[112]604 if !strings.ContainsRune(addr, ':') {
605 addr = addr + ":6697"
606 }
607
608 dc.logger.Printf("trying to connect to new network %q", addr)
609 if err := sanityCheckServer(addr); err != nil {
610 dc.logger.Printf("failed to connect to %q: %v", addr, err)
611 return ircError{&irc.Message{
612 Command: irc.ERR_PASSWDMISMATCH,
[168]613 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", dc.networkName)},
[112]614 }}
615 }
616
[168]617 dc.logger.Printf("auto-saving network %q", dc.networkName)
[112]618 var err error
[120]619 network, err = dc.user.createNetwork(&Network{
[168]620 Addr: dc.networkName,
[120]621 Nick: dc.nick,
622 })
[112]623 if err != nil {
624 return err
625 }
626 }
627
628 dc.network = network
629 return nil
630}
631
[168]632func (dc *downstreamConn) welcome() error {
633 if dc.user == nil || !dc.registered {
634 panic("tried to welcome an unregistered connection")
[37]635 }
636
[168]637 // TODO: doing this might take some time. We should do it in dc.register
638 // instead, but we'll potentially be adding a new network and this must be
639 // done in the user goroutine.
640 if err := dc.loadNetwork(); err != nil {
641 return err
[85]642 }
643
[55]644 dc.SendMessage(&irc.Message{
645 Prefix: dc.srv.prefix(),
[13]646 Command: irc.RPL_WELCOME,
[98]647 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
[54]648 })
[55]649 dc.SendMessage(&irc.Message{
650 Prefix: dc.srv.prefix(),
[13]651 Command: irc.RPL_YOURHOST,
[55]652 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]653 })
[55]654 dc.SendMessage(&irc.Message{
655 Prefix: dc.srv.prefix(),
[13]656 Command: irc.RPL_CREATED,
[55]657 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]658 })
[55]659 dc.SendMessage(&irc.Message{
660 Prefix: dc.srv.prefix(),
[13]661 Command: irc.RPL_MYINFO,
[98]662 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
[54]663 })
[93]664 // TODO: RPL_ISUPPORT
[55]665 dc.SendMessage(&irc.Message{
666 Prefix: dc.srv.prefix(),
[13]667 Command: irc.ERR_NOMOTD,
[55]668 Params: []string{dc.nick, "No MOTD"},
[54]669 })
[13]670
[73]671 dc.forEachUpstream(func(uc *upstreamConn) {
[30]672 for _, ch := range uc.channels {
673 if ch.complete {
[132]674 dc.SendMessage(&irc.Message{
675 Prefix: dc.prefix(),
676 Command: "JOIN",
[260]677 Params: []string{dc.marshalEntity(ch.conn.network, ch.Name)},
[132]678 })
679
[55]680 forwardChannel(dc, ch)
[30]681 }
682 }
[143]683 })
[50]684
[143]685 dc.forEachNetwork(func(net *network) {
[253]686 // Only send history if we're the first connected client with that name
687 // for the network
688 if _, ok := net.offlineClients[dc.clientName]; ok {
689 dc.sendNetworkHistory(net)
690 delete(net.offlineClients, dc.clientName)
[227]691 }
[253]692 })
[57]693
[253]694 return nil
695}
[144]696
[253]697func (dc *downstreamConn) sendNetworkHistory(net *network) {
698 for target, history := range net.history {
699 seq, ok := history.offlineClients[dc.clientName]
700 if !ok {
701 continue
702 }
703 delete(history.offlineClients, dc.clientName)
704
705 // If all clients have received history, no need to keep the
706 // ring buffer around
707 if len(history.offlineClients) == 0 {
708 delete(net.history, target)
709 }
710
711 consumer := history.ring.NewConsumer(seq)
712
[256]713 batchRef := "history"
714 if dc.caps["batch"] {
715 dc.SendMessage(&irc.Message{
716 Prefix: dc.srv.prefix(),
717 Command: "BATCH",
[260]718 Params: []string{"+" + batchRef, "chathistory", dc.marshalEntity(net, target)},
[256]719 })
720 }
721
[227]722 for {
[233]723 msg := consumer.Consume()
[227]724 if msg == nil {
725 break
[204]726 }
727
[245]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
[256]740 if dc.caps["batch"] {
741 msg = msg.Copy()
742 msg.Tags["batch"] = irc.TagValue(batchRef)
743 }
744
[261]745 dc.SendMessage(dc.marshalMessage(msg, net))
[227]746 }
[256]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 }
[253]755 }
[13]756}
757
[103]758func (dc *downstreamConn) runUntilRegistered() error {
759 for !dc.registered {
[212]760 msg, err := dc.ReadMessage()
[106]761 if err != nil {
[103]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
[55]777func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]778 switch msg.Command {
[111]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 }
[107]787 case "PING":
788 dc.SendMessage(&irc.Message{
789 Prefix: dc.srv.prefix(),
790 Command: "PONG",
791 Params: msg.Params,
792 })
793 return nil
[42]794 case "USER":
[13]795 return ircError{&irc.Message{
796 Command: irc.ERR_ALREADYREGISTERED,
[55]797 Params: []string{dc.nick, "You may not reregister"},
[13]798 }}
[42]799 case "NICK":
[90]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
[73]817 dc.forEachUpstream(func(uc *upstreamConn) {
[60]818 uc.SendMessage(msg)
[42]819 })
[146]820 case "JOIN":
821 var namesStr string
822 if err := parseMessageParams(msg, &namesStr); err != nil {
[48]823 return err
824 }
825
[146]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, ",") {
[145]832 uc, upstreamName, err := dc.unmarshalEntity(name)
833 if err != nil {
[158]834 return err
[145]835 }
[48]836
[146]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 }
[145]846 uc.SendMessage(&irc.Message{
[146]847 Command: "JOIN",
848 Params: params,
[145]849 })
[89]850
[222]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)
[89]854 }
855 }
[146]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 {
[158]870 return err
[146]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
[222]882 if err := uc.network.deleteChannel(upstreamName); err != nil {
883 dc.logger.Printf("failed to delete channel %q: %v", upstreamName, err)
[146]884 }
885 }
[159]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 }
[69]942 case "MODE":
[46]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
[139]953 if name == dc.nick {
[46]954 if modeStr != "" {
[73]955 dc.forEachUpstream(func(uc *upstreamConn) {
[69]956 uc.SendMessage(&irc.Message{
957 Command: "MODE",
958 Params: []string{uc.nick, modeStr},
959 })
[46]960 })
961 } else {
[55]962 dc.SendMessage(&irc.Message{
963 Prefix: dc.srv.prefix(),
[46]964 Command: irc.RPL_UMODEIS,
[129]965 Params: []string{dc.nick, ""}, // TODO
[54]966 })
[46]967 }
[139]968 return nil
[46]969 }
[139]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 })
[162]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 }
[139]1021 }
[160]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 }
[177]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 }
[181]1083 uc.trySendLIST(dc.id)
[177]1084 })
[140]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
[176]1107 uc.SendMessageLabeled(dc.id, &irc.Message{
[140]1108 Command: "NAMES",
1109 Params: []string{upstreamChannel},
1110 })
1111 }
1112 }
[127]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,
[140]1119 Params: []string{dc.nick, "*", "End of /WHO list"},
[127]1120 })
1121 return nil
1122 }
1123
1124 // TODO: support WHO masks
1125 entity := msg.Params[0]
1126
[142]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,
[184]1132 Params: []string{dc.nick, "*", dc.user.Username, dc.hostname, dc.srv.Hostname, dc.nick, "H", "0 " + dc.realname},
[142]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
[127]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
[176]1154 uc.SendMessageLabeled(dc.id, &irc.Message{
[127]1155 Command: "WHO",
1156 Params: params,
1157 })
[128]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
[142]1179 if mask == dc.nick {
1180 dc.SendMessage(&irc.Message{
1181 Prefix: dc.srv.prefix(),
1182 Command: irc.RPL_WHOISUSER,
[184]1183 Params: []string{dc.nick, dc.nick, dc.user.Username, dc.hostname, "*", dc.realname},
[142]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
[128]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
[176]1211 uc.SendMessageLabeled(dc.id, &irc.Message{
[128]1212 Command: "WHOIS",
1213 Params: params,
1214 })
[58]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, ",") {
[117]1222 if name == serviceNick {
1223 handleServicePRIVMSG(dc, text)
1224 continue
1225 }
1226
[127]1227 uc, upstreamName, err := dc.unmarshalEntity(name)
[58]1228 if err != nil {
1229 return err
1230 }
1231
[95]1232 if upstreamName == "NickServ" {
1233 dc.handleNickServPRIVMSG(uc, text)
1234 }
1235
[69]1236 uc.SendMessage(&irc.Message{
[58]1237 Command: "PRIVMSG",
[69]1238 Params: []string{upstreamName, text},
[60]1239 })
[105]1240
[113]1241 echoMsg := &irc.Message{
1242 Prefix: &irc.Prefix{
1243 Name: uc.nick,
1244 User: uc.username,
1245 },
[114]1246 Command: "PRIVMSG",
[113]1247 Params: []string{upstreamName, text},
1248 }
[105]1249
[239]1250 uc.produce(upstreamName, echoMsg, dc)
[58]1251 }
[164]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 }
[163]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
[176]1293 uc.SendMessageLabeled(dc.id, &irc.Message{
[163]1294 Command: "INVITE",
1295 Params: []string{upstreamUser, upstreamChannel},
1296 })
[13]1297 default:
[55]1298 dc.logger.Printf("unhandled message: %v", msg)
[13]1299 return newUnknownCommandError(msg.Command)
1300 }
[42]1301 return nil
[13]1302}
[95]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
[182]1334 password = params[0]
[95]1335 } else {
1336 username = params[0]
[182]1337 password = params[1]
[95]1338 }
[182]1339 case "SET":
1340 if len(params) == 2 && strings.EqualFold(params[0], "PASSWORD") {
1341 username = nick
1342 password = params[1]
1343 }
[95]1344 }
1345 return username, password, true
1346}
Note: See TracBrowser for help on using the repository browser.