Split into separate files

This commit is contained in:
Trevor Slocum 2016-09-01 22:54:54 -07:00
parent d914fecabd
commit b46940bed3
5 changed files with 504 additions and 401 deletions

View file

@ -18,4 +18,5 @@ AnonIRCd
- move userlist updates to more efficient goroutine monitoring changes
- whois anonymous<#> easter egg, could be pre-programmed witty phrases/quotes
- op users (locally) when they are logged in to a founder/admin/mod password for client compatibility
- send supported user and channel modes when new user connects
- send supported user and channel modes when new user connects
- SSL-only channel mode

View file

@ -1,54 +1,36 @@
// AnonIRCd
// https://github.com/tslocum/anonircd
//
// Written by Trevor 'tee' Slocum <tslocum@gmail.com>
// Inspired by a Java implementation written by Daniel da Silva 'meltingwax'
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"net"
"log"
"sync"
"math/rand"
irc "gopkg.in/sorcix/irc.v2"
"fmt"
"time"
"strings"
"strconv"
irc "gopkg.in/sorcix/irc.v2"
)
type Channel struct {
clients map[string]int
topic string
topictime int64
*sync.RWMutex
}
type Client struct {
identifier string
nick string
user string
host string
conn net.Conn
pings []string
writebuffer chan *irc.Message
reader *irc.Decoder
writer *irc.Encoder
capHostInNames bool
*sync.RWMutex
}
type Server struct {
clients map[string]*Client
channels map[string]*Channel
*sync.RWMutex
}
var anonymous = irc.Prefix{"Anonymous", "Anon", "IRC"}
var anonirc = irc.Prefix{Name:"anonirc"}
var anonirc = irc.Prefix{Name:"AnonIRC"}
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
const motd = `
_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_|_| _|_| _|_|_| _| _| _| _|
@ -56,6 +38,10 @@ _|_|_|_| _| _| _| _| _| _| _| _|_|_| _|
_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _|_| _| _| _|_|_| _| _| _|_|_|
`
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
const umodes = "a"
const cmodes = "it"
const cmodesarg = "kl"
func randomIdentifier() string {
b := make([]byte, 10)
@ -65,366 +51,9 @@ func randomIdentifier() string {
return string(b)
}
func (c *Client) getPrefix() *irc.Prefix {
return &irc.Prefix{Name:c.nick, User:c.user, Host:c.host}
}
func (s *Server) getAnonymousPrefix(i int) *irc.Prefix {
prefix := anonymous
if i > 1 {
prefix.Name += fmt.Sprintf("%d", i)
}
return &prefix
}
func (s *Server) getChannels(client string) map[string]*Channel {
channels := make(map[string]*Channel)
for channelname, channel := range s.channels {
if s.inChannel(channelname, client) {
channels[channelname] = channel
}
}
return channels
}
func (s *Server) getClient(client string) *Client {
if _, ok := s.clients[client]; ok {
return s.clients[client]
}
return nil
}
func (s *Server) getClients(channel string) map[string]*Client {
clients := make(map[string]*Client)
if !s.channelExists(channel) {
return clients
}
for clientname := range s.channels[channel].clients {
cl := s.getClient(clientname)
if cl != nil {
clients[clientname] = cl
}
}
return clients
}
func (s *Server) channelExists(channel string) bool {
if _, ok := s.channels[channel]; ok {
return true
}
return false
}
func (s *Server) inChannel(channel string, client string) bool {
if s.channelExists(channel) {
if _, ok := s.channels[channel].clients[client]; ok {
return true
}
}
return false
}
func (s *Server) joinChannel(channel string, client string) {
if s.inChannel(channel, client) {
return // Already in channel
}
if !s.channelExists(channel) {
s.channels[channel] = &Channel{make(map[string]int), "", 0, new(sync.RWMutex)}
}
s.channels[channel].Lock()
s.channels[channel].clients[client] = len(s.channels[channel].clients) + 1
s.clients[client].writebuffer <- &irc.Message{nil, irc.JOIN, []string{channel}}
s.sendNames(channel, client)
s.updateUserCount(channel)
s.sendTopic(channel, client, false)
s.channels[channel].Unlock()
}
func (s *Server) partChannel(channel string, client string) {
if !s.inChannel(channel, client) {
return // Not in channel
}
msgout := irc.Message{nil, irc.PART, []string{channel}}
s.clients[client].writebuffer <- &msgout
s.channels[channel].Lock()
delete(s.channels[channel].clients, client)
s.updateUserCount(channel)
s.channels[channel].Unlock()
}
func (s *Server) partAllChannels(client string) {
for channelname := range s.getChannels(client) {
s.partChannel(channelname, client)
}
}
func (s *Server) updateUserCount(channel string) {
for cclient, ccount := range s.channels[channel].clients {
if ccount < len(s.channels[channel].clients) {
for i := ccount; i < len(s.channels[channel].clients); i++ {
prefix := anonymous
if i > 1 {
prefix.Name += fmt.Sprintf("%d", i)
}
msgout := irc.Message{s.getAnonymousPrefix(i), irc.JOIN, []string{channel}}
s.clients[cclient].writebuffer <- &msgout
}
s.channels[channel].clients[cclient] = len(s.channels[channel].clients)
} else if ccount > len(s.channels[channel].clients) {
for i := ccount; i > len(s.channels[channel].clients); i-- {
msgout := irc.Message{s.getAnonymousPrefix(i), irc.PART, []string{channel}}
s.clients[cclient].writebuffer <- &msgout
}
s.channels[channel].clients[cclient] = len(s.channels[channel].clients)
}
}
}
func (s *Server) sendNames(channel string, clientname string) {
if s.inChannel(channel, clientname) {
c := s.getClient(clientname)
names := []string{}
if c.capHostInNames {
names = append(names, c.getPrefix().String())
} else {
names = append(names, c.nick)
}
for i := 1; i < len(s.channels[channel].clients); i++ {
if c.capHostInNames {
names = append(names, s.getAnonymousPrefix(i).String())
} else {
names = append(names, s.getAnonymousPrefix(i).Name)
}
}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_NAMREPLY, []string{"=", channel, strings.Join(names, " ")}}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_ENDOFNAMES, []string{channel, "End of /NAMES list."}}
}
}
func (s *Server) sendTopic(channel string, client string, changed bool) {
if !s.inChannel(channel, client) {
return // Not in channel TODO: Send error instead
}
if s.channels[channel].topic != "" {
tprefix := anonymous
tcommand := irc.TOPIC
if !changed {
tprefix = anonirc
tcommand = irc.RPL_TOPIC
}
msgout := irc.Message{&tprefix, tcommand, []string{channel, s.channels[channel].topic}}
s.clients[client].writebuffer <- &msgout
if !changed {
msgout2 := irc.Message{&anonirc, strings.Join([]string{irc.RPL_TOPICWHOTIME, s.clients[client].nick, channel, "Anonymous", fmt.Sprintf("%d", s.channels[channel].topictime)}, " "), nil}
s.clients[client].writebuffer <- &msgout2
}
}
}
func (s *Server) handleTopic(channel string, client string, topic string) {
if !s.inChannel(channel, client) {
return // Not in channel TODO: Send error
}
if topic != "" {
s.channels[channel].Lock()
s.channels[channel].topic = topic
s.channels[channel].topictime = time.Now().Unix()
for sclient := range s.channels[channel].clients {
s.sendTopic(channel, sclient, true)
}
s.channels[channel].Unlock()
} else {
s.sendTopic(channel, client, false)
}
}
func (s *Server) msgChannel(channel string, client string, message string) {
if !s.inChannel(channel, client) {
return // Not in channel TODO: Send error message
}
for sclient := range s.channels[channel].clients {
if s.clients[sclient].identifier != client {
msgout := irc.Message{&anonymous, irc.PRIVMSG, []string{channel, message}}
s.clients[sclient].writebuffer <- &msgout
}
}
}
func (s *Server) handleRead(c *Client) {
for {
c.conn.SetDeadline(time.Now().Add(300 * time.Second))
msg, err := s.clients[c.identifier].reader.Decode()
if err != nil {
fmt.Println("Unable to read from client:", err)
s.partAllChannels(c.identifier)
return
}
if (msg.Command != irc.PING && msg.Command != irc.PONG) {
fmt.Println(c.identifier, "<-", fmt.Sprintf("%v", msg))
}
if (msg.Command == irc.CAP && len(msg.Params) > 0 && msg.Params[0] == irc.CAP_LS) {
c.writebuffer <- &irc.Message{&anonirc, irc.CAP, []string{msg.Params[0], "userhost-in-names"}}
} else if (msg.Command == irc.CAP && len(msg.Params) > 0 && msg.Params[0] == irc.CAP_REQ) {
if strings.Contains(msg.Trailing(), "userhost-in-names") {
c.capHostInNames = true
}
c.writebuffer <- &irc.Message{&anonirc, irc.CAP, []string{irc.CAP_ACK, msg.Trailing()}}
} else if (msg.Command == irc.CAP && len(msg.Params) > 0 && msg.Params[0] == irc.CAP_LIST) {
caps := []string{}
if c.capHostInNames {
caps = append(caps, "userhost-in-names")
}
c.writebuffer <- &irc.Message{&anonirc, irc.CAP, []string{msg.Params[0], strings.Join(caps, " ")}}
} else if (msg.Command == irc.PING) {
c.writebuffer <- &irc.Message{&anonirc, irc.PONG, []string{msg.Params[0]}}
} else if (msg.Command == irc.NICK && c.nick == "*" && msg.Params[0] != "" && msg.Params[0] != "*") {
c.nick = strings.Trim(msg.Params[0], "\"")
} else if (msg.Command == irc.USER && c.user == "" && len(msg.Params) >= 3 && msg.Params[0] != "" && msg.Params[2] != "") {
c.user = strings.Trim(msg.Params[0], "\"")
c.host = strings.Trim(msg.Params[2], "\"")
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_WELCOME, []string{"Welcome to AnonIRC " + c.getPrefix().String()}}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_YOURHOST, []string{"Your host is AnonIRC, running version AnonIRCd"}}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_CREATED, []string{fmt.Sprintf("This server was created %s", time.UTC.String())}}
c.writebuffer <- &irc.Message{&anonirc, strings.Join([]string{irc.RPL_MYINFO, c.nick, "anonirc AnonIRCd r t lk"}, " "), []string{}}
motdsplit := strings.Split(motd, "\n")
for i, motdmsg := range motdsplit {
var motdcode string
if (i == 0) {
motdcode = irc.RPL_MOTDSTART
} else if (i < len(motdsplit) - 1) {
motdcode = irc.RPL_MOTD
} else {
motdcode = irc.RPL_ENDOFMOTD
}
c.writebuffer <- &irc.Message{&anonirc, motdcode, []string{" " + motdmsg}}
}
s.joinChannel("#lobby", c.identifier)
} else if (msg.Command == irc.JOIN && msg.Params[0][0] == '#') {
s.joinChannel(msg.Params[0], c.identifier)
} else if (msg.Command == irc.NAMES && msg.Params[0][0] == '#') {
s.sendNames(msg.Params[0], c.identifier)
} else if (msg.Command == irc.WHO && msg.Params[0][0] == '#') {
if s.inChannel(msg.Params[0], c.identifier) {
i := 0
for _, cl := range s.getClients(msg.Params[0]) {
var prfx *irc.Prefix
if cl.identifier == c.identifier {
prfx = c.getPrefix()
} else {
i++
prfx = s.getAnonymousPrefix(i)
}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_WHOREPLY, []string{msg.Params[0], prfx.User, prfx.Host, "anonirc", prfx.Name, "Hr", "0 Anonymous"}}
}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_ENDOFWHO, []string{msg.Params[0], "End of /WHO list."}}
}
} else if (msg.Command == irc.MODE && msg.Params[0][0] == '#') {
c.writebuffer <- &irc.Message{&anonirc, strings.Join([]string{irc.RPL_CHANNELMODEIS, c.nick, msg.Params[0], "+ns"}, " "), []string{}}
if len(msg.Params) == 1 {
// Send channel creation time
c.writebuffer <- &irc.Message{&anonirc, strings.Join([]string{"329", c.nick, msg.Params[0], fmt.Sprintf("%d", int32(time.Now().Unix()))}, " "), []string{}}
}
} else if (msg.Command == irc.TOPIC) {
s.handleTopic(msg.Params[0], c.identifier, msg.Trailing())
} else if (msg.Command == irc.PRIVMSG) {
s.msgChannel(msg.Params[0], c.identifier, msg.Trailing())
} else if (msg.Command == irc.PART && msg.Params[0][0] == '#') {
s.partChannel(msg.Params[0], c.identifier)
} else if (msg.Command == irc.QUIT) {
s.partAllChannels(c.identifier)
}
}
}
func (s *Server) handleWrite(c *Client) {
for msg := range c.writebuffer {
if msg.Prefix == nil && c.nick != "" {
msg.Prefix = c.getPrefix()
}
addnick := false
if _, err := strconv.Atoi(msg.Command); err == nil {
addnick = true
} else if msg.Command == irc.CAP {
addnick = true
}
if addnick {
msg.Params = append([]string{c.nick}, msg.Params...)
}
if (msg.Command != irc.PING && msg.Command != irc.PONG) {
fmt.Println(c.identifier, "->", msg)
}
c.writer.Encode(msg)
}
}
func (s *Server) handleConnection(conn net.Conn) {
client := Client{randomIdentifier(), "*", "", "", conn, []string{}, make(chan *irc.Message), irc.NewDecoder(conn), irc.NewEncoder(conn), false, new(sync.RWMutex)}
defer conn.Close()
s.Lock()
s.clients[client.identifier] = &client
s.Unlock()
go s.handleWrite(&client)
s.handleRead(&client)
}
func (s *Server) pingClients() {
for _, c := range s.clients {
ping := fmt.Sprintf("anonirc%d%d", int32(time.Now().Unix()), rand.Intn(1000))
c.pings = append(c.pings, ping)
c.writebuffer <- &irc.Message{&anonirc, irc.PING, []string{ping}}
}
time.Sleep(15 * time.Second)
}
func (s *Server) listen() {
func main() {
rand.Seed(time.Now().UTC().UnixNano())
listener, err := net.Listen("tcp", ":6667")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
defer listener.Close()
go s.pingClients()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
go s.handleConnection(conn)
}
}
func main() {
server := Server{make(map[string]*Client), make(map[string]*Channel), new(sync.RWMutex)}
server := Server{time.Now().Unix(), make(map[string]*Client), make(map[string]*Channel), new(sync.RWMutex)}
server.listen()
}

53
channel.go Normal file
View file

@ -0,0 +1,53 @@
package main
import (
"sync"
"fmt"
"strings"
)
type Channel struct {
created int64
clients map[string]int
modes map[string]string
topic string
topictime int64
*sync.RWMutex
}
func (c *Channel) hasMode(mode string) bool {
if _, ok := c.modes[mode]; ok {
return true
}
return false
}
func (c *Channel) addMode(mode string, param string) {
if strings.Index(cmodes, mode) != -1 && !c.hasMode(mode) {
c.modes[mode] = param
}
}
func (c *Channel) addModes(modes string) {
for _, mode := range strings.Split(modes, "") {
c.addMode(fmt.Sprintf("%s", mode), "")
}
}
func (c *Channel) removeMode(mode string) {
if c.hasMode(mode) {
delete(c.modes, mode)
}
}
func (c *Channel) printModes(lastmodes map[string]string) string {
modes := ""
for mode := range c.modes {
modes += mode
}
return "+" + modes
}

30
client.go Normal file
View file

@ -0,0 +1,30 @@
package main
import (
"net"
"sync"
irc "gopkg.in/sorcix/irc.v2"
)
type Client struct {
identifier string
nick string
user string
host string
conn net.Conn
pings []string
writebuffer chan *irc.Message
reader *irc.Decoder
writer *irc.Encoder
capHostInNames bool
*sync.RWMutex
}
func (c *Client) getPrefix() *irc.Prefix {
return &irc.Prefix{Name:c.nick, User:c.user, Host:c.host}
}

390
server.go Normal file
View file

@ -0,0 +1,390 @@
package main
import (
"fmt"
"net"
"sync"
"time"
"log"
"strconv"
"strings"
irc "gopkg.in/sorcix/irc.v2"
"math/rand"
)
type Server struct {
created int64
clients map[string]*Client
channels map[string]*Channel
*sync.RWMutex
}
func (s *Server) getAnonymousPrefix(i int) *irc.Prefix {
prefix := anonymous
if i > 1 {
prefix.Name += fmt.Sprintf("%d", i)
}
return &prefix
}
func (s *Server) getChannels(client string) map[string]*Channel {
channels := make(map[string]*Channel)
for channelname, channel := range s.channels {
if s.inChannel(channelname, client) {
channels[channelname] = channel
}
}
return channels
}
func (s *Server) getClient(client string) *Client {
if _, ok := s.clients[client]; ok {
return s.clients[client]
}
return nil
}
func (s *Server) getClients(channel string) map[string]*Client {
clients := make(map[string]*Client)
if !s.channelExists(channel) {
return clients
}
for clientname := range s.channels[channel].clients {
cl := s.getClient(clientname)
if cl != nil {
clients[clientname] = cl
}
}
return clients
}
func (s *Server) channelExists(channel string) bool {
if _, ok := s.channels[channel]; ok {
return true
}
return false
}
func (s *Server) inChannel(channel string, client string) bool {
if s.channelExists(channel) {
if _, ok := s.channels[channel].clients[client]; ok {
return true
}
}
return false
}
func (s *Server) joinChannel(channel string, client string) {
if s.inChannel(channel, client) {
return // Already in channel
}
if !s.channelExists(channel) {
s.channels[channel] = &Channel{time.Now().Unix(), make(map[string]int), make(map[string]string), "", 0, new(sync.RWMutex)}
}
s.channels[channel].Lock()
s.channels[channel].clients[client] = len(s.channels[channel].clients) + 1
s.channels[channel].Unlock()
s.clients[client].writebuffer <- &irc.Message{nil, irc.JOIN, []string{channel}}
s.sendNames(channel, client)
s.updateUserCount(channel)
s.sendTopic(channel, client, false)
}
func (s *Server) partChannel(channel string, client string) {
if !s.inChannel(channel, client) {
return // Not in channel
}
msgout := irc.Message{nil, irc.PART, []string{channel}}
s.clients[client].writebuffer <- &msgout
s.channels[channel].Lock()
delete(s.channels[channel].clients, client)
s.channels[channel].Unlock()
s.updateUserCount(channel)
}
func (s *Server) partAllChannels(client string) {
for channelname := range s.getChannels(client) {
s.partChannel(channelname, client)
}
}
func (s *Server) updateUserCount(channel string) {
for cclient, ccount := range s.channels[channel].clients {
if ccount < len(s.channels[channel].clients) {
for i := ccount; i < len(s.channels[channel].clients); i++ {
prefix := anonymous
if i > 1 {
prefix.Name += fmt.Sprintf("%d", i)
}
msgout := irc.Message{s.getAnonymousPrefix(i), irc.JOIN, []string{channel}}
s.clients[cclient].writebuffer <- &msgout
}
s.channels[channel].clients[cclient] = len(s.channels[channel].clients)
} else if ccount > len(s.channels[channel].clients) {
for i := ccount; i > len(s.channels[channel].clients); i-- {
msgout := irc.Message{s.getAnonymousPrefix(i - 1), irc.PART, []string{channel}}
s.clients[cclient].writebuffer <- &msgout
}
s.channels[channel].clients[cclient] = len(s.channels[channel].clients)
}
}
}
func (s *Server) sendNames(channel string, clientname string) {
if s.inChannel(channel, clientname) {
c := s.getClient(clientname)
names := []string{}
if c.capHostInNames {
names = append(names, c.getPrefix().String())
} else {
names = append(names, c.nick)
}
for i := 1; i < len(s.channels[channel].clients); i++ {
if c.capHostInNames {
names = append(names, s.getAnonymousPrefix(i).String())
} else {
names = append(names, s.getAnonymousPrefix(i).Name)
}
}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_NAMREPLY, []string{"=", channel, strings.Join(names, " ")}}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_ENDOFNAMES, []string{channel, "End of /NAMES list."}}
}
}
func (s *Server) sendTopic(channel string, client string, changed bool) {
if !s.inChannel(channel, client) {
return // Not in channel TODO: Send error instead
}
if s.channels[channel].topic != "" {
tprefix := anonymous
tcommand := irc.TOPIC
if !changed {
tprefix = anonirc
tcommand = irc.RPL_TOPIC
}
msgout := irc.Message{&tprefix, tcommand, []string{channel, s.channels[channel].topic}}
s.clients[client].writebuffer <- &msgout
if !changed {
msgout2 := irc.Message{&anonirc, strings.Join([]string{irc.RPL_TOPICWHOTIME, s.clients[client].nick, channel, "Anonymous", fmt.Sprintf("%d", s.channels[channel].topictime)}, " "), nil}
s.clients[client].writebuffer <- &msgout2
}
}
}
func (s *Server) handleTopic(channel string, client string, topic string) {
if !s.inChannel(channel, client) {
return // Not in channel TODO: Send error
}
if topic != "" {
s.channels[channel].Lock()
s.channels[channel].topic = topic
s.channels[channel].topictime = time.Now().Unix()
s.channels[channel].Unlock()
for sclient := range s.channels[channel].clients {
s.sendTopic(channel, sclient, true)
}
} else {
s.sendTopic(channel, client, false)
}
}
func (s *Server) handleMode(c *Client, params []string) {
if len(params) == 0 || params[0][0] != '#' {
return // TODO: Send error
}
if len(params) > 1 && params[1][0] == '+' {
s.channels[params[0]].Lock()
s.channels[params[0]].addModes(params[1][1:])
s.channels[params[0]].Unlock()
}
c.writebuffer <- &irc.Message{&anonirc, strings.Join([]string{irc.RPL_CHANNELMODEIS, c.nick, params[0], s.channels[params[0]].printModes(nil)}, " "), []string{}}
if len(params) == 1 {
// Send channel creation time
c.writebuffer <- &irc.Message{&anonirc, strings.Join([]string{"329", c.nick, params[0], fmt.Sprintf("%d", int32(s.channels[params[0]].created))}, " "), []string{}}
}
}
func (s *Server) msgChannel(channel string, client string, message string) {
if !s.inChannel(channel, client) {
return // Not in channel TODO: Send error message
}
for sclient := range s.channels[channel].clients {
if s.clients[sclient].identifier != client {
msgout := irc.Message{&anonymous, irc.PRIVMSG, []string{channel, message}}
s.clients[sclient].writebuffer <- &msgout
}
}
}
func (s *Server) handleRead(c *Client) {
for {
c.conn.SetDeadline(time.Now().Add(300 * time.Second))
msg, err := s.clients[c.identifier].reader.Decode()
if err != nil {
fmt.Println("Unable to read from client:", err)
s.partAllChannels(c.identifier)
return
}
if (msg.Command != irc.PING && msg.Command != irc.PONG) {
fmt.Println(c.identifier, "<-", fmt.Sprintf("%s", msg))
}
if (msg.Command == irc.CAP && len(msg.Params) > 0 && msg.Params[0] == irc.CAP_LS) {
c.writebuffer <- &irc.Message{&anonirc, irc.CAP, []string{msg.Params[0], "userhost-in-names"}}
} else if (msg.Command == irc.CAP && len(msg.Params) > 0 && msg.Params[0] == irc.CAP_REQ) {
if strings.Contains(msg.Trailing(), "userhost-in-names") {
c.capHostInNames = true
}
c.writebuffer <- &irc.Message{&anonirc, irc.CAP, []string{irc.CAP_ACK, msg.Trailing()}}
} else if (msg.Command == irc.CAP && len(msg.Params) > 0 && msg.Params[0] == irc.CAP_LIST) {
caps := []string{}
if c.capHostInNames {
caps = append(caps, "userhost-in-names")
}
c.writebuffer <- &irc.Message{&anonirc, irc.CAP, []string{msg.Params[0], strings.Join(caps, " ")}}
} else if (msg.Command == irc.PING) {
c.writebuffer <- &irc.Message{&anonirc, irc.PONG, []string{msg.Params[0]}}
} else if (msg.Command == irc.NICK && c.nick == "*" && msg.Params[0] != "" && msg.Params[0] != "*") {
c.nick = strings.Trim(msg.Params[0], "\"")
} else if (msg.Command == irc.USER && c.user == "" && len(msg.Params) >= 3 && msg.Params[0] != "" && msg.Params[2] != "") {
c.user = strings.Trim(msg.Params[0], "\"")
c.host = strings.Trim(msg.Params[2], "\"")
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_WELCOME, []string{"Welcome to AnonIRC " + c.getPrefix().String()}}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_YOURHOST, []string{"Your host is AnonIRC, running version AnonIRCd"}}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_CREATED, []string{fmt.Sprintf("This server was created %s", time.Unix(s.created, 0).UTC())}}
c.writebuffer <- &irc.Message{&anonirc, strings.Join([]string{irc.RPL_MYINFO, c.nick, "AnonIRC AnonIRCd ", umodes, cmodes, cmodesarg}, " "), []string{}}
motdsplit := strings.Split(motd, "\n")
for i, motdmsg := range motdsplit {
var motdcode string
if (i == 0) {
motdcode = irc.RPL_MOTDSTART
} else if (i < len(motdsplit) - 1) {
motdcode = irc.RPL_MOTD
} else {
motdcode = irc.RPL_ENDOFMOTD
}
c.writebuffer <- &irc.Message{&anonirc, motdcode, []string{" " + motdmsg}}
}
s.joinChannel("#lobby", c.identifier)
} else if (msg.Command == irc.JOIN && msg.Params[0][0] == '#') {
s.joinChannel(msg.Params[0], c.identifier)
} else if (msg.Command == irc.NAMES && msg.Params[0][0] == '#') {
s.sendNames(msg.Params[0], c.identifier)
} else if (msg.Command == irc.WHO && msg.Params[0][0] == '#') {
if s.inChannel(msg.Params[0], c.identifier) {
i := 0
for _, cl := range s.getClients(msg.Params[0]) {
var prfx *irc.Prefix
if cl.identifier == c.identifier {
prfx = c.getPrefix()
} else {
i++
prfx = s.getAnonymousPrefix(i)
}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_WHOREPLY, []string{msg.Params[0], prfx.User, prfx.Host, "AnonIRC", prfx.Name, "H", "0 Anonymous"}}
}
c.writebuffer <- &irc.Message{&anonirc, irc.RPL_ENDOFWHO, []string{msg.Params[0], "End of /WHO list."}}
}
} else if (msg.Command == irc.MODE && msg.Params[0][0] == '#') {
s.handleMode(c, msg.Params)
} else if (msg.Command == irc.TOPIC) {
s.handleTopic(msg.Params[0], c.identifier, msg.Trailing())
} else if (msg.Command == irc.PRIVMSG) {
s.msgChannel(msg.Params[0], c.identifier, msg.Trailing())
} else if (msg.Command == irc.PART && msg.Params[0][0] == '#') {
s.partChannel(msg.Params[0], c.identifier)
} else if (msg.Command == irc.QUIT) {
s.partAllChannels(c.identifier)
}
}
}
func (s *Server) handleWrite(c *Client) {
for msg := range c.writebuffer {
if msg.Prefix == nil && c.nick != "" {
msg.Prefix = c.getPrefix()
}
addnick := false
if _, err := strconv.Atoi(msg.Command); err == nil {
addnick = true
} else if msg.Command == irc.CAP {
addnick = true
}
if addnick {
msg.Params = append([]string{c.nick}, msg.Params...)
}
if (msg.Command != irc.PING && msg.Command != irc.PONG) {
fmt.Println(c.identifier, "->", msg)
}
c.writer.Encode(msg)
}
}
func (s *Server) handleConnection(conn net.Conn) {
client := Client{randomIdentifier(), "*", "", "", conn, []string{}, make(chan *irc.Message), irc.NewDecoder(conn), irc.NewEncoder(conn), false, new(sync.RWMutex)}
defer conn.Close()
s.Lock()
s.clients[client.identifier] = &client
s.Unlock()
go s.handleWrite(&client)
s.handleRead(&client)
}
func (s *Server) pingClients() {
for _, c := range s.clients {
ping := fmt.Sprintf("anonirc%d%d", int32(time.Now().Unix()), rand.Intn(1000))
c.pings = append(c.pings, ping)
c.writebuffer <- &irc.Message{&anonirc, irc.PING, []string{ping}}
}
time.Sleep(15 * time.Second)
}
func (s *Server) listen() {
listener, err := net.Listen("tcp", ":6667")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
defer listener.Close()
go s.pingClients()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
continue
}
go s.handleConnection(conn)
}
}