source: code/trunk/downstream.go@ 42

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

Allow changing nickname

File size: 5.2 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)
59 conn := &downstreamConn{
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 {
69 if err := conn.irc.WriteMessage(msg); err != nil {
70 conn.logger.Printf("failed to write message: %v", err)
71 }
72 }
73 }()
74
75 return conn
[22]76}
77
[27]78func (c *downstreamConn) prefix() *irc.Prefix {
79 return &irc.Prefix{
80 Name: c.nick,
81 User: c.username,
82 // TODO: fill the host?
83 }
84}
85
[22]86func (c *downstreamConn) readMessages() error {
87 c.logger.Printf("new connection")
88 defer c.Close()
89
90 for {
91 msg, err := c.irc.ReadMessage()
92 if err == io.EOF {
93 break
94 } else if err != nil {
95 return fmt.Errorf("failed to read IRC command: %v", err)
96 }
97
98 err = c.handleMessage(msg)
99 if ircErr, ok := err.(ircError); ok {
100 ircErr.Message.Prefix = c.srv.prefix()
[26]101 c.messages <- ircErr.Message
[22]102 } else if err != nil {
103 return fmt.Errorf("failed to handle IRC command %q: %v", msg.Command, err)
104 }
105
106 if c.closed {
107 return nil
108 }
109 }
110
111 return c.Close()
112}
113
[13]114func (c *downstreamConn) Close() error {
[26]115 if c.closed {
116 return fmt.Errorf("downstream connection already closed")
117 }
[40]118
119 if u := c.user; u != nil {
120 u.lock.Lock()
121 for i := range u.downstreamConns {
122 if u.downstreamConns[i] == c {
123 u.downstreamConns = append(u.downstreamConns[:i], u.downstreamConns[i+1:]...)
124 }
125 }
126 u.lock.Unlock()
[13]127 }
[40]128
[26]129 close(c.messages)
[13]130 c.closed = true
[40]131
132 return c.net.Close()
[13]133}
134
135func (c *downstreamConn) handleMessage(msg *irc.Message) error {
136 switch msg.Command {
[28]137 case "QUIT":
138 return c.Close()
[13]139 case "PING":
140 // TODO: handle params
[31]141 c.messages <- &irc.Message{
142 Prefix: c.srv.prefix(),
[13]143 Command: "PONG",
144 Params: []string{c.srv.Hostname},
[31]145 }
[26]146 return nil
[13]147 default:
148 if c.registered {
149 return c.handleMessageRegistered(msg)
150 } else {
151 return c.handleMessageUnregistered(msg)
152 }
153 }
154}
155
156func (c *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
157 switch msg.Command {
158 case "NICK":
159 if len(msg.Params) != 1 {
160 return newNeedMoreParamsError(msg.Command)
161 }
162 c.nick = msg.Params[0]
163 case "USER":
164 if len(msg.Params) != 4 {
165 return newNeedMoreParamsError(msg.Command)
166 }
167 c.username = "~" + msg.Params[0]
168 c.realname = msg.Params[3]
169 default:
[22]170 c.logger.Printf("unhandled message: %v", msg)
[13]171 return newUnknownCommandError(msg.Command)
172 }
173 if c.username != "" && c.nick != "" {
174 return c.register()
175 }
176 return nil
177}
178
179func (c *downstreamConn) register() error {
[39]180 u := c.srv.getUser(strings.TrimPrefix(c.username, "~"))
[38]181 if u == nil {
[39]182 c.logger.Printf("failed authentication: unknown username %q", c.username)
[37]183 c.messages <- &irc.Message{
184 Prefix: c.srv.prefix(),
185 Command: irc.ERR_PASSWDMISMATCH,
186 Params: []string{"*", "Invalid username or password"},
187 }
188 return nil
189 }
190
[13]191 c.registered = true
[37]192 c.user = u
[13]193
[40]194 u.lock.Lock()
195 u.downstreamConns = append(u.downstreamConns, c)
196 u.lock.Unlock()
197
[31]198 c.messages <- &irc.Message{
199 Prefix: c.srv.prefix(),
[13]200 Command: irc.RPL_WELCOME,
201 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
[31]202 }
203 c.messages <- &irc.Message{
204 Prefix: c.srv.prefix(),
[13]205 Command: irc.RPL_YOURHOST,
206 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
[31]207 }
208 c.messages <- &irc.Message{
209 Prefix: c.srv.prefix(),
[13]210 Command: irc.RPL_CREATED,
211 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
[31]212 }
213 c.messages <- &irc.Message{
214 Prefix: c.srv.prefix(),
[13]215 Command: irc.RPL_MYINFO,
[28]216 Params: []string{c.nick, c.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
[31]217 }
218 c.messages <- &irc.Message{
219 Prefix: c.srv.prefix(),
[13]220 Command: irc.ERR_NOMOTD,
221 Params: []string{c.nick, "No MOTD"},
[31]222 }
[13]223
[39]224 u.forEachUpstream(func(uc *upstreamConn) {
[30]225 // TODO: fix races accessing upstream connection data
226 for _, ch := range uc.channels {
227 if ch.complete {
228 forwardChannel(c, ch)
229 }
230 }
[39]231 })
[13]232 return nil
233}
234
235func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
236 switch msg.Command {
[42]237 case "USER":
[13]238 return ircError{&irc.Message{
239 Command: irc.ERR_ALREADYREGISTERED,
[42]240 Params: []string{c.nick, "You may not reregister"},
[13]241 }}
[42]242 case "NICK":
243 c.user.forEachUpstream(func(uc *upstreamConn) {
244 uc.messages <- msg
245 })
[13]246 default:
[22]247 c.logger.Printf("unhandled message: %v", msg)
[13]248 return newUnknownCommandError(msg.Command)
249 }
[42]250 return nil
[13]251}
Note: See TracBrowser for help on using the repository browser.