]> Cypherpunks.ru repositories - goircd.git/blob - client.go
Split long lines
[goircd.git] / client.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         "bytes"
23         "log"
24         "net"
25         "strings"
26         "time"
27 )
28
29 const (
30         CRLF    = "\x0d\x0a"
31         BufSize = 1380
32 )
33
34 type Client struct {
35         hostname   *string
36         conn       net.Conn
37         registered bool
38         nickname   string
39         username   string
40         realname   string
41         password   string
42         away       *string
43 }
44
45 type ClientAlivenessState struct {
46         pingSent  bool
47         timestamp time.Time
48 }
49
50 func (client Client) String() string {
51         return client.nickname + "!" + client.username + "@" + client.conn.RemoteAddr().String()
52 }
53
54 func NewClient(hostname *string, conn net.Conn) *Client {
55         return &Client{hostname: hostname, conn: conn, nickname: "*", password: ""}
56 }
57
58 // Client processor blockingly reads everything remote client sends,
59 // splits messages by CRLF and send them to Daemon gorouting for processing
60 // it futher. Also it can signalize that client is unavailable (disconnected).
61 func (client *Client) Processor(sink chan<- ClientEvent) {
62         var bufNet []byte
63         buf := make([]byte, 0)
64         log.Println(client, "New client")
65         sink <- ClientEvent{client, EventNew, ""}
66         for {
67                 bufNet = make([]byte, BufSize)
68                 _, err := client.conn.Read(bufNet)
69                 if err != nil {
70                         sink <- ClientEvent{client, EventDel, ""}
71                         break
72                 }
73                 bufNet = bytes.TrimRight(bufNet, "\x00")
74                 buf = append(buf, bufNet...)
75                 if !bytes.HasSuffix(buf, []byte(CRLF)) {
76                         continue
77                 }
78                 for _, msg := range bytes.Split(buf[:len(buf)-2], []byte(CRLF)) {
79                         if len(msg) > 0 {
80                                 sink <- ClientEvent{client, EventMsg, string(msg)}
81                         }
82                 }
83                 buf = []byte{}
84         }
85 }
86
87 // Send message as is with CRLF appended.
88 func (client *Client) Msg(text string) {
89         client.conn.Write([]byte(text + CRLF))
90 }
91
92 // Send message from server. It has ": servername" prefix.
93 func (client *Client) Reply(text string) {
94         client.Msg(":" + *client.hostname + " " + text)
95 }
96
97 // Send server message, concatenating all provided text parts and
98 // prefix the last one with ":".
99 func (client *Client) ReplyParts(code string, text ...string) {
100         parts := []string{code}
101         for _, t := range text {
102                 parts = append(parts, t)
103         }
104         parts[len(parts)-1] = ":" + parts[len(parts)-1]
105         client.Reply(strings.Join(parts, " "))
106 }
107
108 // Send nicknamed server message. After servername it always has target
109 // client's nickname. The last part is prefixed with ":".
110 func (client *Client) ReplyNicknamed(code string, text ...string) {
111         client.ReplyParts(code, append([]string{client.nickname}, text...)...)
112 }
113
114 // Reply "461 not enough parameters" error for given command.
115 func (client *Client) ReplyNotEnoughParameters(command string) {
116         client.ReplyNicknamed("461", command, "Not enough parameters")
117 }
118
119 // Reply "403 no such channel" error for specified channel.
120 func (client *Client) ReplyNoChannel(channel string) {
121         client.ReplyNicknamed("403", channel, "No such channel")
122 }
123
124 func (client *Client) ReplyNoNickChan(channel string) {
125         client.ReplyNicknamed("401", channel, "No such nick/channel")
126 }