nmdc-ircfrontend/client.go

304 lines
8.2 KiB
Go
Raw Normal View History

2013-08-24 06:48:28 +00:00
package main
import (
"bytes"
"fmt"
"io"
"sort"
"strings"
"time"
)
func (c *Client) setNick(nick string) {
if c.nick != "" {
2013-08-29 19:50:10 +00:00
delete(c.server.clientMap, c.key)
2013-08-24 06:48:28 +00:00
for _, channel := range c.channelMap {
2013-08-29 19:50:10 +00:00
delete(channel.clientMap, c.key)
2013-08-24 06:48:28 +00:00
}
}
//Set up new nick
oldNick := c.nick
c.nick = nick
2013-08-29 19:50:10 +00:00
c.key = strings.ToLower(c.nick)
c.server.clientMap[c.key] = c
2013-08-24 06:48:28 +00:00
clients := make([]string, 0, 100)
for _, channel := range c.channelMap {
2013-08-29 19:50:10 +00:00
channel.clientMap[c.key] = c
2013-08-24 06:48:28 +00:00
//Collect list of client nicks who can see us
2013-08-27 12:40:51 +00:00
for _, client := range channel.clientMap {
clients = append(clients, client.nick)
2013-08-24 06:48:28 +00:00
}
}
//By sorting the nicks and skipping duplicates we send each client one message
sort.Strings(clients)
prevNick := ""
for _, nick := range clients {
if nick == prevNick {
continue
}
prevNick = nick
2013-08-27 12:40:51 +00:00
client, exists := c.server.clientMap[strings.ToLower(nick)]
2013-08-24 06:48:28 +00:00
if exists {
client.reply(rplNickChange, oldNick, c.nick)
}
}
}
func (c *Client) joinChannel(channelName string) {
2013-08-27 17:50:37 +00:00
newChannel := false
2013-08-27 12:40:51 +00:00
channelKey := strings.ToLower(channelName)
channel, exists := c.server.channelMap[channelKey]
2013-08-24 06:48:28 +00:00
if exists == false {
channel = &Channel{name: channelName,
topic: "",
2013-08-27 17:50:37 +00:00
clientMap: make(map[string]*Client),
modeMap: make(map[string]*ClientMode)}
2013-08-27 12:40:51 +00:00
c.server.channelMap[channelKey] = channel
2013-08-27 17:50:37 +00:00
newChannel = true
2013-08-24 06:48:28 +00:00
}
2013-08-29 19:50:10 +00:00
if _, inChannel := channel.clientMap[c.nick]; inChannel {
2013-08-27 12:44:42 +00:00
//Client is already in the channel, do nothing
return
}
2013-08-27 17:50:37 +00:00
mode := new(ClientMode)
if newChannel {
//If they created the channel, make them op
mode.operator = true
}
2013-08-29 19:50:10 +00:00
channel.clientMap[c.nick] = c
channel.modeMap[c.nick] = mode
2013-08-27 12:40:51 +00:00
c.channelMap[channelKey] = channel
2013-08-24 06:48:28 +00:00
for _, client := range channel.clientMap {
client.reply(rplJoin, c.nick, channel.name)
2013-08-24 06:48:28 +00:00
}
if channel.topic != "" {
c.reply(rplTopic, channel.name, channel.topic)
2013-08-24 06:48:28 +00:00
} else {
c.reply(rplNoTopic, channel.name)
2013-08-24 06:48:28 +00:00
}
nicks := make([]string, 0, 100)
2013-08-27 12:40:51 +00:00
for _, client := range channel.clientMap {
2013-08-27 17:50:37 +00:00
prefix := ""
2013-08-29 19:50:10 +00:00
if mode, exists := channel.modeMap[client.nick]; exists {
2013-08-28 15:38:54 +00:00
prefix = mode.Prefix()
2013-08-27 17:50:37 +00:00
}
nicks = append(nicks, fmt.Sprintf("%s%s", prefix, client.nick))
2013-08-24 06:48:28 +00:00
}
c.reply(rplNames, channelName, strings.Join(nicks, " "))
}
func (c *Client) partChannel(channelName, reason string) {
2013-08-27 12:40:51 +00:00
channelKey := strings.ToLower(channelName)
channel, exists := c.server.channelMap[channelKey]
2013-08-24 06:48:28 +00:00
if exists == false {
return
}
2013-08-27 12:44:42 +00:00
if _, inChannel := channel.clientMap[strings.ToLower(c.nick)]; inChannel == false {
//Client isn't in this channel, do nothing
return
}
2013-08-24 06:48:28 +00:00
//Notify clients of the part
for _, client := range channel.clientMap {
client.reply(rplPart, c.nick, channel.name, reason)
2013-08-24 06:48:28 +00:00
}
2013-08-27 12:40:51 +00:00
delete(channel.clientMap, strings.ToLower(c.nick))
if len(channel.clientMap) == 0 {
delete(c.channelMap, channelKey)
}
2013-08-24 06:48:28 +00:00
}
func (c *Client) disconnect() {
c.connected = false
c.signalChan <- signalStop
}
//Send a reply to a user with the code specified
2013-08-24 19:58:38 +00:00
func (c *Client) reply(code replyCode, args ...string) {
2013-08-24 06:48:28 +00:00
if c.connected == false {
return
}
switch code {
case rplWelcome:
c.outputChan <- fmt.Sprintf(":%s 001 %s :Welcome to %s", c.server.name, c.nick, c.server.name)
case rplJoin:
c.outputChan <- fmt.Sprintf(":%s JOIN %s", args[0], args[1])
case rplPart:
c.outputChan <- fmt.Sprintf(":%s PART %s %s", args[0], args[1], args[2])
2013-08-24 06:48:28 +00:00
case rplTopic:
c.outputChan <- fmt.Sprintf(":%s 332 %s %s :%s", c.server.name, c.nick, args[0], args[1])
case rplNoTopic:
c.outputChan <- fmt.Sprintf(":%s 331 %s %s :No topic is set", c.server.name, c.nick, args[0])
case rplNames:
//TODO: break long lists up into multiple messages
c.outputChan <- fmt.Sprintf(":%s 353 %s = %s :%s", c.server.name, c.nick, args[0], args[1])
c.outputChan <- fmt.Sprintf(":%s 366 %s", c.server.name, c.nick)
case rplNickChange:
c.outputChan <- fmt.Sprintf(":%s NICK %s", args[0], args[1])
case rplKill:
2013-08-29 20:20:34 +00:00
c.outputChan <- fmt.Sprintf(":%s KILL %s A %s", args[0], c.nick, args[1])
2013-08-24 06:48:28 +00:00
case rplMsg:
c.outputChan <- fmt.Sprintf(":%s PRIVMSG %s %s", args[0], args[1], args[2])
case rplList:
c.outputChan <- fmt.Sprintf(":%s 321 %s", c.server.name, c.nick)
for _, listItem := range args {
c.outputChan <- fmt.Sprintf(":%s 322 %s %s", c.server.name, c.nick, listItem)
}
c.outputChan <- fmt.Sprintf(":%s 323 %s", c.server.name, c.nick)
case rplOper:
c.outputChan <- fmt.Sprintf(":%s 381 %s :You are now an operator", c.server.name, c.nick)
2013-08-27 18:34:52 +00:00
case rplChannelModeIs:
c.outputChan <- fmt.Sprintf(":%s 324 %s %s %s %s", c.server.name, c.nick, args[0], args[1], args[2])
2013-08-29 20:10:28 +00:00
case rplKick:
c.outputChan <- fmt.Sprintf(":%s KICK %s %s %s", args[0], args[1], args[2], args[3])
2013-08-30 22:17:54 +00:00
case rplInfo:
c.outputChan <- fmt.Sprintf(":%s 371 %s :%s", c.server.name, c.nick, args[0])
case rplVersion:
c.outputChan <- fmt.Sprintf(":%s 351 %s %s", c.server.name, c.nick, args[0])
2013-09-08 15:06:03 +00:00
case rplMOTD:
2013-09-08 19:35:30 +00:00
motd := args[0]
2013-09-08 15:06:03 +00:00
c.outputChan <- fmt.Sprintf(":%s 375 %s", c.server.name, c.nick)
2013-09-08 19:35:30 +00:00
for size := len(motd); size > 0; size = len(motd) {
if size <= 80 {
c.outputChan <- fmt.Sprintf(":%s 372 %s :- %s", c.server.name, c.nick, motd)
break
}
c.outputChan <- fmt.Sprintf(":%s 372 %s :- %s", c.server.name, c.nick, motd[:80])
motd = motd[80:]
2013-09-08 15:06:03 +00:00
}
c.outputChan <- fmt.Sprintf(":%s 376 %s", c.server.name, c.nick)
2013-08-24 06:48:28 +00:00
case errMoreArgs:
c.outputChan <- fmt.Sprintf(":%s 461 %s :Not enough params", c.server.name, c.nick)
case errNoNick:
c.outputChan <- fmt.Sprintf(":%s 431 %s :No nickname given", c.server.name, c.nick)
case errInvalidNick:
c.outputChan <- fmt.Sprintf(":%s 432 %s %s :Erronenous nickname", c.server.name, c.nick, args[0])
case errNickInUse:
c.outputChan <- fmt.Sprintf(":%s 433 %s %s :Nick already in use", c.server.name, c.nick, args[0])
case errAlreadyReg:
c.outputChan <- fmt.Sprintf(":%s 462 :You need a valid nick first", c.server.name)
case errNoSuchNick:
c.outputChan <- fmt.Sprintf(":%s 401 %s %s :No such nick/channel", c.server.name, c.nick, args[0])
case errUnknownCommand:
c.outputChan <- fmt.Sprintf(":%s 421 %s %s :Unknown command", c.server.name, c.nick, args[0])
case errNotReg:
c.outputChan <- fmt.Sprintf(":%s 451 :You have not registered", c.server.name)
case errPassword:
c.outputChan <- fmt.Sprintf(":%s 464 %s :Error, password incorrect", c.server.name, c.nick)
case errNoPriv:
c.outputChan <- fmt.Sprintf(":%s 481 %s :Permission denied", c.server.name, c.nick)
2013-08-27 18:40:14 +00:00
case errCannotSend:
c.outputChan <- fmt.Sprintf(":%s 404 %s %s :Cannot send to channel", c.server.name, c.nick, args[0])
2013-08-24 06:48:28 +00:00
}
}
func (c *Client) clientThread() {
2013-08-24 19:58:38 +00:00
readSignalChan := make(chan signalCode, 3)
writeSignalChan := make(chan signalCode, 3)
2013-08-24 06:48:28 +00:00
writeChan := make(chan string, 100)
2013-09-08 19:35:30 +00:00
c.server.eventChan <- Event{client: c, event: connected}
2013-08-24 06:48:28 +00:00
go c.readThread(readSignalChan)
go c.writeThread(writeSignalChan, writeChan)
defer func() {
//Part from all channels
for channelName := range c.channelMap {
c.partChannel(channelName, "Disconnecting")
2013-08-24 06:48:28 +00:00
}
2013-08-27 12:40:51 +00:00
delete(c.server.clientMap, strings.ToLower(c.nick))
2013-08-24 07:36:57 +00:00
c.connection.Close()
2013-08-24 06:48:28 +00:00
}()
for {
select {
case signal := <-c.signalChan:
if signal == signalStop {
readSignalChan <- signalStop
writeSignalChan <- signalStop
return
}
case line := <-c.outputChan:
select {
case writeChan <- line:
2013-08-24 07:36:57 +00:00
continue
2013-08-24 06:48:28 +00:00
default:
2013-08-24 07:36:57 +00:00
c.disconnect()
2013-08-24 06:48:28 +00:00
}
}
}
}
2013-08-24 19:58:38 +00:00
func (c *Client) readThread(signalChan chan signalCode) {
2013-08-24 06:48:28 +00:00
for {
select {
case signal := <-signalChan:
if signal == signalStop {
return
}
default:
c.connection.SetReadDeadline(time.Now().Add(time.Second * 3))
buf := make([]byte, 512)
ln, err := c.connection.Read(buf)
if err != nil {
if err == io.EOF {
c.disconnect()
return
}
continue
}
rawLines := buf[:ln]
lines := bytes.Split(rawLines, []byte("\r\n"))
for _, line := range lines {
if len(line) > 0 {
2013-09-08 15:24:17 +00:00
c.server.eventChan <- Event{client: c, event: command, input: string(line)}
2013-08-24 06:48:28 +00:00
}
}
}
}
}
2013-08-24 19:58:38 +00:00
func (c *Client) writeThread(signalChan chan signalCode, outputChan chan string) {
2013-08-24 06:48:28 +00:00
for {
select {
case signal := <-signalChan:
if signal == signalStop {
return
}
case output := <-outputChan:
line := []byte(fmt.Sprintf("%s\r\n", output))
c.connection.SetWriteDeadline(time.Now().Add(time.Second * 30))
2013-08-27 12:40:51 +00:00
if _, err := c.connection.Write(line); err != nil {
2013-08-24 06:48:28 +00:00
c.disconnect()
return
}
}
}
}