]> Cypherpunks.ru repositories - goircd.git/blob - room.go
9e1900206c71bc6be54af15526458798d6deb199
[goircd.git] / room.go
1 /*
2 goircd -- minimalistic simple Internet Relay Chat (IRC) server
3 Copyright (C) 2014 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 package main
19
20 import (
21         "fmt"
22         "log"
23         "regexp"
24         "sort"
25         "strings"
26 )
27
28 // Sanitize room's name. It can consist of 1 to 50 ASCII symbols
29 // with some exclusions. All room names will have "#" prefix.
30 func RoomNameSanitize(name string) (n string, valid bool) {
31         n = strings.TrimLeft(strings.ToLower(name), "&#+!")
32         valid, _ = regexp.MatchString("^[^\x00\x07\x0a\x0d ,:/]{1,50}$", n)
33         return "#" + n, valid
34 }
35
36 type Room struct {
37         name       string
38         topic      string
39         key        string
40         members    map[*Client]bool
41         hostname   string
42         log_sink   chan LogEvent
43         state_sink chan StateEvent
44 }
45
46 func NewRoom(hostname, name string, log_sink chan LogEvent, state_sink chan StateEvent) *Room {
47         room := Room{name: name}
48         room.members = make(map[*Client]bool)
49         room.topic = ""
50         room.key = ""
51         room.hostname = hostname
52         room.log_sink = log_sink
53         room.state_sink = state_sink
54         return &room
55 }
56
57 func (room *Room) SendTopic(client *Client) {
58         if room.topic == "" {
59                 client.ReplyNicknamed("331", room.name, "No topic is set")
60         } else {
61                 client.ReplyNicknamed("332", room.name, room.topic)
62         }
63 }
64
65 // Send message to all room's subscribers, possibly excluding someone
66 func (room *Room) Broadcast(msg string, client_to_ignore ...*Client) {
67         for member := range room.members {
68                 if (len(client_to_ignore) > 0) && member == client_to_ignore[0] {
69                         continue
70                 }
71                 member.Msg(msg)
72         }
73 }
74
75 func (room *Room) StateSave() {
76         room.state_sink <- StateEvent{room.name, room.topic, room.key}
77 }
78
79 func (room *Room) Processor(events chan ClientEvent) {
80         var client *Client
81         for event := range events {
82                 client = event.client
83                 switch event.event_type {
84                 case EVENT_NEW:
85                         room.members[client] = true
86                         log.Println(client, "joined", room.name)
87                         room.SendTopic(client)
88                         go room.Broadcast(fmt.Sprintf(":%s JOIN %s", client, room.name))
89                         room.log_sink <- LogEvent{room.name, client.nickname, "joined", true}
90                         nicknames := []string{}
91                         for member := range room.members {
92                                 nicknames = append(nicknames, member.nickname)
93                         }
94                         sort.Strings(nicknames)
95                         client.ReplyNicknamed("353", "=", room.name, strings.Join(nicknames, " "))
96                         client.ReplyNicknamed("366", room.name, "End of NAMES list")
97                 case EVENT_DEL:
98                         if _, subscribed := room.members[client]; !subscribed {
99                                 client.ReplyNicknamed("442", room.name, "You are not on that channel")
100                                 continue
101                         }
102                         delete(room.members, client)
103                         msg := fmt.Sprintf(":%s PART %s :%s", client, room.name, client.nickname)
104                         go room.Broadcast(msg)
105                         room.log_sink <- LogEvent{room.name, client.nickname, "left", true}
106                 case EVENT_TOPIC:
107                         if _, subscribed := room.members[client]; !subscribed {
108                                 client.ReplyParts("442", room.name, "You are not on that channel")
109                                 continue
110                         }
111                         if event.text == "" {
112                                 go room.SendTopic(client)
113                                 continue
114                         }
115                         room.topic = strings.TrimLeft(event.text, ":")
116                         msg := fmt.Sprintf(":%s TOPIC %s :%s", client, room.name, room.topic)
117                         go room.Broadcast(msg)
118                         room.log_sink <- LogEvent{room.name, client.nickname, "set topic to " + room.topic, true}
119                         room.StateSave()
120                 case EVENT_WHO:
121                         for m := range room.members {
122                                 client.ReplyNicknamed("352", room.name, m.username, m.conn.RemoteAddr().String(), room.hostname, m.nickname, "H", "0 "+m.realname)
123                         }
124                         client.ReplyNicknamed("315", room.name, "End of /WHO list")
125                 case EVENT_MODE:
126                         if event.text == "" {
127                                 mode := "+"
128                                 if room.key != "" {
129                                         mode = mode + "k"
130                                 }
131                                 client.Msg(fmt.Sprintf("324 %s %s %s", client.nickname, room.name, mode))
132                                 continue
133                         }
134                         if strings.HasPrefix(event.text, "-k") || strings.HasPrefix(event.text, "+k") {
135                                 if _, subscribed := room.members[client]; !subscribed {
136                                         client.ReplyParts("442", room.name, "You are not on that channel")
137                                         continue
138                                 }
139                         } else {
140                                 client.ReplyNicknamed("472", event.text, "Unknown MODE flag")
141                                 continue
142                         }
143                         var msg string
144                         var msg_log string
145                         if strings.HasPrefix(event.text, "+k") {
146                                 cols := strings.Split(event.text, " ")
147                                 if len(cols) == 1 {
148                                         client.ReplyNotEnoughParameters("MODE")
149                                         continue
150                                 }
151                                 room.key = cols[1]
152                                 msg = fmt.Sprintf(":%s MODE %s +k %s", client, room.name, room.key)
153                                 msg_log = "set channel key to " + room.key
154                         } else if strings.HasPrefix(event.text, "-k") {
155                                 room.key = ""
156                                 msg = fmt.Sprintf(":%s MODE %s -k", client, room.name)
157                                 msg_log = "removed channel key"
158                         }
159                         go room.Broadcast(msg)
160                         room.log_sink <- LogEvent{room.name, client.nickname, msg_log, true}
161                         room.StateSave()
162                 case EVENT_MSG:
163                         sep := strings.Index(event.text, " ")
164                         go room.Broadcast(fmt.Sprintf(":%s %s %s :%s", client, event.text[:sep], room.name, event.text[sep+1:]), client)
165                         room.log_sink <- LogEvent{room.name, client.nickname, event.text[sep+1:], false}
166                 }
167         }
168 }