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
RevLine 
[1]1package jounce
2
3import (
4 "fmt"
[3]5 "io"
[1]6 "log"
7 "net"
8
9 "gopkg.in/irc.v3"
10)
11
[4]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
[3]42type conn struct {
[4]43 net net.Conn
44 irc *irc.Conn
[5]45 srv *Server
[4]46 registered bool
[5]47 closed bool
[4]48 nick string
49 username string
50 realname string
[3]51}
[1]52
[5]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
[4]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]
[5]79 case "QUIT":
80 return c.Close()
[4]81 default:
82 return newUnknownCommandError(msg.Command)
83 }
[5]84 if c.username != "" && c.nick != "" {
85 return c.register()
86 }
[4]87 return nil
88}
89
[5]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
[6]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
[5]133 return nil
134}
135
[4]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 }}
[5]146 case "QUIT":
147 return c.Close()
[4]148 default:
149 return newUnknownCommandError(msg.Command)
150 }
151}
152
[5]153type Server struct{
154 Hostname string
155}
[3]156
[5]157func (s *Server) prefix() *irc.Prefix {
158 return &irc.Prefix{Name: s.Hostname}
159}
160
[3]161func (s *Server) handleConn(netConn net.Conn) error {
[5]162 c := conn{net: netConn, irc: irc.NewConn(netConn), srv: s}
163 defer c.Close()
[1]164 for {
[4]165 msg, err := c.irc.ReadMessage()
[3]166 if err == io.EOF {
167 break
168 } else if err != nil {
[4]169 return fmt.Errorf("failed to read IRC command: %v", err)
[1]170 }
[3]171 log.Println(msg)
[1]172
[4]173 if c.registered {
174 err = c.handleMessage(msg)
175 } else {
176 err = c.handleMessageUnregistered(msg)
[3]177 }
[4]178 if ircErr, ok := err.(ircError); ok {
[5]179 ircErr.Message.Prefix = s.prefix()
180 if err := c.WriteMessage(ircErr.Message); err != nil {
[4]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 }
[5]186
187 if c.closed {
188 return nil
189 }
[1]190 }
[3]191
[5]192 return c.Close()
[1]193}
194
[3]195func (s *Server) Serve(ln net.Listener) error {
[1]196 for {
[3]197 c, err := ln.Accept()
[1]198 if err != nil {
199 return fmt.Errorf("failed to accept connection: %v", err)
200 }
201
202 go func() {
[3]203 if err := s.handleConn(c); err != nil {
[1]204 log.Printf("error handling connection: %v", err)
205 }
206 }()
207 }
208}
Note: See TracBrowser for help on using the repository browser.