2020-02-25 15:51:22 +00:00
|
|
|
package irc
|
2019-08-20 00:50:40 +00:00
|
|
|
|
|
|
|
import (
|
2019-08-20 02:15:56 +00:00
|
|
|
"bytes"
|
2019-08-21 08:15:45 +00:00
|
|
|
"fmt"
|
2019-08-20 02:15:56 +00:00
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/sirupsen/logrus"
|
2019-08-20 00:50:40 +00:00
|
|
|
"net"
|
2019-08-21 06:15:12 +00:00
|
|
|
"strings"
|
2019-08-20 00:50:40 +00:00
|
|
|
)
|
|
|
|
|
2019-08-21 08:15:45 +00:00
|
|
|
func newIrcClient(conn net.Conn, log *logrus.Logger, mc chan *ircMessage, s *ircd) *ircClient {
|
2019-08-20 02:15:56 +00:00
|
|
|
c := new(ircClient)
|
|
|
|
c.Id = uuid.New().String()
|
|
|
|
c.conn = conn
|
|
|
|
c.log = log
|
|
|
|
c.mc = mc
|
2019-08-21 06:15:12 +00:00
|
|
|
c.server = s
|
2019-08-20 02:15:56 +00:00
|
|
|
c.inputBytes = new(bytes.Buffer)
|
|
|
|
c.outputBytes = new(bytes.Buffer)
|
2019-08-21 06:15:12 +00:00
|
|
|
c.session = NewIrcUserSession()
|
2019-08-20 02:15:56 +00:00
|
|
|
return c
|
2019-08-20 00:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 02:15:56 +00:00
|
|
|
type ircClient struct {
|
2019-08-21 08:15:45 +00:00
|
|
|
//FIXME add a mutex and protect during writes
|
2019-08-20 02:15:56 +00:00
|
|
|
Id string
|
2019-08-20 00:50:40 +00:00
|
|
|
conn net.Conn
|
2019-08-20 02:15:56 +00:00
|
|
|
log *logrus.Logger
|
|
|
|
session *ircUserSession
|
2019-08-20 00:50:40 +00:00
|
|
|
inputBytes *bytes.Buffer
|
|
|
|
outputBytes *bytes.Buffer
|
2019-08-20 02:15:56 +00:00
|
|
|
mc chan *ircMessage
|
2019-08-21 08:15:45 +00:00
|
|
|
server *ircd
|
2019-08-20 00:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-08-21 06:15:12 +00:00
|
|
|
func (c *ircClient) ServerName() string {
|
|
|
|
return c.server.serverName
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) Kill() {
|
|
|
|
//server will remove it from the server-managed client list
|
|
|
|
//and will call c.CleanupAndClose() when it pops from this channel
|
|
|
|
c.server.deadClients <- c
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) CleanupAndClose() {
|
2019-08-20 02:15:56 +00:00
|
|
|
//client cleanup
|
2019-08-21 06:15:12 +00:00
|
|
|
//FIXME truncate client buffers and whatnot here
|
|
|
|
// server object already handles removing it from the list of clients
|
|
|
|
// for the server, so don't do it here
|
|
|
|
c.log.Infof("client=%s cleanup", c.Id)
|
|
|
|
c.conn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) AppendStringToOutputBuffer(input string) {
|
2019-08-21 08:15:45 +00:00
|
|
|
// woo boilerplate because no polymorphism
|
|
|
|
c.AppendBytesToOutputBuffer([]byte(input))
|
2019-08-21 06:15:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) AppendBytesToOutputBuffer(input []byte) {
|
|
|
|
c.outputBytes.Write(input)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) FlushOutputBuffer() bool {
|
|
|
|
numBytes, err := c.outputBytes.WriteTo(c.conn)
|
|
|
|
if err != nil {
|
|
|
|
c.log.Debugf("failed to write completely to client<%s>, marking dead", c.Id)
|
|
|
|
c.Kill()
|
2019-08-21 08:15:45 +00:00
|
|
|
return false
|
2019-08-21 06:15:12 +00:00
|
|
|
} else {
|
|
|
|
c.log.Debugf("wrote %d bytes to client<%s>", numBytes, c.Id)
|
2019-08-21 08:15:45 +00:00
|
|
|
return true
|
2019-08-21 06:15:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) RespUnknownCommand(command string) {
|
|
|
|
//421 == unknown command
|
|
|
|
c.SendServerMessage(421, []string{command, "Unknown command. Please try your call again later."})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) SendServerMessage(code uint16, params []string) bool {
|
|
|
|
//FIXME this is terrible hacky duct tape code, redo it
|
|
|
|
p := ""
|
|
|
|
if len(params) > 0 {
|
|
|
|
// pop off last item for separate processing
|
|
|
|
lastelem := params[len(params)-1]
|
|
|
|
params = params[:len(params)-1]
|
|
|
|
|
|
|
|
for _, elem := range params {
|
|
|
|
if strings.ContainsAny(elem, " \t\r\n") {
|
|
|
|
c.log.Errorf("wtf %s", elem)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.ContainsAny(lastelem, "\t\r\n") {
|
|
|
|
c.log.Errorf("wtf %s", lastelem)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.ContainsAny(lastelem, " ") {
|
|
|
|
lastelem = fmt.Sprintf(":%s", lastelem)
|
|
|
|
}
|
|
|
|
p = strings.Join(params, " ")
|
|
|
|
if len(p) > 0 {
|
|
|
|
p = " " + lastelem
|
|
|
|
} else {
|
|
|
|
p = lastelem
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-25 15:51:22 +00:00
|
|
|
sendstr := fmt.Sprintf(":%s %d %s", c.ServerName(), code, c.session.nick)
|
2019-08-21 06:15:12 +00:00
|
|
|
if len(p) > 0 {
|
|
|
|
sendstr = sendstr + " " + p
|
|
|
|
}
|
|
|
|
sendstr = sendstr + "\r\n"
|
|
|
|
c.log.Debugf("sending string '%s' to client<%s>", sendstr, c.Id)
|
|
|
|
c.AppendStringToOutputBuffer(sendstr)
|
|
|
|
return c.FlushOutputBuffer()
|
2019-08-20 02:15:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) AppendInputBuffer(input []byte) {
|
2019-08-20 00:50:40 +00:00
|
|
|
// Read the incoming connection into the buffer.
|
2019-08-20 02:15:56 +00:00
|
|
|
c.log.Printf("conn<%s>: got %d bytes from net", c.Id, len(input))
|
|
|
|
c.inputBytes.Write(input)
|
2019-08-20 03:48:43 +00:00
|
|
|
c.log.Debugf("conn<%s> buffer now %d bytes total", c.Id, c.inputBytes.Len())
|
|
|
|
c.ParseInputBuffer()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *ircClient) ParseInputBuffer() {
|
2019-08-21 08:15:45 +00:00
|
|
|
//FIXME update a timer here on the client when a line is parsed
|
|
|
|
//successfully and time them out after a while
|
2019-08-20 03:48:43 +00:00
|
|
|
c.log.Debugf("my input buffer is %d bytes", c.inputBytes.Len())
|
|
|
|
c.log.Debugf("my input buffer is: '%s'", c.inputBytes.String())
|
2019-08-21 06:15:12 +00:00
|
|
|
line, err := c.inputBytes.ReadString(byte('\n'))
|
|
|
|
if err == nil {
|
|
|
|
c.mc <- c.ParseSingleInput(line)
|
|
|
|
c.ParseInputBuffer()
|
|
|
|
} else {
|
2019-08-21 08:15:45 +00:00
|
|
|
// error parsing input buffer, probably don't have a full line yet
|
|
|
|
return
|
2019-08-21 06:15:12 +00:00
|
|
|
}
|
2019-08-20 03:48:43 +00:00
|
|
|
c.log.Debugf("my input buffer is %d bytes", c.inputBytes.Len())
|
|
|
|
c.log.Debugf("my input buffer is: '%s'", c.inputBytes.String())
|
2019-08-20 00:50:40 +00:00
|
|
|
}
|
|
|
|
|
2019-08-20 03:48:43 +00:00
|
|
|
func (c *ircClient) ParseSingleInput(line string) *ircMessage {
|
2019-08-21 06:15:12 +00:00
|
|
|
return NewIrcMessageFromString(line, c)
|
2019-08-20 00:50:40 +00:00
|
|
|
}
|