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