source: code/trunk/upstream.go@ 154

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

Add upstream batch capability support

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