Sfoglia il codice sorgente

Merge remote-tracking branch 'slingmann/membersmutex.2'

tags/v0.10.1
Daniel Oaks 6 anni fa
parent
commit
c09ca729c4
9 ha cambiato i file con 439 aggiunte e 346 eliminazioni
  1. 203
    180
      irc/channel.go
  2. 1
    4
      irc/chanserv.go
  3. 34
    8
      irc/client.go
  4. 50
    16
      irc/client_lookup_set.go
  5. 94
    0
      irc/getters.go
  6. 33
    58
      irc/modes.go
  7. 1
    3
      irc/roleplay.go
  8. 23
    67
      irc/server.go
  9. 0
    10
      irc/types.go

+ 203
- 180
irc/channel.go Vedi File

23
 	flags          ModeSet
23
 	flags          ModeSet
24
 	lists          map[Mode]*UserMaskSet
24
 	lists          map[Mode]*UserMaskSet
25
 	key            string
25
 	key            string
26
-	membersMutex   sync.RWMutex
27
 	members        MemberSet
26
 	members        MemberSet
27
+	membersCache   []*Client // allow iteration over channel members without holding the lock
28
 	name           string
28
 	name           string
29
 	nameCasefolded string
29
 	nameCasefolded string
30
 	server         *Server
30
 	server         *Server
31
 	createdTime    time.Time
31
 	createdTime    time.Time
32
+	stateMutex     sync.RWMutex
32
 	topic          string
33
 	topic          string
33
 	topicSetBy     string
34
 	topicSetBy     string
34
 	topicSetTime   time.Time
35
 	topicSetTime   time.Time
68
 	return channel
69
 	return channel
69
 }
70
 }
70
 
71
 
71
-// IsEmpty returns true if the channel has no clients.
72
-func (channel *Channel) IsEmpty() bool {
73
-	channel.membersMutex.RLock()
74
-	defer channel.membersMutex.RUnlock()
75
-
76
-	return channel.isEmptyNoMutex()
77
-}
72
+func (channel *Channel) regenerateMembersCache() {
73
+	// this is eventually consistent even without holding the writable Lock()
74
+	// throughout the update; all updates to `members` while holding Lock()
75
+	// have a serial order, so the call to `regenerateMembersCache` that
76
+	// happens-after the last one will see *all* the updates
77
+	channel.stateMutex.RLock()
78
+	result := make([]*Client, len(channel.members))
79
+	i := 0
80
+	for client := range channel.members {
81
+		result[i] = client
82
+		i++
83
+	}
84
+	channel.stateMutex.RUnlock()
85
+	channel.stateMutex.Lock()
86
+	channel.membersCache = result
87
+	channel.stateMutex.Unlock()
88
+	return
78
 
89
 
79
-func (channel *Channel) isEmptyNoMutex() bool {
80
-	return len(channel.members) == 0
81
 }
90
 }
82
 
91
 
83
 // Names sends the list of users joined to the channel to the given client.
92
 // Names sends the list of users joined to the channel to the given client.
84
 func (channel *Channel) Names(client *Client) {
93
 func (channel *Channel) Names(client *Client) {
85
-	channel.membersMutex.RLock()
86
-	defer channel.membersMutex.RUnlock()
87
-
88
-	channel.namesNoMutex(client)
89
-}
90
-
91
-func (channel *Channel) namesNoMutex(client *Client) {
92
-	currentNicks := channel.nicksNoMutex(client)
94
+	currentNicks := channel.nicks(client)
93
 	// assemble and send replies
95
 	// assemble and send replies
94
 	maxNamLen := 480 - len(client.server.name) - len(client.nick)
96
 	maxNamLen := 480 - len(client.server.name) - len(client.nick)
95
 	var buffer string
97
 	var buffer string
115
 
117
 
116
 // ClientIsAtLeast returns whether the client has at least the given channel privilege.
118
 // ClientIsAtLeast returns whether the client has at least the given channel privilege.
117
 func (channel *Channel) ClientIsAtLeast(client *Client, permission Mode) bool {
119
 func (channel *Channel) ClientIsAtLeast(client *Client, permission Mode) bool {
118
-	channel.membersMutex.RLock()
119
-	defer channel.membersMutex.RUnlock()
120
-
121
-	return channel.clientIsAtLeastNoMutex(client, permission)
122
-}
123
-
124
-func (channel *Channel) clientIsAtLeastNoMutex(client *Client, permission Mode) bool {
125
-	// requires RLock()
120
+	channel.stateMutex.RLock()
121
+	defer channel.stateMutex.RUnlock()
126
 
122
 
127
 	// get voice, since it's not a part of ChannelPrivModes
123
 	// get voice, since it's not a part of ChannelPrivModes
128
 	if channel.members.HasMode(client, permission) {
124
 	if channel.members.HasMode(client, permission) {
164
 	return prefixes
160
 	return prefixes
165
 }
161
 }
166
 
162
 
167
-func (channel *Channel) nicksNoMutex(target *Client) []string {
163
+func (channel *Channel) ClientPrefixes(client *Client, isMultiPrefix bool) string {
164
+	channel.stateMutex.RLock()
165
+	defer channel.stateMutex.RUnlock()
166
+	modes, present := channel.members[client]
167
+	if !present {
168
+		return ""
169
+	} else {
170
+		return modes.Prefixes(isMultiPrefix)
171
+	}
172
+}
173
+
174
+func (channel *Channel) ClientHasPrivsOver(client *Client, target *Client) bool {
175
+	channel.stateMutex.RLock()
176
+	defer channel.stateMutex.RUnlock()
177
+
178
+	clientModes := channel.members[client]
179
+	targetModes := channel.members[target]
180
+	result := false
181
+	for _, mode := range ChannelPrivModes {
182
+		if clientModes[mode] {
183
+			result = true
184
+			// admins cannot kick other admins
185
+			if mode == ChannelAdmin && targetModes[ChannelAdmin] {
186
+				result = false
187
+			}
188
+			break
189
+		} else if channel.members[target][mode] {
190
+			break
191
+		}
192
+	}
193
+	return result
194
+}
195
+
196
+func (channel *Channel) nicks(target *Client) []string {
168
 	isMultiPrefix := (target != nil) && target.capabilities.Has(caps.MultiPrefix)
197
 	isMultiPrefix := (target != nil) && target.capabilities.Has(caps.MultiPrefix)
169
 	isUserhostInNames := (target != nil) && target.capabilities.Has(caps.UserhostInNames)
198
 	isUserhostInNames := (target != nil) && target.capabilities.Has(caps.UserhostInNames)
170
-	nicks := make([]string, len(channel.members))
199
+
200
+	// slightly cumbersome: get the mutex and copy both the client pointers and
201
+	// the mode prefixes
202
+	channel.stateMutex.RLock()
203
+	length := len(channel.members)
204
+	clients := make([]*Client, length)
205
+	result := make([]string, length)
171
 	i := 0
206
 	i := 0
172
 	for client, modes := range channel.members {
207
 	for client, modes := range channel.members {
173
-		nicks[i] += modes.Prefixes(isMultiPrefix)
208
+		clients[i] = client
209
+		result[i] = modes.Prefixes(isMultiPrefix)
210
+		i++
211
+	}
212
+	channel.stateMutex.RUnlock()
213
+
214
+	i = 0
215
+	for i < length {
174
 		if isUserhostInNames {
216
 		if isUserhostInNames {
175
-			nicks[i] += client.nickMaskString
217
+			result[i] += clients[i].getNickMaskString()
176
 		} else {
218
 		} else {
177
-			nicks[i] += client.nick
219
+			result[i] += clients[i].getNick()
178
 		}
220
 		}
179
 		i++
221
 		i++
180
 	}
222
 	}
181
-	return nicks
223
+
224
+	return result
225
+}
226
+
227
+func (channel *Channel) hasClient(client *Client) bool {
228
+	channel.stateMutex.RLock()
229
+	defer channel.stateMutex.RUnlock()
230
+	_, present := channel.members[client]
231
+	return present
182
 }
232
 }
183
 
233
 
184
 // <mode> <mode params>
234
 // <mode> <mode params>
185
-func (channel *Channel) modeStringNoLock(client *Client) (str string) {
186
-	// RLock()
187
-	isMember := client.flags[Operator] || channel.members.Has(client)
188
-	// RUnlock()
235
+func (channel *Channel) modeStrings(client *Client) (result []string) {
236
+	isMember := client.HasMode(Operator) || channel.hasClient(client)
189
 	showKey := isMember && (channel.key != "")
237
 	showKey := isMember && (channel.key != "")
190
 	showUserLimit := channel.userLimit > 0
238
 	showUserLimit := channel.userLimit > 0
191
 
239
 
240
+	modes := "+"
241
+
192
 	// flags with args
242
 	// flags with args
193
 	if showKey {
243
 	if showKey {
194
-		str += Key.String()
244
+		modes += Key.String()
195
 	}
245
 	}
196
 	if showUserLimit {
246
 	if showUserLimit {
197
-		str += UserLimit.String()
247
+		modes += UserLimit.String()
198
 	}
248
 	}
199
 
249
 
250
+	channel.stateMutex.RLock()
251
+	defer channel.stateMutex.RUnlock()
252
+
200
 	// flags
253
 	// flags
201
 	for mode := range channel.flags {
254
 	for mode := range channel.flags {
202
-		str += mode.String()
255
+		modes += mode.String()
203
 	}
256
 	}
204
 
257
 
205
-	str = "+" + str
258
+	result = []string{modes}
206
 
259
 
207
 	// args for flags with args: The order must match above to keep
260
 	// args for flags with args: The order must match above to keep
208
 	// positional arguments in place.
261
 	// positional arguments in place.
209
 	if showKey {
262
 	if showKey {
210
-		str += " " + channel.key
263
+		result = append(result, channel.key)
211
 	}
264
 	}
212
 	if showUserLimit {
265
 	if showUserLimit {
213
-		str += " " + strconv.FormatUint(channel.userLimit, 10)
266
+		result = append(result, strconv.FormatUint(channel.userLimit, 10))
214
 	}
267
 	}
215
 
268
 
216
-	return str
269
+	return
217
 }
270
 }
218
 
271
 
219
 // IsFull returns true if this channel is at its' members limit.
272
 // IsFull returns true if this channel is at its' members limit.
220
 func (channel *Channel) IsFull() bool {
273
 func (channel *Channel) IsFull() bool {
274
+	channel.stateMutex.RLock()
275
+	defer channel.stateMutex.RUnlock()
221
 	return (channel.userLimit > 0) && (uint64(len(channel.members)) >= channel.userLimit)
276
 	return (channel.userLimit > 0) && (uint64(len(channel.members)) >= channel.userLimit)
222
 }
277
 }
223
 
278
 
229
 // Join joins the given client to this channel (if they can be joined).
284
 // Join joins the given client to this channel (if they can be joined).
230
 //TODO(dan): /SAJOIN and maybe a ForceJoin function?
285
 //TODO(dan): /SAJOIN and maybe a ForceJoin function?
231
 func (channel *Channel) Join(client *Client, key string) {
286
 func (channel *Channel) Join(client *Client, key string) {
232
-	channel.membersMutex.Lock()
233
-	defer channel.membersMutex.Unlock()
234
-	if channel.members.Has(client) {
287
+	if channel.hasClient(client) {
235
 		// already joined, no message needs to be sent
288
 		// already joined, no message needs to be sent
236
 		return
289
 		return
237
 	}
290
 	}
261
 
314
 
262
 	client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", client.nick, channel.name))
315
 	client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", client.nick, channel.name))
263
 
316
 
264
-	for member := range channel.members {
317
+	for _, member := range channel.Members() {
265
 		if member.capabilities.Has(caps.ExtendedJoin) {
318
 		if member.capabilities.Has(caps.ExtendedJoin) {
266
 			member.Send(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
319
 			member.Send(nil, client.nickMaskString, "JOIN", channel.name, client.account.Name, client.realname)
267
 		} else {
320
 		} else {
269
 		}
322
 		}
270
 	}
323
 	}
271
 
324
 
272
-	client.channels.Add(channel)
325
+	channel.stateMutex.Lock()
273
 	channel.members.Add(client)
326
 	channel.members.Add(client)
327
+	firstJoin := len(channel.members) == 1
328
+	channel.stateMutex.Unlock()
329
+	channel.regenerateMembersCache()
330
+
331
+	client.addChannel(channel)
274
 
332
 
275
 	// give channel mode if necessary
333
 	// give channel mode if necessary
276
 	var newChannel bool
334
 	var newChannel bool
281
 		chanReg := client.server.loadChannelNoMutex(tx, channel.nameCasefolded)
339
 		chanReg := client.server.loadChannelNoMutex(tx, channel.nameCasefolded)
282
 
340
 
283
 		if chanReg == nil {
341
 		if chanReg == nil {
284
-			if len(channel.members) == 1 {
342
+			if firstJoin {
343
+				channel.stateMutex.Lock()
285
 				channel.createdTime = time.Now()
344
 				channel.createdTime = time.Now()
286
 				channel.members[client][ChannelOperator] = true
345
 				channel.members[client][ChannelOperator] = true
346
+				channel.stateMutex.Unlock()
287
 				givenMode = &ChannelOperator
347
 				givenMode = &ChannelOperator
288
 				newChannel = true
348
 				newChannel = true
289
 			}
349
 			}
290
 		} else {
350
 		} else {
291
 			// we should only do this on registered channels
351
 			// we should only do this on registered channels
292
 			if client.account != nil && client.account.Name == chanReg.Founder {
352
 			if client.account != nil && client.account.Name == chanReg.Founder {
353
+				channel.stateMutex.Lock()
293
 				channel.members[client][ChannelFounder] = true
354
 				channel.members[client][ChannelFounder] = true
355
+				channel.stateMutex.Unlock()
294
 				givenMode = &ChannelFounder
356
 				givenMode = &ChannelFounder
295
 			}
357
 			}
296
-			if len(channel.members) == 1 {
358
+			if firstJoin {
297
 				// apply other details if new channel
359
 				// apply other details if new channel
360
+				channel.stateMutex.Lock()
298
 				channel.topic = chanReg.Topic
361
 				channel.topic = chanReg.Topic
299
 				channel.topicSetBy = chanReg.TopicSetBy
362
 				channel.topicSetBy = chanReg.TopicSetBy
300
 				channel.topicSetTime = chanReg.TopicSetTime
363
 				channel.topicSetTime = chanReg.TopicSetTime
309
 				for _, mask := range chanReg.Invitelist {
372
 				for _, mask := range chanReg.Invitelist {
310
 					channel.lists[InviteMask].Add(mask)
373
 					channel.lists[InviteMask].Add(mask)
311
 				}
374
 				}
375
+				channel.stateMutex.Unlock()
312
 			}
376
 			}
313
 		}
377
 		}
314
 		return nil
378
 		return nil
321
 	}
385
 	}
322
 	// don't sent topic when it's an entirely new channel
386
 	// don't sent topic when it's an entirely new channel
323
 	if !newChannel {
387
 	if !newChannel {
324
-		channel.getTopicNoMutex(client) // we already have Lock
388
+		channel.SendTopic(client)
325
 	}
389
 	}
326
-	channel.namesNoMutex(client)
390
+	channel.Names(client)
327
 	if givenMode != nil {
391
 	if givenMode != nil {
328
-		for member := range channel.members {
392
+		for _, member := range channel.Members() {
329
 			member.Send(nil, client.server.name, "MODE", channel.name, fmt.Sprintf("+%v", *givenMode), client.nick)
393
 			member.Send(nil, client.server.name, "MODE", channel.name, fmt.Sprintf("+%v", *givenMode), client.nick)
330
 		}
394
 		}
331
 	}
395
 	}
333
 
397
 
334
 // Part parts the given client from this channel, with the given message.
398
 // Part parts the given client from this channel, with the given message.
335
 func (channel *Channel) Part(client *Client, message string) {
399
 func (channel *Channel) Part(client *Client, message string) {
336
-	channel.membersMutex.Lock()
337
-	defer channel.membersMutex.Unlock()
338
-
339
-	if !channel.members.Has(client) {
400
+	if !channel.hasClient(client) {
340
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
401
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
341
 		return
402
 		return
342
 	}
403
 	}
343
 
404
 
344
-	for member := range channel.members {
405
+	for _, member := range channel.Members() {
345
 		member.Send(nil, client.nickMaskString, "PART", channel.name, message)
406
 		member.Send(nil, client.nickMaskString, "PART", channel.name, message)
346
 	}
407
 	}
347
-	channel.quitNoMutex(client)
408
+	channel.Quit(client)
348
 
409
 
349
 	client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", client.nick, channel.name))
410
 	client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", client.nick, channel.name))
350
 }
411
 }
351
 
412
 
352
-// GetTopic sends the channel topic to the given client.
353
-func (channel *Channel) GetTopic(client *Client) {
354
-	channel.membersMutex.RLock()
355
-	defer channel.membersMutex.RUnlock()
356
-
357
-	channel.getTopicNoMutex(client)
358
-}
359
-
360
-// GetTopic sends the channel topic to the given client without getting the membersMutex.
361
-// This is required because of channel joins.
362
-func (channel *Channel) getTopicNoMutex(client *Client) {
363
-	if !channel.members.Has(client) {
413
+// SendTopic sends the channel topic to the given client.
414
+func (channel *Channel) SendTopic(client *Client) {
415
+	if !channel.hasClient(client) {
364
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, "You're not on that channel")
416
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, client.nick, channel.name, "You're not on that channel")
365
 		return
417
 		return
366
 	}
418
 	}
367
 
419
 
368
-	if channel.topic == "" {
369
-		client.Send(nil, client.server.name, RPL_NOTOPIC, client.nick, channel.name, "No topic is set")
420
+	channel.stateMutex.RLock()
421
+	name := channel.name
422
+	topic := channel.topic
423
+	topicSetBy := channel.topicSetBy
424
+	topicSetTime := channel.topicSetTime
425
+	channel.stateMutex.RUnlock()
426
+
427
+	if topic == "" {
428
+		client.Send(nil, client.server.name, RPL_NOTOPIC, client.nick, name, "No topic is set")
370
 		return
429
 		return
371
 	}
430
 	}
372
 
431
 
373
-	client.Send(nil, client.server.name, RPL_TOPIC, client.nick, channel.name, channel.topic)
374
-	client.Send(nil, client.server.name, RPL_TOPICTIME, client.nick, channel.name, channel.topicSetBy, strconv.FormatInt(channel.topicSetTime.Unix(), 10))
432
+	client.Send(nil, client.server.name, RPL_TOPIC, client.nick, name, topic)
433
+	client.Send(nil, client.server.name, RPL_TOPICTIME, client.nick, name, topicSetBy, strconv.FormatInt(topicSetTime.Unix(), 10))
375
 }
434
 }
376
 
435
 
377
 // SetTopic sets the topic of this channel, if the client is allowed to do so.
436
 // SetTopic sets the topic of this channel, if the client is allowed to do so.
378
 func (channel *Channel) SetTopic(client *Client, topic string) {
437
 func (channel *Channel) SetTopic(client *Client, topic string) {
379
-	channel.membersMutex.RLock()
380
-	defer channel.membersMutex.RUnlock()
381
-
382
-	if !(client.flags[Operator] || channel.members.Has(client)) {
438
+	if !(client.flags[Operator] || channel.hasClient(client)) {
383
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
439
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
384
 		return
440
 		return
385
 	}
441
 	}
386
 
442
 
387
-	if channel.flags[OpOnlyTopic] && !channel.ClientIsAtLeast(client, ChannelOperator) {
443
+	if channel.HasMode(OpOnlyTopic) && !channel.ClientIsAtLeast(client, ChannelOperator) {
388
 		client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
444
 		client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
389
 		return
445
 		return
390
 	}
446
 	}
393
 		topic = topic[:client.server.limits.TopicLen]
449
 		topic = topic[:client.server.limits.TopicLen]
394
 	}
450
 	}
395
 
451
 
452
+	channel.stateMutex.Lock()
396
 	channel.topic = topic
453
 	channel.topic = topic
397
 	channel.topicSetBy = client.nickMaskString
454
 	channel.topicSetBy = client.nickMaskString
398
 	channel.topicSetTime = time.Now()
455
 	channel.topicSetTime = time.Now()
456
+	channel.stateMutex.Unlock()
399
 
457
 
400
-	for member := range channel.members {
401
-		member.Send(nil, client.nickMaskString, "TOPIC", channel.name, channel.topic)
458
+	for _, member := range channel.Members() {
459
+		member.Send(nil, client.nickMaskString, "TOPIC", channel.name, topic)
402
 	}
460
 	}
403
 
461
 
404
 	// update saved channel topic for registered chans
462
 	// update saved channel topic for registered chans
422
 
480
 
423
 // CanSpeak returns true if the client can speak on this channel.
481
 // CanSpeak returns true if the client can speak on this channel.
424
 func (channel *Channel) CanSpeak(client *Client) bool {
482
 func (channel *Channel) CanSpeak(client *Client) bool {
425
-	channel.membersMutex.RLock()
426
-	defer channel.membersMutex.RUnlock()
483
+	channel.stateMutex.RLock()
484
+	defer channel.stateMutex.RUnlock()
427
 
485
 
428
-	if channel.flags[NoOutside] && !channel.members.Has(client) {
486
+	_, hasClient := channel.members[client]
487
+	if channel.flags[NoOutside] && !hasClient {
429
 		return false
488
 		return false
430
 	}
489
 	}
431
-	if channel.flags[Moderated] && !channel.clientIsAtLeastNoMutex(client, Voice) {
490
+	if channel.flags[Moderated] && !channel.ClientIsAtLeast(client, Voice) {
432
 		return false
491
 		return false
433
 	}
492
 	}
434
 	if channel.flags[RegisteredOnly] && client.account == &NoAccount {
493
 	if channel.flags[RegisteredOnly] && client.account == &NoAccount {
449
 		return
508
 		return
450
 	}
509
 	}
451
 
510
 
452
-	channel.membersMutex.RLock()
453
-	defer channel.membersMutex.RUnlock()
454
-
455
 	// for STATUSMSG
511
 	// for STATUSMSG
456
 	var minPrefixMode Mode
512
 	var minPrefixMode Mode
457
 	if minPrefix != nil {
513
 	if minPrefix != nil {
458
 		minPrefixMode = *minPrefix
514
 		minPrefixMode = *minPrefix
459
 	}
515
 	}
460
-	for member := range channel.members {
516
+	for _, member := range channel.Members() {
461
 		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
517
 		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
462
 			// STATUSMSG
518
 			// STATUSMSG
463
 			continue
519
 			continue
505
 		return
561
 		return
506
 	}
562
 	}
507
 
563
 
508
-	channel.membersMutex.RLock()
509
-	defer channel.membersMutex.RUnlock()
510
-
511
 	// for STATUSMSG
564
 	// for STATUSMSG
512
 	var minPrefixMode Mode
565
 	var minPrefixMode Mode
513
 	if minPrefix != nil {
566
 	if minPrefix != nil {
514
 		minPrefixMode = *minPrefix
567
 		minPrefixMode = *minPrefix
515
 	}
568
 	}
516
-	for member := range channel.members {
569
+	for _, member := range channel.Members() {
517
 		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
570
 		if minPrefix != nil && !channel.ClientIsAtLeast(member, minPrefixMode) {
518
 			// STATUSMSG
571
 			// STATUSMSG
519
 			continue
572
 			continue
534
 	}
587
 	}
535
 }
588
 }
536
 
589
 
537
-func (channel *Channel) applyModeFlag(client *Client, mode Mode,
538
-	op ModeOp) bool {
539
-	if !channel.ClientIsAtLeast(client, ChannelOperator) {
540
-		client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
541
-		return false
542
-	}
543
-
544
-	switch op {
545
-	case Add:
546
-		if channel.flags[mode] {
547
-			return false
548
-		}
549
-		channel.flags[mode] = true
550
-		return true
551
-
552
-	case Remove:
553
-		if !channel.flags[mode] {
554
-			return false
555
-		}
556
-		delete(channel.flags, mode)
557
-		return true
558
-	}
559
-	return false
560
-}
561
-
562
 func (channel *Channel) applyModeMemberNoMutex(client *Client, mode Mode,
590
 func (channel *Channel) applyModeMemberNoMutex(client *Client, mode Mode,
563
 	op ModeOp, nick string) *ModeChange {
591
 	op ModeOp, nick string) *ModeChange {
564
-	// requires Lock()
565
-
566
 	if nick == "" {
592
 	if nick == "" {
567
 		//TODO(dan): shouldn't this be handled before it reaches this function?
593
 		//TODO(dan): shouldn't this be handled before it reaches this function?
568
 		client.Send(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
594
 		client.Send(nil, client.server.name, ERR_NEEDMOREPARAMS, "MODE", "Not enough parameters")
576
 		return nil
602
 		return nil
577
 	}
603
 	}
578
 
604
 
579
-	if !channel.members.Has(target) {
580
-		client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
581
-		return nil
605
+	channel.stateMutex.Lock()
606
+	modeset, exists := channel.members[target]
607
+	var already bool
608
+	if exists {
609
+		enable := op == Add
610
+		already = modeset[mode] == enable
611
+		modeset[mode] = enable
582
 	}
612
 	}
613
+	channel.stateMutex.Unlock()
583
 
614
 
584
-	switch op {
585
-	case Add:
586
-		if channel.members[target][mode] {
587
-			return nil
588
-		}
589
-		channel.members[target][mode] = true
590
-		return &ModeChange{
591
-			op:   Add,
592
-			mode: mode,
593
-			arg:  nick,
594
-		}
595
-
596
-	case Remove:
597
-		if !channel.members[target][mode] {
598
-			return nil
599
-		}
600
-		channel.members[target][mode] = false
615
+	if !exists {
616
+		client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
617
+		return nil
618
+	} else if already {
619
+		return nil
620
+	} else {
601
 		return &ModeChange{
621
 		return &ModeChange{
602
-			op:   Remove,
622
+			op:   op,
603
 			mode: mode,
623
 			mode: mode,
604
 			arg:  nick,
624
 			arg:  nick,
605
 		}
625
 		}
606
 	}
626
 	}
607
-	return nil
608
 }
627
 }
609
 
628
 
610
 // ShowMaskList shows the given list to the client.
629
 // ShowMaskList shows the given list to the client.
622
 		rplendoflist = RPL_ENDOFINVITELIST
641
 		rplendoflist = RPL_ENDOFINVITELIST
623
 	}
642
 	}
624
 
643
 
625
-	// send out responses
644
+	nick := client.getNick()
645
+	channel.stateMutex.RLock()
646
+	// XXX don't acquire any new locks in this section, besides Socket.Write
626
 	for mask := range channel.lists[mode].masks {
647
 	for mask := range channel.lists[mode].masks {
627
-		client.Send(nil, client.server.name, rpllist, client.nick, channel.name, mask)
648
+		client.Send(nil, client.server.name, rpllist, nick, channel.name, mask)
628
 	}
649
 	}
629
-	client.Send(nil, client.server.name, rplendoflist, client.nick, channel.name, "End of list")
650
+	channel.stateMutex.RUnlock()
651
+
652
+	client.Send(nil, client.server.name, rplendoflist, nick, channel.name, "End of list")
630
 }
653
 }
631
 
654
 
632
 func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask string) bool {
655
 func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask string) bool {
657
 	return false
680
 	return false
658
 }
681
 }
659
 
682
 
660
-// Quit removes the given client from the channel, and also updates friends with the latest client list.
661
-func (channel *Channel) Quit(client *Client, friends *ClientSet) {
662
-	channel.membersMutex.Lock()
663
-	defer channel.membersMutex.Unlock()
664
-
665
-	channel.quitNoMutex(client)
666
-
667
-	for friend := range channel.members {
668
-		friends.Add(friend)
669
-	}
670
-}
671
-
672
-func (channel *Channel) quitNoMutex(client *Client) {
683
+// Quit removes the given client from the channel
684
+func (channel *Channel) Quit(client *Client) {
685
+	channel.stateMutex.Lock()
673
 	channel.members.Remove(client)
686
 	channel.members.Remove(client)
674
-	client.channels.Remove(channel)
687
+	empty := len(channel.members) == 0
688
+	channel.stateMutex.Unlock()
689
+	channel.regenerateMembersCache()
690
+
691
+	client.removeChannel(channel)
675
 
692
 
676
-	if channel.isEmptyNoMutex() {
693
+	//TODO(slingamn) fold this operation into a channelmanager type
694
+	if empty {
677
 		channel.server.channels.Remove(channel)
695
 		channel.server.channels.Remove(channel)
678
 	}
696
 	}
679
 }
697
 }
680
 
698
 
681
-func (channel *Channel) kickNoMutex(client *Client, target *Client, comment string) {
682
-	// needs a Lock()
683
-
684
-	if !(client.flags[Operator] || channel.members.Has(client)) {
699
+func (channel *Channel) Kick(client *Client, target *Client, comment string) {
700
+	if !(client.flags[Operator] || channel.hasClient(client)) {
685
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
701
 		client.Send(nil, client.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
686
 		return
702
 		return
687
 	}
703
 	}
688
-	if !channel.clientIsAtLeastNoMutex(client, ChannelOperator) {
704
+	if !channel.ClientIsAtLeast(client, ChannelOperator) {
689
 		client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
705
 		client.Send(nil, client.server.name, ERR_CANNOTSENDTOCHAN, channel.name, "Cannot send to channel")
690
 		return
706
 		return
691
 	}
707
 	}
692
-	if !channel.members.Has(target) {
708
+	if !channel.hasClient(target) {
693
 		client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
709
 		client.Send(nil, client.server.name, ERR_USERNOTINCHANNEL, client.nick, channel.name, "They aren't on that channel")
694
 		return
710
 		return
695
 	}
711
 	}
712
+	if !channel.ClientHasPrivsOver(client, target) {
713
+		client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, channel.name, "You're not a channel operator")
714
+		return
715
+	}
696
 
716
 
697
-	if len(comment) > client.server.limits.KickLen {
698
-		comment = comment[:client.server.limits.KickLen]
717
+	kicklimit := client.server.getLimits().KickLen
718
+	if len(comment) > kicklimit {
719
+		comment = comment[:kicklimit]
699
 	}
720
 	}
700
 
721
 
701
-	for member := range channel.members {
702
-		member.Send(nil, client.nickMaskString, "KICK", channel.name, target.nick, comment)
722
+	clientMask := client.getNickMaskString()
723
+	targetNick := target.getNick()
724
+	for _, member := range channel.Members() {
725
+		member.Send(nil, clientMask, "KICK", channel.name, targetNick, comment)
703
 	}
726
 	}
704
-	channel.quitNoMutex(target)
727
+
728
+	channel.Quit(target)
705
 }
729
 }
706
 
730
 
707
 // Invite invites the given client to the channel, if the inviter can do so.
731
 // Invite invites the given client to the channel, if the inviter can do so.
711
 		return
735
 		return
712
 	}
736
 	}
713
 
737
 
714
-	channel.membersMutex.RLock()
715
-	defer channel.membersMutex.RUnlock()
716
-
717
-	if !channel.members.Has(inviter) {
738
+	if !channel.hasClient(inviter) {
718
 		inviter.Send(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
739
 		inviter.Send(nil, inviter.server.name, ERR_NOTONCHANNEL, channel.name, "You're not on that channel")
719
 		return
740
 		return
720
 	}
741
 	}
721
 
742
 
722
 	//TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list?
743
 	//TODO(dan): handle this more nicely, keep a list of last X invited channels on invitee rather than explicitly modifying the invite list?
723
 	if channel.flags[InviteOnly] {
744
 	if channel.flags[InviteOnly] {
724
-		channel.lists[InviteMask].Add(invitee.nickMaskCasefolded)
745
+		nmc := invitee.getNickCasefolded()
746
+		channel.stateMutex.Lock()
747
+		channel.lists[InviteMask].Add(nmc)
748
+		channel.stateMutex.Unlock()
725
 	}
749
 	}
726
 
750
 
727
-	// send invite-notify
728
-	for member := range channel.members {
751
+	for _, member := range channel.Members() {
729
 		if member.capabilities.Has(caps.InviteNotify) && member != inviter && member != invitee && channel.ClientIsAtLeast(member, Halfop) {
752
 		if member.capabilities.Has(caps.InviteNotify) && member != inviter && member != invitee && channel.ClientIsAtLeast(member, Halfop) {
730
-			member.Send(nil, inviter.nickMaskString, "INVITE", invitee.nick, channel.name)
753
+			member.Send(nil, inviter.getNickMaskString(), "INVITE", invitee.getNick(), channel.name)
731
 		}
754
 		}
732
 	}
755
 	}
733
 
756
 

+ 1
- 4
irc/chanserv.go Vedi File

105
 			server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
105
 			server.logger.Info("chanserv", fmt.Sprintf("Client %s registered channel %s", client.nick, channelName))
106
 			server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
106
 			server.snomasks.Send(sno.LocalChannels, fmt.Sprintf(ircfmt.Unescape("Channel registered $c[grey][$r%s$c[grey]] by $c[grey][$r%s$c[grey]]"), channelName, client.nickMaskString))
107
 
107
 
108
-			channelInfo.membersMutex.Lock()
109
-			defer channelInfo.membersMutex.Unlock()
110
-
111
 			// give them founder privs
108
 			// give them founder privs
112
 			change := channelInfo.applyModeMemberNoMutex(client, ChannelFounder, Add, client.nickCasefolded)
109
 			change := channelInfo.applyModeMemberNoMutex(client, ChannelFounder, Add, client.nickCasefolded)
113
 			if change != nil {
110
 			if change != nil {
114
 				//TODO(dan): we should change the name of String and make it return a slice here
111
 				//TODO(dan): we should change the name of String and make it return a slice here
115
 				//TODO(dan): unify this code with code in modes.go
112
 				//TODO(dan): unify this code with code in modes.go
116
 				args := append([]string{channelName}, strings.Split(change.String(), " ")...)
113
 				args := append([]string{channelName}, strings.Split(change.String(), " ")...)
117
-				for member := range channelInfo.members {
114
+				for _, member := range channelInfo.Members() {
118
 					member.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "MODE", args...)
115
 					member.Send(nil, fmt.Sprintf("ChanServ!services@%s", client.server.name), "MODE", args...)
119
 				}
116
 				}
120
 			}
117
 			}

+ 34
- 8
irc/client.go Vedi File

14
 	"strconv"
14
 	"strconv"
15
 	"strings"
15
 	"strings"
16
 	"sync"
16
 	"sync"
17
+	"sync/atomic"
17
 	"time"
18
 	"time"
18
 
19
 
19
 	"github.com/goshuirc/irc-go/ircfmt"
20
 	"github.com/goshuirc/irc-go/ircfmt"
55
 	idletimer          *IdleTimer
56
 	idletimer          *IdleTimer
56
 	isDestroyed        bool
57
 	isDestroyed        bool
57
 	isQuitting         bool
58
 	isQuitting         bool
59
+	maxlenTags         uint32
60
+	maxlenRest         uint32
58
 	nick               string
61
 	nick               string
59
 	nickCasefolded     string
62
 	nickCasefolded     string
60
 	nickMaskCasefolded string
63
 	nickMaskCasefolded string
162
 // command goroutine
165
 // command goroutine
163
 //
166
 //
164
 
167
 
165
-func (client *Client) maxlens() (int, int) {
168
+func (client *Client) recomputeMaxlens() (int, int) {
166
 	maxlenTags := 512
169
 	maxlenTags := 512
167
 	maxlenRest := 512
170
 	maxlenRest := 512
168
 	if client.capabilities.Has(caps.MessageTags) {
171
 	if client.capabilities.Has(caps.MessageTags) {
175
 		}
178
 		}
176
 		maxlenRest = limits.LineLen.Rest
179
 		maxlenRest = limits.LineLen.Rest
177
 	}
180
 	}
181
+
182
+	atomic.StoreUint32(&client.maxlenTags, uint32(maxlenTags))
183
+	atomic.StoreUint32(&client.maxlenRest, uint32(maxlenRest))
184
+
178
 	return maxlenTags, maxlenRest
185
 	return maxlenTags, maxlenRest
179
 }
186
 }
180
 
187
 
188
+// allow these negotiated length limits to be read without locks; this is a convenience
189
+// so that Client.Send doesn't have to acquire any Client locks
190
+func (client *Client) maxlens() (int, int) {
191
+	return int(atomic.LoadUint32(&client.maxlenTags)), int(atomic.LoadUint32(&client.maxlenRest))
192
+}
193
+
181
 func (client *Client) run() {
194
 func (client *Client) run() {
182
 	var err error
195
 	var err error
183
 	var isExiting bool
196
 	var isExiting bool
192
 	client.rawHostname = utils.AddrLookupHostname(client.socket.conn.RemoteAddr())
205
 	client.rawHostname = utils.AddrLookupHostname(client.socket.conn.RemoteAddr())
193
 
206
 
194
 	for {
207
 	for {
208
+		maxlenTags, maxlenRest := client.recomputeMaxlens()
209
+
195
 		line, err = client.socket.Read()
210
 		line, err = client.socket.Read()
196
 		if err != nil {
211
 		if err != nil {
197
 			client.Quit("connection closed")
212
 			client.Quit("connection closed")
198
 			break
213
 			break
199
 		}
214
 		}
200
 
215
 
201
-		maxlenTags, maxlenRest := client.maxlens()
202
-
203
 		client.server.logger.Debug("userinput ", client.nick, "<- ", line)
216
 		client.server.logger.Debug("userinput ", client.nick, "<- ", line)
204
 
217
 
205
 		msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
218
 		msg, err = ircmsg.ParseLineMaxLen(line, maxlenTags, maxlenRest)
338
 		friends.Add(client)
351
 		friends.Add(client)
339
 	}
352
 	}
340
 
353
 
341
-	for channel := range client.channels {
342
-		channel.membersMutex.RLock()
343
-		for member := range channel.members {
354
+	for _, channel := range client.Channels() {
355
+		for _, member := range channel.Members() {
344
 			// make sure they have all the required caps
356
 			// make sure they have all the required caps
345
 			hasCaps = true
357
 			hasCaps = true
346
 			for _, capab := range capabs {
358
 			for _, capab := range capabs {
353
 				friends.Add(member)
365
 				friends.Add(member)
354
 			}
366
 			}
355
 		}
367
 		}
356
-		channel.membersMutex.RUnlock()
357
 	}
368
 	}
358
 	return friends
369
 	return friends
359
 }
370
 }
527
 	// clean up channels
538
 	// clean up channels
528
 	client.server.channelJoinPartMutex.Lock()
539
 	client.server.channelJoinPartMutex.Lock()
529
 	for channel := range client.channels {
540
 	for channel := range client.channels {
530
-		channel.Quit(client, &friends)
541
+		channel.Quit(client)
542
+		for _, member := range channel.Members() {
543
+			friends.Add(member)
544
+		}
531
 	}
545
 	}
532
 	client.server.channelJoinPartMutex.Unlock()
546
 	client.server.channelJoinPartMutex.Unlock()
533
 
547
 
663
 		client.Send(nil, client.server.name, "NOTICE", client.nick, line)
677
 		client.Send(nil, client.server.name, "NOTICE", client.nick, line)
664
 	}
678
 	}
665
 }
679
 }
680
+
681
+func (client *Client) addChannel(channel *Channel) {
682
+	client.stateMutex.Lock()
683
+	client.channels[channel] = true
684
+	client.stateMutex.Unlock()
685
+}
686
+
687
+func (client *Client) removeChannel(channel *Channel) {
688
+	client.stateMutex.Lock()
689
+	delete(client.channels, channel)
690
+	client.stateMutex.Unlock()
691
+}

+ 50
- 16
irc/client_lookup_set.go Vedi File

227
 
227
 
228
 // UserMaskSet holds a set of client masks and lets you match  hostnames to them.
228
 // UserMaskSet holds a set of client masks and lets you match  hostnames to them.
229
 type UserMaskSet struct {
229
 type UserMaskSet struct {
230
+	sync.RWMutex
230
 	masks  map[string]bool
231
 	masks  map[string]bool
231
 	regexp *regexp.Regexp
232
 	regexp *regexp.Regexp
232
 }
233
 }
245
 		log.Println(fmt.Sprintf("ERROR: Could not add mask to usermaskset: [%s]", mask))
246
 		log.Println(fmt.Sprintf("ERROR: Could not add mask to usermaskset: [%s]", mask))
246
 		return false
247
 		return false
247
 	}
248
 	}
248
-	if set.masks[casefoldedMask] {
249
+
250
+	set.Lock()
251
+	already := set.masks[casefoldedMask]
252
+	set.masks[casefoldedMask] = true
253
+	set.Unlock()
254
+
255
+	if already {
249
 		return false
256
 		return false
257
+	} else {
258
+		set.setRegexp()
259
+		return true
250
 	}
260
 	}
251
-	set.masks[casefoldedMask] = true
252
-	set.setRegexp()
253
-	return true
254
 }
261
 }
255
 
262
 
256
 // AddAll adds the given masks to this set.
263
 // AddAll adds the given masks to this set.
257
 func (set *UserMaskSet) AddAll(masks []string) (added bool) {
264
 func (set *UserMaskSet) AddAll(masks []string) (added bool) {
265
+	set.Lock()
266
+	defer set.Unlock()
267
+
258
 	for _, mask := range masks {
268
 	for _, mask := range masks {
259
 		if !added && !set.masks[mask] {
269
 		if !added && !set.masks[mask] {
260
 			added = true
270
 			added = true
267
 
277
 
268
 // Remove removes the given mask from this set.
278
 // Remove removes the given mask from this set.
269
 func (set *UserMaskSet) Remove(mask string) bool {
279
 func (set *UserMaskSet) Remove(mask string) bool {
270
-	if !set.masks[mask] {
280
+	set.Lock()
281
+	already := !set.masks[mask]
282
+	delete(set.masks, mask)
283
+	set.Unlock()
284
+
285
+	if !already {
271
 		return false
286
 		return false
287
+	} else {
288
+		set.setRegexp()
289
+		return true
272
 	}
290
 	}
273
-	delete(set.masks, mask)
274
-	set.setRegexp()
275
-	return true
276
 }
291
 }
277
 
292
 
278
 // Match matches the given n!u@h.
293
 // Match matches the given n!u@h.
279
 func (set *UserMaskSet) Match(userhost string) bool {
294
 func (set *UserMaskSet) Match(userhost string) bool {
280
-	if set.regexp == nil {
295
+	set.RLock()
296
+	regexp := set.regexp
297
+	set.RUnlock()
298
+
299
+	if regexp == nil {
281
 		return false
300
 		return false
282
 	}
301
 	}
283
-	return set.regexp.MatchString(userhost)
302
+	return regexp.MatchString(userhost)
284
 }
303
 }
285
 
304
 
286
 // String returns the masks in this set.
305
 // String returns the masks in this set.
287
 func (set *UserMaskSet) String() string {
306
 func (set *UserMaskSet) String() string {
307
+	set.RLock()
288
 	masks := make([]string, len(set.masks))
308
 	masks := make([]string, len(set.masks))
289
 	index := 0
309
 	index := 0
290
 	for mask := range set.masks {
310
 	for mask := range set.masks {
291
 		masks[index] = mask
311
 		masks[index] = mask
292
 		index++
312
 		index++
293
 	}
313
 	}
314
+	set.RUnlock()
294
 	return strings.Join(masks, " ")
315
 	return strings.Join(masks, " ")
295
 }
316
 }
296
 
317
 
318
+func (set *UserMaskSet) Length() int {
319
+	set.RLock()
320
+	defer set.RUnlock()
321
+	return len(set.masks)
322
+}
323
+
297
 // setRegexp generates a regular expression from the set of user mask
324
 // setRegexp generates a regular expression from the set of user mask
298
 // strings. Masks are split at the two types of wildcards, `*` and
325
 // strings. Masks are split at the two types of wildcards, `*` and
299
 // `?`. All the pieces are meta-escaped. `*` is replaced with `.*`,
326
 // `?`. All the pieces are meta-escaped. `*` is replaced with `.*`,
301
 // parts are re-joined and finally all masks are joined into a big
328
 // parts are re-joined and finally all masks are joined into a big
302
 // or-expression.
329
 // or-expression.
303
 func (set *UserMaskSet) setRegexp() {
330
 func (set *UserMaskSet) setRegexp() {
304
-	if len(set.masks) == 0 {
305
-		set.regexp = nil
306
-		return
307
-	}
331
+	var re *regexp.Regexp
308
 
332
 
333
+	set.RLock()
309
 	maskExprs := make([]string, len(set.masks))
334
 	maskExprs := make([]string, len(set.masks))
310
 	index := 0
335
 	index := 0
311
 	for mask := range set.masks {
336
 	for mask := range set.masks {
320
 			manyExprs[mindex] = strings.Join(oneExprs, ".")
345
 			manyExprs[mindex] = strings.Join(oneExprs, ".")
321
 		}
346
 		}
322
 		maskExprs[index] = strings.Join(manyExprs, ".*")
347
 		maskExprs[index] = strings.Join(manyExprs, ".*")
348
+		index++
323
 	}
349
 	}
324
-	expr := "^" + strings.Join(maskExprs, "|") + "$"
325
-	set.regexp, _ = regexp.Compile(expr)
350
+	set.RUnlock()
351
+
352
+	if index > 0 {
353
+		expr := "^" + strings.Join(maskExprs, "|") + "$"
354
+		re, _ = regexp.Compile(expr)
355
+	}
356
+
357
+	set.Lock()
358
+	set.regexp = re
359
+	set.Unlock()
326
 }
360
 }

+ 94
- 0
irc/getters.go Vedi File

53
 	return client.nickCasefolded
53
 	return client.nickCasefolded
54
 }
54
 }
55
 
55
 
56
+func (client *Client) Username() string {
57
+	client.stateMutex.RLock()
58
+	defer client.stateMutex.RUnlock()
59
+	return client.username
60
+}
61
+
62
+func (client *Client) Hostname() string {
63
+	client.stateMutex.RLock()
64
+	defer client.stateMutex.RUnlock()
65
+	return client.hostname
66
+}
67
+
68
+func (client *Client) Realname() string {
69
+	client.stateMutex.RLock()
70
+	defer client.stateMutex.RUnlock()
71
+	return client.realname
72
+}
73
+
56
 func (client *Client) Registered() bool {
74
 func (client *Client) Registered() bool {
57
 	client.stateMutex.RLock()
75
 	client.stateMutex.RLock()
58
 	defer client.stateMutex.RUnlock()
76
 	defer client.stateMutex.RUnlock()
64
 	defer client.stateMutex.RUnlock()
82
 	defer client.stateMutex.RUnlock()
65
 	return client.isDestroyed
83
 	return client.isDestroyed
66
 }
84
 }
85
+
86
+func (client *Client) HasMode(mode Mode) bool {
87
+	client.stateMutex.RLock()
88
+	defer client.stateMutex.RUnlock()
89
+	return client.flags[mode]
90
+}
91
+
92
+func (client *Client) Channels() (result []*Channel) {
93
+	client.stateMutex.RLock()
94
+	defer client.stateMutex.RUnlock()
95
+	length := len(client.channels)
96
+	result = make([]*Channel, length)
97
+	i := 0
98
+	for channel := range client.channels {
99
+		result[i] = channel
100
+		i++
101
+	}
102
+	return
103
+}
104
+
105
+func (channel *Channel) Name() string {
106
+	channel.stateMutex.RLock()
107
+	defer channel.stateMutex.RUnlock()
108
+	return channel.name
109
+}
110
+
111
+func (channel *Channel) Members() (result []*Client) {
112
+	channel.stateMutex.RLock()
113
+	defer channel.stateMutex.RUnlock()
114
+	return channel.membersCache
115
+}
116
+
117
+func (channel *Channel) UserLimit() uint64 {
118
+	channel.stateMutex.RLock()
119
+	defer channel.stateMutex.RUnlock()
120
+	return channel.userLimit
121
+}
122
+
123
+func (channel *Channel) setUserLimit(limit uint64) {
124
+	channel.stateMutex.Lock()
125
+	channel.userLimit = limit
126
+	channel.stateMutex.Unlock()
127
+}
128
+
129
+func (channel *Channel) Key() string {
130
+	channel.stateMutex.RLock()
131
+	defer channel.stateMutex.RUnlock()
132
+	return channel.key
133
+}
134
+
135
+func (channel *Channel) setKey(key string) {
136
+	channel.stateMutex.Lock()
137
+	channel.key = key
138
+	channel.stateMutex.Unlock()
139
+}
140
+
141
+func (channel *Channel) HasMode(mode Mode) bool {
142
+	channel.stateMutex.RLock()
143
+	defer channel.stateMutex.RUnlock()
144
+	return channel.flags[mode]
145
+}
146
+
147
+// set a channel mode, return whether it was already set
148
+func (channel *Channel) setMode(mode Mode, enable bool) (already bool) {
149
+	channel.stateMutex.Lock()
150
+	already = (channel.flags[mode] == enable)
151
+	if !already {
152
+		if enable {
153
+			channel.flags[mode] = true
154
+		} else {
155
+			delete(channel.flags, mode)
156
+		}
157
+	}
158
+	channel.stateMutex.Unlock()
159
+	return
160
+}

+ 33
- 58
irc/modes.go Vedi File

327
 // MODE <target> [<modestring> [<mode arguments>...]]
327
 // MODE <target> [<modestring> [<mode arguments>...]]
328
 func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
328
 func umodeHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
329
 	nickname, err := CasefoldName(msg.Params[0])
329
 	nickname, err := CasefoldName(msg.Params[0])
330
-
331
 	target := server.clients.Get(nickname)
330
 	target := server.clients.Get(nickname)
332
-
333
 	if err != nil || target == nil {
331
 	if err != nil || target == nil {
334
 		if len(msg.Params[0]) > 0 {
332
 		if len(msg.Params[0]) > 0 {
335
 			client.Send(nil, server.name, ERR_NOSUCHNICK, client.nick, msg.Params[0], "No such nick")
333
 			client.Send(nil, server.name, ERR_NOSUCHNICK, client.nick, msg.Params[0], "No such nick")
337
 		return false
335
 		return false
338
 	}
336
 	}
339
 
337
 
340
-	if client != target && msg.Command != "SAMODE" {
338
+	targetNick := target.getNick()
339
+	hasPrivs := client == target || msg.Command == "SAMODE"
340
+
341
+	if !hasPrivs {
341
 		if len(msg.Params) > 1 {
342
 		if len(msg.Params) > 1 {
342
 			client.Send(nil, server.name, ERR_USERSDONTMATCH, client.nick, "Can't change modes for other users")
343
 			client.Send(nil, server.name, ERR_USERSDONTMATCH, client.nick, "Can't change modes for other users")
343
 		} else {
344
 		} else {
367
 	}
368
 	}
368
 
369
 
369
 	if len(applied) > 0 {
370
 	if len(applied) > 0 {
370
-		client.Send(nil, client.nickMaskString, "MODE", target.nick, applied.String())
371
-	} else if client == target {
372
-		client.Send(nil, target.nickMaskString, RPL_UMODEIS, target.nick, target.ModeString())
371
+		client.Send(nil, client.nickMaskString, "MODE", targetNick, applied.String())
372
+	} else if hasPrivs {
373
+		client.Send(nil, target.nickMaskString, RPL_UMODEIS, targetNick, target.ModeString())
373
 		if client.flags[LocalOperator] || client.flags[Operator] {
374
 		if client.flags[LocalOperator] || client.flags[Operator] {
374
 			masks := server.snomasks.String(client)
375
 			masks := server.snomasks.String(client)
375
 			if 0 < len(masks) {
376
 			if 0 < len(masks) {
376
-				client.Send(nil, target.nickMaskString, RPL_SNOMASKIS, target.nick, masks, "Server notice masks")
377
+				client.Send(nil, target.nickMaskString, RPL_SNOMASKIS, targetNick, masks, "Server notice masks")
377
 			}
378
 			}
378
 		}
379
 		}
379
 	}
380
 	}
475
 }
476
 }
476
 
477
 
477
 // ApplyChannelModeChanges applies a given set of mode changes.
478
 // ApplyChannelModeChanges applies a given set of mode changes.
478
-func ApplyChannelModeChanges(channel *Channel, client *Client, isSamode bool, changes ModeChanges) ModeChanges {
479
+func (channel *Channel) ApplyChannelModeChanges(client *Client, isSamode bool, changes ModeChanges) ModeChanges {
479
 	// so we only output one warning for each list type when full
480
 	// so we only output one warning for each list type when full
480
 	listFullWarned := make(map[Mode]bool)
481
 	listFullWarned := make(map[Mode]bool)
481
 
482
 
482
-	clientIsOp := channel.clientIsAtLeastNoMutex(client, ChannelOperator)
483
+	clientIsOp := channel.ClientIsAtLeast(client, ChannelOperator)
483
 	var alreadySentPrivError bool
484
 	var alreadySentPrivError bool
484
 
485
 
485
 	applied := make(ModeChanges, 0)
486
 	applied := make(ModeChanges, 0)
498
 		switch change.mode {
499
 		switch change.mode {
499
 		case BanMask, ExceptMask, InviteMask:
500
 		case BanMask, ExceptMask, InviteMask:
500
 			mask := change.arg
501
 			mask := change.arg
501
-			list := channel.lists[change.mode]
502
-			if list == nil {
503
-				// This should never happen, but better safe than panicky.
504
-				client.Send(nil, client.server.name, ERR_UNKNOWNERROR, client.nick, "MODE", "Could not complete MODE command")
505
-				return changes
506
-			}
507
 
502
 
508
 			if (change.op == List) || (mask == "") {
503
 			if (change.op == List) || (mask == "") {
509
 				channel.ShowMaskList(client, change.mode)
504
 				channel.ShowMaskList(client, change.mode)
518
 
513
 
519
 			switch change.op {
514
 			switch change.op {
520
 			case Add:
515
 			case Add:
521
-				if len(list.masks) >= client.server.limits.ChanListModes {
516
+				if channel.lists[change.mode].Length() >= client.server.getLimits().ChanListModes {
522
 					if !listFullWarned[change.mode] {
517
 					if !listFullWarned[change.mode] {
523
-						client.Send(nil, client.server.name, ERR_BANLISTFULL, client.nick, channel.name, change.mode.String(), "Channel list is full")
518
+						client.Send(nil, client.server.name, ERR_BANLISTFULL, client.getNick(), channel.Name(), change.mode.String(), "Channel list is full")
524
 						listFullWarned[change.mode] = true
519
 						listFullWarned[change.mode] = true
525
 					}
520
 					}
526
 					continue
521
 					continue
527
 				}
522
 				}
528
 
523
 
529
-				list.Add(mask)
524
+				channel.lists[change.mode].Add(mask)
530
 				applied = append(applied, change)
525
 				applied = append(applied, change)
531
 
526
 
532
 			case Remove:
527
 			case Remove:
533
-				list.Remove(mask)
528
+				channel.lists[change.mode].Remove(mask)
534
 				applied = append(applied, change)
529
 				applied = append(applied, change)
535
 			}
530
 			}
536
 
531
 
539
 			case Add:
534
 			case Add:
540
 				val, err := strconv.ParseUint(change.arg, 10, 64)
535
 				val, err := strconv.ParseUint(change.arg, 10, 64)
541
 				if err == nil {
536
 				if err == nil {
542
-					channel.userLimit = val
537
+					channel.setUserLimit(val)
543
 					applied = append(applied, change)
538
 					applied = append(applied, change)
544
 				}
539
 				}
545
 
540
 
546
 			case Remove:
541
 			case Remove:
547
-				channel.userLimit = 0
542
+				channel.setUserLimit(0)
548
 				applied = append(applied, change)
543
 				applied = append(applied, change)
549
 			}
544
 			}
550
 
545
 
551
 		case Key:
546
 		case Key:
552
 			switch change.op {
547
 			switch change.op {
553
 			case Add:
548
 			case Add:
554
-				channel.key = change.arg
549
+				channel.setKey(change.arg)
555
 
550
 
556
 			case Remove:
551
 			case Remove:
557
-				channel.key = ""
552
+				channel.setKey("")
558
 			}
553
 			}
559
 			applied = append(applied, change)
554
 			applied = append(applied, change)
560
 
555
 
561
 		case InviteOnly, Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, Secret, ChanRoleplaying:
556
 		case InviteOnly, Moderated, NoOutside, OpOnlyTopic, RegisteredOnly, Secret, ChanRoleplaying:
562
-			switch change.op {
563
-			case Add:
564
-				if channel.flags[change.mode] {
565
-					continue
566
-				}
567
-				channel.flags[change.mode] = true
568
-				applied = append(applied, change)
557
+			if change.op == List {
558
+				continue
559
+			}
569
 
560
 
570
-			case Remove:
571
-				if !channel.flags[change.mode] {
572
-					continue
573
-				}
574
-				delete(channel.flags, change.mode)
561
+			already := channel.setMode(change.mode, change.op == Add)
562
+			if !already {
575
 				applied = append(applied, change)
563
 				applied = append(applied, change)
576
 			}
564
 			}
577
 
565
 
578
 		case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
566
 		case ChannelFounder, ChannelAdmin, ChannelOperator, Halfop, Voice:
567
+			if change.op == List {
568
+				continue
569
+			}
579
 			// make sure client has privs to edit the given prefix
570
 			// make sure client has privs to edit the given prefix
580
 			hasPrivs := isSamode
571
 			hasPrivs := isSamode
581
 
572
 
582
-			if !hasPrivs {
583
-				for _, mode := range ChannelPrivModes {
584
-					if channel.members[client][mode] {
585
-						hasPrivs = true
586
-
587
-						// Admins can't give other people Admin or remove it from others,
588
-						// standard for that channel mode, we worry about this later
589
-						if mode == ChannelAdmin && change.mode == ChannelAdmin {
590
-							hasPrivs = false
591
-						}
592
-
593
-						break
594
-					} else if mode == change.mode {
595
-						break
596
-					}
597
-				}
573
+			// Admins can't give other people Admin or remove it from others,
574
+			// standard for that channel mode, we worry about this later
575
+			if !hasPrivs && change.mode != ChannelAdmin {
576
+				hasPrivs = channel.ClientIsAtLeast(client, change.mode)
598
 			}
577
 			}
599
 
578
 
600
 			casefoldedName, err := CasefoldName(change.arg)
579
 			casefoldedName, err := CasefoldName(change.arg)
634
 		return false
613
 		return false
635
 	}
614
 	}
636
 
615
 
637
-	channel.membersMutex.Lock()
638
-	defer channel.membersMutex.Unlock()
639
-
640
 	// applied mode changes
616
 	// applied mode changes
641
 	applied := make(ModeChanges, 0)
617
 	applied := make(ModeChanges, 0)
642
 
618
 
654
 		}
630
 		}
655
 
631
 
656
 		// apply mode changes
632
 		// apply mode changes
657
-		applied = ApplyChannelModeChanges(channel, client, msg.Command == "SAMODE", changes)
633
+		applied = channel.ApplyChannelModeChanges(client, msg.Command == "SAMODE", changes)
658
 	}
634
 	}
659
 
635
 
660
 	// save changes to banlist/exceptlist/invexlist
636
 	// save changes to banlist/exceptlist/invexlist
707
 	if len(applied) > 0 {
683
 	if len(applied) > 0 {
708
 		//TODO(dan): we should change the name of String and make it return a slice here
684
 		//TODO(dan): we should change the name of String and make it return a slice here
709
 		args := append([]string{channel.name}, strings.Split(applied.String(), " ")...)
685
 		args := append([]string{channel.name}, strings.Split(applied.String(), " ")...)
710
-		for member := range channel.members {
686
+		for _, member := range channel.Members() {
711
 			member.Send(nil, client.nickMaskString, "MODE", args...)
687
 			member.Send(nil, client.nickMaskString, "MODE", args...)
712
 		}
688
 		}
713
 	} else {
689
 	} else {
714
-		//TODO(dan): we should just make ModeString return a slice here
715
-		args := append([]string{client.nick, channel.name}, strings.Split(channel.modeStringNoLock(client), " ")...)
690
+		args := append([]string{client.nick, channel.name}, channel.modeStrings(client)...)
716
 		client.Send(nil, client.nickMaskString, RPL_CHANNELMODEIS, args...)
691
 		client.Send(nil, client.nickMaskString, RPL_CHANNELMODEIS, args...)
717
 		client.Send(nil, client.nickMaskString, RPL_CHANNELCREATED, client.nick, channel.name, strconv.FormatInt(channel.createdTime.Unix(), 10))
692
 		client.Send(nil, client.nickMaskString, RPL_CHANNELCREATED, client.nick, channel.name, strconv.FormatInt(channel.createdTime.Unix(), 10))
718
 	}
693
 	}

+ 1
- 3
irc/roleplay.go Vedi File

88
 			return
88
 			return
89
 		}
89
 		}
90
 
90
 
91
-		channel.membersMutex.RLock()
92
-		for member := range channel.members {
91
+		for _, member := range channel.Members() {
93
 			if member == client && !client.capabilities.Has(caps.EchoMessage) {
92
 			if member == client && !client.capabilities.Has(caps.EchoMessage) {
94
 				continue
93
 				continue
95
 			}
94
 			}
96
 			member.Send(nil, source, "PRIVMSG", channel.name, message)
95
 			member.Send(nil, source, "PRIVMSG", channel.name, message)
97
 		}
96
 		}
98
-		channel.membersMutex.RUnlock()
99
 	} else {
97
 	} else {
100
 		target, err := CasefoldName(targetString)
98
 		target, err := CasefoldName(targetString)
101
 		user := server.clients.Get(target)
99
 		user := server.clients.Get(target)

+ 23
- 67
irc/server.go Vedi File

587
 		return false
587
 		return false
588
 	}
588
 	}
589
 
589
 
590
-	channel.membersMutex.Lock()
591
-	defer channel.membersMutex.Unlock()
592
-
593
 	casefoldedNewName, err := CasefoldChannel(newName)
590
 	casefoldedNewName, err := CasefoldChannel(newName)
594
 	if err != nil {
591
 	if err != nil {
595
 		//TODO(dan): Change this to ERR_CANNOTRENAME
592
 		//TODO(dan): Change this to ERR_CANNOTRENAME
646
 	})
643
 	})
647
 
644
 
648
 	// send RENAME messages
645
 	// send RENAME messages
649
-	for mcl := range channel.members {
646
+	for _, mcl := range channel.Members() {
650
 		if mcl.capabilities.Has(caps.Rename) {
647
 		if mcl.capabilities.Has(caps.Rename) {
651
 			mcl.Send(nil, client.nickMaskString, "RENAME", oldName, newName, reason)
648
 			mcl.Send(nil, client.nickMaskString, "RENAME", oldName, newName, reason)
652
 		} else {
649
 		} else {
755
 	if len(msg.Params) > 1 {
752
 	if len(msg.Params) > 1 {
756
 		channel.SetTopic(client, msg.Params[1])
753
 		channel.SetTopic(client, msg.Params[1])
757
 	} else {
754
 	} else {
758
-		channel.GetTopic(client)
755
+		channel.SendTopic(client)
759
 	}
756
 	}
760
 	return false
757
 	return false
761
 }
758
 }
964
 func (client *Client) WhoisChannelsNames(target *Client) []string {
961
 func (client *Client) WhoisChannelsNames(target *Client) []string {
965
 	isMultiPrefix := target.capabilities.Has(caps.MultiPrefix)
962
 	isMultiPrefix := target.capabilities.Has(caps.MultiPrefix)
966
 	var chstrs []string
963
 	var chstrs []string
967
-	index := 0
968
-	for channel := range client.channels {
969
-		channel.membersMutex.RLock()
970
-		defer channel.membersMutex.RUnlock()
971
-
964
+	for _, channel := range client.Channels() {
972
 		// channel is secret and the target can't see it
965
 		// channel is secret and the target can't see it
973
-		if !target.flags[Operator] && channel.flags[Secret] && !channel.members.Has(target) {
966
+		if !target.flags[Operator] && channel.HasMode(Secret) && !channel.hasClient(target) {
974
 			continue
967
 			continue
975
 		}
968
 		}
976
-		chstrs = append(chstrs, channel.members[client].Prefixes(isMultiPrefix)+channel.name)
977
-		index++
969
+		chstrs = append(chstrs, channel.ClientPrefixes(client, isMultiPrefix)+channel.name)
978
 	}
970
 	}
979
 	return chstrs
971
 	return chstrs
980
 }
972
 }
1050
 	client.Send(nil, client.server.name, RPL_WHOISIDLE, client.nick, target.nick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), "seconds idle, signon time")
1042
 	client.Send(nil, client.server.name, RPL_WHOISIDLE, client.nick, target.nick, strconv.FormatUint(target.IdleSeconds(), 10), strconv.FormatInt(target.SignonTime(), 10), "seconds idle, signon time")
1051
 }
1043
 }
1052
 
1044
 
1053
-// RplWhoReplyNoMutex returns the WHO reply between one user and another channel/user.
1045
+// rplWhoReply returns the WHO reply between one user and another channel/user.
1054
 // <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
1046
 // <channel> <user> <host> <server> <nick> ( "H" / "G" ) ["*"] [ ( "@" / "+" ) ]
1055
 // :<hopcount> <real name>
1047
 // :<hopcount> <real name>
1056
-func (target *Client) RplWhoReplyNoMutex(channel *Channel, client *Client) {
1048
+func (target *Client) rplWhoReply(channel *Channel, client *Client) {
1057
 	channelName := "*"
1049
 	channelName := "*"
1058
 	flags := ""
1050
 	flags := ""
1059
 
1051
 
1060
-	if client.flags[Away] {
1052
+	if client.HasMode(Away) {
1061
 		flags = "G"
1053
 		flags = "G"
1062
 	} else {
1054
 	} else {
1063
 		flags = "H"
1055
 		flags = "H"
1064
 	}
1056
 	}
1065
-	if client.flags[Operator] {
1057
+	if client.HasMode(Operator) {
1066
 		flags += "*"
1058
 		flags += "*"
1067
 	}
1059
 	}
1068
 
1060
 
1069
 	if channel != nil {
1061
 	if channel != nil {
1070
-		flags += channel.members[client].Prefixes(target.capabilities.Has(caps.MultiPrefix))
1062
+		flags += channel.ClientPrefixes(client, target.capabilities.Has(caps.MultiPrefix))
1071
 		channelName = channel.name
1063
 		channelName = channel.name
1072
 	}
1064
 	}
1073
-	target.Send(nil, target.server.name, RPL_WHOREPLY, target.nick, channelName, client.username, client.hostname, client.server.name, client.nick, flags, strconv.Itoa(client.hops)+" "+client.realname)
1065
+	target.Send(nil, target.server.name, RPL_WHOREPLY, target.nick, channelName, client.Username(), client.Hostname(), client.server.name, client.getNick(), flags, strconv.Itoa(client.hops)+" "+client.Realname())
1074
 }
1066
 }
1075
 
1067
 
1076
 func whoChannel(client *Client, channel *Channel, friends ClientSet) {
1068
 func whoChannel(client *Client, channel *Channel, friends ClientSet) {
1077
-	channel.membersMutex.RLock()
1078
-	defer channel.membersMutex.RUnlock()
1079
-
1080
-	for member := range channel.members {
1069
+	for _, member := range channel.Members() {
1081
 		if !client.flags[Invisible] || friends[client] {
1070
 		if !client.flags[Invisible] || friends[client] {
1082
-			client.RplWhoReplyNoMutex(channel, member)
1071
+			client.rplWhoReply(channel, member)
1083
 		}
1072
 		}
1084
 	}
1073
 	}
1085
 }
1074
 }
1120
 		}
1109
 		}
1121
 	} else {
1110
 	} else {
1122
 		for mclient := range server.clients.FindAll(mask) {
1111
 		for mclient := range server.clients.FindAll(mask) {
1123
-			client.RplWhoReplyNoMutex(nil, mclient)
1112
+			client.rplWhoReply(nil, mclient)
1124
 		}
1113
 		}
1125
 	}
1114
 	}
1126
 
1115
 
1792
 			continue
1781
 			continue
1793
 		}
1782
 		}
1794
 
1783
 
1795
-		// make sure client has privs to kick the given user
1796
-		//TODO(dan): split this into a separate function that checks if users have privs
1797
-		// over other users, useful for things like -aoh as well
1798
-		channel.membersMutex.Lock()
1799
-
1800
-		var hasPrivs bool
1801
-		for _, mode := range ChannelPrivModes {
1802
-			if channel.members[client][mode] {
1803
-				hasPrivs = true
1804
-
1805
-				// admins cannot kick other admins
1806
-				if mode == ChannelAdmin && channel.members[target][ChannelAdmin] {
1807
-					hasPrivs = false
1808
-				}
1809
-
1810
-				break
1811
-			} else if channel.members[target][mode] {
1812
-				break
1813
-			}
1784
+		if comment == "" {
1785
+			comment = nickname
1814
 		}
1786
 		}
1815
-
1816
-		if hasPrivs {
1817
-			if comment == "" {
1818
-				comment = nickname
1819
-			}
1820
-			channel.kickNoMutex(client, target, comment)
1821
-		} else {
1822
-			client.Send(nil, client.server.name, ERR_CHANOPRIVSNEEDED, chname, "You're not a channel operator")
1823
-		}
1824
-
1825
-		channel.membersMutex.Unlock()
1787
+		channel.Kick(client, target, comment)
1826
 	}
1788
 	}
1827
 	return false
1789
 	return false
1828
 }
1790
 }
1837
 
1799
 
1838
 // Matches checks whether the given channel matches our matches.
1800
 // Matches checks whether the given channel matches our matches.
1839
 func (matcher *elistMatcher) Matches(channel *Channel) bool {
1801
 func (matcher *elistMatcher) Matches(channel *Channel) bool {
1840
-	channel.membersMutex.RLock()
1841
-	defer channel.membersMutex.RUnlock()
1842
-
1843
 	if matcher.MinClientsActive {
1802
 	if matcher.MinClientsActive {
1844
-		if len(channel.members) < matcher.MinClients {
1803
+		if len(channel.Members()) < matcher.MinClients {
1845
 			return false
1804
 			return false
1846
 		}
1805
 		}
1847
 	}
1806
 	}
1848
 
1807
 
1849
 	if matcher.MaxClientsActive {
1808
 	if matcher.MaxClientsActive {
1850
-		if matcher.MaxClients < len(channel.members) {
1809
+		if len(channel.Members()) < len(channel.members) {
1851
 			return false
1810
 			return false
1852
 		}
1811
 		}
1853
 	}
1812
 	}
1933
 
1892
 
1934
 // RplList returns the RPL_LIST numeric for the given channel.
1893
 // RplList returns the RPL_LIST numeric for the given channel.
1935
 func (target *Client) RplList(channel *Channel) {
1894
 func (target *Client) RplList(channel *Channel) {
1936
-	channel.membersMutex.RLock()
1937
-	defer channel.membersMutex.RUnlock()
1938
-
1939
 	// get the correct number of channel members
1895
 	// get the correct number of channel members
1940
 	var memberCount int
1896
 	var memberCount int
1941
-	if target.flags[Operator] || channel.members.Has(target) {
1942
-		memberCount = len(channel.members)
1897
+	if target.flags[Operator] || channel.hasClient(target) {
1898
+		memberCount = len(channel.Members())
1943
 	} else {
1899
 	} else {
1944
-		for member := range channel.members {
1945
-			if !member.flags[Invisible] {
1900
+		for _, member := range channel.Members() {
1901
+			if !member.HasMode(Invisible) {
1946
 				memberCount++
1902
 				memberCount++
1947
 			}
1903
 			}
1948
 		}
1904
 		}

+ 0
- 10
irc/types.go Vedi File

139
 
139
 
140
 // ChannelSet is a set of channels.
140
 // ChannelSet is a set of channels.
141
 type ChannelSet map[*Channel]bool
141
 type ChannelSet map[*Channel]bool
142
-
143
-// Add adds the given channel to this set.
144
-func (channels ChannelSet) Add(channel *Channel) {
145
-	channels[channel] = true
146
-}
147
-
148
-// Remove removes the given channel from this set.
149
-func (channels ChannelSet) Remove(channel *Channel) {
150
-	delete(channels, channel)
151
-}

Loading…
Annulla
Salva