source: code/trunk/downstream.go@ 38

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

Add Server.getUser

File size: 4.8 KB
RevLine 
[13]1package jounce
2
3import (
4 "fmt"
5 "io"
6 "net"
7
8 "gopkg.in/irc.v3"
9)
10
11type ircError struct {
12 Message *irc.Message
13}
14
15func newUnknownCommandError(cmd string) ircError {
16 return ircError{&irc.Message{
17 Command: irc.ERR_UNKNOWNCOMMAND,
18 Params: []string{
19 "*",
20 cmd,
21 "Unknown command",
22 },
23 }}
24}
25
26func newNeedMoreParamsError(cmd string) ircError {
27 return ircError{&irc.Message{
28 Command: irc.ERR_NEEDMOREPARAMS,
29 Params: []string{
30 "*",
31 cmd,
32 "Not enough parameters",
33 },
34 }}
35}
36
37func (err ircError) Error() string {
38 return err.Message.String()
39}
40
41type downstreamConn struct {
[26]42 net net.Conn
43 irc *irc.Conn
44 srv *Server
45 logger Logger
46 messages chan<- *irc.Message
[22]47
[13]48 registered bool
[37]49 user *user
[13]50 closed bool
51 nick string
52 username string
53 realname string
54}
55
[22]56func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
[26]57 msgs := make(chan *irc.Message, 64)
58 conn := &downstreamConn{
59 net: netConn,
60 irc: irc.NewConn(netConn),
61 srv: srv,
62 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
63 messages: msgs,
[22]64 }
[26]65
66 go func() {
67 for msg := range msgs {
68 if err := conn.irc.WriteMessage(msg); err != nil {
69 conn.logger.Printf("failed to write message: %v", err)
70 }
71 }
72 }()
73
74 return conn
[22]75}
76
[27]77func (c *downstreamConn) prefix() *irc.Prefix {
78 return &irc.Prefix{
79 Name: c.nick,
80 User: c.username,
81 // TODO: fill the host?
82 }
83}
84
[22]85func (c *downstreamConn) readMessages() error {
86 c.logger.Printf("new connection")
87 defer c.Close()
88
89 for {
90 msg, err := c.irc.ReadMessage()
91 if err == io.EOF {
92 break
93 } else if err != nil {
94 return fmt.Errorf("failed to read IRC command: %v", err)
95 }
96
97 err = c.handleMessage(msg)
98 if ircErr, ok := err.(ircError); ok {
99 ircErr.Message.Prefix = c.srv.prefix()
[26]100 c.messages <- ircErr.Message
[22]101 } else if err != nil {
102 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
103 }
104
105 if c.closed {
106 return nil
107 }
108 }
109
110 return c.Close()
111}
112
[13]113func (c *downstreamConn) Close() error {
[26]114 if c.closed {
115 return fmt.Errorf("downstream connection already closed")
116 }
[13]117 if err := c.net.Close(); err != nil {
118 return err
119 }
[26]120 close(c.messages)
[13]121 c.closed = true
122 return nil
123}
124
125func (c *downstreamConn) handleMessage(msg *irc.Message) error {
126 switch msg.Command {
[28]127 case "QUIT":
128 return c.Close()
[13]129 case "PING":
130 // TODO: handle params
[31]131 c.messages <- &irc.Message{
132 Prefix: c.srv.prefix(),
[13]133 Command: "PONG",
134 Params: []string{c.srv.Hostname},
[31]135 }
[26]136 return nil
[13]137 default:
138 if c.registered {
139 return c.handleMessageRegistered(msg)
140 } else {
141 return c.handleMessageUnregistered(msg)
142 }
143 }
144}
145
146func (c *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
147 switch msg.Command {
148 case "NICK":
149 if len(msg.Params) != 1 {
150 return newNeedMoreParamsError(msg.Command)
151 }
152 c.nick = msg.Params[0]
153 case "USER":
154 if len(msg.Params) != 4 {
155 return newNeedMoreParamsError(msg.Command)
156 }
157 c.username = "~" + msg.Params[0]
158 c.realname = msg.Params[3]
159 default:
[22]160 c.logger.Printf("unhandled message: %v", msg)
[13]161 return newUnknownCommandError(msg.Command)
162 }
163 if c.username != "" && c.nick != "" {
164 return c.register()
165 }
166 return nil
167}
168
169func (c *downstreamConn) register() error {
[38]170 u := c.srv.getUser(c.username)
171 if u == nil {
[37]172 c.messages <- &irc.Message{
173 Prefix: c.srv.prefix(),
174 Command: irc.ERR_PASSWDMISMATCH,
175 Params: []string{"*", "Invalid username or password"},
176 }
177 return nil
178 }
179
[13]180 c.registered = true
[37]181 c.user = u
[13]182
[31]183 c.messages <- &irc.Message{
184 Prefix: c.srv.prefix(),
[13]185 Command: irc.RPL_WELCOME,
186 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
[31]187 }
188 c.messages <- &irc.Message{
189 Prefix: c.srv.prefix(),
[13]190 Command: irc.RPL_YOURHOST,
191 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
[31]192 }
193 c.messages <- &irc.Message{
194 Prefix: c.srv.prefix(),
[13]195 Command: irc.RPL_CREATED,
196 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
[31]197 }
198 c.messages <- &irc.Message{
199 Prefix: c.srv.prefix(),
[13]200 Command: irc.RPL_MYINFO,
[28]201 Params: []string{c.nick, c.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
[31]202 }
203 c.messages <- &irc.Message{
204 Prefix: c.srv.prefix(),
[13]205 Command: irc.ERR_NOMOTD,
206 Params: []string{c.nick, "No MOTD"},
[31]207 }
[13]208
[37]209 u.lock.Lock()
210 for _, uc := range u.upstreamConns {
[30]211 // TODO: fix races accessing upstream connection data
212 if !uc.registered {
213 continue
214 }
215 for _, ch := range uc.channels {
216 if ch.complete {
217 forwardChannel(c, ch)
218 }
219 }
220 }
[37]221 u.lock.Unlock()
[30]222
[13]223 return nil
224}
225
226func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
227 switch msg.Command {
228 case "NICK", "USER":
229 return ircError{&irc.Message{
230 Command: irc.ERR_ALREADYREGISTERED,
231 Params: []string{
232 c.nick,
233 "You may not reregister",
234 },
235 }}
236 default:
[22]237 c.logger.Printf("unhandled message: %v", msg)
[13]238 return newUnknownCommandError(msg.Command)
239 }
240}
Note: See TracBrowser for help on using the repository browser.