ircd/irc/client.go

243 lines
4.5 KiB
Go
Raw Permalink Normal View History

2016-07-17 21:52:03 +00:00
// vim:ts=4:sts=4:sw=4:noet:tw=72
2021-05-19 07:41:47 +00:00
package irc
2014-11-22 13:21:30 +00:00
import (
2017-08-22 05:23:30 +00:00
"bytes"
2014-11-22 13:21:30 +00:00
"fmt"
"io"
"net"
"strings"
"time"
2021-05-19 07:41:47 +00:00
"git.dnix.de/an/xlog"
2016-07-17 21:52:03 +00:00
)
2014-11-22 13:21:30 +00:00
2016-07-18 19:22:03 +00:00
type Client interface {
Name() string
Password() string
2016-07-21 08:30:44 +00:00
Register(bool)
2021-05-19 07:41:47 +00:00
Send(*Message)
Receive(*Message)
2016-07-18 19:22:03 +00:00
Destroy()
}
type RemoteClient struct {
2014-11-22 13:21:30 +00:00
server *Server
name string
password string
modes string
isSSL bool
isRegistered bool
isAuthed bool
isClosed bool
2021-05-19 07:41:47 +00:00
receive chan *Message
2016-07-21 08:30:44 +00:00
registered chan bool
2014-11-22 13:21:30 +00:00
2016-07-17 21:52:03 +00:00
conn net.Conn
writeq chan string
2014-11-22 13:21:30 +00:00
}
2016-07-20 16:17:13 +00:00
func NewRemoteClient(sv *Server, conn net.Conn) *RemoteClient {
2016-07-18 19:22:03 +00:00
cl := new(RemoteClient)
2016-07-21 08:30:44 +00:00
2016-07-20 16:17:13 +00:00
cl.server = sv
2014-11-22 13:21:30 +00:00
cl.name = ""
cl.password = ""
cl.modes = ""
2021-05-19 07:41:47 +00:00
cl.receive = make(chan *Message, 1024)
2016-07-21 08:30:44 +00:00
cl.registered = make(chan bool)
2014-11-22 13:21:30 +00:00
cl.isRegistered = false
cl.isAuthed = false
cl.isClosed = false
cl.conn = conn
cl.writeq = make(chan string, 1024)
2014-11-22 13:21:30 +00:00
go cl.connReader()
go cl.connWriter()
2016-07-21 22:00:35 +00:00
go cl.dispatcher()
2014-11-22 13:21:30 +00:00
2016-07-18 19:22:03 +00:00
xlog.Info("RemoteClient connected")
2014-11-22 13:21:30 +00:00
return cl
}
2016-07-18 19:22:03 +00:00
func (cl *RemoteClient) Name() string {
2014-11-22 13:21:30 +00:00
return cl.name
}
func (cl *RemoteClient) Password() string {
return cl.password
}
2016-07-21 08:30:44 +00:00
func (cl *RemoteClient) Register(success bool) {
cl.registered <- success
}
2021-05-19 07:41:47 +00:00
func (cl *RemoteClient) Send(msg *Message) {
2016-07-17 21:52:03 +00:00
msg.Pre = cl.name
2016-07-21 22:00:35 +00:00
cl.server.Dispatch(msg)
2014-11-22 13:21:30 +00:00
}
2021-05-19 07:41:47 +00:00
func (cl *RemoteClient) Receive(msg *Message) {
2014-11-22 13:21:30 +00:00
cl.receive <- msg
}
2016-07-18 19:22:03 +00:00
func (cl *RemoteClient) AddMode(mode string) {
2014-11-22 13:21:30 +00:00
cl.modes = cl.modes + mode
}
2016-07-18 19:22:03 +00:00
func (cl *RemoteClient) DelMode(mode string) {
2014-11-22 13:21:30 +00:00
cl.modes = strings.Replace(cl.modes, mode, "", -1)
}
2016-07-18 19:22:03 +00:00
func (cl *RemoteClient) HasMode(mode string) bool {
2014-11-22 13:21:30 +00:00
return strings.IndexRune(cl.modes, rune(mode[0])) != -1
}
2016-07-18 19:22:03 +00:00
func (cl *RemoteClient) Destroy() {
if cl.isClosed {
return
}
cl.isClosed = true
close(cl.writeq)
cl.conn.Write([]byte("ERROR :Closing link"))
cl.conn.Close()
if cl.name != "" {
xlog.Info("RemoteClient '%s' disconnected", cl.name)
} else {
xlog.Info("RemoteClient disconnected")
}
2014-11-22 13:21:30 +00:00
}
2016-07-21 22:00:35 +00:00
func (cl *RemoteClient) dispatcher() {
2014-11-22 13:21:30 +00:00
for {
2017-08-22 05:23:30 +00:00
time.Sleep(10 * time.Millisecond)
2014-11-22 13:21:30 +00:00
if cl.isClosed {
return
}
select {
case msg := <-cl.receive:
cl.writeMsg(msg)
2016-07-21 08:30:44 +00:00
case success := <-cl.registered:
if !success {
2016-07-22 13:56:50 +00:00
cl.name = ""
2016-07-21 08:30:44 +00:00
cl.Destroy()
xlog.Debug("User registration failed: '%s'", cl.name)
return
}
xlog.Debug("User '%s' registered", cl.name)
2014-11-22 13:21:30 +00:00
default:
continue
}
}
}
2017-08-22 05:23:30 +00:00
/*func (cl *RemoteClient) pinger() {
for {
time.Sleep(30 * time.Second)
if cl.isClosed {
return
}
cl.writeLine("PING %s", cl.server.host)
}
}*/
2016-07-18 19:22:03 +00:00
func (cl *RemoteClient) connReader() {
2014-11-22 13:21:30 +00:00
for {
2017-08-22 05:23:30 +00:00
time.Sleep(10 * time.Millisecond)
buf := make([]byte, 4096)
2016-07-18 12:26:46 +00:00
if cl.isClosed {
xlog.Debug("connReader: exiting")
return
}
2017-08-22 05:23:30 +00:00
cl.conn.SetReadDeadline(time.Now().Add(time.Second * 5))
n, err := cl.conn.Read(buf)
2014-11-22 13:21:30 +00:00
if err == io.EOF {
2016-07-18 12:26:46 +00:00
xlog.Info("connReader: Connection closed by peer")
2016-07-21 22:00:35 +00:00
cl.server.DelClient(cl)
2014-11-22 13:21:30 +00:00
return
}
if err != nil {
2017-08-22 05:23:30 +00:00
continue
}
raw := buf[:n]
raw = bytes.Replace(raw, []byte("\r\n"), []byte("\n"), -1)
raw = bytes.Replace(raw, []byte("\r"), []byte("\n"), -1)
lines := bytes.Split(raw, []byte("\n"))
for _, line := range lines {
if len(line) > 0 {
cl.handleCmd(string(line))
}
2014-11-22 13:21:30 +00:00
}
}
}
2016-07-18 19:22:03 +00:00
func (cl *RemoteClient) connWriter() {
2014-11-22 13:21:30 +00:00
for line := range cl.writeq {
2017-08-22 05:23:30 +00:00
time.Sleep(10 * time.Millisecond)
if cl.isClosed {
return
}
2014-11-22 13:21:30 +00:00
written := 0
bytes := []byte(line + "\r\n")
for written < len(line) {
2017-08-22 05:23:30 +00:00
cl.conn.SetWriteDeadline(time.Now().Add(time.Second * 5))
2014-11-22 13:21:30 +00:00
n, err := cl.conn.Write(bytes[written:])
if err == io.EOF {
2016-07-18 12:26:46 +00:00
xlog.Info("connWriter: Connection closed by peer")
2016-07-21 22:00:35 +00:00
cl.server.DelClient(cl)
2014-11-22 13:21:30 +00:00
return
} else if err != nil {
2016-07-18 12:26:46 +00:00
xlog.Error("connWriter: %s", err.Error())
2016-07-21 22:00:35 +00:00
cl.server.DelClient(cl)
2014-11-22 13:21:30 +00:00
return
}
written += n
}
}
2016-07-18 12:26:46 +00:00
xlog.Debug("connWriter: exiting")
2014-11-22 13:21:30 +00:00
}
2021-05-19 07:41:47 +00:00
func (cl *RemoteClient) writeMsg(msg *Message) {
2017-08-22 05:23:30 +00:00
clid := strings.ToLower(msg.Pre)
if _, exists := cl.server.clients[clid]; exists {
msg.Pre = msg.Pre + "!" + msg.Pre + "@" + cl.server.clHosts[clid]
}
2016-07-18 19:22:03 +00:00
cl.writeLine(msg.String())
}
func (cl *RemoteClient) writeLine(format string, a ...interface{}) {
cl.writeq <- fmt.Sprintf(format, a...)
}
2016-07-20 16:17:13 +00:00
func (cl *RemoteClient) handleCmd(s string) {
2021-05-19 07:41:47 +00:00
msg := Parse(s)
2016-07-21 08:30:44 +00:00
msg.Cmd = strings.ToUpper(msg.Cmd)
2016-07-20 16:17:13 +00:00
if cl.name != "" {
2016-07-18 12:26:46 +00:00
cl.Send(msg)
2016-07-20 16:17:13 +00:00
return
}
// If client name is not set, intercept cmds until we get one
switch msg.Cmd {
case "PASS":
cl.password = msg.Args[0]
case "NICK":
cl.name = msg.Args[0]
2016-07-21 22:00:35 +00:00
cl.server.AddClient(cl)
2016-07-20 16:17:13 +00:00
case "USER":
2016-07-18 12:26:46 +00:00
}
2014-11-22 13:21:30 +00:00
}
2016-07-18 19:22:03 +00:00
func checkAuth(cl *RemoteClient) {
2014-11-22 13:21:30 +00:00
if cl.name == "" || cl.password == "" {
return
}
}