Somehow it works
This commit is contained in:
parent
1fb13ec5fd
commit
1c671592bb
3 changed files with 207 additions and 42 deletions
1
README
1
README
|
@ -1 +0,0 @@
|
|||
how do i shot web
|
20
README.md
Normal file
20
README.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
how do i shot web
|
||||
-----------------
|
||||
TODO:
|
||||
- configuration system
|
||||
- database (sqlite for portability?)
|
||||
- ssl
|
||||
- verify pings and prune lagging clients
|
||||
- admin/mod login via server password
|
||||
- admin/mod commands via /anonirc <args>
|
||||
- admins/mods can say something official in a channel, it will also come with a notice to grab attention
|
||||
- server admin password (set in config) allows global admin privileges
|
||||
- channel registration to three passwords (founder/admin/mod)
|
||||
- only the founder and optionally some admins can regenerate these passwords
|
||||
- each channel password can be supplied during connection as server password (e.g. #lobby/swordfish:#lounge/8ball) or via a command
|
||||
- private channels (+k implementation)
|
||||
- implement read locks...? are they necessary?
|
||||
- respond to /who and /names
|
||||
- /list support
|
||||
- move userlist updates to more efficient goroutine monitoring changes
|
||||
- whois anonymous<#> easter egg, could be pre-programmed witty phrases/quotes
|
228
anonircd.go
228
anonircd.go
|
@ -9,17 +9,23 @@ import (
|
|||
"fmt"
|
||||
"time"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Channel struct {
|
||||
clients Client
|
||||
clients map[string]int
|
||||
topic string
|
||||
topictime int64
|
||||
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
identifier string
|
||||
nick string
|
||||
|
||||
conn net.Conn
|
||||
pings []string
|
||||
|
||||
writebuffer chan *irc.Message
|
||||
|
||||
reader *irc.Decoder
|
||||
|
@ -29,12 +35,14 @@ type Client struct {
|
|||
}
|
||||
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
channels map[string]Channel
|
||||
clients map[string]*Client
|
||||
channels map[string]*Channel
|
||||
|
||||
*sync.RWMutex
|
||||
}
|
||||
|
||||
var anonymous = irc.Prefix{"Anonymous", "", "AnonIRC"}
|
||||
var anonircd = irc.Prefix{Name:"AnonIRCd"}
|
||||
|
||||
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
const motd = `
|
||||
|
@ -53,44 +61,201 @@ func randomIdentifier() string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
func (c *Client) getPrefix() *irc.Prefix {
|
||||
return &irc.Prefix{Name:c.nick, User:"", Host:"AnonIRC"}
|
||||
}
|
||||
|
||||
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) inChannel(channel string, client string) bool {
|
||||
if _, ok := s.channels[channel]; ok {
|
||||
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 _, ok := s.channels[channel]; !ok {
|
||||
s.channels[channel] = &Channel{make(map[string]int), "", 0, new(sync.RWMutex)}
|
||||
}
|
||||
s.channels[channel].Lock()
|
||||
s.channels[channel].clients[client] = 1
|
||||
|
||||
msgout := irc.Message{nil, irc.JOIN, []string{channel}}
|
||||
s.clients[client].writebuffer <- &msgout
|
||||
|
||||
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{&prefix, 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-- {
|
||||
prefix := anonymous
|
||||
if i > 1 {
|
||||
prefix.Name += fmt.Sprintf("%d", i)
|
||||
}
|
||||
msgout := irc.Message{&prefix, irc.PART, []string{channel}}
|
||||
s.clients[cclient].writebuffer <- &msgout
|
||||
}
|
||||
|
||||
s.channels[channel].clients[cclient] = len(s.channels[channel].clients)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) sendTopic(channel string, client string, changed bool) {
|
||||
if s.channels[channel].topic != "" {
|
||||
tprefix := anonymous
|
||||
tcommand := irc.TOPIC
|
||||
if !changed {
|
||||
tprefix = anonircd
|
||||
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{&anonircd, 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 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) {
|
||||
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
|
||||
}
|
||||
fmt.Println(fmt.Sprintf("%v", msg))
|
||||
|
||||
fmt.Println("PREFIX: " + fmt.Sprintf("%v", msg.Prefix))
|
||||
fmt.Println("COMMAND: " + fmt.Sprintf("%v", msg.Command))
|
||||
fmt.Println("PARAMS: " + fmt.Sprintf("%v", msg.Params))
|
||||
fmt.Println("Read: " + fmt.Sprintf("%v", msg))
|
||||
if (msg.Command == irc.CAP && len(msg.Params) > 0 && (msg.Params[0] == irc.CAP_LS || msg.Params[0] == irc.CAP_LIST)) {
|
||||
response := irc.Message{nil, irc.CAP, []string{"*", msg.Params[0], ""}}
|
||||
response := irc.Message{&anonircd, irc.CAP, []string{msg.Params[0], ""}}
|
||||
c.writebuffer <- &response
|
||||
} else if (msg.Command == irc.PING) {
|
||||
c.writebuffer <- &irc.Message{nil, irc.PONG, []string{msg.Params[0]}}
|
||||
}
|
||||
c.writebuffer <- &irc.Message{&anonircd, irc.PONG, []string{msg.Params[0]}}
|
||||
} else if (msg.Command == irc.NICK && c.nick == "*" && msg.Params[0] != "" && msg.Params[0] != "*") {
|
||||
c.nick = msg.Params[0]
|
||||
|
||||
if (msg.Command == irc.PRIVMSG) {
|
||||
for _, sclient := range s.clients {
|
||||
msgout := irc.Message{&anonymous, irc.PRIVMSG, []string{"#test", msg.Trailing()}}
|
||||
sclient.writebuffer <- &msgout
|
||||
c.writebuffer <- &irc.Message{&anonircd, irc.RPL_WELCOME, []string{"Welcome to AnonIRC."}}
|
||||
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{&anonircd, 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.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()
|
||||
}
|
||||
if _, err := strconv.Atoi(msg.Command); err == nil {
|
||||
msg.Params = append([]string{c.nick}, msg.Params...)
|
||||
}
|
||||
fmt.Println("Writing...", 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), new(sync.RWMutex)}
|
||||
client := Client{randomIdentifier(), "*", conn, []string{}, make(chan *irc.Message), irc.NewDecoder(conn), irc.NewEncoder(conn), new(sync.RWMutex)}
|
||||
defer conn.Close()
|
||||
|
||||
s.Lock()
|
||||
|
@ -98,23 +263,6 @@ func (s *Server) handleConnection(conn net.Conn) {
|
|||
s.Unlock()
|
||||
|
||||
go s.handleWrite(&client)
|
||||
|
||||
client.writebuffer <- &irc.Message{&irc.Prefix{Name:"anonircd"}, irc.RPL_WELCOME, []string{" Welcome to"}}
|
||||
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
|
||||
}
|
||||
client.writebuffer <- &irc.Message{&irc.Prefix{Name:"anonircd"}, motdcode, []string{" " + motdmsg}}
|
||||
}
|
||||
|
||||
client.writebuffer <- &irc.Message{&anonymous, irc.JOIN, []string{"#lobby"}}
|
||||
|
||||
s.handleRead(&client)
|
||||
}
|
||||
func (s *Server) pingClients() {
|
||||
|
@ -126,7 +274,7 @@ func (s *Server) pingClients() {
|
|||
time.Sleep(15 * time.Second)
|
||||
}
|
||||
|
||||
func (s *Server) listen(server *Server) {
|
||||
func (s *Server) listen() {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
listener, err := net.Listen("tcp", ":6667")
|
||||
|
@ -140,7 +288,7 @@ func (s *Server) listen(server *Server) {
|
|||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting connection: %s", err)
|
||||
fmt.Println("Error accepting connection:", err)
|
||||
continue
|
||||
}
|
||||
go s.handleConnection(conn)
|
||||
|
@ -148,8 +296,6 @@ func (s *Server) listen(server *Server) {
|
|||
}
|
||||
|
||||
func main() {
|
||||
server := Server{
|
||||
clients: make(map[string]*Client),
|
||||
channels: make(map[string]Channel)}
|
||||
server.listen(&server)
|
||||
}
|
||||
server := Server{make(map[string]*Client), make(map[string]*Channel), new(sync.RWMutex)}
|
||||
server.listen()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue