]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/channels.cpp
Seperate ModeReference into ChanModeReference and UserModeReference
[user/henk/code/inspircd.git] / src / channels.cpp
index 91f9b8072c254caed0c090dcfe32331455c1fd47..0cf05f75ddf577c5d31f6f60eb30384456054312 100644 (file)
  */
 
 
-/* $Core */
-
 #include "inspircd.h"
 #include "listmode.h"
 #include <cstdarg>
 #include "mode.h"
 
-static ModeReference ban(NULL, "ban");
+static ChanModeReference ban(NULL, "ban");
 
 Channel::Channel(const std::string &cname, time_t ts)
 {
@@ -44,11 +42,6 @@ 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;
@@ -56,12 +49,9 @@ void Channel::SetMode(ModeHandler* mh, bool on)
 
 void Channel::SetModeParam(char mode, 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);
        if (parameter.empty())
        {
+               custom_mode_params.erase(mode);
                modes[mode-65] = false;
        }
        else
@@ -92,38 +82,14 @@ std::string Channel::GetModeParameter(ModeHandler* mode)
        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;
 }
 
 Membership* Channel::AddUser(User* user)
@@ -138,32 +104,42 @@ 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(I_OnChannelDelete, 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)
@@ -350,16 +326,16 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
 
 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)
@@ -481,55 +457,54 @@ ModResult Channel::GetExtBanStatus(User *user, char type)
 }
 
 /* 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));
 
                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++)
                        {
@@ -547,18 +522,13 @@ void Channel::KickUser(User *src, User *user, const std::string& reason)
                }
        }
 
-       if (memb)
-       {
-               CUList except_list;
-               FOREACH_MOD(I_OnUserKick,OnUserKick(src, memb, reason, except_list));
+       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());
+       WriteAllExcept(src, false, 0, except_list, "KICK %s %s :%s", name.c_str(), victim->nick.c_str(), reason.c_str());
 
-               user->chans.erase(this);
-               this->RemoveAllPrefixes(user);
-       }
-
-       this->DelUser(user);
+       victim->chans.erase(this);
+       this->DelUser(victimiter);
 }
 
 void Channel::WriteChannel(User* user, const char* text, ...)
@@ -649,22 +619,6 @@ void Channel::WriteAllExceptSender(User* user, bool serversource, char status, c
        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;
@@ -703,29 +657,29 @@ const char* Channel::ChanModes(bool showkey)
  */
 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"))
        {
                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('s') ? '@' : this->IsModeSet('p') ? '*' : '=');
+       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;
@@ -738,8 +692,8 @@ void Channel::UserList(User *user)
                        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));
 
@@ -747,32 +701,25 @@ void Channel::UserList(User *user)
                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));
-
-                       /* 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;
+                       user->WriteNumeric(RPL_NAMREPLY, list);
 
-                       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());
+               list.append(prefixlist).append(nick).push_back(' ');
 
-               curlen += ptrlen;
-               ptr += ptrlen;
-
-               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());
@@ -873,15 +820,6 @@ bool Channel::SetPrefix(User* user, char prefix, bool adding)
        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))