|
@@ -6,6 +6,7 @@
|
6
|
6
|
package irc
|
7
|
7
|
|
8
|
8
|
import (
|
|
9
|
+ "fmt"
|
9
|
10
|
"strconv"
|
10
|
11
|
"strings"
|
11
|
12
|
|
|
@@ -104,17 +105,15 @@ func ParseDefaultChannelModes(rawModes *string) modes.Modes {
|
104
|
105
|
}
|
105
|
106
|
|
106
|
107
|
// ApplyChannelModeChanges applies a given set of mode changes.
|
107
|
|
-func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) modes.ModeChanges {
|
|
108
|
+func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes modes.ModeChanges, rb *ResponseBuffer) (applied modes.ModeChanges) {
|
108
|
109
|
// so we only output one warning for each list type when full
|
109
|
110
|
listFullWarned := make(map[modes.Mode]bool)
|
110
|
111
|
|
111
|
112
|
var alreadySentPrivError bool
|
112
|
113
|
|
113
|
|
- applied := make(modes.ModeChanges, 0)
|
114
|
|
-
|
115
|
|
- isListOp := func(change modes.ModeChange) bool {
|
116
|
|
- return (change.Op == modes.List) || (change.Arg == "")
|
117
|
|
- }
|
|
114
|
+ maskOpCount := 0
|
|
115
|
+ chname := channel.Name()
|
|
116
|
+ details := client.Details()
|
118
|
117
|
|
119
|
118
|
hasPrivs := func(change modes.ModeChange) bool {
|
120
|
119
|
if isSamode {
|
|
@@ -127,18 +126,19 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
|
127
|
126
|
return true
|
128
|
127
|
}
|
129
|
128
|
cfarg, _ := CasefoldName(change.Arg)
|
130
|
|
- isSelfChange := cfarg == client.NickCasefolded()
|
|
129
|
+ isSelfChange := cfarg == details.nickCasefolded
|
131
|
130
|
if change.Op == modes.Remove && isSelfChange {
|
132
|
131
|
// "There is no restriction, however, on anyone `deopping' themselves"
|
133
|
132
|
// <https://tools.ietf.org/html/rfc2812#section-3.1.5>
|
134
|
133
|
return true
|
135
|
134
|
}
|
136
|
135
|
return channelUserModeHasPrivsOver(channel.HighestUserMode(client), change.Mode)
|
137
|
|
- case modes.BanMask:
|
138
|
|
- // #163: allow unprivileged users to list ban masks
|
139
|
|
- return isListOp(change) || channel.ClientIsAtLeast(client, modes.ChannelOperator)
|
140
|
|
- default:
|
|
136
|
+ case modes.InviteMask, modes.ExceptMask:
|
|
137
|
+ // listing these requires privileges
|
141
|
138
|
return channel.ClientIsAtLeast(client, modes.ChannelOperator)
|
|
139
|
+ default:
|
|
140
|
+ // #163: allow unprivileged users to list ban masks, and any other modes
|
|
141
|
+ return change.Op == modes.List || channel.ClientIsAtLeast(client, modes.ChannelOperator)
|
142
|
142
|
}
|
143
|
143
|
}
|
144
|
144
|
|
|
@@ -146,14 +146,15 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
|
146
|
146
|
if !hasPrivs(change) {
|
147
|
147
|
if !alreadySentPrivError {
|
148
|
148
|
alreadySentPrivError = true
|
149
|
|
- rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, client.Nick(), channel.name, client.t("You're not a channel operator"))
|
|
149
|
+ rb.Add(nil, client.server.name, ERR_CHANOPRIVSNEEDED, details.nick, channel.name, client.t("You're not a channel operator"))
|
150
|
150
|
}
|
151
|
151
|
continue
|
152
|
152
|
}
|
153
|
153
|
|
154
|
154
|
switch change.Mode {
|
155
|
155
|
case modes.BanMask, modes.ExceptMask, modes.InviteMask:
|
156
|
|
- if isListOp(change) {
|
|
156
|
+ maskOpCount += 1
|
|
157
|
+ if change.Op == modes.List {
|
157
|
158
|
channel.ShowMaskList(client, change.Mode, rb)
|
158
|
159
|
continue
|
159
|
160
|
}
|
|
@@ -163,20 +164,33 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
|
163
|
164
|
case modes.Add:
|
164
|
165
|
if channel.lists[change.Mode].Length() >= client.server.Config().Limits.ChanListModes {
|
165
|
166
|
if !listFullWarned[change.Mode] {
|
166
|
|
- rb.Add(nil, client.server.name, ERR_BANLISTFULL, client.Nick(), channel.Name(), change.Mode.String(), client.t("Channel list is full"))
|
|
167
|
+ rb.Add(nil, client.server.name, ERR_BANLISTFULL, details.nick, chname, change.Mode.String(), client.t("Channel list is full"))
|
167
|
168
|
listFullWarned[change.Mode] = true
|
168
|
169
|
}
|
169
|
170
|
continue
|
170
|
171
|
}
|
171
|
172
|
|
172
|
|
- details := client.Details()
|
173
|
|
- if success := channel.lists[change.Mode].Add(mask, details.nickMask, details.accountName); success {
|
174
|
|
- applied = append(applied, change)
|
|
173
|
+ maskAdded, err := channel.lists[change.Mode].Add(mask, details.nickMask, details.accountName)
|
|
174
|
+ if maskAdded != "" {
|
|
175
|
+ appliedChange := change
|
|
176
|
+ appliedChange.Arg = maskAdded
|
|
177
|
+ applied = append(applied, appliedChange)
|
|
178
|
+ } else if err != nil {
|
|
179
|
+ rb.Add(nil, client.server.name, ERR_INVALIDMODEPARAM, details.nick, mask, fmt.Sprintf(client.t("Invalid mode %s parameter: %s"), string(change.Mode), mask))
|
|
180
|
+ } else {
|
|
181
|
+ rb.Add(nil, client.server.name, ERR_LISTMODEALREADYSET, chname, mask, string(change.Mode), fmt.Sprintf(client.t("Channel %s list already contains %s"), chname, mask))
|
175
|
182
|
}
|
176
|
183
|
|
177
|
184
|
case modes.Remove:
|
178
|
|
- if success := channel.lists[change.Mode].Remove(mask); success {
|
179
|
|
- applied = append(applied, change)
|
|
185
|
+ maskRemoved, err := channel.lists[change.Mode].Remove(mask)
|
|
186
|
+ if maskRemoved != "" {
|
|
187
|
+ appliedChange := change
|
|
188
|
+ appliedChange.Arg = maskRemoved
|
|
189
|
+ applied = append(applied, appliedChange)
|
|
190
|
+ } else if err != nil {
|
|
191
|
+ rb.Add(nil, client.server.name, ERR_INVALIDMODEPARAM, details.nick, mask, fmt.Sprintf(client.t("Invalid mode %s parameter: %s"), string(change.Mode), mask))
|
|
192
|
+ } else {
|
|
193
|
+ rb.Add(nil, client.server.name, ERR_LISTMODENOTSET, chname, mask, string(change.Mode), fmt.Sprintf(client.t("Channel %s list does not contain %s"), chname, mask))
|
180
|
194
|
}
|
181
|
195
|
}
|
182
|
196
|
|
|
@@ -221,7 +235,7 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
|
221
|
235
|
nick := change.Arg
|
222
|
236
|
if nick == "" {
|
223
|
237
|
rb.Add(nil, client.server.name, ERR_NEEDMOREPARAMS, client.Nick(), "MODE", client.t("Not enough parameters"))
|
224
|
|
- return nil
|
|
238
|
+ continue
|
225
|
239
|
}
|
226
|
240
|
|
227
|
241
|
change := channel.applyModeToMember(client, change.Mode, change.Op, nick, rb)
|
|
@@ -231,6 +245,13 @@ func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, c
|
231
|
245
|
}
|
232
|
246
|
}
|
233
|
247
|
|
|
248
|
+ // #649: don't send 324 RPL_CHANNELMODEIS if we were only working with mask lists
|
|
249
|
+ if len(applied) == 0 && !alreadySentPrivError && (maskOpCount == 0 || maskOpCount < len(changes)) {
|
|
250
|
+ args := append([]string{details.nick, chname}, channel.modeStrings(client)...)
|
|
251
|
+ rb.Add(nil, client.server.name, RPL_CHANNELMODEIS, args...)
|
|
252
|
+ rb.Add(nil, client.server.name, RPL_CREATIONTIME, details.nick, chname, strconv.FormatInt(channel.createdTime.Unix(), 10))
|
|
253
|
+ }
|
|
254
|
+
|
234
|
255
|
return applied
|
235
|
256
|
}
|
236
|
257
|
|