source: code/trunk/server.go@ 7

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

Handle PING

File size: 4.1 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) handleMessage(msg *irc.Message) error {
67 switch msg.Command {
68 case "PING":
69 // TODO: handle params
70 return c.WriteMessage(&irc.Message{
71 Command: "PONG",
72 Params: []string{c.srv.Hostname},
73 })
74 default:
75 if c.registered {
76 return c.handleMessageRegistered(msg)
77 } else {
78 return c.handleMessageUnregistered(msg)
79 }
80 }
81}
82
83func (c *conn) handleMessageUnregistered(msg *irc.Message) error {
84 switch msg.Command {
85 case "NICK":
86 if len(msg.Params) != 1 {
87 return newNeedMoreParamsError(msg.Command)
88 }
89 c.nick = msg.Params[0]
90 case "USER":
91 if len(msg.Params) != 4 {
92 return newNeedMoreParamsError(msg.Command)
93 }
94 c.username = "~" + msg.Params[0]
95 c.realname = msg.Params[3]
96 case "QUIT":
97 return c.Close()
98 default:
99 return newUnknownCommandError(msg.Command)
100 }
101 if c.username != "" && c.nick != "" {
102 return c.register()
103 }
104 return nil
105}
106
107func (c *conn) register() error {
108 c.registered = true
109
110 err := c.WriteMessage(&irc.Message{
111 Command: irc.RPL_WELCOME,
112 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
113 })
114 if err != nil {
115 return err
116 }
117
118 err = c.WriteMessage(&irc.Message{
119 Command: irc.RPL_YOURHOST,
120 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
121 })
122 if err != nil {
123 return err
124 }
125
126 err = c.WriteMessage(&irc.Message{
127 Command: irc.RPL_CREATED,
128 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
129 })
130 if err != nil {
131 return err
132 }
133
134 err = c.WriteMessage(&irc.Message{
135 Command: irc.RPL_MYINFO,
136 Params: []string{c.nick, c.srv.Hostname, "unknown", "", ""},
137 })
138 if err != nil {
139 return err
140 }
141
142 err = c.WriteMessage(&irc.Message{
143 Command: irc.ERR_NOMOTD,
144 Params: []string{c.nick, "No MOTD"},
145 })
146 if err != nil {
147 return err
148 }
149
150 return nil
151}
152
153func (c *conn) handleMessageRegistered(msg *irc.Message) error {
154 switch msg.Command {
155 case "NICK", "USER":
156 return ircError{&irc.Message{
157 Command: irc.ERR_ALREADYREGISTERED,
158 Params: []string{
159 c.nick,
160 "You may not reregister",
161 },
162 }}
163 case "QUIT":
164 return c.Close()
165 default:
166 return newUnknownCommandError(msg.Command)
167 }
168}
169
170type Server struct{
171 Hostname string
172}
173
174func (s *Server) prefix() *irc.Prefix {
175 return &irc.Prefix{Name: s.Hostname}
176}
177
178func (s *Server) handleConn(netConn net.Conn) error {
179 c := conn{net: netConn, irc: irc.NewConn(netConn), srv: s}
180 defer c.Close()
181 for {
182 msg, err := c.irc.ReadMessage()
183 if err == io.EOF {
184 break
185 } else if err != nil {
186 return fmt.Errorf("failed to read IRC command: %v", err)
187 }
188 log.Println(msg)
189
190 err = c.handleMessage(msg)
191 if ircErr, ok := err.(ircError); ok {
192 ircErr.Message.Prefix = s.prefix()
193 if err := c.WriteMessage(ircErr.Message); err != nil {
194 return fmt.Errorf("failed to write IRC reply: %v", err)
195 }
196 } else if err != nil {
197 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
198 }
199
200 if c.closed {
201 return nil
202 }
203 }
204
205 return c.Close()
206}
207
208func (s *Server) Serve(ln net.Listener) error {
209 for {
210 c, err := ln.Accept()
211 if err != nil {
212 return fmt.Errorf("failed to accept connection: %v", err)
213 }
214
215 go func() {
216 if err := s.handleConn(c); err != nil {
217 log.Printf("error handling connection: %v", err)
218 }
219 }()
220 }
221}
Note: See TracBrowser for help on using the repository browser.