source: code/trunk/upstream.go@ 139

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

Add MODE arguments support

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