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}$")
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)
44 members map[*Client]bool
46 logSink chan<- LogEvent
47 stateSink chan<- StateEvent
50 func (room Room) String() string {
54 func NewRoom(hostname *string, name string, logSink chan<- LogEvent, stateSink chan<- StateEvent) *Room {
55 room := Room{name: name}
56 room.members = make(map[*Client]bool)
59 room.hostname = hostname
60 room.logSink = logSink
61 room.stateSink = stateSink
65 func (room *Room) SendTopic(client *Client) {
67 client.ReplyNicknamed("331", room.name, "No topic is set")
69 client.ReplyNicknamed("332", room.name, room.topic)
73 // Send message to all room's subscribers, possibly excluding someone
74 func (room *Room) Broadcast(msg string, clientToIgnore ...*Client) {
75 for member := range room.members {
76 if (len(clientToIgnore) > 0) && member == clientToIgnore[0] {
83 func (room *Room) StateSave() {
84 room.stateSink <- StateEvent{room.name, room.topic, room.key}
87 func (room *Room) Processor(events <-chan ClientEvent) {
89 for event := range events {
91 switch event.eventType {
93 room.members[client] = true
95 log.Println(client, "joined", room.name)
97 room.SendTopic(client)
98 room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, room.name))
99 room.logSink <- LogEvent{room.name, client.nickname, "joined", true}
100 nicknames := []string{}
101 for member := range room.members {
102 nicknames = append(nicknames, member.nickname)
104 sort.Strings(nicknames)
105 client.ReplyNicknamed("353", "=", room.name, strings.Join(nicknames, " "))
106 client.ReplyNicknamed("366", room.name, "End of NAMES list")
108 if _, subscribed := room.members[client]; !subscribed {
109 client.ReplyNicknamed("442", room.name, "You are not on that channel")
112 delete(room.members, client)
113 msg := fmt.Sprintf(":%s PART %s :%s", client, room.name, client.nickname)
115 room.logSink <- LogEvent{room.name, client.nickname, "left", true}
117 if _, subscribed := room.members[client]; !subscribed {
118 client.ReplyParts("442", room.name, "You are not on that channel")
121 if event.text == "" {
122 go room.SendTopic(client)
125 room.topic = strings.TrimLeft(event.text, ":")
126 msg := fmt.Sprintf(":%s TOPIC %s :%s", client, room.name, room.topic)
127 go room.Broadcast(msg)
128 room.logSink <- LogEvent{
131 "set topic to " + room.topic,
136 for m := range room.members {
137 client.ReplyNicknamed(
141 m.conn.RemoteAddr().String(),
148 client.ReplyNicknamed("315", room.name, "End of /WHO list")
150 if event.text == "" {
155 client.Msg(fmt.Sprintf("324 %s %s %s", client.nickname, room.name, mode))
158 if strings.HasPrefix(event.text, "b") {
159 client.ReplyNicknamed("368", room.name, "End of channel ban list")
162 if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") {
163 if _, subscribed := room.members[client]; !subscribed {
164 client.ReplyParts("442", room.name, "You are not on that channel")
168 client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
173 if strings.HasPrefix(event.text, "+k") {
174 cols := strings.Split(event.text, " ")
176 client.ReplyNotEnoughParameters("MODE")
180 msg = fmt.Sprintf(":%s MODE %s +k %s", client, room.name, room.key)
181 msgLog = "set channel key to " + room.key
182 } else if strings.HasPrefix(event.text, "-k") {
184 msg = fmt.Sprintf(":%s MODE %s -k", client, room.name)
185 msgLog = "removed channel key"
187 go room.Broadcast(msg)
188 room.logSink <- LogEvent{room.name, client.nickname, msgLog, true}
191 sep := strings.Index(event.text, " ")
192 room.Broadcast(fmt.Sprintf(
200 room.logSink <- LogEvent{