source: code/trunk/downstream.go@ 55

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

Abbreviate {upstream,downstream}Conn with uc and dc

This makes it clearer than just c when we manipulate both kinds at the
same time.

File size: 7.5 KB
RevLine 
[13]1package jounce
2
3import (
4 "fmt"
5 "io"
6 "net"
[39]7 "strings"
[13]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 downstreamConn struct {
[26]43 net net.Conn
44 irc *irc.Conn
45 srv *Server
46 logger Logger
47 messages chan<- *irc.Message
[22]48
[13]49 registered bool
[37]50 user *user
[13]51 closed bool
52 nick string
53 username string
54 realname string
55}
56
[22]57func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
[26]58 msgs := make(chan *irc.Message, 64)
[55]59 dc := &downstreamConn{
[26]60 net: netConn,
61 irc: irc.NewConn(netConn),
62 srv: srv,
63 logger: &prefixLogger{srv.Logger, fmt.Sprintf("downstream %q: ", netConn.RemoteAddr())},
64 messages: msgs,
[22]65 }
[26]66
67 go func() {
68 for msg := range msgs {
[55]69 if err := dc.irc.WriteMessage(msg); err != nil {
70 dc.logger.Printf("failed to write message: %v", err)
[26]71 }
72 }
[55]73 if err := dc.net.Close(); err != nil {
74 dc.logger.Printf("failed to close connection: %v", err)
[45]75 } else {
[55]76 dc.logger.Printf("connection closed")
[45]77 }
[26]78 }()
79
[55]80 return dc
[22]81}
82
[55]83func (dc *downstreamConn) prefix() *irc.Prefix {
[27]84 return &irc.Prefix{
[55]85 Name: dc.nick,
86 User: dc.username,
[27]87 // TODO: fill the host?
88 }
89}
90
[55]91func (dc *downstreamConn) readMessages() error {
92 dc.logger.Printf("new connection")
[22]93
94 for {
[55]95 msg, err := dc.irc.ReadMessage()
[22]96 if err == io.EOF {
97 break
98 } else if err != nil {
99 return fmt.Errorf("failed to read IRC command: %v", err)
100 }
101
[55]102 err = dc.handleMessage(msg)
[22]103 if ircErr, ok := err.(ircError); ok {
[55]104 ircErr.Message.Prefix = dc.srv.prefix()
105 dc.SendMessage(ircErr.Message)
[22]106 } else if err != nil {
107 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
108 }
109
[55]110 if dc.closed {
[22]111 return nil
112 }
113 }
114
[45]115 return nil
[22]116}
117
[55]118func (dc *downstreamConn) Close() error {
119 if dc.closed {
[26]120 return fmt.Errorf("downstream connection already closed")
121 }
[40]122
[55]123 if u := dc.user; u != nil {
[40]124 u.lock.Lock()
125 for i := range u.downstreamConns {
[55]126 if u.downstreamConns[i] == dc {
[40]127 u.downstreamConns = append(u.downstreamConns[:i], u.downstreamConns[i+1:]...)
128 }
129 }
130 u.lock.Unlock()
[51]131
132 // TODO: figure out a better way to advance the ring buffer consumer cursor
133 u.forEachUpstream(func(uc *upstreamConn) {
134 // TODO: let clients specify the ring buffer name in their username
135 uc.ring.Consumer("").Reset()
136 })
[13]137 }
[40]138
[55]139 close(dc.messages)
140 dc.closed = true
[40]141
[45]142 return nil
[13]143}
144
[55]145func (dc *downstreamConn) SendMessage(msg *irc.Message) {
146 dc.messages <- msg
[54]147}
148
[55]149func (dc *downstreamConn) handleMessage(msg *irc.Message) error {
[13]150 switch msg.Command {
[28]151 case "QUIT":
[55]152 return dc.Close()
[13]153 case "PING":
154 // TODO: handle params
[55]155 dc.SendMessage(&irc.Message{
156 Prefix: dc.srv.prefix(),
[13]157 Command: "PONG",
[55]158 Params: []string{dc.srv.Hostname},
[54]159 })
[26]160 return nil
[13]161 default:
[55]162 if dc.registered {
163 return dc.handleMessageRegistered(msg)
[13]164 } else {
[55]165 return dc.handleMessageUnregistered(msg)
[13]166 }
167 }
168}
169
[55]170func (dc *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
[13]171 switch msg.Command {
172 case "NICK":
[55]173 if err := parseMessageParams(msg, &dc.nick); err != nil {
[43]174 return err
[13]175 }
176 case "USER":
[43]177 var username string
[55]178 if err := parseMessageParams(msg, &username, nil, nil, &dc.realname); err != nil {
[43]179 return err
[13]180 }
[55]181 dc.username = "~" + username
[13]182 default:
[55]183 dc.logger.Printf("unhandled message: %v", msg)
[13]184 return newUnknownCommandError(msg.Command)
185 }
[55]186 if dc.username != "" && dc.nick != "" {
187 return dc.register()
[13]188 }
189 return nil
190}
191
[55]192func (dc *downstreamConn) register() error {
193 u := dc.srv.getUser(strings.TrimPrefix(dc.username, "~"))
[38]194 if u == nil {
[55]195 dc.logger.Printf("failed authentication: unknown username %q", dc.username)
196 dc.SendMessage(&irc.Message{
197 Prefix: dc.srv.prefix(),
[37]198 Command: irc.ERR_PASSWDMISMATCH,
199 Params: []string{"*", "Invalid username or password"},
[54]200 })
[37]201 return nil
202 }
203
[55]204 dc.registered = true
205 dc.user = u
[13]206
[40]207 u.lock.Lock()
[55]208 u.downstreamConns = append(u.downstreamConns, dc)
[40]209 u.lock.Unlock()
210
[55]211 dc.SendMessage(&irc.Message{
212 Prefix: dc.srv.prefix(),
[13]213 Command: irc.RPL_WELCOME,
[55]214 Params: []string{dc.nick, "Welcome to jounce, " + dc.nick},
[54]215 })
[55]216 dc.SendMessage(&irc.Message{
217 Prefix: dc.srv.prefix(),
[13]218 Command: irc.RPL_YOURHOST,
[55]219 Params: []string{dc.nick, "Your host is " + dc.srv.Hostname},
[54]220 })
[55]221 dc.SendMessage(&irc.Message{
222 Prefix: dc.srv.prefix(),
[13]223 Command: irc.RPL_CREATED,
[55]224 Params: []string{dc.nick, "Who cares when the server was created?"},
[54]225 })
[55]226 dc.SendMessage(&irc.Message{
227 Prefix: dc.srv.prefix(),
[13]228 Command: irc.RPL_MYINFO,
[55]229 Params: []string{dc.nick, dc.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
[54]230 })
[55]231 dc.SendMessage(&irc.Message{
232 Prefix: dc.srv.prefix(),
[13]233 Command: irc.ERR_NOMOTD,
[55]234 Params: []string{dc.nick, "No MOTD"},
[54]235 })
[13]236
[39]237 u.forEachUpstream(func(uc *upstreamConn) {
[30]238 // TODO: fix races accessing upstream connection data
239 for _, ch := range uc.channels {
240 if ch.complete {
[55]241 forwardChannel(dc, ch)
[30]242 }
243 }
[50]244
[51]245 // TODO: let clients specify the ring buffer name in their username
246 consumer := uc.ring.Consumer("")
[50]247 for {
[51]248 // TODO: these messages will get lost if the connection is closed
[50]249 msg := consumer.Consume()
250 if msg == nil {
251 break
252 }
[55]253 dc.SendMessage(msg)
[50]254 }
[39]255 })
[50]256
[13]257 return nil
258}
259
[55]260func (dc *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
[13]261 switch msg.Command {
[42]262 case "USER":
[13]263 return ircError{&irc.Message{
264 Command: irc.ERR_ALREADYREGISTERED,
[55]265 Params: []string{dc.nick, "You may not reregister"},
[13]266 }}
[42]267 case "NICK":
[55]268 dc.user.forEachUpstream(func(uc *upstreamConn) {
[42]269 uc.messages <- msg
270 })
[48]271 case "JOIN":
272 var name string
273 if err := parseMessageParams(msg, &name); err != nil {
274 return err
275 }
276
[55]277 if ch, _ := dc.user.getChannel(name); ch != nil {
[48]278 break // already joined
279 }
280
281 // TODO: extract network name from channel name
282 return ircError{&irc.Message{
283 Command: irc.ERR_NOSUCHCHANNEL,
284 Params: []string{name, "Channel name ambiguous"},
285 }}
[49]286 case "PART":
287 var name string
288 if err := parseMessageParams(msg, &name); err != nil {
289 return err
290 }
291
[55]292 ch, err := dc.user.getChannel(name)
[49]293 if err != nil {
294 return err
295 }
296
297 ch.conn.messages <- msg
298 // TODO: remove channel from upstream config
[46]299 case "MODE":
300 var name string
301 if err := parseMessageParams(msg, &name); err != nil {
302 return err
303 }
304
305 var modeStr string
306 if len(msg.Params) > 1 {
307 modeStr = msg.Params[1]
308 }
309
310 if msg.Prefix.Name != name {
[55]311 ch, err := dc.user.getChannel(name)
[46]312 if err != nil {
313 return err
314 }
315
316 if modeStr != "" {
317 ch.conn.messages <- msg
318 } else {
[55]319 dc.SendMessage(&irc.Message{
320 Prefix: dc.srv.prefix(),
[46]321 Command: irc.RPL_CHANNELMODEIS,
322 Params: []string{ch.Name, string(ch.modes)},
[54]323 })
[46]324 }
325 } else {
[55]326 if name != dc.nick {
[46]327 return ircError{&irc.Message{
328 Command: irc.ERR_USERSDONTMATCH,
[55]329 Params: []string{dc.nick, "Cannot change mode for other users"},
[46]330 }}
331 }
332
333 if modeStr != "" {
[55]334 dc.user.forEachUpstream(func(uc *upstreamConn) {
[46]335 uc.messages <- msg
336 })
337 } else {
[55]338 dc.SendMessage(&irc.Message{
339 Prefix: dc.srv.prefix(),
[46]340 Command: irc.RPL_UMODEIS,
341 Params: []string{""}, // TODO
[54]342 })
[46]343 }
344 }
[13]345 default:
[55]346 dc.logger.Printf("unhandled message: %v", msg)
[13]347 return newUnknownCommandError(msg.Command)
348 }
[42]349 return nil
[13]350}
Note: See TracBrowser for help on using the repository browser.