]> Cypherpunks.ru repositories - goircd.git/blob - room.go
Example lighttpd configuration for logs directory viewing
[goircd.git] / room.go
1 /*
2 goircd -- minimalistic simple Internet Relay Chat (IRC) server
3 Copyright (C) 2014-2016 Sergey Matveev <stargrave@stargrave.org>
4
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.
9
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.
14
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/>.
17 */
18
19 package main
20
21 import (
22         "fmt"
23         "log"
24         "regexp"
25         "sort"
26         "strings"
27         "sync"
28 )
29
30 var (
31         RERoom = regexp.MustCompile("^#[^\x00\x07\x0a\x0d ,:/]{1,200}$")
32 )
33
34 // Sanitize room's name. It can consist of 1 to 50 ASCII symbols
35 // with some exclusions. All room names will have "#" prefix.
36 func RoomNameValid(name string) bool {
37         return RERoom.MatchString(name)
38 }
39
40 type Room struct {
41         name    *string
42         topic   *string
43         key     *string
44         members map[*Client]struct{}
45         sync.RWMutex
46 }
47
48 func (room *Room) String() (name string) {
49         room.RLock()
50         name = *room.name
51         room.RUnlock()
52         return
53 }
54
55 func NewRoom(name string) *Room {
56         topic := ""
57         key := ""
58         return &Room{
59                 name:    &name,
60                 topic:   &topic,
61                 key:     &key,
62                 members: make(map[*Client]struct{}),
63         }
64 }
65
66 func (room *Room) SendTopic(client *Client) {
67         room.RLock()
68         if *room.topic == "" {
69                 client.ReplyNicknamed("331", room.String(), "No topic is set")
70         } else {
71                 client.ReplyNicknamed("332", room.String(), *room.topic)
72         }
73         room.RUnlock()
74 }
75
76 // Send message to all room's subscribers, possibly excluding someone.
77 func (room *Room) Broadcast(msg string, clientToIgnore ...*Client) {
78         room.RLock()
79         for member := range room.members {
80                 if (len(clientToIgnore) > 0) && member == clientToIgnore[0] {
81                         continue
82                 }
83                 member.Msg(msg)
84         }
85         room.RUnlock()
86 }
87
88 func (room *Room) StateSave() {
89         room.RLock()
90         stateSink <- StateEvent{room.String(), *room.topic, *room.key}
91         room.RUnlock()
92 }
93
94 func (room *Room) Processor(events <-chan ClientEvent) {
95         var client *Client
96         for event := range events {
97                 client = event.client
98                 switch event.eventType {
99                 case EventTerm:
100                         roomsGroup.Done()
101                         return
102                 case EventNew:
103                         room.Lock()
104                         room.members[client] = struct{}{}
105                         if *verbose {
106                                 log.Println(client, "joined", room.name)
107                         }
108                         room.Unlock()
109                         room.SendTopic(client)
110                         room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, room.String()))
111                         logSink <- LogEvent{room.String(), *client.nickname, "joined", true}
112                         nicknames := make([]string, 0)
113                         room.RLock()
114                         for member := range room.members {
115                                 nicknames = append(nicknames, *member.nickname)
116                         }
117                         room.RUnlock()
118                         sort.Strings(nicknames)
119                         client.ReplyNicknamed("353", "=", room.String(), strings.Join(nicknames, " "))
120                         client.ReplyNicknamed("366", room.String(), "End of NAMES list")
121                 case EventDel:
122                         room.RLock()
123                         if _, subscribed := room.members[client]; !subscribed {
124                                 client.ReplyNicknamed("442", room.String(), "You are not on that channel")
125                                 room.RUnlock()
126                                 continue
127                         }
128                         room.RUnlock()
129                         room.Lock()
130                         delete(room.members, client)
131                         room.Unlock()
132                         room.RLock()
133                         msg := fmt.Sprintf(":%s PART %s :%s", client, room.String(), *client.nickname)
134                         room.Broadcast(msg)
135                         logSink <- LogEvent{room.String(), *client.nickname, "left", true}
136                         room.RUnlock()
137                 case EventTopic:
138                         room.RLock()
139                         if _, subscribed := room.members[client]; !subscribed {
140                                 client.ReplyParts("442", room.String(), "You are not on that channel")
141                                 room.RUnlock()
142                                 continue
143                         }
144                         if event.text == "" {
145                                 room.SendTopic(client)
146                                 room.RUnlock()
147                                 continue
148                         }
149                         room.RUnlock()
150                         topic := strings.TrimLeft(event.text, ":")
151                         room.Lock()
152                         room.topic = &topic
153                         room.Unlock()
154                         room.RLock()
155                         msg := fmt.Sprintf(":%s TOPIC %s :%s", client, room.String(), *room.topic)
156                         room.Broadcast(msg)
157                         logSink <- LogEvent{
158                                 room.String(),
159                                 *client.nickname,
160                                 "set topic to " + *room.topic,
161                                 true,
162                         }
163                         room.RUnlock()
164                         room.StateSave()
165                 case EventWho:
166                         room.RLock()
167                         for m := range room.members {
168                                 client.ReplyNicknamed(
169                                         "352",
170                                         room.String(),
171                                         *m.username,
172                                         m.Host(),
173                                         *hostname,
174                                         *m.nickname,
175                                         "H",
176                                         "0 "+*m.realname,
177                                 )
178                         }
179                         client.ReplyNicknamed("315", room.String(), "End of /WHO list")
180                         room.RUnlock()
181                 case EventMode:
182                         room.RLock()
183                         if event.text == "" {
184                                 mode := "+"
185                                 if *room.key != "" {
186                                         mode = mode + "k"
187                                 }
188                                 client.Msg(fmt.Sprintf("324 %s %s %s", *client.nickname, room.String(), mode))
189                                 room.RUnlock()
190                                 continue
191                         }
192                         if strings.HasPrefix(event.text, "b") {
193                                 client.ReplyNicknamed("368", room.String(), "End of channel ban list")
194                                 room.RUnlock()
195                                 continue
196                         }
197                         if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") {
198                                 if _, subscribed := room.members[client]; !subscribed {
199                                         client.ReplyParts("442", room.String(), "You are not on that channel")
200                                         room.RUnlock()
201                                         continue
202                                 }
203                         } else {
204                                 client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
205                                 room.RUnlock()
206                                 continue
207                         }
208                         room.RUnlock()
209                         var msg string
210                         var msgLog string
211                         if strings.HasPrefix(event.text, "+k") {
212                                 cols := strings.Split(event.text, " ")
213                                 if len(cols) == 1 {
214                                         client.ReplyNotEnoughParameters("MODE")
215                                         continue
216                                 }
217                                 room.Lock()
218                                 room.key = &cols[1]
219                                 msg = fmt.Sprintf(":%s MODE %s +k %s", client, *room.name, *room.key)
220                                 msgLog = "set channel key to " + *room.key
221                                 room.Unlock()
222                         } else {
223                                 key := ""
224                                 room.Lock()
225                                 room.key = &key
226                                 msg = fmt.Sprintf(":%s MODE %s -k", client, *room.name)
227                                 room.Unlock()
228                                 msgLog = "removed channel key"
229                         }
230                         room.Broadcast(msg)
231                         logSink <- LogEvent{room.String(), *client.nickname, msgLog, true}
232                         room.StateSave()
233                 case EventMsg:
234                         sep := strings.Index(event.text, " ")
235                         room.Broadcast(fmt.Sprintf(
236                                 ":%s %s %s :%s",
237                                 client,
238                                 event.text[:sep],
239                                 room.String(),
240                                 event.text[sep+1:]),
241                                 client,
242                         )
243                         logSink <- LogEvent{
244                                 room.String(),
245                                 *client.nickname,
246                                 event.text[sep+1:],
247                                 false,
248                         }
249                 }
250         }
251 }