Browse Source

mask lists (ban, except, invite)

tags/v0.1.0
Jeremy Latt 10 years ago
parent
commit
d4093e7f8b
4 changed files with 120 additions and 15 deletions
  1. 53
    13
      irc/channel.go
  2. 57
    0
      irc/client_lookup_set.go
  3. 10
    0
      irc/reply.go
  4. 0
    2
      irc/types.go

+ 53
- 13
irc/channel.go View File

@@ -8,7 +8,7 @@ import (
8 8
 
9 9
 type Channel struct {
10 10
 	flags     ChannelModeSet
11
-	lists     map[ChannelMode]UserMaskSet
11
+	lists     map[ChannelMode]*UserMaskSet
12 12
 	key       string
13 13
 	members   MemberSet
14 14
 	name      string
@@ -26,10 +26,10 @@ func IsChannel(target string) bool {
26 26
 func NewChannel(s *Server, name string) *Channel {
27 27
 	channel := &Channel{
28 28
 		flags: make(ChannelModeSet),
29
-		lists: map[ChannelMode]UserMaskSet{
30
-			BanMask:    make(UserMaskSet),
31
-			ExceptMask: make(UserMaskSet),
32
-			InviteMask: make(UserMaskSet),
29
+		lists: map[ChannelMode]*UserMaskSet{
30
+			BanMask:    NewUserMaskSet(),
31
+			ExceptMask: NewUserMaskSet(),
32
+			InviteMask: NewUserMaskSet(),
33 33
 		},
34 34
 		members: make(MemberSet),
35 35
 		name:    strings.ToLower(name),
@@ -151,6 +151,19 @@ func (channel *Channel) Join(client *Client, key string) {
151 151
 		return
152 152
 	}
153 153
 
154
+	isInvited := channel.lists[InviteMask].Match(client.UserHost())
155
+	if channel.flags[InviteOnly] && !isInvited {
156
+		client.ErrInviteOnlyChan(channel)
157
+		return
158
+	}
159
+
160
+	if channel.lists[BanMask].Match(client.UserHost()) &&
161
+		!isInvited &&
162
+		!channel.lists[ExceptMask].Match(client.UserHost()) {
163
+		client.ErrBannedFromChan(channel)
164
+		return
165
+	}
166
+
154 167
 	client.channels.Add(channel)
155 168
 	channel.members.Add(client)
156 169
 	if !channel.flags[Persistent] && (len(channel.members) == 1) {
@@ -213,7 +226,7 @@ func (channel *Channel) SetTopic(client *Client, topic string) {
213 226
 	}
214 227
 
215 228
 	if err := channel.Persist(); err != nil {
216
-		log.Println(err)
229
+		log.Println("Channel.Persist:", channel, err)
217 230
 	}
218 231
 }
219 232
 
@@ -310,15 +323,35 @@ func (channel *Channel) applyModeMember(client *Client, mode ChannelMode,
310 323
 	return false
311 324
 }
312 325
 
326
+func (channel *Channel) applyModeMask(client *Client, mode ChannelMode, op ModeOp, mask string) bool {
327
+	if !channel.ClientIsOperator(client) {
328
+		client.ErrChanOPrivIsNeeded(channel)
329
+		return false
330
+	}
331
+
332
+	list := channel.lists[mode]
333
+	if list == nil {
334
+		// This should never happen, but better safe than panicky.
335
+		return false
336
+	}
337
+
338
+	if op == Add {
339
+		list.Add(mask)
340
+	} else if op == Remove {
341
+		list.Remove(mask)
342
+	}
343
+
344
+	for lmask := range channel.lists[mode].masks {
345
+		client.RplMaskList(mode, channel, lmask)
346
+	}
347
+	client.RplEndOfMaskList(mode, channel)
348
+	return true
349
+}
350
+
313 351
 func (channel *Channel) applyMode(client *Client, change *ChannelModeChange) bool {
314 352
 	switch change.mode {
315 353
 	case BanMask, ExceptMask, InviteMask:
316
-		// TODO add/remove
317
-
318
-		for mask := range channel.lists[change.mode] {
319
-			client.RplMaskList(change.mode, channel, mask)
320
-		}
321
-		client.RplEndOfMaskList(change.mode, channel)
354
+		return channel.applyModeMask(client, change.mode, change.op, change.arg)
322 355
 
323 356
 	case Moderated, NoOutside, OpOnlyTopic, Persistent, Private:
324 357
 		return channel.applyModeFlag(client, change.mode, change.op)
@@ -390,7 +423,7 @@ func (channel *Channel) Mode(client *Client, changes ChannelModeChanges) {
390 423
 		}
391 424
 
392 425
 		if err := channel.Persist(); err != nil {
393
-			log.Println(err)
426
+			log.Println("Channel.Persist:", channel, err)
394 427
 		}
395 428
 	}
396 429
 }
@@ -464,6 +497,13 @@ func (channel *Channel) Invite(invitee *Client, inviter *Client) {
464 497
 		return
465 498
 	}
466 499
 
500
+	if channel.flags[InviteOnly] {
501
+		channel.lists[InviteMask].Add(invitee.UserHost())
502
+		if err := channel.Persist(); err != nil {
503
+			log.Println("Channel.Persist:", channel, err)
504
+		}
505
+	}
506
+
467 507
 	inviter.RplInviting(invitee, channel.name)
468 508
 	invitee.Reply(RplInviteMsg(inviter, invitee, channel.name))
469 509
 	if invitee.flags[Away] {

+ 57
- 0
irc/client_lookup_set.go View File

@@ -178,3 +178,60 @@ func (db *ClientDB) Remove(client *Client) {
178 178
 		}
179 179
 	}
180 180
 }
181
+
182
+//
183
+// usermask to regexp
184
+//
185
+
186
+type UserMaskSet struct {
187
+	masks  map[string]bool
188
+	regexp *regexp.Regexp
189
+}
190
+
191
+func NewUserMaskSet() *UserMaskSet {
192
+	return &UserMaskSet{
193
+		masks: make(map[string]bool),
194
+	}
195
+}
196
+
197
+func (set *UserMaskSet) Add(mask string) {
198
+	set.masks[mask] = true
199
+	set.setRegexp()
200
+}
201
+
202
+func (set *UserMaskSet) Remove(mask string) {
203
+	delete(set.masks, mask)
204
+	set.setRegexp()
205
+}
206
+
207
+func (set *UserMaskSet) Match(userhost string) bool {
208
+	if set.regexp == nil {
209
+		return false
210
+	}
211
+	return set.regexp.MatchString(userhost)
212
+}
213
+
214
+func (set *UserMaskSet) setRegexp() {
215
+	if len(set.masks) == 0 {
216
+		set.regexp = nil
217
+		return
218
+	}
219
+
220
+	maskExprs := make([]string, len(set.masks))
221
+	index := 0
222
+	for mask := range set.masks {
223
+		manyParts := strings.Split(mask, "*")
224
+		manyExprs := make([]string, len(manyParts))
225
+		for mindex, manyPart := range manyParts {
226
+			oneParts := strings.Split(manyPart, "?")
227
+			oneExprs := make([]string, len(oneParts))
228
+			for oindex, onePart := range oneParts {
229
+				oneExprs[oindex] = regexp.QuoteMeta(onePart)
230
+			}
231
+			manyExprs[mindex] = strings.Join(oneExprs, ".")
232
+		}
233
+		maskExprs[index] = strings.Join(manyExprs, ".*")
234
+	}
235
+	expr := "^" + strings.Join(maskExprs, "|") + "$"
236
+	set.regexp, _ = regexp.Compile(expr)
237
+}

+ 10
- 0
irc/reply.go View File

@@ -541,3 +541,13 @@ func (target *Client) ErrInvalidCapCmd(subCommand CapSubCommand) {
541 541
 	target.NumericReply(ERR_INVALIDCAPCMD,
542 542
 		"%s :Invalid CAP subcommand", subCommand)
543 543
 }
544
+
545
+func (target *Client) ErrBannedFromChan(channel *Channel) {
546
+	target.NumericReply(ERR_BANNEDFROMCHAN,
547
+		"%s :Cannot join channel (+b)", channel)
548
+}
549
+
550
+func (target *Client) ErrInviteOnlyChan(channel *Channel) {
551
+	target.NumericReply(ERR_INVITEONLYCHAN,
552
+		"%s :Cannot join channel (+i)", channel)
553
+}

+ 0
- 2
irc/types.go View File

@@ -9,8 +9,6 @@ import (
9 9
 // simple types
10 10
 //
11 11
 
12
-type UserMaskSet map[string]bool
13
-
14 12
 type CapSubCommand string
15 13
 
16 14
 type Capability string

Loading…
Cancel
Save