Browse Source

Merge pull request #1169 from slingamn/banrace.2

fix #1166 and #1168
tags/v2.2.0-rc1
Shivaram Lingamneni 4 years ago
parent
commit
09b74aaa37
No account linked to committer's email address
6 changed files with 80 additions and 54 deletions
  1. 33
    32
      irc/channel.go
  2. 2
    2
      irc/channelmanager.go
  3. 16
    7
      irc/client.go
  4. 5
    0
      irc/errors.go
  5. 23
    12
      irc/handlers.go
  6. 1
    1
      irc/types.go

+ 33
- 32
irc/channel.go View File

@@ -660,7 +660,7 @@ func (channel *Channel) AddHistoryItem(item history.Item, account string) (err e
660 660
 }
661 661
 
662 662
 // Join joins the given client to this channel (if they can be joined).
663
-func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) {
663
+func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *ResponseBuffer) error {
664 664
 	details := client.Details()
665 665
 
666 666
 	channel.stateMutex.RLock()
@@ -676,39 +676,43 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
676 676
 
677 677
 	if alreadyJoined {
678 678
 		// no message needs to be sent
679
-		return
679
+		return nil
680 680
 	}
681 681
 
682
-	// the founder can always join (even if they disabled auto +q on join);
683
-	// anyone who automatically receives halfop or higher can always join
684
-	hasPrivs := isSajoin || (founder != "" && founder == details.account) || (persistentMode != 0 && persistentMode != modes.Voice)
682
+	// 0. SAJOIN always succeeds
683
+	// 1. the founder can always join (even if they disabled auto +q on join)
684
+	// 2. anyone who automatically receives halfop or higher can always join
685
+	// 3. people invited with INVITE can join
686
+	hasPrivs := isSajoin || (founder != "" && founder == details.account) ||
687
+		(persistentMode != 0 && persistentMode != modes.Voice) ||
688
+		client.CheckInvited(chcfname)
689
+	if !hasPrivs {
690
+		if limit != 0 && chcount >= limit {
691
+			return errLimitExceeded
692
+		}
685 693
 
686
-	if !hasPrivs && limit != 0 && chcount >= limit {
687
-		rb.Add(nil, client.server.name, ERR_CHANNELISFULL, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "l"))
688
-		return
689
-	}
694
+		if chkey != "" && !utils.SecretTokensMatch(chkey, key) {
695
+			return errWrongChannelKey
696
+		}
690 697
 
691
-	if !hasPrivs && chkey != "" && !utils.SecretTokensMatch(chkey, key) {
692
-		rb.Add(nil, client.server.name, ERR_BADCHANNELKEY, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "k"))
693
-		return
694
-	}
698
+		if channel.flags.HasMode(modes.InviteOnly) &&
699
+			!channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
700
+			return errInviteOnly
701
+		}
695 702
 
696
-	isInvited := client.CheckInvited(chcfname) || channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded)
697
-	if !hasPrivs && channel.flags.HasMode(modes.InviteOnly) && !isInvited {
698
-		rb.Add(nil, client.server.name, ERR_INVITEONLYCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "i"))
699
-		return
700
-	}
703
+		if channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) &&
704
+			!channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) &&
705
+			!channel.lists[modes.InviteMask].Match(details.nickMaskCasefolded) {
706
+			return errBanned
707
+		}
701 708
 
702
-	if !hasPrivs && channel.lists[modes.BanMask].Match(details.nickMaskCasefolded) &&
703
-		!isInvited &&
704
-		!channel.lists[modes.ExceptMask].Match(details.nickMaskCasefolded) {
705
-		rb.Add(nil, client.server.name, ERR_BANNEDFROMCHAN, details.nick, chname, fmt.Sprintf(client.t("Cannot join channel (+%s)"), "b"))
706
-		return
709
+		if channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" {
710
+			return errRegisteredOnly
711
+		}
707 712
 	}
708 713
 
709
-	if !hasPrivs && channel.flags.HasMode(modes.RegisteredOnly) && details.account == "" && !isInvited {
710
-		rb.Add(nil, client.server.name, ERR_NEEDREGGEDNICK, details.nick, chname, client.t("You must be registered to join that channel"))
711
-		return
714
+	if joinErr := client.addChannel(channel, rb == nil); joinErr != nil {
715
+		return joinErr
712 716
 	}
713 717
 
714 718
 	client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", details.nick, chname))
@@ -753,10 +757,8 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
753 757
 		channel.AddHistoryItem(histItem, details.account)
754 758
 	}
755 759
 
756
-	client.addChannel(channel, rb == nil)
757
-
758 760
 	if rb == nil {
759
-		return
761
+		return nil
760 762
 	}
761 763
 
762 764
 	var modestr string
@@ -799,6 +801,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
799 801
 	rb.Flush(true)
800 802
 
801 803
 	channel.autoReplayHistory(client, rb, message.Msgid)
804
+	return nil
802 805
 }
803 806
 
804 807
 func (channel *Channel) autoReplayHistory(client *Client, rb *ResponseBuffer, skipMsgid string) {
@@ -1437,9 +1440,7 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuf
1437 1440
 		return
1438 1441
 	}
1439 1442
 
1440
-	if channel.flags.HasMode(modes.InviteOnly) {
1441
-		invitee.Invite(channel.NameCasefolded())
1442
-	}
1443
+	invitee.Invite(channel.NameCasefolded())
1443 1444
 
1444 1445
 	for _, member := range channel.Members() {
1445 1446
 		if member == inviter || member == invitee || !channel.ClientIsAtLeast(member, modes.Halfop) {

+ 2
- 2
irc/channelmanager.go View File

@@ -130,11 +130,11 @@ func (cm *ChannelManager) Join(client *Client, name string, key string, isSajoin
130 130
 	}
131 131
 
132 132
 	channel.EnsureLoaded()
133
-	channel.Join(client, key, isSajoin, rb)
133
+	err = channel.Join(client, key, isSajoin, rb)
134 134
 
135 135
 	cm.maybeCleanup(channel, true)
136 136
 
137
-	return nil
137
+	return err
138 138
 }
139 139
 
140 140
 func (cm *ChannelManager) maybeCleanup(channel *Channel, afterJoin bool) {

+ 16
- 7
irc/client.go View File

@@ -62,7 +62,7 @@ type Client struct {
62 62
 	exitedSnomaskSent  bool
63 63
 	modes              modes.ModeSet
64 64
 	hostname           string
65
-	invitedTo          map[string]bool
65
+	invitedTo          StringSet
66 66
 	isSTSOnly          bool
67 67
 	languages          []string
68 68
 	lastActive         time.Time            // last time they sent a command that wasn't PONG or similar
@@ -1595,15 +1595,24 @@ func (session *Session) Notice(text string) {
1595 1595
 
1596 1596
 // `simulated` is for the fake join of an always-on client
1597 1597
 // (we just read the channel name from the database, there's no need to write it back)
1598
-func (client *Client) addChannel(channel *Channel, simulated bool) {
1598
+func (client *Client) addChannel(channel *Channel, simulated bool) (err error) {
1599
+	config := client.server.Config()
1600
+
1599 1601
 	client.stateMutex.Lock()
1600
-	client.channels[channel] = true
1601 1602
 	alwaysOn := client.alwaysOn
1603
+	if client.destroyed {
1604
+		err = errClientDestroyed
1605
+	} else if client.oper == nil && len(client.channels) >= config.Channels.MaxChannelsPerClient {
1606
+		err = errTooManyChannels
1607
+	} else {
1608
+		client.channels[channel] = empty{} // success
1609
+	}
1602 1610
 	client.stateMutex.Unlock()
1603 1611
 
1604
-	if alwaysOn && !simulated {
1612
+	if err == nil && alwaysOn && !simulated {
1605 1613
 		client.markDirty(IncludeChannels)
1606 1614
 	}
1615
+	return
1607 1616
 }
1608 1617
 
1609 1618
 func (client *Client) removeChannel(channel *Channel) {
@@ -1623,10 +1632,10 @@ func (client *Client) Invite(casefoldedChannel string) {
1623 1632
 	defer client.stateMutex.Unlock()
1624 1633
 
1625 1634
 	if client.invitedTo == nil {
1626
-		client.invitedTo = make(map[string]bool)
1635
+		client.invitedTo = make(StringSet)
1627 1636
 	}
1628 1637
 
1629
-	client.invitedTo[casefoldedChannel] = true
1638
+	client.invitedTo.Add(casefoldedChannel)
1630 1639
 }
1631 1640
 
1632 1641
 // Checks that the client was invited to join a given channel
@@ -1634,7 +1643,7 @@ func (client *Client) CheckInvited(casefoldedChannel string) (invited bool) {
1634 1643
 	client.stateMutex.Lock()
1635 1644
 	defer client.stateMutex.Unlock()
1636 1645
 
1637
-	invited = client.invitedTo[casefoldedChannel]
1646
+	invited = client.invitedTo.Has(casefoldedChannel)
1638 1647
 	// joining an invited channel "uses up" your invite, so you can't rejoin on kick
1639 1648
 	delete(client.invitedTo, casefoldedChannel)
1640 1649
 	return

+ 5
- 0
irc/errors.go View File

@@ -67,6 +67,11 @@ var (
67 67
 	errInvalidMultilineBatch          = errors.New("Invalid multiline batch")
68 68
 	errTimedOut                       = errors.New("Operation timed out")
69 69
 	errInvalidUtf8                    = errors.New("Message rejected for invalid utf8")
70
+	errClientDestroyed                = errors.New("Client was already destroyed")
71
+	errTooManyChannels                = errors.New("You have joined too many channels")
72
+	errWrongChannelKey                = errors.New("Cannot join password-protected channel without the password")
73
+	errInviteOnly                     = errors.New("Cannot join invite-only channel without an invite")
74
+	errRegisteredOnly                 = errors.New("Cannot join registered-only channel without an account")
70 75
 )
71 76
 
72 77
 // Socket Errors

+ 23
- 12
irc/handlers.go View File

@@ -1142,16 +1142,10 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1142 1142
 		keys = strings.Split(msg.Params[1], ",")
1143 1143
 	}
1144 1144
 
1145
-	config := server.Config()
1146
-	oper := client.Oper()
1147 1145
 	for i, name := range channels {
1148 1146
 		if name == "" {
1149 1147
 			continue // #679
1150 1148
 		}
1151
-		if config.Channels.MaxChannelsPerClient <= client.NumChannels() && oper == nil {
1152
-			rb.Add(nil, server.name, ERR_TOOMANYCHANNELS, client.Nick(), name, client.t("You have joined too many channels"))
1153
-			return false
1154
-		}
1155 1149
 		var key string
1156 1150
 		if len(keys) > i {
1157 1151
 			key = keys[i]
@@ -1165,18 +1159,35 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1165 1159
 }
1166 1160
 
1167 1161
 func sendJoinError(client *Client, name string, rb *ResponseBuffer, err error) {
1168
-	var errMsg string
1162
+	var code, errMsg, forbiddingMode string
1169 1163
 	switch err {
1170 1164
 	case errInsufficientPrivs:
1171
-		errMsg = `Only server operators can create new channels`
1165
+		code, errMsg = ERR_NOSUCHCHANNEL, `Only server operators can create new channels`
1172 1166
 	case errConfusableIdentifier:
1173
-		errMsg = `That channel name is too close to the name of another channel`
1167
+		code, errMsg = ERR_NOSUCHCHANNEL, `That channel name is too close to the name of another channel`
1174 1168
 	case errChannelPurged:
1175
-		errMsg = err.Error()
1169
+		code, errMsg = ERR_NOSUCHCHANNEL, err.Error()
1170
+	case errTooManyChannels:
1171
+		code, errMsg = ERR_TOOMANYCHANNELS, `You have joined too many channels`
1172
+	case errLimitExceeded:
1173
+		code, forbiddingMode = ERR_CHANNELISFULL, "l"
1174
+	case errWrongChannelKey:
1175
+		code, forbiddingMode = ERR_BADCHANNELKEY, "k"
1176
+	case errInviteOnly:
1177
+		code, forbiddingMode = ERR_INVITEONLYCHAN, "i"
1178
+	case errBanned:
1179
+		code, forbiddingMode = ERR_BANNEDFROMCHAN, "b"
1180
+	case errRegisteredOnly:
1181
+		code, errMsg = ERR_NEEDREGGEDNICK, `You must be registered to join that channel`
1176 1182
 	default:
1177
-		errMsg = `No such channel`
1183
+		code, errMsg = ERR_NOSUCHCHANNEL, `No such channel`
1184
+	}
1185
+	if forbiddingMode != "" {
1186
+		errMsg = fmt.Sprintf(client.t("Cannot join channel (+%s)"), forbiddingMode)
1187
+	} else {
1188
+		errMsg = client.t(errMsg)
1178 1189
 	}
1179
-	rb.Add(nil, client.server.name, ERR_NOSUCHCHANNEL, client.Nick(), utils.SafeErrorParam(name), client.t(errMsg))
1190
+	rb.Add(nil, client.server.name, code, client.Nick(), utils.SafeErrorParam(name), errMsg)
1180 1191
 }
1181 1192
 
1182 1193
 // SAJOIN [nick] #channel{,#channel}

+ 1
- 1
irc/types.go View File

@@ -69,4 +69,4 @@ func (members MemberSet) AnyHasMode(mode modes.Mode) bool {
69 69
 }
70 70
 
71 71
 // ChannelSet is a set of channels.
72
-type ChannelSet map[*Channel]bool
72
+type ChannelSet map[*Channel]empty

Loading…
Cancel
Save