source: code/trunk/upstream.go@ 141

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

Add downstream NAMES support

NAMES reply for channels currently joined will be returned from cache;
requests for channels not joined will be forwarded from upstream.

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