]> Cypherpunks.ru repositories - goircd.git/blob - room.go
Better FreeBSD's startup script
[goircd.git] / room.go
1 /*
2 goircd -- minimalistic simple Internet Relay Chat (IRC) server
3 Copyright (C) 2014-2015 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 )
28
29 var (
30         RERoom = regexp.MustCompile("^#[^\x00\x07\x0a\x0d ,:/]{1,200}$")
31
32         rooms map[string]*Room = make(map[string]*Room)
33
34         roomSinks map[*Room]chan ClientEvent = make(map[*Room]chan ClientEvent)
35 )
36
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)
41 }
42
43 type Room struct {
44         name    *string
45         topic   *string
46         key     *string
47         members map[*Client]struct{}
48 }
49
50 func (room Room) String() string {
51         return *room.name
52 }
53
54 func NewRoom(name string) *Room {
55         topic := ""
56         return &Room{
57                 name:    &name,
58                 topic:   &topic,
59                 members: make(map[*Client]struct{}),
60         }
61 }
62
63 func (room *Room) SendTopic(client *Client) {
64         if *room.topic == "" {
65                 client.ReplyNicknamed("331", *room.name, "No topic is set")
66         } else {
67                 client.ReplyNicknamed("332", *room.name, *room.topic)
68         }
69 }
70
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] {
75                         continue
76                 }
77                 member.Msg(msg)
78         }
79 }
80
81 func (room *Room) StateSave() {
82         var key string
83         if room.key != nil {
84                 key = *room.key
85         }
86         stateSink <- StateEvent{*room.name, *room.topic, key}
87 }
88
89 func (room *Room) Processor(events <-chan ClientEvent) {
90         var client *Client
91         for event := range events {
92                 client = event.client
93                 switch event.eventType {
94                 case EventTerm:
95                         roomsGroup.Done()
96                         return
97                 case EventNew:
98                         room.members[client] = struct{}{}
99                         if *verbose {
100                                 log.Println(client, "joined", room.name)
101                         }
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)
108                         }
109                         sort.Strings(nicknames)
110                         client.ReplyNicknamed("353", "=", *room.name, strings.Join(nicknames, " "))
111                         client.ReplyNicknamed("366", *room.name, "End of NAMES list")
112                 case EventDel:
113                         if _, subscribed := room.members[client]; !subscribed {
114                                 client.ReplyNicknamed("442", *room.name, "You are not on that channel")
115                                 continue
116                         }
117                         delete(room.members, client)
118                         msg := fmt.Sprintf(":%s PART %s :%s", client, *room.name, *client.nickname)
119                         room.Broadcast(msg)
120                         logSink <- LogEvent{*room.name, *client.nickname, "left", true}
121                 case EventTopic:
122                         if _, subscribed := room.members[client]; !subscribed {
123                                 client.ReplyParts("442", *room.name, "You are not on that channel")
124                                 continue
125                         }
126                         if event.text == "" {
127                                 room.SendTopic(client)
128                                 continue
129                         }
130                         topic := strings.TrimLeft(event.text, ":")
131                         room.topic = &topic
132                         msg := fmt.Sprintf(":%s TOPIC %s :%s", client, *room.name, *room.topic)
133                         room.Broadcast(msg)
134                         logSink <- LogEvent{
135                                 *room.name,
136                                 *client.nickname,
137                                 "set topic to " + *room.topic,
138                                 true,
139                         }
140                         room.StateSave()
141                 case EventWho:
142                         for m := range room.members {
143                                 client.ReplyNicknamed(
144                                         "352",
145                                         *room.name,
146                                         *m.username,
147                                         m.conn.RemoteAddr().String(),
148                                         *hostname,
149                                         *m.nickname,
150                                         "H",
151                                         "0 "+*m.realname,
152                                 )
153                         }
154                         client.ReplyNicknamed("315", *room.name, "End of /WHO list")
155                 case EventMode:
156                         if event.text == "" {
157                                 mode := "+"
158                                 if room.key != nil {
159                                         mode = mode + "k"
160                                 }
161                                 client.Msg(fmt.Sprintf("324 %s %s %s", *client.nickname, *room.name, mode))
162                                 continue
163                         }
164                         if strings.HasPrefix(event.text, "b") {
165                                 client.ReplyNicknamed("368", *room.name, "End of channel ban list")
166                                 continue
167                         }
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")
171                                         continue
172                                 }
173                         } else {
174                                 client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
175                                 continue
176                         }
177                         var msg string
178                         var msgLog string
179                         if strings.HasPrefix(event.text, "+k") {
180                                 cols := strings.Split(event.text, " ")
181                                 if len(cols) == 1 {
182                                         client.ReplyNotEnoughParameters("MODE")
183                                         continue
184                                 }
185                                 room.key = &cols[1]
186                                 msg = fmt.Sprintf(":%s MODE %s +k %s", client, *room.name, *room.key)
187                                 msgLog = "set channel key to " + *room.key
188                         } else {
189                                 room.key = nil
190                                 msg = fmt.Sprintf(":%s MODE %s -k", client, *room.name)
191                                 msgLog = "removed channel key"
192                         }
193                         room.Broadcast(msg)
194                         logSink <- LogEvent{*room.name, *client.nickname, msgLog, true}
195                         room.StateSave()
196                 case EventMsg:
197                         sep := strings.Index(event.text, " ")
198                         room.Broadcast(fmt.Sprintf(
199                                 ":%s %s %s :%s",
200                                 client,
201                                 event.text[:sep],
202                                 *room.name,
203                                 event.text[sep+1:]),
204                                 client,
205                         )
206                         logSink <- LogEvent{
207                                 *room.name,
208                                 *client.nickname,
209                                 event.text[sep+1:],
210                                 false,
211                         }
212                 }
213         }
214 }