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
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
[249]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.
[257]129func (dc *downstreamConn) marshalEntity(uc *upstreamConn, name string) string {
130 if dc.network != nil {
131 return name
[119]132 }
[257]133 if name == uc.nick {
134 return dc.nick
135 }
136 return name + "/" + uc.network.GetName()
[119]137}
138
139func (dc *downstreamConn) marshalChannel(uc *upstreamConn, name string) string {
[257]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 }
[130]151 if dc.network != nil {
[257]152 return prefix
[119]153 }
[257]154 return &irc.Prefix{
155 Name: prefix.Name + "/" + uc.network.GetName(),
156 User: prefix.User,
157 Host: prefix.Host,
158 }
[119]159}
160
[249]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.
[127]165func (dc *downstreamConn) unmarshalEntity(name string) (*upstreamConn, string, error) {
[89]166 if uc := dc.upstream(); uc != nil {
167 return uc, name, nil
168 }
169
[127]170 var conn *upstreamConn
[119]171 if i := strings.LastIndexByte(name, '/'); i >= 0 {
[127]172 network := name[i+1:]
[119]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
[127]183 if conn == nil {
[73]184 return nil, "", ircError{&irc.Message{
185 Command: irc.ERR_NOSUCHCHANNEL,
186 Params: []string{name, "No such channel"},
187 }}
[69]188 }
[127]189 return conn, name, nil
[69]190}
191
[165]192func (dc *downstreamConn) readMessages(ch chan<- event) error {
[22]193 for {
[210]194 msg, err := dc.ReadMessage()
[22]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
[165]201 ch <- eventDownstreamMessage{msg, dc}
[22]202 }
203
[45]204 return nil
[22]205}
206
[230]207// SendMessage sends an outgoing message.
208//
209// This can only called from the user goroutine.
[55]210func (dc *downstreamConn) SendMessage(msg *irc.Message) {
[230]211 if !dc.caps["message-tags"] {
[216]212 msg = msg.Copy()
213 for name := range msg.Tags {
214 supported := false
215 switch name {
216 case "time":
[230]217 supported = dc.caps["server-time"]
[216]218 }
219 if !supported {
220 delete(msg.Tags, name)
221 }
222 }
223 }
224
[210]225 dc.conn.SendMessage(msg)
[54]226}
227
[245]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 {
[227]232 msg = msg.Copy()
[245]233 msg.Prefix = dc.marshalUserPrefix(uc, msg.Prefix)
234
[227]235 switch msg.Command {
236 case "PRIVMSG", "NOTICE":
237 msg.Params[0] = dc.marshalEntity(uc, msg.Params[0])
[245]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
[227]252 default:
253 panic(fmt.Sprintf("unexpected %q message", msg.Command))
254 }
255
[245]256 return msg
[227]257}
258
[55]259func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]260 switch msg.Command {
[28]261 case "QUIT":
[55]262 return dc.Close()
[13]263 default:
[55]264 if dc.registered {
265 return dc.handleMessageRegistered(msg)
[13]266 } else {
[55]267 return dc.handleMessageUnregistered(msg)
[13]268 }
269 }
270}
271
[55]272func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]273 switch msg.Command {
274 case "NICK":
[117]275 var nick string
276 if err := parseMessageParams(msg, &nick); err != nil {
[43]277 return err
[13]278 }
[117]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
[13]286 case "USER":
[117]287 if err := parseMessageParams(msg, &dc.rawUsername, nil, nil, &dc.realname); err != nil {
[43]288 return err
[13]289 }
[85]290 case "PASS":
291 if err := parseMessageParams(msg, &dc.password); err != nil {
292 return err
293 }
[108]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 }
[112]302 case "AUTHENTICATE":
[230]303 if !dc.caps["sasl"] {
[112]304 return ircError{&irc.Message{
[125]305 Command: irc.ERR_SASLFAIL,
[112]306 Params: []string{"*", "AUTHENTICATE requires the \"sasl\" capability to be enabled"},
307 }}
308 }
309 if len(msg.Params) == 0 {
310 return ircError{&irc.Message{
[125]311 Command: irc.ERR_SASLFAIL,
[112]312 Params: []string{"*", "Missing AUTHENTICATE argument"},
313 }}
314 }
315 if dc.nick == "" {
316 return ircError{&irc.Message{
[125]317 Command: irc.ERR_SASLFAIL,
[112]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{
[125]332 Command: irc.ERR_SASLFAIL,
[112]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{
[125]339 Command: irc.ERR_SASLABORTED,
[112]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{
[125]351 Command: irc.ERR_SASLFAIL,
[112]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{
[125]362 Command: irc.ERR_SASLFAIL,
[112]363 Params: []string{"*", ircErr.Message.Params[1]},
364 }}
365 }
366 dc.SendMessage(&irc.Message{
367 Prefix: dc.srv.prefix(),
[125]368 Command: irc.ERR_SASLFAIL,
[112]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(),
[125]376 Command: irc.RPL_LOGGEDIN,
[112]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(),
[125]381 Command: irc.RPL_SASLSUCCESS,
[112]382 Params: []string{dc.nick, "SASL authentication successful"},
383 })
384 } else {
385 challengeStr := "+"
[135]386 if len(challenge) > 0 {
[112]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 }
[13]397 default:
[55]398 dc.logger.Printf("unhandled message: %v", msg)
[13]399 return newUnknownCommandError(msg.Command)
400 }
[108]401 if dc.rawUsername != "" && dc.nick != "" && !dc.negociatingCaps {
[55]402 return dc.register()
[13]403 }
404 return nil
405}
406
[108]407func (dc *downstreamConn) handleCapCommand(cmd string, args []string) error {
[111]408 cmd = strings.ToUpper(cmd)
409
[108]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
[256]424 caps := []string{"message-tags", "server-time", "echo-message", "batch"}
[194]425
[112]426 if dc.capVersion >= 302 {
[108]427 caps = append(caps, "sasl=PLAIN")
428 } else {
429 caps = append(caps, "sasl")
[112]430 }
[108]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 {
[256]477 case "sasl", "message-tags", "server-time", "echo-message", "batch":
[112]478 dc.caps[name] = enable
[108]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
[91]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
[183]513func unmarshalUsername(rawUsername string) (username, client, network string) {
[112]514 username = rawUsername
[183]515
516 i := strings.IndexAny(username, "/@")
517 j := strings.LastIndexAny(username, "/@")
518 if i >= 0 {
519 username = rawUsername[:i]
[73]520 }
[183]521 if j >= 0 {
[190]522 if rawUsername[j] == '@' {
523 client = rawUsername[j+1:]
524 } else {
525 network = rawUsername[j+1:]
526 }
[73]527 }
[183]528 if i >= 0 && j >= 0 && i < j {
[190]529 if rawUsername[i] == '@' {
530 client = rawUsername[i+1 : j]
531 } else {
532 network = rawUsername[i+1 : j]
533 }
[183]534 }
535
536 return username, client, network
[112]537}
[73]538
[168]539func (dc *downstreamConn) authenticate(username, password string) error {
[183]540 username, clientName, networkName := unmarshalUsername(username)
[168]541
[173]542 u, err := dc.srv.db.GetUser(username)
543 if err != nil {
544 dc.logger.Printf("failed authentication for %q: %v", username, err)
[168]545 return errAuthFailed
546 }
547
[173]548 err = bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
[168]549 if err != nil {
550 dc.logger.Printf("failed authentication for %q: %v", username, err)
551 return errAuthFailed
552 }
553
[173]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 }
[183]559 dc.clientName = clientName
[168]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
[183]577 if dc.clientName == "" && dc.networkName == "" {
578 _, dc.clientName, dc.networkName = unmarshalUsername(dc.rawUsername)
[168]579 }
580
581 dc.registered = true
[184]582 dc.logger.Printf("registration complete for user %q", dc.user.Username)
[168]583 return nil
584}
585
586func (dc *downstreamConn) loadNetwork() error {
587 if dc.networkName == "" {
[112]588 return nil
589 }
[85]590
[168]591 network := dc.user.getNetwork(dc.networkName)
[112]592 if network == nil {
[168]593 addr := dc.networkName
[112]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,
[168]603 Params: []string{"*", fmt.Sprintf("Failed to connect to %q", dc.networkName)},
[112]604 }}
605 }
606
[168]607 dc.logger.Printf("auto-saving network %q", dc.networkName)
[112]608 var err error
[120]609 network, err = dc.user.createNetwork(&Network{
[168]610 Addr: dc.networkName,
[120]611 Nick: dc.nick,
612 })
[112]613 if err != nil {
614 return err
615 }
616 }
617
618 dc.network = network
619 return nil
620}
621
[168]622func (dc *downstreamConn) welcome() error {
623 if dc.user == nil || !dc.registered {
624 panic("tried to welcome an unregistered connection")
[37]625 }
626
[168]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
[85]632 }
633
[55]634 dc.SendMessage(&irc.Message{
635 Prefix: dc.srv.prefix(),
[13]636 Command: irc.RPL_WELCOME,
[98]637 Params: []string{dc.nick, "Welcome to soju, " + dc.nick},
[54]638 })
[55]639 dc.SendMessage(&irc.Message{
640 Prefix: dc.srv.prefix(),
[13]641 Command: irc.RPL_YOURHOST,
[55]642 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]643 })
[55]644 dc.SendMessage(&irc.Message{
645 Prefix: dc.srv.prefix(),
[13]646 Command: irc.RPL_CREATED,
[55]647 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]648 })
[55]649 dc.SendMessage(&irc.Message{
650 Prefix: dc.srv.prefix(),
[13]651 Command: irc.RPL_MYINFO,
[98]652 Params: []string{dc.nick, dc.srv.Hostname, "soju", "aiwroO", "OovaimnqpsrtklbeI"},
[54]653 })
[93]654 // TODO: RPL_ISUPPORT
[55]655 dc.SendMessage(&irc.Message{
656 Prefix: dc.srv.prefix(),
[13]657 Command: irc.ERR_NOMOTD,
[55]658 Params: []string{dc.nick, "No MOTD"},
[54]659 })
[13]660
[73]661 dc.forEachUpstream(func(uc *upstreamConn) {
[30]662 for _, ch := range uc.channels {
663 if ch.complete {
[132]664 dc.SendMessage(&irc.Message{
665 Prefix: dc.prefix(),
666 Command: "JOIN",
667 Params: []string{dc.marshalChannel(ch.conn, ch.Name)},
668 })
669
[55]670 forwardChannel(dc, ch)
[30]671 }
672 }
[143]673 })
[50]674
[143]675 dc.forEachNetwork(func(net *network) {
[253]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)
[227]681 }
[253]682 })
[57]683
[253]684 return nil
685}
[144]686
[253]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
[227]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
[50]711 }
[57]712
[256]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
[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
[245]745 dc.SendMessage(dc.marshalMessage(msg, uc))
[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.