From 30fc51c6ddca487a1b89da9ab0ab59da003aee36 Mon Sep 17 00:00:00 2001 From: Attila Molnar Date: Mon, 2 Nov 2015 13:28:55 +0100 Subject: Rewrite invite system - Moved out of core, now lives entirely in core_channel - Accessible using the provided API after including the appropriate header - Invites are stored in an extension attached to LocalUser/Channel objects, they no longer need special handling when destroying these objects or when lowering TS - Expiration of timed invites are implemented using Timers - When creating a new invite let a non-timed invite override a timed one --- include/modules/invite.h | 121 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 include/modules/invite.h (limited to 'include/modules/invite.h') diff --git a/include/modules/invite.h b/include/modules/invite.h new file mode 100644 index 000000000..ab907e970 --- /dev/null +++ b/include/modules/invite.h @@ -0,0 +1,121 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2012, 2015 Attila Molnar + * + * 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 . + */ + + +#pragma once + +namespace Invite +{ + class APIBase; + class API; + class Invite; + + typedef insp::intrusive_list 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 +{ + public: + API(Module* parent) + : dynamic_reference(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, public insp::intrusive_list_node +{ + 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); } + + 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(); +}; -- cgit v1.2.3 From a124a3783d8e8136b63f188bbd292c391811df54 Mon Sep 17 00:00:00 2001 From: Attila Molnar Date: Mon, 2 Nov 2015 13:40:15 +0100 Subject: core_channel Implement invite (un)serialization --- include/modules/invite.h | 7 +++++++ src/coremods/core_channel/invite.cpp | 31 +++++++++++++++++++++++++++++++ src/coremods/core_channel/invite.h | 18 +++++++++++++++++- 3 files changed, 55 insertions(+), 1 deletion(-) (limited to 'include/modules/invite.h') diff --git a/include/modules/invite.h b/include/modules/invite.h index ab907e970..e53d5202f 100644 --- a/include/modules/invite.h +++ b/include/modules/invite.h @@ -100,6 +100,13 @@ class Invite::Invite : public insp::intrusive_list_node, publ */ 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: diff --git a/src/coremods/core_channel/invite.cpp b/src/coremods/core_channel/invite.cpp index 253c571ca..7ac662edc 100644 --- a/src/coremods/core_channel/invite.cpp +++ b/src/coremods/core_channel/invite.cpp @@ -38,6 +38,11 @@ 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") { @@ -150,6 +155,17 @@ const Invite::List* Invite::APIImpl::GetList(LocalUser* user) 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) @@ -163,6 +179,21 @@ Invite::Invite::~Invite() 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) diff --git a/src/coremods/core_channel/invite.h b/src/coremods/core_channel/invite.h index f11ff9043..2a99ec2df 100644 --- a/src/coremods/core_channel/invite.h +++ b/src/coremods/core_channel/invite.h @@ -40,6 +40,7 @@ namespace Invite } extern void RemoveInvite(Invite::Invite* inv, bool remove_user, bool remove_chan); +extern void UnserializeInvite(LocalUser* user, const std::string& value); template class Invite::ExtItem : public ExtensionItem @@ -84,11 +85,25 @@ class Invite::ExtItem : public ExtensionItem std::string serialize(SerializeFormat format, const Extensible* container, void* item) const CXX11_OVERRIDE { - return std::string(); + if (format == FORMAT_NETWORK) + return std::string(); + + std::string ret; + Store* store = static_cast*>(item); + for (typename insp::intrusive_list::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(container), value); } }; @@ -108,4 +123,5 @@ class Invite::APIImpl : public APIBase 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); }; -- cgit v1.2.3