source: code/trunk/upstream.go@ 72

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

Remove upstream prefix for outgoing MODE and PRIVMSG

We shouldn't set the prefix when sending these commands.

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