source: code/trunk/upstream.go@ 129

Last change on this file since 129 was 129, checked in by delthas, 5 years ago

Fix MODE downstream support

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