]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/channels.cpp
Merge pull request #1018 from SaberUK/insp20+hidekills
[user/henk/code/inspircd.git] / src / channels.cpp
index 5603ef1ddf2f16171b692663f1e845ea2d1081d9..6802e3d7dd7d049468dbe0ce6f52ad2a3171df20 100644 (file)
@@ -1,16 +1,28 @@
-/*       +------------------------------------+
- *       | Inspire Internet Relay Chat Daemon |
- *       +------------------------------------+
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
+ *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
+ *   Copyright (C) 2006, 2008 Oliver Lupton <oliverlupton@gmail.com>
+ *   Copyright (C) 2008 Pippijn van Steenhoven <pip88nl@gmail.com>
+ *   Copyright (C) 2003-2008 Craig Edwards <craigedwards@brainbox.cc>
+ *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
+ *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
  *
- *  InspIRCd: (C) 2002-2010 InspIRCd Development Team
- * See: http://wiki.inspircd.org/Credits
+ * This file is part of InspIRCd.  InspIRCd is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation, version 2.
  *
- * This program is free but copyrighted software; see
- *            the file COPYING for details.
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ * details.
  *
- * ---------------------------------------------------
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+
 /* $Core */
 
 #include "inspircd.h"
 
 Channel::Channel(const std::string &cname, time_t ts)
 {
-       chan_hash::iterator findchan = ServerInstance->chanlist->find(cname);
-       if (findchan != ServerInstance->chanlist->end())
+       if (!ServerInstance->chanlist->insert(std::make_pair(cname, this)).second)
                throw CoreException("Cannot create duplicate channel " + cname);
 
-       (*(ServerInstance->chanlist))[cname.c_str()] = this;
-       this->name.assign(cname, 0, ServerInstance->Config->Limits.ChanMax);
+       this->name = cname;
        this->age = ts ? ts : ServerInstance->Time();
 
        maxbans = topicset = 0;
@@ -92,29 +102,22 @@ int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
                        return CMD_FAILURE;
                if (res != MOD_RES_ALLOW)
                {
-                       bool defok = IsModeSet('t') ? GetPrefixValue(u) >= HALFOP_VALUE : HasUser(u);
-                       if (!ServerInstance->OnCheckExemption(u,this,"topiclock").check(defok))
+                       if (!this->HasUser(u))
                        {
-                               if (!this->HasUser(u))
-                                       u->WriteNumeric(442, "%s %s :You're not on that channel!",u->nick.c_str(), this->name.c_str());
-                               else
-                                       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());
+                               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);
-       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();
 
@@ -159,6 +162,8 @@ void Channel::DelUser(User* user)
                        FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
                        ServerInstance->chanlist->erase(iter);
                }
+
+               ClearInvites();
                ServerInstance->GlobalCulls.AddItem(this);
        }
 }
@@ -197,10 +202,18 @@ void Channel::SetDefaultModes()
                if (mode)
                {
                        if (mode->GetNumParams(true))
+                       {
                                list.GetToken(parameter);
+                               // If the parameter begins with a ':' then it's invalid
+                               if (parameter.c_str()[0] == ':')
+                                       continue;
+                       }
                        else
                                parameter.clear();
 
+                       if ((mode->GetNumParams(true)) && (parameter.empty()))
+                               continue;
+
                        mode->OnModeChange(ServerInstance->FakeClient, ServerInstance->FakeClient, this, parameter, true);
                }
        }
@@ -216,7 +229,6 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char
        if (!user || !cn || user->registered != REG_ALL)
                return NULL;
 
-       char cname[MAXBUF];
        std::string privs;
        Channel *Ptr;
 
@@ -249,7 +261,8 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char
                }
        }
 
-       strlcpy(cname, cn, ServerInstance->Config->Limits.ChanMax);
+       std::string cname;
+       cname.assign(std::string(cn), 0, ServerInstance->Config->Limits.ChanMax);
        Ptr = ServerInstance->FindChan(cname);
        bool created_by_local = false;
 
@@ -261,7 +274,7 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char
                if (!IS_LOCAL(user))
                {
                        if (!TS)
-                               ServerInstance->Logs->Log("CHANNEL",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
+                               ServerInstance->Logs->Log("CHANNELS",DEBUG,"*** BUG *** Channel::JoinUser called for REMOTE user '%s' on channel '%s' but no TS given!", user->nick.c_str(), cn);
                }
                else
                {
@@ -272,7 +285,7 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char
                if (IS_LOCAL(user) && override == false)
                {
                        ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname, privs, key ? key : ""));
+                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, NULL, cname.c_str(), privs, key ? key : ""));
                        if (MOD_RESULT == MOD_RES_DENY)
                                return NULL;
                }
@@ -292,7 +305,7 @@ Channel* Channel::JoinUser(User *user, const char* cn, bool override, const char
                if (IS_LOCAL(user) && override == false)
                {
                        ModResult MOD_RESULT;
-                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname, privs, key ? key : ""));
+                       FIRST_MOD_RESULT(OnUserPreJoin, MOD_RESULT, (user, Ptr, cname.c_str(), privs, key ? key : ""));
                        if (MOD_RESULT == MOD_RES_DENY)
                        {
                                return NULL;
@@ -387,16 +400,19 @@ Channel* Channel::ForceChan(Channel* Ptr, User* user, const std::string &privs,
        Ptr->WriteAllExcept(user, false, 0, except_list, "JOIN :%s", Ptr->name.c_str());
 
        /* Theyre not the first ones in here, make sure everyone else sees the modes we gave the user */
-       std::string ms = memb->modes;
-       for(unsigned int i=0; i < memb->modes.length(); i++)
-               ms.append(" ").append(user->nick);
-       if ((Ptr->GetUserCounter() > 1) && (ms.length()))
-               Ptr->WriteAllExceptSender(user, true, 0, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
+       if ((Ptr->GetUserCounter() > 1) && (!memb->modes.empty()))
+       {
+               std::string ms = memb->modes;
+               for(unsigned int i=0; i < memb->modes.length(); i++)
+                       ms.append(" ").append(user->nick);
+
+               except_list.insert(user);
+               Ptr->WriteAllExcept(user, !ServerInstance->Config->CycleHostsFromUser, 0, except_list, "MODE %s +%s", Ptr->name.c_str(), ms.c_str());
+       }
 
-       /* Major improvement by Brain - we dont need to be calculating all this pointlessly for remote users */
        if (IS_LOCAL(user))
        {
-               if (Ptr->topicset)
+               if (!Ptr->topic.empty())
                {
                        user->WriteNumeric(RPL_TOPIC, "%s %s :%s", user->nick.c_str(), Ptr->name.c_str(), Ptr->topic.c_str());
                        user->WriteNumeric(RPL_TOPICTIME, "%s %s %s %lu", user->nick.c_str(), Ptr->name.c_str(), Ptr->setby.c_str(), (unsigned long)Ptr->topicset);
@@ -431,7 +447,7 @@ bool Channel::CheckBan(User* user, const std::string& mask)
                return (result == MOD_RES_DENY);
 
        // extbans were handled above, if this is one it obviously didn't match
-       if (mask[1] == ':')
+       if ((mask.length() <= 2) || (mask[1] == ':'))
                return false;
 
        std::string::size_type at = mask.find('@');
@@ -525,11 +541,19 @@ void Channel::KickUser(User *src, User *user, const char* reason)
 
                if (res == MOD_RES_PASSTHRU)
                {
-                       int them = this->GetPrefixValue(src);
-                       int us = this->GetPrefixValue(user);
-                       if ((them < HALFOP_VALUE) || (them < us))
+                       unsigned int them = this->GetPrefixValue(src);
+                       unsigned int req = HALFOP_VALUE;
+                       for (std::string::size_type i = 0; i < memb->modes.length(); i++)
+                       {
+                               ModeHandler* mh = ServerInstance->Modes->FindMode(memb->modes[i], MODETYPE_CHANNEL);
+                               if (mh && mh->GetLevelRequired() > req)
+                                       req = mh->GetLevelRequired();
+                       }
+
+                       if (them < req)
                        {
-                               src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",src->nick.c_str(), this->name.c_str(), them >= HALFOP_VALUE ? "" : "half-");
+                               src->WriteNumeric(ERR_CHANOPRIVSNEEDED, "%s %s :You must be a channel %soperator",
+                                       src->nick.c_str(), this->name.c_str(), req > HALFOP_VALUE ? "" : "half-");
                                return;
                        }
                }
@@ -635,7 +659,7 @@ void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList
        if (!text)
                return;
 
-       int offset = snprintf(textbuffer,MAXBUF,":%s ", user->GetFullHost().c_str());
+       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);
@@ -649,7 +673,6 @@ void Channel::WriteAllExcept(User* user, bool serversource, char status, CUList
        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));
 }
@@ -692,7 +715,7 @@ int Channel::CountInvisible()
        int count = 0;
        for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
        {
-               if (!(i->first->IsModeSet('i')))
+               if (!i->first->quitting && !i->first->IsModeSet('i'))
                        count++;
        }
 
@@ -743,31 +766,39 @@ 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"))
+       bool has_privs = user->HasPrivPermission("channels/auspex");
+
+       if (this->IsModeSet('s') && !this->HasUser(user) && !has_privs)
        {
                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++)
+       const size_t maxlen = MAXBUF - 10 - ServerInstance->Config->ServerName.size();
+       std::string prefixlist;
+       std::string nick;
+       for (UserMembIter i = userlist.begin(); i != userlist.end(); ++i)
        {
-               if ((!has_user) && (i->first->IsModeSet('i')))
+               if (i->first->quitting)
+                       continue;
+               if ((!has_user) && (i->first->IsModeSet('i')) && (!has_privs))
                {
                        /*
                         * user is +i, and source not on the channel, does not show
@@ -776,8 +807,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));
 
@@ -785,33 +816,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 > maxlen)
                {
                        /* 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);
 
-                       ptrlen = 0;
-                       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());
@@ -946,3 +969,90 @@ void Channel::RemoveAllPrefixes(User* user)
                m->second->modes.clear();
        }
 }
+
+void Invitation::Create(Channel* c, LocalUser* u, time_t timeout)
+{
+       if ((timeout != 0) && (ServerInstance->Time() >= timeout))
+               // Expired, don't bother
+               return;
+
+       ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create chan=%s user=%s", c->name.c_str(), u->uuid.c_str());
+
+       Invitation* inv = Invitation::Find(c, u, false);
+       if (inv)
+       {
+                if ((inv->expiry == 0) || (inv->expiry > timeout))
+                       return;
+               inv->expiry = timeout;
+               ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
+       }
+       else
+       {
+               inv = new Invitation(c, u, timeout);
+               c->invites.push_back(inv);
+               u->invites.push_back(inv);
+               ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
+       }
+}
+
+Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
+{
+       ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
+       if (!u || u->invites.empty())
+               return NULL;
+
+       InviteList locallist;
+       locallist.swap(u->invites);
+
+       Invitation* result = NULL;
+       for (InviteList::iterator i = locallist.begin(); i != locallist.end(); )
+       {
+               Invitation* inv = *i;
+               if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
+               {
+                       /* Expired invite, remove it. */
+                       std::string expiration = ServerInstance->TimeString(inv->expiry);
+                       ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
+                       i = locallist.erase(i);
+                       inv->cull();
+                       delete inv;
+               }
+               else
+               {
+                       /* Is it what we're searching for? */
+                       if (inv->chan == c)
+                       {
+                               result = inv;
+                               break;
+                       }
+                       ++i;
+               }
+       }
+
+       locallist.swap(u->invites);
+       ServerInstance->Logs->Log("INVITATION", DEBUG, "Invitation::Find result=%p", (void*) result);
+       return result;
+}
+
+Invitation::~Invitation()
+{
+       // Remove this entry from both lists
+       InviteList::iterator it = std::find(chan->invites.begin(), chan->invites.end(), this);
+       if (it != chan->invites.end())
+               chan->invites.erase(it);
+       it = std::find(user->invites.begin(), user->invites.end(), this);
+       if (it != user->invites.end())
+               user->invites.erase(it);
+}
+
+void InviteBase::ClearInvites()
+{
+       ServerInstance->Logs->Log("INVITEBASE", DEBUG, "InviteBase::ClearInvites %p", (void*) this);
+       InviteList locallist;
+       locallist.swap(invites);
+       for (InviteList::const_iterator i = locallist.begin(); i != locallist.end(); ++i)
+       {
+               (*i)->cull();
+               delete *i;
+       }
+}