source: code/trunk/upstream.go@ 149

Last change on this file since 149 was 146, checked in by contact, 5 years ago

Add support for channel keys

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 params := []string{ch.Name}
321 if ch.Key != "" {
322 params = append(params, ch.Key)
323 }
324 uc.SendMessage(&irc.Message{
325 Command: "JOIN",
326 Params: params,
327 })
328 }
329 case irc.RPL_MYINFO:
330 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
331 return err
332 }
333 case irc.RPL_ISUPPORT:
334 if err := parseMessageParams(msg, nil, nil); err != nil {
335 return err
336 }
337 for _, token := range msg.Params[1 : len(msg.Params)-1] {
338 negate := false
339 parameter := token
340 value := ""
341 if strings.HasPrefix(token, "-") {
342 negate = true
343 token = token[1:]
344 } else {
345 if i := strings.IndexByte(token, '='); i >= 0 {
346 parameter = token[:i]
347 value = token[i+1:]
348 }
349 }
350 if !negate {
351 switch parameter {
352 case "CHANMODES":
353 parts := strings.SplitN(value, ",", 5)
354 if len(parts) < 4 {
355 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", value)
356 }
357 modes := make(map[byte]channelModeType)
358 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
359 for j := 0; j < len(parts[i]); j++ {
360 mode := parts[i][j]
361 modes[mode] = mt
362 }
363 }
364 uc.availableChannelModes = modes
365 case "CHANTYPES":
366 uc.availableChannelTypes = value
367 case "PREFIX":
368 if value == "" {
369 uc.availableMemberships = nil
370 } else {
371 if value[0] != '(' {
372 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
373 }
374 sep := strings.IndexByte(value, ')')
375 if sep < 0 || len(value) != sep*2 {
376 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", value)
377 }
378 memberships := make([]membership, len(value)/2-1)
379 for i := range memberships {
380 memberships[i] = membership{
381 Mode: value[i+1],
382 Prefix: value[sep+i+1],
383 }
384 }
385 uc.availableMemberships = memberships
386 }
387 }
388 } else {
389 // TODO: handle ISUPPORT negations
390 }
391 }
392 case "NICK":
393 if msg.Prefix == nil {
394 return fmt.Errorf("expected a prefix")
395 }
396
397 var newNick string
398 if err := parseMessageParams(msg, &newNick); err != nil {
399 return err
400 }
401
402 if msg.Prefix.Name == uc.nick {
403 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
404 uc.nick = newNick
405 }
406
407 for _, ch := range uc.channels {
408 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
409 delete(ch.Members, msg.Prefix.Name)
410 ch.Members[newNick] = membership
411 }
412 }
413
414 if msg.Prefix.Name != uc.nick {
415 uc.forEachDownstream(func(dc *downstreamConn) {
416 dc.SendMessage(&irc.Message{
417 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
418 Command: "NICK",
419 Params: []string{newNick},
420 })
421 })
422 }
423 case "JOIN":
424 if msg.Prefix == nil {
425 return fmt.Errorf("expected a prefix")
426 }
427
428 var channels string
429 if err := parseMessageParams(msg, &channels); err != nil {
430 return err
431 }
432
433 for _, ch := range strings.Split(channels, ",") {
434 if msg.Prefix.Name == uc.nick {
435 uc.logger.Printf("joined channel %q", ch)
436 uc.channels[ch] = &upstreamChannel{
437 Name: ch,
438 conn: uc,
439 Members: make(map[string]*membership),
440 }
441
442 uc.SendMessage(&irc.Message{
443 Command: "MODE",
444 Params: []string{ch},
445 })
446 } else {
447 ch, err := uc.getChannel(ch)
448 if err != nil {
449 return err
450 }
451 ch.Members[msg.Prefix.Name] = nil
452 }
453
454 uc.forEachDownstream(func(dc *downstreamConn) {
455 dc.SendMessage(&irc.Message{
456 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
457 Command: "JOIN",
458 Params: []string{dc.marshalChannel(uc, ch)},
459 })
460 })
461 }
462 case "PART":
463 if msg.Prefix == nil {
464 return fmt.Errorf("expected a prefix")
465 }
466
467 var channels string
468 if err := parseMessageParams(msg, &channels); err != nil {
469 return err
470 }
471
472 for _, ch := range strings.Split(channels, ",") {
473 if msg.Prefix.Name == uc.nick {
474 uc.logger.Printf("parted channel %q", ch)
475 delete(uc.channels, ch)
476 } else {
477 ch, err := uc.getChannel(ch)
478 if err != nil {
479 return err
480 }
481 delete(ch.Members, msg.Prefix.Name)
482 }
483
484 uc.forEachDownstream(func(dc *downstreamConn) {
485 dc.SendMessage(&irc.Message{
486 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
487 Command: "PART",
488 Params: []string{dc.marshalChannel(uc, ch)},
489 })
490 })
491 }
492 case "QUIT":
493 if msg.Prefix == nil {
494 return fmt.Errorf("expected a prefix")
495 }
496
497 if msg.Prefix.Name == uc.nick {
498 uc.logger.Printf("quit")
499 }
500
501 for _, ch := range uc.channels {
502 delete(ch.Members, msg.Prefix.Name)
503 }
504
505 if msg.Prefix.Name != uc.nick {
506 uc.forEachDownstream(func(dc *downstreamConn) {
507 dc.SendMessage(&irc.Message{
508 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
509 Command: "QUIT",
510 Params: msg.Params,
511 })
512 })
513 }
514 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
515 var name, topic string
516 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
517 return err
518 }
519 ch, err := uc.getChannel(name)
520 if err != nil {
521 return err
522 }
523 if msg.Command == irc.RPL_TOPIC {
524 ch.Topic = topic
525 } else {
526 ch.Topic = ""
527 }
528 case "TOPIC":
529 var name string
530 if err := parseMessageParams(msg, &name); err != nil {
531 return err
532 }
533 ch, err := uc.getChannel(name)
534 if err != nil {
535 return err
536 }
537 if len(msg.Params) > 1 {
538 ch.Topic = msg.Params[1]
539 } else {
540 ch.Topic = ""
541 }
542 uc.forEachDownstream(func(dc *downstreamConn) {
543 params := []string{dc.marshalChannel(uc, name)}
544 if ch.Topic != "" {
545 params = append(params, ch.Topic)
546 }
547 dc.SendMessage(&irc.Message{
548 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
549 Command: "TOPIC",
550 Params: params,
551 })
552 })
553 case "MODE":
554 var name, modeStr string
555 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
556 return err
557 }
558
559 if !uc.isChannel(name) { // user mode change
560 if name != uc.nick {
561 return fmt.Errorf("received MODE message for unknown nick %q", name)
562 }
563 return uc.modes.Apply(modeStr)
564 // TODO: notify downstreams about user mode change?
565 } else { // channel mode change
566 ch, err := uc.getChannel(name)
567 if err != nil {
568 return err
569 }
570
571 if ch.modes != nil {
572 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[2:]...); err != nil {
573 return err
574 }
575 }
576
577 uc.forEachDownstream(func(dc *downstreamConn) {
578 params := []string{dc.marshalChannel(uc, name), modeStr}
579 params = append(params, msg.Params[2:]...)
580
581 dc.SendMessage(&irc.Message{
582 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
583 Command: "MODE",
584 Params: params,
585 })
586 })
587 }
588 case irc.RPL_UMODEIS:
589 if err := parseMessageParams(msg, nil); err != nil {
590 return err
591 }
592 modeStr := ""
593 if len(msg.Params) > 1 {
594 modeStr = msg.Params[1]
595 }
596
597 uc.modes = ""
598 if err := uc.modes.Apply(modeStr); err != nil {
599 return err
600 }
601 // TODO: send RPL_UMODEIS to downstream connections when applicable
602 case irc.RPL_CHANNELMODEIS:
603 var channel string
604 if err := parseMessageParams(msg, nil, &channel); err != nil {
605 return err
606 }
607 modeStr := ""
608 if len(msg.Params) > 2 {
609 modeStr = msg.Params[2]
610 }
611
612 ch, err := uc.getChannel(channel)
613 if err != nil {
614 return err
615 }
616
617 firstMode := ch.modes == nil
618 ch.modes = make(map[byte]string)
619 if err := ch.modes.Apply(uc.availableChannelModes, modeStr, msg.Params[3:]...); err != nil {
620 return err
621 }
622 if firstMode {
623 modeStr, modeParams := ch.modes.Format()
624
625 uc.forEachDownstream(func(dc *downstreamConn) {
626 params := []string{dc.nick, dc.marshalChannel(uc, channel), modeStr}
627 params = append(params, modeParams...)
628
629 dc.SendMessage(&irc.Message{
630 Prefix: dc.srv.prefix(),
631 Command: irc.RPL_CHANNELMODEIS,
632 Params: params,
633 })
634 })
635 }
636 case rpl_topicwhotime:
637 var name, who, timeStr string
638 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
639 return err
640 }
641 ch, err := uc.getChannel(name)
642 if err != nil {
643 return err
644 }
645 ch.TopicWho = who
646 sec, err := strconv.ParseInt(timeStr, 10, 64)
647 if err != nil {
648 return fmt.Errorf("failed to parse topic time: %v", err)
649 }
650 ch.TopicTime = time.Unix(sec, 0)
651 case irc.RPL_NAMREPLY:
652 var name, statusStr, members string
653 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
654 return err
655 }
656
657 ch, ok := uc.channels[name]
658 if !ok {
659 // NAMES on a channel we have not joined, forward to downstream
660 uc.forEachDownstream(func(dc *downstreamConn) {
661 channel := dc.marshalChannel(uc, name)
662 members := strings.Split(members, " ")
663 for i, member := range members {
664 membership, nick := uc.parseMembershipPrefix(member)
665 members[i] = membership.String() + dc.marshalNick(uc, nick)
666 }
667 memberStr := strings.Join(members, " ")
668
669 dc.SendMessage(&irc.Message{
670 Prefix: dc.srv.prefix(),
671 Command: irc.RPL_NAMREPLY,
672 Params: []string{dc.nick, statusStr, channel, memberStr},
673 })
674 })
675 return nil
676 }
677
678 status, err := parseChannelStatus(statusStr)
679 if err != nil {
680 return err
681 }
682 ch.Status = status
683
684 for _, s := range strings.Split(members, " ") {
685 membership, nick := uc.parseMembershipPrefix(s)
686 ch.Members[nick] = membership
687 }
688 case irc.RPL_ENDOFNAMES:
689 var name string
690 if err := parseMessageParams(msg, nil, &name); err != nil {
691 return err
692 }
693
694 ch, ok := uc.channels[name]
695 if !ok {
696 // NAMES on a channel we have not joined, forward to downstream
697 uc.forEachDownstream(func(dc *downstreamConn) {
698 channel := dc.marshalChannel(uc, name)
699
700 dc.SendMessage(&irc.Message{
701 Prefix: dc.srv.prefix(),
702 Command: irc.RPL_ENDOFNAMES,
703 Params: []string{dc.nick, channel, "End of /NAMES list"},
704 })
705 })
706 return nil
707 }
708
709 if ch.complete {
710 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
711 }
712 ch.complete = true
713
714 uc.forEachDownstream(func(dc *downstreamConn) {
715 forwardChannel(dc, ch)
716 })
717 case irc.RPL_WHOREPLY:
718 var channel, username, host, server, nick, mode, trailing string
719 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
720 return err
721 }
722
723 parts := strings.SplitN(trailing, " ", 2)
724 if len(parts) != 2 {
725 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
726 }
727 realname := parts[1]
728 hops, err := strconv.Atoi(parts[0])
729 if err != nil {
730 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
731 }
732 hops++
733
734 trailing = strconv.Itoa(hops) + " " + realname
735
736 uc.forEachDownstream(func(dc *downstreamConn) {
737 channel := channel
738 if channel != "*" {
739 channel = dc.marshalChannel(uc, channel)
740 }
741 nick := dc.marshalNick(uc, nick)
742 dc.SendMessage(&irc.Message{
743 Prefix: dc.srv.prefix(),
744 Command: irc.RPL_WHOREPLY,
745 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
746 })
747 })
748 case irc.RPL_ENDOFWHO:
749 var name string
750 if err := parseMessageParams(msg, nil, &name); err != nil {
751 return err
752 }
753
754 uc.forEachDownstream(func(dc *downstreamConn) {
755 name := name
756 if name != "*" {
757 // TODO: support WHO masks
758 name = dc.marshalEntity(uc, name)
759 }
760 dc.SendMessage(&irc.Message{
761 Prefix: dc.srv.prefix(),
762 Command: irc.RPL_ENDOFWHO,
763 Params: []string{dc.nick, name, "End of /WHO list"},
764 })
765 })
766 case irc.RPL_WHOISUSER:
767 var nick, username, host, realname string
768 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
769 return err
770 }
771
772 uc.forEachDownstream(func(dc *downstreamConn) {
773 nick := dc.marshalNick(uc, nick)
774 dc.SendMessage(&irc.Message{
775 Prefix: dc.srv.prefix(),
776 Command: irc.RPL_WHOISUSER,
777 Params: []string{dc.nick, nick, username, host, "*", realname},
778 })
779 })
780 case irc.RPL_WHOISSERVER:
781 var nick, server, serverInfo string
782 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
783 return err
784 }
785
786 uc.forEachDownstream(func(dc *downstreamConn) {
787 nick := dc.marshalNick(uc, nick)
788 dc.SendMessage(&irc.Message{
789 Prefix: dc.srv.prefix(),
790 Command: irc.RPL_WHOISSERVER,
791 Params: []string{dc.nick, nick, server, serverInfo},
792 })
793 })
794 case irc.RPL_WHOISOPERATOR:
795 var nick string
796 if err := parseMessageParams(msg, nil, &nick); err != nil {
797 return err
798 }
799
800 uc.forEachDownstream(func(dc *downstreamConn) {
801 nick := dc.marshalNick(uc, nick)
802 dc.SendMessage(&irc.Message{
803 Prefix: dc.srv.prefix(),
804 Command: irc.RPL_WHOISOPERATOR,
805 Params: []string{dc.nick, nick, "is an IRC operator"},
806 })
807 })
808 case irc.RPL_WHOISIDLE:
809 var nick string
810 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
811 return err
812 }
813
814 uc.forEachDownstream(func(dc *downstreamConn) {
815 nick := dc.marshalNick(uc, nick)
816 params := []string{dc.nick, nick}
817 params = append(params, msg.Params[2:]...)
818 dc.SendMessage(&irc.Message{
819 Prefix: dc.srv.prefix(),
820 Command: irc.RPL_WHOISIDLE,
821 Params: params,
822 })
823 })
824 case irc.RPL_WHOISCHANNELS:
825 var nick, channelList string
826 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
827 return err
828 }
829 channels := strings.Split(channelList, " ")
830
831 uc.forEachDownstream(func(dc *downstreamConn) {
832 nick := dc.marshalNick(uc, nick)
833 channelList := make([]string, len(channels))
834 for i, channel := range channels {
835 prefix, channel := uc.parseMembershipPrefix(channel)
836 channel = dc.marshalChannel(uc, channel)
837 channelList[i] = prefix.String() + channel
838 }
839 channels := strings.Join(channelList, " ")
840 dc.SendMessage(&irc.Message{
841 Prefix: dc.srv.prefix(),
842 Command: irc.RPL_WHOISCHANNELS,
843 Params: []string{dc.nick, nick, channels},
844 })
845 })
846 case irc.RPL_ENDOFWHOIS:
847 var nick string
848 if err := parseMessageParams(msg, nil, &nick); err != nil {
849 return err
850 }
851
852 uc.forEachDownstream(func(dc *downstreamConn) {
853 nick := dc.marshalNick(uc, nick)
854 dc.SendMessage(&irc.Message{
855 Prefix: dc.srv.prefix(),
856 Command: irc.RPL_ENDOFWHOIS,
857 Params: []string{dc.nick, nick, "End of /WHOIS list"},
858 })
859 })
860 case "PRIVMSG":
861 if msg.Prefix == nil {
862 return fmt.Errorf("expected a prefix")
863 }
864
865 var nick string
866 if err := parseMessageParams(msg, &nick, nil); err != nil {
867 return err
868 }
869
870 if msg.Prefix.Name == serviceNick {
871 uc.logger.Printf("skipping PRIVMSG from soju's service: %v", msg)
872 break
873 }
874 if nick == serviceNick {
875 uc.logger.Printf("skipping PRIVMSG to soju's service: %v", msg)
876 break
877 }
878
879 uc.network.ring.Produce(msg)
880 case "INVITE":
881 var nick string
882 var channel string
883 if err := parseMessageParams(msg, &nick, &channel); err != nil {
884 return err
885 }
886
887 uc.forEachDownstream(func(dc *downstreamConn) {
888 dc.SendMessage(&irc.Message{
889 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
890 Command: "INVITE",
891 Params: []string{dc.marshalNick(uc, nick), dc.marshalChannel(uc, channel)},
892 })
893 })
894 case irc.RPL_YOURHOST, irc.RPL_CREATED:
895 // Ignore
896 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
897 // Ignore
898 case irc.RPL_MOTDSTART, irc.RPL_MOTD, irc.RPL_ENDOFMOTD:
899 // Ignore
900 case rpl_localusers, rpl_globalusers:
901 // Ignore
902 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
903 // Ignore
904 default:
905 uc.logger.Printf("unhandled message: %v", msg)
906 }
907 return nil
908}
909
910func (uc *upstreamConn) register() {
911 uc.nick = uc.network.Nick
912 uc.username = uc.network.Username
913 if uc.username == "" {
914 uc.username = uc.nick
915 }
916 uc.realname = uc.network.Realname
917 if uc.realname == "" {
918 uc.realname = uc.nick
919 }
920
921 uc.SendMessage(&irc.Message{
922 Command: "CAP",
923 Params: []string{"LS", "302"},
924 })
925
926 if uc.network.Pass != "" {
927 uc.SendMessage(&irc.Message{
928 Command: "PASS",
929 Params: []string{uc.network.Pass},
930 })
931 }
932
933 uc.SendMessage(&irc.Message{
934 Command: "NICK",
935 Params: []string{uc.nick},
936 })
937 uc.SendMessage(&irc.Message{
938 Command: "USER",
939 Params: []string{uc.username, "0", "*", uc.realname},
940 })
941}
942
943func (uc *upstreamConn) requestSASL() bool {
944 if uc.network.SASL.Mechanism == "" {
945 return false
946 }
947
948 v, ok := uc.caps["sasl"]
949 if !ok {
950 return false
951 }
952 if v != "" {
953 mechanisms := strings.Split(v, ",")
954 found := false
955 for _, mech := range mechanisms {
956 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
957 found = true
958 break
959 }
960 }
961 if !found {
962 return false
963 }
964 }
965
966 return true
967}
968
969func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
970 auth := &uc.network.SASL
971 switch name {
972 case "sasl":
973 if !ok {
974 uc.logger.Printf("server refused to acknowledge the SASL capability")
975 return nil
976 }
977
978 switch auth.Mechanism {
979 case "PLAIN":
980 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
981 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
982 default:
983 return fmt.Errorf("unsupported SASL mechanism %q", name)
984 }
985
986 uc.SendMessage(&irc.Message{
987 Command: "AUTHENTICATE",
988 Params: []string{auth.Mechanism},
989 })
990 }
991 return nil
992}
993
994func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
995 for {
996 msg, err := uc.irc.ReadMessage()
997 if err == io.EOF {
998 break
999 } else if err != nil {
1000 return fmt.Errorf("failed to read IRC command: %v", err)
1001 }
1002
1003 if uc.srv.Debug {
1004 uc.logger.Printf("received: %v", msg)
1005 }
1006
1007 ch <- upstreamIncomingMessage{msg, uc}
1008 }
1009
1010 return nil
1011}
1012
1013func (uc *upstreamConn) SendMessage(msg *irc.Message) {
1014 uc.outgoing <- msg
1015}
Note: See TracBrowser for help on using the repository browser.