source: code/trunk/upstream.go@ 44

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

Add upstreamConn.register

File size: 7.6 KB
RevLine 
[13]1package jounce
2
3import (
4 "crypto/tls"
5 "fmt"
6 "io"
7 "net"
[19]8 "strconv"
[17]9 "strings"
[19]10 "time"
[13]11
12 "gopkg.in/irc.v3"
13)
14
[19]15type upstreamChannel struct {
16 Name string
17 Topic string
18 TopicWho string
19 TopicTime time.Time
20 Status channelStatus
[35]21 modes modeSet
[19]22 Members map[string]membership
[25]23 complete bool
[19]24}
25
[13]26type upstreamConn struct {
[19]27 upstream *Upstream
[21]28 logger Logger
[19]29 net net.Conn
30 irc *irc.Conn
31 srv *Server
[37]32 user *user
[33]33 messages chan<- *irc.Message
[16]34
35 serverName string
36 availableUserModes string
37 availableChannelModes string
38 channelModesWithParam string
[19]39
40 registered bool
[42]41 nick string
[33]42 closed bool
[19]43 modes modeSet
44 channels map[string]*upstreamChannel
[13]45}
46
[37]47func connectToUpstream(u *user, upstream *Upstream) (*upstreamConn, error) {
48 logger := &prefixLogger{u.srv.Logger, fmt.Sprintf("upstream %q: ", upstream.Addr)}
[33]49 logger.Printf("connecting to server")
50
51 netConn, err := tls.Dial("tcp", upstream.Addr, nil)
52 if err != nil {
53 return nil, fmt.Errorf("failed to dial %q: %v", upstream.Addr, err)
54 }
55
56 msgs := make(chan *irc.Message, 64)
57 conn := &upstreamConn{
58 upstream: upstream,
59 logger: logger,
60 net: netConn,
61 irc: irc.NewConn(netConn),
[37]62 srv: u.srv,
63 user: u,
[33]64 messages: msgs,
65 channels: make(map[string]*upstreamChannel),
66 }
67
68 go func() {
69 for msg := range msgs {
70 if err := conn.irc.WriteMessage(msg); err != nil {
71 conn.logger.Printf("failed to write message: %v", err)
72 }
73 }
74 }()
75
76 return conn, nil
77}
78
79func (c *upstreamConn) Close() error {
80 if c.closed {
81 return fmt.Errorf("upstream connection already closed")
82 }
83 if err := c.net.Close(); err != nil {
84 return err
85 }
86 close(c.messages)
87 c.closed = true
88 return nil
89}
90
[19]91func (c *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
92 ch, ok := c.channels[name]
93 if !ok {
94 return nil, fmt.Errorf("unknown channel %q", name)
95 }
96 return ch, nil
97}
98
[13]99func (c *upstreamConn) handleMessage(msg *irc.Message) error {
100 switch msg.Command {
101 case "PING":
102 // TODO: handle params
[33]103 c.messages <- &irc.Message{
[13]104 Command: "PONG",
105 Params: []string{c.srv.Hostname},
[33]106 }
107 return nil
[17]108 case "MODE":
[43]109 var name, modeStr string
110 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
111 return err
[17]112 }
[35]113
114 if name == msg.Prefix.Name { // user mode change
[42]115 if name != c.nick {
[35]116 return fmt.Errorf("received MODE message for unknow nick %q", name)
117 }
118 return c.modes.Apply(modeStr)
119 } else { // channel mode change
120 ch, err := c.getChannel(name)
121 if err != nil {
122 return err
123 }
124 if err := ch.modes.Apply(modeStr); err != nil {
125 return err
126 }
127
[40]128 c.user.forEachDownstream(func(dc *downstreamConn) {
[35]129 dc.messages <- msg
[40]130 })
[17]131 }
[18]132 case "NOTICE":
[21]133 c.logger.Print(msg)
[14]134 case irc.RPL_WELCOME:
135 c.registered = true
[21]136 c.logger.Printf("connection registered")
[19]137
138 for _, ch := range c.upstream.Channels {
[33]139 c.messages <- &irc.Message{
[19]140 Command: "JOIN",
141 Params: []string{ch},
142 }
143 }
[16]144 case irc.RPL_MYINFO:
[43]145 if err := parseMessageParams(msg, nil, &c.serverName, nil, &c.availableUserModes, &c.availableChannelModes); err != nil {
146 return err
[16]147 }
148 if len(msg.Params) > 5 {
149 c.channelModesWithParam = msg.Params[5]
150 }
[42]151 case "NICK":
[43]152 var newNick string
153 if err := parseMessageParams(msg, &newNick); err != nil {
154 return err
[42]155 }
156
157 if msg.Prefix.Name == c.nick {
158 c.logger.Printf("changed nick from %q to %q", c.nick, newNick)
159 c.nick = newNick
160 }
161
162 for _, ch := range c.channels {
163 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
164 delete(ch.Members, msg.Prefix.Name)
165 ch.Members[newNick] = membership
166 }
167 }
168
169 c.user.forEachDownstream(func(dc *downstreamConn) {
170 dc.messages <- msg
171 })
[19]172 case "JOIN":
[43]173 var channels string
174 if err := parseMessageParams(msg, &channels); err != nil {
175 return err
[19]176 }
[34]177
[43]178 for _, ch := range strings.Split(channels, ",") {
[42]179 if msg.Prefix.Name == c.nick {
[34]180 c.logger.Printf("joined channel %q", ch)
181 c.channels[ch] = &upstreamChannel{
182 Name: ch,
183 Members: make(map[string]membership),
184 }
185 } else {
186 ch, err := c.getChannel(ch)
187 if err != nil {
188 return err
189 }
190 ch.Members[msg.Prefix.Name] = 0
[19]191 }
192 }
[34]193
[40]194 c.user.forEachDownstream(func(dc *downstreamConn) {
[34]195 dc.messages <- msg
[40]196 })
[34]197 case "PART":
[43]198 var channels string
199 if err := parseMessageParams(msg, &channels); err != nil {
200 return err
[34]201 }
202
[43]203 for _, ch := range strings.Split(channels, ",") {
[42]204 if msg.Prefix.Name == c.nick {
[34]205 c.logger.Printf("parted channel %q", ch)
206 delete(c.channels, ch)
207 } else {
208 ch, err := c.getChannel(ch)
209 if err != nil {
210 return err
211 }
212 delete(ch.Members, msg.Prefix.Name)
213 }
214 }
215
[40]216 c.user.forEachDownstream(func(dc *downstreamConn) {
[34]217 dc.messages <- msg
[40]218 })
[19]219 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
[43]220 var name, topic string
221 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
222 return err
[19]223 }
[43]224 ch, err := c.getChannel(name)
[19]225 if err != nil {
226 return err
227 }
228 if msg.Command == irc.RPL_TOPIC {
[43]229 ch.Topic = topic
[19]230 } else {
231 ch.Topic = ""
232 }
233 case "TOPIC":
[43]234 var name string
235 if err := parseMessageParams(msg, nil, &name); err != nil {
236 return err
[19]237 }
[43]238 ch, err := c.getChannel(name)
[19]239 if err != nil {
240 return err
241 }
242 if len(msg.Params) > 1 {
243 ch.Topic = msg.Params[1]
244 } else {
245 ch.Topic = ""
246 }
247 case rpl_topicwhotime:
[43]248 var name, who, timeStr string
249 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
250 return err
[19]251 }
[43]252 ch, err := c.getChannel(name)
[19]253 if err != nil {
254 return err
255 }
[43]256 ch.TopicWho = who
257 sec, err := strconv.ParseInt(timeStr, 10, 64)
[19]258 if err != nil {
259 return fmt.Errorf("failed to parse topic time: %v", err)
260 }
261 ch.TopicTime = time.Unix(sec, 0)
262 case irc.RPL_NAMREPLY:
[43]263 var name, statusStr, members string
264 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
265 return err
[19]266 }
[43]267 ch, err := c.getChannel(name)
[19]268 if err != nil {
269 return err
270 }
271
[43]272 status, err := parseChannelStatus(statusStr)
[19]273 if err != nil {
274 return err
275 }
276 ch.Status = status
277
[43]278 for _, s := range strings.Split(members, " ") {
[19]279 membership, nick := parseMembershipPrefix(s)
280 ch.Members[nick] = membership
281 }
282 case irc.RPL_ENDOFNAMES:
[43]283 var name string
284 if err := parseMessageParams(msg, nil, &name); err != nil {
285 return err
[25]286 }
[43]287 ch, err := c.getChannel(name)
[25]288 if err != nil {
289 return err
290 }
291
[34]292 if ch.complete {
293 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
294 }
[25]295 ch.complete = true
[27]296
[40]297 c.user.forEachDownstream(func(dc *downstreamConn) {
[27]298 forwardChannel(dc, ch)
[40]299 })
[36]300 case "PRIVMSG":
[40]301 c.user.forEachDownstream(func(dc *downstreamConn) {
[36]302 dc.messages <- msg
[40]303 })
[16]304 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]305 // Ignore
306 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
307 // Ignore
308 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
309 // Ignore
310 case rpl_localusers, rpl_globalusers:
311 // Ignore
312 case irc.RPL_STATSVLINE, irc.RPL_STATSPING, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
313 // Ignore
[13]314 default:
[21]315 c.logger.Printf("unhandled upstream message: %v", msg)
[13]316 }
[14]317 return nil
[13]318}
319
[44]320func (c *upstreamConn) register() {
[42]321 c.nick = c.upstream.Nick
[33]322 c.messages <- &irc.Message{
[13]323 Command: "NICK",
[23]324 Params: []string{c.upstream.Nick},
[13]325 }
[33]326 c.messages <- &irc.Message{
[13]327 Command: "USER",
[23]328 Params: []string{c.upstream.Username, "0", "*", c.upstream.Realname},
[13]329 }
[44]330}
[13]331
[44]332func (c *upstreamConn) readMessages() error {
333 defer c.Close()
334
[13]335 for {
336 msg, err := c.irc.ReadMessage()
337 if err == io.EOF {
338 break
339 } else if err != nil {
340 return fmt.Errorf("failed to read IRC command: %v", err)
341 }
342
343 if err := c.handleMessage(msg); err != nil {
[21]344 c.logger.Printf("failed to handle message %q: %v", msg, err)
[13]345 }
346 }
347
[33]348 return c.Close()
[13]349}
Note: See TracBrowser for help on using the repository browser.