Browse Source

fix #1507

Registered channels should be eagerly created on startup, and should
remain (and be visible in LIST) even when they have no members.
tags/v2.6.0-rc1
Shivaram Lingamneni 3 years ago
parent
commit
cc6be14c1d
3 changed files with 82 additions and 23 deletions
  1. 30
    11
      irc/channel.go
  2. 50
    9
      irc/channelmanager.go
  3. 2
    3
      irc/handlers.go

+ 30
- 11
irc/channel.go View File

@@ -210,8 +210,6 @@ func (channel *Channel) MarkDirty(dirtyBits uint) {
210 210
 // ChannelManager's lock (that way, no one can join and make the channel dirty again
211 211
 // between this method exiting and the actual deletion).
212 212
 func (channel *Channel) IsClean() bool {
213
-	config := channel.server.Config()
214
-
215 213
 	if !channel.writerSemaphore.TryAcquire() {
216 214
 		// a database write (which may fail) is in progress, the channel cannot be cleaned up
217 215
 		return false
@@ -223,13 +221,8 @@ func (channel *Channel) IsClean() bool {
223 221
 	if len(channel.members) != 0 {
224 222
 		return false
225 223
 	}
226
-	if channel.registeredFounder == "" {
227
-		return true
228
-	}
229
-	// a registered channel must be fully written to the DB,
230
-	// and not set to ephemeral history (#704)
231
-	return channel.dirtyBits == 0 &&
232
-		channelHistoryStatus(config, true, channel.settings.History) != HistoryEphemeral
224
+	// see #1507 and #704 among others; registered channels should never be removed
225
+	return channel.registeredFounder == ""
233 226
 }
234 227
 
235 228
 func (channel *Channel) wakeWriter() {
@@ -793,7 +786,7 @@ func (channel *Channel) Join(client *Client, key string, isSajoin bool, rb *Resp
793 786
 		return joinErr, ""
794 787
 	}
795 788
 
796
-	client.server.logger.Debug("join", fmt.Sprintf("%s joined channel %s", details.nick, chname))
789
+	client.server.logger.Debug("channels", fmt.Sprintf("%s joined channel %s", details.nick, chname))
797 790
 
798 791
 	givenMode := func() (givenMode modes.Mode) {
799 792
 		channel.joinPartMutex.Lock()
@@ -1033,7 +1026,7 @@ func (channel *Channel) Part(client *Client, message string, rb *ResponseBuffer)
1033 1026
 		}, details.account)
1034 1027
 	}
1035 1028
 
1036
-	client.server.logger.Debug("part", fmt.Sprintf("%s left channel %s", details.nick, chname))
1029
+	client.server.logger.Debug("channels", fmt.Sprintf("%s left channel %s", details.nick, chname))
1037 1030
 }
1038 1031
 
1039 1032
 // Resume is called after a successful global resume to:
@@ -1539,6 +1532,32 @@ func (channel *Channel) Kick(client *Client, target *Client, comment string, rb
1539 1532
 	channel.Quit(target)
1540 1533
 }
1541 1534
 
1535
+// handle a purge: kick everyone off the channel, clean up all the pointers between
1536
+// *Channel and *Client
1537
+func (channel *Channel) Purge(source string) {
1538
+	if source == "" {
1539
+		source = channel.server.name
1540
+	}
1541
+
1542
+	channel.stateMutex.Lock()
1543
+	chname := channel.name
1544
+	members := channel.membersCache
1545
+	channel.membersCache = nil
1546
+	channel.members = make(MemberSet)
1547
+	// TODO try to prevent Purge racing against (pending) Join?
1548
+	channel.stateMutex.Unlock()
1549
+
1550
+	now := time.Now().UTC()
1551
+	for _, member := range members {
1552
+		tnick := member.Nick()
1553
+		msgid := utils.GenerateSecretToken()
1554
+		for _, session := range member.Sessions() {
1555
+			session.sendFromClientInternal(false, now, msgid, source, "*", nil, "KICK", chname, tnick, member.t("This channel has been purged by the server administrators and cannot be used"))
1556
+		}
1557
+		member.removeChannel(channel)
1558
+	}
1559
+}
1560
+
1542 1561
 // Invite invites the given client to the channel, if the inviter can do so.
1543 1562
 func (channel *Channel) Invite(invitee *Client, inviter *Client, rb *ResponseBuffer) {
1544 1563
 	channel.stateMutex.RLock()

+ 50
- 9
irc/channelmanager.go View File

@@ -39,9 +39,9 @@ func (cm *ChannelManager) Initialize(server *Server) {
39 39
 	cm.chansSkeletons = make(utils.StringSet)
40 40
 	cm.server = server
41 41
 
42
-	cm.loadRegisteredChannels(server.Config())
43 42
 	// purging should work even if registration is disabled
44 43
 	cm.purgedChannels = cm.server.channelRegistry.PurgedChannels()
44
+	cm.loadRegisteredChannels(server.Config())
45 45
 }
46 46
 
47 47
 func (cm *ChannelManager) loadRegisteredChannels(config *Config) {
@@ -49,23 +49,48 @@ func (cm *ChannelManager) loadRegisteredChannels(config *Config) {
49 49
 		return
50 50
 	}
51 51
 
52
+	var newChannels []*Channel
53
+	var collisions []string
54
+	defer func() {
55
+		for _, ch := range newChannels {
56
+			ch.EnsureLoaded()
57
+			cm.server.logger.Debug("channels", "initialized registered channel", ch.Name())
58
+		}
59
+		for _, collision := range collisions {
60
+			cm.server.logger.Warning("channels", "registered channel collides with existing channel", collision)
61
+		}
62
+	}()
63
+
52 64
 	rawNames := cm.server.channelRegistry.AllChannels()
53
-	registeredChannels := make(utils.StringSet, len(rawNames))
54
-	registeredSkeletons := make(utils.StringSet, len(rawNames))
65
+
66
+	cm.Lock()
67
+	defer cm.Unlock()
68
+
69
+	cm.registeredChannels = make(utils.StringSet, len(rawNames))
70
+	cm.registeredSkeletons = make(utils.StringSet, len(rawNames))
55 71
 	for _, name := range rawNames {
56 72
 		cfname, err := CasefoldChannel(name)
57 73
 		if err == nil {
58
-			registeredChannels.Add(cfname)
74
+			cm.registeredChannels.Add(cfname)
59 75
 		}
60 76
 		skeleton, err := Skeleton(name)
61 77
 		if err == nil {
62
-			registeredSkeletons.Add(skeleton)
78
+			cm.registeredSkeletons.Add(skeleton)
79
+		}
80
+
81
+		if !cm.purgedChannels.Has(cfname) {
82
+			if _, ok := cm.chans[cfname]; !ok {
83
+				ch := NewChannel(cm.server, name, cfname, true)
84
+				cm.chans[cfname] = &channelManagerEntry{
85
+					channel:      ch,
86
+					pendingJoins: 0,
87
+				}
88
+				newChannels = append(newChannels, ch)
89
+			} else {
90
+				collisions = append(collisions, name)
91
+			}
63 92
 		}
64 93
 	}
65
-	cm.Lock()
66
-	defer cm.Unlock()
67
-	cm.registeredChannels = registeredChannels
68
-	cm.registeredSkeletons = registeredSkeletons
69 94
 }
70 95
 
71 96
 // Get returns an existing channel with name equivalent to `name`, or nil
@@ -366,12 +391,28 @@ func (cm *ChannelManager) Purge(chname string, record ChannelPurgeRecord) (err e
366 391
 	if err != nil {
367 392
 		return errInvalidChannelName
368 393
 	}
394
+	skel, err := Skeleton(chname)
395
+	if err != nil {
396
+		return errInvalidChannelName
397
+	}
369 398
 
370 399
 	cm.Lock()
371 400
 	cm.purgedChannels.Add(chname)
401
+	entry := cm.chans[chname]
402
+	if entry != nil {
403
+		delete(cm.chans, chname)
404
+		if entry.channel.Founder() != "" {
405
+			delete(cm.registeredSkeletons, skel)
406
+		} else {
407
+			delete(cm.chansSkeletons, skel)
408
+		}
409
+	}
372 410
 	cm.Unlock()
373 411
 
374 412
 	cm.server.channelRegistry.PurgeChannel(chname, record)
413
+	if entry != nil {
414
+		entry.channel.Purge("")
415
+	}
375 416
 	return nil
376 417
 }
377 418
 

+ 2
- 3
irc/handlers.go View File

@@ -1614,9 +1614,8 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage, rb *Resp
1614 1614
 
1615 1615
 	nick := client.Nick()
1616 1616
 	rplList := func(channel *Channel) {
1617
-		if members, name, topic := channel.listData(); members != 0 {
1618
-			rb.Add(nil, client.server.name, RPL_LIST, nick, name, strconv.Itoa(members), topic)
1619
-		}
1617
+		members, name, topic := channel.listData()
1618
+		rb.Add(nil, client.server.name, RPL_LIST, nick, name, strconv.Itoa(members), topic)
1620 1619
 	}
1621 1620
 
1622 1621
 	clientIsOp := client.HasMode(modes.Operator)

Loading…
Cancel
Save