source: code/trunk/upstream.go@ 144

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

Move upstreamConn.ring to network

This handles upstream disconnection and re-connection better.

File size: 24.4 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 channelModes
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
38 serverName string
39 availableUserModes string
40 availableChannelModes map[byte]channelModeType
41 availableChannelTypes string
42 availableMemberships []membership
43
44 registered bool
45 nick string
46 username string
47 realname string
48 closed bool
49 modes userModes
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 channels: make(map[string]*upstreamChannel),
83 caps: make(map[string]string),
84 availableChannelTypes: stdChannelTypes,
85 availableChannelModes: stdChannelModes,
86 availableMemberships: stdMemberships,
87 }
88
89 go func() {
90 for msg := range outgoing {
91 if uc.srv.Debug {
92 uc.logger.Printf("sent: %v", msg)
93 }
94 if err := uc.irc.WriteMessage(msg); err != nil {
95 uc.logger.Printf("failed to write message: %v", err)
96 }
97 }
98 if err := uc.net.Close(); err != nil {
99 uc.logger.Printf("failed to close connection: %v", err)
100 } else {
101 uc.logger.Printf("connection closed")
102 }
103 }()
104
105 return uc, nil
106}
107
108func (uc *upstreamConn) Close() error {
109 if uc.closed {
110 return fmt.Errorf("upstream connection already closed")
111 }
112 close(uc.outgoing)
113 uc.closed = true
114 return nil
115}
116
117func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
118 uc.user.forEachDownstream(func(dc *downstreamConn) {
119 if dc.network != nil && dc.network != uc.network {
120 return
121 }
122 f(dc)
123 })
124}
125
126func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
127 ch, ok := uc.channels[name]
128 if !ok {
129 return nil, fmt.Errorf("unknown channel %q", name)
130 }
131 return ch, nil
132}
133
134func (uc *upstreamConn) isChannel(entity string) bool {
135 if i := strings.IndexByte(uc.availableChannelTypes, entity[0]); i >= 0 {
136 return true
137 }
138 return false
139}
140
141func (uc *upstreamConn) parseMembershipPrefix(s string) (membership *membership, nick string) {
142 for _, m := range uc.availableMemberships {
143 if m.Prefix == s[0] {
144 return &m, s[1:]
145 }
146 }
147 return nil, s
148}
149
150func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
151 switch msg.Command {
152 case "PING":
153 uc.SendMessage(&irc.Message{
154 Command: "PONG",
155 Params: msg.Params,
156 })
157 return nil
158 case "NOTICE":
159 uc.logger.Print(msg)
160
161 uc.forEachDownstream(func(dc *downstreamConn) {
162 dc.SendMessage(msg)
163 })
164 case "CAP":
165 var subCmd string
166 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
167 return err
168 }
169 subCmd = strings.ToUpper(subCmd)
170 subParams := msg.Params[2:]
171 switch subCmd {
172 case "LS":
173 if len(subParams) < 1 {
174 return newNeedMoreParamsError(msg.Command)
175 }
176 caps := strings.Fields(subParams[len(subParams)-1])
177 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
178
179 for _, s := range caps {
180 kv := strings.SplitN(s, "=", 2)
181 k := strings.ToLower(kv[0])
182 var v string
183 if len(kv) == 2 {
184 v = kv[1]
185 }
186 uc.caps[k] = v
187 }
188
189 if more {
190 break // wait to receive all capabilities
191 }
192
193 if uc.requestSASL() {
194 uc.SendMessage(&irc.Message{
195 Command: "CAP",
196 Params: []string{"REQ", "sasl"},
197 })
198 break // we'll send CAP END after authentication is completed
199 }
200
201 uc.SendMessage(&irc.Message{
202 Command: "CAP",
203 Params: []string{"END"},
204 })
205 case "ACK", "NAK":
206 if len(subParams) < 1 {
207 return newNeedMoreParamsError(msg.Command)
208 }
209 caps := strings.Fields(subParams[0])
210
211 for _, name := range caps {
212 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
213 return err
214 }
215 }
216
217 if uc.saslClient == nil {
218 uc.SendMessage(&irc.Message{
219 Command: "CAP",
220 Params: []string{"END"},
221 })
222 }
223 default:
224 uc.logger.Printf("unhandled message: %v", msg)
225 }
226 case "AUTHENTICATE":
227 if uc.saslClient == nil {
228 return fmt.Errorf("received unexpected AUTHENTICATE message")
229 }
230
231 // TODO: if a challenge is 400 bytes long, buffer it
232 var challengeStr string
233 if err := parseMessageParams(msg, &challengeStr); err != nil {
234 uc.SendMessage(&irc.Message{
235 Command: "AUTHENTICATE",
236 Params: []string{"*"},
237 })
238 return err
239 }
240
241 var challenge []byte
242 if challengeStr != "+" {
243 var err error
244 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
245 if err != nil {
246 uc.SendMessage(&irc.Message{
247 Command: "AUTHENTICATE",
248 Params: []string{"*"},
249 })
250 return err
251 }
252 }
253
254 var resp []byte
255 var err error
256 if !uc.saslStarted {
257 _, resp, err = uc.saslClient.Start()
258 uc.saslStarted = true
259 } else {
260 resp, err = uc.saslClient.Next(challenge)
261 }
262 if err != nil {
263 uc.SendMessage(&irc.Message{
264 Command: "AUTHENTICATE",
265 Params: []string{"*"},
266 })
267 return err
268 }
269
270 // TODO: send response in multiple chunks if >= 400 bytes
271 var respStr = "+"
272 if resp != nil {
273 respStr = base64.StdEncoding.EncodeToString(resp)
274 }
275
276 uc.SendMessage(&irc.Message{
277 Command: "AUTHENTICATE",
278 Params: []string{respStr},
279 })
280 case irc.RPL_LOGGEDIN:
281 var account string
282 if err := parseMessageParams(msg, nil, nil, &account); err != nil {
283 return err
284 }
285 uc.logger.Printf("logged in with account %q", account)
286 case irc.RPL_LOGGEDOUT:
287 uc.logger.Printf("logged out")
288 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
289 var info string
290 if err := parseMessageParams(msg, nil, &info); err != nil {
291 return err
292 }
293 switch msg.Command {
294 case irc.ERR_NICKLOCKED:
295 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
296 case irc.ERR_SASLFAIL:
297 uc.logger.Printf("SASL authentication failed: %v", info)
298 case irc.ERR_SASLTOOLONG:
299 uc.logger.Printf("SASL message too long: %v", info)
300 }
301
302 uc.saslClient = nil
303 uc.saslStarted = false
304
305 uc.SendMessage(&irc.Message{
306 Command: "CAP",
307 Params: []string{"END"},
308 })
309 case irc.RPL_WELCOME:
310 uc.registered = true
311 uc.logger.Printf("connection registered")
312
313 channels, err := uc.srv.db.ListChannels(uc.network.ID)
314 if err != nil {
315 uc.logger.Printf("failed to list channels from database: %v", err)
316 break
317 }
318
319 for _, ch := range channels {
320 uc.SendMessage(&irc.Message{
321 Command: "JOIN",
322 Params: []string{ch.Name},
323 })
324 }
325 case irc.RPL_MYINFO:
326 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
327 return err
328 }
329 case irc.RPL_ISUPPORT:
330 if err := parseMessageParams(msg, nil, nil); err != nil {
331 return err
332 }
333 for _, token := range msg.Params[1 : len(msg.Params)-1] {
334 negate := false
335 parameter := token
336 value := ""
337 if strings.HasPrefix(token, "-") {
338 negate = true
339 token = token[1:]
340 } else {
341 if i := strings.IndexByte(token, '='); i >= 0 {
342 parameter = token[:i]
343 value = token[i+1:]
344 }
345 }
346 if !negate {
347 switch parameter {
348 case "CHANMODES":
349 parts := strings.SplitN(value, ",", 5)
350 if len(parts) < 4 {
351 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", value)
352 }
353 modes := make(map[byte]channelModeType)
354 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
355 for j := 0; j < len(parts[i]); j++ {
356 mode := parts[i][j]
357 modes[mode] = mt
358 }
359 }
360 uc.availableChannelModes = modes
361 case "CHANTYPES":
362 uc.availableChannelTypes = value
363 case "PREFIX":
364 if value == "" {
365 uc.availableMemberships = nil
366 } else {
367 if value[0] != '(' {
368 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
369 }
370 sep := strings.IndexByte(value, ')')
371 if sep < 0 || len(value) != sep*2 {
372 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
373 }
374 memberships := make([]membership, len(value)/2-1)
375 for i := range memberships {
376 memberships[i] = membership{
377 Mode: value[i+1],
378 Prefix: value[sep+i+1],
379 }
380 }
381 uc.availableMemberships = memberships
382 }
383 }
384 } else {
385 // TODO: handle ISUPPORT negations
386 }
387 }
388 case "NICK":
389 if msg.Prefix == nil {
390 return fmt.Errorf("expected a prefix")
391 }
392
393 var newNick string
394 if err := parseMessageParams(msg, &newNick); err != nil {
395 return err
396 }
397
398 if msg.Prefix.Name == uc.nick {
399 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
400 uc.nick = newNick
401 }
402
403 for _, ch := range uc.channels {
404 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
405 delete(ch.Members, msg.Prefix.Name)
406 ch.Members[newNick] = membership
407 }
408 }
409
410 if msg.Prefix.Name != uc.nick {
411 uc.forEachDownstream(func(dc *downstreamConn) {
412 dc.SendMessage(&irc.Message{
413 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
414 Command: "NICK",
415 Params: []string{newNick},
416 })
417 })
418 }
419 case "JOIN":
420 if msg.Prefix == nil {
421 return fmt.Errorf("expected a prefix")
422 }
423
424 var channels string
425 if err := parseMessageParams(msg, &channels); err != nil {
426 return err
427 }
428
429 for _, ch := range strings.Split(channels, ",") {
430 if msg.Prefix.Name == uc.nick {
431 uc.logger.Printf("joined channel %q", ch)
432 uc.channels[ch] = &upstreamChannel{
433 Name: ch,
434 conn: uc,
435 Members: make(map[string]*membership),
436 }
437
438 uc.SendMessage(&irc.Message{
439 Command: "MODE",
440 Params: []string{ch},
441 })
442 } else {
443 ch, err := uc.getChannel(ch)
444 if err != nil {
445 return err
446 }
447 ch.Members[msg.Prefix.Name] = nil
448 }
449
450 uc.forEachDownstream(func(dc *downstreamConn) {
451 dc.SendMessage(&irc.Message{
452 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
453 Command: "JOIN",
454 Params: []string{dc.marshalChannel(uc, ch)},
455 })
456 })
457 }
458 case "PART":
459 if msg.Prefix == nil {
460 return fmt.Errorf("expected a prefix")
461 }
462
463 var channels string
464 if err := parseMessageParams(msg, &channels); err != nil {
465 return err
466 }
467
468 for _, ch := range strings.Split(channels, ",") {
469 if msg.Prefix.Name == uc.nick {
470 uc.logger.Printf("parted channel %q", ch)
471 delete(uc.channels, ch)
472 } else {
473 ch, err := uc.getChannel(ch)
474 if err != nil {
475 return err
476 }
477 delete(ch.Members, msg.Prefix.Name)
478 }
479
480 uc.forEachDownstream(func(dc *downstreamConn) {
481 dc.SendMessage(&irc.Message{
482 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
483 Command: "PART",
484 Params: []string{dc.marshalChannel(uc, ch)},
485 })
486 })
487 }
488 case "QUIT":
489 if msg.Prefix == nil {
490 return fmt.Errorf("expected a prefix")
491 }
492
493 if msg.Prefix.Name == uc.nick {
494 uc.logger.Printf("quit")
495 }
496
497 for _, ch := range uc.channels {
498 delete(ch.Members, msg.Prefix.Name)
499 }
500
501 if msg.Prefix.Name != uc.nick {
502 uc.forEachDownstream(func(dc *downstreamConn) {
503 dc.SendMessage(&irc.Message{
504 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
505 Command: "QUIT",
506 Params: msg.Params,
507 })
508 })
509 }
510 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
511 var name, topic string
512 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
513 return err
514 }
515 ch, err := uc.getChannel(name)
516 if err != nil {
517 return err
518 }
519 if msg.Command == irc.RPL_TOPIC {
520 ch.Topic = topic
521 } else {
522 ch.Topic = ""
523 }
524 case "TOPIC":
525 var name string
526 if err := parseMessageParams(msg, &name); err != nil {
527 return err
528 }
529 ch, err := uc.getChannel(name)
530 if err != nil {
531 return err
532 }
533 if len(msg.Params) > 1 {
534 ch.Topic = msg.Params[1]
535 } else {
536 ch.Topic = ""
537 }
538 uc.forEachDownstream(func(dc *downstreamConn) {
539 params := []string{dc.marshalChannel(uc, name)}
540 if ch.Topic != "" {
541 params = append(params, ch.Topic)
542 }
543 dc.SendMessage(&irc.Message{
544 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
545 Command: "TOPIC",
546 Params: params,
547 })
548 })
549 case "MODE":
550 var name, modeStr string
551 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
552 return err
553 }
554
555 if !uc.isChannel(name) { // user mode change
556 if name != uc.nick {
557 return fmt.Errorf("received MODE message for unknown nick %q", name)
558 }
559 return uc.modes.Apply(modeStr)
560 // TODO: notify downstreams about user mode change?
561 } else { // channel mode change
562 ch, err := uc.getChannel(name)
563 if err != nil {
564 return err
565 }
566
567 if ch.modes != nil {
568 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[2:]...); err != nil {
569 return err
570 }
571 }
572
573 uc.forEachDownstream(func(dc *downstreamConn) {
574 params := []string{dc.marshalChannel(uc, name), modeStr}
575 params = append(params, msg.Params[2:]...)
576
577 dc.SendMessage(&irc.Message{
578 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
579 Command: "MODE",
580 Params: params,
581 })
582 })
583 }
584 case irc.RPL_UMODEIS:
585 if err := parseMessageParams(msg, nil); err != nil {
586 return err
587 }
588 modeStr := ""
589 if len(msg.Params) > 1 {
590 modeStr = msg.Params[1]
591 }
592
593 uc.modes = ""
594 if err := uc.modes.Apply(modeStr); err != nil {
595 return err
596 }
597 // TODO: send RPL_UMODEIS to downstream connections when applicable
598 case irc.RPL_CHANNELMODEIS:
599 var channel string
600 if err := parseMessageParams(msg, nil, &channel); err != nil {
601 return err
602 }
603 modeStr := ""
604 if len(msg.Params) > 2 {
605 modeStr = msg.Params[2]
606 }
607
608 ch, err := uc.getChannel(channel)
609 if err != nil {
610 return err
611 }
612
613 firstMode := ch.modes == nil
614 ch.modes = make(map[byte]string)
615 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[3:]...); err != nil {
616 return err
617 }
618 if firstMode {
619 modeStr, modeParams := ch.modes.Format()
620
621 uc.forEachDownstream(func(dc *downstreamConn) {
622 params := []string{dc.nick, dc.marshalChannel(uc, channel), modeStr}
623 params = append(params, modeParams...)
624
625 dc.SendMessage(&irc.Message{
626 Prefix: dc.srv.prefix(),
627 Command: irc.RPL_CHANNELMODEIS,
628 Params: params,
629 })
630 })
631 }
632 case rpl_topicwhotime:
633 var name, who, timeStr string
634 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
635 return err
636 }
637 ch, err := uc.getChannel(name)
638 if err != nil {
639 return err
640 }
641 ch.TopicWho = who
642 sec, err := strconv.ParseInt(timeStr, 10, 64)
643 if err != nil {
644 return fmt.Errorf("failed to parse topic time: %v", err)
645 }
646 ch.TopicTime = time.Unix(sec, 0)
647 case irc.RPL_NAMREPLY:
648 var name, statusStr, members string
649 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
650 return err
651 }
652
653 ch, ok := uc.channels[name]
654 if !ok {
655 // NAMES on a channel we have not joined, forward to downstream
656 uc.forEachDownstream(func(dc *downstreamConn) {
657 channel := dc.marshalChannel(uc, name)
658 members := strings.Split(members, " ")
659 for i, member := range members {
660 membership, nick := uc.parseMembershipPrefix(member)
661 members[i] = membership.String() + dc.marshalNick(uc, nick)
662 }
663 memberStr := strings.Join(members, " ")
664
665 dc.SendMessage(&irc.Message{
666 Prefix: dc.srv.prefix(),
667 Command: irc.RPL_NAMREPLY,
668 Params: []string{dc.nick, statusStr, channel, memberStr},
669 })
670 })
671 return nil
672 }
673
674 status, err := parseChannelStatus(statusStr)
675 if err != nil {
676 return err
677 }
678 ch.Status = status
679
680 for _, s := range strings.Split(members, " ") {
681 membership, nick := uc.parseMembershipPrefix(s)
682 ch.Members[nick] = membership
683 }
684 case irc.RPL_ENDOFNAMES:
685 var name string
686 if err := parseMessageParams(msg, nil, &name); err != nil {
687 return err
688 }
689
690 ch, ok := uc.channels[name]
691 if !ok {
692 // NAMES on a channel we have not joined, forward to downstream
693 uc.forEachDownstream(func(dc *downstreamConn) {
694 channel := dc.marshalChannel(uc, name)
695
696 dc.SendMessage(&irc.Message{
697 Prefix: dc.srv.prefix(),
698 Command: irc.RPL_ENDOFNAMES,
699 Params: []string{dc.nick, channel, "End of /NAMES list"},
700 })
701 })
702 return nil
703 }
704
705 if ch.complete {
706 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
707 }
708 ch.complete = true
709
710 uc.forEachDownstream(func(dc *downstreamConn) {
711 forwardChannel(dc, ch)
712 })
713 case irc.RPL_WHOREPLY:
714 var channel, username, host, server, nick, mode, trailing string
715 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
716 return err
717 }
718
719 parts := strings.SplitN(trailing, " ", 2)
720 if len(parts) != 2 {
721 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
722 }
723 realname := parts[1]
724 hops, err := strconv.Atoi(parts[0])
725 if err != nil {
726 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
727 }
728 hops++
729
730 trailing = strconv.Itoa(hops) + " " + realname
731
732 uc.forEachDownstream(func(dc *downstreamConn) {
733 channel := channel
734 if channel != "*" {
735 channel = dc.marshalChannel(uc, channel)
736 }
737 nick := dc.marshalNick(uc, nick)
738 dc.SendMessage(&irc.Message{
739 Prefix: dc.srv.prefix(),
740 Command: irc.RPL_WHOREPLY,
741 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
742 })
743 })
744 case irc.RPL_ENDOFWHO:
745 var name string
746 if err := parseMessageParams(msg, nil, &name); err != nil {
747 return err
748 }
749
750 uc.forEachDownstream(func(dc *downstreamConn) {
751 name := name
752 if name != "*" {
753 // TODO: support WHO masks
754 name = dc.marshalEntity(uc, name)
755 }
756 dc.SendMessage(&irc.Message{
757 Prefix: dc.srv.prefix(),
758 Command: irc.RPL_ENDOFWHO,
759 Params: []string{dc.nick, name, "End of /WHO list"},
760 })
761 })
762 case irc.RPL_WHOISUSER:
763 var nick, username, host, realname string
764 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
765 return err
766 }
767
768 uc.forEachDownstream(func(dc *downstreamConn) {
769 nick := dc.marshalNick(uc, nick)
770 dc.SendMessage(&irc.Message{
771 Prefix: dc.srv.prefix(),
772 Command: irc.RPL_WHOISUSER,
773 Params: []string{dc.nick, nick, username, host, "*", realname},
774 })
775 })
776 case irc.RPL_WHOISSERVER:
777 var nick, server, serverInfo string
778 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
779 return err
780 }
781
782 uc.forEachDownstream(func(dc *downstreamConn) {
783 nick := dc.marshalNick(uc, nick)
784 dc.SendMessage(&irc.Message{
785 Prefix: dc.srv.prefix(),
786 Command: irc.RPL_WHOISSERVER,
787 Params: []string{dc.nick, nick, server, serverInfo},
788 })
789 })
790 case irc.RPL_WHOISOPERATOR:
791 var nick string
792 if err := parseMessageParams(msg, nil, &nick); err != nil {
793 return err
794 }
795
796 uc.forEachDownstream(func(dc *downstreamConn) {
797 nick := dc.marshalNick(uc, nick)
798 dc.SendMessage(&irc.Message{
799 Prefix: dc.srv.prefix(),
800 Command: irc.RPL_WHOISOPERATOR,
801 Params: []string{dc.nick, nick, "is an IRC operator"},
802 })
803 })
804 case irc.RPL_WHOISIDLE:
805 var nick string
806 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
807 return err
808 }
809
810 uc.forEachDownstream(func(dc *downstreamConn) {
811 nick := dc.marshalNick(uc, nick)
812 params := []string{dc.nick, nick}
813 params = append(params, msg.Params[2:]...)
814 dc.SendMessage(&irc.Message{
815 Prefix: dc.srv.prefix(),
816 Command: irc.RPL_WHOISIDLE,
817 Params: params,
818 })
819 })
820 case irc.RPL_WHOISCHANNELS:
821 var nick, channelList string
822 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
823 return err
824 }
825 channels := strings.Split(channelList, " ")
826
827 uc.forEachDownstream(func(dc *downstreamConn) {
828 nick := dc.marshalNick(uc, nick)
829 channelList := make([]string, len(channels))
830 for i, channel := range channels {
831 prefix, channel := uc.parseMembershipPrefix(channel)
832 channel = dc.marshalChannel(uc, channel)
833 channelList[i] = prefix.String() + channel
834 }
835 channels := strings.Join(channelList, " ")
836 dc.SendMessage(&irc.Message{
837 Prefix: dc.srv.prefix(),
838 Command: irc.RPL_WHOISCHANNELS,
839 Params: []string{dc.nick, nick, channels},
840 })
841 })
842 case irc.RPL_ENDOFWHOIS:
843 var nick string
844 if err := parseMessageParams(msg, nil, &nick); err != nil {
845 return err
846 }
847
848 uc.forEachDownstream(func(dc *downstreamConn) {
849 nick := dc.marshalNick(uc, nick)
850 dc.SendMessage(&irc.Message{
851 Prefix: dc.srv.prefix(),
852 Command: irc.RPL_ENDOFWHOIS,
853 Params: []string{dc.nick, nick, "End of /WHOIS list"},
854 })
855 })
856 case "PRIVMSG":
857 if msg.Prefix == nil {
858 return fmt.Errorf("expected a prefix")
859 }
860
861 var nick string
862 if err := parseMessageParams(msg, &nick, nil); err != nil {
863 return err
864 }
865
866 if msg.Prefix.Name == serviceNick {
867 uc.logger.Printf("skipping PRIVMSG from soju's service: %v", msg)
868 break
869 }
870 if nick == serviceNick {
871 uc.logger.Printf("skipping PRIVMSG to soju's service: %v", msg)
872 break
873 }
874
875 uc.network.ring.Produce(msg)
876 case "INVITE":
877 var nick string
878 var channel string
879 if err := parseMessageParams(msg, &nick, &channel); err != nil {
880 return err
881 }
882
883 uc.forEachDownstream(func(dc *downstreamConn) {
884 dc.SendMessage(&irc.Message{
885 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
886 Command: "INVITE",
887 Params: []string{dc.marshalNick(uc, nick), dc.marshalChannel(uc, channel)},
888 })
889 })
890 case irc.RPL_YOURHOST, irc.RPL_CREATED:
891 // Ignore
892 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
893 // Ignore
894 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
895 // Ignore
896 case rpl_localusers, rpl_globalusers:
897 // Ignore
898 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
899 // Ignore
900 default:
901 uc.logger.Printf("unhandled message: %v", msg)
902 }
903 return nil
904}
905
906func (uc *upstreamConn) register() {
907 uc.nick = uc.network.Nick
908 uc.username = uc.network.Username
909 if uc.username == "" {
910 uc.username = uc.nick
911 }
912 uc.realname = uc.network.Realname
913 if uc.realname == "" {
914 uc.realname = uc.nick
915 }
916
917 uc.SendMessage(&irc.Message{
918 Command: "CAP",
919 Params: []string{"LS", "302"},
920 })
921
922 if uc.network.Pass != "" {
923 uc.SendMessage(&irc.Message{
924 Command: "PASS",
925 Params: []string{uc.network.Pass},
926 })
927 }
928
929 uc.SendMessage(&irc.Message{
930 Command: "NICK",
931 Params: []string{uc.nick},
932 })
933 uc.SendMessage(&irc.Message{
934 Command: "USER",
935 Params: []string{uc.username, "0", "*", uc.realname},
936 })
937}
938
939func (uc *upstreamConn) requestSASL() bool {
940 if uc.network.SASL.Mechanism == "" {
941 return false
942 }
943
944 v, ok := uc.caps["sasl"]
945 if !ok {
946 return false
947 }
948 if v != "" {
949 mechanisms := strings.Split(v, ",")
950 found := false
951 for _, mech := range mechanisms {
952 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
953 found = true
954 break
955 }
956 }
957 if !found {
958 return false
959 }
960 }
961
962 return true
963}
964
965func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
966 auth := &uc.network.SASL
967 switch name {
968 case "sasl":
969 if !ok {
970 uc.logger.Printf("server refused to acknowledge the SASL capability")
971 return nil
972 }
973
974 switch auth.Mechanism {
975 case "PLAIN":
976 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
977 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
978 default:
979 return fmt.Errorf("unsupported SASL mechanism %q", name)
980 }
981
982 uc.SendMessage(&irc.Message{
983 Command: "AUTHENTICATE",
984 Params: []string{auth.Mechanism},
985 })
986 }
987 return nil
988}
989
990func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
991 for {
992 msg, err := uc.irc.ReadMessage()
993 if err == io.EOF {
994 break
995 } else if err != nil {
996 return fmt.Errorf("failed to read IRC command: %v", err)
997 }
998
999 if uc.srv.Debug {
1000 uc.logger.Printf("received: %v", msg)
1001 }
1002
1003 ch <- upstreamIncomingMessage{msg, uc}
1004 }
1005
1006 return nil
1007}
1008
1009func (uc *upstreamConn) SendMessage(msg *irc.Message) {
1010 uc.outgoing <- msg
1011}
Note: See TracBrowser for help on using the repository browser.