source: code/trunk/downstream.go@ 39

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

Add user.forEachUpstream

File size: 4.9 KB
Line 
1package jounce
2
3import (
4 "fmt"
5 "io"
6 "net"
7 "strings"
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 {
43 net net.Conn
44 irc *irc.Conn
45 srv *Server
46 logger Logger
47 messages chan<- *irc.Message
48
49 registered bool
50 user *user
51 closed bool
52 nick string
53 username string
54 realname string
55}
56
57func newDownstreamConn(srv *Server, netConn net.Conn) *downstreamConn {
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,
65 }
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
76}
77
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
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()
101 c.messages <- ircErr.Message
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
114func (c *downstreamConn) Close() error {
115 if c.closed {
116 return fmt.Errorf("downstream connection already closed")
117 }
118 if err := c.net.Close(); err != nil {
119 return err
120 }
121 close(c.messages)
122 c.closed = true
123 return nil
124}
125
126func (c *downstreamConn) handleMessage(msg *irc.Message) error {
127 switch msg.Command {
128 case "QUIT":
129 return c.Close()
130 case "PING":
131 // TODO: handle params
132 c.messages <- &irc.Message{
133 Prefix: c.srv.prefix(),
134 Command: "PONG",
135 Params: []string{c.srv.Hostname},
136 }
137 return nil
138 default:
139 if c.registered {
140 return c.handleMessageRegistered(msg)
141 } else {
142 return c.handleMessageUnregistered(msg)
143 }
144 }
145}
146
147func (c *downstreamConn) handleMessageUnregistered(msg *irc.Message) error {
148 switch msg.Command {
149 case "NICK":
150 if len(msg.Params) != 1 {
151 return newNeedMoreParamsError(msg.Command)
152 }
153 c.nick = msg.Params[0]
154 case "USER":
155 if len(msg.Params) != 4 {
156 return newNeedMoreParamsError(msg.Command)
157 }
158 c.username = "~" + msg.Params[0]
159 c.realname = msg.Params[3]
160 default:
161 c.logger.Printf("unhandled message: %v", msg)
162 return newUnknownCommandError(msg.Command)
163 }
164 if c.username != "" && c.nick != "" {
165 return c.register()
166 }
167 return nil
168}
169
170func (c *downstreamConn) register() error {
171 u := c.srv.getUser(strings.TrimPrefix(c.username, "~"))
172 if u == nil {
173 c.logger.Printf("failed authentication: unknown username %q", c.username)
174 c.messages <- &irc.Message{
175 Prefix: c.srv.prefix(),
176 Command: irc.ERR_PASSWDMISMATCH,
177 Params: []string{"*", "Invalid username or password"},
178 }
179 return nil
180 }
181
182 c.registered = true
183 c.user = u
184
185 c.messages <- &irc.Message{
186 Prefix: c.srv.prefix(),
187 Command: irc.RPL_WELCOME,
188 Params: []string{c.nick, "Welcome to jounce, " + c.nick},
189 }
190 c.messages <- &irc.Message{
191 Prefix: c.srv.prefix(),
192 Command: irc.RPL_YOURHOST,
193 Params: []string{c.nick, "Your host is " + c.srv.Hostname},
194 }
195 c.messages <- &irc.Message{
196 Prefix: c.srv.prefix(),
197 Command: irc.RPL_CREATED,
198 Params: []string{c.nick, "This server was created <datetime>"}, // TODO
199 }
200 c.messages <- &irc.Message{
201 Prefix: c.srv.prefix(),
202 Command: irc.RPL_MYINFO,
203 Params: []string{c.nick, c.srv.Hostname, "jounce", "aiwroO", "OovaimnqpsrtklbeI"},
204 }
205 c.messages <- &irc.Message{
206 Prefix: c.srv.prefix(),
207 Command: irc.ERR_NOMOTD,
208 Params: []string{c.nick, "No MOTD"},
209 }
210
211 u.forEachUpstream(func(uc *upstreamConn) {
212 // TODO: fix races accessing upstream connection data
213 for _, ch := range uc.channels {
214 if ch.complete {
215 forwardChannel(c, ch)
216 }
217 }
218 })
219 return nil
220}
221
222func (c *downstreamConn) handleMessageRegistered(msg *irc.Message) error {
223 switch msg.Command {
224 case "NICK", "USER":
225 return ircError{&irc.Message{
226 Command: irc.ERR_ALREADYREGISTERED,
227 Params: []string{
228 c.nick,
229 "You may not reregister",
230 },
231 }}
232 default:
233 c.logger.Printf("unhandled message: %v", msg)
234 return newUnknownCommandError(msg.Command)
235 }
236}
Note: See TracBrowser for help on using the repository browser.