source: code/trunk/downstream.go@ 37

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

Per-user connections

File size: 4.9 KB
Line 
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 {
42 net net.Conn
43 irc *irc.Conn
44 srv *Server
45 logger Logger
46 messages chan<- *irc.Message
47
48 registered bool
49 user *user
50 closed bool
51 nick string
52 username string
53 realname string
54}
55
56func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
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,
64 }
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
75}
76
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
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()
100 c.messages <- ircErr.Message
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
113func (c *downstreamConn) Close() error {
114 if c.closed {
115 return fmt.Errorf("downstream connection already closed")
116 }
117 if err := c.net.Close(); err != nil {
118 return err
119 }
120 close(c.messages)
121 c.closed = true
122 return nil
123}
124
125func (c *downstreamConn) handleMessage(msg *irc.Message) error {
126 switch msg.Command {
127 case "QUIT":
128 return c.Close()
129 case "PING":
130 // TODO: handle params
131 c.messages <- &irc.Message{
132 Prefix: c.srv.prefix(),
133 Command: "PONG",
134 Params: []string{c.srv.Hostname},
135 }
136 return nil
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:
160 c.logger.Printf("unhandled message: %v", msg)
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 {
170 c.srv.lock.Lock()
171 u, ok := c.srv.users[c.username]
172 c.srv.lock.Unlock()
173
174 if !ok {
175 c.messages <- &irc.Message{
176 Prefix: c.srv.prefix(),
177 Command: irc.ERR_PASSWDMISMATCH,
178 Params: []string{"*", "Invalid username or password"},
179 }
180 return nil
181 }
182
183 c.registered = true
184 c.user = u
185
186 c.messages <- &irc.Message{
187 Prefix: c.srv.prefix(),
188 Command: irc.RPL_WELCOME,
189 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
190 }
191 c.messages <- &irc.Message{
192 Prefix: c.srv.prefix(),
193 Command: irc.RPL_YOURHOST,
194 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
195 }
196 c.messages <- &irc.Message{
197 Prefix: c.srv.prefix(),
198 Command: irc.RPL_CREATED,
199 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
200 }
201 c.messages <- &irc.Message{
202 Prefix: c.srv.prefix(),
203 Command: irc.RPL_MYINFO,
204 Params: []string{c.nick, c.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
205 }
206 c.messages <- &irc.Message{
207 Prefix: c.srv.prefix(),
208 Command: irc.ERR_NOMOTD,
209 Params: []string{c.nick, "No MOTD"},
210 }
211
212 u.lock.Lock()
213 for _, uc := range u.upstreamConns {
214 // TODO: fix races accessing upstream connection data
215 if !uc.registered {
216 continue
217 }
218 for _, ch := range uc.channels {
219 if ch.complete {
220 forwardChannel(c, ch)
221 }
222 }
223 }
224 u.lock.Unlock()
225
226 return nil
227}
228
229func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
230 switch msg.Command {
231 case "NICK", "USER":
232 return ircError{&irc.Message{
233 Command: irc.ERR_ALREADYREGISTERED,
234 Params: []string{
235 c.nick,
236 "You may not reregister",
237 },
238 }}
239 default:
240 c.logger.Printf("unhandled message: %v", msg)
241 return newUnknownCommandError(msg.Command)
242 }
243}
Note: See TracBrowser for help on using the repository browser.