summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAttila Molnar <attilamolnar@hush.com>2015-11-03 12:40:24 +0100
committerAttila Molnar <attilamolnar@hush.com>2015-11-03 12:40:24 +0100
commit2c51a2bf17b6c377f0207f3d89b4b3f399fc0178 (patch)
tree93f446719933bcfcb2f3580fef778a37c70dc57e
parenta2461fb7e1c28e28e64b61256d7a547162a83f6e (diff)
parenta124a3783d8e8136b63f188bbd292c391811df54 (diff)
Merge branch 'master+invite'
-rw-r--r--include/channels.h2
-rw-r--r--include/configreader.h4
-rw-r--r--include/membership.h83
-rw-r--r--include/modules/invite.h128
-rw-r--r--include/usermanager.h1
-rw-r--r--include/users.h23
-rw-r--r--src/channels.cpp86
-rw-r--r--src/configreader.cpp3
-rw-r--r--src/coremods/core_channel/cmd_invite.cpp13
-rw-r--r--src/coremods/core_channel/core_channel.cpp66
-rw-r--r--src/coremods/core_channel/core_channel.h9
-rw-r--r--src/coremods/core_channel/invite.cpp208
-rw-r--r--src/coremods/core_channel/invite.h127
-rw-r--r--src/modules/m_override.cpp5
-rw-r--r--src/modules/m_spanningtree/fjoin.cpp3
-rw-r--r--src/modules/m_uninvite.cpp8
-rw-r--r--src/usermanager.cpp3
-rw-r--r--src/users.cpp23
18 files changed, 560 insertions, 235 deletions
diff --git a/include/channels.h b/include/channels.h
index 4d1d14c13..be84ac800 100644
--- a/include/channels.h
+++ b/include/channels.h
@@ -34,7 +34,7 @@
* This class represents a channel, and contains its name, modes, topic, topic set time,
* etc, and an instance of the BanList type.
*/
-class CoreExport Channel : public Extensible, public InviteBase<Channel>
+class CoreExport Channel : public Extensible
{
public:
/** A map of Memberships on a channel keyed by User pointers
diff --git a/include/configreader.h b/include/configreader.h
index 57d7ab069..9b73dd3c6 100644
--- a/include/configreader.h
+++ b/include/configreader.h
@@ -495,10 +495,6 @@ class CoreExport ServerConfig
*/
static std::string Escape(const std::string& str, bool xml = true);
- /** If this value is true, invites will bypass more than just +i
- */
- bool InvBypassModes;
-
/** If this value is true, snotices will not stack when repeats are sent
*/
bool NoSnoticeStack;
diff --git a/include/membership.h b/include/membership.h
index 11c142912..05d6b3796 100644
--- a/include/membership.h
+++ b/include/membership.h
@@ -112,86 +112,3 @@ class CoreExport Membership : public Extensible, public insp::intrusive_list_nod
*/
const char* GetAllPrefixChars() const;
};
-
-template <typename T>
-class InviteBase
-{
- protected:
- /** List of pending Invitations
- */
- insp::intrusive_list<Invitation, T> invites;
-
- public:
- /** Remove and destruct all pending invitations this user or channel has.
- * Must be called before the object is destroyed, also called when the TS of the channel is lowered.
- */
- void ClearInvites();
-
- friend class Invitation;
-};
-
-/**
- * The Invitation class contains all data about a pending invitation.
- * Invitation objects are referenced from the user and the channel they belong to.
- */
-class CoreExport Invitation : public insp::intrusive_list_node<Invitation, Channel>, public insp::intrusive_list_node<Invitation, LocalUser>
-{
- /** Constructs an Invitation, only called by Create()
- * @param c Channel the user is invited to
- * @param u User being invited
- * @param timeout Expiration time for this Invitation
- */
- Invitation(Channel* c, LocalUser* u, time_t timeout) : user(u), chan(c), expiry(timeout) {}
-
- public:
- /** User the invitation is for
- */
- LocalUser* const user;
-
- /** Channel where the user is invited to
- */
- Channel* const chan;
-
- /** Timestamp when this Invitation expires or 0 if it doesn't expire.
- * Invitation::Create() can update this field; see that for more info.
- */
- time_t expiry;
-
- /** Destructor
- * Removes references to this Invitation from the associated user and channel.
- */
- ~Invitation();
-
- /** Create or extend an Invitation.
- * When a user is invited to join a channel either a new Invitation object is created or
- * or the expiration timestamp is updated if there is already a pending Invitation for
- * the given (user, channel) pair and the new expiration time is further than the current.
- * @param c Target channel
- * @param u Target user
- * @param timeout Timestamp when the invite should expire, 0 for no expiration
- */
- static void Create(Channel* c, LocalUser* u, time_t timeout);
-
- /** Finds the Invitation object for the given channel/user pair.
- * @param c Target channel, can be NULL to remove expired entries
- * @param u Target user, cannot be NULL
- * @param check_expired Pass true to remove all expired invites found while searching, false
- * to return with an Invitation even if it's expired
- * @return Invitation object for the given (channel, user) pair if it exists, NULL otherwise
- */
- static Invitation* Find(Channel* c, LocalUser* u, bool check_expired = true);
-};
-
-typedef insp::intrusive_list<Invitation, LocalUser> InviteList;
-
-template<typename T>
-inline void InviteBase<T>::ClearInvites()
-{
- for (typename insp::intrusive_list<Invitation, T>::iterator i = invites.begin(); i != invites.end(); )
- {
- Invitation* inv = *i;
- // Destructing the Invitation invalidates the iterator, so move it now
- ++i;
- delete inv;
- }
-}
diff --git a/include/modules/invite.h b/include/modules/invite.h
new file mode 100644
index 000000000..e53d5202f
--- /dev/null
+++ b/include/modules/invite.h
@@ -0,0 +1,128 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012, 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * 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 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/>.
+ */
+
+
+#pragma once
+
+namespace Invite
+{
+ class APIBase;
+ class API;
+ class Invite;
+
+ typedef insp::intrusive_list<Invite, LocalUser> List;
+}
+
+class Invite::APIBase : public DataProvider
+{
+ public:
+ APIBase(Module* parent);
+
+ /** Create or extend an Invite.
+ * When a user is invited to join a channel either a new Invite object is created or
+ * or the expiration timestamp is updated if there is already a pending Invite for
+ * the given (user, channel) pair and the new expiration time is further than the current.
+ * @param user Target user
+ * @param chan Target channel
+ * @param timeout Timestamp when the invite should expire, 0 for no expiration
+ */
+ virtual void Create(LocalUser* user, Channel* chan, time_t timeout) = 0;
+
+ /** Retrieves the Invite object for the given (user, channel) pair
+ * @param user Target user
+ * @param chan Target channel
+ * @return Invite object for the given (channel, user) pair if it exists, NULL otherwise
+ */
+ virtual Invite* Find(LocalUser* user, Channel* chan) = 0;
+
+ /** Returns the list of channels a user has been invited to but has not yet joined.
+ * @param user User whose invite list to retrieve
+ * @return List of channels the user is invited to or NULL if the list is empty
+ */
+ virtual const List* GetList(LocalUser* user) = 0;
+
+ /** Check if a user is invited to a channel
+ * @param user User to check
+ * @param chan Channel to check
+ * @return True if the user is invited to the channel, false otherwise
+ */
+ bool IsInvited(LocalUser* user, Channel* chan) { return (Find(user, chan) != NULL); }
+
+ /** Removes an Invite if it exists
+ * @param user User whose invite to remove
+ * @param chan Channel to remove the invite to
+ * @return True if the user was invited to the channel and the invite was removed, false if the user wasn't invited
+ */
+ virtual bool Remove(LocalUser* user, Channel* chan) = 0;
+};
+
+class Invite::API : public dynamic_reference<APIBase>
+{
+ public:
+ API(Module* parent)
+ : dynamic_reference<APIBase>(parent, "core_channel_invite")
+ {
+ }
+};
+
+/**
+ * The Invite class contains all data about a pending invite.
+ * Invite objects are referenced from the user and the channel they belong to.
+ */
+class Invite::Invite : public insp::intrusive_list_node<Invite, LocalUser>, public insp::intrusive_list_node<Invite, Channel>
+{
+ public:
+ /** User the invite is for
+ */
+ LocalUser* const user;
+
+ /** Channel where the user is invited to
+ */
+ Channel* const chan;
+
+ /** Check whether the invite will expire or not
+ * @return True if the invite is timed, false if it doesn't expire
+ */
+ bool IsTimed() const { return (expiretimer != NULL); }
+
+ /** Serialize this object
+ * @param format Serialization format
+ * @param show_chans True to include channel in the output, false to include the nick/uuid
+ * @param out Output will be appended to this string
+ */
+ void Serialize(SerializeFormat format, bool show_chans, std::string& out);
+
+ friend class APIImpl;
+
+ private:
+ /** Timer handling expiration. If NULL this invite doesn't expire.
+ */
+ Timer* expiretimer;
+
+ /** Constructor, only available to the module providing the invite API (core_channel).
+ * To create Invites use InviteAPI::Create().
+ * @param user User being invited
+ * @param chan Channel the user is invited to
+ */
+ Invite(LocalUser* user, Channel* chan);
+
+ /** Destructor, only available to the module providing the invite API (core_channel).
+ * To remove Invites use InviteAPI::Remove().
+ */
+ ~Invite();
+};
diff --git a/include/usermanager.h b/include/usermanager.h
index a67f90224..eee076802 100644
--- a/include/usermanager.h
+++ b/include/usermanager.h
@@ -85,7 +85,6 @@ class CoreExport UserManager : public fakederef<UserManager>
/**
* Reset the already_sent IDs so we don't wrap it around and drop a message
- * Also removes all expired invites
*/
void GarbageCollect();
diff --git a/include/users.h b/include/users.h
index fa8f610bc..03540018b 100644
--- a/include/users.h
+++ b/include/users.h
@@ -641,7 +641,7 @@ class CoreExport UserIOHandler : public StreamSocket
typedef unsigned int already_sent_t;
-class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public insp::intrusive_list_node<LocalUser>
+class CoreExport LocalUser : public User, public insp::intrusive_list_node<LocalUser>
{
public:
LocalUser(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
@@ -748,27 +748,6 @@ class CoreExport LocalUser : public User, public InviteBase<LocalUser>, public i
void Write(const std::string& text);
void Write(const char*, ...) CUSTOM_PRINTF(2, 3);
- /** Returns the list of channels this user has been invited to but has not yet joined.
- * @return A list of channels the user is invited to
- */
- InviteList& GetInviteList();
-
- /** Returns true if a user is invited to a channel.
- * @param chan A channel to look up
- * @return True if the user is invited to the given channel
- */
- bool IsInvited(Channel* chan) { return (Invitation::Find(chan, this) != NULL); }
-
- /** Removes a channel from a users invite list.
- * This member function is called on successfully joining an invite only channel
- * to which the user has previously been invited, to clear the invitation.
- * @param chan The channel to remove the invite to
- * @return True if the user was invited to the channel and the invite was erased, false if the user wasn't invited
- */
- bool RemoveInvite(Channel* chan);
-
- void RemoveExpiredInvites();
-
/** Returns true or false for if a user can execute a privilaged oper command.
* This is done by looking up their oper type from User::oper, then referencing
* this to their oper classes and checking the commands they can execute.
diff --git a/src/channels.cpp b/src/channels.cpp
index d99a57723..6e3bc5e04 100644
--- a/src/channels.cpp
+++ b/src/channels.cpp
@@ -90,7 +90,6 @@ void Channel::CheckDestroy()
FOREACH_MOD(OnChannelDelete, (this));
ServerInstance->chanlist.erase(iter);
- ClearInvites();
ServerInstance->GlobalCulls.AddItem(this);
}
@@ -235,13 +234,10 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
if (MOD_RESULT == MOD_RES_PASSTHRU)
{
std::string ckey = chan->GetModeParameter(keymode);
- bool invited = user->IsInvited(chan);
- bool can_bypass = ServerInstance->Config->InvBypassModes && invited;
-
if (!ckey.empty())
{
FIRST_MOD_RESULT(OnCheckKey, MOD_RESULT, (user, chan, key));
- if (!MOD_RESULT.check(InspIRCd::TimingSafeCompare(ckey, key) || can_bypass))
+ if (!MOD_RESULT.check(InspIRCd::TimingSafeCompare(ckey, key)))
{
// If no key provided, or key is not the right one, and can't bypass +k (not invited or option not enabled)
user->WriteNumeric(ERR_BADCHANNELKEY, "%s :Cannot join channel (Incorrect channel key)", chan->name.c_str());
@@ -252,7 +248,7 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
if (chan->IsModeSet(inviteonlymode))
{
FIRST_MOD_RESULT(OnCheckInvite, MOD_RESULT, (user, chan));
- if (!MOD_RESULT.check(invited))
+ if (MOD_RESULT != MOD_RES_ALLOW)
{
user->WriteNumeric(ERR_INVITEONLYCHAN, "%s :Cannot join channel (Invite only)", chan->name.c_str());
return NULL;
@@ -263,27 +259,18 @@ Channel* Channel::JoinUser(LocalUser* user, std::string cname, bool override, co
if (!limit.empty())
{
FIRST_MOD_RESULT(OnCheckLimit, MOD_RESULT, (user, chan));
- if (!MOD_RESULT.check((chan->GetUserCounter() < atol(limit.c_str()) || can_bypass)))
+ if (!MOD_RESULT.check((chan->GetUserCounter() < atol(limit.c_str()))))
{
user->WriteNumeric(ERR_CHANNELISFULL, "%s :Cannot join channel (Channel is full)", chan->name.c_str());
return NULL;
}
}
- if (chan->IsBanned(user) && !can_bypass)
+ if (chan->IsBanned(user))
{
user->WriteNumeric(ERR_BANNEDFROMCHAN, "%s :Cannot join channel (You're banned)", chan->name.c_str());
return NULL;
}
-
- /*
- * If the user has invites for this channel, remove them now
- * after a successful join so they don't build up.
- */
- if (invited)
- {
- user->RemoveInvite(chan);
- }
}
}
}
@@ -650,68 +637,3 @@ bool Membership::SetPrefix(PrefixMode* delta_mh, bool adding)
modes.push_back(prefix);
return adding;
}
-
-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", LOG_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", LOG_DEBUG, "Invitation::Create changed expiry in existing invitation %p", (void*) inv);
- }
- else
- {
- inv = new Invitation(c, u, timeout);
- c->invites.push_front(inv);
- u->invites.push_front(inv);
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Create created new invitation %p", (void*) inv);
- }
-}
-
-Invitation* Invitation::Find(Channel* c, LocalUser* u, bool check_expired)
-{
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find chan=%s user=%s check_expired=%d", c ? c->name.c_str() : "NULL", u ? u->uuid.c_str() : "NULL", check_expired);
-
- Invitation* result = NULL;
- for (InviteList::iterator i = u->invites.begin(); i != u->invites.end(); )
- {
- Invitation* inv = *i;
- ++i;
-
- if ((check_expired) && (inv->expiry != 0) && (inv->expiry <= ServerInstance->Time()))
- {
- /* Expired invite, remove it. */
- std::string expiration = InspIRCd::TimeString(inv->expiry);
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find ecountered expired entry: %p expired %s", (void*) inv, expiration.c_str());
- delete inv;
- }
- else
- {
- /* Is it what we're searching for? */
- if (inv->chan == c)
- {
- result = inv;
- break;
- }
- }
- }
-
- ServerInstance->Logs->Log("INVITATION", LOG_DEBUG, "Invitation::Find result=%p", (void*) result);
- return result;
-}
-
-Invitation::~Invitation()
-{
- // Remove this entry from both lists
- chan->invites.erase(this);
- user->invites.erase(this);
- ServerInstance->Logs->Log("INVITEBASE", LOG_DEBUG, "Invitation::~ %p", (void*) this);
-}
diff --git a/src/configreader.cpp b/src/configreader.cpp
index 974e52abf..a81a1b646 100644
--- a/src/configreader.cpp
+++ b/src/configreader.cpp
@@ -56,7 +56,7 @@ ServerConfig::ServerConfig()
, NoSnoticeStack(false)
{
RawLog = HideBans = HideSplits = UndernetMsgPrefix = false;
- WildcardIPv6 = InvBypassModes = true;
+ WildcardIPv6 = true;
dns_timeout = 5;
MaxTargets = 20;
NetBufferSize = 10240;
@@ -427,7 +427,6 @@ void ServerConfig::Fill()
Paths.Data = ConfValue("path")->getString("datadir", INSPIRCD_DATA_PATH);
Paths.Log = ConfValue("path")->getString("logdir", INSPIRCD_LOG_PATH);
Paths.Module = ConfValue("path")->getString("moduledir", INSPIRCD_MODULE_PATH);
- InvBypassModes = options->getBool("invitebypassmodes", true);
NoSnoticeStack = options->getBool("nosnoticestack", false);
if (Network.find(' ') != std::string::npos)
diff --git a/src/coremods/core_channel/cmd_invite.cpp b/src/coremods/core_channel/cmd_invite.cpp
index 7bf669b29..96f560582 100644
--- a/src/coremods/core_channel/cmd_invite.cpp
+++ b/src/coremods/core_channel/cmd_invite.cpp
@@ -22,9 +22,11 @@
#include "inspircd.h"
#include "core_channel.h"
+#include "invite.h"
-CommandInvite::CommandInvite(Module* parent)
+CommandInvite::CommandInvite(Module* parent, Invite::APIImpl& invapiimpl)
: Command(parent, "INVITE", 0, 0)
+ , invapi(invapiimpl)
{
Penalty = 4;
syntax = "[<nick> <channel>]";
@@ -109,7 +111,7 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
if (IS_LOCAL(u))
{
- Invitation::Create(c, IS_LOCAL(u), timeout);
+ invapi.Create(IS_LOCAL(u), c, timeout);
u->WriteFrom(user,"INVITE %s :%s",u->nick.c_str(),c->name.c_str());
}
@@ -150,10 +152,11 @@ CmdResult CommandInvite::Handle (const std::vector<std::string>& parameters, Use
{
// pinched from ircu - invite with not enough parameters shows channels
// youve been invited to but haven't joined yet.
- InviteList& il = IS_LOCAL(user)->GetInviteList();
- for (InviteList::const_iterator i = il.begin(); i != il.end(); ++i)
+ const Invite::List* list = invapi.GetList(IS_LOCAL(user));
+ if (list)
{
- user->WriteNumeric(RPL_INVITELIST, ":%s", (*i)->chan->name.c_str());
+ for (Invite::List::const_iterator i = list->begin(); i != list->end(); ++i)
+ user->WriteNumeric(RPL_INVITELIST, ":%s", (*i)->chan->name.c_str());
}
user->WriteNumeric(RPL_ENDOFINVITELIST, ":End of INVITE list");
}
diff --git a/src/coremods/core_channel/core_channel.cpp b/src/coremods/core_channel/core_channel.cpp
index 99ad74d3d..aba4d9769 100644
--- a/src/coremods/core_channel/core_channel.cpp
+++ b/src/coremods/core_channel/core_channel.cpp
@@ -1,7 +1,7 @@
/*
* InspIRCd -- Internet Relay Chat Daemon
*
- * Copyright (C) 2014 Attila Molnar <attilamolnar@hush.com>
+ * Copyright (C) 2014-2015 Attila Molnar <attilamolnar@hush.com>
*
* 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
@@ -19,27 +19,54 @@
#include "inspircd.h"
#include "core_channel.h"
+#include "invite.h"
class CoreModChannel : public Module
{
+ Invite::APIImpl invapi;
CommandInvite cmdinvite;
CommandJoin cmdjoin;
CommandKick cmdkick;
CommandNames cmdnames;
CommandTopic cmdtopic;
+ ModResult IsInvited(User* user, Channel* chan)
+ {
+ LocalUser* localuser = IS_LOCAL(user);
+ if ((localuser) && (invapi.IsInvited(localuser, chan)))
+ return MOD_RES_ALLOW;
+ return MOD_RES_PASSTHRU;
+ }
+
public:
CoreModChannel()
- : cmdinvite(this), cmdjoin(this), cmdkick(this), cmdnames(this), cmdtopic(this)
+ : invapi(this)
+ , cmdinvite(this, invapi), cmdjoin(this), cmdkick(this), cmdnames(this), cmdtopic(this)
{
}
+ void ReadConfig(ConfigStatus& status) CXX11_OVERRIDE
+ {
+ ConfigTag* optionstag = ServerInstance->Config->ConfValue("options");
+ Implementation events[] = { I_OnCheckKey, I_OnCheckLimit, I_OnCheckChannelBan };
+ if (optionstag->getBool("invitebypassmodes", true))
+ ServerInstance->Modules.Attach(events, this, sizeof(events)/sizeof(Implementation));
+ else
+ {
+ for (unsigned int i = 0; i < sizeof(events)/sizeof(Implementation); i++)
+ ServerInstance->Modules.Detach(events[i], this);
+ }
+ }
+
void OnPostJoin(Membership* memb) CXX11_OVERRIDE
{
Channel* const chan = memb->chan;
LocalUser* const localuser = IS_LOCAL(memb->user);
if (localuser)
{
+ // Remove existing invite, if any
+ invapi.Remove(localuser, chan);
+
if (chan->topicset)
Topic::ShowTopic(localuser, chan);
@@ -48,6 +75,41 @@ class CoreModChannel : public Module
}
}
+ ModResult OnCheckKey(User* user, Channel* chan, const std::string& keygiven) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckChannelBan(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckLimit(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook only runs when being invited bypasses +bkl
+ return IsInvited(user, chan);
+ }
+
+ ModResult OnCheckInvite(User* user, Channel* chan) CXX11_OVERRIDE
+ {
+ // Hook always runs
+ return IsInvited(user, chan);
+ }
+
+ void OnUserDisconnect(LocalUser* user) CXX11_OVERRIDE
+ {
+ invapi.RemoveAll(user);
+ }
+
+ void OnChannelDelete(Channel* chan) CXX11_OVERRIDE
+ {
+ // Make sure the channel won't appear in invite lists from now on, don't wait for cull to unset the ext
+ invapi.RemoveAll(chan);
+ }
+
void Prioritize() CXX11_OVERRIDE
{
ServerInstance->Modules.SetPriority(this, I_OnPostJoin, PRIORITY_FIRST);
diff --git a/src/coremods/core_channel/core_channel.h b/src/coremods/core_channel/core_channel.h
index 755f876f6..2a2800523 100644
--- a/src/coremods/core_channel/core_channel.h
+++ b/src/coremods/core_channel/core_channel.h
@@ -26,14 +26,21 @@ namespace Topic
void ShowTopic(LocalUser* user, Channel* chan);
}
+namespace Invite
+{
+ class APIImpl;
+}
+
/** Handle /INVITE.
*/
class CommandInvite : public Command
{
+ Invite::APIImpl& invapi;
+
public:
/** Constructor for invite.
*/
- CommandInvite (Module* parent);
+ CommandInvite(Module* parent, Invite::APIImpl& invapiimpl);
/** Handle command.
* @param parameters The parameters to the command
diff --git a/src/coremods/core_channel/invite.cpp b/src/coremods/core_channel/invite.cpp
new file mode 100644
index 000000000..7ac662edc
--- /dev/null
+++ b/src/coremods/core_channel/invite.cpp
@@ -0,0 +1,208 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2012, 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * 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 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/>.
+ */
+
+
+#include "inspircd.h"
+
+#include "invite.h"
+
+class InviteExpireTimer : public Timer
+{
+ Invite::Invite* const inv;
+
+ bool Tick(time_t currtime) CXX11_OVERRIDE;
+
+ public:
+ InviteExpireTimer(Invite::Invite* invite, time_t timeout);
+};
+
+static Invite::APIImpl* apiimpl;
+
+void RemoveInvite(Invite::Invite* inv, bool remove_user, bool remove_chan)
+{
+ apiimpl->Destruct(inv, remove_user, remove_chan);
+}
+
+void UnserializeInvite(LocalUser* user, const std::string& str)
+{
+ apiimpl->Unserialize(user, str);
+}
+
+Invite::APIBase::APIBase(Module* parent)
+ : DataProvider(parent, "core_channel_invite")
+{
+}
+
+Invite::APIImpl::APIImpl(Module* parent)
+ : APIBase(parent)
+ , userext(parent, "invite_user")
+ , chanext(parent, "invite_chan")
+{
+ apiimpl = this;
+}
+
+void Invite::APIImpl::Destruct(Invite* inv, bool remove_user, bool remove_chan)
+{
+ Store<LocalUser>* ustore = userext.get(inv->user);
+ if (ustore)
+ {
+ ustore->invites.erase(inv);
+ if ((remove_user) && (ustore->invites.empty()))
+ userext.unset(inv->user);
+ }
+
+ Store<Channel>* cstore = chanext.get(inv->chan);
+ if (cstore)
+ {
+ cstore->invites.erase(inv);
+ if ((remove_chan) && (cstore->invites.empty()))
+ chanext.unset(inv->chan);
+ }
+
+ delete inv;
+}
+
+bool Invite::APIImpl::Remove(LocalUser* user, Channel* chan)
+{
+ Invite* inv = Find(user, chan);
+ if (inv)
+ {
+ Destruct(inv);
+ return true;
+ }
+ return false;
+}
+
+void Invite::APIImpl::Create(LocalUser* user, Channel* chan, time_t timeout)
+{
+ if ((timeout != 0) && (ServerInstance->Time() >= timeout))
+ // Expired, don't bother
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): user=%s chan=%s timeout=%lu", user->uuid.c_str(), chan->name.c_str(), (unsigned long)timeout);
+
+ Invite* inv = Find(user, chan);
+ if (inv)
+ {
+ // We only ever extend invites, so nothing to do if the existing one is not timed
+ if (!inv->IsTimed())
+ return;
+
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): changing expiration in %p", (void*) inv);
+ if (timeout == 0)
+ {
+ // Convert timed invite to non-expiring
+ delete inv->expiretimer;
+ inv->expiretimer = NULL;
+ }
+ else if (inv->expiretimer->GetTrigger() >= ServerInstance->Time() + timeout)
+ {
+ // New expiration time is further than the current, extend the expiration
+ inv->expiretimer->SetInterval(timeout - ServerInstance->Time());
+ }
+ }
+ else
+ {
+ inv = new Invite(user, chan);
+ if (timeout)
+ {
+ inv->expiretimer = new InviteExpireTimer(inv, timeout - ServerInstance->Time());
+ ServerInstance->Timers.AddTimer(inv->expiretimer);
+ }
+
+ userext.get(user, true)->invites.push_front(inv);
+ chanext.get(chan, true)->invites.push_front(inv);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::APIImpl::Create(): created new Invite %p", (void*) inv);
+ }
+}
+
+Invite::Invite* Invite::APIImpl::Find(LocalUser* user, Channel* chan)
+{
+ const List* list = APIImpl::GetList(user);
+ if (!list)
+ return NULL;
+
+ for (List::iterator i = list->begin(); i != list->end(); ++i)
+ {
+ Invite* inv = *i;
+ if (inv->chan == chan)
+ return inv;
+ }
+
+ return NULL;
+}
+
+const Invite::List* Invite::APIImpl::GetList(LocalUser* user)
+{
+ Store<LocalUser>* list = userext.get(user);
+ if (list)
+ return &list->invites;
+ return NULL;
+}
+
+void Invite::APIImpl::Unserialize(LocalUser* user, const std::string& value)
+{
+ irc::spacesepstream ss(value);
+ for (std::string channame, exptime; (ss.GetToken(channame) && ss.GetToken(exptime)); )
+ {
+ Channel* chan = ServerInstance->FindChan(channame);
+ if (chan)
+ Create(user, chan, ConvToInt(exptime));
+ }
+}
+
+Invite::Invite::Invite(LocalUser* u, Channel* c)
+ : user(u)
+ , chan(c)
+ , expiretimer(NULL)
+{
+}
+
+Invite::Invite::~Invite()
+{
+ delete expiretimer;
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "Invite::~ %p", (void*) this);
+}
+
+void Invite::Invite::Serialize(SerializeFormat format, bool show_chans, std::string& out)
+{
+ if (show_chans)
+ out.append(this->chan->name);
+ else
+ out.append((format == FORMAT_USER) ? user->nick : user->uuid);
+ out.push_back(' ');
+
+ if (expiretimer)
+ out.append(ConvToStr(expiretimer->GetTrigger()));
+ else
+ out.push_back('0');
+ out.push_back(' ');
+}
+
+InviteExpireTimer::InviteExpireTimer(Invite::Invite* invite, time_t timeout)
+ : Timer(timeout)
+ , inv(invite)
+{
+}
+
+bool InviteExpireTimer::Tick(time_t currtime)
+{
+ ServerInstance->Logs->Log(MODNAME, LOG_DEBUG, "InviteExpireTimer::Tick(): expired %p", (void*) inv);
+ apiimpl->Destruct(inv);
+ return false;
+}
diff --git a/src/coremods/core_channel/invite.h b/src/coremods/core_channel/invite.h
new file mode 100644
index 000000000..2a99ec2df
--- /dev/null
+++ b/src/coremods/core_channel/invite.h
@@ -0,0 +1,127 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2015 Attila Molnar <attilamolnar@hush.com>
+ *
+ * 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 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/>.
+ */
+
+
+#pragma once
+
+#include "modules/invite.h"
+
+namespace Invite
+{
+ template<typename T>
+ struct Store
+ {
+ typedef insp::intrusive_list<Invite, T> List;
+
+ /** List of pending Invites
+ */
+ List invites;
+ };
+
+ template<typename T, ExtensionItem::ExtensibleType ExtType>
+ class ExtItem;
+
+ class APIImpl;
+}
+
+extern void RemoveInvite(Invite::Invite* inv, bool remove_user, bool remove_chan);
+extern void UnserializeInvite(LocalUser* user, const std::string& value);
+
+template<typename T, ExtensionItem::ExtensibleType ExtType>
+class Invite::ExtItem : public ExtensionItem
+{
+ public:
+ ExtItem(Module* owner, const char* extname)
+ : ExtensionItem(extname, ExtType, owner)
+ {
+ }
+
+ Store<T>* get(Extensible* ext, bool create = false)
+ {
+ Store<T>* store = static_cast<Store<T>*>(get_raw(ext));
+ if ((create) && (!store))
+ {
+ store = new Store<T>;
+ set_raw(ext, store);
+ }
+ return store;
+ }
+
+ void unset(Extensible* ext)
+ {
+ void* store = unset_raw(ext);
+ if (store)
+ free(store);
+ }
+
+ void free(void* item) CXX11_OVERRIDE
+ {
+ Store<T>* store = static_cast<Store<T>*>(item);
+ for (typename Store<T>::List::iterator i = store->invites.begin(); i != store->invites.end(); )
+ {
+ Invite* inv = *i;
+ // Destructing the Invite invalidates the iterator, so move it now
+ ++i;
+ RemoveInvite(inv, (ExtType != ExtensionItem::EXT_USER), (ExtType == ExtensionItem::EXT_USER));
+ }
+
+ delete store;
+ }
+
+ std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE
+ {
+ if (format == FORMAT_NETWORK)
+ return std::string();
+
+ std::string ret;
+ Store<T>* store = static_cast<Store<T>*>(item);
+ for (typename insp::intrusive_list<Invite, T>::iterator i = store->invites.begin(); i != store->invites.end(); ++i)
+ {
+ Invite* inv = *i;
+ inv->Serialize(format, (ExtType == ExtensionItem::EXT_USER), ret);
+ }
+ if (!ret.empty())
+ ret.erase(ret.length()-1);
+ return ret;
+ }
+
+ void unserialize(SerializeFormat format, Extensible* container, const std::string& value) CXX11_OVERRIDE
+ {
+ if ((ExtType != ExtensionItem::EXT_CHANNEL) && (format != FORMAT_NETWORK))
+ UnserializeInvite(static_cast<LocalUser*>(container), value);
+ }
+};
+
+class Invite::APIImpl : public APIBase
+{
+ ExtItem<LocalUser, ExtensionItem::EXT_USER> userext;
+ ExtItem<Channel, ExtensionItem::EXT_CHANNEL> chanext;
+
+ public:
+ APIImpl(Module* owner);
+
+ void Create(LocalUser* user, Channel* chan, time_t timeout) CXX11_OVERRIDE;
+ Invite* Find(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+ bool Remove(LocalUser* user, Channel* chan) CXX11_OVERRIDE;
+ const List* GetList(LocalUser* user) CXX11_OVERRIDE;
+
+ void RemoveAll(LocalUser* user) { userext.unset(user); }
+ void RemoveAll(Channel* chan) { chanext.unset(chan); }
+ void Destruct(Invite* inv, bool remove_chan = true, bool remove_user = true);
+ void Unserialize(LocalUser* user, const std::string& value);
+};
diff --git a/src/modules/m_override.cpp b/src/modules/m_override.cpp
index 69f4b3bca..8bf1d3079 100644
--- a/src/modules/m_override.cpp
+++ b/src/modules/m_override.cpp
@@ -25,6 +25,7 @@
#include "inspircd.h"
+#include "modules/invite.h"
class ModuleOverride : public Module
{
@@ -34,6 +35,7 @@ class ModuleOverride : public Module
ChanModeReference inviteonly;
ChanModeReference key;
ChanModeReference limit;
+ Invite::API invapi;
static bool IsOverride(unsigned int userlevel, const Modes::ChangeList::List& list)
{
@@ -67,6 +69,7 @@ class ModuleOverride : public Module
, inviteonly(this, "inviteonly")
, key(this, "key")
, limit(this, "limit")
+ , invapi(this)
{
}
@@ -177,7 +180,7 @@ class ModuleOverride : public Module
{
if (chan->IsModeSet(inviteonly) && (CanOverride(user,"INVITE")))
{
- if (!user->IsInvited(chan))
+ if (!invapi->IsInvited(user, chan))
return HandleJoinOverride(user, chan, keygiven, "invite-only", "+i");
return MOD_RES_ALLOW;
}
diff --git a/src/modules/m_spanningtree/fjoin.cpp b/src/modules/m_spanningtree/fjoin.cpp
index 687bf305e..0879e730a 100644
--- a/src/modules/m_spanningtree/fjoin.cpp
+++ b/src/modules/m_spanningtree/fjoin.cpp
@@ -272,9 +272,6 @@ void CommandFJoin::LowerTS(Channel* chan, time_t TS, const std::string& newname)
chan->name = newname;
chan->age = TS;
- // Remove all pending invites
- chan->ClearInvites();
-
// Clear all modes
CommandFJoin::RemoveStatus(chan);
diff --git a/src/modules/m_uninvite.cpp b/src/modules/m_uninvite.cpp
index 97ad841f1..e92688d1d 100644
--- a/src/modules/m_uninvite.cpp
+++ b/src/modules/m_uninvite.cpp
@@ -21,13 +21,17 @@
#include "inspircd.h"
+#include "modules/invite.h"
/** Handle /UNINVITE
*/
class CommandUninvite : public Command
{
+ Invite::API invapi;
public:
- CommandUninvite(Module* Creator) : Command(Creator,"UNINVITE", 2)
+ CommandUninvite(Module* Creator)
+ : Command(Creator, "UNINVITE", 2)
+ , invapi(Creator)
{
syntax = "<nick> <channel>";
TRANSLATE2(TR_NICK, TR_TEXT);
@@ -73,7 +77,7 @@ class CommandUninvite : public Command
LocalUser* lu = IS_LOCAL(u);
if (lu)
{
- if (!lu->RemoveInvite(c))
+ if (!invapi->Remove(lu, c))
{
user->SendText(":%s 505 %s %s %s :Is not invited to channel %s", user->server->GetName().c_str(), user->nick.c_str(), u->nick.c_str(), c->name.c_str(), c->name.c_str());
return CMD_FAILURE;
diff --git a/src/usermanager.cpp b/src/usermanager.cpp
index 7e92507ca..41061f6d1 100644
--- a/src/usermanager.cpp
+++ b/src/usermanager.cpp
@@ -283,10 +283,7 @@ void UserManager::GarbageCollect()
// Reset the already_sent IDs so we don't wrap it around and drop a message
LocalUser::already_sent_id = 0;
for (LocalList::const_iterator i = local_users.begin(); i != local_users.end(); ++i)
- {
(**i).already_sent = 0;
- (**i).RemoveExpiredInvites();
- }
}
/* this returns true when all modules are satisfied that the user should be allowed onto the irc server
diff --git a/src/users.cpp b/src/users.cpp
index d760c713f..d503844e7 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -139,28 +139,6 @@ const std::string& User::GetFullRealHost()
return this->cached_fullrealhost;
}
-InviteList& LocalUser::GetInviteList()
-{
- RemoveExpiredInvites();
- return invites;
-}
-
-bool LocalUser::RemoveInvite(Channel* chan)
-{
- Invitation* inv = Invitation::Find(chan, this);
- if (inv)
- {
- delete inv;
- return true;
- }
- return false;
-}
-
-void LocalUser::RemoveExpiredInvites()
-{
- Invitation::Find(NULL, this);
-}
-
bool User::HasModePermission(unsigned char, ModeType)
{
return true;
@@ -328,7 +306,6 @@ CullResult User::cull()
CullResult LocalUser::cull()
{
- ClearInvites();
eh.cull();
return User::cull();
}