]> git.netwichtig.de Git - user/henk/code/inspircd.git/blobdiff - src/channels.cpp
Use the preprocessor to detect whether hash_map is available.
[user/henk/code/inspircd.git] / src / channels.cpp
index 3f0fc4a36271077bc78b15f056b86a109210c6c7..672d46ea90696e3a63702219e992911a8113462f 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-2009 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"
@@ -36,7 +48,12 @@ void Channel::SetMode(char mode,bool mode_on)
        modes[mode-65] = mode_on;
 }
 
-void Channel::SetModeParam(char mode, std::string parameter)
+void Channel::SetMode(ModeHandler* mh, bool on)
+{
+       modes[mh->GetModeChar() - 65] = 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
@@ -53,6 +70,11 @@ void Channel::SetModeParam(char mode, std::string parameter)
        }
 }
 
+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);
@@ -61,31 +83,36 @@ std::string Channel::GetModeParameter(char mode)
        return "";
 }
 
+std::string Channel::GetModeParameter(ModeHandler* mode)
+{
+       CustomModeList::iterator n = custom_mode_params.find(mode->GetModeChar());
+       if (n != custom_mode_params.end())
+               return n->second;
+       return "";
+}
+
 int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
 {
-       if (u)
+       if (!u)
+               u = ServerInstance->FakeClient;
+       if (IS_LOCAL(u) && !forceset)
        {
-               if(!forceset)
-               {
-                       ModResult res;
-                       /* 0: check status, 1: don't, -1: disallow change silently */
-
-                       FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
+               ModResult res;
+               FIRST_MOD_RESULT(OnPreTopicChange, res, (u,this,ntopic));
 
-                       if (res == MOD_RES_DENY)
+               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 (res != MOD_RES_ALLOW)
+                       }
+                       if (IsModeSet('t') && !ServerInstance->OnCheckExemption(u,this,"topiclock").check(GetPrefixValue(u) >= HALFOP_VALUE))
                        {
-                               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 ((this->IsModeSet('t')) && (this->GetPrefixValue(u) < HALFOP_VALUE))
-                               {
-                                       u->WriteNumeric(482, "%s %s :You must be at least a half-operator to change the topic on this channel", u->nick.c_str(), this->name.c_str());
-                                       return CMD_FAILURE;
-                               }
+                               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;
                        }
                }
        }
@@ -104,11 +131,7 @@ int Channel::SetTopic(User *u, std::string &ntopic, bool forceset)
 
        this->topicset = ServerInstance->Time();
 
-       // XXX: this check for 'u' is probably pre-fake-user, and it fucking sucks anyway. we need to change this.
-       if (u)
-       {
-               FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
-       }
+       FOREACH_MOD(I_OnPostTopicChange,OnPostTopicChange(u, this, this->topic));
 
        return CMD_SUCCESS;
 }
@@ -149,6 +172,8 @@ void Channel::DelUser(User* user)
                        FOREACH_MOD(I_OnChannelDelete, OnChannelDelete(this));
                        ServerInstance->chanlist->erase(iter);
                }
+
+               ClearInvites();
                ServerInstance->GlobalCulls.AddItem(this);
        }
 }
@@ -251,7 +276,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
                {
@@ -377,13 +402,16 @@ 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)
@@ -421,7 +449,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('@');
@@ -515,11 +543,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;
                        }
                }
@@ -625,7 +661,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);
@@ -682,7 +718,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++;
        }
 
@@ -706,30 +742,13 @@ char* Channel::ChanModes(bool showkey)
                {
                        *offset++ = n + 65;
                        extparam.clear();
-                       switch (n)
+                       if (n == 'k' - 65 && !showkey)
                        {
-                               case CM_KEY:
-                                       // Unfortunately this must be special-cased, as we definitely don't want to always display key.
-                                       if (showkey)
-                                       {
-                                               extparam = this->GetModeParameter('k');
-                                       }
-                                       else
-                                       {
-                                               extparam = "<key>";
-                                       }
-                                       break;
-                               case CM_NOEXTERNAL:
-                               case CM_TOPICLOCK:
-                               case CM_INVITEONLY:
-                               case CM_MODERATED:
-                               case CM_SECRET:
-                               case CM_PRIVATE:
-                                       /* We know these have no parameters */
-                               break;
-                               default:
-                                       extparam = this->GetModeParameter(n + 65);
-                               break;
+                               extparam = "<key>";
+                       }
+                       else
+                       {
+                               extparam = this->GetModeParameter(n + 65);
                        }
                        if (!extparam.empty())
                        {
@@ -752,20 +771,14 @@ void Channel::UserList(User *user)
 {
        char list[MAXBUF];
        size_t dlen, curlen;
-       ModResult call_modules;
 
        if (!IS_LOCAL(user))
                return;
 
-       FIRST_MOD_RESULT(OnUserList, call_modules, (user, this));
-
-       if (call_modules != MOD_RES_ALLOW)
+       if (this->IsModeSet('s') && !this->HasUser(user) && !user->HasPrivPermission("channels/auspex"))
        {
-               if ((this->IsModeSet('s')) && (!this->HasUser(user)))
-               {
-                       user->WriteNumeric(ERR_NOSUCHNICK, "%s %s :No such nick/channel",user->nick.c_str(), this->name.c_str());
-                       return;
-               }
+               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());
@@ -780,6 +793,8 @@ void Channel::UserList(User *user)
 
        for (UserMembIter i = userlist.begin(); i != userlist.end(); i++)
        {
+               if (i->first->quitting)
+                       continue;
                if ((!has_user) && (i->first->IsModeSet('i')))
                {
                        /*
@@ -792,14 +807,11 @@ void Channel::UserList(User *user)
                std::string prefixlist = this->GetPrefixChar(i->first);
                std::string nick = i->first->nick;
 
-               if (call_modules != MOD_RES_DENY)
-               {
-                       FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
+               FOREACH_MOD(I_OnNamesListItem, OnNamesListItem(user, i->second, prefixlist, nick));
 
-                       /* Nick was nuked, a module wants us to skip it */
-                       if (nick.empty())
-                               continue;
-               }
+               /* Nick was nuked, a module wants us to skip it */
+               if (nick.empty())
+                       continue;
 
                size_t ptrlen = 0;
 
@@ -812,7 +824,6 @@ void Channel::UserList(User *user)
                        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;
 
-                       ptrlen = 0;
                        numusers = 0;
                }
 
@@ -928,14 +939,14 @@ unsigned int Channel::GetPrefixValue(User* user)
        return m->second->getRank();
 }
 
-void Channel::SetPrefix(User* user, char prefix, bool adding)
+bool Channel::SetPrefix(User* user, char prefix, bool adding)
 {
        ModeHandler* delta_mh = ServerInstance->Modes->FindMode(prefix, MODETYPE_CHANNEL);
        if (!delta_mh)
-               return;
+               return false;
        UserMembIter m = userlist.find(user);
        if (m == userlist.end())
-               return;
+               return false;
        for(unsigned int i=0; i < m->second->modes.length(); i++)
        {
                char mchar = m->second->modes[i];
@@ -946,11 +957,12 @@ void Channel::SetPrefix(User* user, char prefix, bool adding)
                                m->second->modes.substr(0,i) +
                                (adding ? std::string(1, prefix) : "") +
                                m->second->modes.substr(mchar == prefix ? i+1 : i);
-                       return;
+                       return adding != (mchar == prefix);
                }
        }
        if (adding)
                m->second->modes += std::string(1, prefix);
+       return adding;
 }
 
 void Channel::RemoveAllPrefixes(User* user)
@@ -961,3 +973,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;
+       }
+}