source: code/trunk/upstream.go@ 174

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

Fix parsing wrong empty element in RPL_WHOISCHANNELS channel list

Some servers add a trailing space to the channel list in
RPL_WHOISCHANNELS. This commit works around this issue by removing any
empty trailing element after splitting.

Since RPL_WHOISCHANNELS could send an empty channel parameter, we can't
just use strings.TrimRight(s, " "), because splitting on an empty string
would still return an empty element.

Closes: https://todo.sr.ht/~emersion/soju/25

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