source: code/trunk/server.go@ 8

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

Rename conn to downstreamConn

File size: 4.2 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
[8]42type downstreamConn 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
[8]53func (c *downstreamConn) Close() error {
[5]54 if err := c.net.Close(); err != nil {
55 return err
56 }
57 c.closed = true
58 return nil
59}
60
[8]61func (c *downstreamConn) WriteMessage(msg *irc.Message) error {
[5]62 msg.Prefix = c.srv.prefix()
63 return c.irc.WriteMessage(msg)
64}
65
[8]66func (c *downstreamConn) handleMessage(msg *irc.Message) error {
[7]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
[8]83func (c *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[4]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]
[5]96 case "QUIT":
97 return c.Close()
[4]98 default:
99 return newUnknownCommandError(msg.Command)
100 }
[5]101 if c.username != "" && c.nick != "" {
102 return c.register()
103 }
[4]104 return nil
105}
106
[8]107func (c *downstreamConn) register() error {
[5]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
[6]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
[5]150 return nil
151}
152
[8]153func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[4]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 }}
[5]163 case "QUIT":
164 return c.Close()
[4]165 default:
166 return newUnknownCommandError(msg.Command)
167 }
168}
169
[5]170type Server struct{
171 Hostname string
172}
[3]173
[5]174func (s *Server) prefix() *irc.Prefix {
175 return &irc.Prefix{Name: s.Hostname}
176}
177
[3]178func (s *Server) handleConn(netConn net.Conn) error {
[8]179 c := downstreamConn{net: netConn, irc: irc.NewConn(netConn), srv: s}
[5]180 defer c.Close()
[1]181 for {
[4]182 msg, err := c.irc.ReadMessage()
[3]183 if err == io.EOF {
184 break
185 } else if err != nil {
[4]186 return fmt.Errorf("failed to read IRC command: %v", err)
[1]187 }
[3]188 log.Println(msg)
[1]189
[7]190 err = c.handleMessage(msg)
[4]191 if ircErr, ok := err.(ircError); ok {
[5]192 ircErr.Message.Prefix = s.prefix()
193 if err := c.WriteMessage(ircErr.Message); err != nil {
[4]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 }
[5]199
200 if c.closed {
201 return nil
202 }
[1]203 }
[3]204
[5]205 return c.Close()
[1]206}
207
[3]208func (s *Server) Serve(ln net.Listener) error {
[1]209 for {
[3]210 c, err := ln.Accept()
[1]211 if err != nil {
212 return fmt.Errorf("failed to accept connection: %v", err)
213 }
214
215 go func() {
[3]216 if err := s.handleConn(c); err != nil {
[1]217 log.Printf("error handling connection: %v", err)
218 }
219 }()
220 }
221}
Note: See TracBrowser for help on using the repository browser.