diff options
author | Attila Molnar <attilamolnar@hush.com> | 2015-11-03 12:40:24 +0100 |
---|---|---|
committer | Attila Molnar <attilamolnar@hush.com> | 2015-11-03 12:40:24 +0100 |
commit | 2c51a2bf17b6c377f0207f3d89b4b3f399fc0178 (patch) | |
tree | 93f446719933bcfcb2f3580fef778a37c70dc57e /src/coremods | |
parent | a2461fb7e1c28e28e64b61256d7a547162a83f6e (diff) | |
parent | a124a3783d8e8136b63f188bbd292c391811df54 (diff) |
Merge branch 'master+invite'
Diffstat (limited to 'src/coremods')
-rw-r--r-- | src/coremods/core_channel/cmd_invite.cpp | 13 | ||||
-rw-r--r-- | src/coremods/core_channel/core_channel.cpp | 66 | ||||
-rw-r--r-- | src/coremods/core_channel/core_channel.h | 9 | ||||
-rw-r--r-- | src/coremods/core_channel/invite.cpp | 208 | ||||
-rw-r--r-- | src/coremods/core_channel/invite.h | 127 |
5 files changed, 415 insertions, 8 deletions
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); +}; |