2 goircd -- minimalistic simple Internet Relay Chat (IRC) server
3 Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 RERoom = regexp.MustCompile("^#[^\x00\x07\x0a\x0d ,:/]{1,200}$")
32 rooms map[string]*Room = make(map[string]*Room)
34 roomSinks map[*Room]chan ClientEvent = make(map[*Room]chan ClientEvent)
37 // Sanitize room's name. It can consist of 1 to 50 ASCII symbols
38 // with some exclusions. All room names will have "#" prefix.
39 func RoomNameValid(name string) bool {
40 return RERoom.MatchString(name)
47 members map[*Client]struct{}
50 func (room Room) String() string {
54 func NewRoom(name string) *Room {
59 members: make(map[*Client]struct{}),
63 func (room *Room) SendTopic(client *Client) {
64 if *room.topic == "" {
65 client.ReplyNicknamed("331", *room.name, "No topic is set")
67 client.ReplyNicknamed("332", *room.name, *room.topic)
71 // Send message to all room's subscribers, possibly excluding someone.
72 func (room *Room) Broadcast(msg string, clientToIgnore ...*Client) {
73 for member := range room.members {
74 if (len(clientToIgnore) > 0) && member == clientToIgnore[0] {
81 func (room *Room) StateSave() {
86 stateSink <- StateEvent{*room.name, *room.topic, key}
89 func (room *Room) Processor(events <-chan ClientEvent) {
91 for event := range events {
93 switch event.eventType {
98 room.members[client] = struct{}{}
100 log.Println(client, "joined", room.name)
102 room.SendTopic(client)
103 room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, *room.name))
104 logSink <- LogEvent{*room.name, *client.nickname, "joined", true}
105 nicknames := make([]string, 0)
106 for member := range room.members {
107 nicknames = append(nicknames, *member.nickname)
109 sort.Strings(nicknames)
110 client.ReplyNicknamed("353", "=", *room.name, strings.Join(nicknames, " "))
111 client.ReplyNicknamed("366", *room.name, "End of NAMES list")
113 if _, subscribed := room.members[client]; !subscribed {
114 client.ReplyNicknamed("442", *room.name, "You are not on that channel")
117 delete(room.members, client)
118 msg := fmt.Sprintf(":%s PART %s :%s", client, *room.name, *client.nickname)
120 logSink <- LogEvent{*room.name, *client.nickname, "left", true}
122 if _, subscribed := room.members[client]; !subscribed {
123 client.ReplyParts("442", *room.name, "You are not on that channel")
126 if event.text == "" {
127 room.SendTopic(client)
130 topic := strings.TrimLeft(event.text, ":")
132 msg := fmt.Sprintf(":%s TOPIC %s :%s", client, *room.name, *room.topic)
137 "set topic to " + *room.topic,
142 for m := range room.members {
143 client.ReplyNicknamed(
147 m.conn.RemoteAddr().String(),
154 client.ReplyNicknamed("315", *room.name, "End of /WHO list")
156 if event.text == "" {
161 client.Msg(fmt.Sprintf("324 %s %s %s", *client.nickname, *room.name, mode))
164 if strings.HasPrefix(event.text, "b") {
165 client.ReplyNicknamed("368", *room.name, "End of channel ban list")
168 if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") {
169 if _, subscribed := room.members[client]; !subscribed {
170 client.ReplyParts("442", *room.name, "You are not on that channel")
174 client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
179 if strings.HasPrefix(event.text, "+k") {
180 cols := strings.Split(event.text, " ")
182 client.ReplyNotEnoughParameters("MODE")
186 msg = fmt.Sprintf(":%s MODE %s +k %s", client, *room.name, *room.key)
187 msgLog = "set channel key to " + *room.key
190 msg = fmt.Sprintf(":%s MODE %s -k", client, *room.name)
191 msgLog = "removed channel key"
194 logSink <- LogEvent{*room.name, *client.nickname, msgLog, true}
197 sep := strings.Index(event.text, " ")
198 room.Broadcast(fmt.Sprintf(