mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-10 22:19:31 +01:00
remove channelJoinPartMutex
This commit is contained in:
parent
d715abf0f0
commit
94cf438f51
@ -7,7 +7,6 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ type Channel struct {
|
|||||||
func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
|
func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
|
||||||
casefoldedName, err := CasefoldChannel(name)
|
casefoldedName, err := CasefoldChannel(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(fmt.Sprintf("ERROR: Channel name is bad: [%s]", name), err.Error())
|
s.logger.Error("internal", fmt.Sprintf("Bad channel name %s: %v", name, err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,13 +58,11 @@ func NewChannel(s *Server, name string, addDefaultModes bool) *Channel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if addDefaultModes {
|
if addDefaultModes {
|
||||||
for _, mode := range s.GetDefaultChannelModes() {
|
for _, mode := range s.DefaultChannelModes() {
|
||||||
channel.flags[mode] = true
|
channel.flags[mode] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.channels.Add(channel)
|
|
||||||
|
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,6 +278,12 @@ func (channel *Channel) CheckKey(key string) bool {
|
|||||||
return (channel.key == "") || (channel.key == key)
|
return (channel.key == "") || (channel.key == key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) IsEmpty() bool {
|
||||||
|
channel.stateMutex.RLock()
|
||||||
|
defer channel.stateMutex.RUnlock()
|
||||||
|
return len(channel.members) == 0
|
||||||
|
}
|
||||||
|
|
||||||
// Join joins the given client to this channel (if they can be joined).
|
// Join joins the given client to this channel (if they can be joined).
|
||||||
//TODO(dan): /SAJOIN and maybe a ForceJoin function?
|
//TODO(dan): /SAJOIN and maybe a ForceJoin function?
|
||||||
func (channel *Channel) Join(client *Client, key string) {
|
func (channel *Channel) Join(client *Client, key string) {
|
||||||
@ -684,16 +687,10 @@ func (channel *Channel) applyModeMask(client *Client, mode Mode, op ModeOp, mask
|
|||||||
func (channel *Channel) Quit(client *Client) {
|
func (channel *Channel) Quit(client *Client) {
|
||||||
channel.stateMutex.Lock()
|
channel.stateMutex.Lock()
|
||||||
channel.members.Remove(client)
|
channel.members.Remove(client)
|
||||||
empty := len(channel.members) == 0
|
|
||||||
channel.stateMutex.Unlock()
|
channel.stateMutex.Unlock()
|
||||||
channel.regenerateMembersCache()
|
channel.regenerateMembersCache()
|
||||||
|
|
||||||
client.removeChannel(channel)
|
client.removeChannel(channel)
|
||||||
|
|
||||||
//TODO(slingamn) fold this operation into a channelmanager type
|
|
||||||
if empty {
|
|
||||||
channel.server.channels.Remove(channel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
func (channel *Channel) Kick(client *Client, target *Client, comment string) {
|
||||||
|
162
irc/channelmanager.go
Normal file
162
irc/channelmanager.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
// Copyright (c) 2017 Shivaram Lingamneni <slingamn@cs.stanford.edu>
|
||||||
|
// released under the MIT license
|
||||||
|
|
||||||
|
package irc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
InvalidChannelName = errors.New("Invalid channel name")
|
||||||
|
NoSuchChannel = errors.New("No such channel")
|
||||||
|
ChannelNameInUse = errors.New("Channel name in use")
|
||||||
|
)
|
||||||
|
|
||||||
|
type channelManagerEntry struct {
|
||||||
|
channel *Channel
|
||||||
|
// this is a refcount for joins, so we can avoid a race where we incorrectly
|
||||||
|
// think the channel is empty (without holding a lock across the entire Channel.Join()
|
||||||
|
// call)
|
||||||
|
pendingJoins int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelManager keeps track of all the channels on the server,
|
||||||
|
// providing synchronization for creation of new channels on first join,
|
||||||
|
// cleanup of empty channels on last part, and renames.
|
||||||
|
type ChannelManager struct {
|
||||||
|
sync.RWMutex // tier 2
|
||||||
|
chans map[string]*channelManagerEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewChannelManager returns a new ChannelManager.
|
||||||
|
func NewChannelManager() *ChannelManager {
|
||||||
|
return &ChannelManager{
|
||||||
|
chans: make(map[string]*channelManagerEntry),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns an existing channel with name equivalent to `name`, or nil
|
||||||
|
func (cm *ChannelManager) Get(name string) *Channel {
|
||||||
|
name, err := CasefoldChannel(name)
|
||||||
|
if err == nil {
|
||||||
|
cm.RLock()
|
||||||
|
defer cm.RUnlock()
|
||||||
|
return cm.chans[name].channel
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join causes `client` to join the channel named `name`, creating it if necessary.
|
||||||
|
func (cm *ChannelManager) Join(client *Client, name string, key string) error {
|
||||||
|
server := client.server
|
||||||
|
casefoldedName, err := CasefoldChannel(name)
|
||||||
|
if err != nil || len(casefoldedName) > server.getLimits().ChannelLen {
|
||||||
|
return NoSuchChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.Lock()
|
||||||
|
entry := cm.chans[casefoldedName]
|
||||||
|
if entry == nil {
|
||||||
|
entry = &channelManagerEntry{
|
||||||
|
channel: NewChannel(server, name, true),
|
||||||
|
pendingJoins: 0,
|
||||||
|
}
|
||||||
|
cm.chans[casefoldedName] = entry
|
||||||
|
}
|
||||||
|
entry.pendingJoins += 1
|
||||||
|
cm.Unlock()
|
||||||
|
|
||||||
|
entry.channel.Join(client, key)
|
||||||
|
|
||||||
|
cm.maybeCleanup(entry, true)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *ChannelManager) maybeCleanup(entry *channelManagerEntry, afterJoin bool) {
|
||||||
|
cm.Lock()
|
||||||
|
defer cm.Unlock()
|
||||||
|
|
||||||
|
if entry.channel == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if afterJoin {
|
||||||
|
entry.pendingJoins -= 1
|
||||||
|
}
|
||||||
|
if entry.channel.IsEmpty() && entry.pendingJoins == 0 {
|
||||||
|
// reread the name, handling the case where the channel was renamed
|
||||||
|
casefoldedName := entry.channel.NameCasefolded()
|
||||||
|
delete(cm.chans, casefoldedName)
|
||||||
|
// invalidate the entry (otherwise, a subsequent cleanup attempt could delete
|
||||||
|
// a valid, distinct entry under casefoldedName):
|
||||||
|
entry.channel = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part parts `client` from the channel named `name`, deleting it if it's empty.
|
||||||
|
func (cm *ChannelManager) Part(client *Client, name string, message string) error {
|
||||||
|
casefoldedName, err := CasefoldChannel(name)
|
||||||
|
if err != nil {
|
||||||
|
return NoSuchChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.RLock()
|
||||||
|
entry := cm.chans[casefoldedName]
|
||||||
|
cm.RUnlock()
|
||||||
|
|
||||||
|
if entry == nil {
|
||||||
|
return NoSuchChannel
|
||||||
|
}
|
||||||
|
entry.channel.Part(client, message)
|
||||||
|
cm.maybeCleanup(entry, false)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename renames a channel (but does not notify the members)
|
||||||
|
func (cm *ChannelManager) Rename(name string, newname string) error {
|
||||||
|
cfname, err := CasefoldChannel(name)
|
||||||
|
if err != nil {
|
||||||
|
return NoSuchChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
cfnewname, err := CasefoldChannel(newname)
|
||||||
|
if err != nil {
|
||||||
|
return InvalidChannelName
|
||||||
|
}
|
||||||
|
|
||||||
|
cm.Lock()
|
||||||
|
defer cm.Unlock()
|
||||||
|
|
||||||
|
if cm.chans[cfnewname] != nil {
|
||||||
|
return ChannelNameInUse
|
||||||
|
}
|
||||||
|
entry := cm.chans[cfname]
|
||||||
|
if entry == nil {
|
||||||
|
return NoSuchChannel
|
||||||
|
}
|
||||||
|
delete(cm.chans, cfname)
|
||||||
|
cm.chans[cfnewname] = entry
|
||||||
|
entry.channel.setName(newname)
|
||||||
|
entry.channel.setNameCasefolded(cfnewname)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Len returns the number of channels
|
||||||
|
func (cm *ChannelManager) Len() int {
|
||||||
|
cm.RLock()
|
||||||
|
defer cm.RUnlock()
|
||||||
|
return len(cm.chans)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channels returns a slice containing all current channels
|
||||||
|
func (cm *ChannelManager) Channels() (result []*Channel) {
|
||||||
|
cm.RLock()
|
||||||
|
defer cm.RUnlock()
|
||||||
|
for _, entry := range cm.chans {
|
||||||
|
result = append(result, entry.channel)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -548,14 +548,12 @@ func (client *Client) destroy() {
|
|||||||
client.server.monitorManager.RemoveAll(client)
|
client.server.monitorManager.RemoveAll(client)
|
||||||
|
|
||||||
// clean up channels
|
// clean up channels
|
||||||
client.server.channelJoinPartMutex.Lock()
|
for _, channel := range client.Channels() {
|
||||||
for channel := range client.channels {
|
|
||||||
channel.Quit(client)
|
channel.Quit(client)
|
||||||
for _, member := range channel.Members() {
|
for _, member := range channel.Members() {
|
||||||
friends.Add(member)
|
friends.Add(member)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
client.server.channelJoinPartMutex.Unlock()
|
|
||||||
|
|
||||||
// clean up server
|
// clean up server
|
||||||
client.server.clients.Remove(client)
|
client.server.clients.Remove(client)
|
||||||
|
@ -41,6 +41,12 @@ func (server *Server) WebIRCConfig() []webircConfig {
|
|||||||
return server.webirc
|
return server.webirc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (server *Server) DefaultChannelModes() Modes {
|
||||||
|
server.configurableStateMutex.RLock()
|
||||||
|
defer server.configurableStateMutex.RUnlock()
|
||||||
|
return server.defaultChannelModes
|
||||||
|
}
|
||||||
|
|
||||||
func (client *Client) getNick() string {
|
func (client *Client) getNick() string {
|
||||||
client.stateMutex.RLock()
|
client.stateMutex.RLock()
|
||||||
defer client.stateMutex.RUnlock()
|
defer client.stateMutex.RUnlock()
|
||||||
@ -114,6 +120,24 @@ func (channel *Channel) Name() string {
|
|||||||
return channel.name
|
return channel.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) setName(name string) {
|
||||||
|
channel.stateMutex.Lock()
|
||||||
|
defer channel.stateMutex.Unlock()
|
||||||
|
channel.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) NameCasefolded() string {
|
||||||
|
channel.stateMutex.RLock()
|
||||||
|
defer channel.stateMutex.RUnlock()
|
||||||
|
return channel.nameCasefolded
|
||||||
|
}
|
||||||
|
|
||||||
|
func (channel *Channel) setNameCasefolded(nameCasefolded string) {
|
||||||
|
channel.stateMutex.Lock()
|
||||||
|
defer channel.stateMutex.Unlock()
|
||||||
|
channel.nameCasefolded = nameCasefolded
|
||||||
|
}
|
||||||
|
|
||||||
func (channel *Channel) Members() (result []*Client) {
|
func (channel *Channel) Members() (result []*Client) {
|
||||||
channel.stateMutex.RLock()
|
channel.stateMutex.RLock()
|
||||||
defer channel.stateMutex.RUnlock()
|
defer channel.stateMutex.RUnlock()
|
||||||
|
@ -52,12 +52,9 @@ func (manager *MonitorManager) AlertAbout(client *Client, online bool) {
|
|||||||
command = RPL_MONONLINE
|
command = RPL_MONONLINE
|
||||||
}
|
}
|
||||||
|
|
||||||
// asynchronously send all the notifications
|
|
||||||
go func() {
|
|
||||||
for _, mClient := range watchers {
|
for _, mClient := range watchers {
|
||||||
mClient.Send(nil, client.server.name, command, mClient.getNick(), nick)
|
mClient.Send(nil, client.server.name, command, mClient.getNick(), nick)
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add registers `client` to receive notifications about `nick`.
|
// Add registers `client` to receive notifications about `nick`.
|
||||||
|
147
irc/server.go
147
irc/server.go
@ -9,6 +9,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@ -39,6 +40,8 @@ var (
|
|||||||
|
|
||||||
// common error responses
|
// common error responses
|
||||||
couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line()
|
couldNotParseIPMsg, _ = (&[]ircmsg.IrcMessage{ircmsg.MakeMessage(nil, "", "ERROR", "Unable to parse your IP address")}[0]).Line()
|
||||||
|
|
||||||
|
RenamePrivsNeeded = errors.New("Only chanops can rename channels")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -80,8 +83,7 @@ type Server struct {
|
|||||||
accountRegistration *AccountRegistration
|
accountRegistration *AccountRegistration
|
||||||
accounts map[string]*ClientAccount
|
accounts map[string]*ClientAccount
|
||||||
channelRegistrationEnabled bool
|
channelRegistrationEnabled bool
|
||||||
channels ChannelNameMap
|
channels *ChannelManager
|
||||||
channelJoinPartMutex sync.Mutex // used when joining/parting channels to prevent stomping over each others' access and all
|
|
||||||
checkIdent bool
|
checkIdent bool
|
||||||
clients *ClientLookupSet
|
clients *ClientLookupSet
|
||||||
commands chan Command
|
commands chan Command
|
||||||
@ -147,7 +149,7 @@ func NewServer(config *Config, logger *logger.Manager) (*Server, error) {
|
|||||||
// initialize data structures
|
// initialize data structures
|
||||||
server := &Server{
|
server := &Server{
|
||||||
accounts: make(map[string]*ClientAccount),
|
accounts: make(map[string]*ClientAccount),
|
||||||
channels: *NewChannelNameMap(),
|
channels: NewChannelManager(),
|
||||||
clients: NewClientLookupSet(),
|
clients: NewClientLookupSet(),
|
||||||
commands: make(chan Command),
|
commands: make(chan Command),
|
||||||
connectionLimiter: connection_limits.NewLimiter(),
|
connectionLimiter: connection_limits.NewLimiter(),
|
||||||
@ -553,53 +555,62 @@ func pongHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RENAME <oldchan> <newchan> [<reason>]
|
// RENAME <oldchan> <newchan> [<reason>]
|
||||||
//TODO(dan): Clean up this function so it doesn't look like an eldrich horror... prolly by putting it into a server.renameChannel function.
|
func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) (result bool) {
|
||||||
func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
result = false
|
||||||
// get lots of locks... make sure nobody touches anything while we're doing this
|
|
||||||
|
// TODO(slingamn, #152) clean up locking here
|
||||||
server.registeredChannelsMutex.Lock()
|
server.registeredChannelsMutex.Lock()
|
||||||
defer server.registeredChannelsMutex.Unlock()
|
defer server.registeredChannelsMutex.Unlock()
|
||||||
server.channels.ChansLock.Lock()
|
|
||||||
defer server.channels.ChansLock.Unlock()
|
errorResponse := func(err error, name string) {
|
||||||
|
// TODO: send correct error codes, e.g., ERR_CANNOTRENAME, ERR_CHANNAMEINUSE
|
||||||
|
var code string
|
||||||
|
switch err {
|
||||||
|
case NoSuchChannel:
|
||||||
|
code = ERR_NOSUCHCHANNEL
|
||||||
|
case RenamePrivsNeeded:
|
||||||
|
code = ERR_CHANOPRIVSNEEDED
|
||||||
|
case InvalidChannelName:
|
||||||
|
code = ERR_UNKNOWNERROR
|
||||||
|
case ChannelNameInUse:
|
||||||
|
code = ERR_UNKNOWNERROR
|
||||||
|
default:
|
||||||
|
code = ERR_UNKNOWNERROR
|
||||||
|
}
|
||||||
|
client.Send(nil, server.name, code, client.getNick(), "RENAME", name, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
oldName := strings.TrimSpace(msg.Params[0])
|
oldName := strings.TrimSpace(msg.Params[0])
|
||||||
newName := strings.TrimSpace(msg.Params[1])
|
newName := strings.TrimSpace(msg.Params[1])
|
||||||
|
if oldName == "" || newName == "" {
|
||||||
|
errorResponse(InvalidChannelName, "<empty>")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
casefoldedOldName, err := CasefoldChannel(oldName)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(InvalidChannelName, oldName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
casefoldedNewName, err := CasefoldChannel(newName)
|
||||||
|
if err != nil {
|
||||||
|
errorResponse(InvalidChannelName, newName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
reason := "No reason"
|
reason := "No reason"
|
||||||
if 2 < len(msg.Params) {
|
if 2 < len(msg.Params) {
|
||||||
reason = msg.Params[2]
|
reason = msg.Params[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for all the reasons why the rename couldn't happen
|
channel := server.channels.Get(oldName)
|
||||||
casefoldedOldName, err := CasefoldChannel(oldName)
|
|
||||||
if err != nil {
|
|
||||||
//TODO(dan): Change this to ERR_CANNOTRENAME
|
|
||||||
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "RENAME", oldName, "Old channel name is invalid")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
channel := server.channels.Chans[casefoldedOldName]
|
|
||||||
if channel == nil {
|
if channel == nil {
|
||||||
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, oldName, "No such channel")
|
errorResponse(NoSuchChannel, oldName)
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO(dan): allow IRCops to do this?
|
//TODO(dan): allow IRCops to do this?
|
||||||
if !channel.ClientIsAtLeast(client, Operator) {
|
if !channel.ClientIsAtLeast(client, Operator) {
|
||||||
client.Send(nil, server.name, ERR_CHANOPRIVSNEEDED, client.nick, oldName, "Only chanops can rename channels")
|
errorResponse(RenamePrivsNeeded, oldName)
|
||||||
return false
|
return
|
||||||
}
|
|
||||||
|
|
||||||
casefoldedNewName, err := CasefoldChannel(newName)
|
|
||||||
if err != nil {
|
|
||||||
//TODO(dan): Change this to ERR_CANNOTRENAME
|
|
||||||
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "RENAME", newName, "New channel name is invalid")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
newChannel := server.channels.Chans[casefoldedNewName]
|
|
||||||
if newChannel != nil {
|
|
||||||
//TODO(dan): Change this to ERR_CHANNAMEINUSE
|
|
||||||
client.Send(nil, server.name, ERR_UNKNOWNERROR, client.nick, "RENAME", newName, "New channel name is in use")
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var canEdit bool
|
var canEdit bool
|
||||||
@ -622,11 +633,11 @@ func renameHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// perform the channel rename
|
// perform the channel rename
|
||||||
server.channels.Chans[casefoldedOldName] = nil
|
err = server.channels.Rename(oldName, newName)
|
||||||
server.channels.Chans[casefoldedNewName] = channel
|
if err != nil {
|
||||||
|
errorResponse(err, newName)
|
||||||
channel.name = strings.TrimSpace(msg.Params[1])
|
return
|
||||||
channel.nameCasefolded = casefoldedNewName
|
}
|
||||||
|
|
||||||
// rename stored channel info if any exists
|
// rename stored channel info if any exists
|
||||||
server.store.Update(func(tx *buntdb.Tx) error {
|
server.store.Update(func(tx *buntdb.Tx) error {
|
||||||
@ -679,34 +690,15 @@ func joinHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
keys = strings.Split(msg.Params[1], ",")
|
keys = strings.Split(msg.Params[1], ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
// get lock
|
|
||||||
server.channelJoinPartMutex.Lock()
|
|
||||||
defer server.channelJoinPartMutex.Unlock()
|
|
||||||
|
|
||||||
for i, name := range channels {
|
for i, name := range channels {
|
||||||
casefoldedName, err := CasefoldChannel(name)
|
|
||||||
if err != nil {
|
|
||||||
if len(name) > 0 {
|
|
||||||
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
channel := server.channels.Get(casefoldedName)
|
|
||||||
if channel == nil {
|
|
||||||
if len(casefoldedName) > server.getLimits().ChannelLen {
|
|
||||||
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, name, "No such channel")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
channel = NewChannel(server, name, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
var key string
|
var key string
|
||||||
if len(keys) > i {
|
if len(keys) > i {
|
||||||
key = keys[i]
|
key = keys[i]
|
||||||
}
|
}
|
||||||
|
err := server.channels.Join(client, name, key)
|
||||||
channel.Join(client, key)
|
if err == NoSuchChannel {
|
||||||
|
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.getNick(), name, "No such channel")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -719,22 +711,11 @@ func partHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
reason = msg.Params[1]
|
reason = msg.Params[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// get lock
|
|
||||||
server.channelJoinPartMutex.Lock()
|
|
||||||
defer server.channelJoinPartMutex.Unlock()
|
|
||||||
|
|
||||||
for _, chname := range channels {
|
for _, chname := range channels {
|
||||||
casefoldedChannelName, err := CasefoldChannel(chname)
|
err := server.channels.Part(client, chname, reason)
|
||||||
channel := server.channels.Get(casefoldedChannelName)
|
if err == NoSuchChannel {
|
||||||
|
|
||||||
if err != nil || channel == nil {
|
|
||||||
if len(chname) > 0 {
|
|
||||||
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, "No such channel")
|
client.Send(nil, server.name, ERR_NOSUCHCHANNEL, client.nick, chname, "No such channel")
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.Part(client, reason)
|
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1096,11 +1077,9 @@ func whoHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
if mask == "" {
|
if mask == "" {
|
||||||
server.channels.ChansLock.RLock()
|
for _, channel := range server.channels.Channels() {
|
||||||
for _, channel := range server.channels.Chans {
|
|
||||||
whoChannel(client, channel, friends)
|
whoChannel(client, channel, friends)
|
||||||
}
|
}
|
||||||
server.channels.ChansLock.RUnlock()
|
|
||||||
} else if mask[0] == '#' {
|
} else if mask[0] == '#' {
|
||||||
// TODO implement wildcard matching
|
// TODO implement wildcard matching
|
||||||
//TODO(dan): ^ only for opers
|
//TODO(dan): ^ only for opers
|
||||||
@ -1859,8 +1838,7 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(channels) == 0 {
|
if len(channels) == 0 {
|
||||||
server.channels.ChansLock.RLock()
|
for _, channel := range server.channels.Channels() {
|
||||||
for _, channel := range server.channels.Chans {
|
|
||||||
if !client.flags[Operator] && channel.flags[Secret] {
|
if !client.flags[Operator] && channel.flags[Secret] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -1868,7 +1846,6 @@ func listHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
client.RplList(channel)
|
client.RplList(channel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
server.channels.ChansLock.RUnlock()
|
|
||||||
} else {
|
} else {
|
||||||
// limit regular users to only listing one channel
|
// limit regular users to only listing one channel
|
||||||
if !client.flags[Operator] {
|
if !client.flags[Operator] {
|
||||||
@ -1922,11 +1899,9 @@ func namesHandler(server *Server, client *Client, msg ircmsg.IrcMessage) bool {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
if len(channels) == 0 {
|
if len(channels) == 0 {
|
||||||
server.channels.ChansLock.RLock()
|
for _, channel := range server.channels.Channels() {
|
||||||
for _, channel := range server.channels.Chans {
|
|
||||||
channel.Names(client)
|
channel.Names(client)
|
||||||
}
|
}
|
||||||
server.channels.ChansLock.RUnlock()
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
irc/types.go
55
irc/types.go
@ -6,64 +6,9 @@
|
|||||||
package irc
|
package irc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ChannelNameMap is a map that converts channel names to actual channel objects.
|
|
||||||
type ChannelNameMap struct {
|
|
||||||
ChansLock sync.RWMutex
|
|
||||||
Chans map[string]*Channel
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewChannelNameMap returns a new ChannelNameMap.
|
|
||||||
func NewChannelNameMap() *ChannelNameMap {
|
|
||||||
var channels ChannelNameMap
|
|
||||||
channels.Chans = make(map[string]*Channel)
|
|
||||||
return &channels
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the given channel if it exists.
|
|
||||||
func (channels *ChannelNameMap) Get(name string) *Channel {
|
|
||||||
name, err := CasefoldChannel(name)
|
|
||||||
if err == nil {
|
|
||||||
channels.ChansLock.RLock()
|
|
||||||
defer channels.ChansLock.RUnlock()
|
|
||||||
return channels.Chans[name]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds the given channel to our map.
|
|
||||||
func (channels *ChannelNameMap) Add(channel *Channel) error {
|
|
||||||
channels.ChansLock.Lock()
|
|
||||||
defer channels.ChansLock.Unlock()
|
|
||||||
if channels.Chans[channel.nameCasefolded] != nil {
|
|
||||||
return fmt.Errorf("%s: already set", channel.name)
|
|
||||||
}
|
|
||||||
channels.Chans[channel.nameCasefolded] = channel
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove removes the given channel from our map.
|
|
||||||
func (channels *ChannelNameMap) Remove(channel *Channel) error {
|
|
||||||
channels.ChansLock.Lock()
|
|
||||||
defer channels.ChansLock.Unlock()
|
|
||||||
if channel != channels.Chans[channel.nameCasefolded] {
|
|
||||||
return fmt.Errorf("%s: mismatch", channel.name)
|
|
||||||
}
|
|
||||||
delete(channels.Chans, channel.nameCasefolded)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns how many channels we have.
|
|
||||||
func (channels *ChannelNameMap) Len() int {
|
|
||||||
channels.ChansLock.RLock()
|
|
||||||
defer channels.ChansLock.RUnlock()
|
|
||||||
return len(channels.Chans)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ModeSet holds a set of modes.
|
// ModeSet holds a set of modes.
|
||||||
type ModeSet map[Mode]bool
|
type ModeSet map[Mode]bool
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user