source: code/trunk/server.go@ 6

Last change on this file since 6 was 6, checked in by contact, 5 years ago

Send ERR_NOMOTD on registration

File size: 3.8 KB
Line 
1package jounce
2
3import (
4 "fmt"
5 "io"
6 "log"
7 "net"
8
9 "gopkg.in/irc.v3"
10)
11
12type ircError struct {
13 Message *irc.Message
14}
15
16func newUnknownCommandError(cmd string) ircError {
17 return ircError{&irc.Message{
18 Command: irc.ERR_UNKNOWNCOMMAND,
19 Params: []string{
20 "*",
21 cmd,
22 "Unknown command",
23 },
24 }}
25}
26
27func newNeedMoreParamsError(cmd string) ircError {
28 return ircError{&irc.Message{
29 Command: irc.ERR_NEEDMOREPARAMS,
30 Params: []string{
31 "*",
32 cmd,
33 "Not enough parameters",
34 },
35 }}
36}
37
38func (err ircError) Error() string {
39 return err.Message.String()
40}
41
42type conn struct {
43 net net.Conn
44 irc *irc.Conn
45 srv *Server
46 registered bool
47 closed bool
48 nick string
49 username string
50 realname string
51}
52
53func (c *conn) Close() error {
54 if err := c.net.Close(); err != nil {
55 return err
56 }
57 c.closed = true
58 return nil
59}
60
61func (c *conn) WriteMessage(msg *irc.Message) error {
62 msg.Prefix = c.srv.prefix()
63 return c.irc.WriteMessage(msg)
64}
65
66func (c *conn) handleMessageUnregistered(msg *irc.Message) error {
67 switch msg.Command {
68 case "NICK":
69 if len(msg.Params) != 1 {
70 return newNeedMoreParamsError(msg.Command)
71 }
72 c.nick = msg.Params[0]
73 case "USER":
74 if len(msg.Params) != 4 {
75 return newNeedMoreParamsError(msg.Command)
76 }
77 c.username = "~" + msg.Params[0]
78 c.realname = msg.Params[3]
79 case "QUIT":
80 return c.Close()
81 default:
82 return newUnknownCommandError(msg.Command)
83 }
84 if c.username != "" && c.nick != "" {
85 return c.register()
86 }
87 return nil
88}
89
90func (c *conn) register() error {
91 c.registered = true
92
93 err := c.WriteMessage(&irc.Message{
94 Command: irc.RPL_WELCOME,
95 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
96 })
97 if err != nil {
98 return err
99 }
100
101 err = c.WriteMessage(&irc.Message{
102 Command: irc.RPL_YOURHOST,
103 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
104 })
105 if err != nil {
106 return err
107 }
108
109 err = c.WriteMessage(&irc.Message{
110 Command: irc.RPL_CREATED,
111 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
112 })
113 if err != nil {
114 return err
115 }
116
117 err = c.WriteMessage(&irc.Message{
118 Command: irc.RPL_MYINFO,
119 Params: []string{c.nick, c.srv.Hostname, "unknown", "", ""},
120 })
121 if err != nil {
122 return err
123 }
124
125 err = c.WriteMessage(&irc.Message{
126 Command: irc.ERR_NOMOTD,
127 Params: []string{c.nick, "No MOTD"},
128 })
129 if err != nil {
130 return err
131 }
132
133 return nil
134}
135
136func (c *conn) handleMessage(msg *irc.Message) error {
137 switch msg.Command {
138 case "NICK", "USER":
139 return ircError{&irc.Message{
140 Command: irc.ERR_ALREADYREGISTERED,
141 Params: []string{
142 c.nick,
143 "You may not reregister",
144 },
145 }}
146 case "QUIT":
147 return c.Close()
148 default:
149 return newUnknownCommandError(msg.Command)
150 }
151}
152
153type Server struct{
154 Hostname string
155}
156
157func (s *Server) prefix() *irc.Prefix {
158 return &irc.Prefix{Name: s.Hostname}
159}
160
161func (s *Server) handleConn(netConn net.Conn) error {
162 c := conn{net: netConn, irc: irc.NewConn(netConn), srv: s}
163 defer c.Close()
164 for {
165 msg, err := c.irc.ReadMessage()
166 if err == io.EOF {
167 break
168 } else if err != nil {
169 return fmt.Errorf("failed to read IRC command: %v", err)
170 }
171 log.Println(msg)
172
173 if c.registered {
174 err = c.handleMessage(msg)
175 } else {
176 err = c.handleMessageUnregistered(msg)
177 }
178 if ircErr, ok := err.(ircError); ok {
179 ircErr.Message.Prefix = s.prefix()
180 if err := c.WriteMessage(ircErr.Message); err != nil {
181 return fmt.Errorf("failed to write IRC reply: %v", err)
182 }
183 } else if err != nil {
184 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
185 }
186
187 if c.closed {
188 return nil
189 }
190 }
191
192 return c.Close()
193}
194
195func (s *Server) Serve(ln net.Listener) error {
196 for {
197 c, err := ln.Accept()
198 if err != nil {
199 return fmt.Errorf("failed to accept connection: %v", err)
200 }
201
202 go func() {
203 if err := s.handleConn(c); err != nil {
204 log.Printf("error handling connection: %v", err)
205 }
206 }()
207 }
208}
Note: See TracBrowser for help on using the repository browser.