mirror of
https://github.com/anope/anope.git
synced 2026-06-12 22:34:46 +02:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c2e09fdaea | |||
| 01768bc73e | |||
| c18eedec18 | |||
| 0e0615c6fe | |||
| 5bd3fde79b | |||
| 647f8cd4e6 | |||
| b2e25db159 | |||
| 9f9371531d | |||
| b578ed2544 | |||
| 0a7f167060 | |||
| d427772bd3 | |||
| b2d670298b | |||
| 6b646fa1f8 | |||
| 72269c44e5 | |||
| 3b2366e4ba | |||
| 257a8a9a24 | |||
| 7a1d2e11dd | |||
| 8dc687b657 | |||
| 18fc113984 | |||
| 20c1a5d638 | |||
| 8000ae0c0f | |||
| 284d95bfe2 | |||
| c7d5ee4bec | |||
| 4c4cc0ded7 | |||
| 83f89bfece | |||
| 7cf4245359 | |||
| 9284441491 | |||
| 7592a6981b | |||
| 56b158ea43 | |||
| 0143dafe51 | |||
| a2dbcc620e | |||
| 9b5fdea8f2 | |||
| fab07d6b75 | |||
| 1d198da436 | |||
| 8b6e17fe7e | |||
| 86484b6c2b | |||
| c14ae99d32 | |||
| cb0cda2f8d | |||
| 2af363755a | |||
| 753b8dc989 | |||
| d2ea1e7917 | |||
| a8f66578b1 | |||
| ca39aeb554 | |||
| a941f9bde0 | |||
| f4f6787c9c | |||
| 82816cb206 | |||
| 58f0c8c612 | |||
| 21e1913c0f | |||
| 79e7c3f98a | |||
| 72127e1b00 | |||
| ed47588087 | |||
| 448967cefc | |||
| 979027138b | |||
| 176e0e4f37 | |||
| b431d4dccf | |||
| 1b12b80b48 | |||
| 52fdc45597 | |||
| 91762524e2 | |||
| 413b38b1c1 | |||
| ecdccd6ad6 | |||
| e0fd118b3e | |||
| c10d7bdada | |||
| 4e2ca31cf5 | |||
| addd2a1987 | |||
| 4071487f80 | |||
| 9c17c2d818 | |||
| dd9fcca45d |
+14
-6
@@ -87,6 +87,9 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
if(GCC_VERSION LESS 4.2)
|
||||
message(FATAL_ERROR "Your compiler is too old to build Anope. Upgrade to GCC 4.2 or newer!")
|
||||
endif(GCC_VERSION LESS 4.2)
|
||||
if(GCC_VERSION GREATER 6.0 OR GCC_VERSION EQUAL 6.0)
|
||||
set(CXXFLAGS "${CXXFLAGS} -fno-delete-null-pointer-checks")
|
||||
endif(GCC_VERSION GREATER 6.0 OR GCC_VERSION EQUAL 6.0)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
# If we are using a GNU compiler (have to use CXX because it seems to fail on C), we will be able to determine it's default paths for libraries and includes
|
||||
@@ -239,6 +242,11 @@ option(USE_PCH "Use precompiled headers" OFF)
|
||||
# source include directory so the precompiled headers work correctly.
|
||||
include_directories(${Anope_BINARY_DIR}/include ${Anope_SOURCE_DIR}/include ${Anope_BINARY_DIR}/language ${Anope_SOURCE_DIR}/modules/pseudoclients)
|
||||
|
||||
# Pass on REPRODUCIBLE_BUILD
|
||||
if(REPRODUCIBLE_BUILD)
|
||||
add_definitions(-DREPRODUCIBLE_BUILD)
|
||||
endif(REPRODUCIBLE_BUILD)
|
||||
|
||||
# If using Windows, always add the _WIN32 define
|
||||
if(WIN32)
|
||||
add_definitions(-D_WIN32)
|
||||
@@ -481,16 +489,16 @@ get_target_property(SERVICES_BINARY ${PROGRAM_NAME} LOCATION)
|
||||
get_filename_component(SERVICES_BINARY ${SERVICES_BINARY} NAME)
|
||||
|
||||
# At install time, create the following additional directories
|
||||
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${DB_DIR}/backups\")")
|
||||
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/${LOGS_DIR}\")")
|
||||
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DB_DIR}/backups\")")
|
||||
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${LOGS_DIR}\")")
|
||||
if(WIN32)
|
||||
install(CODE "file(MAKE_DIRECTORY \"\${CMAKE_INSTALL_PREFIX}/${DB_DIR}/runtime\")")
|
||||
install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${DB_DIR}/runtime\")")
|
||||
endif(WIN32)
|
||||
# On non-Windows platforms, if RUNGROUP is set, change the permissions of the below directories, as well as the group of the data directory
|
||||
if(NOT WIN32 AND RUNGROUP)
|
||||
install(CODE "execute_process(COMMAND ${CHMOD} 2775 \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/\${DB_DIR}/backups\")")
|
||||
install(CODE "execute_process(COMMAND ${CHMOD} 2775 \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/\${LOGS_DIR}\")")
|
||||
install(CODE "execute_process(COMMAND ${CHGRP} -R ${RUNGROUP} \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}\")")
|
||||
install(CODE "execute_process(COMMAND ${CHMOD} 2775 \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${DB_DIR}/backups\")")
|
||||
install(CODE "execute_process(COMMAND ${CHMOD} 2775 \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\${LOGS_DIR}\")")
|
||||
install(CODE "execute_process(COMMAND ${CHGRP} -R ${RUNGROUP} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}\")")
|
||||
endif(NOT WIN32 AND RUNGROUP)
|
||||
# On Windows platforms, install extra files
|
||||
if(WIN32)
|
||||
|
||||
@@ -166,7 +166,7 @@ done
|
||||
# Check for CMake and (optionally) install it
|
||||
###########################################################################
|
||||
|
||||
which cmake > /dev/null
|
||||
cmake --version 2>&1 > /dev/null
|
||||
if [ $? -ne 0 ] ; then
|
||||
clear
|
||||
echo "Anope requires CMake 2.4 or newer, which can be downloaded at http://cmake.org or through your system's package manager."
|
||||
|
||||
@@ -542,24 +542,6 @@ module { name = "help" }
|
||||
*/
|
||||
#module { name = "m_sasl" }
|
||||
|
||||
/*
|
||||
* m_sasl_dh-aes [EXTRA]
|
||||
*
|
||||
* Add the DH-AES mechanism to SASL.
|
||||
* Requires m_sasl to be loaded.
|
||||
* Requires openssl.
|
||||
*/
|
||||
#module { name = "m_sasl_dh-aes" }
|
||||
|
||||
/*
|
||||
* m_sasl_dh-blowfish [EXTRA]
|
||||
*
|
||||
* Add the DH-BLOWFISH mechanism to SASL.
|
||||
* Requires m_sasl to be loaded.
|
||||
* Requires openssl.
|
||||
*/
|
||||
#module { name = "m_sasl_dh-blowfish" }
|
||||
|
||||
/*
|
||||
* m_ssl_gnutls [EXTRA]
|
||||
*
|
||||
|
||||
+21
-2
@@ -1,4 +1,23 @@
|
||||
Anope Version 2.0.3-git
|
||||
Anope Version 2.0.4
|
||||
-------------------
|
||||
Add notice rpc method to XMLRPC
|
||||
Fix access check in cs_updown to not allow actions on users with equal access
|
||||
Fix randomnews to work when there are more than 'newscount' random news
|
||||
Fix crash from handling nick introduction collisions on unreal4
|
||||
Add support for GCC6
|
||||
Fix handling /join 0 on ratbox
|
||||
Fix saset display to update the account of the proper user
|
||||
Fix nickserv/confirm to send account and +r when appropriate
|
||||
Fix chankill to not add duplicate akills
|
||||
Allow nickserv/maxemail to disregard gmail style aliases when comparing emails
|
||||
Fix chanserv/mode when setting listmodes with CIDR ranges
|
||||
Fix reported expiry time when the time is divisible by 1 year
|
||||
Clearify botserv repeat kicker help and allow setting repeat times to 1
|
||||
Send vident/vhost with SASL login
|
||||
Add support for SASL reauth
|
||||
Fix log and database backup directories to be properly created by the installer
|
||||
|
||||
Anope Version 2.0.3
|
||||
-------------------
|
||||
Add support for UnrealIRCd 4
|
||||
Fix cs_access to respect chanserv/access/list privilege
|
||||
@@ -16,7 +35,7 @@ Fix crash from unloading regex modules when regex xlines exist
|
||||
Fix irc2sql to store away messages
|
||||
Add notice method to m_xmlrpc_main
|
||||
Fix numerics on InspIRCd from being sent to user UID
|
||||
Fix +I on InspIRCd to be considered oper only
|
||||
Fix usermode +I on UnrealIRCd to be considered oper only
|
||||
Fix crash in webcpanel when deleting your own access on a channel
|
||||
Fix webcpanel allowing suspended users to login
|
||||
Fix hs_off on InspIRCd to not desync active user host
|
||||
|
||||
+5
-1
@@ -1,4 +1,8 @@
|
||||
Anope Version 2.0.3-git
|
||||
Anope Version 2.0.4
|
||||
-------------------
|
||||
Removed m_sasl_dh-aes and m_sasl_dh-blowfish
|
||||
|
||||
Anope Version 2.0.3
|
||||
-------------------
|
||||
Add operserv/chankill to default globops log
|
||||
Add ns_identify:maxlogins to limit the max number of concurrent logins per account
|
||||
|
||||
@@ -113,6 +113,21 @@ class AnopeXMLRPC
|
||||
{
|
||||
return $this->run("channel", [$channel]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent a notice to a user.
|
||||
* Returns an array containing channel information, or an array of size one
|
||||
* (just containing the name) if the channel does not exist
|
||||
*
|
||||
* @param $source
|
||||
* @param $target
|
||||
* @param $message
|
||||
* @return array|null
|
||||
*/
|
||||
public function notice($source, $target, $message)
|
||||
{
|
||||
return $this->run("notice", [$source, $target, $message]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like channel(), but different.
|
||||
|
||||
@@ -148,12 +148,7 @@ while (1)
|
||||
|
||||
if ($input eq "q") {
|
||||
if (-e "build/CMakeFiles") {
|
||||
if (-e "cmake-bin") {
|
||||
my $cmake_path = `find cmake-bin -name cmake -print0`;
|
||||
system($cmake_path, "build/.");
|
||||
} else {
|
||||
system("cmake", "build/.");
|
||||
}
|
||||
system("cmake", "build/.");
|
||||
print "\nNow cd build, then run make to build Anope.\n\n";
|
||||
} else {
|
||||
print "\nBuild directory not found. You should run ./Config now.\n\n"
|
||||
|
||||
+10
-11
@@ -78,12 +78,7 @@ class CoreExport ChanAccess : public Serializable
|
||||
Serialize::Reference<NickCore> nc;
|
||||
|
||||
public:
|
||||
typedef std::multimap<const ChanAccess *, const ChanAccess *> Set;
|
||||
/* shows the 'path' taken to determine if an access entry matches a user
|
||||
* .first are access entries checked
|
||||
* .second are access entries which match
|
||||
*/
|
||||
typedef std::pair<Set, Set> Path;
|
||||
typedef std::vector<ChanAccess *> Path;
|
||||
|
||||
/* The provider that created this access entry */
|
||||
AccessProvider *provider;
|
||||
@@ -103,12 +98,14 @@ class CoreExport ChanAccess : public Serializable
|
||||
void Serialize(Serialize::Data &data) const anope_override;
|
||||
static Serializable* Unserialize(Serializable *obj, Serialize::Data &);
|
||||
|
||||
static const unsigned int MAX_DEPTH = 4;
|
||||
|
||||
/** Check if this access entry matches the given user or account
|
||||
* @param u The user
|
||||
* @param nc The account
|
||||
* @param p The path to the access object which matches will be put here
|
||||
* @param next Next channel to check if any
|
||||
*/
|
||||
virtual bool Matches(const User *u, const NickCore *nc, Path &p) const;
|
||||
virtual bool Matches(const User *u, const NickCore *nc, ChannelInfo* &next) const;
|
||||
|
||||
/** Check if this access entry has the given privilege.
|
||||
* @param name The privilege name
|
||||
@@ -136,13 +133,13 @@ class CoreExport ChanAccess : public Serializable
|
||||
/* A group of access entries. This is used commonly, for example with ChannelInfo::AccessFor,
|
||||
* to show what access a user has on a channel because users can match multiple access entries.
|
||||
*/
|
||||
class CoreExport AccessGroup : public std::vector<ChanAccess *>
|
||||
class CoreExport AccessGroup
|
||||
{
|
||||
public:
|
||||
/* access entries + paths */
|
||||
std::vector<ChanAccess::Path> paths;
|
||||
/* Channel these access entries are on */
|
||||
const ChannelInfo *ci;
|
||||
/* Path from these entries to other entries that they depend on */
|
||||
ChanAccess::Path path;
|
||||
/* Account these entries affect, if any */
|
||||
const NickCore *nc;
|
||||
/* super_admin always gets all privs. founder is a special case where ci->founder == nc */
|
||||
@@ -170,6 +167,8 @@ class CoreExport AccessGroup : public std::vector<ChanAccess *>
|
||||
bool operator<(const AccessGroup &other) const;
|
||||
bool operator>=(const AccessGroup &other) const;
|
||||
bool operator<=(const AccessGroup &other) const;
|
||||
|
||||
inline bool empty() const { return paths.empty(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -336,7 +336,9 @@ namespace Anope
|
||||
template<typename T> class multimap : public std::multimap<string, T, ci::less> { };
|
||||
template<typename T> class hash_map : public TR1NS::unordered_map<string, T, hash_ci, compare> { };
|
||||
|
||||
#ifndef REPRODUCIBLE_BUILD
|
||||
static const char *const compiled = __TIME__ " " __DATE__;
|
||||
#endif
|
||||
|
||||
/** The time Anope started.
|
||||
*/
|
||||
|
||||
@@ -302,6 +302,8 @@ class CoreExport Channel : public Base, public Extensible
|
||||
*/
|
||||
static Channel *FindOrCreate(const Anope::string &name, bool &created, time_t ts = Anope::CurTime);
|
||||
|
||||
void QueueForDeletion();
|
||||
|
||||
static void DeleteChannels();
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
class CoreExport Memo : public Serializable
|
||||
{
|
||||
public:
|
||||
MemoInfo *mi;
|
||||
bool unread;
|
||||
bool receipt;
|
||||
Memo();
|
||||
|
||||
@@ -395,6 +395,7 @@ class CoreExport Entry
|
||||
Anope::string mask;
|
||||
public:
|
||||
unsigned short cidr_len;
|
||||
int family;
|
||||
Anope::string nick, user, host, real;
|
||||
|
||||
/** Constructor
|
||||
|
||||
+2
-1
@@ -227,8 +227,9 @@ class CoreExport IRCDProto : public Service
|
||||
*/
|
||||
virtual void SendOper(User *u);
|
||||
|
||||
virtual void SendSASLMechanisms(std::vector<Anope::string> &) { }
|
||||
virtual void SendSASLMessage(const SASL::Message &) { }
|
||||
virtual void SendSVSLogin(const Anope::string &uid, const Anope::string &acc) { }
|
||||
virtual void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) { }
|
||||
|
||||
virtual bool IsNickValid(const Anope::string &);
|
||||
virtual bool IsChannelValid(const Anope::string &);
|
||||
|
||||
@@ -38,6 +38,10 @@ union CoreExport sockaddrs
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/** Get address family.
|
||||
*/
|
||||
int family() const;
|
||||
|
||||
/** Get the size of the sockaddr we represent
|
||||
* @return The size
|
||||
*/
|
||||
|
||||
+273
-248
File diff suppressed because it is too large
Load Diff
@@ -663,17 +663,30 @@ class CommandBSKickRepeat : public CommandBSKickBase
|
||||
kd->repeattimes = convertTo<int16_t>(times);
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
if (kd->repeattimes < 2)
|
||||
if (kd->repeattimes < 1)
|
||||
kd->repeattimes = 3;
|
||||
|
||||
kd->repeat = true;
|
||||
if (kd->ttb[TTB_REPEAT])
|
||||
source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n"
|
||||
"same thing %d times), and will place a ban after %d\n"
|
||||
"kicks for the same user."), kd->repeattimes + 1, kd->ttb[TTB_REPEAT]);
|
||||
{
|
||||
if (kd->repeattimes != 1)
|
||||
source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
|
||||
"same message %d times), and will place a ban after %d\n"
|
||||
"kicks for the same user."), kd->repeattimes, kd->ttb[TTB_REPEAT]);
|
||||
else
|
||||
source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
|
||||
"same message %d time), and will place a ban after %d\n"
|
||||
"kicks for the same user."), kd->repeattimes, kd->ttb[TTB_REPEAT]);
|
||||
}
|
||||
else
|
||||
source.Reply(_("Bot will now kick for \002repeats\002 (users that say the\n"
|
||||
"same thing %d times)."), kd->repeattimes + 1);
|
||||
{
|
||||
if (kd->repeattimes != 1)
|
||||
source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
|
||||
"same message %d times)."), kd->repeattimes);
|
||||
else
|
||||
source.Reply(_("Bot will now kick for \002repeats\002 (users that repeat the\n"
|
||||
"same message %d time)."), kd->repeattimes);
|
||||
}
|
||||
}
|
||||
else if (params[1].equals_ci("OFF"))
|
||||
{
|
||||
|
||||
@@ -370,7 +370,7 @@ class CommandCSAccess : public Command
|
||||
if (ci->c)
|
||||
for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
|
||||
{
|
||||
ChanAccess::Path p;
|
||||
ChannelInfo *p;
|
||||
if (access->Matches(cit->second->user, cit->second->user->Account(), p))
|
||||
timebuf = "Now";
|
||||
}
|
||||
@@ -407,7 +407,7 @@ class CommandCSAccess : public Command
|
||||
if (ci->c)
|
||||
for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit)
|
||||
{
|
||||
ChanAccess::Path p;
|
||||
ChannelInfo *p;
|
||||
if (access->Matches(cit->second->user, cit->second->user->Account(), p))
|
||||
timebuf = "Now";
|
||||
}
|
||||
|
||||
@@ -169,6 +169,7 @@ public:
|
||||
delete target_ci;
|
||||
target_ci = new ChannelInfo(*ci);
|
||||
target_ci->name = target;
|
||||
target_ci->time_registered = Anope::CurTime;
|
||||
(*RegisteredChannelList)[target_ci->name] = target_ci;
|
||||
target_ci->c = Channel::Find(target_ci->name);
|
||||
|
||||
|
||||
@@ -236,9 +236,14 @@ class CommandSeen : public Command
|
||||
|
||||
AccessGroup ag = source.c->ci->AccessFor(na->nc);
|
||||
time_t last = 0;
|
||||
for (unsigned i = 0; i < ag.size(); ++i)
|
||||
for (unsigned int i = 0; i < ag.paths.size(); ++i)
|
||||
{
|
||||
ChanAccess *a = ag[i];
|
||||
ChanAccess::Path &p = ag.paths[i];
|
||||
|
||||
if (p.empty())
|
||||
continue;
|
||||
|
||||
ChanAccess *a = p[p.size() - 1];
|
||||
|
||||
if (a->GetAccount() == na->nc && a->last_seen > last)
|
||||
last = a->last_seen;
|
||||
|
||||
@@ -742,10 +742,9 @@ class CommandCSSetSecure : public Command
|
||||
this->SendSyntax(source);
|
||||
source.Reply(" ");
|
||||
source.Reply(_("Enables or disables security features for a\n"
|
||||
"channel. When \002%s\002 is set, only users who have\n"
|
||||
"registered their nicknames and IDENTIFY'd\n"
|
||||
"with their password will be given access to the channel\n"
|
||||
"as controlled by the access list."), this->name.c_str());
|
||||
"channel. When \002SECURE\002 is set, only users who have\n"
|
||||
"identified to services, and are not only recognized, will be\n"
|
||||
"given access to channels from account-based access entries."));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -1136,6 +1135,7 @@ class CSSet : public Module
|
||||
ChannelInfo *ci = anope_dynamic_static_cast<ChannelInfo *>(s);
|
||||
Anope::string modes;
|
||||
data["last_modes"] >> modes;
|
||||
ci->last_modes.clear();
|
||||
for (spacesepstream sep(modes); sep.GetToken(modes);)
|
||||
{
|
||||
size_t c = modes.find(',');
|
||||
|
||||
@@ -57,11 +57,26 @@ public:
|
||||
{
|
||||
source.Reply(_("Access for \002%s\002 on \002%s\002:"), nick.c_str(), ci->name.c_str());
|
||||
|
||||
for (unsigned i = 0; i < ag.size(); ++i)
|
||||
for (unsigned i = 0; i < ag.paths.size(); ++i)
|
||||
{
|
||||
ChanAccess *acc = ag[i];
|
||||
ChanAccess::Path &p = ag.paths[i];
|
||||
|
||||
source.Reply(_("\002%s\002 matches access entry %s, which has privilege %s."), nick.c_str(), acc->Mask().c_str(), acc->AccessSerialize().c_str());
|
||||
if (p.empty())
|
||||
continue;
|
||||
|
||||
if (p.size() == 1)
|
||||
{
|
||||
ChanAccess *acc = p[0];
|
||||
|
||||
source.Reply(_("\002%s\002 matches access entry %s, which has privilege %s."), nick.c_str(), acc->Mask().c_str(), acc->AccessSerialize().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
ChanAccess *first = p[0];
|
||||
ChanAccess *acc = p[p.size() - 1];
|
||||
|
||||
source.Reply(_("\002%s\002 matches access entry %s (from entry %s), which has privilege %s."), nick.c_str(), acc->Mask().c_str(), first->Mask().c_str(), acc->AccessSerialize().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ class CommandCSUp : public Command
|
||||
}
|
||||
else if (source.GetUser() && u != source.GetUser() && c->ci->HasExt("PEACE"))
|
||||
{
|
||||
if (c->ci->AccessFor(u) > c->ci->AccessFor(source.GetUser()))
|
||||
if (c->ci->AccessFor(u) >= c->ci->AccessFor(source.GetUser()))
|
||||
{
|
||||
if (source.HasPriv("chanserv/administration"))
|
||||
override = true;
|
||||
@@ -199,7 +199,7 @@ class CommandCSDown : public Command
|
||||
}
|
||||
else if (source.GetUser() && u != source.GetUser() && c->ci->HasExt("PEACE"))
|
||||
{
|
||||
if (c->ci->AccessFor(u) > c->ci->AccessFor(source.GetUser()))
|
||||
if (c->ci->AccessFor(u) >= c->ci->AccessFor(source.GetUser()))
|
||||
{
|
||||
if (source.HasPriv("chanserv/administration"))
|
||||
override = true;
|
||||
|
||||
@@ -86,8 +86,17 @@ class CommandNSAList : public Command
|
||||
|
||||
entry["Number"] = stringify(chan_count);
|
||||
entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name;
|
||||
for (unsigned j = 0; j < access.size(); ++j)
|
||||
entry["Access"] = entry["Access"] + ", " + access[j]->AccessSerialize();
|
||||
for (unsigned j = 0; j < access.paths.size(); ++j)
|
||||
{
|
||||
ChanAccess::Path &p = access.paths[j];
|
||||
|
||||
// not interested in indirect access
|
||||
if (p.size() != 1)
|
||||
continue;
|
||||
|
||||
ChanAccess *a = p[0];
|
||||
entry["Access"] = entry["Access"] + ", " + a->AccessSerialize();
|
||||
}
|
||||
entry["Access"] = entry["Access"].substr(2);
|
||||
entry["Description"] = ci->desc;
|
||||
list.AddEntry(entry);
|
||||
|
||||
@@ -40,6 +40,20 @@ class CommandNSConfirm : public Command
|
||||
FOREACH_MOD(OnNickConfirm, (source.GetUser(), na->nc));
|
||||
Log(LOG_ADMIN, source, this) << "to confirm nick " << na->nick << " (" << na->nc->display << ")";
|
||||
source.Reply(_("Nick \002%s\002 has been confirmed."), na->nick.c_str());
|
||||
|
||||
/* Login the users online already */
|
||||
for (std::list<User *>::iterator it = na->nc->users.begin(); it != na->nc->users.end(); ++it)
|
||||
{
|
||||
User *u = *it;
|
||||
|
||||
IRCD->SendLogin(u, na);
|
||||
|
||||
NickAlias *u_na = NickAlias::Find(u->nick);
|
||||
|
||||
/* Set +r if they're on a nick in the group */
|
||||
if (!Config->GetModule("nickserv")->Get<bool>("nonicknameownership") && u_na && *u_na->nc == *na->nc)
|
||||
u->SetMode(source.service, "REGISTERED");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (source.nc)
|
||||
|
||||
@@ -361,8 +361,14 @@ class CommandNSSetDisplay : public Command
|
||||
Log(user_na->nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to change the display of " << user_na->nc->display << " to " << na->nick;
|
||||
|
||||
user_na->nc->SetDisplay(na);
|
||||
if (source.GetUser())
|
||||
IRCD->SendLogin(source.GetUser(), na);
|
||||
|
||||
/* Send updated account name */
|
||||
for (std::list<User *>::iterator it = user_na->nc->users.begin(); it != user_na->nc->users.end(); ++it)
|
||||
{
|
||||
User *u = *it;
|
||||
IRCD->SendLogin(u, user_na);
|
||||
}
|
||||
|
||||
source.Reply(NICK_SET_DISPLAY_CHANGED, user_na->nc->display.c_str());
|
||||
}
|
||||
|
||||
@@ -1205,6 +1211,7 @@ class NSSet : public Module
|
||||
NickCore *nc = anope_dynamic_static_cast<NickCore *>(s);
|
||||
Anope::string modes;
|
||||
data["last_modes"] >> modes;
|
||||
nc->last_modes.clear();
|
||||
for (spacesepstream sep(modes); sep.GetToken(modes);)
|
||||
{
|
||||
size_t c = modes.find(',');
|
||||
|
||||
@@ -76,7 +76,11 @@ class CommandOSChanKill : public Command
|
||||
if (uc->user->server == Me || uc->user->HasMode("OPER"))
|
||||
continue;
|
||||
|
||||
XLine *x = new XLine("*@" + uc->user->host, source.GetNick(), expires, realreason, XLineManager::GenerateUID());
|
||||
Anope::string akillmask = "*@" + uc->user->host;
|
||||
if (akills->HasEntry(akillmask))
|
||||
continue;
|
||||
|
||||
XLine *x = new XLine(akillmask, source.GetNick(), expires, realreason, XLineManager::GenerateUID());
|
||||
akills->AddXLine(x);
|
||||
akills->OnMatch(uc->user, x);
|
||||
}
|
||||
|
||||
@@ -354,8 +354,7 @@ class OSDefcon : public Module
|
||||
dconfig.message = block->Get<const Anope::string>("message");
|
||||
dconfig.offmessage = block->Get<const Anope::string>("offmessage");
|
||||
|
||||
Module *session = ModuleManager::FindModule("os_session");
|
||||
block = conf->GetModule(session);
|
||||
block = conf->GetModule("os_session");
|
||||
|
||||
dconfig.max_session_kill = block->Get<int>("maxsessionkill");
|
||||
dconfig.session_autokill_expiry = block->Get<time_t>("sessionautokillexpiry");
|
||||
|
||||
@@ -405,7 +405,7 @@ class OSNews : public Module
|
||||
|
||||
int start = 0;
|
||||
|
||||
if (type != NEWS_RANDOM)
|
||||
if (Type != NEWS_RANDOM)
|
||||
{
|
||||
start = newsList.size() - news_count;
|
||||
if (start < 0)
|
||||
|
||||
@@ -23,8 +23,8 @@ class StatusUpdate : public Module
|
||||
{
|
||||
User *user = it->second->user;
|
||||
|
||||
ChanAccess::Path p;
|
||||
if (user->server != Me && access->Matches(user, user->Account(), p))
|
||||
ChannelInfo *next;
|
||||
if (user->server != Me && access->Matches(user, user->Account(), next))
|
||||
{
|
||||
AccessGroup ag = ci->AccessFor(user);
|
||||
|
||||
@@ -46,8 +46,8 @@ class StatusUpdate : public Module
|
||||
{
|
||||
User *user = it->second->user;
|
||||
|
||||
ChanAccess::Path p;
|
||||
if (user->server != Me && access->Matches(user, user->Account(), p))
|
||||
ChannelInfo *next;
|
||||
if (user->server != Me && access->Matches(user, user->Account(), next))
|
||||
{
|
||||
AccessGroup ag = ci->AccessFor(user);
|
||||
|
||||
|
||||
@@ -590,6 +590,7 @@ static void LoadNicks()
|
||||
READ(read_string(m->text, f));
|
||||
m->owner = nc->display;
|
||||
nc->memos.memos->push_back(m);
|
||||
m->mi = &nc->memos;
|
||||
}
|
||||
READ(read_uint16(&u16, f));
|
||||
READ(read_int16(&i16, f));
|
||||
@@ -956,6 +957,7 @@ static void LoadChannels()
|
||||
READ(read_string(m->text, f));
|
||||
m->owner = ci->name;
|
||||
ci->memos.memos->push_back(m);
|
||||
m->mi = &ci->memos;
|
||||
}
|
||||
|
||||
READ(read_string(buffer, f));
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2014 Daniel Vassdal <shutter@canternet.org>
|
||||
* (C) 2014-2016 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*/
|
||||
|
||||
/* RequiredLibraries: ssl,crypto */
|
||||
/* RequiredWindowsLibraries: ssleay32,libeay32 */
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/sasl.h"
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/aes.h>
|
||||
|
||||
using namespace SASL;
|
||||
|
||||
class DHAES : public Mechanism
|
||||
{
|
||||
void Err(Session* sess, BIGNUM* key = NULL)
|
||||
{
|
||||
if (key)
|
||||
BN_free(key);
|
||||
|
||||
sasl->Fail(sess);
|
||||
delete sess;
|
||||
}
|
||||
|
||||
public:
|
||||
struct DHAESSession : SASL::Session
|
||||
{
|
||||
DH* dh;
|
||||
DHAESSession(Mechanism *m, const Anope::string &u, DH* dh_params) : SASL::Session(m, u)
|
||||
{
|
||||
if (!(dh = DH_new()))
|
||||
return;
|
||||
|
||||
dh->g = BN_dup(dh_params->g);
|
||||
dh->p = BN_dup(dh_params->p);
|
||||
|
||||
if (!DH_generate_key(dh))
|
||||
{
|
||||
DH_free(dh);
|
||||
dh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
~DHAESSession()
|
||||
{
|
||||
if (dh)
|
||||
DH_free(dh);
|
||||
}
|
||||
};
|
||||
|
||||
DH* dh_params;
|
||||
const size_t keysize;
|
||||
SASL::Session* CreateSession(const Anope::string &uid) anope_override
|
||||
{
|
||||
return new DHAESSession(this, uid, dh_params);
|
||||
}
|
||||
|
||||
DHAES(Module *o) : Mechanism(o, "DH-AES"), keysize(256 / 8)
|
||||
{
|
||||
if (!(dh_params = DH_new()))
|
||||
throw ModuleException("DH_new() failed!");
|
||||
|
||||
if (!DH_generate_parameters_ex(dh_params, keysize * 8, 5, NULL))
|
||||
{
|
||||
DH_free(dh_params);
|
||||
throw ModuleException("Could not generate DH-params");
|
||||
}
|
||||
}
|
||||
|
||||
~DHAES()
|
||||
{
|
||||
DH_free(dh_params);
|
||||
}
|
||||
|
||||
void ProcessMessage(SASL::Session *session, const SASL::Message &m) anope_override
|
||||
{
|
||||
DHAESSession *sess = anope_dynamic_static_cast<DHAESSession *>(session);
|
||||
|
||||
if (!sess->dh)
|
||||
{
|
||||
sasl->SendMessage(sess, "D", "A");
|
||||
delete sess;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.type == "S")
|
||||
{
|
||||
// Format: [ss]<p>[ss]<g>[ss]<pub_key>
|
||||
// Where ss is a unsigned short with the size of the key
|
||||
const BIGNUM* dhval[] = { sess->dh->p, sess->dh->g, sess->dh->pub_key };
|
||||
|
||||
// Find the size of our buffer - initialized at 6 because of string size data
|
||||
size_t size = 6;
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
size += BN_num_bytes(dhval[i]);
|
||||
|
||||
// Fill in the DH data
|
||||
std::vector<unsigned char> buffer(size);
|
||||
for (size_t i = 0, pos = 0; i < 3; i++)
|
||||
{
|
||||
*reinterpret_cast<uint16_t*>(&buffer[pos]) = htons(BN_num_bytes(dhval[i]));
|
||||
pos += 2;
|
||||
BN_bn2bin(dhval[i], &buffer[pos]);
|
||||
pos += BN_num_bytes(dhval[i]);
|
||||
}
|
||||
|
||||
Anope::string encoded;
|
||||
Anope::B64Encode(Anope::string(buffer.begin(), buffer.end()), encoded);
|
||||
sasl->SendMessage(sess, "C", encoded);
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
// Make sure we have some data - actual size check is done later
|
||||
if (m.data.length() < 10)
|
||||
return Err(sess);
|
||||
|
||||
// Format: [ss]<key>[ss]<iv>[ss]<encrypted>
|
||||
// <encrypted> = <username>\0<password>\0
|
||||
|
||||
Anope::string decoded;
|
||||
Anope::B64Decode(m.data, decoded);
|
||||
|
||||
// Make sure we have an IV and at least one encrypted block
|
||||
if ((decoded.length() < keysize + 2 + (AES_BLOCK_SIZE * 2)) || ((decoded.length() - keysize - 2) % AES_BLOCK_SIZE))
|
||||
return Err(sess);
|
||||
|
||||
const unsigned char* data = reinterpret_cast<const unsigned char*>(decoded.data());
|
||||
|
||||
// Control the size of the key
|
||||
if (ntohs(*reinterpret_cast<const uint16_t*>(&data[0])) != keysize)
|
||||
return Err(sess);
|
||||
|
||||
// Convert pubkey from binary
|
||||
size_t pos = 2;
|
||||
BIGNUM* pubkey = BN_bin2bn(&data[pos], keysize, NULL);
|
||||
if (!pubkey)
|
||||
return Err(sess);
|
||||
|
||||
// Find shared key
|
||||
std::vector<unsigned char> secretkey(keysize);
|
||||
if (DH_compute_key(&secretkey[0], pubkey, sess->dh) != static_cast<int>(keysize))
|
||||
return Err(sess, pubkey);
|
||||
|
||||
// Set decryption key
|
||||
AES_KEY AESKey;
|
||||
AES_set_decrypt_key(&secretkey[0], keysize * 8, &AESKey);
|
||||
|
||||
// Fetch IV
|
||||
pos += keysize;
|
||||
std::vector<unsigned char> IV(data + pos, data + pos + AES_BLOCK_SIZE);
|
||||
|
||||
// Find encrypted blocks, and decrypt
|
||||
pos += AES_BLOCK_SIZE;
|
||||
size_t size = decoded.length() - pos;
|
||||
std::vector<char> decrypted(size + 2, 0);
|
||||
AES_cbc_encrypt(&data[pos], reinterpret_cast<unsigned char*>(&decrypted[0]), size, &AESKey, &IV[0], AES_DECRYPT);
|
||||
|
||||
std::string username = &decrypted[0];
|
||||
std::string password = &decrypted[username.length() + 1];
|
||||
|
||||
if (username.empty() || password.empty() || !IRCD->IsNickValid(username) || password.find_first_of("\r\n") != Anope::string::npos)
|
||||
return Err(sess, pubkey);
|
||||
|
||||
SASL::IdentifyRequest* req = new SASL::IdentifyRequest(this->owner, m.source, username, password);
|
||||
FOREACH_MOD(OnCheckAuthentication, (NULL, req));
|
||||
req->Dispatch();
|
||||
|
||||
BN_free(pubkey);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ModuleSASLDHAES : public Module
|
||||
{
|
||||
DHAES dhaes;
|
||||
|
||||
public:
|
||||
ModuleSASLDHAES(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA),
|
||||
dhaes(this)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleSASLDHAES)
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* (C) 2014 Daniel Vassdal <shutter@canternet.org>
|
||||
* (C) 2011-2016 Anope Team
|
||||
* Contact us at team@anope.org
|
||||
*
|
||||
* Please read COPYING and README for further details.
|
||||
*/
|
||||
|
||||
/* RequiredLibraries: ssl,crypto */
|
||||
/* RequiredWindowsLibraries: ssleay32,libeay32 */
|
||||
|
||||
#include "module.h"
|
||||
#include "modules/sasl.h"
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/blowfish.h>
|
||||
|
||||
using namespace SASL;
|
||||
|
||||
class DHBS : public Mechanism
|
||||
{
|
||||
void Err(Session* sess, BIGNUM* key = NULL)
|
||||
{
|
||||
if (key)
|
||||
BN_free(key);
|
||||
|
||||
sasl->Fail(sess);
|
||||
delete sess;
|
||||
}
|
||||
|
||||
public:
|
||||
struct DHBSSession : SASL::Session
|
||||
{
|
||||
DH* dh;
|
||||
DHBSSession(Mechanism *m, const Anope::string &u, DH* dh_params) : SASL::Session(m, u)
|
||||
{
|
||||
if (!(dh = DH_new()))
|
||||
return;
|
||||
|
||||
dh->g = BN_dup(dh_params->g);
|
||||
dh->p = BN_dup(dh_params->p);
|
||||
|
||||
if (!DH_generate_key(dh))
|
||||
{
|
||||
DH_free(dh);
|
||||
dh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
~DHBSSession()
|
||||
{
|
||||
if (dh)
|
||||
DH_free(dh);
|
||||
}
|
||||
};
|
||||
|
||||
DH* dh_params;
|
||||
const size_t keysize;
|
||||
SASL::Session* CreateSession(const Anope::string &uid) anope_override
|
||||
{
|
||||
return new DHBSSession(this, uid, dh_params);
|
||||
}
|
||||
|
||||
DHBS(Module *o) : Mechanism(o, "DH-BLOWFISH"), keysize(256 / 8)
|
||||
{
|
||||
if (!(dh_params = DH_new()))
|
||||
throw ModuleException("DH_new() failed!");
|
||||
|
||||
if (!DH_generate_parameters_ex(dh_params, keysize * 8, 5, NULL))
|
||||
{
|
||||
DH_free(dh_params);
|
||||
throw ModuleException("Could not generate DH-params");
|
||||
}
|
||||
}
|
||||
|
||||
~DHBS()
|
||||
{
|
||||
DH_free(dh_params);
|
||||
}
|
||||
|
||||
void ProcessMessage(SASL::Session *session, const SASL::Message &m) anope_override
|
||||
{
|
||||
DHBSSession *sess = anope_dynamic_static_cast<DHBSSession *>(session);
|
||||
|
||||
if (!sess->dh)
|
||||
{
|
||||
sasl->SendMessage(sess, "D", "A");
|
||||
delete sess;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m.type == "S")
|
||||
{
|
||||
// Format: [ss]<p>[ss]<g>[ss]<pub_key>
|
||||
// Where ss is a unsigned short with the size of the key
|
||||
const BIGNUM* dhval[] = { sess->dh->p, sess->dh->g, sess->dh->pub_key };
|
||||
|
||||
// Find the size of our buffer - initialized at 6 because of string size data
|
||||
size_t size = 6;
|
||||
for (size_t i = 0; i < 3; i++)
|
||||
size += BN_num_bytes(dhval[i]);
|
||||
|
||||
// Fill in the DH data
|
||||
std::vector<unsigned char> buffer(size);
|
||||
for (size_t i = 0, pos = 0; i < 3; i++)
|
||||
{
|
||||
*reinterpret_cast<uint16_t*>(&buffer[pos]) = htons(BN_num_bytes(dhval[i]));
|
||||
pos += 2;
|
||||
BN_bn2bin(dhval[i], &buffer[pos]);
|
||||
pos += BN_num_bytes(dhval[i]);
|
||||
}
|
||||
|
||||
Anope::string encoded;
|
||||
Anope::B64Encode(Anope::string(buffer.begin(), buffer.end()), encoded);
|
||||
sasl->SendMessage(sess, "C", encoded);
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
// Make sure we have some data - actual size check is done later
|
||||
if (m.data.length() < 10)
|
||||
return Err(sess);
|
||||
|
||||
// Format: [ss]<key><username><\0><encrypted>
|
||||
|
||||
Anope::string decoded;
|
||||
Anope::B64Decode(m.data, decoded);
|
||||
|
||||
// As we rely on the client giving us a null terminator at the right place,
|
||||
// let's add one extra in case the client tries to crash us
|
||||
const size_t decodedlen = decoded.length();
|
||||
decoded.push_back('\0');
|
||||
|
||||
// Make sure we have enough data for at least the key, a one letter username, and a block of data
|
||||
if (decodedlen < keysize + 2 + 2 + 8)
|
||||
return Err(sess);
|
||||
|
||||
const unsigned char* data = reinterpret_cast<const unsigned char*>(decoded.data());
|
||||
|
||||
// Control the size of the key
|
||||
if (ntohs(*reinterpret_cast<const uint16_t*>(&data[0])) != keysize)
|
||||
return Err(sess);
|
||||
|
||||
// Convert pubkey from binary
|
||||
size_t pos = 2;
|
||||
BIGNUM* pubkey = BN_bin2bn(&data[pos], keysize, NULL);
|
||||
if (!pubkey)
|
||||
return Err(sess);
|
||||
|
||||
// Find shared key
|
||||
std::vector<unsigned char> secretkey(DH_size(sess->dh) + 1, 0);
|
||||
if (DH_compute_key(&secretkey[0], pubkey, sess->dh) != static_cast<int>(keysize))
|
||||
return Err(sess, pubkey);
|
||||
|
||||
// Set decryption key
|
||||
BF_KEY BFKey;
|
||||
BF_set_key(&BFKey, keysize, &secretkey[0]);
|
||||
|
||||
pos += keysize;
|
||||
const Anope::string username = reinterpret_cast<const char*>(&data[pos]);
|
||||
// Check that the username is valid, and that we have at least one block of data
|
||||
// 2 + 1 + 8 = uint16_t size for keylen, \0 for username, 8 for one block of data
|
||||
if (username.empty() || username.length() + keysize + 2 + 1 + 8 > decodedlen || !IRCD->IsNickValid(username))
|
||||
return Err(sess, pubkey);
|
||||
|
||||
pos += username.length() + 1;
|
||||
size_t size = decodedlen - pos;
|
||||
|
||||
// Blowfish data blocks are 64 bits wide - valid format?
|
||||
if (size % 8)
|
||||
return Err(sess, pubkey);
|
||||
|
||||
std::vector<char> decrypted(size + 1, 0);
|
||||
for (size_t i = 0; i < size; i += 8)
|
||||
BF_ecb_encrypt(&data[pos + i], reinterpret_cast<unsigned char*>(&decrypted[i]), &BFKey, BF_DECRYPT);
|
||||
|
||||
std::string password = &decrypted[0];
|
||||
if (password.empty() || password.find_first_of("\r\n") != Anope::string::npos)
|
||||
return Err(sess, pubkey);
|
||||
|
||||
SASL::IdentifyRequest* req = new SASL::IdentifyRequest(this->owner, m.source, username, password);
|
||||
FOREACH_MOD(OnCheckAuthentication, (NULL, req));
|
||||
req->Dispatch();
|
||||
|
||||
BN_free(pubkey);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ModuleSASLDHBS : public Module
|
||||
{
|
||||
DHBS dhbs;
|
||||
|
||||
public:
|
||||
ModuleSASLDHBS(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR | EXTRA),
|
||||
dhbs(this)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleSASLDHBS)
|
||||
+12
-6
@@ -103,8 +103,7 @@ class Packet : public Query
|
||||
/* +1 pos either to one byte after the compression pointer or one byte after the ending \0 */
|
||||
++pos;
|
||||
|
||||
if (name.empty())
|
||||
throw SocketException("Unable to unpack name - no name");
|
||||
/* Empty names are valid (root domain) */
|
||||
|
||||
Log(LOG_DEBUG_2) << "Resolver: UnpackName successfully unpacked " << name;
|
||||
|
||||
@@ -253,11 +252,18 @@ class Packet : public Query
|
||||
for (unsigned i = 0; i < ancount; ++i)
|
||||
this->answers.push_back(this->UnpackResourceRecord(input, len, packet_pos));
|
||||
|
||||
for (unsigned i = 0; i < nscount; ++i)
|
||||
this->authorities.push_back(this->UnpackResourceRecord(input, len, packet_pos));
|
||||
try
|
||||
{
|
||||
for (unsigned i = 0; i < nscount; ++i)
|
||||
this->authorities.push_back(this->UnpackResourceRecord(input, len, packet_pos));
|
||||
|
||||
for (unsigned i = 0; i < arcount; ++i)
|
||||
this->additional.push_back(this->UnpackResourceRecord(input, len, packet_pos));
|
||||
for (unsigned i = 0; i < arcount; ++i)
|
||||
this->additional.push_back(this->UnpackResourceRecord(input, len, packet_pos));
|
||||
}
|
||||
catch (const SocketException &ex)
|
||||
{
|
||||
Log(LOG_DEBUG_2) << "Unable to parse ns/ar records: " << ex.GetReason();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short Pack(unsigned char *output, unsigned short output_size)
|
||||
|
||||
+45
-2
@@ -97,7 +97,7 @@ class External : public Mechanism
|
||||
}
|
||||
else if (m.type == "C")
|
||||
{
|
||||
if (!certs)
|
||||
if (!certs || mysess->cert.empty())
|
||||
{
|
||||
sasl->Fail(sess);
|
||||
delete sess;
|
||||
@@ -223,7 +223,18 @@ class SASLService : public SASL::Service, public Timer
|
||||
|
||||
void Succeed(Session *session, NickCore *nc) anope_override
|
||||
{
|
||||
IRCD->SendSVSLogin(session->uid, nc->display);
|
||||
// If the user is already introduced then we log them in now.
|
||||
// Otherwise, we send an SVSLOGIN to log them in later.
|
||||
User *user = User::Find(session->uid);
|
||||
NickAlias *na = NickAlias::Find(nc->display);
|
||||
if (user)
|
||||
{
|
||||
user->Identify(na);
|
||||
}
|
||||
else
|
||||
{
|
||||
IRCD->SendSVSLogin(session->uid, nc->display, na->GetVhostIdent(), na->GetVhostHost());
|
||||
}
|
||||
this->SendMessage(session, "D", "S");
|
||||
}
|
||||
|
||||
@@ -266,6 +277,21 @@ class ModuleSASL : public Module
|
||||
Plain plain;
|
||||
External *external;
|
||||
|
||||
std::vector<Anope::string> mechs;
|
||||
|
||||
void CheckMechs()
|
||||
{
|
||||
std::vector<Anope::string> newmechs = ::Service::GetServiceKeys("SASL::Mechanism");
|
||||
if (newmechs == mechs)
|
||||
return;
|
||||
|
||||
mechs = newmechs;
|
||||
|
||||
// If we are connected to the network then broadcast the mechlist.
|
||||
if (Me && Me->IsSynced())
|
||||
IRCD->SendSASLMechanisms(mechs);
|
||||
}
|
||||
|
||||
public:
|
||||
ModuleSASL(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
|
||||
sasl(this), plain(this), external(NULL)
|
||||
@@ -273,6 +299,7 @@ class ModuleSASL : public Module
|
||||
try
|
||||
{
|
||||
external = new External(this);
|
||||
CheckMechs();
|
||||
}
|
||||
catch (ModuleException &) { }
|
||||
}
|
||||
@@ -281,6 +308,22 @@ class ModuleSASL : public Module
|
||||
{
|
||||
delete external;
|
||||
}
|
||||
|
||||
void OnModuleLoad(User *, Module *) anope_override
|
||||
{
|
||||
CheckMechs();
|
||||
}
|
||||
|
||||
void OnModuleUnload(User *, Module *) anope_override
|
||||
{
|
||||
CheckMechs();
|
||||
}
|
||||
|
||||
void OnPreUplinkSync(Server *) anope_override
|
||||
{
|
||||
// We have not yet sent a mechanism list so always do it here.
|
||||
IRCD->SendSASLMechanisms(mechs);
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_INIT(ModuleSASL)
|
||||
|
||||
@@ -273,6 +273,8 @@ class MyXMLRPCEvent : public XMLRPCEvent
|
||||
return;
|
||||
|
||||
u->SendMessage(bi, message);
|
||||
|
||||
request.reply("result", "Success");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+32
-1
@@ -14,6 +14,27 @@
|
||||
|
||||
class NSMaxEmail : public Module
|
||||
{
|
||||
bool clean;
|
||||
|
||||
/* strip dots from username, and remove anything after the first + */
|
||||
Anope::string CleanMail(const Anope::string &email)
|
||||
{
|
||||
size_t host = email.find('@');
|
||||
if (host == Anope::string::npos)
|
||||
return email;
|
||||
|
||||
Anope::string username = email.substr(0, host);
|
||||
username = username.replace_all_cs(".", "");
|
||||
|
||||
size_t sz = username.find('+');
|
||||
if (sz != Anope::string::npos)
|
||||
username = username.substr(0, sz);
|
||||
|
||||
Anope::string cleaned = username + email.substr(host);
|
||||
Log(LOG_DEBUG) << "cleaned " << email << " to " << cleaned;
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
bool CheckLimitReached(CommandSource &source, const Anope::string &email)
|
||||
{
|
||||
int NSEmailMax = Config->GetModule(this)->Get<int>("maxemails");
|
||||
@@ -39,11 +60,15 @@ class NSMaxEmail : public Module
|
||||
if (email.empty())
|
||||
return 0;
|
||||
|
||||
Anope::string cleanemail = clean ? CleanMail(email) : email;
|
||||
|
||||
for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it)
|
||||
{
|
||||
const NickCore *nc = it->second;
|
||||
|
||||
if (unc != nc && !nc->email.empty() && nc->email.equals_ci(email))
|
||||
Anope::string cleannc = clean ? CleanMail(nc->email) : nc->email;
|
||||
|
||||
if (unc != nc && cleanemail == cleannc)
|
||||
++count;
|
||||
}
|
||||
|
||||
@@ -52,9 +77,15 @@ class NSMaxEmail : public Module
|
||||
|
||||
public:
|
||||
NSMaxEmail(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR)
|
||||
, clean(false)
|
||||
{
|
||||
}
|
||||
|
||||
void OnReload(Configuration::Conf *conf) anope_override
|
||||
{
|
||||
clean = conf->GetModule(this)->Get<bool>("remove_aliases", "true");
|
||||
}
|
||||
|
||||
EventReturn OnPreCommand(CommandSource &source, Command *command, std::vector<Anope::string> ¶ms) anope_override
|
||||
{
|
||||
if (source.IsOper())
|
||||
|
||||
@@ -386,7 +386,12 @@ struct IRCDMessageNick : IRCDMessage
|
||||
User::OnIntroduce(params[0], params[4], params[5], "", params[8], s, params[9], signon, params[3], "", na ? *na->nc : NULL);
|
||||
}
|
||||
else
|
||||
source.GetUser()->ChangeNick(params[0]);
|
||||
{
|
||||
User *u = source.GetUser();
|
||||
|
||||
if (u)
|
||||
u->ChangeNick(params[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -31,13 +31,29 @@ class ChannelModeLargeBan : public ChannelMode
|
||||
|
||||
class CharybdisProto : public IRCDProto
|
||||
{
|
||||
BotInfo *FindIntroduced()
|
||||
{
|
||||
BotInfo *bi = Config->GetClient("OperServ");
|
||||
|
||||
if (bi && bi->introduced)
|
||||
return bi;
|
||||
|
||||
for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it)
|
||||
if (it->second->introduced)
|
||||
return it->second;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
CharybdisProto(Module *creator) : IRCDProto(creator, "Charybdis 3.4+")
|
||||
{
|
||||
DefaultPseudoclientModes = "+oiS";
|
||||
CanCertFP = true;
|
||||
CanSNLine = true;
|
||||
CanSQLine = true;
|
||||
CanSQLineChannel = true;
|
||||
CanSZLine = true;
|
||||
CanSVSNick = true;
|
||||
CanSVSHold = true;
|
||||
@@ -54,7 +70,6 @@ class CharybdisProto : public IRCDProto
|
||||
void SendSGLineDel(const XLine *x) anope_override { ratbox->SendSGLineDel(x); }
|
||||
void SendAkill(User *u, XLine *x) anope_override { ratbox->SendAkill(u, x); }
|
||||
void SendAkillDel(const XLine *x) anope_override { ratbox->SendAkillDel(x); }
|
||||
void SendSQLineDel(const XLine *x) anope_override { ratbox->SendSQLineDel(x); }
|
||||
void SendJoin(User *user, Channel *c, const ChannelStatus *status) anope_override { ratbox->SendJoin(user, c, status); }
|
||||
void SendServer(const Server *server) anope_override { ratbox->SendServer(server); }
|
||||
void SendChannel(Channel *c) anope_override { ratbox->SendChannel(c); }
|
||||
@@ -63,9 +78,32 @@ class CharybdisProto : public IRCDProto
|
||||
void SendLogin(User *u, NickAlias *na) anope_override { ratbox->SendLogin(u, na); }
|
||||
void SendLogout(User *u) anope_override { ratbox->SendLogout(u); }
|
||||
|
||||
void SendSASLMechanisms(std::vector<Anope::string> &mechanisms) anope_override
|
||||
{
|
||||
Anope::string mechlist;
|
||||
|
||||
for (unsigned i = 0; i < mechanisms.size(); ++i)
|
||||
{
|
||||
mechlist += "," + mechanisms[i];
|
||||
}
|
||||
|
||||
UplinkSocket::Message(Me) << "ENCAP * MECHLIST :" << (mechanisms.empty() ? "" : mechlist.substr(1));
|
||||
}
|
||||
|
||||
void SendSQLine(User *, const XLine *x) anope_override
|
||||
{
|
||||
UplinkSocket::Message(Me) << "RESV * " << x->mask << " :" << x->GetReason();
|
||||
/* Calculate the time left before this would expire, capping it at 2 days */
|
||||
time_t timeleft = x->expires - Anope::CurTime;
|
||||
|
||||
if (timeleft > 172800 || !x->expires)
|
||||
timeleft = 172800;
|
||||
|
||||
UplinkSocket::Message(FindIntroduced()) << "ENCAP * RESV " << timeleft << " " << x->mask << " 0 :" << x->GetReason();
|
||||
}
|
||||
|
||||
void SendSQLineDel(const XLine *x) anope_override
|
||||
{
|
||||
UplinkSocket::Message(Config->GetClient("OperServ")) << "ENCAP * UNRESV " << x->mask;
|
||||
}
|
||||
|
||||
void SendConnect() anope_override
|
||||
@@ -147,17 +185,21 @@ class CharybdisProto : public IRCDProto
|
||||
UplinkSocket::Message(Me) << "ENCAP " << (s ? s->GetName() : message.target.substr(0, 3)) << " SASL " << message.source << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : (" " + message.ext));
|
||||
}
|
||||
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc) anope_override
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
|
||||
{
|
||||
Server *s = Server::Find(uid.substr(0, 3));
|
||||
UplinkSocket::Message(Me) << "ENCAP " << (s ? s->GetName() : uid.substr(0, 3)) << " SVSLOGIN " << uid << " * * * " << acc;
|
||||
UplinkSocket::Message(Me) << "ENCAP " << (s ? s->GetName() : uid.substr(0, 3)) << " SVSLOGIN " << uid << " * " << (!vident.empty() ? vident : '*') << " " << (!vhost.empty() ? vhost : '*') << " " << acc;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct IRCDMessageEncap : IRCDMessage
|
||||
{
|
||||
IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 3) { SetFlag(IRCDMESSAGE_SOFT_LIMIT);}
|
||||
IRCDMessageEncap(Module *creator) : IRCDMessage(creator, "ENCAP", 3)
|
||||
{
|
||||
SetFlag(IRCDMESSAGE_SOFT_LIMIT);
|
||||
SetFlag(IRCDMESSAGE_REQUIRE_USER);
|
||||
}
|
||||
|
||||
void Run(MessageSource &source, const std::vector<Anope::string> ¶ms) anope_override
|
||||
{
|
||||
@@ -225,7 +267,7 @@ struct IRCDMessageEUID : IRCDMessage
|
||||
if (params[9] != "*")
|
||||
na = NickAlias::Find(params[9]);
|
||||
|
||||
User::OnIntroduce(params[0], params[4], params[8], params[5], params[6], source.GetServer(), params[10], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime, params[3], params[7], na ? *na->nc : NULL);
|
||||
User::OnIntroduce(params[0], params[4], (params[8] != "*" ? params[8] : params[5]), params[5], params[6], source.GetServer(), params[10], params[2].is_pos_number_only() ? convertTo<time_t>(params[2]) : Anope::CurTime, params[3], params[7], na ? *na->nc : NULL);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -406,7 +406,7 @@ class InspIRCd12Proto : public IRCDProto
|
||||
UplinkSocket::Message(Me) << "ENCAP " << message.target.substr(0, 3) << " SASL " << message.source << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : (" " + message.ext));
|
||||
}
|
||||
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc) anope_override
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
|
||||
{
|
||||
UplinkSocket::Message(Me) << "METADATA " << uid << " accountname :" << acc;
|
||||
|
||||
|
||||
@@ -42,6 +42,15 @@ class InspIRCd20Proto : public IRCDProto
|
||||
insp12->SendConnect();
|
||||
}
|
||||
|
||||
void SendSASLMechanisms(std::vector<Anope::string> &mechanisms) anope_override
|
||||
{
|
||||
Anope::string mechlist;
|
||||
for (unsigned i = 0; i < mechanisms.size(); ++i)
|
||||
mechlist += "," + mechanisms[i];
|
||||
|
||||
UplinkSocket::Message(Me) << "METADATA * saslmechlist :" << (mechanisms.empty() ? "" : mechlist.substr(1));
|
||||
}
|
||||
|
||||
void SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) anope_override { insp12->SendSVSKillInternal(source, user, buf); }
|
||||
void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { insp12->SendGlobalNotice(bi, dest, msg); }
|
||||
void SendGlobalPrivmsg(BotInfo *bi, const Server *dest, const Anope::string &msg) anope_override { insp12->SendGlobalPrivmsg(bi, dest, msg); }
|
||||
@@ -72,11 +81,26 @@ class InspIRCd20Proto : public IRCDProto
|
||||
void SendLogout(User *u) anope_override { insp12->SendLogout(u); }
|
||||
void SendChannel(Channel *c) anope_override { insp12->SendChannel(c); }
|
||||
void SendSASLMessage(const SASL::Message &message) anope_override { insp12->SendSASLMessage(message); }
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc) anope_override { insp12->SendSVSLogin(uid, acc); }
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override { insp12->SendSVSLogin(uid, acc, vident, vhost); }
|
||||
bool IsExtbanValid(const Anope::string &mask) anope_override { return insp12->IsExtbanValid(mask); }
|
||||
bool IsIdentValid(const Anope::string &ident) anope_override { return insp12->IsIdentValid(ident); }
|
||||
};
|
||||
|
||||
class InspIRCdAutoOpMode : public ChannelModeList
|
||||
{
|
||||
public:
|
||||
InspIRCdAutoOpMode(char mode) : ChannelModeList("AUTOOP", mode)
|
||||
{
|
||||
}
|
||||
|
||||
bool IsValid(Anope::string &mask) const anope_override
|
||||
{
|
||||
// We can not validate this because we don't know about the
|
||||
// privileges of the setter so just reject attempts to set it.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class InspIRCdExtBan : public ChannelModeVirtual<ChannelModeList>
|
||||
{
|
||||
char ext;
|
||||
@@ -395,6 +419,8 @@ struct IRCDMessageCapab : Message::Capab
|
||||
}
|
||||
else if (modename.equals_cs("auditorium"))
|
||||
cm = new ChannelMode("AUDITORIUM", modechar[0]);
|
||||
else if (modename.equals_cs("autoop"))
|
||||
cm = new InspIRCdAutoOpMode(modechar[0]);
|
||||
else if (modename.equals_cs("ban"))
|
||||
cm = new ChannelModeList("BAN", modechar[0]);
|
||||
else if (modename.equals_cs("banexception"))
|
||||
|
||||
@@ -280,7 +280,7 @@ struct IRCDMessageChaninfo : IRCDMessage
|
||||
|
||||
struct IRCDMessageJoin : Message::Join
|
||||
{
|
||||
IRCDMessageJoin(Module *creator) : Message::Join(creator, "JOIN") { }
|
||||
IRCDMessageJoin(Module *creator) : Message::Join(creator, "JOIN") { SetFlag(IRCDMESSAGE_REQUIRE_USER); }
|
||||
|
||||
/*
|
||||
* <@po||ux> DukeP: RFC 2813, 4.2.1: the JOIN command on server-server links
|
||||
@@ -440,8 +440,11 @@ struct IRCDMessageNick : IRCDMessage
|
||||
{
|
||||
if (params.size() == 1)
|
||||
{
|
||||
User *u = source.GetUser();
|
||||
|
||||
// we have a nickchange
|
||||
source.GetUser()->ChangeNick(params[0]);
|
||||
if (u)
|
||||
u->ChangeNick(params[0]);
|
||||
}
|
||||
else if (params.size() == 7)
|
||||
{
|
||||
|
||||
@@ -139,6 +139,25 @@ struct IRCDMessageEncap : IRCDMessage
|
||||
}
|
||||
};
|
||||
|
||||
struct IRCDMessageJoin : Message::Join
|
||||
{
|
||||
IRCDMessageJoin(Module *creator) : Message::Join(creator, "JOIN") { }
|
||||
|
||||
void Run(MessageSource &source, const std::vector<Anope::string> ¶ms) anope_override
|
||||
{
|
||||
if (params.size() == 1 && params[0] == "0")
|
||||
return Message::Join::Run(source, params);
|
||||
|
||||
if (params.size() < 2)
|
||||
return;
|
||||
|
||||
std::vector<Anope::string> p = params;
|
||||
p.erase(p.begin());
|
||||
|
||||
return Message::Join::Run(source, p);
|
||||
}
|
||||
};
|
||||
|
||||
struct IRCDMessagePass : IRCDMessage
|
||||
{
|
||||
IRCDMessagePass(Module *creator) : IRCDMessage(creator, "PASS", 4) { SetFlag(IRCDMESSAGE_REQUIRE_SERVER); }
|
||||
@@ -229,11 +248,12 @@ class ProtoRatbox : public Module
|
||||
Message::Whois message_whois;
|
||||
|
||||
/* Hybrid message handlers */
|
||||
ServiceAlias message_bmask, message_join, message_nick, message_pong, message_sid,
|
||||
ServiceAlias message_bmask, message_nick, message_pong, message_sid,
|
||||
message_sjoin, message_tmode;
|
||||
|
||||
/* Our message handlers */
|
||||
IRCDMessageEncap message_encap;
|
||||
IRCDMessageJoin message_join;
|
||||
IRCDMessagePass message_pass;
|
||||
IRCDMessageServer message_server;
|
||||
IRCDMessageTBurst message_tburst;
|
||||
@@ -296,12 +316,11 @@ class ProtoRatbox : public Module
|
||||
message_ping(this), message_privmsg(this), message_quit(this), message_squit(this), message_stats(this),
|
||||
message_time(this), message_topic(this), message_version(this), message_whois(this),
|
||||
|
||||
message_bmask("IRCDMessage", "ratbox/bmask", "hybrid/bmask"), message_join("IRCDMessage", "ratbox/join", "hybrid/join"),
|
||||
message_nick("IRCDMessage", "ratbox/nick", "hybrid/nick"), message_pong("IRCDMessage", "ratbox/pong", "hybrid/pong"),
|
||||
message_sid("IRCDMessage", "ratbox/sid", "hybrid/sid"), message_sjoin("IRCDMessage", "ratbox/sjoin", "hybrid/sjoin"),
|
||||
message_tmode("IRCDMessage", "ratbox/tmode", "hybrid/tmode"),
|
||||
message_bmask("IRCDMessage", "ratbox/bmask", "hybrid/bmask"), message_nick("IRCDMessage", "ratbox/nick", "hybrid/nick"),
|
||||
message_pong("IRCDMessage", "ratbox/pong", "hybrid/pong"), message_sid("IRCDMessage", "ratbox/sid", "hybrid/sid"),
|
||||
message_sjoin("IRCDMessage", "ratbox/sjoin", "hybrid/sjoin"), message_tmode("IRCDMessage", "ratbox/tmode", "hybrid/tmode"),
|
||||
|
||||
message_encap(this), message_pass(this), message_server(this), message_tburst(this), message_uid(this)
|
||||
message_encap(this), message_join(this), message_pass(this), message_server(this), message_tburst(this), message_uid(this)
|
||||
{
|
||||
|
||||
if (ModuleManager::LoadModule("hybrid", User::Find(creator)) != MOD_ERR_OK)
|
||||
|
||||
@@ -386,7 +386,7 @@ class UnrealIRCdProto : public IRCDProto
|
||||
UplinkSocket::Message(BotInfo::Find(message.source)) << "SASL " << message.target.substr(0, p) << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : " " + message.ext);
|
||||
}
|
||||
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc) anope_override
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
|
||||
{
|
||||
size_t p = uid.find('!');
|
||||
if (p == Anope::string::npos)
|
||||
@@ -929,7 +929,11 @@ struct IRCDMessageNick : IRCDMessage
|
||||
User::OnIntroduce(params[0], params[3], params[4], vhost, ip, s, params[10], user_ts, params[7], "", na ? *na->nc : NULL);
|
||||
}
|
||||
else
|
||||
source.GetUser()->ChangeNick(params[0]);
|
||||
{
|
||||
User *u = source.GetUser();
|
||||
if (u)
|
||||
u->ChangeNick(params[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ class UnrealIRCdProto : public IRCDProto
|
||||
UplinkSocket::Message(BotInfo::Find(message.source)) << "SASL " << message.target.substr(0, p) << " " << message.target << " " << message.type << " " << message.data << (message.ext.empty() ? "" : " " + message.ext);
|
||||
}
|
||||
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc) anope_override
|
||||
void SendSVSLogin(const Anope::string &uid, const Anope::string &acc, const Anope::string &vident, const Anope::string &vhost) anope_override
|
||||
{
|
||||
size_t p = uid.find('!');
|
||||
if (p == Anope::string::npos)
|
||||
@@ -966,7 +966,11 @@ struct IRCDMessageNick : IRCDMessage
|
||||
User::OnIntroduce(params[0], params[3], params[4], vhost, ip, s, params[10], user_ts, params[7], "", na ? *na->nc : NULL);
|
||||
}
|
||||
else
|
||||
source.GetUser()->ChangeNick(params[0]);
|
||||
{
|
||||
User *u = source.GetUser();
|
||||
if (u)
|
||||
u->ChangeNick(params[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1279,7 +1283,7 @@ struct IRCDMessageUID : IRCDMessage
|
||||
|
||||
User *u = User::OnIntroduce(nickname, username, hostname, vhost, ip, source.GetServer(), info, user_ts, umodes, uid, na ? *na->nc : NULL);
|
||||
|
||||
if (!chost.empty() && chost != u->GetCloakedHost())
|
||||
if (u && !chost.empty() && chost != u->GetCloakedHost())
|
||||
u->SetCloakedHost(chost);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -67,6 +67,7 @@ class MemoServCore : public Module, public MemoServService
|
||||
sender->lastmemosend = Anope::CurTime;
|
||||
|
||||
Memo *m = new Memo();
|
||||
m->mi = mi;
|
||||
mi->memos->push_back(m);
|
||||
m->owner = target;
|
||||
m->sender = source;
|
||||
|
||||
@@ -97,7 +97,12 @@ class SQLineManager : public XLineManager
|
||||
u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->reason);
|
||||
}
|
||||
else if (x->mask[0] != '#' || IRCD->CanSQLineChannel)
|
||||
{
|
||||
IRCD->SendSQLine(u, x);
|
||||
/* If it is an oper, assume they're walking it, otherwise kill for good measure */
|
||||
if (u && !u->HasMode("OPER"))
|
||||
u->Kill(Config->GetClient("OperServ"), "Q-Lined: " + x->reason);
|
||||
}
|
||||
}
|
||||
|
||||
void SendDel(XLine *x) anope_override
|
||||
|
||||
@@ -46,10 +46,9 @@ bool WebCPanel::NickServ::Alist::OnRequest(HTTPProvider *server, const Anope::st
|
||||
|
||||
replacements["NUMBERS"] = stringify(chan_count);
|
||||
replacements["CHANNELS"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name;
|
||||
Anope::string access_str;
|
||||
for (unsigned i = 0; i < access.size(); ++i)
|
||||
access_str += ", " + access[i]->AccessSerialize();
|
||||
replacements["ACCESSES"] = access_str.substr(2);
|
||||
|
||||
const ChanAccess *highest = access.Highest();
|
||||
replacements["ACCESSES"] = highest ? highest->AccessSerialize() : "";
|
||||
}
|
||||
|
||||
TemplateFileServer page("nickserv/alist.html");
|
||||
|
||||
+42
-51
@@ -250,8 +250,10 @@ Serializable* ChanAccess::Unserialize(Serializable *obj, Serialize::Data &data)
|
||||
return access;
|
||||
}
|
||||
|
||||
bool ChanAccess::Matches(const User *u, const NickCore *acc, Path &p) const
|
||||
bool ChanAccess::Matches(const User *u, const NickCore *acc, ChannelInfo* &next) const
|
||||
{
|
||||
next = NULL;
|
||||
|
||||
if (this->nc)
|
||||
return this->nc == acc;
|
||||
|
||||
@@ -276,28 +278,7 @@ bool ChanAccess::Matches(const User *u, const NickCore *acc, Path &p) const
|
||||
|
||||
if (IRCD->IsChannelValid(this->mask))
|
||||
{
|
||||
ChannelInfo *tci = ChannelInfo::Find(this->mask);
|
||||
if (tci)
|
||||
{
|
||||
for (unsigned i = 0; i < tci->GetAccessCount(); ++i)
|
||||
{
|
||||
ChanAccess *a = tci->GetAccess(i);
|
||||
std::pair<const ChanAccess *, const ChanAccess *> pair = std::make_pair(this, a);
|
||||
|
||||
std::pair<Set::iterator, Set::iterator> range = p.first.equal_range(this);
|
||||
for (; range.first != range.second; ++range.first)
|
||||
if (range.first->first == pair.first && range.first->second == pair.second)
|
||||
goto cont;
|
||||
|
||||
p.first.insert(pair);
|
||||
if (a->Matches(u, acc, p))
|
||||
p.second.insert(pair);
|
||||
|
||||
cont:;
|
||||
}
|
||||
|
||||
return p.second.count(this) > 0;
|
||||
}
|
||||
next = ChannelInfo::Find(this->mask);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -347,37 +328,30 @@ bool ChanAccess::operator<=(const ChanAccess &other) const
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
AccessGroup::AccessGroup() : std::vector<ChanAccess *>()
|
||||
AccessGroup::AccessGroup()
|
||||
{
|
||||
this->ci = NULL;
|
||||
this->nc = NULL;
|
||||
this->super_admin = this->founder = false;
|
||||
}
|
||||
|
||||
static bool HasPriv(const AccessGroup &ag, const ChanAccess *access, const Anope::string &name)
|
||||
static bool HasPriv(const ChanAccess::Path &path, const Anope::string &name)
|
||||
{
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnCheckPriv, MOD_RESULT, (access, name));
|
||||
if (MOD_RESULT == EVENT_ALLOW || access->HasPriv(name))
|
||||
if (path.empty())
|
||||
return false;
|
||||
|
||||
for (unsigned int i = 0; i < path.size(); ++i)
|
||||
{
|
||||
typedef std::multimap<const ChanAccess *, const ChanAccess *> path;
|
||||
std::pair<path::const_iterator, path::const_iterator> it = ag.path.second.equal_range(access);
|
||||
if (it.first != it.second)
|
||||
/* check all of the paths for this entry */
|
||||
for (; it.first != it.second; ++it.first)
|
||||
{
|
||||
const ChanAccess *a = it.first->second;
|
||||
/* if only one path fully matches then we are ok */
|
||||
if (HasPriv(ag, a, name))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
/* entry is the end of a chain, all entries match, ok */
|
||||
return true;
|
||||
ChanAccess *access = path[i];
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(OnCheckPriv, MOD_RESULT, (access, name));
|
||||
|
||||
if (MOD_RESULT != EVENT_ALLOW && !access->HasPriv(name))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* entry does not match or none of the chains fully match */
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AccessGroup::HasPriv(const Anope::string &name) const
|
||||
@@ -393,7 +367,7 @@ bool AccessGroup::HasPriv(const Anope::string &name) const
|
||||
bool auto_mode = !name.find("AUTO");
|
||||
|
||||
/* Only grant founder privilege if this isn't an auto mode or if they don't match any entries in this group */
|
||||
if ((!auto_mode || this->empty()) && this->founder)
|
||||
if ((!auto_mode || paths.empty()) && this->founder)
|
||||
return true;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
@@ -401,23 +375,40 @@ bool AccessGroup::HasPriv(const Anope::string &name) const
|
||||
if (MOD_RESULT != EVENT_CONTINUE)
|
||||
return MOD_RESULT == EVENT_ALLOW;
|
||||
|
||||
for (unsigned i = this->size(); i > 0; --i)
|
||||
for (unsigned int i = paths.size(); i > 0; --i)
|
||||
{
|
||||
ChanAccess *access = this->at(i - 1);
|
||||
const ChanAccess::Path &path = paths[i - 1];
|
||||
|
||||
if (::HasPriv(*this, access, name))
|
||||
if (::HasPriv(path, name))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static ChanAccess *HighestInPath(const ChanAccess::Path &path)
|
||||
{
|
||||
ChanAccess *highest = NULL;
|
||||
|
||||
for (unsigned int i = 0; i < path.size(); ++i)
|
||||
if (highest == NULL || *path[i] > *highest)
|
||||
highest = path[i];
|
||||
|
||||
return highest;
|
||||
}
|
||||
|
||||
const ChanAccess *AccessGroup::Highest() const
|
||||
{
|
||||
ChanAccess *highest = NULL;
|
||||
for (unsigned i = 0; i < this->size(); ++i)
|
||||
if (highest == NULL || *this->at(i) > *highest)
|
||||
highest = this->at(i);
|
||||
|
||||
for (unsigned int i = 0; i < paths.size(); ++i)
|
||||
{
|
||||
ChanAccess *hip = HighestInPath(paths[i]);
|
||||
|
||||
if (highest == NULL || *hip > *highest)
|
||||
highest = hip;
|
||||
}
|
||||
|
||||
return highest;
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -168,8 +168,7 @@ void Channel::DeleteUser(User *user)
|
||||
Log(LOG_DEBUG) << "Channel::DeleteUser() tried to delete non-existent channel " << this->name << " from " << user->nick << "'s channel list";
|
||||
delete cu;
|
||||
|
||||
if (std::find(deleting.begin(), deleting.end(), this) == deleting.end())
|
||||
deleting.push_back(this);
|
||||
QueueForDeletion();
|
||||
}
|
||||
|
||||
ChanUserContainer *Channel::FindUser(User *u) const
|
||||
@@ -938,6 +937,12 @@ Channel *Channel::FindOrCreate(const Anope::string &name, bool &created, time_t
|
||||
return chan;
|
||||
}
|
||||
|
||||
void Channel::QueueForDeletion()
|
||||
{
|
||||
if (std::find(deleting.begin(), deleting.end(), this) == deleting.end())
|
||||
deleting.push_back(this);
|
||||
}
|
||||
|
||||
void Channel::DeleteChannels()
|
||||
{
|
||||
for (unsigned int i = 0; i < deleting.size(); ++i)
|
||||
|
||||
+1
-1
@@ -117,7 +117,7 @@ void CommandSource::Reply(const Anope::string &message)
|
||||
this->reply->SendMessage(this->service, tok);
|
||||
}
|
||||
|
||||
Command::Command(Module *o, const Anope::string &sname, size_t minparams, size_t maxparams) : Service(o, "Command", sname), max_params(maxparams), min_params(minparams), module(owner)
|
||||
Command::Command(Module *o, const Anope::string &sname, size_t minparams, size_t maxparams) : Service(o, "Command", sname), max_params(maxparams), min_params(minparams), module(o)
|
||||
{
|
||||
allow_unregistered = require_user = false;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ const char *Language::Translate(const NickCore *nc, const char *string)
|
||||
|
||||
#if GETTEXT_FOUND
|
||||
|
||||
#ifdef __USE_GNU_GETTEXT
|
||||
extern "C" int _nl_msg_cat_cntr;
|
||||
#endif
|
||||
|
||||
const char *Language::Translate(const char *lang, const char *string)
|
||||
{
|
||||
if (!string || !*string)
|
||||
@@ -83,6 +87,10 @@ const char *Language::Translate(const char *lang, const char *string)
|
||||
if (!lang || !*lang)
|
||||
lang = Config->DefLanguage.c_str();
|
||||
|
||||
#ifdef __USE_GNU_GETTEXT
|
||||
++_nl_msg_cat_cntr;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
SetThreadLocale(MAKELCID(MAKELANGID(WindowsGetLanguage(lang), SUBLANG_DEFAULT), SORT_DEFAULT));
|
||||
#else
|
||||
|
||||
+12
-3
@@ -19,13 +19,12 @@
|
||||
|
||||
Memo::Memo() : Serializable("Memo")
|
||||
{
|
||||
mi = NULL;
|
||||
unread = receipt = false;
|
||||
}
|
||||
|
||||
Memo::~Memo()
|
||||
{
|
||||
bool ischan;
|
||||
MemoInfo *mi = MemoInfo::GetMemoInfo(this->owner, ischan);
|
||||
if (mi)
|
||||
{
|
||||
std::vector<Memo *>::iterator it = std::find(mi->memos->begin(), mi->memos->end(), this);
|
||||
@@ -60,7 +59,10 @@ Serializable* Memo::Unserialize(Serializable *obj, Serialize::Data &data)
|
||||
if (obj)
|
||||
m = anope_dynamic_static_cast<Memo *>(obj);
|
||||
else
|
||||
{
|
||||
m = new Memo();
|
||||
m->mi = mi;
|
||||
}
|
||||
|
||||
m->owner = owner;
|
||||
data["time"] >> m->time;
|
||||
@@ -99,7 +101,14 @@ void MemoInfo::Del(unsigned index)
|
||||
{
|
||||
if (index >= this->memos->size())
|
||||
return;
|
||||
delete this->GetMemo(index);
|
||||
|
||||
Memo *m = this->GetMemo(index);
|
||||
|
||||
std::vector<Memo *>::iterator it = std::find(memos->begin(), memos->end(), m);
|
||||
if (it != memos->end())
|
||||
memos->erase(it);
|
||||
|
||||
delete m;
|
||||
}
|
||||
|
||||
bool MemoInfo::HasIgnore(User *u)
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ void Join::SJoin(MessageSource &source, const Anope::string &chan, time_t ts, co
|
||||
c->Sync();
|
||||
|
||||
if (c->CheckDelete())
|
||||
delete c;
|
||||
c->QueueForDeletion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -320,7 +320,7 @@ Anope::string Anope::Duration(time_t t, const NickCore *nc)
|
||||
time_t minutes = (t / 60) % 60;
|
||||
time_t seconds = (t) % 60;
|
||||
|
||||
if (!days && !hours && !minutes)
|
||||
if (!years && !days && !hours && !minutes)
|
||||
return stringify(seconds) + " " + (seconds != 1 ? Language::Translate(nc, _("seconds")) : Language::Translate(nc, _("second")));
|
||||
else
|
||||
{
|
||||
@@ -632,7 +632,11 @@ Anope::string Anope::VersionShort()
|
||||
|
||||
Anope::string Anope::VersionBuildString()
|
||||
{
|
||||
#ifdef REPRODUCIBLE_BUILD
|
||||
Anope::string s = "build #" + stringify(BUILD);
|
||||
#else
|
||||
Anope::string s = "build #" + stringify(BUILD) + ", compiled " + Anope::compiled;
|
||||
#endif
|
||||
Anope::string flags;
|
||||
|
||||
#ifdef DEBUG_BUILD
|
||||
|
||||
+20
-8
@@ -742,7 +742,7 @@ void ModeManager::StackerDel(Mode *m)
|
||||
}
|
||||
}
|
||||
|
||||
Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh), cidr_len(0)
|
||||
Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh), cidr_len(0), family(0)
|
||||
{
|
||||
Anope::string n, u, h;
|
||||
|
||||
@@ -802,14 +802,13 @@ Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh
|
||||
if (addr.valid() && cidr_range.is_pos_number_only())
|
||||
{
|
||||
this->cidr_len = convertTo<unsigned short>(cidr_range);
|
||||
/* If we got here, cidr_len is a valid number.
|
||||
* If cidr_len is >32 (or 128) it is handled later in
|
||||
* cidr::match
|
||||
*/
|
||||
|
||||
/* If we got here, cidr_len is a valid number. */
|
||||
|
||||
this->host = cidr_ip;
|
||||
this->family = addr.family();
|
||||
|
||||
Log(LOG_DEBUG) << "Ban " << m << " has cidr " << this->cidr_len;
|
||||
Log(LOG_DEBUG) << "Ban " << mask << " has cidr " << this->cidr_len;
|
||||
}
|
||||
}
|
||||
catch (const ConvertException &) { }
|
||||
@@ -830,8 +829,21 @@ const Anope::string Entry::GetNUHMask() const
|
||||
Anope::string n = nick.empty() ? "*" : nick,
|
||||
u = user.empty() ? "*" : user,
|
||||
h = host.empty() ? "*" : host,
|
||||
r = real.empty() ? "" : "#" + real;
|
||||
return n + "!" + u + "@" + h + r;
|
||||
r = real.empty() ? "" : "#" + real,
|
||||
c;
|
||||
switch (family)
|
||||
{
|
||||
case AF_INET:
|
||||
if (cidr_len <= 32)
|
||||
c = "/" + stringify(cidr_len);
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (cidr_len <= 128)
|
||||
c = "/" + stringify(cidr_len);
|
||||
break;
|
||||
}
|
||||
|
||||
return n + "!" + u + "@" + h + c + r;
|
||||
}
|
||||
|
||||
bool Entry::Matches(User *u, bool full) const
|
||||
|
||||
+69
-35
@@ -157,7 +157,7 @@ ChannelInfo::~ChannelInfo()
|
||||
if (this->c)
|
||||
{
|
||||
if (this->c && this->c->CheckDelete())
|
||||
delete this->c;
|
||||
this->c->QueueForDeletion();
|
||||
|
||||
this->c = NULL;
|
||||
}
|
||||
@@ -384,6 +384,39 @@ ChanAccess *ChannelInfo::GetAccess(unsigned index) const
|
||||
return acc;
|
||||
}
|
||||
|
||||
static void FindMatchesRecurse(ChannelInfo *ci, const User *u, const NickCore *account, unsigned int depth, std::vector<ChanAccess::Path> &paths, ChanAccess::Path &path)
|
||||
{
|
||||
if (depth > ChanAccess::MAX_DEPTH)
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < ci->GetAccessCount(); ++i)
|
||||
{
|
||||
ChanAccess *a = ci->GetAccess(i);
|
||||
ChannelInfo *next = NULL;
|
||||
|
||||
if (a->Matches(u, account, next))
|
||||
{
|
||||
ChanAccess::Path next_path = path;
|
||||
next_path.push_back(a);
|
||||
|
||||
paths.push_back(next_path);
|
||||
}
|
||||
else if (next)
|
||||
{
|
||||
ChanAccess::Path next_path = path;
|
||||
next_path.push_back(a);
|
||||
|
||||
FindMatchesRecurse(next, u, account, depth + 1, paths, next_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void FindMatches(AccessGroup &group, ChannelInfo *ci, const User *u, const NickCore *account)
|
||||
{
|
||||
ChanAccess::Path path;
|
||||
FindMatchesRecurse(ci, u, account, 0, group.paths, path);
|
||||
}
|
||||
|
||||
AccessGroup ChannelInfo::AccessFor(const User *u, bool updateLastUsed)
|
||||
{
|
||||
AccessGroup group;
|
||||
@@ -404,20 +437,20 @@ AccessGroup ChannelInfo::AccessFor(const User *u, bool updateLastUsed)
|
||||
group.ci = this;
|
||||
group.nc = nc;
|
||||
|
||||
for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i)
|
||||
{
|
||||
ChanAccess *a = this->GetAccess(i);
|
||||
if (a->Matches(u, u->Account(), group.path))
|
||||
group.push_back(a);
|
||||
}
|
||||
FindMatches(group, this, u, u->Account());
|
||||
|
||||
if (group.founder || !group.empty())
|
||||
if (group.founder || !group.paths.empty())
|
||||
{
|
||||
if (updateLastUsed)
|
||||
this->last_used = Anope::CurTime;
|
||||
|
||||
for (unsigned i = 0; i < group.size(); ++i)
|
||||
group[i]->last_seen = Anope::CurTime;
|
||||
for (unsigned i = 0; i < group.paths.size(); ++i)
|
||||
{
|
||||
ChanAccess::Path &p = group.paths[i];
|
||||
|
||||
for (unsigned int j = 0; j < p.size(); ++j)
|
||||
p[j]->last_seen = Anope::CurTime;
|
||||
}
|
||||
}
|
||||
|
||||
return group;
|
||||
@@ -431,14 +464,9 @@ AccessGroup ChannelInfo::AccessFor(const NickCore *nc, bool updateLastUsed)
|
||||
group.ci = this;
|
||||
group.nc = nc;
|
||||
|
||||
for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i)
|
||||
{
|
||||
ChanAccess *a = this->GetAccess(i);
|
||||
if (a->Matches(NULL, nc, group.path))
|
||||
group.push_back(a);
|
||||
}
|
||||
FindMatches(group, this, NULL, nc);
|
||||
|
||||
if (group.founder || !group.empty())
|
||||
if (group.founder || !group.paths.empty())
|
||||
if (updateLastUsed)
|
||||
this->last_used = Anope::CurTime;
|
||||
|
||||
@@ -452,28 +480,34 @@ unsigned ChannelInfo::GetAccessCount() const
|
||||
return this->access->size();
|
||||
}
|
||||
|
||||
unsigned ChannelInfo::GetDeepAccessCount() const
|
||||
static unsigned int GetDeepAccessCount(const ChannelInfo *ci, std::set<const ChannelInfo *> &seen, unsigned int depth)
|
||||
{
|
||||
ChanAccess::Path path;
|
||||
for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i)
|
||||
if (depth > ChanAccess::MAX_DEPTH || seen.count(ci))
|
||||
return 0;
|
||||
seen.insert(ci);
|
||||
|
||||
unsigned int total = 0;
|
||||
|
||||
for (unsigned int i = 0; i < ci->GetAccessCount(); ++i)
|
||||
{
|
||||
ChanAccess *a = this->GetAccess(i);
|
||||
a->Matches(NULL, NULL, path);
|
||||
ChanAccess::Path path;
|
||||
ChanAccess *a = ci->GetAccess(i);
|
||||
ChannelInfo *next = NULL;
|
||||
|
||||
a->Matches(NULL, NULL, next);
|
||||
++total;
|
||||
|
||||
if (next)
|
||||
total += GetDeepAccessCount(ci, seen, depth + 1);
|
||||
}
|
||||
|
||||
unsigned count = this->GetAccessCount();
|
||||
std::set<const ChannelInfo *> channels;
|
||||
channels.insert(this);
|
||||
for (ChanAccess::Set::iterator it = path.first.begin(); it != path.first.end(); ++it)
|
||||
{
|
||||
const ChannelInfo *ci = it->first->ci;
|
||||
if (!channels.count(ci))
|
||||
{
|
||||
channels.count(ci);
|
||||
count += ci->GetAccessCount();
|
||||
}
|
||||
}
|
||||
return count;
|
||||
return total;
|
||||
}
|
||||
|
||||
unsigned ChannelInfo::GetDeepAccessCount() const
|
||||
{
|
||||
std::set<const ChannelInfo *> seen;
|
||||
return ::GetDeepAccessCount(this, seen, 0);
|
||||
}
|
||||
|
||||
ChanAccess *ChannelInfo::EraseAccess(unsigned index)
|
||||
|
||||
@@ -39,6 +39,11 @@ void sockaddrs::clear()
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
|
||||
int sockaddrs::family() const
|
||||
{
|
||||
return sa.sa_family;
|
||||
}
|
||||
|
||||
size_t sockaddrs::size() const
|
||||
{
|
||||
switch (sa.sa_family)
|
||||
|
||||
@@ -48,5 +48,5 @@ endif(NOT WIN32)
|
||||
|
||||
# On non-Windows platforms, if RUNGROUP is set, change the permissions of the tools directory
|
||||
if(NOT WIN32 AND RUNGROUP)
|
||||
install(CODE "execute_process(COMMAND ${CHMOD} 2770 \"\$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin\")")
|
||||
install(CODE "execute_process(COMMAND ${CHMOD} 2770 \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin\")")
|
||||
endif(NOT WIN32 AND RUNGROUP)
|
||||
|
||||
@@ -507,6 +507,8 @@ void User::UpdateHost()
|
||||
Anope::string last_realhost = this->GetIdent() + "@" + this->host;
|
||||
na->last_usermask = last_usermask;
|
||||
na->last_realhost = last_realhost;
|
||||
// This is called on signon, and if users are introduced with an account it won't update
|
||||
na->last_realname = this->realname;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -2,6 +2,6 @@
|
||||
|
||||
VERSION_MAJOR=2
|
||||
VERSION_MINOR=0
|
||||
VERSION_PATCH=3
|
||||
VERSION_PATCH=4
|
||||
VERSION_EXTRA=""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user