2 goircd -- minimalistic simple Internet Relay Chat (IRC) server
3 Copyright (C) 2014-2019 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, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
30 RERoom = regexp.MustCompile("^#[^\x00\x07\x0a\x0d ,:/]{1,200}$")
33 // Sanitize room's name. It can consist of 1 to 50 ASCII symbols
34 // with some exclusions. All room names will have "#" prefix.
35 func RoomNameValid(name string) bool {
36 return RERoom.MatchString(name)
43 members map[*Client]struct{}
47 func (room *Room) String() (name string) {
54 func NewRoom(name string) *Room {
61 members: make(map[*Client]struct{}),
65 func (room *Room) SendTopic(client *Client) {
67 if *room.topic == "" {
68 client.ReplyNicknamed("331", room.String(), "No topic is set")
70 client.ReplyNicknamed("332", room.String(), *room.topic)
75 // Send message to all room's subscribers, possibly excluding someone.
76 func (room *Room) Broadcast(msg string, clientToIgnore ...*Client) {
78 for member := range room.members {
79 if (len(clientToIgnore) > 0) && member == clientToIgnore[0] {
87 func (room *Room) StateSave() {
89 stateSink <- StateEvent{room.String(), *room.topic, *room.key}
93 func (room *Room) Processor(events <-chan ClientEvent) {
95 for event := range events {
97 switch event.eventType {
103 room.members[client] = struct{}{}
105 log.Println(client, "joined", room.name)
108 room.SendTopic(client)
109 room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, room.String()))
110 logSink <- LogEvent{room.String(), *client.nickname, "joined", true}
111 nicknames := make([]string, 0)
113 for member := range room.members {
114 nicknames = append(nicknames, *member.nickname)
117 sort.Strings(nicknames)
118 client.ReplyNicknamed("353", "=", room.String(), strings.Join(nicknames, " "))
119 client.ReplyNicknamed("366", room.String(), "End of NAMES list")
122 if _, subscribed := room.members[client]; !subscribed {
123 client.ReplyNicknamed("442", room.String(), "You are not on that channel")
129 delete(room.members, client)
132 msg := fmt.Sprintf(":%s PART %s :%s", client, room.String(), *client.nickname)
134 logSink <- LogEvent{room.String(), *client.nickname, "left", true}
138 if _, subscribed := room.members[client]; !subscribed {
139 client.ReplyParts("442", room.String(), "You are not on that channel")
143 if event.text == "" {
144 room.SendTopic(client)
149 topic := strings.TrimLeft(event.text, ":")
154 msg := fmt.Sprintf(":%s TOPIC %s :%s", client, room.String(), *room.topic)
159 "set topic to " + *room.topic,
166 for m := range room.members {
167 client.ReplyNicknamed(
178 client.ReplyNicknamed("315", room.String(), "End of /WHO list")
182 if event.text == "" {
187 client.Msg(fmt.Sprintf("324 %s %s %s", *client.nickname, room.String(), mode))
191 if strings.HasPrefix(event.text, "b") {
192 client.ReplyNicknamed("368", room.String(), "End of channel ban list")
196 if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") {
197 if _, subscribed := room.members[client]; !subscribed {
198 client.ReplyParts("442", room.String(), "You are not on that channel")
203 client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
210 if strings.HasPrefix(event.text, "+k") {
211 cols := strings.Split(event.text, " ")
213 client.ReplyNotEnoughParameters("MODE")
218 msg = fmt.Sprintf(":%s MODE %s +k %s", client, *room.name, *room.key)
219 msgLog = "set channel key to " + *room.key
225 msg = fmt.Sprintf(":%s MODE %s -k", client, *room.name)
227 msgLog = "removed channel key"
230 logSink <- LogEvent{room.String(), *client.nickname, msgLog, true}
233 sep := strings.Index(event.text, " ")
234 room.Broadcast(fmt.Sprintf(