source: code/trunk/upstream.go@ 134

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

Move upstreamConn.history to network

File size: 20.2 KB
Line 
1package soju
2
3import (
4 "crypto/tls"
5 "encoding/base64"
6 "fmt"
7 "io"
8 "net"
9 "strconv"
10 "strings"
11 "time"
12
13 "github.com/emersion/go-sasl"
14 "gopkg.in/irc.v3"
15)
16
17type upstreamChannel struct {
18 Name string
19 conn *upstreamConn
20 Topic string
21 TopicWho string
22 TopicTime time.Time
23 Status channelStatus
24 modes modeSet
25 Members map[string]membership
26 complete bool
27}
28
29type upstreamConn struct {
30 network *network
31 logger Logger
32 net net.Conn
33 irc *irc.Conn
34 srv *Server
35 user *user
36 outgoing chan<- *irc.Message
37 ring *Ring
38
39 serverName string
40 availableUserModes string
41 availableChannelModes string
42 channelModesWithParam string
43
44 registered bool
45 nick string
46 username string
47 realname string
48 closed bool
49 modes modeSet
50 channels map[string]*upstreamChannel
51 caps map[string]string
52
53 saslClient sasl.Client
54 saslStarted bool
55}
56
57func connectToUpstream(network *network) (*upstreamConn, error) {
58 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
59
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)
67 if err != nil {
68 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
69 }
70
71 setKeepAlive(netConn)
72
73 outgoing := make(chan *irc.Message, 64)
74 uc := &upstreamConn{
75 network: network,
76 logger: logger,
77 net: netConn,
78 irc: irc.NewConn(netConn),
79 srv: network.user.srv,
80 user: network.user,
81 outgoing: outgoing,
82 ring: NewRing(network.user.srv.RingCap),
83 channels: make(map[string]*upstreamChannel),
84 caps: make(map[string]string),
85 }
86
87 go func() {
88 for msg := range outgoing {
89 if uc.srv.Debug {
90 uc.logger.Printf("sent: %v", msg)
91 }
92 if err := uc.irc.WriteMessage(msg); err != nil {
93 uc.logger.Printf("failed to write message: %v", err)
94 }
95 }
96 if err := uc.net.Close(); err != nil {
97 uc.logger.Printf("failed to close connection: %v", err)
98 } else {
99 uc.logger.Printf("connection closed")
100 }
101 }()
102
103 return uc, nil
104}
105
106func (uc *upstreamConn) Close() error {
107 if uc.closed {
108 return fmt.Errorf("upstream connection already closed")
109 }
110 close(uc.outgoing)
111 uc.closed = true
112 return nil
113}
114
115func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
116 uc.user.forEachDownstream(func(dc *downstreamConn) {
117 if dc.network != nil && dc.network != uc.network {
118 return
119 }
120 f(dc)
121 })
122}
123
124func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
125 ch, ok := uc.channels[name]
126 if !ok {
127 return nil, fmt.Errorf("unknown channel %q", name)
128 }
129 return ch, nil
130}
131
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
144func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
145 switch msg.Command {
146 case "PING":
147 uc.SendMessage(&irc.Message{
148 Command: "PONG",
149 Params: msg.Params,
150 })
151 return nil
152 case "MODE":
153 var name, modeStr string
154 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
155 return err
156 }
157
158 if !uc.isChannel(name) { // user mode change
159 if name != uc.nick {
160 return fmt.Errorf("received MODE message for unknown nick %q", name)
161 }
162 return uc.modes.Apply(modeStr)
163 } else { // channel mode change
164 // TODO: handle MODE channel mode arguments
165 ch, err := uc.getChannel(name)
166 if err != nil {
167 return err
168 }
169 if err := ch.modes.Apply(modeStr); err != nil {
170 return err
171 }
172
173 uc.forEachDownstream(func(dc *downstreamConn) {
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 })
180 }
181 case "NOTICE":
182 uc.logger.Print(msg)
183
184 uc.forEachDownstream(func(dc *downstreamConn) {
185 dc.SendMessage(msg)
186 })
187 case "CAP":
188 var subCmd string
189 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
190 return err
191 }
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] == "*"
201
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
210 }
211
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
224 uc.SendMessage(&irc.Message{
225 Command: "CAP",
226 Params: []string{"END"},
227 })
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)
248 }
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 })
303 case irc.RPL_LOGGEDIN:
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)
309 case irc.RPL_LOGGEDOUT:
310 uc.logger.Printf("logged out")
311 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
312 var info string
313 if err := parseMessageParams(msg, nil, &info); err != nil {
314 return err
315 }
316 switch msg.Command {
317 case irc.ERR_NICKLOCKED:
318 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
319 case irc.ERR_SASLFAIL:
320 uc.logger.Printf("SASL authentication failed: %v", info)
321 case irc.ERR_SASLTOOLONG:
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 })
332 case irc.RPL_WELCOME:
333 uc.registered = true
334 uc.logger.Printf("connection registered")
335
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 {
343 uc.SendMessage(&irc.Message{
344 Command: "JOIN",
345 Params: []string{ch.Name},
346 })
347 }
348 case irc.RPL_MYINFO:
349 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, &uc.availableChannelModes); err != nil {
350 return err
351 }
352 if len(msg.Params) > 5 {
353 uc.channelModesWithParam = msg.Params[5]
354 }
355 case "NICK":
356 if msg.Prefix == nil {
357 return fmt.Errorf("expected a prefix")
358 }
359
360 var newNick string
361 if err := parseMessageParams(msg, &newNick); err != nil {
362 return err
363 }
364
365 if msg.Prefix.Name == uc.nick {
366 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
367 uc.nick = newNick
368 }
369
370 for _, ch := range uc.channels {
371 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
372 delete(ch.Members, msg.Prefix.Name)
373 ch.Members[newNick] = membership
374 }
375 }
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 }
386 case "JOIN":
387 if msg.Prefix == nil {
388 return fmt.Errorf("expected a prefix")
389 }
390
391 var channels string
392 if err := parseMessageParams(msg, &channels); err != nil {
393 return err
394 }
395
396 for _, ch := range strings.Split(channels, ",") {
397 if msg.Prefix.Name == uc.nick {
398 uc.logger.Printf("joined channel %q", ch)
399 uc.channels[ch] = &upstreamChannel{
400 Name: ch,
401 conn: uc,
402 Members: make(map[string]membership),
403 }
404 } else {
405 ch, err := uc.getChannel(ch)
406 if err != nil {
407 return err
408 }
409 ch.Members[msg.Prefix.Name] = 0
410 }
411
412 uc.forEachDownstream(func(dc *downstreamConn) {
413 dc.SendMessage(&irc.Message{
414 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
415 Command: "JOIN",
416 Params: []string{dc.marshalChannel(uc, ch)},
417 })
418 })
419 }
420 case "PART":
421 if msg.Prefix == nil {
422 return fmt.Errorf("expected a prefix")
423 }
424
425 var channels string
426 if err := parseMessageParams(msg, &channels); err != nil {
427 return err
428 }
429
430 for _, ch := range strings.Split(channels, ",") {
431 if msg.Prefix.Name == uc.nick {
432 uc.logger.Printf("parted channel %q", ch)
433 delete(uc.channels, ch)
434 } else {
435 ch, err := uc.getChannel(ch)
436 if err != nil {
437 return err
438 }
439 delete(ch.Members, msg.Prefix.Name)
440 }
441
442 uc.forEachDownstream(func(dc *downstreamConn) {
443 dc.SendMessage(&irc.Message{
444 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
445 Command: "PART",
446 Params: []string{dc.marshalChannel(uc, ch)},
447 })
448 })
449 }
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 }
472 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
473 var name, topic string
474 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
475 return err
476 }
477 ch, err := uc.getChannel(name)
478 if err != nil {
479 return err
480 }
481 if msg.Command == irc.RPL_TOPIC {
482 ch.Topic = topic
483 } else {
484 ch.Topic = ""
485 }
486 case "TOPIC":
487 var name string
488 if err := parseMessageParams(msg, &name); err != nil {
489 return err
490 }
491 ch, err := uc.getChannel(name)
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 }
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 })
511 case rpl_topicwhotime:
512 var name, who, timeStr string
513 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
514 return err
515 }
516 ch, err := uc.getChannel(name)
517 if err != nil {
518 return err
519 }
520 ch.TopicWho = who
521 sec, err := strconv.ParseInt(timeStr, 10, 64)
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:
527 var name, statusStr, members string
528 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
529 return err
530 }
531 ch, err := uc.getChannel(name)
532 if err != nil {
533 return err
534 }
535
536 status, err := parseChannelStatus(statusStr)
537 if err != nil {
538 return err
539 }
540 ch.Status = status
541
542 for _, s := range strings.Split(members, " ") {
543 membership, nick := parseMembershipPrefix(s)
544 ch.Members[nick] = membership
545 }
546 case irc.RPL_ENDOFNAMES:
547 var name string
548 if err := parseMessageParams(msg, nil, &name); err != nil {
549 return err
550 }
551 ch, err := uc.getChannel(name)
552 if err != nil {
553 return err
554 }
555
556 if ch.complete {
557 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
558 }
559 ch.complete = true
560
561 uc.forEachDownstream(func(dc *downstreamConn) {
562 forwardChannel(dc, ch)
563 })
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,
610 Params: []string{dc.nick, name, "End of WHO list"},
611 })
612 })
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 })
707 case "PRIVMSG":
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 {
714 return err
715 }
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
726 uc.ring.Produce(msg)
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 })
741 case irc.RPL_YOURHOST, irc.RPL_CREATED:
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
749 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
750 // Ignore
751 default:
752 uc.logger.Printf("unhandled message: %v", msg)
753 }
754 return nil
755}
756
757func (uc *upstreamConn) register() {
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
768 uc.SendMessage(&irc.Message{
769 Command: "CAP",
770 Params: []string{"LS", "302"},
771 })
772
773 if uc.network.Pass != "" {
774 uc.SendMessage(&irc.Message{
775 Command: "PASS",
776 Params: []string{uc.network.Pass},
777 })
778 }
779
780 uc.SendMessage(&irc.Message{
781 Command: "NICK",
782 Params: []string{uc.nick},
783 })
784 uc.SendMessage(&irc.Message{
785 Command: "USER",
786 Params: []string{uc.username, "0", "*", uc.realname},
787 })
788}
789
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
841func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
842 for {
843 msg, err := uc.irc.ReadMessage()
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
850 if uc.srv.Debug {
851 uc.logger.Printf("received: %v", msg)
852 }
853
854 ch <- upstreamIncomingMessage{msg, uc}
855 }
856
857 return nil
858}
859
860func (uc *upstreamConn) SendMessage(msg *irc.Message) {
861 uc.outgoing <- msg
862}
Note: See TracBrowser for help on using the repository browser.