2 goircd -- minimalistic simple Internet Relay Chat (IRC) server
3 Copyright (C) 2014-2015 Sergey Matveev <stargrave@stargrave.org>
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.
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.
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/>.
36 CRLF []byte = []byte{'\x0d', '\x0a'}
47 recvTimestamp time.Time
48 sendTimestamp time.Time
54 func (c Client) Host() string {
55 addr := c.conn.RemoteAddr().String()
56 if host, _, err := net.SplitHostPort(addr); err == nil {
59 if domains, err := net.LookupAddr(addr); err == nil {
60 addr = strings.TrimSuffix(domains[0], ".")
65 func (c Client) String() string {
66 return *c.nickname + "!" + *c.username + "@" + c.Host()
69 func NewClient(conn net.Conn) *Client {
76 recvTimestamp: time.Now(),
77 sendTimestamp: time.Now(),
79 outBuf: make(chan *string, MaxOutBuf),
85 func (c *Client) SetDead() {
90 func (c *Client) Close() {
98 // Client processor blockingly reads everything remote client sends,
99 // splits messages by CRLF and send them to Daemon gorouting for processing
100 // it futher. Also it can signalize that client is unavailable (disconnected).
101 func (c *Client) Processor(sink chan ClientEvent) {
102 sink <- ClientEvent{c, EventNew, ""}
103 log.Println(c, "New client")
104 buf := make([]byte, BufSize*2)
111 log.Println(c, "input buffer size exceeded, kicking him")
114 n, err = c.conn.Read(buf[prev:])
120 i = bytes.Index(buf[:prev], CRLF)
124 sink <- ClientEvent{c, EventMsg, string(buf[:i])}
125 copy(buf, buf[i+2:prev])
130 sink <- ClientEvent{c, EventDel, ""}
133 func (c *Client) MsgSender() {
134 for msg := range c.outBuf {
139 c.conn.Write(append([]byte(*msg), CRLF...))
143 // Send message as is with CRLF appended.
144 func (c *Client) Msg(text string) {
150 if len(c.outBuf) == MaxOutBuf {
151 log.Println(c, "output buffer size exceeded, kicking him")
160 // Send message from server. It has ": servername" prefix.
161 func (c *Client) Reply(text string) {
162 c.Msg(":" + *hostname + " " + text)
165 // Send server message, concatenating all provided text parts and
166 // prefix the last one with ":".
167 func (c *Client) ReplyParts(code string, text ...string) {
168 parts := []string{code}
169 for _, t := range text {
170 parts = append(parts, t)
172 parts[len(parts)-1] = ":" + parts[len(parts)-1]
173 c.Reply(strings.Join(parts, " "))
176 // Send nicknamed server message. After servername it always has target
177 // client's nickname. The last part is prefixed with ":".
178 func (c *Client) ReplyNicknamed(code string, text ...string) {
179 c.ReplyParts(code, append([]string{*c.nickname}, text...)...)
182 // Reply "461 not enough parameters" error for given command.
183 func (c *Client) ReplyNotEnoughParameters(command string) {
184 c.ReplyNicknamed("461", command, "Not enough parameters")
187 // Reply "403 no such channel" error for specified channel.
188 func (c *Client) ReplyNoChannel(channel string) {
189 c.ReplyNicknamed("403", channel, "No such channel")
192 func (c *Client) ReplyNoNickChan(channel string) {
193 c.ReplyNicknamed("401", channel, "No such nick/channel")