]> Cypherpunks.ru repositories - goircd.git/blob - room.go
Split long lines
[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
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)
37 }
38
39 type Room struct {
40         Verbose   bool
41         name      string
42         topic     string
43         key       string
44         members   map[*Client]bool
45         hostname  *string
46         logSink   chan<- LogEvent
47         stateSink chan<- StateEvent
48 }
49
50 func (room Room) String() string {
51         return room.name
52 }
53
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)
57         room.topic = ""
58         room.key = ""
59         room.hostname = hostname
60         room.logSink = logSink
61         room.stateSink = stateSink
62         return &room
63 }
64
65 func (room *Room) SendTopic(client *Client) {
66         if room.topic == "" {
67                 client.ReplyNicknamed("331", room.name, "No topic is set")
68         } else {
69                 client.ReplyNicknamed("332", room.name, room.topic)
70         }
71 }
72
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] {
77                         continue
78                 }
79                 member.Msg(msg)
80         }
81 }
82
83 func (room *Room) StateSave() {
84         room.stateSink <- StateEvent{room.name, room.topic, room.key}
85 }
86
87 func (room *Room) Processor(events <-chan ClientEvent) {
88         var client *Client
89         for event := range events {
90                 client = event.client
91                 switch event.eventType {
92                 case EventNew:
93                         room.members[client] = true
94                         if room.Verbose {
95                                 log.Println(client, "joined", room.name)
96                         }
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)
103                         }
104                         sort.Strings(nicknames)
105                         client.ReplyNicknamed("353", "=", room.name, strings.Join(nicknames, " "))
106                         client.ReplyNicknamed("366", room.name, "End of NAMES list")
107                 case EventDel:
108                         if _, subscribed := room.members[client]; !subscribed {
109                                 client.ReplyNicknamed("442", room.name, "You are not on that channel")
110                                 continue
111                         }
112                         delete(room.members, client)
113                         msg := fmt.Sprintf(":%s PART %s :%s", client, room.name, client.nickname)
114                         room.Broadcast(msg)
115                         room.logSink <- LogEvent{room.name, client.nickname, "left", true}
116                 case EventTopic:
117                         if _, subscribed := room.members[client]; !subscribed {
118                                 client.ReplyParts("442", room.name, "You are not on that channel")
119                                 continue
120                         }
121                         if event.text == "" {
122                                 go room.SendTopic(client)
123                                 continue
124                         }
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{
129                                 room.name,
130                                 client.nickname,
131                                 "set topic to " + room.topic,
132                                 true,
133                         }
134                         room.StateSave()
135                 case EventWho:
136                         for m := range room.members {
137                                 client.ReplyNicknamed(
138                                         "352",
139                                         room.name,
140                                         m.username,
141                                         m.conn.RemoteAddr().String(),
142                                         *room.hostname,
143                                         m.nickname,
144                                         "H",
145                                         "0 "+m.realname,
146                                 )
147                         }
148                         client.ReplyNicknamed("315", room.name, "End of /WHO list")
149                 case EventMode:
150                         if event.text == "" {
151                                 mode := "+"
152                                 if room.key != "" {
153                                         mode = mode + "k"
154                                 }
155                                 client.Msg(fmt.Sprintf("324 %s %s %s", client.nickname, room.name, mode))
156                                 continue
157                         }
158                         if strings.HasPrefix(event.text, "b") {
159                                 client.ReplyNicknamed("368", room.name, "End of channel ban list")
160                                 continue
161                         }
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")
165                                         continue
166                                 }
167                         } else {
168                                 client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
169                                 continue
170                         }
171                         var msg string
172                         var msgLog string
173                         if strings.HasPrefix(event.text, "+k") {
174                                 cols := strings.Split(event.text, " ")
175                                 if len(cols) == 1 {
176                                         client.ReplyNotEnoughParameters("MODE")
177                                         continue
178                                 }
179                                 room.key = cols[1]
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") {
183                                 room.key = ""
184                                 msg = fmt.Sprintf(":%s MODE %s -k", client, room.name)
185                                 msgLog = "removed channel key"
186                         }
187                         go room.Broadcast(msg)
188                         room.logSink <- LogEvent{room.name, client.nickname, msgLog, true}
189                         room.StateSave()
190                 case EventMsg:
191                         sep := strings.Index(event.text, " ")
192                         room.Broadcast(fmt.Sprintf(
193                                 ":%s %s %s :%s",
194                                 client,
195                                 event.text[:sep],
196                                 room.name,
197                                 event.text[sep+1:]),
198                                 client,
199                         )
200                         room.logSink <- LogEvent{
201                                 room.name,
202                                 client.nickname,
203                                 event.text[sep+1:],
204                                 false,
205                         }
206                 }
207         }
208 }