source: code/trunk/upstream.go@ 146

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

Add support for channel keys

File size: 24.4 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
[95]52
53 saslClient sasl.Client
54 saslStarted bool
[13]55}
56
[77]57func connectToUpstream(network *network) (*upstreamConn, error) {
58 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
[33]59
[77]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)
[33]67 if err != nil {
[77]68 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
[33]69 }
70
[67]71 setKeepAlive(netConn)
72
[102]73 outgoing := make(chan *irc.Message, 64)
[55]74 uc := &upstreamConn{
[139]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,
[33]87 }
88
89 go func() {
[102]90 for msg := range outgoing {
[64]91 if uc.srv.Debug {
92 uc.logger.Printf("sent: %v", msg)
93 }
[55]94 if err := uc.irc.WriteMessage(msg); err != nil {
95 uc.logger.Printf("failed to write message: %v", err)
[33]96 }
97 }
[55]98 if err := uc.net.Close(); err != nil {
99 uc.logger.Printf("failed to close connection: %v", err)
[45]100 } else {
[55]101 uc.logger.Printf("connection closed")
[45]102 }
[33]103 }()
104
[55]105 return uc, nil
[33]106}
107
[55]108func (uc *upstreamConn) Close() error {
109 if uc.closed {
[33]110 return fmt.Errorf("upstream connection already closed")
111 }
[102]112 close(uc.outgoing)
[55]113 uc.closed = true
[33]114 return nil
115}
116
[73]117func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
118 uc.user.forEachDownstream(func(dc *downstreamConn) {
[77]119 if dc.network != nil && dc.network != uc.network {
[73]120 return
121 }
122 f(dc)
123 })
124}
125
[55]126func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
127 ch, ok := uc.channels[name]
[19]128 if !ok {
129 return nil, fmt.Errorf("unknown channel %q", name)
130 }
131 return ch, nil
132}
133
[129]134func (uc *upstreamConn) isChannel(entity string) bool {
[139]135 if i := strings.IndexByte(uc.availableChannelTypes, entity[0]); i >= 0 {
136 return true
[129]137 }
138 return false
139}
140
[139]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
[55]150func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
[13]151 switch msg.Command {
152 case "PING":
[60]153 uc.SendMessage(&irc.Message{
[13]154 Command: "PONG",
[68]155 Params: msg.Params,
[60]156 })
[33]157 return nil
[18]158 case "NOTICE":
[55]159 uc.logger.Print(msg)
[97]160
161 uc.forEachDownstream(func(dc *downstreamConn) {
162 dc.SendMessage(msg)
163 })
[92]164 case "CAP":
[95]165 var subCmd string
166 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
167 return err
[92]168 }
[95]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] == "*"
[92]178
[95]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
[92]187 }
188
[95]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
[92]201 uc.SendMessage(&irc.Message{
202 Command: "CAP",
203 Params: []string{"END"},
204 })
[95]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)
[92]225 }
[95]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 })
[125]280 case irc.RPL_LOGGEDIN:
[95]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)
[125]286 case irc.RPL_LOGGEDOUT:
[95]287 uc.logger.Printf("logged out")
[125]288 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
[95]289 var info string
290 if err := parseMessageParams(msg, nil, &info); err != nil {
291 return err
292 }
293 switch msg.Command {
[125]294 case irc.ERR_NICKLOCKED:
[95]295 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
[125]296 case irc.ERR_SASLFAIL:
[95]297 uc.logger.Printf("SASL authentication failed: %v", info)
[125]298 case irc.ERR_SASLTOOLONG:
[95]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 })
[14]309 case irc.RPL_WELCOME:
[55]310 uc.registered = true
311 uc.logger.Printf("connection registered")
[19]312
[77]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 {
[146]320 params := []string{ch.Name}
321 if ch.Key != "" {
322 params = append(params, ch.Key)
323 }
[60]324 uc.SendMessage(&irc.Message{
[19]325 Command: "JOIN",
[146]326 Params: params,
[60]327 })
[19]328 }
[16]329 case irc.RPL_MYINFO:
[139]330 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
[43]331 return err
[16]332 }
[139]333 case irc.RPL_ISUPPORT:
334 if err := parseMessageParams(msg, nil, nil); err != nil {
335 return err
[16]336 }
[139]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 }
[42]392 case "NICK":
[83]393 if msg.Prefix == nil {
394 return fmt.Errorf("expected a prefix")
395 }
396
[43]397 var newNick string
398 if err := parseMessageParams(msg, &newNick); err != nil {
399 return err
[42]400 }
401
[55]402 if msg.Prefix.Name == uc.nick {
403 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
404 uc.nick = newNick
[42]405 }
406
[55]407 for _, ch := range uc.channels {
[42]408 if membership, ok := ch.Members[msg.Prefix.Name]; ok {
409 delete(ch.Members, msg.Prefix.Name)
410 ch.Members[newNick] = membership
411 }
412 }
[82]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 }
[69]423 case "JOIN":
424 if msg.Prefix == nil {
425 return fmt.Errorf("expected a prefix")
426 }
[42]427
[43]428 var channels string
429 if err := parseMessageParams(msg, &channels); err != nil {
430 return err
[19]431 }
[34]432
[43]433 for _, ch := range strings.Split(channels, ",") {
[55]434 if msg.Prefix.Name == uc.nick {
435 uc.logger.Printf("joined channel %q", ch)
436 uc.channels[ch] = &upstreamChannel{
[34]437 Name: ch,
[55]438 conn: uc,
[139]439 Members: make(map[string]*membership),
[34]440 }
[139]441
442 uc.SendMessage(&irc.Message{
443 Command: "MODE",
444 Params: []string{ch},
445 })
[34]446 } else {
[55]447 ch, err := uc.getChannel(ch)
[34]448 if err != nil {
449 return err
450 }
[139]451 ch.Members[msg.Prefix.Name] = nil
[19]452 }
[69]453
[73]454 uc.forEachDownstream(func(dc *downstreamConn) {
[69]455 dc.SendMessage(&irc.Message{
456 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
457 Command: "JOIN",
458 Params: []string{dc.marshalChannel(uc, ch)},
459 })
460 })
[19]461 }
[69]462 case "PART":
463 if msg.Prefix == nil {
464 return fmt.Errorf("expected a prefix")
465 }
[34]466
[43]467 var channels string
468 if err := parseMessageParams(msg, &channels); err != nil {
469 return err
[34]470 }
471
[43]472 for _, ch := range strings.Split(channels, ",") {
[55]473 if msg.Prefix.Name == uc.nick {
474 uc.logger.Printf("parted channel %q", ch)
475 delete(uc.channels, ch)
[34]476 } else {
[55]477 ch, err := uc.getChannel(ch)
[34]478 if err != nil {
479 return err
480 }
481 delete(ch.Members, msg.Prefix.Name)
482 }
[69]483
[73]484 uc.forEachDownstream(func(dc *downstreamConn) {
[69]485 dc.SendMessage(&irc.Message{
486 Prefix: dc.marshalUserPrefix(uc, msg.Prefix),
487 Command: "PART",
488 Params: []string{dc.marshalChannel(uc, ch)},
489 })
490 })
[34]491 }
[83]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 }
[19]514 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
[43]515 var name, topic string
516 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
517 return err
[19]518 }
[55]519 ch, err := uc.getChannel(name)
[19]520 if err != nil {
521 return err
522 }
523 if msg.Command == irc.RPL_TOPIC {
[43]524 ch.Topic = topic
[19]525 } else {
526 ch.Topic = ""
527 }
528 case "TOPIC":
[43]529 var name string
[74]530 if err := parseMessageParams(msg, &name); err != nil {
[43]531 return err
[19]532 }
[55]533 ch, err := uc.getChannel(name)
[19]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 }
[74]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 })
[139]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 }
[19]636 case rpl_topicwhotime:
[43]637 var name, who, timeStr string
638 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
639 return err
[19]640 }
[55]641 ch, err := uc.getChannel(name)
[19]642 if err != nil {
643 return err
644 }
[43]645 ch.TopicWho = who
646 sec, err := strconv.ParseInt(timeStr, 10, 64)
[19]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:
[43]652 var name, statusStr, members string
653 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
654 return err
[19]655 }
[140]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
[19]676 }
677
[43]678 status, err := parseChannelStatus(statusStr)
[19]679 if err != nil {
680 return err
681 }
682 ch.Status = status
683
[43]684 for _, s := range strings.Split(members, " ") {
[139]685 membership, nick := uc.parseMembershipPrefix(s)
[19]686 ch.Members[nick] = membership
687 }
688 case irc.RPL_ENDOFNAMES:
[43]689 var name string
690 if err := parseMessageParams(msg, nil, &name); err != nil {
691 return err
[25]692 }
[140]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
[25]707 }
708
[34]709 if ch.complete {
710 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
711 }
[25]712 ch.complete = true
[27]713
[73]714 uc.forEachDownstream(func(dc *downstreamConn) {
[27]715 forwardChannel(dc, ch)
[40]716 })
[127]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,
[142]763 Params: []string{dc.nick, name, "End of /WHO list"},
[127]764 })
765 })
[128]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 {
[139]835 prefix, channel := uc.parseMembershipPrefix(channel)
[128]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,
[142]857 Params: []string{dc.nick, nick, "End of /WHOIS list"},
[128]858 })
859 })
[36]860 case "PRIVMSG":
[117]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 {
[69]867 return err
868 }
[117]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
[143]879 uc.network.ring.Produce(msg)
[115]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 })
[16]894 case irc.RPL_YOURHOST, irc.RPL_CREATED:
[14]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
[96]902 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
[14]903 // Ignore
[13]904 default:
[95]905 uc.logger.Printf("unhandled message: %v", msg)
[13]906 }
[14]907 return nil
[13]908}
909
[55]910func (uc *upstreamConn) register() {
[77]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
[60]921 uc.SendMessage(&irc.Message{
[92]922 Command: "CAP",
923 Params: []string{"LS", "302"},
924 })
925
[93]926 if uc.network.Pass != "" {
927 uc.SendMessage(&irc.Message{
928 Command: "PASS",
929 Params: []string{uc.network.Pass},
930 })
931 }
932
[92]933 uc.SendMessage(&irc.Message{
[13]934 Command: "NICK",
[69]935 Params: []string{uc.nick},
[60]936 })
937 uc.SendMessage(&irc.Message{
[13]938 Command: "USER",
[77]939 Params: []string{uc.username, "0", "*", uc.realname},
[60]940 })
[44]941}
[13]942
[95]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
[103]994func (uc *upstreamConn) readMessages(ch chan<- upstreamIncomingMessage) error {
[13]995 for {
[55]996 msg, err := uc.irc.ReadMessage()
[13]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
[64]1003 if uc.srv.Debug {
1004 uc.logger.Printf("received: %v", msg)
1005 }
1006
[103]1007 ch <- upstreamIncomingMessage{msg, uc}
[13]1008 }
1009
[45]1010 return nil
[13]1011}
[60]1012
1013func (uc *upstreamConn) SendMessage(msg *irc.Message) {
[102]1014 uc.outgoing <- msg
[60]1015}
Note: See TracBrowser for help on using the repository browser.