source: code/trunk/upstream.go@ 478

Last change on this file since 478 was 478, checked in by hubert, 4 years ago

Implement casemapping

TL;DR: supports for casemapping, now logs are saved in
casemapped/canonical/tolower form
(eg. in the #channel directory instead of #Channel... or something)

What is casemapping?

see <https://modern.ircdocs.horse/#casemapping-parameter>

Casemapping and multi-upstream

Since each upstream does not necessarily use the same casemapping, and
since casemappings cannot coexist [0],

  1. soju must also update the database accordingly to upstreams' casemapping, otherwise it will end up inconsistent,
  2. soju must "normalize" entity names and expose only one casemapping that is a subset of all supported casemappings (here, ascii).

[0] On some upstreams, "emersion[m]" and "emersion{m}" refer to the same
user (upstreams that advertise rfc1459 for example), while on others
(upstreams that advertise ascii) they don't.

Once upstream's casemapping is known (default to rfc1459), entity names
in map keys are made into casemapped form, for upstreamConn,
upstreamChannel and network.

downstreamConn advertises "CASEMAPPING=ascii", and always casemap map
keys with ascii.

Some functions require the caller to casemap their argument (to avoid
needless calls to casemapping functions).

Message forwarding and casemapping

downstream message handling (joins and parts basically):
When relaying entity names from downstreams to upstreams, soju uses the
upstream casemapping, in order to not get in the way of the user. This
does not brings any issue, as long as soju replies with the ascii
casemapping in mind (solves point 1.).

marshalEntity/marshalUserPrefix:
When relaying entity names from upstreams with non-ascii casemappings,
soju *partially* casemap them: it only change the case of characters
which are not ascii letters. ASCII case is thus kept intact, while
special symbols like []{} are the same every time soju sends them to
downstreams (solves point 2.).

Casemapping changes

Casemapping changes are not fully supported by this patch and will
result in loss of history. This is a limitation of the protocol and
should be solved by the RENAME spec.

File size: 46.3 KB
Line 
1package soju
2
3import (
4 "crypto"
5 "crypto/sha256"
6 "crypto/tls"
7 "crypto/x509"
8 "encoding/base64"
9 "errors"
10 "fmt"
11 "io"
12 "net"
13 "strconv"
14 "strings"
15 "time"
16 "unicode"
17 "unicode/utf8"
18
19 "github.com/emersion/go-sasl"
20 "gopkg.in/irc.v3"
21)
22
23// permanentUpstreamCaps is the static list of upstream capabilities always
24// requested when supported.
25var permanentUpstreamCaps = map[string]bool{
26 "away-notify": true,
27 "batch": true,
28 "extended-join": true,
29 "invite-notify": true,
30 "labeled-response": true,
31 "message-tags": true,
32 "multi-prefix": true,
33 "server-time": true,
34}
35
36type registrationError string
37
38func (err registrationError) Error() string {
39 return fmt.Sprintf("registration error: %v", string(err))
40}
41
42type upstreamChannel struct {
43 Name string
44 conn *upstreamConn
45 Topic string
46 TopicWho *irc.Prefix
47 TopicTime time.Time
48 Status channelStatus
49 modes channelModes
50 creationTime string
51 Members membershipsCasemapMap
52 complete bool
53 detachTimer *time.Timer
54}
55
56func (uc *upstreamChannel) updateAutoDetach(dur time.Duration) {
57 if uc.detachTimer != nil {
58 uc.detachTimer.Stop()
59 uc.detachTimer = nil
60 }
61
62 if dur == 0 {
63 return
64 }
65
66 uc.detachTimer = time.AfterFunc(dur, func() {
67 uc.conn.network.user.events <- eventChannelDetach{
68 uc: uc.conn,
69 name: uc.Name,
70 }
71 })
72}
73
74type upstreamConn struct {
75 conn
76
77 network *network
78 user *user
79
80 serverName string
81 availableUserModes string
82 availableChannelModes map[byte]channelModeType
83 availableChannelTypes string
84 availableMemberships []membership
85 isupport map[string]*string
86
87 registered bool
88 nick string
89 nickCM string
90 username string
91 realname string
92 modes userModes
93 channels upstreamChannelCasemapMap
94 supportedCaps map[string]string
95 caps map[string]bool
96 batches map[string]batch
97 away bool
98 nextLabelID uint64
99
100 saslClient sasl.Client
101 saslStarted bool
102
103 casemapIsSet bool
104
105 // set of LIST commands in progress, per downstream
106 pendingLISTDownstreamSet map[uint64]struct{}
107}
108
109func connectToUpstream(network *network) (*upstreamConn, error) {
110 logger := &prefixLogger{network.user.srv.Logger, fmt.Sprintf("upstream %q: ", network.Addr)}
111
112 dialer := net.Dialer{Timeout: connectTimeout}
113
114 u, err := network.URL()
115 if err != nil {
116 return nil, err
117 }
118
119 var netConn net.Conn
120 switch u.Scheme {
121 case "ircs":
122 addr := u.Host
123 host, _, err := net.SplitHostPort(u.Host)
124 if err != nil {
125 host = u.Host
126 addr = u.Host + ":6697"
127 }
128
129 logger.Printf("connecting to TLS server at address %q", addr)
130
131 tlsConfig := &tls.Config{ServerName: host, NextProtos: []string{"irc"}}
132 if network.SASL.Mechanism == "EXTERNAL" {
133 if network.SASL.External.CertBlob == nil {
134 return nil, fmt.Errorf("missing certificate for authentication")
135 }
136 if network.SASL.External.PrivKeyBlob == nil {
137 return nil, fmt.Errorf("missing private key for authentication")
138 }
139 key, err := x509.ParsePKCS8PrivateKey(network.SASL.External.PrivKeyBlob)
140 if err != nil {
141 return nil, fmt.Errorf("failed to parse private key: %v", err)
142 }
143 tlsConfig.Certificates = []tls.Certificate{
144 {
145 Certificate: [][]byte{network.SASL.External.CertBlob},
146 PrivateKey: key.(crypto.PrivateKey),
147 },
148 }
149 logger.Printf("using TLS client certificate %x", sha256.Sum256(network.SASL.External.CertBlob))
150 }
151
152 netConn, err = dialer.Dial("tcp", addr)
153 if err != nil {
154 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
155 }
156
157 // Don't do the TLS handshake immediately, because we need to register
158 // the new connection with identd ASAP. See:
159 // https://todo.sr.ht/~emersion/soju/69#event-41859
160 netConn = tls.Client(netConn, tlsConfig)
161 case "irc+insecure":
162 addr := u.Host
163 if _, _, err := net.SplitHostPort(addr); err != nil {
164 addr = addr + ":6667"
165 }
166
167 logger.Printf("connecting to plain-text server at address %q", addr)
168 netConn, err = dialer.Dial("tcp", addr)
169 if err != nil {
170 return nil, fmt.Errorf("failed to dial %q: %v", addr, err)
171 }
172 case "irc+unix", "unix":
173 logger.Printf("connecting to Unix socket at path %q", u.Path)
174 netConn, err = dialer.Dial("unix", u.Path)
175 if err != nil {
176 return nil, fmt.Errorf("failed to connect to Unix socket %q: %v", u.Path, err)
177 }
178 default:
179 return nil, fmt.Errorf("failed to dial %q: unknown scheme: %v", network.Addr, u.Scheme)
180 }
181
182 options := connOptions{
183 Logger: logger,
184 RateLimitDelay: upstreamMessageDelay,
185 RateLimitBurst: upstreamMessageBurst,
186 }
187
188 uc := &upstreamConn{
189 conn: *newConn(network.user.srv, newNetIRCConn(netConn), &options),
190 network: network,
191 user: network.user,
192 channels: upstreamChannelCasemapMap{newCasemapMap(0)},
193 supportedCaps: make(map[string]string),
194 caps: make(map[string]bool),
195 batches: make(map[string]batch),
196 availableChannelTypes: stdChannelTypes,
197 availableChannelModes: stdChannelModes,
198 availableMemberships: stdMemberships,
199 isupport: make(map[string]*string),
200 pendingLISTDownstreamSet: make(map[uint64]struct{}),
201 }
202 return uc, nil
203}
204
205func (uc *upstreamConn) forEachDownstream(f func(*downstreamConn)) {
206 uc.network.forEachDownstream(f)
207}
208
209func (uc *upstreamConn) forEachDownstreamByID(id uint64, f func(*downstreamConn)) {
210 uc.forEachDownstream(func(dc *downstreamConn) {
211 if id != 0 && id != dc.id {
212 return
213 }
214 f(dc)
215 })
216}
217
218func (uc *upstreamConn) getChannel(name string) (*upstreamChannel, error) {
219 ch := uc.channels.Value(name)
220 if ch == nil {
221 return nil, fmt.Errorf("unknown channel %q", name)
222 }
223 return ch, nil
224}
225
226func (uc *upstreamConn) isChannel(entity string) bool {
227 return strings.ContainsRune(uc.availableChannelTypes, rune(entity[0]))
228}
229
230func (uc *upstreamConn) isOurNick(nick string) bool {
231 return uc.nickCM == uc.network.casemap(nick)
232}
233
234func (uc *upstreamConn) getPendingLIST() *pendingLIST {
235 for _, pl := range uc.user.pendingLISTs {
236 if _, ok := pl.pendingCommands[uc.network.ID]; !ok {
237 continue
238 }
239 return &pl
240 }
241 return nil
242}
243
244func (uc *upstreamConn) endPendingLISTs(all bool) (found bool) {
245 found = false
246 for i := 0; i < len(uc.user.pendingLISTs); i++ {
247 pl := uc.user.pendingLISTs[i]
248 if _, ok := pl.pendingCommands[uc.network.ID]; !ok {
249 continue
250 }
251 delete(pl.pendingCommands, uc.network.ID)
252 if len(pl.pendingCommands) == 0 {
253 uc.user.pendingLISTs = append(uc.user.pendingLISTs[:i], uc.user.pendingLISTs[i+1:]...)
254 i--
255 uc.forEachDownstreamByID(pl.downstreamID, func(dc *downstreamConn) {
256 dc.SendMessage(&irc.Message{
257 Prefix: dc.srv.prefix(),
258 Command: irc.RPL_LISTEND,
259 Params: []string{dc.nick, "End of /LIST"},
260 })
261 })
262 }
263 found = true
264 if !all {
265 delete(uc.pendingLISTDownstreamSet, pl.downstreamID)
266 uc.user.forEachUpstream(func(uc *upstreamConn) {
267 uc.trySendLIST(pl.downstreamID)
268 })
269 return
270 }
271 }
272 return
273}
274
275func (uc *upstreamConn) trySendLIST(downstreamID uint64) {
276 if _, ok := uc.pendingLISTDownstreamSet[downstreamID]; ok {
277 // a LIST command is already pending
278 // we will try again when that command is completed
279 return
280 }
281
282 for _, pl := range uc.user.pendingLISTs {
283 if pl.downstreamID != downstreamID {
284 continue
285 }
286 // this is the first pending LIST command list of the downstream
287 listCommand, ok := pl.pendingCommands[uc.network.ID]
288 if !ok {
289 // there is no command for this upstream in these LIST commands
290 // do not send anything
291 continue
292 }
293 // there is a command for this upstream in these LIST commands
294 // send it now
295
296 uc.SendMessageLabeled(downstreamID, listCommand)
297
298 uc.pendingLISTDownstreamSet[downstreamID] = struct{}{}
299 return
300 }
301}
302
303func (uc *upstreamConn) parseMembershipPrefix(s string) (ms *memberships, nick string) {
304 memberships := make(memberships, 0, 4)
305 i := 0
306 for _, m := range uc.availableMemberships {
307 if i >= len(s) {
308 break
309 }
310 if s[i] == m.Prefix {
311 memberships = append(memberships, m)
312 i++
313 }
314 }
315 return &memberships, s[i:]
316}
317
318func isWordBoundary(r rune) bool {
319 switch r {
320 case '-', '_', '|':
321 return false
322 case '\u00A0':
323 return true
324 default:
325 return !unicode.IsLetter(r) && !unicode.IsNumber(r)
326 }
327}
328
329func isHighlight(text, nick string) bool {
330 for {
331 i := strings.Index(text, nick)
332 if i < 0 {
333 return false
334 }
335
336 // Detect word boundaries
337 var left, right rune
338 if i > 0 {
339 left, _ = utf8.DecodeLastRuneInString(text[:i])
340 }
341 if i < len(text) {
342 right, _ = utf8.DecodeRuneInString(text[i+len(nick):])
343 }
344 if isWordBoundary(left) && isWordBoundary(right) {
345 return true
346 }
347
348 text = text[i+len(nick):]
349 }
350}
351
352func (uc *upstreamConn) handleMessage(msg *irc.Message) error {
353 var label string
354 if l, ok := msg.GetTag("label"); ok {
355 label = l
356 }
357
358 var msgBatch *batch
359 if batchName, ok := msg.GetTag("batch"); ok {
360 b, ok := uc.batches[batchName]
361 if !ok {
362 return fmt.Errorf("unexpected batch reference: batch was not defined: %q", batchName)
363 }
364 msgBatch = &b
365 if label == "" {
366 label = msgBatch.Label
367 }
368 delete(msg.Tags, "batch")
369 }
370
371 var downstreamID uint64 = 0
372 if label != "" {
373 var labelOffset uint64
374 n, err := fmt.Sscanf(label, "sd-%d-%d", &downstreamID, &labelOffset)
375 if err == nil && n < 2 {
376 err = errors.New("not enough arguments")
377 }
378 if err != nil {
379 return fmt.Errorf("unexpected message label: invalid downstream reference for label %q: %v", label, err)
380 }
381 }
382
383 if _, ok := msg.Tags["time"]; !ok {
384 msg.Tags["time"] = irc.TagValue(time.Now().UTC().Format(serverTimeLayout))
385 }
386
387 switch msg.Command {
388 case "PING":
389 uc.SendMessage(&irc.Message{
390 Command: "PONG",
391 Params: msg.Params,
392 })
393 return nil
394 case "NOTICE", "PRIVMSG", "TAGMSG":
395 if msg.Prefix == nil {
396 return fmt.Errorf("expected a prefix")
397 }
398
399 var entity, text string
400 if msg.Command != "TAGMSG" {
401 if err := parseMessageParams(msg, &entity, &text); err != nil {
402 return err
403 }
404 } else {
405 if err := parseMessageParams(msg, &entity); err != nil {
406 return err
407 }
408 }
409
410 if msg.Prefix.Name == serviceNick {
411 uc.logger.Printf("skipping %v from soju's service: %v", msg.Command, msg)
412 break
413 }
414 if entity == serviceNick {
415 uc.logger.Printf("skipping %v to soju's service: %v", msg.Command, msg)
416 break
417 }
418
419 if msg.Prefix.User == "" && msg.Prefix.Host == "" { // server message
420 uc.produce("", msg, nil)
421 } else { // regular user message
422 target := entity
423 if uc.isOurNick(target) {
424 target = msg.Prefix.Name
425 }
426
427 ch := uc.network.channels.Value(target)
428 if ch != nil {
429 if ch.Detached {
430 uc.handleDetachedMessage(msg.Prefix.Name, text, ch)
431 }
432
433 highlight := msg.Prefix.Name != uc.nick && isHighlight(text, uc.nick)
434 if ch.DetachOn == FilterMessage || ch.DetachOn == FilterDefault || (ch.DetachOn == FilterHighlight && highlight) {
435 uc.updateChannelAutoDetach(target)
436 }
437 }
438
439 uc.produce(target, msg, nil)
440 }
441 case "CAP":
442 var subCmd string
443 if err := parseMessageParams(msg, nil, &subCmd); err != nil {
444 return err
445 }
446 subCmd = strings.ToUpper(subCmd)
447 subParams := msg.Params[2:]
448 switch subCmd {
449 case "LS":
450 if len(subParams) < 1 {
451 return newNeedMoreParamsError(msg.Command)
452 }
453 caps := subParams[len(subParams)-1]
454 more := len(subParams) >= 2 && msg.Params[len(subParams)-2] == "*"
455
456 uc.handleSupportedCaps(caps)
457
458 if more {
459 break // wait to receive all capabilities
460 }
461
462 uc.requestCaps()
463
464 if uc.requestSASL() {
465 break // we'll send CAP END after authentication is completed
466 }
467
468 uc.SendMessage(&irc.Message{
469 Command: "CAP",
470 Params: []string{"END"},
471 })
472 case "ACK", "NAK":
473 if len(subParams) < 1 {
474 return newNeedMoreParamsError(msg.Command)
475 }
476 caps := strings.Fields(subParams[0])
477
478 for _, name := range caps {
479 if err := uc.handleCapAck(strings.ToLower(name), subCmd == "ACK"); err != nil {
480 return err
481 }
482 }
483
484 if uc.registered {
485 uc.forEachDownstream(func(dc *downstreamConn) {
486 dc.updateSupportedCaps()
487 })
488 }
489 case "NEW":
490 if len(subParams) < 1 {
491 return newNeedMoreParamsError(msg.Command)
492 }
493 uc.handleSupportedCaps(subParams[0])
494 uc.requestCaps()
495 case "DEL":
496 if len(subParams) < 1 {
497 return newNeedMoreParamsError(msg.Command)
498 }
499 caps := strings.Fields(subParams[0])
500
501 for _, c := range caps {
502 delete(uc.supportedCaps, c)
503 delete(uc.caps, c)
504 }
505
506 if uc.registered {
507 uc.forEachDownstream(func(dc *downstreamConn) {
508 dc.updateSupportedCaps()
509 })
510 }
511 default:
512 uc.logger.Printf("unhandled message: %v", msg)
513 }
514 case "AUTHENTICATE":
515 if uc.saslClient == nil {
516 return fmt.Errorf("received unexpected AUTHENTICATE message")
517 }
518
519 // TODO: if a challenge is 400 bytes long, buffer it
520 var challengeStr string
521 if err := parseMessageParams(msg, &challengeStr); err != nil {
522 uc.SendMessage(&irc.Message{
523 Command: "AUTHENTICATE",
524 Params: []string{"*"},
525 })
526 return err
527 }
528
529 var challenge []byte
530 if challengeStr != "+" {
531 var err error
532 challenge, err = base64.StdEncoding.DecodeString(challengeStr)
533 if err != nil {
534 uc.SendMessage(&irc.Message{
535 Command: "AUTHENTICATE",
536 Params: []string{"*"},
537 })
538 return err
539 }
540 }
541
542 var resp []byte
543 var err error
544 if !uc.saslStarted {
545 _, resp, err = uc.saslClient.Start()
546 uc.saslStarted = true
547 } else {
548 resp, err = uc.saslClient.Next(challenge)
549 }
550 if err != nil {
551 uc.SendMessage(&irc.Message{
552 Command: "AUTHENTICATE",
553 Params: []string{"*"},
554 })
555 return err
556 }
557
558 // TODO: send response in multiple chunks if >= 400 bytes
559 var respStr = "+"
560 if len(resp) != 0 {
561 respStr = base64.StdEncoding.EncodeToString(resp)
562 }
563
564 uc.SendMessage(&irc.Message{
565 Command: "AUTHENTICATE",
566 Params: []string{respStr},
567 })
568 case irc.RPL_LOGGEDIN:
569 var account string
570 if err := parseMessageParams(msg, nil, nil, &account); err != nil {
571 return err
572 }
573 uc.logger.Printf("logged in with account %q", account)
574 case irc.RPL_LOGGEDOUT:
575 uc.logger.Printf("logged out")
576 case irc.ERR_NICKLOCKED, irc.RPL_SASLSUCCESS, irc.ERR_SASLFAIL, irc.ERR_SASLTOOLONG, irc.ERR_SASLABORTED:
577 var info string
578 if err := parseMessageParams(msg, nil, &info); err != nil {
579 return err
580 }
581 switch msg.Command {
582 case irc.ERR_NICKLOCKED:
583 uc.logger.Printf("invalid nick used with SASL authentication: %v", info)
584 case irc.ERR_SASLFAIL:
585 uc.logger.Printf("SASL authentication failed: %v", info)
586 case irc.ERR_SASLTOOLONG:
587 uc.logger.Printf("SASL message too long: %v", info)
588 }
589
590 uc.saslClient = nil
591 uc.saslStarted = false
592
593 uc.SendMessage(&irc.Message{
594 Command: "CAP",
595 Params: []string{"END"},
596 })
597 case irc.RPL_WELCOME:
598 uc.registered = true
599 uc.logger.Printf("connection registered")
600
601 if uc.network.channels.Len() > 0 {
602 var channels, keys []string
603 for _, entry := range uc.network.channels.innerMap {
604 ch := entry.value.(*Channel)
605 channels = append(channels, ch.Name)
606 keys = append(keys, ch.Key)
607 }
608
609 for _, msg := range join(channels, keys) {
610 uc.SendMessage(msg)
611 }
612 }
613 case irc.RPL_MYINFO:
614 if err := parseMessageParams(msg, nil, &uc.serverName, nil, &uc.availableUserModes, nil); err != nil {
615 return err
616 }
617 case irc.RPL_ISUPPORT:
618 if err := parseMessageParams(msg, nil, nil); err != nil {
619 return err
620 }
621
622 var downstreamIsupport []string
623 for _, token := range msg.Params[1 : len(msg.Params)-1] {
624 parameter := token
625 var negate, hasValue bool
626 var value string
627 if strings.HasPrefix(token, "-") {
628 negate = true
629 token = token[1:]
630 } else if i := strings.IndexByte(token, '='); i >= 0 {
631 parameter = token[:i]
632 value = token[i+1:]
633 hasValue = true
634 }
635
636 if hasValue {
637 uc.isupport[parameter] = &value
638 } else if !negate {
639 uc.isupport[parameter] = nil
640 } else {
641 delete(uc.isupport, parameter)
642 }
643
644 var err error
645 switch parameter {
646 case "CASEMAPPING":
647 casemap, ok := parseCasemappingToken(value)
648 if !ok {
649 casemap = casemapRFC1459
650 }
651 uc.network.updateCasemapping(casemap)
652 uc.nickCM = uc.network.casemap(uc.nick)
653 uc.casemapIsSet = true
654 case "CHANMODES":
655 if !negate {
656 err = uc.handleChanModes(value)
657 } else {
658 uc.availableChannelModes = stdChannelModes
659 }
660 case "CHANTYPES":
661 if !negate {
662 uc.availableChannelTypes = value
663 } else {
664 uc.availableChannelTypes = stdChannelTypes
665 }
666 case "PREFIX":
667 if !negate {
668 err = uc.handleMemberships(value)
669 } else {
670 uc.availableMemberships = stdMemberships
671 }
672 }
673 if err != nil {
674 return err
675 }
676
677 if passthroughIsupport[parameter] {
678 downstreamIsupport = append(downstreamIsupport, token)
679 }
680 }
681
682 uc.forEachDownstream(func(dc *downstreamConn) {
683 if dc.network == nil {
684 return
685 }
686 msgs := generateIsupport(dc.srv.prefix(), dc.nick, downstreamIsupport)
687 for _, msg := range msgs {
688 dc.SendMessage(msg)
689 }
690 })
691 case irc.ERR_NOMOTD, irc.RPL_ENDOFMOTD:
692 if !uc.casemapIsSet {
693 // upstream did not send any CASEMAPPING token, thus
694 // we assume it implements the old RFCs with rfc1459.
695 uc.casemapIsSet = true
696 uc.network.updateCasemapping(casemapRFC1459)
697 uc.nickCM = uc.network.casemap(uc.nick)
698 }
699 case "BATCH":
700 var tag string
701 if err := parseMessageParams(msg, &tag); err != nil {
702 return err
703 }
704
705 if strings.HasPrefix(tag, "+") {
706 tag = tag[1:]
707 if _, ok := uc.batches[tag]; ok {
708 return fmt.Errorf("unexpected BATCH reference tag: batch was already defined: %q", tag)
709 }
710 var batchType string
711 if err := parseMessageParams(msg, nil, &batchType); err != nil {
712 return err
713 }
714 label := label
715 if label == "" && msgBatch != nil {
716 label = msgBatch.Label
717 }
718 uc.batches[tag] = batch{
719 Type: batchType,
720 Params: msg.Params[2:],
721 Outer: msgBatch,
722 Label: label,
723 }
724 } else if strings.HasPrefix(tag, "-") {
725 tag = tag[1:]
726 if _, ok := uc.batches[tag]; !ok {
727 return fmt.Errorf("unknown BATCH reference tag: %q", tag)
728 }
729 delete(uc.batches, tag)
730 } else {
731 return fmt.Errorf("unexpected BATCH reference tag: missing +/- prefix: %q", tag)
732 }
733 case "NICK":
734 if msg.Prefix == nil {
735 return fmt.Errorf("expected a prefix")
736 }
737
738 var newNick string
739 if err := parseMessageParams(msg, &newNick); err != nil {
740 return err
741 }
742
743 me := false
744 if uc.isOurNick(msg.Prefix.Name) {
745 uc.logger.Printf("changed nick from %q to %q", uc.nick, newNick)
746 me = true
747 uc.nick = newNick
748 uc.nickCM = uc.network.casemap(uc.nick)
749 }
750
751 for _, entry := range uc.channels.innerMap {
752 ch := entry.value.(*upstreamChannel)
753 memberships := ch.Members.Value(msg.Prefix.Name)
754 if memberships != nil {
755 ch.Members.Delete(msg.Prefix.Name)
756 ch.Members.SetValue(newNick, memberships)
757 uc.appendLog(ch.Name, msg)
758 }
759 }
760
761 if !me {
762 uc.forEachDownstream(func(dc *downstreamConn) {
763 dc.SendMessage(dc.marshalMessage(msg, uc.network))
764 })
765 } else {
766 uc.forEachDownstream(func(dc *downstreamConn) {
767 dc.updateNick()
768 })
769 }
770 case "JOIN":
771 if msg.Prefix == nil {
772 return fmt.Errorf("expected a prefix")
773 }
774
775 var channels string
776 if err := parseMessageParams(msg, &channels); err != nil {
777 return err
778 }
779
780 for _, ch := range strings.Split(channels, ",") {
781 if uc.isOurNick(msg.Prefix.Name) {
782 uc.logger.Printf("joined channel %q", ch)
783 members := membershipsCasemapMap{newCasemapMap(0)}
784 members.casemap = uc.network.casemap
785 uc.channels.SetValue(ch, &upstreamChannel{
786 Name: ch,
787 conn: uc,
788 Members: members,
789 })
790 uc.updateChannelAutoDetach(ch)
791
792 uc.SendMessage(&irc.Message{
793 Command: "MODE",
794 Params: []string{ch},
795 })
796 } else {
797 ch, err := uc.getChannel(ch)
798 if err != nil {
799 return err
800 }
801 ch.Members.SetValue(msg.Prefix.Name, &memberships{})
802 }
803
804 chMsg := msg.Copy()
805 chMsg.Params[0] = ch
806 uc.produce(ch, chMsg, nil)
807 }
808 case "PART":
809 if msg.Prefix == nil {
810 return fmt.Errorf("expected a prefix")
811 }
812
813 var channels string
814 if err := parseMessageParams(msg, &channels); err != nil {
815 return err
816 }
817
818 for _, ch := range strings.Split(channels, ",") {
819 if uc.isOurNick(msg.Prefix.Name) {
820 uc.logger.Printf("parted channel %q", ch)
821 uch := uc.channels.Value(ch)
822 if uch != nil {
823 uc.channels.Delete(ch)
824 uch.updateAutoDetach(0)
825 }
826 } else {
827 ch, err := uc.getChannel(ch)
828 if err != nil {
829 return err
830 }
831 ch.Members.Delete(msg.Prefix.Name)
832 }
833
834 chMsg := msg.Copy()
835 chMsg.Params[0] = ch
836 uc.produce(ch, chMsg, nil)
837 }
838 case "KICK":
839 if msg.Prefix == nil {
840 return fmt.Errorf("expected a prefix")
841 }
842
843 var channel, user string
844 if err := parseMessageParams(msg, &channel, &user); err != nil {
845 return err
846 }
847
848 if uc.isOurNick(user) {
849 uc.logger.Printf("kicked from channel %q by %s", channel, msg.Prefix.Name)
850 uc.channels.Delete(channel)
851 } else {
852 ch, err := uc.getChannel(channel)
853 if err != nil {
854 return err
855 }
856 ch.Members.Delete(user)
857 }
858
859 uc.produce(channel, msg, nil)
860 case "QUIT":
861 if msg.Prefix == nil {
862 return fmt.Errorf("expected a prefix")
863 }
864
865 if uc.isOurNick(msg.Prefix.Name) {
866 uc.logger.Printf("quit")
867 }
868
869 for _, entry := range uc.channels.innerMap {
870 ch := entry.value.(*upstreamChannel)
871 if ch.Members.Has(msg.Prefix.Name) {
872 ch.Members.Delete(msg.Prefix.Name)
873
874 uc.appendLog(ch.Name, msg)
875 }
876 }
877
878 if msg.Prefix.Name != uc.nick {
879 uc.forEachDownstream(func(dc *downstreamConn) {
880 dc.SendMessage(dc.marshalMessage(msg, uc.network))
881 })
882 }
883 case irc.RPL_TOPIC, irc.RPL_NOTOPIC:
884 var name, topic string
885 if err := parseMessageParams(msg, nil, &name, &topic); err != nil {
886 return err
887 }
888 ch, err := uc.getChannel(name)
889 if err != nil {
890 return err
891 }
892 if msg.Command == irc.RPL_TOPIC {
893 ch.Topic = topic
894 } else {
895 ch.Topic = ""
896 }
897 case "TOPIC":
898 if msg.Prefix == nil {
899 return fmt.Errorf("expected a prefix")
900 }
901
902 var name string
903 if err := parseMessageParams(msg, &name); err != nil {
904 return err
905 }
906 ch, err := uc.getChannel(name)
907 if err != nil {
908 return err
909 }
910 if len(msg.Params) > 1 {
911 ch.Topic = msg.Params[1]
912 ch.TopicWho = msg.Prefix.Copy()
913 ch.TopicTime = time.Now() // TODO use msg.Tags["time"]
914 } else {
915 ch.Topic = ""
916 }
917 uc.produce(ch.Name, msg, nil)
918 case "MODE":
919 var name, modeStr string
920 if err := parseMessageParams(msg, &name, &modeStr); err != nil {
921 return err
922 }
923
924 if !uc.isChannel(name) { // user mode change
925 if name != uc.nick {
926 return fmt.Errorf("received MODE message for unknown nick %q", name)
927 }
928 return uc.modes.Apply(modeStr)
929 // TODO: notify downstreams about user mode change?
930 } else { // channel mode change
931 ch, err := uc.getChannel(name)
932 if err != nil {
933 return err
934 }
935
936 needMarshaling, err := applyChannelModes(ch, modeStr, msg.Params[2:])
937 if err != nil {
938 return err
939 }
940
941 uc.appendLog(ch.Name, msg)
942
943 c := uc.network.channels.Value(name)
944 if c == nil || !c.Detached {
945 uc.forEachDownstream(func(dc *downstreamConn) {
946 params := make([]string, len(msg.Params))
947 params[0] = dc.marshalEntity(uc.network, name)
948 params[1] = modeStr
949
950 copy(params[2:], msg.Params[2:])
951 for i, modeParam := range params[2:] {
952 if _, ok := needMarshaling[i]; ok {
953 params[2+i] = dc.marshalEntity(uc.network, modeParam)
954 }
955 }
956
957 dc.SendMessage(&irc.Message{
958 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
959 Command: "MODE",
960 Params: params,
961 })
962 })
963 }
964 }
965 case irc.RPL_UMODEIS:
966 if err := parseMessageParams(msg, nil); err != nil {
967 return err
968 }
969 modeStr := ""
970 if len(msg.Params) > 1 {
971 modeStr = msg.Params[1]
972 }
973
974 uc.modes = ""
975 if err := uc.modes.Apply(modeStr); err != nil {
976 return err
977 }
978 // TODO: send RPL_UMODEIS to downstream connections when applicable
979 case irc.RPL_CHANNELMODEIS:
980 var channel string
981 if err := parseMessageParams(msg, nil, &channel); err != nil {
982 return err
983 }
984 modeStr := ""
985 if len(msg.Params) > 2 {
986 modeStr = msg.Params[2]
987 }
988
989 ch, err := uc.getChannel(channel)
990 if err != nil {
991 return err
992 }
993
994 firstMode := ch.modes == nil
995 ch.modes = make(map[byte]string)
996 if _, err := applyChannelModes(ch, modeStr, msg.Params[3:]); err != nil {
997 return err
998 }
999 if firstMode {
1000 c := uc.network.channels.Value(channel)
1001 if c == nil || !c.Detached {
1002 modeStr, modeParams := ch.modes.Format()
1003
1004 uc.forEachDownstream(func(dc *downstreamConn) {
1005 params := []string{dc.nick, dc.marshalEntity(uc.network, channel), modeStr}
1006 params = append(params, modeParams...)
1007
1008 dc.SendMessage(&irc.Message{
1009 Prefix: dc.srv.prefix(),
1010 Command: irc.RPL_CHANNELMODEIS,
1011 Params: params,
1012 })
1013 })
1014 }
1015 }
1016 case rpl_creationtime:
1017 var channel, creationTime string
1018 if err := parseMessageParams(msg, nil, &channel, &creationTime); err != nil {
1019 return err
1020 }
1021
1022 ch, err := uc.getChannel(channel)
1023 if err != nil {
1024 return err
1025 }
1026
1027 firstCreationTime := ch.creationTime == ""
1028 ch.creationTime = creationTime
1029 if firstCreationTime {
1030 uc.forEachDownstream(func(dc *downstreamConn) {
1031 dc.SendMessage(&irc.Message{
1032 Prefix: dc.srv.prefix(),
1033 Command: rpl_creationtime,
1034 Params: []string{dc.nick, dc.marshalEntity(uc.network, ch.Name), creationTime},
1035 })
1036 })
1037 }
1038 case rpl_topicwhotime:
1039 var name, who, timeStr string
1040 if err := parseMessageParams(msg, nil, &name, &who, &timeStr); err != nil {
1041 return err
1042 }
1043 ch, err := uc.getChannel(name)
1044 if err != nil {
1045 return err
1046 }
1047 firstTopicWhoTime := ch.TopicWho == nil
1048 ch.TopicWho = irc.ParsePrefix(who)
1049 sec, err := strconv.ParseInt(timeStr, 10, 64)
1050 if err != nil {
1051 return fmt.Errorf("failed to parse topic time: %v", err)
1052 }
1053 ch.TopicTime = time.Unix(sec, 0)
1054 if firstTopicWhoTime {
1055 uc.forEachDownstream(func(dc *downstreamConn) {
1056 topicWho := dc.marshalUserPrefix(uc.network, ch.TopicWho)
1057 dc.SendMessage(&irc.Message{
1058 Prefix: dc.srv.prefix(),
1059 Command: rpl_topicwhotime,
1060 Params: []string{
1061 dc.nick,
1062 dc.marshalEntity(uc.network, ch.Name),
1063 topicWho.String(),
1064 timeStr,
1065 },
1066 })
1067 })
1068 }
1069 case irc.RPL_LIST:
1070 var channel, clients, topic string
1071 if err := parseMessageParams(msg, nil, &channel, &clients, &topic); err != nil {
1072 return err
1073 }
1074
1075 pl := uc.getPendingLIST()
1076 if pl == nil {
1077 return fmt.Errorf("unexpected RPL_LIST: no matching pending LIST")
1078 }
1079
1080 uc.forEachDownstreamByID(pl.downstreamID, func(dc *downstreamConn) {
1081 dc.SendMessage(&irc.Message{
1082 Prefix: dc.srv.prefix(),
1083 Command: irc.RPL_LIST,
1084 Params: []string{dc.nick, dc.marshalEntity(uc.network, channel), clients, topic},
1085 })
1086 })
1087 case irc.RPL_LISTEND:
1088 ok := uc.endPendingLISTs(false)
1089 if !ok {
1090 return fmt.Errorf("unexpected RPL_LISTEND: no matching pending LIST")
1091 }
1092 case irc.RPL_NAMREPLY:
1093 var name, statusStr, members string
1094 if err := parseMessageParams(msg, nil, &statusStr, &name, &members); err != nil {
1095 return err
1096 }
1097
1098 ch := uc.channels.Value(name)
1099 if ch == nil {
1100 // NAMES on a channel we have not joined, forward to downstream
1101 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1102 channel := dc.marshalEntity(uc.network, name)
1103 members := splitSpace(members)
1104 for i, member := range members {
1105 memberships, nick := uc.parseMembershipPrefix(member)
1106 members[i] = memberships.Format(dc) + dc.marshalEntity(uc.network, nick)
1107 }
1108 memberStr := strings.Join(members, " ")
1109
1110 dc.SendMessage(&irc.Message{
1111 Prefix: dc.srv.prefix(),
1112 Command: irc.RPL_NAMREPLY,
1113 Params: []string{dc.nick, statusStr, channel, memberStr},
1114 })
1115 })
1116 return nil
1117 }
1118
1119 status, err := parseChannelStatus(statusStr)
1120 if err != nil {
1121 return err
1122 }
1123 ch.Status = status
1124
1125 for _, s := range splitSpace(members) {
1126 memberships, nick := uc.parseMembershipPrefix(s)
1127 ch.Members.SetValue(nick, memberships)
1128 }
1129 case irc.RPL_ENDOFNAMES:
1130 var name string
1131 if err := parseMessageParams(msg, nil, &name); err != nil {
1132 return err
1133 }
1134
1135 ch := uc.channels.Value(name)
1136 if ch == nil {
1137 // NAMES on a channel we have not joined, forward to downstream
1138 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1139 channel := dc.marshalEntity(uc.network, name)
1140
1141 dc.SendMessage(&irc.Message{
1142 Prefix: dc.srv.prefix(),
1143 Command: irc.RPL_ENDOFNAMES,
1144 Params: []string{dc.nick, channel, "End of /NAMES list"},
1145 })
1146 })
1147 return nil
1148 }
1149
1150 if ch.complete {
1151 return fmt.Errorf("received unexpected RPL_ENDOFNAMES")
1152 }
1153 ch.complete = true
1154
1155 c := uc.network.channels.Value(name)
1156 if c == nil || !c.Detached {
1157 uc.forEachDownstream(func(dc *downstreamConn) {
1158 forwardChannel(dc, ch)
1159 })
1160 }
1161 case irc.RPL_WHOREPLY:
1162 var channel, username, host, server, nick, mode, trailing string
1163 if err := parseMessageParams(msg, nil, &channel, &username, &host, &server, &nick, &mode, &trailing); err != nil {
1164 return err
1165 }
1166
1167 parts := strings.SplitN(trailing, " ", 2)
1168 if len(parts) != 2 {
1169 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong trailing parameter: %s", trailing)
1170 }
1171 realname := parts[1]
1172 hops, err := strconv.Atoi(parts[0])
1173 if err != nil {
1174 return fmt.Errorf("received malformed RPL_WHOREPLY: wrong hop count: %s", parts[0])
1175 }
1176 hops++
1177
1178 trailing = strconv.Itoa(hops) + " " + realname
1179
1180 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1181 channel := channel
1182 if channel != "*" {
1183 channel = dc.marshalEntity(uc.network, channel)
1184 }
1185 nick := dc.marshalEntity(uc.network, nick)
1186 dc.SendMessage(&irc.Message{
1187 Prefix: dc.srv.prefix(),
1188 Command: irc.RPL_WHOREPLY,
1189 Params: []string{dc.nick, channel, username, host, server, nick, mode, trailing},
1190 })
1191 })
1192 case irc.RPL_ENDOFWHO:
1193 var name string
1194 if err := parseMessageParams(msg, nil, &name); err != nil {
1195 return err
1196 }
1197
1198 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1199 name := name
1200 if name != "*" {
1201 // TODO: support WHO masks
1202 name = dc.marshalEntity(uc.network, name)
1203 }
1204 dc.SendMessage(&irc.Message{
1205 Prefix: dc.srv.prefix(),
1206 Command: irc.RPL_ENDOFWHO,
1207 Params: []string{dc.nick, name, "End of /WHO list"},
1208 })
1209 })
1210 case irc.RPL_WHOISUSER:
1211 var nick, username, host, realname string
1212 if err := parseMessageParams(msg, nil, &nick, &username, &host, nil, &realname); err != nil {
1213 return err
1214 }
1215
1216 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1217 nick := dc.marshalEntity(uc.network, nick)
1218 dc.SendMessage(&irc.Message{
1219 Prefix: dc.srv.prefix(),
1220 Command: irc.RPL_WHOISUSER,
1221 Params: []string{dc.nick, nick, username, host, "*", realname},
1222 })
1223 })
1224 case irc.RPL_WHOISSERVER:
1225 var nick, server, serverInfo string
1226 if err := parseMessageParams(msg, nil, &nick, &server, &serverInfo); err != nil {
1227 return err
1228 }
1229
1230 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1231 nick := dc.marshalEntity(uc.network, nick)
1232 dc.SendMessage(&irc.Message{
1233 Prefix: dc.srv.prefix(),
1234 Command: irc.RPL_WHOISSERVER,
1235 Params: []string{dc.nick, nick, server, serverInfo},
1236 })
1237 })
1238 case irc.RPL_WHOISOPERATOR:
1239 var nick string
1240 if err := parseMessageParams(msg, nil, &nick); err != nil {
1241 return err
1242 }
1243
1244 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1245 nick := dc.marshalEntity(uc.network, nick)
1246 dc.SendMessage(&irc.Message{
1247 Prefix: dc.srv.prefix(),
1248 Command: irc.RPL_WHOISOPERATOR,
1249 Params: []string{dc.nick, nick, "is an IRC operator"},
1250 })
1251 })
1252 case irc.RPL_WHOISIDLE:
1253 var nick string
1254 if err := parseMessageParams(msg, nil, &nick, nil); err != nil {
1255 return err
1256 }
1257
1258 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1259 nick := dc.marshalEntity(uc.network, nick)
1260 params := []string{dc.nick, nick}
1261 params = append(params, msg.Params[2:]...)
1262 dc.SendMessage(&irc.Message{
1263 Prefix: dc.srv.prefix(),
1264 Command: irc.RPL_WHOISIDLE,
1265 Params: params,
1266 })
1267 })
1268 case irc.RPL_WHOISCHANNELS:
1269 var nick, channelList string
1270 if err := parseMessageParams(msg, nil, &nick, &channelList); err != nil {
1271 return err
1272 }
1273 channels := splitSpace(channelList)
1274
1275 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1276 nick := dc.marshalEntity(uc.network, nick)
1277 channelList := make([]string, len(channels))
1278 for i, channel := range channels {
1279 prefix, channel := uc.parseMembershipPrefix(channel)
1280 channel = dc.marshalEntity(uc.network, channel)
1281 channelList[i] = prefix.Format(dc) + channel
1282 }
1283 channels := strings.Join(channelList, " ")
1284 dc.SendMessage(&irc.Message{
1285 Prefix: dc.srv.prefix(),
1286 Command: irc.RPL_WHOISCHANNELS,
1287 Params: []string{dc.nick, nick, channels},
1288 })
1289 })
1290 case irc.RPL_ENDOFWHOIS:
1291 var nick string
1292 if err := parseMessageParams(msg, nil, &nick); err != nil {
1293 return err
1294 }
1295
1296 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1297 nick := dc.marshalEntity(uc.network, nick)
1298 dc.SendMessage(&irc.Message{
1299 Prefix: dc.srv.prefix(),
1300 Command: irc.RPL_ENDOFWHOIS,
1301 Params: []string{dc.nick, nick, "End of /WHOIS list"},
1302 })
1303 })
1304 case "INVITE":
1305 var nick, channel string
1306 if err := parseMessageParams(msg, &nick, &channel); err != nil {
1307 return err
1308 }
1309
1310 weAreInvited := uc.isOurNick(nick)
1311
1312 uc.forEachDownstream(func(dc *downstreamConn) {
1313 if !weAreInvited && !dc.caps["invite-notify"] {
1314 return
1315 }
1316 dc.SendMessage(&irc.Message{
1317 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1318 Command: "INVITE",
1319 Params: []string{dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1320 })
1321 })
1322 case irc.RPL_INVITING:
1323 var nick, channel string
1324 if err := parseMessageParams(msg, nil, &nick, &channel); err != nil {
1325 return err
1326 }
1327
1328 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1329 dc.SendMessage(&irc.Message{
1330 Prefix: dc.srv.prefix(),
1331 Command: irc.RPL_INVITING,
1332 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), dc.marshalEntity(uc.network, channel)},
1333 })
1334 })
1335 case irc.RPL_AWAY:
1336 var nick, reason string
1337 if err := parseMessageParams(msg, nil, &nick, &reason); err != nil {
1338 return err
1339 }
1340
1341 uc.forEachDownstream(func(dc *downstreamConn) {
1342 dc.SendMessage(&irc.Message{
1343 Prefix: dc.srv.prefix(),
1344 Command: irc.RPL_AWAY,
1345 Params: []string{dc.nick, dc.marshalEntity(uc.network, nick), reason},
1346 })
1347 })
1348 case "AWAY":
1349 if msg.Prefix == nil {
1350 return fmt.Errorf("expected a prefix")
1351 }
1352
1353 uc.forEachDownstream(func(dc *downstreamConn) {
1354 if !dc.caps["away-notify"] {
1355 return
1356 }
1357 dc.SendMessage(&irc.Message{
1358 Prefix: dc.marshalUserPrefix(uc.network, msg.Prefix),
1359 Command: "AWAY",
1360 Params: msg.Params,
1361 })
1362 })
1363 case irc.RPL_BANLIST, irc.RPL_INVITELIST, irc.RPL_EXCEPTLIST:
1364 var channel, mask string
1365 if err := parseMessageParams(msg, nil, &channel, &mask); err != nil {
1366 return err
1367 }
1368 var addNick, addTime string
1369 if len(msg.Params) >= 5 {
1370 addNick = msg.Params[3]
1371 addTime = msg.Params[4]
1372 }
1373
1374 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1375 channel := dc.marshalEntity(uc.network, channel)
1376
1377 var params []string
1378 if addNick != "" && addTime != "" {
1379 addNick := dc.marshalEntity(uc.network, addNick)
1380 params = []string{dc.nick, channel, mask, addNick, addTime}
1381 } else {
1382 params = []string{dc.nick, channel, mask}
1383 }
1384
1385 dc.SendMessage(&irc.Message{
1386 Prefix: dc.srv.prefix(),
1387 Command: msg.Command,
1388 Params: params,
1389 })
1390 })
1391 case irc.RPL_ENDOFBANLIST, irc.RPL_ENDOFINVITELIST, irc.RPL_ENDOFEXCEPTLIST:
1392 var channel, trailing string
1393 if err := parseMessageParams(msg, nil, &channel, &trailing); err != nil {
1394 return err
1395 }
1396
1397 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1398 upstreamChannel := dc.marshalEntity(uc.network, channel)
1399 dc.SendMessage(&irc.Message{
1400 Prefix: dc.srv.prefix(),
1401 Command: msg.Command,
1402 Params: []string{dc.nick, upstreamChannel, trailing},
1403 })
1404 })
1405 case irc.ERR_UNKNOWNCOMMAND, irc.RPL_TRYAGAIN:
1406 var command, reason string
1407 if err := parseMessageParams(msg, nil, &command, &reason); err != nil {
1408 return err
1409 }
1410
1411 if command == "LIST" {
1412 ok := uc.endPendingLISTs(false)
1413 if !ok {
1414 return fmt.Errorf("unexpected response for LIST: %q: no matching pending LIST", msg.Command)
1415 }
1416 }
1417
1418 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1419 dc.SendMessage(&irc.Message{
1420 Prefix: uc.srv.prefix(),
1421 Command: msg.Command,
1422 Params: []string{dc.nick, command, reason},
1423 })
1424 })
1425 case "ACK":
1426 // Ignore
1427 case irc.RPL_NOWAWAY, irc.RPL_UNAWAY:
1428 // Ignore
1429 case irc.RPL_YOURHOST, irc.RPL_CREATED:
1430 // Ignore
1431 case irc.RPL_LUSERCLIENT, irc.RPL_LUSEROP, irc.RPL_LUSERUNKNOWN, irc.RPL_LUSERCHANNELS, irc.RPL_LUSERME:
1432 // Ignore
1433 case irc.RPL_MOTDSTART, irc.RPL_MOTD:
1434 // Ignore
1435 case irc.RPL_LISTSTART:
1436 // Ignore
1437 case rpl_localusers, rpl_globalusers:
1438 // Ignore
1439 case irc.RPL_STATSVLINE, rpl_statsping, irc.RPL_STATSBLINE, irc.RPL_STATSDLINE:
1440 // Ignore
1441 case "ERROR":
1442 var text string
1443 if err := parseMessageParams(msg, &text); err != nil {
1444 return err
1445 }
1446 return fmt.Errorf("fatal server error: %v", text)
1447 case irc.ERR_PASSWDMISMATCH, irc.ERR_ERRONEUSNICKNAME, irc.ERR_NICKNAMEINUSE, irc.ERR_NICKCOLLISION, irc.ERR_UNAVAILRESOURCE, irc.ERR_NOPERMFORHOST, irc.ERR_YOUREBANNEDCREEP:
1448 if !uc.registered {
1449 text := msg.Params[len(msg.Params)-1]
1450 return registrationError(text)
1451 }
1452 fallthrough
1453 default:
1454 uc.logger.Printf("unhandled message: %v", msg)
1455
1456 uc.forEachDownstreamByID(downstreamID, func(dc *downstreamConn) {
1457 // best effort marshaling for unknown messages, replies and errors:
1458 // most numerics start with the user nick, marshal it if that's the case
1459 // otherwise, conservately keep the params without marshaling
1460 params := msg.Params
1461 if _, err := strconv.Atoi(msg.Command); err == nil { // numeric
1462 if len(msg.Params) > 0 && isOurNick(uc.network, msg.Params[0]) {
1463 params[0] = dc.nick
1464 }
1465 }
1466 dc.SendMessage(&irc.Message{
1467 Prefix: uc.srv.prefix(),
1468 Command: msg.Command,
1469 Params: params,
1470 })
1471 })
1472 }
1473 return nil
1474}
1475
1476func (uc *upstreamConn) handleDetachedMessage(sender string, text string, ch *Channel) {
1477 highlight := sender != uc.nick && isHighlight(text, uc.nick)
1478 if ch.RelayDetached == FilterMessage || ((ch.RelayDetached == FilterHighlight || ch.RelayDetached == FilterDefault) && highlight) {
1479 uc.forEachDownstream(func(dc *downstreamConn) {
1480 if highlight {
1481 sendServiceNOTICE(dc, fmt.Sprintf("highlight in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text))
1482 } else {
1483 sendServiceNOTICE(dc, fmt.Sprintf("message in %v: <%v> %v", dc.marshalEntity(uc.network, ch.Name), sender, text))
1484 }
1485 })
1486 }
1487 if ch.ReattachOn == FilterMessage || (ch.ReattachOn == FilterHighlight && highlight) {
1488 uc.network.attach(ch)
1489 if err := uc.srv.db.StoreChannel(uc.network.ID, ch); err != nil {
1490 uc.logger.Printf("failed to update channel %q: %v", ch.Name, err)
1491 }
1492 }
1493}
1494
1495func (uc *upstreamConn) handleChanModes(s string) error {
1496 parts := strings.SplitN(s, ",", 5)
1497 if len(parts) < 4 {
1498 return fmt.Errorf("malformed ISUPPORT CHANMODES value: %v", s)
1499 }
1500 modes := make(map[byte]channelModeType)
1501 for i, mt := range []channelModeType{modeTypeA, modeTypeB, modeTypeC, modeTypeD} {
1502 for j := 0; j < len(parts[i]); j++ {
1503 mode := parts[i][j]
1504 modes[mode] = mt
1505 }
1506 }
1507 uc.availableChannelModes = modes
1508 return nil
1509}
1510
1511func (uc *upstreamConn) handleMemberships(s string) error {
1512 if s == "" {
1513 uc.availableMemberships = nil
1514 return nil
1515 }
1516
1517 if s[0] != '(' {
1518 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1519 }
1520 sep := strings.IndexByte(s, ')')
1521 if sep < 0 || len(s) != sep*2 {
1522 return fmt.Errorf("malformed ISUPPORT PREFIX value: %v", s)
1523 }
1524 memberships := make([]membership, len(s)/2-1)
1525 for i := range memberships {
1526 memberships[i] = membership{
1527 Mode: s[i+1],
1528 Prefix: s[sep+i+1],
1529 }
1530 }
1531 uc.availableMemberships = memberships
1532 return nil
1533}
1534
1535func (uc *upstreamConn) handleSupportedCaps(capsStr string) {
1536 caps := strings.Fields(capsStr)
1537 for _, s := range caps {
1538 kv := strings.SplitN(s, "=", 2)
1539 k := strings.ToLower(kv[0])
1540 var v string
1541 if len(kv) == 2 {
1542 v = kv[1]
1543 }
1544 uc.supportedCaps[k] = v
1545 }
1546}
1547
1548func (uc *upstreamConn) requestCaps() {
1549 var requestCaps []string
1550 for c := range permanentUpstreamCaps {
1551 if _, ok := uc.supportedCaps[c]; ok && !uc.caps[c] {
1552 requestCaps = append(requestCaps, c)
1553 }
1554 }
1555
1556 if uc.requestSASL() && !uc.caps["sasl"] {
1557 requestCaps = append(requestCaps, "sasl")
1558 }
1559
1560 if len(requestCaps) == 0 {
1561 return
1562 }
1563
1564 uc.SendMessage(&irc.Message{
1565 Command: "CAP",
1566 Params: []string{"REQ", strings.Join(requestCaps, " ")},
1567 })
1568}
1569
1570func (uc *upstreamConn) requestSASL() bool {
1571 if uc.network.SASL.Mechanism == "" {
1572 return false
1573 }
1574
1575 v, ok := uc.supportedCaps["sasl"]
1576 if !ok {
1577 return false
1578 }
1579 if v != "" {
1580 mechanisms := strings.Split(v, ",")
1581 found := false
1582 for _, mech := range mechanisms {
1583 if strings.EqualFold(mech, uc.network.SASL.Mechanism) {
1584 found = true
1585 break
1586 }
1587 }
1588 if !found {
1589 return false
1590 }
1591 }
1592
1593 return true
1594}
1595
1596func (uc *upstreamConn) handleCapAck(name string, ok bool) error {
1597 uc.caps[name] = ok
1598
1599 switch name {
1600 case "sasl":
1601 if !ok {
1602 uc.logger.Printf("server refused to acknowledge the SASL capability")
1603 return nil
1604 }
1605
1606 auth := &uc.network.SASL
1607 switch auth.Mechanism {
1608 case "PLAIN":
1609 uc.logger.Printf("starting SASL PLAIN authentication with username %q", auth.Plain.Username)
1610 uc.saslClient = sasl.NewPlainClient("", auth.Plain.Username, auth.Plain.Password)
1611 case "EXTERNAL":
1612 uc.logger.Printf("starting SASL EXTERNAL authentication")
1613 uc.saslClient = sasl.NewExternalClient("")
1614 default:
1615 return fmt.Errorf("unsupported SASL mechanism %q", name)
1616 }
1617
1618 uc.SendMessage(&irc.Message{
1619 Command: "AUTHENTICATE",
1620 Params: []string{auth.Mechanism},
1621 })
1622 default:
1623 if permanentUpstreamCaps[name] {
1624 break
1625 }
1626 uc.logger.Printf("received CAP ACK/NAK for a cap we don't support: %v", name)
1627 }
1628 return nil
1629}
1630
1631func splitSpace(s string) []string {
1632 return strings.FieldsFunc(s, func(r rune) bool {
1633 return r == ' '
1634 })
1635}
1636
1637func (uc *upstreamConn) register() {
1638 uc.nick = uc.network.Nick
1639 uc.nickCM = uc.network.casemap(uc.nick)
1640 uc.username = uc.network.GetUsername()
1641 uc.realname = uc.network.GetRealname()
1642
1643 uc.SendMessage(&irc.Message{
1644 Command: "CAP",
1645 Params: []string{"LS", "302"},
1646 })
1647
1648 if uc.network.Pass != "" {
1649 uc.SendMessage(&irc.Message{
1650 Command: "PASS",
1651 Params: []string{uc.network.Pass},
1652 })
1653 }
1654
1655 uc.SendMessage(&irc.Message{
1656 Command: "NICK",
1657 Params: []string{uc.nick},
1658 })
1659 uc.SendMessage(&irc.Message{
1660 Command: "USER",
1661 Params: []string{uc.username, "0", "*", uc.realname},
1662 })
1663}
1664
1665func (uc *upstreamConn) runUntilRegistered() error {
1666 for !uc.registered {
1667 msg, err := uc.ReadMessage()
1668 if err != nil {
1669 return fmt.Errorf("failed to read message: %v", err)
1670 }
1671
1672 if err := uc.handleMessage(msg); err != nil {
1673 if _, ok := err.(registrationError); ok {
1674 return err
1675 } else {
1676 msg.Tags = nil // prevent message tags from cluttering logs
1677 return fmt.Errorf("failed to handle message %q: %v", msg, err)
1678 }
1679 }
1680 }
1681
1682 for _, command := range uc.network.ConnectCommands {
1683 m, err := irc.ParseMessage(command)
1684 if err != nil {
1685 uc.logger.Printf("failed to parse connect command %q: %v", command, err)
1686 } else {
1687 uc.SendMessage(m)
1688 }
1689 }
1690
1691 return nil
1692}
1693
1694func (uc *upstreamConn) readMessages(ch chan<- event) error {
1695 for {
1696 msg, err := uc.ReadMessage()
1697 if err == io.EOF {
1698 break
1699 } else if err != nil {
1700 return fmt.Errorf("failed to read IRC command: %v", err)
1701 }
1702
1703 ch <- eventUpstreamMessage{msg, uc}
1704 }
1705
1706 return nil
1707}
1708
1709func (uc *upstreamConn) SendMessage(msg *irc.Message) {
1710 if !uc.caps["message-tags"] {
1711 msg = msg.Copy()
1712 msg.Tags = nil
1713 }
1714
1715 uc.conn.SendMessage(msg)
1716}
1717
1718func (uc *upstreamConn) SendMessageLabeled(downstreamID uint64, msg *irc.Message) {
1719 if uc.caps["labeled-response"] {
1720 if msg.Tags == nil {
1721 msg.Tags = make(map[string]irc.TagValue)
1722 }
1723 msg.Tags["label"] = irc.TagValue(fmt.Sprintf("sd-%d-%d", downstreamID, uc.nextLabelID))
1724 uc.nextLabelID++
1725 }
1726 uc.SendMessage(msg)
1727}
1728
1729// appendLog appends a message to the log file.
1730//
1731// The internal message ID is returned. If the message isn't recorded in the
1732// log file, an empty string is returned.
1733func (uc *upstreamConn) appendLog(entity string, msg *irc.Message) (msgID string) {
1734 if uc.user.msgStore == nil {
1735 return ""
1736 }
1737 if entity == "NickServ" {
1738 // The messages sent/received from NickServ may contain
1739 // security-related information (like passwords). Don't store these.
1740 return ""
1741 }
1742
1743 detached := false
1744 if ch := uc.network.channels.Value(entity); ch != nil {
1745 detached = ch.Detached
1746 }
1747
1748 delivered := uc.network.delivered.Value(entity)
1749 entityCM := uc.network.casemap(entity)
1750 if delivered == nil {
1751 lastID, err := uc.user.msgStore.LastMsgID(uc.network, entityCM, time.Now())
1752 if err != nil {
1753 uc.logger.Printf("failed to log message: failed to get last message ID: %v", err)
1754 return ""
1755 }
1756
1757 delivered = make(map[string]string)
1758 uc.network.delivered.SetValue(entity, delivered)
1759
1760 for clientName, _ := range uc.network.offlineClients {
1761 delivered[clientName] = lastID
1762 }
1763
1764 if detached {
1765 // If the channel is detached, online clients act as offline
1766 // clients too
1767 uc.forEachDownstream(func(dc *downstreamConn) {
1768 delivered[dc.clientName] = lastID
1769 })
1770 }
1771 }
1772
1773 msgID, err := uc.user.msgStore.Append(uc.network, entityCM, msg)
1774 if err != nil {
1775 uc.logger.Printf("failed to log message: %v", err)
1776 return ""
1777 }
1778
1779 return msgID
1780}
1781
1782// produce appends a message to the logs and forwards it to connected downstream
1783// connections.
1784//
1785// If origin is not nil and origin doesn't support echo-message, the message is
1786// forwarded to all connections except origin.
1787func (uc *upstreamConn) produce(target string, msg *irc.Message, origin *downstreamConn) {
1788 var msgID string
1789 if target != "" {
1790 msgID = uc.appendLog(target, msg)
1791 }
1792
1793 // Don't forward messages if it's a detached channel
1794 ch := uc.network.channels.Value(target)
1795 if ch != nil && ch.Detached {
1796 return
1797 }
1798
1799 uc.forEachDownstream(func(dc *downstreamConn) {
1800 if dc != origin || dc.caps["echo-message"] {
1801 dc.sendMessageWithID(dc.marshalMessage(msg, uc.network), msgID)
1802 } else {
1803 dc.advanceMessageWithID(msg, msgID)
1804 }
1805 })
1806}
1807
1808func (uc *upstreamConn) updateAway() {
1809 away := true
1810 uc.forEachDownstream(func(*downstreamConn) {
1811 away = false
1812 })
1813 if away == uc.away {
1814 return
1815 }
1816 if away {
1817 uc.SendMessage(&irc.Message{
1818 Command: "AWAY",
1819 Params: []string{"Auto away"},
1820 })
1821 } else {
1822 uc.SendMessage(&irc.Message{
1823 Command: "AWAY",
1824 })
1825 }
1826 uc.away = away
1827}
1828
1829func (uc *upstreamConn) updateChannelAutoDetach(name string) {
1830 uch := uc.channels.Value(name)
1831 if uch == nil {
1832 return
1833 }
1834 ch := uc.network.channels.Value(name)
1835 if ch == nil || ch.Detached {
1836 return
1837 }
1838 uch.updateAutoDetach(ch.DetachAfter)
1839}
Note: See TracBrowser for help on using the repository browser.