*/
-/* $Core */
-
#include "inspircd.h"
#include "listmode.h"
#include <cstdarg>
#include "mode.h"
-static ModeReference ban(NULL, "ban");
+namespace
+{
+ ChanModeReference ban(NULL, "ban");
+ ChanModeReference inviteonlymode(NULL, "inviteonly");
+ ChanModeReference keymode(NULL, "key");
+ ChanModeReference limitmode(NULL, "limit");
+ ChanModeReference secretmode(NULL, "secret");
+ ChanModeReference privatemode(NULL, "private");
+ UserModeReference invisiblemode(NULL, "invisible");
+}
Channel::Channel(const std::string &cname, time_t ts)
{
modes.reset();
}
-void Channel::SetMode(char mode,bool mode_on)
-{
- modes[mode-65] = mode_on;
-}
-
void Channel::SetMode(ModeHandler* mh, bool on)
{
modes[mh->GetModeChar() - 65] = on;
}
-void Channel::SetModeParam(char mode, const std::string& parameter)
+void Channel::SetModeParam(ModeHandler* mh, const std::string& parameter)
{
- CustomModeList::iterator n = custom_mode_params.find(mode);
- // always erase, even if changing, so that the map gets the new value
- if (n != custom_mode_params.end())
- custom_mode_params.erase(n);
+ char mode = mh->GetModeChar();
if (parameter.empty())
{
+ custom_mode_params.erase(mode);
modes[mode-65] = false;
}
else
}
}
-void Channel::SetModeParam(ModeHandler* mode, const std::string& parameter)
-{
- SetModeParam(mode->GetModeChar(), parameter);
-}
-
-std::string Channel::GetModeParameter(char mode)
-{
- CustomModeList::iterator n = custom_mode_params.find(mode);
- if (n != custom_mode_params.end())
- return n->second;
- return "";
-}
-
std::string Channel::GetModeParameter(ModeHandler* mode)
{
CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
return "";
}
-int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
+void Channel::SetTopic(User* u, const std::string& ntopic)
{
- if (IS_LOCAL(u) && !forceset)
- {
- ModResult res;
- FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
-
- if (res == MOD_RES_DENY)
- return CMD_FAILURE;
- if (res != MOD_RES_ALLOW)
- {
- if (!this->HasUser(u))
- {
- u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
- return CMD_FAILURE;
- }
- if (IsModeSet('t') && !ServerInstance->OnCheckExemption(u,this,"topiclock").check(GetPrefixValue(u) >= HALFOP_VALUE))
- {
- u->WriteNumeric(482, "%s %s :You do not have access to change the topic on this channel", u->nick.c_str(), this->name.c_str());
- return CMD_FAILURE;
- }
- }
- }
-
this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
this->setby.assign(ServerInstance->Config->FullHostInTopic ? u->GetFullHost() : u->nick, 0, 128);
this->WriteChannel(u, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
this->topicset = ServerInstance->Time();
- FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
-
- return CMD_SUCCESS;
+ FOREACH_MOD(OnPostTopicChange, (u, this, this->topic));
}
Membership* Channel::AddUser(User* user)
void Channel::DelUser(User* user)
{
- UserMembIter a = userlist.find(user);
+ UserMembIter it = userlist.find(user);
+ if (it != userlist.end())
+ DelUser(it);
+}
+
+void Channel::CheckDestroy()
+{
+ if (!userlist.empty())
+ return;
- if (a != userlist.end())
+ ModResult res;
+ FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
+ if (res == MOD_RES_DENY)
+ return;
+
+ chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
+ /* kill the record */
+ if (iter != ServerInstance->chanlist->end())
{
- a->second->cull();
- delete a->second;
- userlist.erase(a);
+ FOREACH_MOD(OnChannelDelete, (this));
+ ServerInstance->chanlist->erase(iter);
}
- if (userlist.empty())
- {
- ModResult res;
- FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
- if (res == MOD_RES_DENY)
- return;
- chan_hash::iterator iter = ServerInstance->chanlist->find(this->name);
- /* kill the record */
- if (iter != ServerInstance->chanlist->end())
- {
- FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
- ServerInstance->chanlist->erase(iter);
- }
+ ClearInvites();
+ ServerInstance->GlobalCulls.AddItem(this);
+}
- ClearInvites();
- ServerInstance->GlobalCulls.AddItem(this);
- }
+void Channel::DelUser(const UserMembIter& membiter)
+{
+ Membership* memb = membiter->second;
+ memb->cull();
+ delete memb;
+ userlist.erase(membiter);
+
+ // If this channel became empty then it should be removed
+ CheckDestroy();
}
Membership* Channel::GetUser(User* user)
// then this entire section is skipped
if (MOD_RESULT == MOD_RES_PASSTHRU)
{
- std::string ckey = chan->GetModeParameter('k');
+ std::string ckey = chan->GetModeParameter(keymode);
bool invited = user->IsInvited(chan);
bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
}
}
- if (chan->IsModeSet('i'))
+ if (chan->IsModeSet(inviteonlymode))
{
FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
if (!MOD_RESULT.check(invited))
}
}
- std::string limit = chan->GetModeParameter('l');
+ std::string limit = chan->GetModeParameter(limitmode);
if (!limit.empty())
{
FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
void Channel::ForceJoin(User* user, const std::string* privs, bool bursting, bool created_by_local)
{
- Membership* memb = this->AddUser(user);
- if (!memb)
- return; // Already on the channel
-
if (IS_SERVER(user))
{
ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "Attempted to join server user " + user->uuid + " to channel " + this->name);
return;
}
+ Membership* memb = this->AddUser(user);
+ if (!memb)
+ return; // Already on the channel
+
user->chans.insert(this);
if (privs)
// Tell modules about this join, they have the chance now to populate except_list with users we won't send the JOIN (and possibly MODE) to
CUList except_list;
- FOREACH_MOD(I_OnUserJoin,OnUserJoin(memb, bursting, created_by_local, except_list));
+ FOREACH_MOD(OnUserJoin, (memb, bursting, created_by_local, except_list));
this->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", this->name.c_str());
this->UserList(user);
}
- FOREACH_MOD(I_OnPostJoin,OnPostJoin(memb));
+ FOREACH_MOD(OnPostJoin, (memb));
}
bool Channel::IsBanned(User* user)
}
/* Channel::PartUser
- * remove a channel from a users record, and return the number of users left.
- * Therefore, if this function returns 0 the caller should delete the Channel.
+ * Remove a channel from a users record, remove the reference to the Membership object
+ * from the channel and destroy it.
*/
void Channel::PartUser(User *user, std::string &reason)
{
- Membership* memb = GetUser(user);
+ UserMembIter membiter = userlist.find(user);
- if (memb)
+ if (membiter != userlist.end())
{
+ Membership* memb = membiter->second;
CUList except_list;
- FOREACH_MOD(I_OnUserPart,OnUserPart(memb, reason, except_list));
+ FOREACH_MOD(OnUserPart, (memb, reason, except_list));
WriteAllExcept(user, false, 0, except_list, "PART %s%s%s", this->name.c_str(), reason.empty() ? "" : " :", reason.c_str());
+ // Remove this channel from the user's chanlist
user->chans.erase(this);
- this->RemoveAllPrefixes(user);
+ // Remove the Membership from this channel's userlist and destroy it
+ this->DelUser(membiter);
}
-
- this->DelUser(user);
}
-void Channel::KickUser(User *src, User *user, const std::string& reason)
+void Channel::KickUser(User* src, User* victim, const std::string& reason, Membership* srcmemb)
{
- Membership* memb = GetUser(user);
- if (IS_LOCAL(src))
+ UserMembIter victimiter = userlist.find(victim);
+ Membership* memb = ((victimiter != userlist.end()) ? victimiter->second : NULL);
+
+ if (!memb)
{
- if (!memb)
- {
- src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), user->nick.c_str(), this->name.c_str());
- return;
- }
- if ((ServerInstance->ULine(user->server)) && (!ServerInstance->ULine(src->server)))
- {
- src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :Only a u-line may kick a u-line from a channel.",src->nick.c_str(), this->name.c_str());
- return;
- }
+ src->WriteNumeric(ERR_USERNOTINCHANNEL, "%s %s %s :They are not on that channel",src->nick.c_str(), victim->nick.c_str(), this->name.c_str());
+ return;
+ }
+ // Do the following checks only if the KICK is done by a local user;
+ // each server enforces its own rules.
+ if (IS_LOCAL(src))
+ {
+ // Modules are allowed to explicitly allow or deny kicks done by local users
ModResult res;
- if (ServerInstance->ULine(src->server))
- res = MOD_RES_ALLOW;
- else
- FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
-
+ FIRST_MOD_RESULT(OnUserPreKick, res, (src,memb,reason));
if (res == MOD_RES_DENY)
return;
if (res == MOD_RES_PASSTHRU)
{
- unsigned int them = this->GetPrefixValue(src);
+ if (!srcmemb)
+ srcmemb = GetUser(src);
+ unsigned int them = srcmemb ? srcmemb->getRank() : 0;
unsigned int req = HALFOP_VALUE;
for (std::string::size_type i = 0; i < memb->modes.length(); i++)
{
}
}
- if (memb)
- {
- CUList except_list;
- FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
-
- WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), user->nick.c_str(), reason.c_str());
+ CUList except_list;
+ FOREACH_MOD(OnUserKick, (src, memb, reason, except_list));
- user->chans.erase(this);
- this->RemoveAllPrefixes(user);
- }
+ WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), victim->nick.c_str(), reason.c_str());
- this->DelUser(user);
+ victim->chans.erase(this);
+ this->DelUser(victimiter);
}
void Channel::WriteChannel(User* user, const char* text, ...)
this->WriteAllExcept(user, serversource, status, except_list, std::string(text));
}
-/*
- * return a count of the users on a specific channel accounting for
- * invisible users who won't increase the count. e.g. for /LIST
- */
-int Channel::CountInvisible()
-{
- int count = 0;
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
- {
- if (!i->first->quitting && !i->first->IsModeSet('i'))
- count++;
- }
-
- return count;
-}
-
const char* Channel::ChanModes(bool showkey)
{
static std::string scratch;
if(this->modes[n])
{
scratch.push_back(n + 65);
+ ModeHandler* mh = ServerInstance->Modes->FindMode(n+'A', MODETYPE_CHANNEL);
+ if (!mh)
+ continue;
+
if (n == 'k' - 65 && !showkey)
{
sparam += " <key>";
}
else
{
- const std::string param = this->GetModeParameter(n + 65);
+ const std::string param = this->GetModeParameter(mh);
if (!param.empty())
{
sparam += ' ';
*/
void Channel::UserList(User *user)
{
- char list[MAXBUF];
- size_t dlen, curlen;
-
- if (!IS_LOCAL(user))
- return;
-
- if (this->IsModeSet('s') && !this->HasUser(user) && !user->HasPrivPermission("channels/auspex"))
+ if (this->IsModeSet(secretmode) && !this->HasUser(user) && !user->HasPrivPermission("channels/auspex"))
{
user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
return;
}
- dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
+ std::string list = user->nick;
+ list.push_back(' ');
+ list.push_back(this->IsModeSet(secretmode) ? '@' : this->IsModeSet(privatemode) ? '*' : '=');
+ list.push_back(' ');
+ list.append(this->name).append(" :");
+ std::string::size_type pos = list.size();
- int numusers = 0;
- char* ptr = list + dlen;
+ bool has_one = false;
/* Improvement by Brain - this doesnt change in value, so why was it inside
* the loop?
*/
bool has_user = this->HasUser(user);
- for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
+ std::string prefixlist;
+ std::string nick;
+ for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
{
if (i->first->quitting)
continue;
- if ((!has_user) && (i->first->IsModeSet('i')))
+ if ((!has_user) && (i->first->IsModeSet(invisiblemode)))
{
/*
* user is +i, and source not on the channel, does not show
continue;
}
- std::string prefixlist = this->GetPrefixChar(i->first);
- std::string nick = i->first->nick;
+ prefixlist = this->GetPrefixChar(i->first);
+ nick = i->first->nick;
- FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
+ FOREACH_MOD(OnNamesListItem, (user, i->second, prefixlist, nick));
/* Nick was nuked, a module wants us to skip it */
if (nick.empty())
continue;
- size_t ptrlen = 0;
-
- if (curlen + prefixlist.length() + nick.length() + 1 > 480)
+ if (list.size() + prefixlist.length() + nick.length() + 1 > 480)
{
/* list overflowed into multiple numerics */
- user->WriteNumeric(RPL_NAMREPLY, std::string(list));
+ user->WriteNumeric(RPL_NAMREPLY, list);
- /* reset our lengths */
- dlen = curlen = snprintf(list,MAXBUF,"%s %c %s :", user->nick.c_str(), this->IsModeSet('s') ? '@' : this->IsModeSet('p') ? '*' : '=', this->name.c_str());
- ptr = list + dlen;
-
- numusers = 0;
+ // Erase all nicks, keep the constant part
+ list.erase(pos);
+ has_one = false;
}
- ptrlen = snprintf(ptr, MAXBUF, "%s%s ", prefixlist.c_str(), nick.c_str());
-
- curlen += ptrlen;
- ptr += ptrlen;
+ list.append(prefixlist).append(nick).push_back(' ');
- numusers++;
+ has_one = true;
}
/* if whats left in the list isnt empty, send it */
- if (numusers)
+ if (has_one)
{
- user->WriteNumeric(RPL_NAMREPLY, std::string(list));
+ user->WriteNumeric(RPL_NAMREPLY, list);
}
user->WriteNumeric(RPL_ENDOFNAMES, "%s %s :End of /NAMES list.", user->nick.c_str(), this->name.c_str());
return adding;
}
-void Channel::RemoveAllPrefixes(User* user)
-{
- UserMembIter m = userlist.find(user);
- if (m != userlist.end())
- {
- m->second->modes.clear();
- }
-}
-
void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
{
if ((timeout != 0) && (ServerInstance->Time() >= timeout))