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