source: code/trunk/upstream.go@ 136

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

Move upstreamConn.history to network

File size: 20.2 KB
RevLine 
[98]1package soju
[13]2
3import (
4 "crypto/tls"
[95]5 "encoding/base64"
[13]6 "fmt"
7 "io"
8 "net"
[19]9 "strconv"
[17]10 "strings"
[19]11 "time"
[13]12
[95]13 "github.com/emersion/go-sasl"
[13]14 "gopkg.in/irc.v3"
15)
16
[19]17type upstreamChannel struct {
18 Name string
[46]19 conn *upstreamConn
[19]20 Topic string
21 TopicWho string
22 TopicTime time.Time
23 Status channelStatus
[35]24 modes modeSet
[19]25 Members map[string]membership
[25]26 complete bool
[19]27}
28
[13]29type upstreamConn struct {
[77]30 network *network
[21]31 logger Logger
[19]32 net net.Conn
33 irc *irc.Conn
34 srv *Server
[37]35 user *user
[102]36 outgoing chan<- *irc.Message
[50]37 ring *Ring
[16]38
39 serverName string
40 availableUserModes string
41 availableChannelModes string
42 channelModesWithParam string
[19]43
44 registered bool
[42]45 nick string
[77]46 username string
47 realname string
[33]48 closed bool
[19]49 modes modeSet
50 channels map[string]*upstreamChannel
[92]51 caps map[string]string
[95]52
53 saslClient sasl.Client
54 saslStarted bool
[13]55}
56
[77]57func connectToUpstream(network *network) (*upstreamConn, error) {
58 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
[33]59
[77]60 addr := network.Addr
61 if !strings.ContainsRune(addr, ':') {
62 addr = addr + ":6697"
63 }
64
65 logger.Printf("connecting to TLS server at address %q", addr)
66 netConn, err := tls.Dial("tcp", addr, nil)
[33]67 if err != nil {
[77]68 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
[33]69 }
70
[67]71 setKeepAlive(netConn)
72
[102]73 outgoing := make(chan *irc.Message, 64)
[55]74 uc := &upstreamConn{
[79]75 network: network,
[33]76 logger: logger,
77 net: netConn,
78 irc: irc.NewConn(netConn),
[77]79 srv: network.user.srv,
80 user: network.user,
[102]81 outgoing: outgoing,
[77]82 ring: NewRing(network.user.srv.RingCap),
[33]83 channels: make(map[string]*upstreamChannel),
[92]84 caps: make(map[string]string),
[33]85 }
86
87 go func() {
[102]88 for msg := range outgoing {
[64]89 if uc.srv.Debug {
90 uc.logger.Printf("sent: %v", msg)
91 }
[55]92 if err := uc.irc.WriteMessage(msg); err != nil {
93 uc.logger.Printf("failed to write message: %v", err)
[33]94 }
95 }
[55]96 if err := uc.net.Close(); err != nil {
97 uc.logger.Printf("failed to close connection: %v", err)
[45]98 } else {
[55]99 uc.logger.Printf("connection closed")
[45]100 }
[33]101 }()
102
[55]103 return uc, nil
[33]104}
105
[55]106func (uc *upstreamConn) Close() error {
107 if uc.closed {
[33]108 return fmt.Errorf("upstream connection already closed")
109 }
[102]110 close(uc.outgoing)
[55]111 uc.closed = true
[33]112 return nil
113}
114
[73]115func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
116 uc.user.forEachDownstream(func(dc *downstreamConn) {
[77]117 if dc.network != nil && dc.network != uc.network {
[73]118 return
119 }
120 f(dc)
121 })
122}
123
[55]124func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
125 ch, ok := uc.channels[name]
[19]126 if !ok {
127 return nil, fmt.Errorf("unknown channel %q", name)
128 }
129 return ch, nil
130}
131
[129]132func (uc *upstreamConn) isChannel(entity string) bool {
133 for _, r := range entity {
134 switch r {
135 // TODO: support upstream ISUPPORT channel prefixes
136 case '#', '&', '+', '!':
137 return true
138 }
139 break
140 }
141 return false
142}
143
[55]144func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
[13]145 switch msg.Command {
146 case "PING":
[60]147 uc.SendMessage(&irc.Message{
[13]148 Command: "PONG",
[68]149 Params: msg.Params,
[60]150 })
[33]151 return nil
[17]152 case "MODE":
[43]153 var name, modeStr string
154 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
155 return err
[17]156 }
[35]157
[129]158 if !uc.isChannel(name) { // user mode change
[55]159 if name != uc.nick {
[129]160 return fmt.Errorf("received MODE message for unknown nick %q", name)
[35]161 }
[55]162 return uc.modes.Apply(modeStr)
[35]163 } else { // channel mode change
[129]164 // TODO: handle MODE channel mode arguments
[55]165 ch, err := uc.getChannel(name)
[35]166 if err != nil {
167 return err
168 }
169 if err := ch.modes.Apply(modeStr); err != nil {
170 return err
171 }
[69]172
[73]173 uc.forEachDownstream(func(dc *downstreamConn) {
[69]174 dc.SendMessage(&irc.Message{
175 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
176 Command: "MODE",
177 Params: []string{dc.marshalChannel(uc, name), modeStr},
178 })
179 })
[46]180 }
[18]181 case "NOTICE":
[55]182 uc.logger.Print(msg)
[97]183
184 uc.forEachDownstream(func(dc *downstreamConn) {
185 dc.SendMessage(msg)
186 })
[92]187 case "CAP":
[95]188 var subCmd string
189 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
190 return err
[92]191 }
[95]192 subCmd = strings.ToUpper(subCmd)
193 subParams := msg.Params[2:]
194 switch subCmd {
195 case "LS":
196 if len(subParams) < 1 {
197 return newNeedMoreParamsError(msg.Command)
198 }
199 caps := strings.Fields(subParams[len(subParams)-1])
200 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
[92]201
[95]202 for _, s := range caps {
203 kv := strings.SplitN(s, "=", 2)
204 k := strings.ToLower(kv[0])
205 var v string
206 if len(kv) == 2 {
207 v = kv[1]
208 }
209 uc.caps[k] = v
[92]210 }
211
[95]212 if more {
213 break // wait to receive all capabilities
214 }
215
216 if uc.requestSASL() {
217 uc.SendMessage(&irc.Message{
218 Command: "CAP",
219 Params: []string{"REQ", "sasl"},
220 })
221 break // we'll send CAP END after authentication is completed
222 }
223
[92]224 uc.SendMessage(&irc.Message{
225 Command: "CAP",
226 Params: []string{"END"},
227 })
[95]228 case "ACK", "NAK":
229 if len(subParams) < 1 {
230 return newNeedMoreParamsError(msg.Command)
231 }
232 caps := strings.Fields(subParams[0])
233
234 for _, name := range caps {
235 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
236 return err
237 }
238 }
239
240 if uc.saslClient == nil {
241 uc.SendMessage(&irc.Message{
242 Command: "CAP",
243 Params: []string{"END"},
244 })
245 }
246 default:
247 uc.logger.Printf("unhandled message: %v", msg)
[92]248 }
[95]249 case "AUTHENTICATE":
250 if uc.saslClient == nil {
251 return fmt.Errorf("received unexpected AUTHENTICATE message")
252 }
253
254 // TODO: if a challenge is 400 bytes long, buffer it
255 var challengeStr string
256 if err := parseMessageParams(msg, &challengeStr); err != nil {
257 uc.SendMessage(&irc.Message{
258 Command: "AUTHENTICATE",
259 Params: []string{"*"},
260 })
261 return err
262 }
263
264 var challenge []byte
265 if challengeStr != "+" {
266 var err error
267 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
268 if err != nil {
269 uc.SendMessage(&irc.Message{
270 Command: "AUTHENTICATE",
271 Params: []string{"*"},
272 })
273 return err
274 }
275 }
276
277 var resp []byte
278 var err error
279 if !uc.saslStarted {
280 _, resp, err = uc.saslClient.Start()
281 uc.saslStarted = true
282 } else {
283 resp, err = uc.saslClient.Next(challenge)
284 }
285 if err != nil {
286 uc.SendMessage(&irc.Message{
287 Command: "AUTHENTICATE",
288 Params: []string{"*"},
289 })
290 return err
291 }
292
293 // TODO: send response in multiple chunks if >= 400 bytes
294 var respStr = "+"
295 if resp != nil {
296 respStr = base64.StdEncoding.EncodeToString(resp)
297 }
298
299 uc.SendMessage(&irc.Message{
300 Command: "AUTHENTICATE",
301 Params: []string{respStr},
302 })
[125]303 case irc.RPL_LOGGEDIN:
[95]304 var account string
305 if err := parseMessageParams(msg, nil, nil, &account); err != nil {
306 return err
307 }
308 uc.logger.Printf("logged in with account %q", account)
[125]309 case irc.RPL_LOGGEDOUT:
[95]310 uc.logger.Printf("logged out")
[125]311 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
[95]312 var info string
313 if err := parseMessageParams(msg, nil, &info); err != nil {
314 return err
315 }
316 switch msg.Command {
[125]317 case irc.ERR_NICKLOCKED:
[95]318 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
[125]319 case irc.ERR_SASLFAIL:
[95]320 uc.logger.Printf("SASL authentication failed: %v", info)
[125]321 case irc.ERR_SASLTOOLONG:
[95]322 uc.logger.Printf("SASL message too long: %v", info)
323 }
324
325 uc.saslClient = nil
326 uc.saslStarted = false
327
328 uc.SendMessage(&irc.Message{
329 Command: "CAP",
330 Params: []string{"END"},
331 })
[14]332 case irc.RPL_WELCOME:
[55]333 uc.registered = true
334 uc.logger.Printf("connection registered")
[19]335
[77]336 channels, err := uc.srv.db.ListChannels(uc.network.ID)
337 if err != nil {
338 uc.logger.Printf("failed to list channels from database: %v", err)
339 break
340 }
341
342 for _, ch := range channels {
[60]343 uc.SendMessage(&irc.Message{
[19]344 Command: "JOIN",
[77]345 Params: []string{ch.Name},
[60]346 })
[19]347 }
[16]348 case irc.RPL_MYINFO:
[55]349 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, &uc.availableChannelModes); err != nil {
[43]350 return err
[16]351 }
352 if len(msg.Params) > 5 {
[55]353 uc.channelModesWithParam = msg.Params[5]
[16]354 }
[42]355 case "NICK":
[83]356 if msg.Prefix == nil {
357 return fmt.Errorf("expected a prefix")
358 }
359
[43]360 var newNick string
361 if err := parseMessageParams(msg, &newNick); err != nil {
362 return err
[42]363 }
364
[55]365 if msg.Prefix.Name == uc.nick {
366 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
367 uc.nick = newNick
[42]368 }
369
[55]370 for _, ch := range uc.channels {
[42]371 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
372 delete(ch.Members, msg.Prefix.Name)
373 ch.Members[newNick] = membership
374 }
375 }
[82]376
377 if msg.Prefix.Name != uc.nick {
378 uc.forEachDownstream(func(dc *downstreamConn) {
379 dc.SendMessage(&irc.Message{
380 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
381 Command: "NICK",
382 Params: []string{newNick},
383 })
384 })
385 }
[69]386 case "JOIN":
387 if msg.Prefix == nil {
388 return fmt.Errorf("expected a prefix")
389 }
[42]390
[43]391 var channels string
392 if err := parseMessageParams(msg, &channels); err != nil {
393 return err
[19]394 }
[34]395
[43]396 for _, ch := range strings.Split(channels, ",") {
[55]397 if msg.Prefix.Name == uc.nick {
398 uc.logger.Printf("joined channel %q", ch)
399 uc.channels[ch] = &upstreamChannel{
[34]400 Name: ch,
[55]401 conn: uc,
[34]402 Members: make(map[string]membership),
403 }
404 } else {
[55]405 ch, err := uc.getChannel(ch)
[34]406 if err != nil {
407 return err
408 }
409 ch.Members[msg.Prefix.Name] = 0
[19]410 }
[69]411
[73]412 uc.forEachDownstream(func(dc *downstreamConn) {
[69]413 dc.SendMessage(&irc.Message{
414 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
415 Command: "JOIN",
416 Params: []string{dc.marshalChannel(uc, ch)},
417 })
418 })
[19]419 }
[69]420 case "PART":
421 if msg.Prefix == nil {
422 return fmt.Errorf("expected a prefix")
423 }
[34]424
[43]425 var channels string
426 if err := parseMessageParams(msg, &channels); err != nil {
427 return err
[34]428 }
429
[43]430 for _, ch := range strings.Split(channels, ",") {
[55]431 if msg.Prefix.Name == uc.nick {
432 uc.logger.Printf("parted channel %q", ch)
433 delete(uc.channels, ch)
[34]434 } else {
[55]435 ch, err := uc.getChannel(ch)
[34]436 if err != nil {
437 return err
438 }
439 delete(ch.Members, msg.Prefix.Name)
440 }
[69]441
[73]442 uc.forEachDownstream(func(dc *downstreamConn) {
[69]443 dc.SendMessage(&irc.Message{
444 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
445 Command: "PART",
446 Params: []string{dc.marshalChannel(uc, ch)},
447 })
448 })
[34]449 }
[83]450 case "QUIT":
451 if msg.Prefix == nil {
452 return fmt.Errorf("expected a prefix")
453 }
454
455 if msg.Prefix.Name == uc.nick {
456 uc.logger.Printf("quit")
457 }
458
459 for _, ch := range uc.channels {
460 delete(ch.Members, msg.Prefix.Name)
461 }
462
463 if msg.Prefix.Name != uc.nick {
464 uc.forEachDownstream(func(dc *downstreamConn) {
465 dc.SendMessage(&irc.Message{
466 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
467 Command: "QUIT",
468 Params: msg.Params,
469 })
470 })
471 }
[19]472 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
[43]473 var name, topic string
474 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
475 return err
[19]476 }
[55]477 ch, err := uc.getChannel(name)
[19]478 if err != nil {
479 return err
480 }
481 if msg.Command == irc.RPL_TOPIC {
[43]482 ch.Topic = topic
[19]483 } else {
484 ch.Topic = ""
485 }
486 case "TOPIC":
[43]487 var name string
[74]488 if err := parseMessageParams(msg, &name); err != nil {
[43]489 return err
[19]490 }
[55]491 ch, err := uc.getChannel(name)
[19]492 if err != nil {
493 return err
494 }
495 if len(msg.Params) > 1 {
496 ch.Topic = msg.Params[1]
497 } else {
498 ch.Topic = ""
499 }
[74]500 uc.forEachDownstream(func(dc *downstreamConn) {
501 params := []string{dc.marshalChannel(uc, name)}
502 if ch.Topic != "" {
503 params = append(params, ch.Topic)
504 }
505 dc.SendMessage(&irc.Message{
506 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
507 Command: "TOPIC",
508 Params: params,
509 })
510 })
[19]511 case rpl_topicwhotime:
[43]512 var name, who, timeStr string
513 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
514 return err
[19]515 }
[55]516 ch, err := uc.getChannel(name)
[19]517 if err != nil {
518 return err
519 }
[43]520 ch.TopicWho = who
521 sec, err := strconv.ParseInt(timeStr, 10, 64)
[19]522 if err != nil {
523 return fmt.Errorf("failed to parse topic time: %v", err)
524 }
525 ch.TopicTime = time.Unix(sec, 0)
526 case irc.RPL_NAMREPLY:
[43]527 var name, statusStr, members string
528 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
529 return err
[19]530 }
[55]531 ch, err := uc.getChannel(name)
[19]532 if err != nil {
533 return err
534 }
535
[43]536 status, err := parseChannelStatus(statusStr)
[19]537 if err != nil {
538 return err
539 }
540 ch.Status = status
541
[43]542 for _, s := range strings.Split(members, " ") {
[19]543 membership, nick := parseMembershipPrefix(s)
544 ch.Members[nick] = membership
545 }
546 case irc.RPL_ENDOFNAMES:
[43]547 var name string
548 if err := parseMessageParams(msg, nil, &name); err != nil {
549 return err
[25]550 }
[55]551 ch, err := uc.getChannel(name)
[25]552 if err != nil {
553 return err
554 }
555
[34]556 if ch.complete {
557 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
558 }
[25]559 ch.complete = true
[27]560
[73]561 uc.forEachDownstream(func(dc *downstreamConn) {
[27]562 forwardChannel(dc, ch)
[40]563 })
[127]564 case irc.RPL_WHOREPLY:
565 var channel, username, host, server, nick, mode, trailing string
566 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
567 return err
568 }
569
570 parts := strings.SplitN(trailing, " ", 2)
571 if len(parts) != 2 {
572 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
573 }
574 realname := parts[1]
575 hops, err := strconv.Atoi(parts[0])
576 if err != nil {
577 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
578 }
579 hops++
580
581 trailing = strconv.Itoa(hops) + " " + realname
582
583 uc.forEachDownstream(func(dc *downstreamConn) {
584 channel := channel
585 if channel != "*" {
586 channel = dc.marshalChannel(uc, channel)
587 }
588 nick := dc.marshalNick(uc, nick)
589 dc.SendMessage(&irc.Message{
590 Prefix: dc.srv.prefix(),
591 Command: irc.RPL_WHOREPLY,
592 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
593 })
594 })
595 case irc.RPL_ENDOFWHO:
596 var name string
597 if err := parseMessageParams(msg, nil, &name); err != nil {
598 return err
599 }
600
601 uc.forEachDownstream(func(dc *downstreamConn) {
602 name := name
603 if name != "*" {
604 // TODO: support WHO masks
605 name = dc.marshalEntity(uc, name)
606 }
607 dc.SendMessage(&irc.Message{
608 Prefix: dc.srv.prefix(),
609 Command: irc.RPL_ENDOFWHO,
[128]610 Params: []string{dc.nick, name, "End of WHO list"},
[127]611 })
612 })
[128]613 case irc.RPL_WHOISUSER:
614 var nick, username, host, realname string
615 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
616 return err
617 }
618
619 uc.forEachDownstream(func(dc *downstreamConn) {
620 nick := dc.marshalNick(uc, nick)
621 dc.SendMessage(&irc.Message{
622 Prefix: dc.srv.prefix(),
623 Command: irc.RPL_WHOISUSER,
624 Params: []string{dc.nick, nick, username, host, "*", realname},
625 })
626 })
627 case irc.RPL_WHOISSERVER:
628 var nick, server, serverInfo string
629 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
630 return err
631 }
632
633 uc.forEachDownstream(func(dc *downstreamConn) {
634 nick := dc.marshalNick(uc, nick)
635 dc.SendMessage(&irc.Message{
636 Prefix: dc.srv.prefix(),
637 Command: irc.RPL_WHOISSERVER,
638 Params: []string{dc.nick, nick, server, serverInfo},
639 })
640 })
641 case irc.RPL_WHOISOPERATOR:
642 var nick string
643 if err := parseMessageParams(msg, nil, &nick); err != nil {
644 return err
645 }
646
647 uc.forEachDownstream(func(dc *downstreamConn) {
648 nick := dc.marshalNick(uc, nick)
649 dc.SendMessage(&irc.Message{
650 Prefix: dc.srv.prefix(),
651 Command: irc.RPL_WHOISOPERATOR,
652 Params: []string{dc.nick, nick, "is an IRC operator"},
653 })
654 })
655 case irc.RPL_WHOISIDLE:
656 var nick string
657 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
658 return err
659 }
660
661 uc.forEachDownstream(func(dc *downstreamConn) {
662 nick := dc.marshalNick(uc, nick)
663 params := []string{dc.nick, nick}
664 params = append(params, msg.Params[2:]...)
665 dc.SendMessage(&irc.Message{
666 Prefix: dc.srv.prefix(),
667 Command: irc.RPL_WHOISIDLE,
668 Params: params,
669 })
670 })
671 case irc.RPL_WHOISCHANNELS:
672 var nick, channelList string
673 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
674 return err
675 }
676 channels := strings.Split(channelList, " ")
677
678 uc.forEachDownstream(func(dc *downstreamConn) {
679 nick := dc.marshalNick(uc, nick)
680 channelList := make([]string, len(channels))
681 for i, channel := range channels {
682 prefix, channel := parseMembershipPrefix(channel)
683 channel = dc.marshalChannel(uc, channel)
684 channelList[i] = prefix.String() + channel
685 }
686 channels := strings.Join(channelList, " ")
687 dc.SendMessage(&irc.Message{
688 Prefix: dc.srv.prefix(),
689 Command: irc.RPL_WHOISCHANNELS,
690 Params: []string{dc.nick, nick, channels},
691 })
692 })
693 case irc.RPL_ENDOFWHOIS:
694 var nick string
695 if err := parseMessageParams(msg, nil, &nick); err != nil {
696 return err
697 }
698
699 uc.forEachDownstream(func(dc *downstreamConn) {
700 nick := dc.marshalNick(uc, nick)
701 dc.SendMessage(&irc.Message{
702 Prefix: dc.srv.prefix(),
703 Command: irc.RPL_ENDOFWHOIS,
704 Params: []string{dc.nick, nick, "End of WHOIS list"},
705 })
706 })
[36]707 case "PRIVMSG":
[117]708 if msg.Prefix == nil {
709 return fmt.Errorf("expected a prefix")
710 }
711
712 var nick string
713 if err := parseMessageParams(msg, &nick, nil); err != nil {
[69]714 return err
715 }
[117]716
717 if msg.Prefix.Name == serviceNick {
718 uc.logger.Printf("skipping PRIVMSG from soju's service: %v", msg)
719 break
720 }
721 if nick == serviceNick {
722 uc.logger.Printf("skipping PRIVMSG to soju's service: %v", msg)
723 break
724 }
725
[55]726 uc.ring.Produce(msg)
[115]727 case "INVITE":
728 var nick string
729 var channel string
730 if err := parseMessageParams(msg, &nick, &channel); err != nil {
731 return err
732 }
733
734 uc.forEachDownstream(func(dc *downstreamConn) {
735 dc.SendMessage(&irc.Message{
736 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
737 Command: "INVITE",
738 Params: []string{dc.marshalNick(uc, nick), dc.marshalChannel(uc, channel)},
739 })
740 })
[16]741 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]742 // Ignore
743 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
744 // Ignore
745 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
746 // Ignore
747 case rpl_localusers, rpl_globalusers:
748 // Ignore
[96]749 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
[14]750 // Ignore
[13]751 default:
[95]752 uc.logger.Printf("unhandled message: %v", msg)
[13]753 }
[14]754 return nil
[13]755}
756
[55]757func (uc *upstreamConn) register() {
[77]758 uc.nick = uc.network.Nick
759 uc.username = uc.network.Username
760 if uc.username == "" {
761 uc.username = uc.nick
762 }
763 uc.realname = uc.network.Realname
764 if uc.realname == "" {
765 uc.realname = uc.nick
766 }
767
[60]768 uc.SendMessage(&irc.Message{
[92]769 Command: "CAP",
770 Params: []string{"LS", "302"},
771 })
772
[93]773 if uc.network.Pass != "" {
774 uc.SendMessage(&irc.Message{
775 Command: "PASS",
776 Params: []string{uc.network.Pass},
777 })
778 }
779
[92]780 uc.SendMessage(&irc.Message{
[13]781 Command: "NICK",
[69]782 Params: []string{uc.nick},
[60]783 })
784 uc.SendMessage(&irc.Message{
[13]785 Command: "USER",
[77]786 Params: []string{uc.username, "0", "*", uc.realname},
[60]787 })
[44]788}
[13]789
[95]790func (uc *upstreamConn) requestSASL() bool {
791 if uc.network.SASL.Mechanism == "" {
792 return false
793 }
794
795 v, ok := uc.caps["sasl"]
796 if !ok {
797 return false
798 }
799 if v != "" {
800 mechanisms := strings.Split(v, ",")
801 found := false
802 for _, mech := range mechanisms {
803 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
804 found = true
805 break
806 }
807 }
808 if !found {
809 return false
810 }
811 }
812
813 return true
814}
815
816func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
817 auth := &uc.network.SASL
818 switch name {
819 case "sasl":
820 if !ok {
821 uc.logger.Printf("server refused to acknowledge the SASL capability")
822 return nil
823 }
824
825 switch auth.Mechanism {
826 case "PLAIN":
827 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
828 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
829 default:
830 return fmt.Errorf("unsupported SASL mechanism %q", name)
831 }
832
833 uc.SendMessage(&irc.Message{
834 Command: "AUTHENTICATE",
835 Params: []string{auth.Mechanism},
836 })
837 }
838 return nil
839}
840
[103]841func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
[13]842 for {
[55]843 msg, err := uc.irc.ReadMessage()
[13]844 if err == io.EOF {
845 break
846 } else if err != nil {
847 return fmt.Errorf("failed to read IRC command: %v", err)
848 }
849
[64]850 if uc.srv.Debug {
851 uc.logger.Printf("received: %v", msg)
852 }
853
[103]854 ch <- upstreamIncomingMessage{msg, uc}
[13]855 }
856
[45]857 return nil
[13]858}
[60]859
860func (uc *upstreamConn) SendMessage(msg *irc.Message) {
[102]861 uc.outgoing <- msg
[60]862}
Note: See TracBrowser for help on using the repository browser.