goircd/room.go

252 lines
6.2 KiB
Go
Raw Normal View History

2014-05-11 16:18:55 +00:00
/*
goircd -- minimalistic simple Internet Relay Chat (IRC) server
2015-12-31 17:23:21 +00:00
Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
2014-05-11 16:18:55 +00:00
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/>.
*/
2015-05-09 15:27:06 +00:00
2014-05-11 16:18:55 +00:00
package main
import (
"fmt"
"log"
"regexp"
"sort"
"strings"
2016-03-26 14:10:04 +00:00
"sync"
2014-05-11 16:18:55 +00:00
)
var (
2014-08-09 12:42:19 +00:00
RERoom = regexp.MustCompile("^#[^\x00\x07\x0a\x0d ,:/]{1,200}$")
)
2014-05-11 16:18:55 +00:00
// Sanitize room's name. It can consist of 1 to 50 ASCII symbols
// with some exclusions. All room names will have "#" prefix.
func RoomNameValid(name string) bool {
2014-08-09 12:42:19 +00:00
return RERoom.MatchString(name)
2014-05-11 16:18:55 +00:00
}
type Room struct {
name *string
topic *string
key *string
members map[*Client]struct{}
2016-03-26 14:10:04 +00:00
sync.RWMutex
2014-05-11 16:18:55 +00:00
}
2016-03-26 14:10:04 +00:00
func (room *Room) String() (name string) {
room.RLock()
name = *room.name
room.RUnlock()
return
}
func NewRoom(name string) *Room {
topic := ""
2015-10-13 06:37:03 +00:00
key := ""
return &Room{
name: &name,
topic: &topic,
2015-10-13 06:37:03 +00:00
key: &key,
members: make(map[*Client]struct{}),
}
2014-05-11 16:18:55 +00:00
}
func (room *Room) SendTopic(client *Client) {
2016-03-26 14:10:04 +00:00
room.RLock()
if *room.topic == "" {
2016-03-26 14:10:04 +00:00
client.ReplyNicknamed("331", room.String(), "No topic is set")
2014-05-11 16:18:55 +00:00
} else {
2016-03-26 14:10:04 +00:00
client.ReplyNicknamed("332", room.String(), *room.topic)
2014-05-11 16:18:55 +00:00
}
2016-03-26 14:10:04 +00:00
room.RUnlock()
2014-05-11 16:18:55 +00:00
}
// Send message to all room's subscribers, possibly excluding someone.
2014-08-09 12:42:19 +00:00
func (room *Room) Broadcast(msg string, clientToIgnore ...*Client) {
2016-03-26 14:10:04 +00:00
room.RLock()
2014-05-11 16:18:55 +00:00
for member := range room.members {
2014-08-09 12:42:19 +00:00
if (len(clientToIgnore) > 0) && member == clientToIgnore[0] {
2014-05-11 16:18:55 +00:00
continue
}
member.Msg(msg)
}
2016-03-26 14:10:04 +00:00
room.RUnlock()
2014-05-11 16:18:55 +00:00
}
func (room *Room) StateSave() {
2016-03-26 14:10:04 +00:00
room.RLock()
stateSink <- StateEvent{room.String(), *room.topic, *room.key}
room.RUnlock()
2014-05-11 16:18:55 +00:00
}
2014-06-08 00:21:06 +00:00
func (room *Room) Processor(events <-chan ClientEvent) {
2014-05-11 16:18:55 +00:00
var client *Client
for event := range events {
client = event.client
2014-08-09 12:42:19 +00:00
switch event.eventType {
case EventTerm:
roomsGroup.Done()
return
2014-08-09 12:42:19 +00:00
case EventNew:
2016-03-26 14:10:04 +00:00
room.Lock()
room.members[client] = struct{}{}
if *verbose {
2014-06-08 00:49:27 +00:00
log.Println(client, "joined", room.name)
}
2016-03-26 14:10:04 +00:00
room.Unlock()
2014-05-11 16:18:55 +00:00
room.SendTopic(client)
2016-03-26 14:10:04 +00:00
room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, room.String()))
logSink <- LogEvent{room.String(), *client.nickname, "joined", true}
nicknames := make([]string, 0)
2016-03-26 14:10:04 +00:00
room.RLock()
2014-05-11 16:18:55 +00:00
for member := range room.members {
nicknames = append(nicknames, *member.nickname)
2014-05-11 16:18:55 +00:00
}
2016-03-26 14:10:04 +00:00
room.RUnlock()
2014-05-11 16:18:55 +00:00
sort.Strings(nicknames)
2016-03-26 14:10:04 +00:00
client.ReplyNicknamed("353", "=", room.String(), strings.Join(nicknames, " "))
client.ReplyNicknamed("366", room.String(), "End of NAMES list")
2014-08-09 12:42:19 +00:00
case EventDel:
2016-03-26 14:10:04 +00:00
room.RLock()
2014-05-11 16:18:55 +00:00
if _, subscribed := room.members[client]; !subscribed {
2016-03-26 14:10:04 +00:00
client.ReplyNicknamed("442", room.String(), "You are not on that channel")
room.RUnlock()
2014-05-11 16:18:55 +00:00
continue
}
2016-03-26 14:10:04 +00:00
room.RUnlock()
room.Lock()
2014-05-11 16:18:55 +00:00
delete(room.members, client)
2016-03-26 14:10:04 +00:00
room.Unlock()
room.RLock()
msg := fmt.Sprintf(":%s PART %s :%s", client, room.String(), *client.nickname)
2014-08-09 11:37:10 +00:00
room.Broadcast(msg)
2016-03-26 14:10:04 +00:00
logSink <- LogEvent{room.String(), *client.nickname, "left", true}
room.RUnlock()
2014-08-09 12:42:19 +00:00
case EventTopic:
2016-03-26 14:10:04 +00:00
room.RLock()
2014-05-11 16:18:55 +00:00
if _, subscribed := room.members[client]; !subscribed {
2016-03-26 14:10:04 +00:00
client.ReplyParts("442", room.String(), "You are not on that channel")
room.RUnlock()
2014-05-11 16:18:55 +00:00
continue
}
if event.text == "" {
room.SendTopic(client)
2016-03-26 14:10:04 +00:00
room.RUnlock()
2014-05-11 16:18:55 +00:00
continue
}
2016-03-26 14:10:04 +00:00
room.RUnlock()
topic := strings.TrimLeft(event.text, ":")
2016-03-26 14:10:04 +00:00
room.Lock()
room.topic = &topic
2016-03-26 14:10:04 +00:00
room.Unlock()
room.RLock()
msg := fmt.Sprintf(":%s TOPIC %s :%s", client, room.String(), *room.topic)
room.Broadcast(msg)
logSink <- LogEvent{
2016-03-26 14:10:04 +00:00
room.String(),
*client.nickname,
"set topic to " + *room.topic,
2015-10-05 20:57:31 +00:00
true,
}
2016-03-26 14:10:04 +00:00
room.RUnlock()
2014-05-11 16:18:55 +00:00
room.StateSave()
2014-08-09 12:42:19 +00:00
case EventWho:
2016-03-26 14:10:04 +00:00
room.RLock()
2014-05-11 16:18:55 +00:00
for m := range room.members {
2015-10-05 20:57:31 +00:00
client.ReplyNicknamed(
"352",
2016-03-26 14:10:04 +00:00
room.String(),
*m.username,
2015-10-18 20:07:57 +00:00
m.Host(),
*hostname,
*m.nickname,
2015-10-05 20:57:31 +00:00
"H",
"0 "+*m.realname,
2015-10-05 20:57:31 +00:00
)
2014-05-11 16:18:55 +00:00
}
2016-03-26 14:10:04 +00:00
client.ReplyNicknamed("315", room.String(), "End of /WHO list")
room.RUnlock()
2014-08-09 12:42:19 +00:00
case EventMode:
2016-03-26 14:10:04 +00:00
room.RLock()
2014-05-11 16:18:55 +00:00
if event.text == "" {
mode := "+"
2015-10-13 06:37:03 +00:00
if *room.key != "" {
2014-05-11 16:18:55 +00:00
mode = mode + "k"
}
2016-03-26 14:10:04 +00:00
client.Msg(fmt.Sprintf("324 %s %s %s", *client.nickname, room.String(), mode))
room.RUnlock()
2014-05-11 16:18:55 +00:00
continue
}
if strings.HasPrefix(event.text, "b") {
2016-03-26 14:10:04 +00:00
client.ReplyNicknamed("368", room.String(), "End of channel ban list")
room.RUnlock()
continue
}
2014-05-11 16:18:55 +00:00
if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") {
if _, subscribed := room.members[client]; !subscribed {
2016-03-26 14:10:04 +00:00
client.ReplyParts("442", room.String(), "You are not on that channel")
room.RUnlock()
2014-05-11 16:18:55 +00:00
continue
}
} else {
client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
2016-03-26 14:10:04 +00:00
room.RUnlock()
2014-05-11 16:18:55 +00:00
continue
}
2016-03-26 14:10:04 +00:00
room.RUnlock()
2014-05-11 16:18:55 +00:00
var msg string
2014-08-09 12:42:19 +00:00
var msgLog string
2014-05-11 16:18:55 +00:00
if strings.HasPrefix(event.text, "+k") {
cols := strings.Split(event.text, " ")
if len(cols) == 1 {
client.ReplyNotEnoughParameters("MODE")
continue
}
2016-03-26 14:10:04 +00:00
room.Lock()
room.key = &cols[1]
msg = fmt.Sprintf(":%s MODE %s +k %s", client, *room.name, *room.key)
msgLog = "set channel key to " + *room.key
2016-03-26 14:10:04 +00:00
room.Unlock()
} else {
2015-10-13 06:37:03 +00:00
key := ""
2016-03-26 14:10:04 +00:00
room.Lock()
2015-10-13 06:37:03 +00:00
room.key = &key
msg = fmt.Sprintf(":%s MODE %s -k", client, *room.name)
2016-03-26 14:10:04 +00:00
room.Unlock()
2014-08-09 12:42:19 +00:00
msgLog = "removed channel key"
2014-05-11 16:18:55 +00:00
}
room.Broadcast(msg)
2016-03-26 14:10:04 +00:00
logSink <- LogEvent{room.String(), *client.nickname, msgLog, true}
2014-05-11 16:18:55 +00:00
room.StateSave()
2014-08-09 12:42:19 +00:00
case EventMsg:
2014-05-11 16:18:55 +00:00
sep := strings.Index(event.text, " ")
2015-10-05 20:57:31 +00:00
room.Broadcast(fmt.Sprintf(
":%s %s %s :%s",
client,
event.text[:sep],
2016-03-26 14:10:04 +00:00
room.String(),
2015-10-05 20:57:31 +00:00
event.text[sep+1:]),
client,
)
logSink <- LogEvent{
2016-03-26 14:10:04 +00:00
room.String(),
*client.nickname,
2015-10-05 20:57:31 +00:00
event.text[sep+1:],
false,
}
2014-05-11 16:18:55 +00:00
}
}
}