source: code/trunk/upstream.go@ 153

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

Add upstream batch capability support

File size: 26.0 KB
RevLine 
[98]1package soju
[13]2
3import (
4 "crypto/tls"
[95]5 "encoding/base64"
[13]6 "fmt"
7 "io"
8 "net"
[19]9 "strconv"
[17]10 "strings"
[19]11 "time"
[13]12
[95]13 "github.com/emersion/go-sasl"
[13]14 "gopkg.in/irc.v3"
15)
16
[19]17type upstreamChannel struct {
18 Name string
[46]19 conn *upstreamConn
[19]20 Topic string
21 TopicWho string
22 TopicTime time.Time
23 Status channelStatus
[139]24 modes channelModes
25 Members map[string]*membership
[25]26 complete bool
[19]27}
28
[13]29type upstreamConn struct {
[77]30 network *network
[21]31 logger Logger
[19]32 net net.Conn
33 irc *irc.Conn
34 srv *Server
[37]35 user *user
[102]36 outgoing chan<- *irc.Message
[16]37
38 serverName string
39 availableUserModes string
[139]40 availableChannelModes map[byte]channelModeType
41 availableChannelTypes string
42 availableMemberships []membership
[19]43
44 registered bool
[42]45 nick string
[77]46 username string
47 realname string
[33]48 closed bool
[139]49 modes userModes
[19]50 channels map[string]*upstreamChannel
[92]51 caps map[string]string
[153]52 batches map[string]batch
[95]53
[152]54 tagsSupported bool
55
[95]56 saslClient sasl.Client
57 saslStarted bool
[13]58}
59
[77]60func connectToUpstream(network *network) (*upstreamConn, error) {
61 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
[33]62
[77]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)
[33]70 if err != nil {
[77]71 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
[33]72 }
73
[67]74 setKeepAlive(netConn)
75
[102]76 outgoing := make(chan *irc.Message, 64)
[55]77 uc := &upstreamConn{
[139]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),
[153]87 batches: make(map[string]batch),
[139]88 availableChannelTypes: stdChannelTypes,
89 availableChannelModes: stdChannelModes,
90 availableMemberships: stdMemberships,
[33]91 }
92
93 go func() {
[102]94 for msg := range outgoing {
[64]95 if uc.srv.Debug {
96 uc.logger.Printf("sent: %v", msg)
97 }
[55]98 if err := uc.irc.WriteMessage(msg); err != nil {
99 uc.logger.Printf("failed to write message: %v", err)
[33]100 }
101 }
[55]102 if err := uc.net.Close(); err != nil {
103 uc.logger.Printf("failed to close connection: %v", err)
[45]104 } else {
[55]105 uc.logger.Printf("connection closed")
[45]106 }
[33]107 }()
108
[55]109 return uc, nil
[33]110}
111
[55]112func (uc *upstreamConn) Close() error {
113 if uc.closed {
[33]114 return fmt.Errorf("upstream connection already closed")
115 }
[102]116 close(uc.outgoing)
[55]117 uc.closed = true
[33]118 return nil
119}
120
[73]121func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
122 uc.user.forEachDownstream(func(dc *downstreamConn) {
[77]123 if dc.network != nil && dc.network != uc.network {
[73]124 return
125 }
126 f(dc)
127 })
128}
129
[55]130func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
131 ch, ok := uc.channels[name]
[19]132 if !ok {
133 return nil, fmt.Errorf("unknown channel %q", name)
134 }
135 return ch, nil
136}
137
[129]138func (uc *upstreamConn) isChannel(entity string) bool {
[139]139 if i := strings.IndexByte(uc.availableChannelTypes, entity[0]); i >= 0 {
140 return true
[129]141 }
142 return false
143}
144
[139]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
[55]154func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
[153]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
[13]164 switch msg.Command {
165 case "PING":
[60]166 uc.SendMessage(&irc.Message{
[13]167 Command: "PONG",
[68]168 Params: msg.Params,
[60]169 })
[33]170 return nil
[18]171 case "NOTICE":
[55]172 uc.logger.Print(msg)
[97]173
174 uc.forEachDownstream(func(dc *downstreamConn) {
175 dc.SendMessage(msg)
176 })
[92]177 case "CAP":
[95]178 var subCmd string
179 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
180 return err
[92]181 }
[95]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] == "*"
[92]191
[95]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
[92]200 }
201
[95]202 if more {
203 break // wait to receive all capabilities
204 }
205
[152]206 requestCaps := make([]string, 0, 16)
[153]207 for _, c := range []string{"message-tags", "batch"} {
[152]208 if _, ok := uc.caps[c]; ok {
209 requestCaps = append(requestCaps, c)
210 }
211 }
212
[95]213 if uc.requestSASL() {
[152]214 requestCaps = append(requestCaps, "sasl")
215 }
216
217 if len(requestCaps) > 0 {
[95]218 uc.SendMessage(&irc.Message{
219 Command: "CAP",
[152]220 Params: []string{"REQ", strings.Join(requestCaps, " ")},
[95]221 })
[152]222 }
223
224 if uc.requestSASL() {
[95]225 break // we'll send CAP END after authentication is completed
226 }
227
[92]228 uc.SendMessage(&irc.Message{
229 Command: "CAP",
230 Params: []string{"END"},
231 })
[95]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)
[92]252 }
[95]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 })
[125]307 case irc.RPL_LOGGEDIN:
[95]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)
[125]313 case irc.RPL_LOGGEDOUT:
[95]314 uc.logger.Printf("logged out")
[125]315 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
[95]316 var info string
317 if err := parseMessageParams(msg, nil, &info); err != nil {
318 return err
319 }
320 switch msg.Command {
[125]321 case irc.ERR_NICKLOCKED:
[95]322 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
[125]323 case irc.ERR_SASLFAIL:
[95]324 uc.logger.Printf("SASL authentication failed: %v", info)
[125]325 case irc.ERR_SASLTOOLONG:
[95]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 })
[14]336 case irc.RPL_WELCOME:
[55]337 uc.registered = true
338 uc.logger.Printf("connection registered")
[19]339
[77]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 {
[146]347 params := []string{ch.Name}
348 if ch.Key != "" {
349 params = append(params, ch.Key)
350 }
[60]351 uc.SendMessage(&irc.Message{
[19]352 Command: "JOIN",
[146]353 Params: params,
[60]354 })
[19]355 }
[16]356 case irc.RPL_MYINFO:
[139]357 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
[43]358 return err
[16]359 }
[139]360 case irc.RPL_ISUPPORT:
361 if err := parseMessageParams(msg, nil, nil); err != nil {
362 return err
[16]363 }
[139]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 }
[153]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 }
[42]448 case "NICK":
[83]449 if msg.Prefix == nil {
450 return fmt.Errorf("expected a prefix")
451 }
452
[43]453 var newNick string
454 if err := parseMessageParams(msg, &newNick); err != nil {
455 return err
[42]456 }
457
[55]458 if msg.Prefix.Name == uc.nick {
459 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
460 uc.nick = newNick
[42]461 }
462
[55]463 for _, ch := range uc.channels {
[42]464 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
465 delete(ch.Members, msg.Prefix.Name)
466 ch.Members[newNick] = membership
467 }
468 }
[82]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 }
[69]479 case "JOIN":
480 if msg.Prefix == nil {
481 return fmt.Errorf("expected a prefix")
482 }
[42]483
[43]484 var channels string
485 if err := parseMessageParams(msg, &channels); err != nil {
486 return err
[19]487 }
[34]488
[43]489 for _, ch := range strings.Split(channels, ",") {
[55]490 if msg.Prefix.Name == uc.nick {
491 uc.logger.Printf("joined channel %q", ch)
492 uc.channels[ch] = &upstreamChannel{
[34]493 Name: ch,
[55]494 conn: uc,
[139]495 Members: make(map[string]*membership),
[34]496 }
[139]497
498 uc.SendMessage(&irc.Message{
499 Command: "MODE",
500 Params: []string{ch},
501 })
[34]502 } else {
[55]503 ch, err := uc.getChannel(ch)
[34]504 if err != nil {
505 return err
506 }
[139]507 ch.Members[msg.Prefix.Name] = nil
[19]508 }
[69]509
[73]510 uc.forEachDownstream(func(dc *downstreamConn) {
[69]511 dc.SendMessage(&irc.Message{
512 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
513 Command: "JOIN",
514 Params: []string{dc.marshalChannel(uc, ch)},
515 })
516 })
[19]517 }
[69]518 case "PART":
519 if msg.Prefix == nil {
520 return fmt.Errorf("expected a prefix")
521 }
[34]522
[43]523 var channels string
524 if err := parseMessageParams(msg, &channels); err != nil {
525 return err
[34]526 }
527
[43]528 for _, ch := range strings.Split(channels, ",") {
[55]529 if msg.Prefix.Name == uc.nick {
530 uc.logger.Printf("parted channel %q", ch)
531 delete(uc.channels, ch)
[34]532 } else {
[55]533 ch, err := uc.getChannel(ch)
[34]534 if err != nil {
535 return err
536 }
537 delete(ch.Members, msg.Prefix.Name)
538 }
[69]539
[73]540 uc.forEachDownstream(func(dc *downstreamConn) {
[69]541 dc.SendMessage(&irc.Message{
542 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
543 Command: "PART",
544 Params: []string{dc.marshalChannel(uc, ch)},
545 })
546 })
[34]547 }
[83]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 }
[19]570 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
[43]571 var name, topic string
572 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
573 return err
[19]574 }
[55]575 ch, err := uc.getChannel(name)
[19]576 if err != nil {
577 return err
578 }
579 if msg.Command == irc.RPL_TOPIC {
[43]580 ch.Topic = topic
[19]581 } else {
582 ch.Topic = ""
583 }
584 case "TOPIC":
[43]585 var name string
[74]586 if err := parseMessageParams(msg, &name); err != nil {
[43]587 return err
[19]588 }
[55]589 ch, err := uc.getChannel(name)
[19]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 }
[74]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 })
[139]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 }
[19]692 case rpl_topicwhotime:
[43]693 var name, who, timeStr string
694 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
695 return err
[19]696 }
[55]697 ch, err := uc.getChannel(name)
[19]698 if err != nil {
699 return err
700 }
[43]701 ch.TopicWho = who
702 sec, err := strconv.ParseInt(timeStr, 10, 64)
[19]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:
[43]708 var name, statusStr, members string
709 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
710 return err
[19]711 }
[140]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
[19]732 }
733
[43]734 status, err := parseChannelStatus(statusStr)
[19]735 if err != nil {
736 return err
737 }
738 ch.Status = status
739
[43]740 for _, s := range strings.Split(members, " ") {
[139]741 membership, nick := uc.parseMembershipPrefix(s)
[19]742 ch.Members[nick] = membership
743 }
744 case irc.RPL_ENDOFNAMES:
[43]745 var name string
746 if err := parseMessageParams(msg, nil, &name); err != nil {
747 return err
[25]748 }
[140]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
[25]763 }
764
[34]765 if ch.complete {
766 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
767 }
[25]768 ch.complete = true
[27]769
[73]770 uc.forEachDownstream(func(dc *downstreamConn) {
[27]771 forwardChannel(dc, ch)
[40]772 })
[127]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,
[142]819 Params: []string{dc.nick, name, "End of /WHO list"},
[127]820 })
821 })
[128]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 {
[139]891 prefix, channel := uc.parseMembershipPrefix(channel)
[128]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,
[142]913 Params: []string{dc.nick, nick, "End of /WHOIS list"},
[128]914 })
915 })
[36]916 case "PRIVMSG":
[117]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 {
[69]923 return err
924 }
[117]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
[143]935 uc.network.ring.Produce(msg)
[115]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 })
[152]950 case "TAGMSG":
951 // TODO: relay to downstream connections that accept message-tags
[16]952 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]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
[96]960 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
[14]961 // Ignore
[13]962 default:
[95]963 uc.logger.Printf("unhandled message: %v", msg)
[13]964 }
[14]965 return nil
[13]966}
967
[55]968func (uc *upstreamConn) register() {
[77]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
[60]979 uc.SendMessage(&irc.Message{
[92]980 Command: "CAP",
981 Params: []string{"LS", "302"},
982 })
983
[93]984 if uc.network.Pass != "" {
985 uc.SendMessage(&irc.Message{
986 Command: "PASS",
987 Params: []string{uc.network.Pass},
988 })
989 }
990
[92]991 uc.SendMessage(&irc.Message{
[13]992 Command: "NICK",
[69]993 Params: []string{uc.nick},
[60]994 })
995 uc.SendMessage(&irc.Message{
[13]996 Command: "USER",
[77]997 Params: []string{uc.username, "0", "*", uc.realname},
[60]998 })
[44]999}
[13]1000
[95]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 })
[152]1048 case "message-tags":
1049 uc.tagsSupported = ok
[95]1050 }
1051 return nil
1052}
1053
[103]1054func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
[13]1055 for {
[55]1056 msg, err := uc.irc.ReadMessage()
[13]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
[64]1063 if uc.srv.Debug {
1064 uc.logger.Printf("received: %v", msg)
1065 }
1066
[103]1067 ch <- upstreamIncomingMessage{msg, uc}
[13]1068 }
1069
[45]1070 return nil
[13]1071}
[60]1072
1073func (uc *upstreamConn) SendMessage(msg *irc.Message) {
[102]1074 uc.outgoing <- msg
[60]1075}
Note: See TracBrowser for help on using the repository browser.