source: code/trunk/server.go@ 5

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

Add connection registration

File size: 3.7 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 return nil
126}
127
128func (c *conn) handleMessage(msg *irc.Message) error {
129 switch msg.Command {
130 case "NICK", "USER":
131 return ircError{&irc.Message{
132 Command: irc.ERR_ALREADYREGISTERED,
133 Params: []string{
134 c.nick,
135 "You may not reregister",
136 },
137 }}
138 case "QUIT":
139 return c.Close()
140 default:
141 return newUnknownCommandError(msg.Command)
142 }
143}
144
145type Server struct{
146 Hostname string
147}
148
149func (s *Server) prefix() *irc.Prefix {
150 return &irc.Prefix{Name: s.Hostname}
151}
152
153func (s *Server) handleConn(netConn net.Conn) error {
154 c := conn{net: netConn, irc: irc.NewConn(netConn), srv: s}
155 defer c.Close()
156 for {
157 msg, err := c.irc.ReadMessage()
158 if err == io.EOF {
159 break
160 } else if err != nil {
161 return fmt.Errorf("failed to read IRC command: %v", err)
162 }
163 log.Println(msg)
164
165 if c.registered {
166 err = c.handleMessage(msg)
167 } else {
168 err = c.handleMessageUnregistered(msg)
169 }
170 if ircErr, ok := err.(ircError); ok {
171 ircErr.Message.Prefix = s.prefix()
172 if err := c.WriteMessage(ircErr.Message); err != nil {
173 return fmt.Errorf("failed to write IRC reply: %v", err)
174 }
175 } else if err != nil {
176 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
177 }
178
179 if c.closed {
180 return nil
181 }
182 }
183
184 return c.Close()
185}
186
187func (s *Server) Serve(ln net.Listener) error {
188 for {
189 c, err := ln.Accept()
190 if err != nil {
191 return fmt.Errorf("failed to accept connection: %v", err)
192 }
193
194 go func() {
195 if err := s.handleConn(c); err != nil {
196 log.Printf("error handling connection: %v", err)
197 }
198 }()
199 }
200}
Note: See TracBrowser for help on using the repository browser.