]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/channels.cpp
m_callerid Route ACCEPT to the server of the target user only, do not send METADATA...
[user/henk/code/inspircd.git] / src / channels.cpp
index 9e9ad670aef0fc1ed77a1c90a5230058d9bc86cc..0d43bc802e129b89151ab5935b10f62aee28682f 100644 (file)
@@ -94,8 +94,6 @@ std::string Channel::GetModeParameter(ModeHandler* mode)
 
 int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
 {
-       if (!u)
-               u = ServerInstance->FakeClient;
        if (IS_LOCAL(u) && !forceset)
        {
                ModResult res;
@@ -119,17 +117,8 @@ int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
        }
 
        this->topic.assign(ntopic, 0, ServerInstance->Config->Limits.MaxTopic);
-       if (u)
-       {
-               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());
-       }
-       else
-       {
-               this->setby.assign(ServerInstance->Config->ServerName);
-               this->WriteChannelWithServ(ServerInstance->Config->ServerName, "TOPIC %s :%s", this->name.c_str(), this->topic.c_str());
-       }
-
+       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));
@@ -137,11 +126,6 @@ int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
        return CMD_SUCCESS;
 }
 
-long Channel::GetUserCounter()
-{
-       return userlist.size();
-}
-
 Membership* Channel::AddUser(User* user)
 {
        Membership*& memb = userlist[user];
@@ -154,37 +138,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);
+}
 
-       if (a != userlist.end())
-       {
-               a->second->cull();
-               delete a->second;
-               userlist.erase(a);
-       }
+void Channel::CheckDestroy()
+{
+       if (!userlist.empty())
+               return;
 
-       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);
-               }
+       ModResult res;
+       FIRST_MOD_RESULT(OnChannelPreDelete, res, (this));
+       if (res == MOD_RES_DENY)
+               return;
 
-               ClearInvites();
-               ServerInstance->GlobalCulls.AddItem(this);
+       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);
 }
 
-bool Channel::HasUser(User* user)
+void Channel::DelUser(const UserMembIter& membiter)
 {
-       return (userlist.find(user) != userlist.end());
+       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)
@@ -195,11 +184,6 @@ Membership* Channel::GetUser(User* user)
        return i->second;
 }
 
-const UserMembList* Channel::GetUsers()
-{
-       return &userlist;
-}
-
 void Channel::SetDefaultModes()
 {
        ServerInstance->Logs->Log("CHANNELS", LOG_DEBUG, "SetDefaultModes %s",
@@ -472,10 +456,9 @@ bool Channel::CheckBan(User* user, const std::string& mask)
        if (at == std::string::npos)
                return false;
 
-       char tomatch[MAXBUF];
-       snprintf(tomatch, MAXBUF, "%s!%s", user->nick.c_str(), user->ident.c_str());
+       const std::string nickIdent = user->nick + "!" + user->ident;
        std::string prefix = mask.substr(0, at);
-       if (InspIRCd::Match(tomatch, prefix, NULL))
+       if (InspIRCd::Match(nickIdent, prefix, NULL))
        {
                std::string suffix = mask.substr(at + 1);
                if (InspIRCd::Match(user->host, suffix, NULL) ||
@@ -508,61 +491,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)
 {
-       if (!user)
-               return;
+       UserMembIter membiter = userlist.find(user);
 
-       Membership* memb = GetUser(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 char* reason)
+void Channel::KickUser(User* src, User* victim, const std::string& reason, Membership* srcmemb)
 {
-       if (!src || !user || !reason)
+       UserMembIter victimiter = userlist.find(victim);
+       Membership* memb = ((victimiter != userlist.end()) ? victimiter->second : NULL);
+
+       if (!memb)
+       {
+               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;
+       }
 
-       Membership* memb = GetUser(user);
+       // Do the following checks only if the KICK is done by a local user;
+       // each server enforces its own rules.
        if (IS_LOCAL(src))
        {
-               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;
-               }
-
+               // 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++)
                        {
@@ -580,78 +556,48 @@ void Channel::KickUser(User *src, User *user, const char* reason)
                }
        }
 
-       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);
+       CUList except_list;
+       FOREACH_MOD(I_OnUserKick,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, ...)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (!user || !text)
-               return;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteChannel(user, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteChannel(user, textbuffer);
 }
 
 void Channel::WriteChannel(User* user, const std::string &text)
 {
-       char tb[MAXBUF];
-
-       if (!user)
-               return;
-
-       snprintf(tb,MAXBUF,":%s %s", user->GetFullHost().c_str(), text.c_str());
-       std::string out = tb;
+       const std::string message = ":" + user->GetFullHost() + " " + text;
 
        for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
        {
                if (IS_LOCAL(i->first))
-                       i->first->Write(out);
+                       i->first->Write(message);
        }
 }
 
 void Channel::WriteChannelWithServ(const std::string& ServName, const char* text, ...)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (!text)
-               return;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteChannelWithServ(ServName, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteChannelWithServ(ServName, textbuffer);
 }
 
 void Channel::WriteChannelWithServ(const std::string& ServName, const std::string &text)
 {
-       char tb[MAXBUF];
-
-       snprintf(tb,MAXBUF,":%s %s", ServName.empty() ? ServerInstance->Config->ServerName.c_str() : ServName.c_str(), text.c_str());
-       std::string out = tb;
+       const std::string message = ":" + (ServName.empty() ? ServerInstance->Config->ServerName : ServName) + " " + text;
 
        for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
        {
                if (IS_LOCAL(i->first))
-                       i->first->Write(out);
+                       i->first->Write(message);
        }
 }
 
@@ -659,44 +605,23 @@ void Channel::WriteChannelWithServ(const std::string& ServName, const std::strin
  * for the sender (for privmsg etc) */
 void Channel::WriteAllExceptSender(User* user, bool serversource, char status, const char* text, ...)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (!text)
-               return;
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer, MAXBUF, text, argsPtr);
-       va_end(argsPtr);
-
-       this->WriteAllExceptSender(user, serversource, status, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       this->WriteAllExceptSender(user, serversource, status, textbuffer);
 }
 
 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const char* text, ...)
 {
-       char textbuffer[MAXBUF];
-       va_list argsPtr;
-
-       if (!text)
-               return;
-
-       int offset = snprintf(textbuffer,MAXBUF,":%s ", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str());
-
-       va_start(argsPtr, text);
-       vsnprintf(textbuffer + offset, MAXBUF - offset, text, argsPtr);
-       va_end(argsPtr);
-
-       this->RawWriteAllExcept(user, serversource, status, except_list, std::string(textbuffer));
+       std::string textbuffer;
+       VAFORMAT(textbuffer, text, text);
+       textbuffer = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + textbuffer;
+       this->RawWriteAllExcept(user, serversource, status, except_list, textbuffer);
 }
 
 void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &text)
 {
-       char tb[MAXBUF];
-
-       snprintf(tb,MAXBUF,":%s %s", serversource ? ServerInstance->Config->ServerName.c_str() : user->GetFullHost().c_str(), text.c_str());
-       std::string out = tb;
-
-       this->RawWriteAllExcept(user, serversource, status, except_list, std::string(tb));
+       const std::string message = ":" + (serversource ? ServerInstance->Config->ServerName : user->GetFullHost()) + " " + text;
+       this->RawWriteAllExcept(user, serversource, status, except_list, message);
 }
 
 void Channel::RawWriteAllExcept(User* user, bool serversource, char status, CUList &except_list, const std::string &out)
@@ -728,59 +653,37 @@ 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()
+const char* Channel::ChanModes(bool showkey)
 {
-       int count = 0;
-       for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
-       {
-               if (!i->first->quitting && !i->first->IsModeSet('i'))
-                       count++;
-       }
-
-       return count;
-}
-
-char* Channel::ChanModes(bool showkey)
-{
-       static char scratch[MAXBUF];
-       static char sparam[MAXBUF];
-       char* offset = scratch;
-       std::string extparam;
+       static std::string scratch;
+       std::string sparam;
 
-       *scratch = '\0';
-       *sparam = '\0';
+       scratch.clear();
 
        /* This was still iterating up to 190, Channel::modes is only 64 elements -- Om */
        for(int n = 0; n < 64; n++)
        {
                if(this->modes[n])
                {
-                       *offset++ = n + 65;
-                       extparam.clear();
+                       scratch.push_back(n + 65);
                        if (n == 'k' - 65 && !showkey)
                        {
-                               extparam = "<key>";
+                               sparam += " <key>";
                        }
                        else
                        {
-                               extparam = this->GetModeParameter(n + 65);
-                       }
-                       if (!extparam.empty())
-                       {
-                               charlcat(sparam,' ',MAXBUF);
-                               strlcat(sparam,extparam.c_str(),MAXBUF);
+                               const std::string param = this->GetModeParameter(n + 65);
+                               if (!param.empty())
+                               {
+                                       sparam += ' ';
+                                       sparam += param;
+                               }
                        }
                }
        }
 
-       /* Null terminate scratch */
-       *offset = '\0';
-       strlcat(scratch,sparam,MAXBUF);
-       return scratch;
+       scratch += sparam;
+       return scratch.c_str();
 }
 
 /* compile a userlist of a channel into a string, each nick seperated by
@@ -788,29 +691,29 @@ 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;
@@ -823,8 +726,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));
 
@@ -832,32 +735,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));
+                       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());
@@ -958,15 +854,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))