diff options
Diffstat (limited to 'src')
67 files changed, 2341 insertions, 3270 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bdec61abf..79d10e39c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,72 +2,37 @@ file(GLOB SRC_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp") if(WIN32) - append_to_list(SRC_SRCS win32/dir/dir.cpp) - append_to_list(SRC_SRCS win32/socket.cpp) - append_to_list(SRC_SRCS win32/windows.cpp) - append_to_list(SRC_SRCS win32/dl/dl.cpp) - append_to_list(SRC_SRCS win32/pipe/pipe.cpp) - append_to_list(SRC_SRCS win32/pthread/pthread.cpp) - append_to_list(SRC_SRCS win32/sigaction/sigaction.cpp) -endif(WIN32) + list(APPEND SRC_SRCS win32/socket.cpp) + list(APPEND SRC_SRCS win32/windows.cpp) + list(APPEND SRC_SRCS win32/dl/dl.cpp) + list(APPEND SRC_SRCS win32/pipe/pipe.cpp) + list(APPEND SRC_SRCS win32/sigaction/sigaction.cpp) +endif() if(HAVE_EPOLL) - append_to_list(SRC_SRCS socketengines/socketengine_epoll.cpp) -else(HAVE_EPOLL) - if(HAVE_KQUEUE) - append_to_list(SRC_SRCS socketengines/socketengine_kqueue.cpp) - else(HAVE_KQUEUE) - if(HAVE_POLL) - append_to_list(SRC_SRCS socketengines/socketengine_poll.cpp) - else(HAVE_POLL) - append_to_list(SRC_SRCS socketengines/socketengine_select.cpp) - endif(HAVE_POLL) - endif(HAVE_KQUEUE) -endif(HAVE_EPOLL) + list(APPEND SRC_SRCS socketengines/epoll.cpp) +elseif(HAVE_KQUEUE) + list(APPEND SRC_SRCS socketengines/kqueue.cpp) +elseif(HAVE_POLL) + list(APPEND SRC_SRCS socketengines/poll.cpp) +else() + list(APPEND SRC_SRCS socketengines/select.cpp) +endif() -sort_list(SRC_SRCS) +list(SORT SRC_SRCS) # Set all the files to use C++ as well as set their compile flags (use the module-specific compile flags, though) set_source_files_properties(${SRC_SRCS} PROPERTIES LANGUAGE CXX COMPILE_FLAGS "${CXXFLAGS}") -# Create an empty list to store extra include directories -set(EXTRA_INCLUDES) -# Iterate through all the source files -foreach(SRC ${SRC_SRCS}) - # Temporary variable for the current source's include directories - set(TEMP_INCLUDES) - # Calculate the header file dependencies for the given source file - calculate_depends(${SRC} TEMP_INCLUDES) - # If there were some extra include directories, add them to the list - if(TEMP_INCLUDES) - append_to_list(EXTRA_INCLUDES ${TEMP_INCLUDES}) - endif(TEMP_INCLUDES) -endforeach(SRC) -# If there were extra include directories, remove the duplicates and add the directories to the include path -if(EXTRA_INCLUDES) - remove_list_duplicates(EXTRA_INCLUDES) - include_directories(${EXTRA_INCLUDES}) -endif(EXTRA_INCLUDES) - # Under Windows, we also include a resource file to the build if(WIN32) # Make sure that the resource file is seen as an RC file to be compiled with a resource compiler, not a C++ compiler set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/win32/win32.rc LANGUAGE RC) # Add the resource file to the list of sources - append_to_list(SRC_SRCS ${CMAKE_CURRENT_BINARY_DIR}/win32/win32.rc) - # For MinGW, we have to change the compile flags - if(MINGW) - set(RC_CFLAGS "-DMINGW -Ocoff -I${Anope_SOURCE_DIR}/include") - # If any sort of debugging is being enabled, add a _DEBUG define to the flags for the resource compiler - if(CMAKE_BUILD_TYPE STREQUAL "DEBUG" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") - set(RC_CFLAGS "${RC_CFLAGS} -D_DEBUG") - endif(CMAKE_BUILD_TYPE STREQUAL "DEBUG" OR CMAKE_BUILD_TYPE STREQUAL "RELWITHDEBINFO") - set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/win32/win32.rc COMPILE_FLAGS "${RC_CFLAGS}") - # For anything else, assumingly Visual Studio at this point, use a different set of compile flags - else(MINGW) - set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/win32/win32.rc COMPILE_FLAGS "/i\"${Anope_SOURCE_DIR}/include\"") - endif(MINGW) -endif(WIN32) + list(APPEND SRC_SRCS ${CMAKE_CURRENT_BINARY_DIR}/win32/win32.rc) + + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/win32/win32.rc COMPILE_FLAGS "/i\"${Anope_SOURCE_DIR}/include\"") +endif() # If compiling with Visual Studio, create a static library out of win32/win32_memory.cpp to be included with everything else, needed to override its override of new/delete operators if(MSVC) @@ -75,26 +40,33 @@ if(MSVC) add_library(win32_memory STATIC win32/win32_memory.cpp) set(WIN32_MEMORY win32_memory) set(EXTRA_LDFLAGS "/OPT:NOREF") # https://sourceware.org/bugzilla/show_bug.cgi?id=12633 -else(MSVC) +else() set(WIN32_MEMORY) -endif(MSVC) +endif() # Generate the Anope executable and set it's linker flags, also set it to export it's symbols even though it's not a module add_executable(${PROGRAM_NAME} ${SRC_SRCS}) set_target_properties(${PROGRAM_NAME} PROPERTIES LINKER_LANGUAGE CXX LINK_FLAGS "${LDFLAGS} ${EXTRA_LDFLAGS}" ENABLE_EXPORTS ON INSTALL_RPATH_USE_LINK_PATH ON BUILD_WITH_INSTALL_RPATH ON) + # On Windows, also link Anope to the wsock32 and Ws2_32 library, as well as set the version if(WIN32) - target_link_libraries(${PROGRAM_NAME} wsock32 Ws2_32 ${LINK_LIBS} ${GETTEXT_LIBRARIES} ${WIN32_MEMORY}) + target_link_libraries(${PROGRAM_NAME} wsock32 Ws2_32 ${LINK_LIBS} ${WIN32_MEMORY}) set_target_properties(${PROGRAM_NAME} PROPERTIES VERSION "${VERSION_DOTTED}") -else(WIN32) - target_link_libraries(${PROGRAM_NAME} ${LINK_LIBS} ${GETTEXT_LIBRARIES}) -endif(WIN32) +else() + target_link_libraries(${PROGRAM_NAME} ${LINK_LIBS}) +endif() + +# If being built with localisation we might need to link against libintl. +if(HAVE_LOCALIZATION AND Intl_LIBRARY) + target_link_libraries(${PROGRAM_NAME} ${Intl_LIBRARY}) +endif() + # Building the Anope executable requires the version.h header to be generated add_dependencies(${PROGRAM_NAME} headers) # Also require the language files if we have gettext -if(GETTEXT_FOUND) +if(HAVE_LOCALIZATION) add_dependencies(${PROGRAM_NAME} language) -endif(GETTEXT_FOUND) +endif() # Get the filename of the Anope executable as it is in on this system set(SERVICES_BINARY "$<TARGET_FILE:${PROGRAM_NAME}>") @@ -108,9 +80,10 @@ configure_file(${Anope_SOURCE_DIR}/include/sysconf.h.cmake ${Anope_BINARY_DIR}/i # Go into the following directories and run their CMakeLists.txt as well if(NOT DISABLE_TOOLS) add_subdirectory(tools) -endif(NOT DISABLE_TOOLS) +endif() # Set Anope to be installed to the bin directory install(TARGETS ${PROGRAM_NAME} DESTINATION ${BIN_DIR} + RUNTIME ) diff --git a/src/access.cpp b/src/access.cpp index fc1d9a51c..1eb7b8930 100644 --- a/src/access.cpp +++ b/src/access.cpp @@ -16,55 +16,8 @@ #include "account.h" #include "protocol.h" -static struct -{ - Anope::string name; - Anope::string desc; -} descriptions[] = { - {"ACCESS_CHANGE", _("Allowed to modify the access list")}, - {"ACCESS_LIST", _("Allowed to view the access list")}, - {"AKICK", _("Allowed to use the AKICK command")}, - {"ASSIGN", _("Allowed to assign/unassign a bot")}, - {"AUTOHALFOP", _("Automatic halfop upon join")}, - {"AUTOOP", _("Automatic channel operator status upon join")}, - {"AUTOOWNER", _("Automatic owner upon join")}, - {"AUTOPROTECT", _("Automatic protect upon join")}, - {"AUTOVOICE", _("Automatic voice on join")}, - {"BADWORDS", _("Allowed to modify channel badwords list")}, - {"BAN", _("Allowed to ban users")}, - {"FANTASIA", _("Allowed to use fantasy commands")}, - {"FOUNDER", _("Allowed to issue commands restricted to channel founders")}, - {"GETKEY", _("Allowed to use GETKEY command")}, - {"GREET", _("Greet message displayed on join")}, - {"HALFOP", _("Allowed to (de)halfop users")}, - {"HALFOPME", _("Allowed to (de)halfop him/herself")}, - {"INFO", _("Allowed to get full INFO output")}, - {"INVITE", _("Allowed to use the INVITE command")}, - {"KICK", _("Allowed to use the KICK command")}, - {"MEMO", _("Allowed to read channel memos")}, - {"MODE", _("Allowed to use the MODE command")}, - {"NOKICK", _("Prevents users being kicked by Services")}, - {"OP", _("Allowed to (de)op users")}, - {"OPME", _("Allowed to (de)op him/herself")}, - {"OWNER", _("Allowed to (de)owner users")}, - {"OWNERME", _("Allowed to (de)owner him/herself")}, - {"PROTECT", _("Allowed to (de)protect users")}, - {"PROTECTME", _("Allowed to (de)protect him/herself")}, - {"SAY", _("Allowed to use SAY and ACT commands")}, - {"SET", _("Allowed to set channel settings")}, - {"SIGNKICK", _("No signed kick when SIGNKICK LEVEL is used")}, - {"TOPIC", _("Allowed to change channel topics")}, - {"UNBAN", _("Allowed to unban users")}, - {"VOICE", _("Allowed to (de)voice users")}, - {"VOICEME", _("Allowed to (de)voice him/herself")} -}; - Privilege::Privilege(const Anope::string &n, const Anope::string &d, int r) : name(n), desc(d), rank(r) { - if (this->desc.empty()) - for (unsigned j = 0; j < sizeof(descriptions) / sizeof(*descriptions); ++j) - if (descriptions[j].name.equals_ci(name)) - this->desc = descriptions[j].desc; } bool Privilege::operator==(const Privilege &other) const @@ -94,10 +47,10 @@ void PrivilegeManager::RemovePrivilege(Privilege &p) if (it != Privileges.end()) Privileges.erase(it); - for (registered_channel_map::const_iterator cit = RegisteredChannelList->begin(), cit_end = RegisteredChannelList->end(); cit != cit_end; ++cit) + for (const auto &[_, ci] : *RegisteredChannelList) { - cit->second->QueueUpdate(); - cit->second->RemoveLevel(p.name); + ci->QueueUpdate(); + ci->RemoveLevel(p.name); } } @@ -138,7 +91,9 @@ const std::list<AccessProvider *>& AccessProvider::GetProviders() return Providers; } -ChanAccess::ChanAccess(AccessProvider *p) : Serializable("ChanAccess"), provider(p) +ChanAccess::ChanAccess(AccessProvider *p) + : Serializable(CHANACCESS_TYPE) + , provider(p) { } @@ -205,18 +160,26 @@ NickCore *ChanAccess::GetAccount() const return nc; } -void ChanAccess::Serialize(Serialize::Data &data) const + +ChanAccess::Type::Type() + : Serialize::Type(CHANACCESS_TYPE) { - data["provider"] << this->provider->name; - data["ci"] << this->ci->name; - data["mask"] << this->Mask(); - data["creator"] << this->creator; - data.SetType("last_seen", Serialize::Data::DT_INT); data["last_seen"] << this->last_seen; - data.SetType("created", Serialize::Data::DT_INT); data["created"] << this->created; - data["data"] << this->AccessSerialize(); } -Serializable* ChanAccess::Unserialize(Serializable *obj, Serialize::Data &data) +void ChanAccess::Type::Serialize(const Serializable *obj, Serialize::Data &data) const +{ + const auto *access = static_cast<const ChanAccess *>(obj); + data.Store("provider", access->provider->name); + data.Store("ci", access->ci->name); + data.Store("mask", access->Mask()); + data.Store("creator", access->creator); + data.Store("description", access->description); + data.Store("last_seen", access->last_seen); + data.Store("created", access->created); + data.Store("data", access->AccessSerialize()); +} + +Serializable *ChanAccess::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { Anope::string provider, chan; @@ -238,6 +201,7 @@ Serializable* ChanAccess::Unserialize(Serializable *obj, Serialize::Data &data) data["mask"] >> m; access->SetMask(m, ci); data["creator"] >> access->creator; + data["description"] >> access->description; data["last_seen"] >> access->last_seen; data["created"] >> access->created; @@ -250,7 +214,7 @@ Serializable* ChanAccess::Unserialize(Serializable *obj, Serialize::Data &data) return access; } -bool ChanAccess::Matches(const User *u, const NickCore *acc, ChannelInfo* &next) const +bool ChanAccess::Matches(const User *u, const NickCore *acc, ChannelInfo *&next) const { next = NULL; @@ -268,9 +232,8 @@ bool ChanAccess::Matches(const User *u, const NickCore *acc, ChannelInfo* &next) if (acc) { - for (unsigned i = 0; i < acc->aliases->size(); ++i) + for (auto *na : *acc->aliases) { - const NickAlias *na = acc->aliases->at(i); if (Anope::Match(na->nick, this->mask)) return true; } @@ -340,10 +303,8 @@ static bool HasPriv(const ChanAccess::Path &path, const Anope::string &name) if (path.empty()) return false; - for (unsigned int i = 0; i < path.size(); ++i) + for (auto *access : path) { - ChanAccess *access = path[i]; - EventReturn MOD_RESULT; FOREACH_RESULT(OnCheckPriv, MOD_RESULT, (access, name)); @@ -390,9 +351,9 @@ 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]; + for (auto *ca : path) + if (highest == NULL || *ca > *highest) + highest = ca; return highest; } @@ -401,9 +362,9 @@ const ChanAccess *AccessGroup::Highest() const { ChanAccess *highest = NULL; - for (unsigned int i = 0; i < paths.size(); ++i) + for (const auto &path : paths) { - ChanAccess *hip = HighestInPath(paths[i]); + ChanAccess *hip = HighestInPath(path); if (highest == NULL || *hip > *highest) highest = hip; diff --git a/src/account.cpp b/src/account.cpp index 6b69d0e3b..95bfc9698 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -18,7 +18,11 @@ std::set<IdentifyRequest *> IdentifyRequest::Requests; -IdentifyRequest::IdentifyRequest(Module *o, const Anope::string &acc, const Anope::string &pass) : owner(o), account(acc), password(pass), dispatched(false), success(false) +IdentifyRequest::IdentifyRequest(Module *o, const Anope::string &acc, const Anope::string &pass, const Anope::string &ip) + : owner(o) + , account(acc) + , password(pass) + , address(ip) { Requests.insert(this); } diff --git a/src/base.cpp b/src/base.cpp index 58ce2f097..3cd026bda 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -13,16 +13,12 @@ std::map<Anope::string, std::map<Anope::string, Service *> > Service::Services; std::map<Anope::string, std::map<Anope::string, Anope::string> > Service::Aliases; -Base::Base() : references(NULL) -{ -} - Base::~Base() { if (this->references != NULL) { - for (std::set<ReferenceBase *>::iterator it = this->references->begin(), it_end = this->references->end(); it != it_end; ++it) - (*it)->Invalidate(); + for (auto *reference : *this->references) + reference->Invalidate(); delete this->references; } } diff --git a/src/bots.cpp b/src/bots.cpp index b4e3a2996..b01b7389f 100644 --- a/src/bots.cpp +++ b/src/bots.cpp @@ -18,9 +18,13 @@ #include "language.h" #include "serialize.h" -Serialize::Checker<botinfo_map> BotListByNick("BotInfo"), BotListByUID("BotInfo"); +Serialize::Checker<botinfo_map> BotListByNick(BOTINFO_TYPE), BotListByUID(BOTINFO_TYPE); -BotInfo::BotInfo(const Anope::string &nnick, const Anope::string &nuser, const Anope::string &nhost, const Anope::string &nreal, const Anope::string &bmodes) : User(nnick, nuser, nhost, "", "", Me, nreal, Anope::CurTime, "", IRCD ? IRCD->UID_Retrieve() : "", NULL), Serializable("BotInfo"), channels("ChannelInfo"), botmodes(bmodes) +BotInfo::BotInfo(const Anope::string &nnick, const Anope::string &nuser, const Anope::string &nhost, const Anope::string &nreal, const Anope::string &bmodes) + : User(nnick, nuser, nhost, "", "", Me, nreal, Anope::CurTime, "", {}, IRCD ? IRCD->UID_Retrieve() : "", NULL) + , Serializable(BOTINFO_TYPE) + , channels(CHANNELINFO_TYPE) + , botmodes(bmodes) { this->lastmsg = this->created = Anope::CurTime; this->introduced = false; @@ -35,9 +39,16 @@ BotInfo::BotInfo(const Anope::string &nnick, const Anope::string &nuser, const A // If we're synchronised with the uplink already, send the bot. if (Me && Me->IsSynced()) { - Anope::string tmodes = !this->botmodes.empty() ? ("+" + this->botmodes) : IRCD->DefaultPseudoclientModes; - if (!tmodes.empty()) - this->SetModesInternal(this, tmodes.c_str()); + spacesepstream modesep(this->botmodes.empty() ? IRCD->DefaultPseudoclientModes : "+" + this->botmodes); + + Anope::string modechars; + modesep.GetToken(modechars); + + std::vector<Anope::string> modeparams; + modesep.GetTokens(modeparams); + + if (!modechars.empty()) + this->SetModesInternal(this, modechars, modeparams); XLine x(this->nick, "Reserved for services"); IRCD->SendSQLine(NULL, &x); @@ -55,7 +66,7 @@ BotInfo::~BotInfo() // If we're synchronised with the uplink already, send the bot. if (Me && Me->IsSynced()) { - IRCD->SendQuit(this, ""); + IRCD->SendQuit(this); FOREACH_MOD(OnUserQuit, (this, "")); this->introduced = false; XLine x(this->nick); @@ -73,19 +84,25 @@ BotInfo::~BotInfo() BotListByUID->erase(this->uid); } -void BotInfo::Serialize(Serialize::Data &data) const +BotInfo::Type::Type() + : Serialize::Type(BOTINFO_TYPE) +{ +} + +void BotInfo::Type::Serialize(const Serializable *obj, Serialize::Data &data) const { - data["nick"] << this->nick; - data["user"] << this->ident; - data["host"] << this->host; - data["realname"] << this->realname; - data["created"] << this->created; - data["oper_only"] << this->oper_only; - - Extensible::ExtensibleSerialize(this, this, data); + const auto *bi = static_cast<const BotInfo *>(obj); + data.Store("nick", bi->nick); + data.Store("user", bi->ident); + data.Store("host", bi->host); + data.Store("realname", bi->realname); + data.Store("created", bi->created); + data.Store("oper_only", bi->oper_only); + + Extensible::ExtensibleSerialize(bi, bi, data); } -Serializable* BotInfo::Unserialize(Serializable *obj, Serialize::Data &data) +Serializable *BotInfo::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { Anope::string nick, user, host, realname, flags; @@ -131,8 +148,8 @@ void BotInfo::OnKill() IRCD->SendClientIntroduction(this); this->introduced = true; - for (User::ChanUserList::const_iterator cit = this->chans.begin(), cit_end = this->chans.end(); cit != cit_end; ++cit) - IRCD->SendJoin(this, cit->second->chan, &cit->second->status); + for (const auto &[_, chan] : this->chans) + IRCD->SendJoin(this, chan->chan, &chan->status); } void BotInfo::SetNewNick(const Anope::string &newnick) @@ -216,23 +233,28 @@ void BotInfo::Part(Channel *c, const Anope::string &reason) FOREACH_MOD(OnPrePartChannel, (this, c)); - IRCD->SendPart(this, c, "%s", !reason.empty() ? reason.c_str() : ""); + IRCD->SendPart(this, c, reason); c->DeleteUser(this); FOREACH_MOD(OnPartChannel, (this, c, c->name, reason)); } -void BotInfo::OnMessage(User *u, const Anope::string &message) +void BotInfo::OnMessage(User *u, const Anope::string &message, const Anope::map<Anope::string> &tags) { if (this->commands.empty()) return; - CommandSource source(u->nick, u, u->Account(), u, this); + Anope::string msgid; + auto iter = tags.find("msgid"); + if (iter != tags.end()) + msgid = iter->second; + + CommandSource source(u->nick, u, u->Account(), u, this, msgid); Command::Run(source, message); } -CommandInfo& BotInfo::SetCommand(const Anope::string &cname, const Anope::string &sname, const Anope::string &permission) +CommandInfo &BotInfo::SetCommand(const Anope::string &cname, const Anope::string &sname, const Anope::string &permission) { CommandInfo ci; ci.name = sname; @@ -249,7 +271,14 @@ CommandInfo *BotInfo::GetCommand(const Anope::string &cname) return NULL; } -BotInfo* BotInfo::Find(const Anope::string &nick, bool nick_only) +Anope::string BotInfo::GetQueryCommand() const +{ + if (Config->ServiceAlias && !this->alias.empty()) + return Anope::printf("/%s", this->alias.c_str()); + return Anope::printf("/msg %s", this->nick.c_str()); +} + +BotInfo *BotInfo::Find(const Anope::string &nick, bool nick_only) { if (!nick_only && IRCD != NULL && IRCD->RequiresID) { diff --git a/src/channels.cpp b/src/channels.cpp index b8e9f024d..3e7fb6e50 100644 --- a/src/channels.cpp +++ b/src/channels.cpp @@ -29,17 +29,12 @@ channel_map ChannelList; std::vector<Channel *> Channel::deleting; Channel::Channel(const Anope::string &nname, time_t ts) + : name(nname) + , creation_time(ts) { if (nname.empty()) throw CoreException("A channel without a name ?"); - this->name = nname; - - this->creation_time = ts; - this->syncing = this->botchannel = false; - this->server_modetime = this->chanserv_modetime = 0; - this->server_modecount = this->chanserv_modecount = this->bouncy_modes = this->topic_ts = this->topic_time = 0; - this->ci = ChannelInfo::Find(this->name); if (this->ci) this->ci->c = this; @@ -71,25 +66,23 @@ void Channel::Reset() { this->modes.clear(); - for (ChanUserList::const_iterator it = this->users.begin(), it_end = this->users.end(); it != it_end; ++it) + for (const auto &[_, uc] : this->users) { - ChanUserContainer *uc = it->second; - ChannelStatus f = uc->status; uc->status.Clear(); /* reset modes for my clients */ if (uc->user->server == Me) { - for (size_t i = 0; i < f.Modes().length(); ++i) - this->SetMode(NULL, ModeManager::FindChannelModeByChar(f.Modes()[i]), uc->user->GetUID(), false); + for (auto mode : f.Modes()) + this->SetMode(NULL, ModeManager::FindChannelModeByChar(mode), uc->user->GetUID(), false); /* Modes might not exist yet, so be sure the status is really reset */ uc->status = f; } } - for (ChanUserList::const_iterator it = this->users.begin(), it_end = this->users.end(); it != it_end; ++it) - this->SetCorrectModes(it->second->user, true); + for (auto &[_, cuc] : this->users) + this->SetCorrectModes(cuc->user, true); // If the channel is syncing now, do not force a sync due to Reset(), as we are probably iterating over users in Message::SJoin // A sync will come soon @@ -113,7 +106,7 @@ void Channel::CheckModes() if (this->chanserv_modetime == Anope::CurTime && this->server_modetime == Anope::CurTime && this->server_modecount >= 3 && this->chanserv_modecount >= 3) { Log() << "Warning: unable to set modes on channel " << this->name << ". Are your servers' U:lines configured correctly?"; - this->bouncy_modes = 1; + this->bouncy_modes = true; return; } @@ -139,12 +132,12 @@ bool Channel::CheckDelete() return MOD_RESULT != EVENT_STOP && this->users.empty(); } -ChanUserContainer* Channel::JoinUser(User *user, const ChannelStatus *status) +ChanUserContainer *Channel::JoinUser(User *user, const ChannelStatus *status) { if (user->server && user->server->IsSynced()) Log(user, this, "join"); - ChanUserContainer *cuc = new ChanUserContainer(user, this); + auto *cuc = new ChanUserContainer(user, this); user->chans[this] = cuc; this->users[user] = cuc; if (status) @@ -203,10 +196,12 @@ size_t Channel::HasMode(const Anope::string &mname, const Anope::string ¶m) { if (param.empty()) return modes.count(mname); - std::vector<Anope::string> v = this->GetModeList(mname); - for (unsigned int i = 0; i < v.size(); ++i) - if (v[i].equals_ci(param)) + + for (const auto &mode : this->GetModeList(mname)) + { + if (mode.equals_ci(param)) return 1; + } return 0; } @@ -214,22 +209,22 @@ Anope::string Channel::GetModes(bool complete, bool plus) { Anope::string res, params; - for (std::multimap<Anope::string, Anope::string>::const_iterator it = this->modes.begin(), it_end = this->modes.end(); it != it_end; ++it) + for (const auto &[mode, value] : this->modes) { - ChannelMode *cm = ModeManager::FindChannelModeByName(it->first); + ChannelMode *cm = ModeManager::FindChannelModeByName(mode); if (!cm || cm->type == MODE_LIST) continue; res += cm->mchar; - if (complete && !it->second.empty()) + if (complete && !value.empty()) { ChannelModeParam *cmp = NULL; if (cm->type == MODE_PARAM) cmp = anope_dynamic_static_cast<ChannelModeParam *>(cm); if (plus || !cmp || !cmp->minus_no_arg) - params += " " + it->second; + params += " " + value; } } @@ -242,7 +237,7 @@ const Channel::ModeList &Channel::GetModes() const } template<typename F, typename S> -struct second +struct second final { S operator()(const std::pair<F, S> &p) { @@ -304,7 +299,7 @@ void Channel::SetModeInternal(MessageSource &setter, ChannelMode *ocm, const Ano else if (this->HasMode(cm->name, param)) return; - this->modes.insert(std::make_pair(cm->name, param)); + this->modes.emplace(cm->name, param); if (param.empty() && cm->type != MODE_REGULAR) { @@ -542,15 +537,21 @@ void Channel::SetModes(BotInfo *bi, bool enforce_mlock, const char *cmodes, ...) { char buf[BUFSIZE] = ""; va_list args; - Anope::string modebuf, sbuf; - int add = -1; va_start(args, cmodes); vsnprintf(buf, BUFSIZE - 1, cmodes, args); va_end(args); + SetModes(bi, enforce_mlock, Anope::string(buf)); +} + + +void Channel::SetModes(BotInfo *bi, bool enforce_mlock, const Anope::string &cmodes) +{ + Anope::string modebuf, sbuf; + int add = -1; Reference<Channel> this_reference(this); - spacesepstream sep(buf); + spacesepstream sep(cmodes); sep.GetToken(modebuf); for (unsigned i = 0, end = modebuf.length(); this_reference && i < end; ++i) { @@ -605,13 +606,13 @@ void Channel::SetModes(BotInfo *bi, bool enforce_mlock, const char *cmodes, ...) } } -void Channel::SetModesInternal(MessageSource &source, const Anope::string &mode, time_t ts, bool enforce_mlock) +void Channel::SetModesInternal(MessageSource &source, const Anope::string &modes, const std::vector<Anope::string> ¶ms, time_t ts, bool enforce_mlock) { if (!ts) ; else if (ts > this->creation_time) { - Log(LOG_DEBUG) << "Dropping mode " << mode << " on " << this->name << ", " << ts << " > " << this->creation_time; + Log(LOG_DEBUG) << "Dropping mode " << modes << " on " << this->name << ", " << ts << " > " << this->creation_time; return; } else if (ts < this->creation_time) @@ -625,20 +626,18 @@ void Channel::SetModesInternal(MessageSource &source, const Anope::string &mode, /* Removing channel modes *may* delete this channel */ Reference<Channel> this_reference(this); - spacesepstream sep_modes(mode); - Anope::string m; - - sep_modes.GetToken(m); - Anope::string modestring; Anope::string paramstring; int add = -1; bool changed = false; - for (unsigned int i = 0, end = m.length(); i < end && this_reference; ++i) + auto paramit = params.begin(); + for (const auto mchar : modes) { - ChannelMode *cm; + if (!this_reference) + break; - switch (m[i]) + ChannelMode *cm; + switch (mchar) { case '+': modestring += '+'; @@ -651,10 +650,10 @@ void Channel::SetModesInternal(MessageSource &source, const Anope::string &mode, default: if (add == -1) continue; - cm = ModeManager::FindChannelModeByChar(m[i]); + cm = ModeManager::FindChannelModeByChar(mchar); if (!cm) { - Log(LOG_DEBUG) << "Channel::SetModeInternal: Unknown mode char " << m[i]; + Log(LOG_DEBUG) << "Channel::SetModeInternal: Unknown mode char " << mchar; continue; } modestring += cm->mchar; @@ -680,9 +679,9 @@ void Channel::SetModesInternal(MessageSource &source, const Anope::string &mode, continue; } } - Anope::string token; - if (sep_modes.GetToken(token)) + if (paramit != params.end()) { + auto token = *paramit++; User *u = NULL; if (cm->type == MODE_STATUS && (u = User::Find(token))) paramstring += " " + u->nick; @@ -697,7 +696,7 @@ void Channel::SetModesInternal(MessageSource &source, const Anope::string &mode, this->RemoveModeInternal(source, cm, token, enforce_mlock); } else - Log() << "warning: Channel::SetModesInternal() received more modes requiring params than params, modes: " << mode; + Log() << "warning: Channel::SetModesInternal() received more modes requiring params than params, modes: " << modes; } if (!this_reference) @@ -728,10 +727,9 @@ bool Channel::MatchesList(User *u, const Anope::string &mode) if (!this->HasMode(mode)) return false; - std::vector<Anope::string> v = this->GetModeList(mode); - for (unsigned i = 0; i < v.size(); ++i) + for (const auto &entry : this->GetModeList(mode)) { - Entry e(mode, v[i]); + Entry e(mode, entry); if (e.Matches(u)) return true; } @@ -778,6 +776,11 @@ bool Channel::Kick(BotInfo *bi, User *u, const char *reason, ...) vsnprintf(buf, BUFSIZE - 1, reason, args); va_end(args); + return Kick(bi, u, Anope::string(buf)); +} + +bool Channel::Kick(BotInfo *bi, User *u, const Anope::string &reason) +{ /* Do not kick protected clients or Ulines */ if (u->IsProtected()) return false; @@ -786,11 +789,11 @@ bool Channel::Kick(BotInfo *bi, User *u, const char *reason, ...) bi = this->WhoSends(); EventReturn MOD_RESULT; - FOREACH_RESULT(OnBotKick, MOD_RESULT, (bi, this, u, buf)); + FOREACH_RESULT(OnBotKick, MOD_RESULT, (bi, this, u, reason)); if (MOD_RESULT == EVENT_STOP) return false; - IRCD->SendKick(bi, this, u, "%s", buf); - this->KickInternal(bi, u->nick, buf); + IRCD->SendKick(bi, this, u, reason); + this->KickInternal(bi, u->nick, reason); return true; } @@ -845,9 +848,8 @@ void Channel::SetCorrectModes(User *user, bool give_modes) bool giving = give_modes; /* whether or not we have given a mode */ bool given = false; - for (unsigned i = 0; i < ModeManager::GetStatusChannelModesByRank().size(); ++i) + for (auto *cm : ModeManager::GetStatusChannelModesByRank()) { - ChannelModeStatus *cm = ModeManager::GetStatusChannelModesByRank()[i]; bool has_priv = u_access.HasPriv("AUTO" + cm->name); if (give_modes && has_priv) @@ -880,10 +882,9 @@ bool Channel::Unban(User *u, const Anope::string &mode, bool full) bool ret = false; - std::vector<Anope::string> v = this->GetModeList(mode); - for (unsigned int i = 0; i < v.size(); ++i) + for (const auto &entry : this->GetModeList(mode)) { - Entry ban(mode, v[i]); + Entry ban(mode, entry); if (ban.Matches(u, full)) { this->RemoveMode(NULL, mode, ban.GetMask()); @@ -921,12 +922,12 @@ bool Channel::CheckKick(User *user) Log(LOG_DEBUG) << "Autokicking " << user->nick << " (" << mask << ") from " << this->name; this->SetMode(NULL, "BAN", mask); - this->Kick(NULL, user, "%s", reason.c_str()); + this->Kick(NULL, user, reason); return true; } -BotInfo* Channel::WhoSends() const +BotInfo *Channel::WhoSends() const { if (ci) return ci->WhoSends(); @@ -941,7 +942,7 @@ BotInfo* Channel::WhoSends() const return NULL; } -Channel* Channel::Find(const Anope::string &name) +Channel *Channel::Find(const Anope::string &name) { channel_map::const_iterator it = ChannelList.find(name); @@ -952,7 +953,7 @@ Channel* Channel::Find(const Anope::string &name) Channel *Channel::FindOrCreate(const Anope::string &name, bool &created, time_t ts) { - Channel* &chan = ChannelList[name]; + Channel *&chan = ChannelList[name]; created = chan == NULL; if (!chan) chan = new Channel(name, ts); @@ -967,10 +968,8 @@ void Channel::QueueForDeletion() void Channel::DeleteChannels() { - for (unsigned int i = 0; i < deleting.size(); ++i) + for (auto *c : deleting) { - Channel *c = deleting[i]; - if (c->CheckDelete()) delete c; } diff --git a/src/command.cpp b/src/command.cpp index eb5c89753..6df28aa9f 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -17,8 +17,19 @@ #include "regchannel.h" #include "channels.h" -CommandSource::CommandSource(const Anope::string &n, User *user, NickCore *core, CommandReply *r, BotInfo *bi) : nick(n), u(user), nc(core), reply(r), - c(NULL), service(bi) +void CommandReply::SendMessage(CommandSource &source, const Anope::string &msg) +{ + SendMessage(source.service, msg); +} + +CommandSource::CommandSource(const Anope::string &n, User *user, NickCore *core, CommandReply *r, BotInfo *bi, const Anope::string &m) + : nick(n) + , u(user) + , nc(core) + , ip(user ? user->ip.addr() : "") + , reply(r) + , service(bi) + , msgid(m) { } @@ -107,6 +118,21 @@ void CommandSource::Reply(const char *message, ...) va_end(args); } +void CommandSource::Reply(int count, const char *single, const char *plural, ...) +{ + va_list args; + char buf[4096]; // Messages can be really big. + + const char *translated_message = Language::Translate(this->nc, count, single, plural); + + va_start(args, plural); + vsnprintf(buf, sizeof(buf), translated_message, args); + + this->Reply(Anope::string(buf)); + + va_end(args); +} + void CommandSource::Reply(const Anope::string &message) { const char *translated_message = Language::Translate(this->nc, message.c_str()); @@ -114,7 +140,7 @@ void CommandSource::Reply(const Anope::string &message) sepstream sep(translated_message, '\n', true); Anope::string tok; while (sep.GetToken(tok)) - this->reply->SendMessage(this->service, tok); + this->reply->SendMessage(*this, 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(o) @@ -122,10 +148,6 @@ Command::Command(Module *o, const Anope::string &sname, size_t minparams, size_t allow_unregistered = require_user = false; } -Command::~Command() -{ -} - void Command::SetDesc(const Anope::string &d) { this->desc = d; @@ -192,14 +214,56 @@ void Command::OnSyntaxError(CommandSource &source, const Anope::string &subcomma this->SendSyntax(source); bool has_help = source.service->commands.find("HELP") != source.service->commands.end(); if (has_help) - source.Reply(MORE_INFO, Config->StrictPrivmsg.c_str(), source.service->nick.c_str(), source.command.c_str()); + source.Reply(MORE_INFO, source.service->GetQueryCommand().c_str(), source.command.c_str()); +} + +namespace +{ + void HandleUnknownCommand(CommandSource& source, const Anope::string &message) + { + // Try to find a similar command. + size_t distance = Config->GetBlock("options").Get<size_t>("didyoumeandifference", "4"); + Anope::string similar; + auto umessage = message.upper(); + for (const auto &[command, info] : source.service->commands) + { + if (info.hide || command == message) + continue; // Don't suggest a hidden alias or a missing command. + + size_t dist = Anope::Distance(umessage, command); + if (dist < distance) + { + distance = dist; + similar = command; + } + } + + bool has_help = source.service->commands.find("HELP") != source.service->commands.end(); + if (has_help && similar.empty()) + { + source.Reply(_("Unknown command \002%s\002. \"%s HELP\" for help."), message.c_str(), + source.service->GetQueryCommand().c_str()); + } + else if (has_help) + { + source.Reply(_("Unknown command \002%s\002. Did you mean \002%s\002? \"%s HELP\" for help."), + message.c_str(), similar.c_str(), source.service->GetQueryCommand().c_str()); + } + else if (similar.empty()) + { + source.Reply(_("Unknown command \002%s\002. Did you mean \002%s\002?"), message.c_str(), similar.c_str()); + } + else + { + source.Reply(_("Unknown command \002%s\002."), message.c_str()); + } + } } void Command::Run(CommandSource &source, const Anope::string &message) { std::vector<Anope::string> params; spacesepstream(message).GetTokens(params); - bool has_help = source.service->commands.find("HELP") != source.service->commands.end(); CommandInfo::map::const_iterator it = source.service->commands.end(); unsigned count = 0; @@ -216,10 +280,7 @@ void Command::Run(CommandSource &source, const Anope::string &message) if (it == source.service->commands.end()) { - if (has_help) - source.Reply(_("Unknown command \002%s\002. \"%s%s HELP\" for help."), message.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str()); - else - source.Reply(_("Unknown command \002%s\002."), message.c_str()); + HandleUnknownCommand(source, message); return; } @@ -227,10 +288,7 @@ void Command::Run(CommandSource &source, const Anope::string &message) ServiceReference<Command> c("Command", info.name); if (!c) { - if (has_help) - source.Reply(_("Unknown command \002%s\002. \"%s%s HELP\" for help."), message.c_str(), Config->StrictPrivmsg.c_str(), source.service->nick.c_str()); - else - source.Reply(_("Unknown command \002%s\002."), message.c_str()); + HandleUnknownCommand(source, message); Log(source.service) << "Command " << it->first << " exists on me, but its service " << info.name << " was not found!"; return; } @@ -288,19 +346,14 @@ void Command::Run(CommandSource &source, const Anope::string &cmdname, const Com FOREACH_MOD(OnPostCommand, (source, this, params)); } -bool Command::FindCommandFromService(const Anope::string &command_service, BotInfo* &bot, Anope::string &name) +bool Command::FindCommandFromService(const Anope::string &command_service, BotInfo *&bot, Anope::string &name) { bot = NULL; - for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it) + for (const auto &[_, bi] : *BotListByNick) { - BotInfo *bi = it->second; - - for (CommandInfo::map::const_iterator cit = bi->commands.begin(), cit_end = bi->commands.end(); cit != cit_end; ++cit) + for (const auto &[c_name, info] : bi->commands) { - const Anope::string &c_name = cit->first; - const CommandInfo &info = cit->second; - if (info.name != command_service) continue; diff --git a/src/config.cpp b/src/config.cpp index 73820b77d..94dd8d222 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -17,11 +17,15 @@ #include "channels.h" #include "hashcomp.h" +#include <stack> +#include <stdexcept> + using Configuration::File; using Configuration::Conf; using Configuration::Internal::Block; +using Configuration::Uplink; -File ServicesConf("services.conf", false); // Services configuration file name +File ServicesConf("anope.conf", false); // Configuration file name Conf *Config = NULL; Block Block::EmptyBlock(""); @@ -40,17 +44,17 @@ int Block::CountBlock(const Anope::string &bname) const return blocks.count(bname); } -const Block* Block::GetBlock(const Anope::string &bname, int num) const +const Block &Block::GetBlock(const Anope::string &bname, int num) const { std::pair<block_map::const_iterator, block_map::const_iterator> it = blocks.equal_range(bname); for (int i = 0; it.first != it.second; ++it.first, ++i) if (i == num) - return &it.first->second; - return &EmptyBlock; + return it.first->second; + return EmptyBlock; } -Block* Block::GetMutableBlock(const Anope::string &bname, int num) +Block *Block::GetMutableBlock(const Anope::string &bname, int num) { std::pair<block_map::iterator, block_map::iterator> it = blocks.equal_range(bname); @@ -66,12 +70,12 @@ bool Block::Set(const Anope::string &tag, const Anope::string &value) return true; } -const Block::item_map* Block::GetItems() const +const Block::item_map &Block::GetItems() const { - return &items; + return items; } -template<> const Anope::string Block::Get(const Anope::string &tag, const Anope::string& def) const +template<> const Anope::string Block::Get(const Anope::string &tag, const Anope::string &def) const { Anope::map<Anope::string>::const_iterator it = items.find(tag); if (it != items.end()) @@ -118,22 +122,22 @@ template<typename T> static void ValidateNotZero(const Anope::string &block, con Conf::Conf() : Block("") { ReadTimeout = 0; - UsePrivmsg = DefPrivmsg = false; + DefPrivmsg = false; this->LoadConf(ServicesConf); for (int i = 0; i < this->CountBlock("include"); ++i) { - const Block *include = this->GetBlock("include", i); + const Block &include = this->GetBlock("include", i); - const Anope::string &type = include->Get<const Anope::string>("type"), - &file = include->Get<const Anope::string>("name"); + const Anope::string &type = include.Get<const Anope::string>("type"), + &file = include.Get<const Anope::string>("name"); File f(file, type == "executable"); this->LoadConf(f); } - FOREACH_MOD(OnReload, (this)); + FOREACH_MOD(OnReload, (*this)); /* Check for modified values that aren't allowed to be modified */ if (Config) @@ -154,78 +158,92 @@ Conf::Conf() : Block("") {"networkinfo", "chanlen"}, }; - for (unsigned i = 0; i < sizeof(noreload) / sizeof(noreload[0]); ++i) - if (this->GetBlock(noreload[i].block)->Get<const Anope::string>(noreload[i].name) != Config->GetBlock(noreload[i].block)->Get<const Anope::string>(noreload[i].name)) - throw ConfigException("<" + noreload[i].block + ":" + noreload[i].name + "> can not be modified once set"); + for (const auto &tag : noreload) + { + if (this->GetBlock(tag.block).Get<const Anope::string>(tag.name) != Config->GetBlock(tag.block).Get<const Anope::string>(tag.name)) + throw ConfigException("<" + tag.block + ":" + tag.name + "> can not be modified once set"); + } } - const Block *serverinfo = this->GetBlock("serverinfo"), *options = this->GetBlock("options"), - *mail = this->GetBlock("mail"), *networkinfo = this->GetBlock("networkinfo"); + const Block &serverinfo = this->GetBlock("serverinfo"), &options = this->GetBlock("options"), + &mail = this->GetBlock("mail"), &networkinfo = this->GetBlock("networkinfo"); - const Anope::string &servername = serverinfo->Get<Anope::string>("name"); + const Anope::string &servername = serverinfo.Get<Anope::string>("name"); ValidateNotEmptyOrSpaces("serverinfo", "name", servername); if (servername.find(' ') != Anope::string::npos || servername.find('.') == Anope::string::npos) throw ConfigException("serverinfo:name is not a valid server name"); - ValidateNotEmpty("serverinfo", "description", serverinfo->Get<const Anope::string>("description")); - ValidateNotEmpty("serverinfo", "pid", serverinfo->Get<const Anope::string>("pid")); - ValidateNotEmpty("serverinfo", "motd", serverinfo->Get<const Anope::string>("motd")); + ValidateNotEmpty("serverinfo", "description", serverinfo.Get<const Anope::string>("description")); + ValidateNotEmpty("serverinfo", "pid", serverinfo.Get<const Anope::string>("pid")); + ValidateNotEmpty("serverinfo", "motd", serverinfo.Get<const Anope::string>("motd")); - ValidateNotZero("options", "readtimeout", options->Get<time_t>("readtimeout")); + ValidateNotZero("options", "readtimeout", options.Get<time_t>("readtimeout")); - ValidateNotZero("networkinfo", "nicklen", networkinfo->Get<unsigned>("nicklen")); - ValidateNotZero("networkinfo", "userlen", networkinfo->Get<unsigned>("userlen")); - ValidateNotZero("networkinfo", "hostlen", networkinfo->Get<unsigned>("hostlen")); - ValidateNotZero("networkinfo", "chanlen", networkinfo->Get<unsigned>("chanlen")); + ValidateNotZero("networkinfo", "nicklen", networkinfo.Get<unsigned>("nicklen", "1")); + ValidateNotZero("networkinfo", "userlen", networkinfo.Get<unsigned>("userlen", "1")); + ValidateNotZero("networkinfo", "hostlen", networkinfo.Get<unsigned>("hostlen", "1")); + ValidateNotZero("networkinfo", "chanlen", networkinfo.Get<unsigned>("chanlen", "1")); - spacesepstream(options->Get<const Anope::string>("ulineservers")).GetTokens(this->Ulines); + spacesepstream(options.Get<const Anope::string>("ulineservers")).GetTokens(this->Ulines); - if (mail->Get<bool>("usemail")) + if (mail.Get<bool>("usemail")) { - Anope::string check[] = { "sendmailpath", "sendfrom", "registration_subject", "registration_message", "emailchange_subject", "emailchange_message", "memo_subject", "memo_message" }; - for (unsigned i = 0; i < sizeof(check) / sizeof(Anope::string); ++i) - ValidateNotEmpty("mail", check[i], mail->Get<const Anope::string>(check[i])); + Anope::string check[] = { "sendfrom", "registration_subject", "registration_message", "emailchange_subject", "emailchange_message", "memo_subject", "memo_message" }; + for (const auto &field : check) + ValidateNotEmpty("mail", field, mail.Get<const Anope::string>(field)); } - this->ReadTimeout = options->Get<time_t>("readtimeout"); - this->UsePrivmsg = options->Get<bool>("useprivmsg"); - this->UseStrictPrivmsg = options->Get<bool>("usestrictprivmsg"); - this->StrictPrivmsg = !UseStrictPrivmsg ? "/msg " : "/"; + this->ReadTimeout = options.Get<time_t>("readtimeout"); + this->ServiceAlias = options.Get<bool>("servicealias"); { std::vector<Anope::string> defaults; - spacesepstream(this->GetModule("nickserv")->Get<const Anope::string>("defaults")).GetTokens(defaults); + spacesepstream(this->GetModule("nickserv").Get<const Anope::string>("defaults")).GetTokens(defaults); this->DefPrivmsg = std::find(defaults.begin(), defaults.end(), "msg") != defaults.end(); } - this->DefLanguage = options->Get<const Anope::string>("defaultlanguage"); - this->TimeoutCheck = options->Get<time_t>("timeoutcheck"); - this->NickChars = networkinfo->Get<Anope::string>("nick_chars"); + this->DefLanguage = options.Get<const Anope::string>("defaultlanguage"); + this->TimeoutCheck = options.Get<time_t>("timeoutcheck"); + this->NickChars = networkinfo.Get<Anope::string>("nick_chars"); for (int i = 0; i < this->CountBlock("uplink"); ++i) { - const Block *uplink = this->GetBlock("uplink", i); - - const Anope::string &host = uplink->Get<const Anope::string>("host"); - bool ipv6 = uplink->Get<bool>("ipv6"); - int port = uplink->Get<int>("port"); - const Anope::string &password = uplink->Get<const Anope::string>("password"); + const Block &uplink = this->GetBlock("uplink", i); + + int protocol; + const Anope::string &protocolstr = uplink.Get<const Anope::string>("protocol", "ipv4"); + if (protocolstr == "ipv4") + protocol = AF_INET; + else if (protocolstr == "ipv6") + protocol = AF_INET6; + else if (protocolstr == "unix") + protocol = AF_UNIX; + else + throw ConfigException("uplink:protocol must be set to ipv4, ipv6, or unix"); + const Anope::string &host = uplink.Get<const Anope::string>("host"); ValidateNotEmptyOrSpaces("uplink", "host", host); - ValidateNotZero("uplink", "port", port); - ValidateNotEmptyOrSpaces("uplink", "password", password); - if (password.find(' ') != Anope::string::npos || password[0] == ':') + int port = 0; + if (protocol != AF_UNIX) + { + port = uplink.Get<int>("port"); + ValidateNotZero("uplink", "port", port); + } + + const Anope::string &password = uplink.Get<const Anope::string>("password"); + ValidateNotEmptyOrSpaces("uplink", "password", password); + if (password[0] == ':') throw ConfigException("uplink:password is not valid"); - this->Uplinks.push_back(Uplink(host, port, password, ipv6)); + this->Uplinks.emplace_back(host, port, password, protocol); } for (int i = 0; i < this->CountBlock("module"); ++i) { - const Block *module = this->GetBlock("module", i); + const Block &module = this->GetBlock("module", i); - const Anope::string &modname = module->Get<const Anope::string>("name"); + const Anope::string &modname = module.Get<const Anope::string>("name"); ValidateNotEmptyOrSpaces("module", "name", modname); @@ -234,17 +252,17 @@ Conf::Conf() : Block("") for (int i = 0; i < this->CountBlock("opertype"); ++i) { - const Block *opertype = this->GetBlock("opertype", i); + const Block &opertype = this->GetBlock("opertype", i); - const Anope::string &oname = opertype->Get<const Anope::string>("name"), - &modes = opertype->Get<const Anope::string>("modes"), - &inherits = opertype->Get<const Anope::string>("inherits"), - &commands = opertype->Get<const Anope::string>("commands"), - &privs = opertype->Get<const Anope::string>("privs"); + const Anope::string &oname = opertype.Get<const Anope::string>("name"), + &modes = opertype.Get<const Anope::string>("modes"), + &inherits = opertype.Get<const Anope::string>("inherits"), + &commands = opertype.Get<const Anope::string>("commands"), + &privs = opertype.Get<const Anope::string>("privs"); ValidateNotEmpty("opertype", "name", oname); - OperType *ot = new OperType(oname); + auto *ot = new OperType(oname); ot->modes = modes; spacesepstream cmdstr(commands); @@ -261,10 +279,8 @@ Conf::Conf() : Block("") /* Strip leading ' ' after , */ if (str.length() > 1 && str[0] == ' ') str.erase(str.begin()); - for (unsigned j = 0; j < this->MyOperTypes.size(); ++j) + for (auto *ot2 : this->MyOperTypes) { - OperType *ot2 = this->MyOperTypes[j]; - if (ot2->GetName().equals_ci(str)) { Log() << "Inheriting commands and privs from " << ot2->GetName() << " to " << ot->GetName(); @@ -279,48 +295,51 @@ Conf::Conf() : Block("") for (int i = 0; i < this->CountBlock("oper"); ++i) { - const Block *oper = this->GetBlock("oper", i); + const Block &oper = this->GetBlock("oper", i); - const Anope::string &nname = oper->Get<const Anope::string>("name"), - &type = oper->Get<const Anope::string>("type"), - &password = oper->Get<const Anope::string>("password"), - &certfp = oper->Get<const Anope::string>("certfp"), - &host = oper->Get<const Anope::string>("host"), - &vhost = oper->Get<const Anope::string>("vhost"); - bool require_oper = oper->Get<bool>("require_oper"); + const Anope::string &nname = oper.Get<const Anope::string>("name"), + &type = oper.Get<const Anope::string>("type"), + &password = oper.Get<const Anope::string>("password"), + &certfp = oper.Get<const Anope::string>("certfp"), + &host = oper.Get<const Anope::string>("host"), + &vhost = oper.Get<const Anope::string>("vhost"); + bool require_oper = oper.Get<bool>("require_oper"); ValidateNotEmptyOrSpaces("oper", "name", nname); ValidateNotEmpty("oper", "type", type); OperType *ot = NULL; - for (unsigned j = 0; j < this->MyOperTypes.size(); ++j) - if (this->MyOperTypes[j]->GetName() == type) - ot = this->MyOperTypes[j]; + for (auto *opertype : this->MyOperTypes) + { + if (opertype->GetName() == type) + ot = opertype; + } if (ot == NULL) throw ConfigException("Oper block for " + nname + " has invalid oper type " + type); - Oper *o = new Oper(nname, ot); + auto *o = new Oper(nname, ot); o->require_oper = require_oper; o->password = password; - o->certfp = certfp; + spacesepstream(certfp).GetTokens(o->certfp); spacesepstream(host).GetTokens(o->hosts); o->vhost = vhost; this->Opers.push_back(o); } - for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it) - it->second->conf = false; + for (const auto &[_, bi] : *BotListByNick) + bi->conf = false; for (int i = 0; i < this->CountBlock("service"); ++i) { - const Block *service = this->GetBlock("service", i); + const Block &service = this->GetBlock("service", i); - const Anope::string &nick = service->Get<const Anope::string>("nick"), - &user = service->Get<const Anope::string>("user"), - &host = service->Get<const Anope::string>("host"), - &gecos = service->Get<const Anope::string>("gecos"), - &modes = service->Get<const Anope::string>("modes"), - &channels = service->Get<const Anope::string>("channels"); + const Anope::string &nick = service.Get<const Anope::string>("nick"), + &user = service.Get<const Anope::string>("user"), + &host = service.Get<const Anope::string>("host"), + &gecos = service.Get<const Anope::string>("gecos"), + &modes = service.Get<const Anope::string>("modes"), + &channels = service.Get<const Anope::string>("channels"), + &alias = service.Get<const Anope::string>("alias", nick.upper()); ValidateNotEmptyOrSpaces("service", "nick", nick); ValidateNotEmptyOrSpaces("service", "user", user); @@ -331,6 +350,8 @@ Conf::Conf() : Block("") BotInfo *bi = BotInfo::Find(nick, true); if (!bi) bi = new BotInfo(nick, user, host, gecos, modes); + + bi->alias = alias; bi->conf = true; std::vector<Anope::string> oldchannels = bi->botchannels; @@ -358,28 +379,29 @@ Conf::Conf() : Block("") /* Remove all existing modes */ ChanUserContainer *cu = c->FindUser(bi); if (cu != NULL) - for (size_t j = 0; j < cu->status.Modes().length(); ++j) - c->RemoveMode(bi, ModeManager::FindChannelModeByChar(cu->status.Modes()[j]), bi->GetUID()); + { + for (auto mode : cu->status.Modes()) + c->RemoveMode(bi, ModeManager::FindChannelModeByChar(mode), bi->GetUID()); + } /* Set the new modes */ - for (unsigned j = 0; j < want_modes.length(); ++j) + for (char want_mode : want_modes) { - ChannelMode *cm = ModeManager::FindChannelModeByChar(want_modes[j]); + ChannelMode *cm = ModeManager::FindChannelModeByChar(want_mode); if (cm == NULL) - cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_modes[j])); + cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_mode)); if (cm && cm->type == MODE_STATUS) c->SetMode(bi, cm, bi->GetUID()); } } - for (unsigned k = 0; k < oldchannels.size(); ++k) + for (const auto &oldchannel : oldchannels) { - size_t ch = oldchannels[k].find('#'); - Anope::string chname = oldchannels[k].substr(ch != Anope::string::npos ? ch : 0); - + size_t ch = oldchannel.find('#'); + Anope::string chname = oldchannel.substr(ch != Anope::string::npos ? ch : 0); bool found = false; - for (unsigned j = 0; j < bi->botchannels.size(); ++j) + for (const auto &botchannel : bi->botchannels) { - ch = bi->botchannels[j].find('#'); - Anope::string ochname = bi->botchannels[j].substr(ch != Anope::string::npos ? ch : 0); + ch = botchannel.find('#'); + Anope::string ochname = botchannel.substr(ch != Anope::string::npos ? ch : 0); if (chname.equals_ci(ochname)) found = true; @@ -399,40 +421,40 @@ Conf::Conf() : Block("") for (int i = 0; i < this->CountBlock("log"); ++i) { - const Block *log = this->GetBlock("log", i); + const Block &log = this->GetBlock("log", i); - int logage = log->Get<int>("logage"); - bool rawio = log->Get<bool>("rawio"); - bool debug = log->Get<bool>("debug"); + int logage = log.Get<int>("logage"); + bool rawio = log.Get<bool>("rawio"); + bool debug = log.Get<bool>("debug"); LogInfo l(logage, rawio, debug); - l.bot = BotInfo::Find(log->Get<const Anope::string>("bot", "Global"), true); - spacesepstream(log->Get<const Anope::string>("target")).GetTokens(l.targets); - spacesepstream(log->Get<const Anope::string>("source")).GetTokens(l.sources); - spacesepstream(log->Get<const Anope::string>("admin")).GetTokens(l.admin); - spacesepstream(log->Get<const Anope::string>("override")).GetTokens(l.override); - spacesepstream(log->Get<const Anope::string>("commands")).GetTokens(l.commands); - spacesepstream(log->Get<const Anope::string>("servers")).GetTokens(l.servers); - spacesepstream(log->Get<const Anope::string>("channels")).GetTokens(l.channels); - spacesepstream(log->Get<const Anope::string>("users")).GetTokens(l.users); - spacesepstream(log->Get<const Anope::string>("other")).GetTokens(l.normal); + l.bot = BotInfo::Find(log.Get<const Anope::string>("bot", "Global"), true); + spacesepstream(log.Get<const Anope::string>("target")).GetTokens(l.targets); + spacesepstream(log.Get<const Anope::string>("source")).GetTokens(l.sources); + spacesepstream(log.Get<const Anope::string>("admin")).GetTokens(l.admin); + spacesepstream(log.Get<const Anope::string>("override")).GetTokens(l.override); + spacesepstream(log.Get<const Anope::string>("commands")).GetTokens(l.commands); + spacesepstream(log.Get<const Anope::string>("servers")).GetTokens(l.servers); + spacesepstream(log.Get<const Anope::string>("channels")).GetTokens(l.channels); + spacesepstream(log.Get<const Anope::string>("users")).GetTokens(l.users); + spacesepstream(log.Get<const Anope::string>("other")).GetTokens(l.normal); this->LogInfos.push_back(l); } - for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it) - it->second->commands.clear(); + for (const auto &[_, bi] : *BotListByNick) + bi->commands.clear(); for (int i = 0; i < this->CountBlock("command"); ++i) { - const Block *command = this->GetBlock("command", i); + const Block &command = this->GetBlock("command", i); - const Anope::string &service = command->Get<const Anope::string>("service"), - &nname = command->Get<const Anope::string>("name"), - &cmd = command->Get<const Anope::string>("command"), - &permission = command->Get<const Anope::string>("permission"), - &group = command->Get<const Anope::string>("group"); - bool hide = command->Get<bool>("hide"); + const Anope::string &service = command.Get<const Anope::string>("service"), + &nname = command.Get<const Anope::string>("name"), + &cmd = command.Get<const Anope::string>("command"), + &permission = command.Get<const Anope::string>("permission"), + &group = command.Get<const Anope::string>("group"); + bool hide = command.Get<bool>("hide"); ValidateNotEmptyOrSpaces("command", "service", service); ValidateNotEmpty("command", "name", nname); @@ -450,25 +472,25 @@ Conf::Conf() : Block("") PrivilegeManager::ClearPrivileges(); for (int i = 0; i < this->CountBlock("privilege"); ++i) { - const Block *privilege = this->GetBlock("privilege", i); + const Block &privilege = this->GetBlock("privilege", i); - const Anope::string &nname = privilege->Get<const Anope::string>("name"), - &desc = privilege->Get<const Anope::string>("desc"); - int rank = privilege->Get<int>("rank"); + const Anope::string &nname = privilege.Get<const Anope::string>("name"), + &desc = privilege.Get<const Anope::string>("desc"); + int rank = privilege.Get<int>("rank"); PrivilegeManager::AddPrivilege(Privilege(nname, desc, rank)); } for (int i = 0; i < this->CountBlock("fantasy"); ++i) { - const Block *fantasy = this->GetBlock("fantasy", i); + const Block &fantasy = this->GetBlock("fantasy", i); - const Anope::string &nname = fantasy->Get<const Anope::string>("name"), - &service = fantasy->Get<const Anope::string>("command"), - &permission = fantasy->Get<const Anope::string>("permission"), - &group = fantasy->Get<const Anope::string>("group"); - bool hide = fantasy->Get<bool>("hide"), - prepend_channel = fantasy->Get<bool>("prepend_channel", "yes"); + const Anope::string &nname = fantasy.Get<const Anope::string>("name"), + &service = fantasy.Get<const Anope::string>("command"), + &permission = fantasy.Get<const Anope::string>("permission"), + &group = fantasy.Get<const Anope::string>("group"); + bool hide = fantasy.Get<bool>("hide"), + prepend_channel = fantasy.Get<bool>("prepend_channel", "yes"); ValidateNotEmpty("fantasy", "name", nname); ValidateNotEmptyOrSpaces("fantasy", "command", service); @@ -483,10 +505,10 @@ Conf::Conf() : Block("") for (int i = 0; i < this->CountBlock("command_group"); ++i) { - const Block *command_group = this->GetBlock("command_group", i); + const Block &command_group = this->GetBlock("command_group", i); - const Anope::string &nname = command_group->Get<const Anope::string>("name"), - &description = command_group->Get<const Anope::string>("description"); + const Anope::string &nname = command_group.Get<const Anope::string>("name"), + &description = command_group.Get<const Anope::string>("description"); CommandGroup gr; gr.name = nname; @@ -499,17 +521,14 @@ Conf::Conf() : Block("") if (Config) /* Clear existing conf opers */ - for (nickcore_map::const_iterator it = NickCoreList->begin(), it_end = NickCoreList->end(); it != it_end; ++it) + for (const auto &[_, nc] : *NickCoreList) { - NickCore *nc = it->second; if (nc->o && std::find(Config->Opers.begin(), Config->Opers.end(), nc->o) != Config->Opers.end()) nc->o = NULL; } /* Apply new opers */ - for (unsigned i = 0; i < this->Opers.size(); ++i) + for (auto *o : this->Opers) { - Oper *o = this->Opers[i]; - NickAlias *na = NickAlias::Find(o->name); if (!na) continue; @@ -525,45 +544,47 @@ Conf::Conf() : Block("") Log() << "Tied oper " << na->nc->display << " to type " << o->ot->GetName(); } - if (options->Get<const Anope::string>("casemap", "ascii") == "ascii") + if (options.Get<const Anope::string>("casemap", "ascii") == "ascii") Anope::casemap = std::locale(std::locale(), new Anope::ascii_ctype<char>()); - else if (options->Get<const Anope::string>("casemap") == "rfc1459") + else if (options.Get<const Anope::string>("casemap") == "rfc1459") Anope::casemap = std::locale(std::locale(), new Anope::rfc1459_ctype<char>()); else { try { - Anope::casemap = std::locale(options->Get<const Anope::string>("casemap").c_str()); + Anope::casemap = std::locale(options.Get<const Anope::string>("casemap").c_str()); } catch (const std::runtime_error &) { - Log() << "Unknown casemap " << options->Get<const Anope::string>("casemap") << " - casemap not changed"; + Log() << "Unknown casemap " << options.Get<const Anope::string>("casemap") << " - casemap not changed"; } } Anope::CaseMapRebuild(); - - /* Check the user keys */ - if (!options->Get<unsigned>("seed")) - Log() << "Configuration option options:seed should be set. It's for YOUR safety! Remember that!"; } Conf::~Conf() { - for (unsigned i = 0; i < MyOperTypes.size(); ++i) - delete MyOperTypes[i]; - for (unsigned i = 0; i < Opers.size(); ++i) - delete Opers[i]; + for (const auto *opertype : MyOperTypes) + delete opertype; + + for (const auto *oper : Opers) + delete oper; } void Conf::Post(Conf *old) { /* Apply module changes */ - for (unsigned i = 0; i < old->ModulesAutoLoad.size(); ++i) - if (std::find(this->ModulesAutoLoad.begin(), this->ModulesAutoLoad.end(), old->ModulesAutoLoad[i]) == this->ModulesAutoLoad.end()) - ModuleManager::UnloadModule(ModuleManager::FindModule(old->ModulesAutoLoad[i]), NULL); - for (unsigned i = 0; i < this->ModulesAutoLoad.size(); ++i) - if (std::find(old->ModulesAutoLoad.begin(), old->ModulesAutoLoad.end(), this->ModulesAutoLoad[i]) == old->ModulesAutoLoad.end()) - ModuleManager::LoadModule(this->ModulesAutoLoad[i], NULL); + for (const auto &mod : old->ModulesAutoLoad) + { + if (std::find(this->ModulesAutoLoad.begin(), this->ModulesAutoLoad.end(), mod) == this->ModulesAutoLoad.end()) + ModuleManager::UnloadModule(ModuleManager::FindModule(mod), NULL); + } + + for (const auto &mod : this->ModulesAutoLoad) + { + if (std::find(old->ModulesAutoLoad.begin(), old->ModulesAutoLoad.end(), mod) == old->ModulesAutoLoad.end()) + ModuleManager::LoadModule(mod, NULL); + } /* Apply opertype changes, as non-conf opers still point to the old oper types */ for (unsigned i = Oper::opers.size(); i > 0; --i) @@ -576,9 +597,11 @@ void Conf::Post(Conf *old) OperType *ot = o->ot; o->ot = NULL; - for (unsigned j = 0; j < MyOperTypes.size(); ++j) - if (ot->GetName() == MyOperTypes[j]->GetName()) - o->ot = MyOperTypes[j]; + for (auto *opertype : MyOperTypes) + { + if (ot->GetName() == opertype->GetName()) + o->ot = opertype; + } if (o->ot == NULL) { @@ -597,30 +620,47 @@ void Conf::Post(Conf *old) } } -Block *Conf::GetModule(Module *m) +Anope::string Uplink::str() const +{ + switch (protocol) + { + case AF_INET: + return Anope::printf("%s:%u", this->host.c_str(), this->port); + case AF_INET6: + return Anope::printf("[%s]:%u", this->host.c_str(), this->port); + case AF_UNIX: + return this->host; + } + + // Should never be reached. + return ""; +} + + +Block &Conf::GetModule(const Module *m) { if (!m) - return NULL; + return Block::EmptyBlock; return GetModule(m->name); } -Block *Conf::GetModule(const Anope::string &mname) +Block &Conf::GetModule(const Anope::string &mname) { std::map<Anope::string, Block *>::iterator it = modules.find(mname); if (it != modules.end()) - return it->second; + return *it->second; - Block* &block = modules[mname]; + Block *&block = modules[mname]; /* Search for the block */ for (std::pair<block_map::iterator, block_map::iterator> iters = blocks.equal_range("module"); iters.first != iters.second; ++iters.first) { - Block *b = &iters.first->second; + Block &b = iters.first->second; - if (b->Get<const Anope::string>("name") == mname) + if (b.Get<const Anope::string>("name") == mname) { - block = b; + block = &b; break; } } @@ -637,28 +677,28 @@ BotInfo *Conf::GetClient(const Anope::string &cname) if (it != bots.end()) return BotInfo::Find(!it->second.empty() ? it->second : cname, true); - Block *block = GetModule(cname.lower()); - const Anope::string &client = block->Get<const Anope::string>("client"); + Block &block = GetModule(cname.lower()); + const Anope::string &client = block.Get<const Anope::string>("client"); bots[cname] = client; return GetClient(cname); } -const Block *Conf::GetCommand(CommandSource &source) +const Block &Conf::GetCommand(CommandSource &source) { const Anope::string &block_name = source.c ? "fantasy" : "command"; for (std::pair<block_map::iterator, block_map::iterator> iters = blocks.equal_range(block_name); iters.first != iters.second; ++iters.first) { - Block *b = &iters.first->second; + Block &b = iters.first->second; - if (b->Get<Anope::string>("name") == source.command) + if (b.Get<Anope::string>("name") == source.command) return b; } - return &Block::EmptyBlock; + return Block::EmptyBlock; } -File::File(const Anope::string &n, bool e) : name(n), executable(e), fp(NULL) +File::File(const Anope::string &n, bool e) : name(n), executable(e) { } @@ -674,7 +714,7 @@ const Anope::string &File::GetName() const Anope::string File::GetPath() const { - return (this->executable ? "" : Anope::ConfigDir + "/") + this->name; + return this->executable ? this->name : Anope::ExpandConfig(this->name); } bool File::IsOpen() const @@ -685,7 +725,7 @@ bool File::IsOpen() const bool File::Open() { this->Close(); - this->fp = (this->executable ? popen(this->name.c_str(), "r") : fopen((Anope::ConfigDir + "/" + this->name).c_str(), "r")); + this->fp = (this->executable ? popen(GetPath().c_str(), "r") : fopen(GetPath().c_str(), "r")); return this->fp != NULL; } @@ -801,12 +841,12 @@ void Conf::LoadConf(File &file) if (block_stack.empty() || itemname.empty()) { file.Close(); - throw ConfigException("Unexpected quoted string: " + file.GetName() + ":" + stringify(linenumber)); + throw ConfigException("Unexpected quoted string: " + file.GetName() + ":" + Anope::ToString(linenumber)); } if (in_word || !wordbuffer.empty()) { file.Close(); - throw ConfigException("Unexpected quoted string (prior unhandled words): " + file.GetName() + ":" + stringify(linenumber)); + throw ConfigException("Unexpected quoted string (prior unhandled words): " + file.GetName() + ":" + Anope::ToString(linenumber)); } in_quote = in_word = true; } @@ -815,13 +855,13 @@ void Conf::LoadConf(File &file) if (block_stack.empty()) { file.Close(); - throw ConfigException("Config item outside of section (or stray '='): " + file.GetName() + ":" + stringify(linenumber)); + throw ConfigException("Config item outside of section (or stray '='): " + file.GetName() + ":" + Anope::ToString(linenumber)); } if (!itemname.empty() || wordbuffer.empty()) { file.Close(); - throw ConfigException("Stray '=' sign or item without value: " + file.GetName() + ":" + stringify(linenumber)); + throw ConfigException("Stray '=' sign or item without value: " + file.GetName() + ":" + Anope::ToString(linenumber)); } in_word = false; @@ -847,7 +887,7 @@ void Conf::LoadConf(File &file) } Block *b = block_stack.empty() ? this : block_stack.top(); - block_map::iterator it = b->blocks.insert(std::make_pair(wordbuffer, Configuration::Block(wordbuffer))); + block_map::iterator it = b->blocks.emplace(wordbuffer, Configuration::Block(wordbuffer)); b = &it->second; b->linenum = linenumber; block_stack.push(b); @@ -868,7 +908,7 @@ void Conf::LoadConf(File &file) if (!in_word && !wordbuffer.empty()) { file.Close(); - throw ConfigException("Unexpected word: " + file.GetName() + ":" + stringify(linenumber)); + throw ConfigException("Unexpected word: " + file.GetName() + ":" + Anope::ToString(linenumber)); } wordbuffer += ch; in_word = true; @@ -895,28 +935,16 @@ void Conf::LoadConf(File &file) if (block_stack.empty()) { file.Close(); - throw ConfigException("Stray ';' outside of block: " + file.GetName() + ":" + stringify(linenumber)); + throw ConfigException("Stray ';' outside of block: " + file.GetName() + ":" + Anope::ToString(linenumber)); } Block *b = block_stack.top(); - if (b) - Log(LOG_DEBUG) << "ln " << linenumber << " EOL: s='" << b->name << "' '" << itemname << "' set to '" << wordbuffer << "'"; - - /* Check defines */ - for (int i = 0; i < this->CountBlock("define"); ++i) { - const Block *define = this->GetBlock("define", i); - - const Anope::string &dname = define->Get<const Anope::string>("name"); - - if (dname == wordbuffer && define != b) - wordbuffer = define->Get<const Anope::string>("value"); + Log(LOG_DEBUG) << "ln " << linenumber << " EOL: s='" << b->name << "' '" << itemname << "' set to '" << wordbuffer << "'"; + b->items[itemname] = ReplaceVars(wordbuffer, file, linenumber); } - if (b) - b->items[itemname] = wordbuffer; - wordbuffer.clear(); itemname.clear(); } @@ -926,7 +954,7 @@ void Conf::LoadConf(File &file) if (block_stack.empty()) { file.Close(); - throw ConfigException("Stray '}': " + file.GetName() + ":" + stringify(linenumber)); + throw ConfigException("Stray '}': " + file.GetName() + ":" + Anope::ToString(linenumber)); } block_stack.pop(); @@ -946,8 +974,57 @@ void Conf::LoadConf(File &file) if (!block_stack.empty()) { if (block_stack.top()) - throw ConfigException("Unterminated block at end of file: " + file.GetName() + ". Block was opened on line " + stringify(block_stack.top()->linenum)); + throw ConfigException("Unterminated block at end of file: " + file.GetName() + ". Block was opened on line " + Anope::ToString(block_stack.top()->linenum)); else throw ConfigException("Unterminated commented block at end of file: " + file.GetName()); } } + +Anope::string Conf::ReplaceVars(const Anope::string &str, const File &file, int linenumber) +{ + Anope::string ret; + for (auto it = str.begin(); it != str.end(); ) + { + if (*it != '$') + { + ret.push_back(*it++); + continue; + } + + if (++it == str.end() || *it != '{') + continue; + + it++; + Anope::string var; + while (it != str.end() && *it != '}') + var.push_back(*it++); + + if (it == str.end()) + throw ConfigException("Unterminated variable: " + file.GetName() + ":" + Anope::ToString(linenumber)); + + it++; + if (var.compare(0, 4, "env.", 4) == 0) + { + // This is an environment variable rather than a defined variable + const char* envstr = getenv(var.c_str() + 4); + if (envstr && envstr) + ret.append(envstr); + continue; + } + + for (int i = 0; i < this->CountBlock("define"); ++i) + { + const auto &define = this->GetBlock("define", i); + const auto defname = define.Get<const Anope::string>("name"); + if (defname == var) + { + ret.append(define.Get<const Anope::string>("value")); + break; + } + } + } + + if (!str.equals_cs(ret)) + Log(LOG_DEBUG) << "Expanded \"" << str << "\" to \"" << ret << "\""; + return ret; +} diff --git a/src/extensible.cpp b/src/extensible.cpp index 844130f30..f2d7ff872 100644 --- a/src/extensible.cpp +++ b/src/extensible.cpp @@ -52,15 +52,14 @@ void Extensible::ExtensibleSerialize(const Extensible *e, const Serializable *s, void Extensible::ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) { - for (std::set<ExtensibleBase *>::iterator it = extensible_items.begin(); it != extensible_items.end(); ++it) + for (auto *extensible_item : extensible_items) { - ExtensibleBase *eb = *it; - eb->ExtensibleUnserialize(e, s, data); + extensible_item->ExtensibleUnserialize(e, s, data); } } template<> -bool* Extensible::Extend(const Anope::string &name, const bool &what) +bool *Extensible::Extend(const Anope::string &name, const bool &what) { ExtensibleRef<bool> ref(name); if (ref) diff --git a/src/hashcomp.cpp b/src/hashcomp.cpp index 3abaa388e..da12be638 100644 --- a/src/hashcomp.cpp +++ b/src/hashcomp.cpp @@ -91,7 +91,7 @@ bool ci::less::operator()(const Anope::string &s1, const Anope::string &s2) cons return s1.ci_str().compare(s2.ci_str()) < 0; } -sepstream::sepstream(const Anope::string &source, char separator, bool ae) : tokens(source), sep(separator), pos(0), allow_empty(ae) +sepstream::sepstream(const Anope::string &source, char separator, bool ae) : tokens(source), sep(separator), allow_empty(ae) { } @@ -106,7 +106,7 @@ bool sepstream::GetToken(Anope::string &token) if (!this->allow_empty) { this->pos = this->tokens.find_first_not_of(this->sep, this->pos); - if (this->pos == std::string::npos) + if (this->pos == Anope::string::npos) { this->pos = this->tokens.length() + 1; token.clear(); @@ -115,7 +115,7 @@ bool sepstream::GetToken(Anope::string &token) } size_t p = this->tokens.find(this->sep, this->pos); - if (p == std::string::npos) + if (p == Anope::string::npos) p = this->tokens.length(); token = this->tokens.substr(this->pos, p - this->pos); @@ -152,7 +152,7 @@ bool sepstream::GetTokenRemainder(Anope::string &token, int num) return false; } -const Anope::string sepstream::GetRemaining() +Anope::string sepstream::GetRemaining() { return !this->StreamEnd() ? this->tokens.substr(this->pos) : ""; } diff --git a/src/init.cpp b/src/init.cpp index df229ce72..757346e0d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -23,13 +23,18 @@ #include <sys/wait.h> #include <sys/stat.h> -#include <errno.h> -#include <sys/types.h> -#include <pwd.h> +#include <cerrno> #include <grp.h> +#include <pwd.h> +#include <sys/types.h> #endif +#include <thread> -Anope::string Anope::ConfigDir = "conf", Anope::DataDir = "data", Anope::ModuleDir = "lib", Anope::LocaleDir = "locale", Anope::LogDir = "logs"; +Anope::string Anope::ConfigDir = DEFAULT_CONF_DIR; +Anope::string Anope::DataDir = DEFAULT_DATA_DIR; +Anope::string Anope::LocaleDir = DEFAULT_LOCALE_DIR; +Anope::string Anope::LogDir = DEFAULT_LOG_DIR; +Anope::string Anope::ModuleDir = DEFAULT_MODULE_DIR; /* Vector of pairs of command line arguments and their params */ static std::vector<std::pair<Anope::string, Anope::string> > CommandLineArguments; @@ -57,7 +62,7 @@ static void ParseCommandLineArguments(int ac, char **av) if (option.empty()) continue; - CommandLineArguments.push_back(std::make_pair(option, param)); + CommandLineArguments.emplace_back(option, param); } } @@ -71,11 +76,11 @@ static bool GetCommandLineArgument(const Anope::string &name, char shortname, An { param.clear(); - for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = CommandLineArguments.begin(), it_end = CommandLineArguments.end(); it != it_end; ++it) + for (const auto &[argument, value] : CommandLineArguments) { - if (it->first.equals_ci(name) || (it->first.length() == 1 && it->first[0] == shortname)) + if (argument.equals_ci(name) || (argument.length() == 1 && argument[0] == shortname)) { - param = it->second; + param = value; return true; } } @@ -131,7 +136,7 @@ void Anope::HandleSignal() try { - Configuration::Conf *new_config = new Configuration::Conf(); + auto *new_config = new Configuration::Conf(); Configuration::Conf *old = Config; Config = new_config; Config->Post(old); @@ -147,14 +152,19 @@ void Anope::HandleSignal() case SIGINT: #ifndef _WIN32 Log() << "Received " << strsignal(Signal) << " signal (" << Signal << "), exiting."; - Anope::QuitReason = Anope::string("Services terminating via signal ") + strsignal(Signal) + " (" + stringify(Signal) + ")"; + Anope::QuitReason = Anope::string("Services terminating via signal ") + strsignal(Signal) + " (" + Anope::ToString(Signal) + ")"; #else Log() << "Received signal " << Signal << ", exiting."; - Anope::QuitReason = Anope::string("Services terminating via signal ") + stringify(Signal); + Anope::QuitReason = Anope::string("Services terminating via signal ") + Anope::ToString(Signal); #endif Anope::Quitting = true; Anope::SaveDatabases(); break; +#ifndef _WIN32 + case SIGUSR1: + Anope::SaveDatabases(); + break; +#endif } Signal = 0; @@ -193,8 +203,10 @@ static void InitSignals() sa.sa_handler = SignalHandler; +#ifndef _WIN32 + sigaction(SIGUSR1, &sa, NULL); +#endif sigaction(SIGHUP, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); @@ -210,62 +222,60 @@ static void InitSignals() static void remove_pidfile() { - remove(Config->GetBlock("serverinfo")->Get<const Anope::string>("pid").c_str()); + auto pidfile = Anope::ExpandData(Config->GetBlock("serverinfo").Get<const Anope::string>("pid")); + if (!pidfile.empty()) + remove(pidfile.c_str()); } /* Create our PID file and write the PID to it. */ static void write_pidfile() { - FILE *pidfile = fopen(Config->GetBlock("serverinfo")->Get<const Anope::string>("pid").c_str(), "w"); - if (pidfile) - { + auto pidfile = Anope::ExpandData(Config->GetBlock("serverinfo").Get<const Anope::string>("pid")); + if (Anope::NoPID || pidfile.empty()) + return; + + std::ofstream stream(pidfile.str()); + if (!stream.is_open()) + throw CoreException("Can not write to PID file " + pidfile); #ifdef _WIN32 - fprintf(pidfile, "%d\n", static_cast<int>(GetCurrentProcessId())); + stream << GetCurrentProcessId() << std::endl; #else - fprintf(pidfile, "%d\n", static_cast<int>(getpid())); + stream << getpid() << std::endl; #endif - fclose(pidfile); - atexit(remove_pidfile); - } - else - throw CoreException("Can not write to PID file " + Config->GetBlock("serverinfo")->Get<const Anope::string>("pid")); + atexit(remove_pidfile); } static void setuidgid() { #ifndef _WIN32 - Configuration::Block *options = Config->GetBlock("options"); + Configuration::Block &options = Config->GetBlock("options"); uid_t uid = -1; gid_t gid = -1; - if (!options->Get<const Anope::string>("user").empty()) + if (!options.Get<const Anope::string>("user").empty()) { errno = 0; - struct passwd *u = getpwnam(options->Get<const Anope::string>("user").c_str()); + struct passwd *u = getpwnam(options.Get<const Anope::string>("user").c_str()); if (u == NULL) - Log() << "Unable to setuid to " << options->Get<const Anope::string>("user") << ": " << Anope::LastError(); + Log() << "Unable to setuid to " << options.Get<const Anope::string>("user") << ": " << Anope::LastError(); else uid = u->pw_uid; } - if (!options->Get<const Anope::string>("group").empty()) + if (!options.Get<const Anope::string>("group").empty()) { errno = 0; - struct group *g = getgrnam(options->Get<const Anope::string>("group").c_str()); + struct group *g = getgrnam(options.Get<const Anope::string>("group").c_str()); if (g == NULL) - Log() << "Unable to setgid to " << options->Get<const Anope::string>("group") << ": " << Anope::LastError(); + Log() << "Unable to setgid to " << options.Get<const Anope::string>("group") << ": " << Anope::LastError(); else gid = g->gr_gid; } - for (unsigned i = 0; i < Config->LogInfos.size(); ++i) + for (const auto &li : Config->LogInfos) { - LogInfo& li = Config->LogInfos[i]; - - for (unsigned j = 0; j < li.logfiles.size(); ++j) + for (const auto *lf : li.logfiles) { - LogFile* lf = li.logfiles[j]; - errno = 0; if (chown(lf->filename.c_str(), uid, gid) != 0) Log() << "Unable to change the ownership of " << lf->filename << " to " << uid << "/" << gid << ": " << Anope::LastError(); @@ -275,27 +285,30 @@ static void setuidgid() if (static_cast<int>(gid) != -1) { if (setgid(gid) == -1) - Log() << "Unable to setgid to " << options->Get<const Anope::string>("group") << ": " << Anope::LastError(); + Log() << "Unable to setgid to " << options.Get<const Anope::string>("group") << ": " << Anope::LastError(); else - Log() << "Successfully set group to " << options->Get<const Anope::string>("group"); + Log() << "Successfully set group to " << options.Get<const Anope::string>("group"); } if (static_cast<int>(uid) != -1) { if (setuid(uid) == -1) - Log() << "Unable to setuid to " << options->Get<const Anope::string>("user") << ": " << Anope::LastError(); + Log() << "Unable to setuid to " << options.Get<const Anope::string>("user") << ": " << Anope::LastError(); else - Log() << "Successfully set user to " << options->Get<const Anope::string>("user"); + Log() << "Successfully set user to " << options.Get<const Anope::string>("user"); } #endif } -void Anope::Init(int ac, char **av) +bool Anope::Init(int ac, char **av) { /* Set file creation mask and group ID. */ #if defined(DEFUMASK) && HAVE_UMASK umask(DEFUMASK); #endif + Anope::UpdateTime(); + Anope::StartTime = Anope::CurTime; + Serialize::RegisterTypes(); /* Parse command line arguments */ @@ -304,7 +317,8 @@ void Anope::Init(int ac, char **av) if (GetCommandLineArgument("version", 'v')) { Log(LOG_TERMINAL) << "Anope-" << Anope::Version() << " -- " << Anope::VersionBuildString(); - throw CoreException(); + Anope::ReturnValue = EXIT_SUCCESS; + return false; } if (GetCommandLineArgument("help", 'h')) @@ -318,10 +332,11 @@ void Anope::Init(int ac, char **av) Log(LOG_TERMINAL) << "-d, --debug[=level]"; Log(LOG_TERMINAL) << "-h, --help"; Log(LOG_TERMINAL) << " --localedir=locale directory"; - Log(LOG_TERMINAL) << " --logdir=logs directory"; - Log(LOG_TERMINAL) << " --modulesdir=modules directory"; + Log(LOG_TERMINAL) << " --logdir=log directory"; + Log(LOG_TERMINAL) << " --moduledir=module directory"; Log(LOG_TERMINAL) << "-e, --noexpire"; Log(LOG_TERMINAL) << "-n, --nofork"; + Log(LOG_TERMINAL) << "-p, --nopid"; Log(LOG_TERMINAL) << " --nothird"; Log(LOG_TERMINAL) << " --protocoldebug"; Log(LOG_TERMINAL) << "-r, --readonly"; @@ -330,7 +345,8 @@ void Anope::Init(int ac, char **av) Log(LOG_TERMINAL) << ""; Log(LOG_TERMINAL) << "Further support is available from https://www.anope.org/"; Log(LOG_TERMINAL) << "Or visit us on IRC at irc.teranova.net #anope"; - throw CoreException(); + Anope::ReturnValue = EXIT_SUCCESS; + return false; } if (GetCommandLineArgument("nofork", 'n')) @@ -348,6 +364,9 @@ void Anope::Init(int ac, char **av) if (GetCommandLineArgument("nothird")) Anope::NoThird = true; + if (GetCommandLineArgument("nopid", 'p')) + Anope::NoPID = true; + if (GetCommandLineArgument("noexpire", 'e')) Anope::NoExpire = true; @@ -359,7 +378,7 @@ void Anope::Init(int ac, char **av) { if (!arg.empty()) { - int level = arg.is_number_only() ? convertTo<int>(arg) : -1; + auto level = Anope::Convert<int>(arg, -1); if (level > 0) Anope::Debug = level; else @@ -397,10 +416,10 @@ void Anope::Init(int ac, char **av) Anope::LocaleDir = arg; } - if (GetCommandLineArgument("modulesdir", 0, arg)) + if (GetCommandLineArgument("moduledir", 0, arg)) { if (arg.empty()) - throw CoreException("The --modulesdir option requires a path"); + throw CoreException("The --moduledir option requires a path"); Anope::ModuleDir = arg; } @@ -411,24 +430,18 @@ void Anope::Init(int ac, char **av) Anope::LogDir = arg; } - /* Chdir to Services data directory. */ - if (chdir(Anope::ServicesDir.c_str()) < 0) + Log(LOG_TERMINAL) << "Anope " << Anope::Version() << ", " << Anope::VersionBuildString(); + + /* Chdir to Anope data directory. */ + Log() << "Moving to " << Anope::ServicesDir; + if (chdir(Anope::ServicesDir.c_str()) != 0) { throw CoreException("Unable to chdir to " + Anope::ServicesDir + ": " + Anope::LastError()); } - Log(LOG_TERMINAL) << "Anope " << Anope::Version() << ", " << Anope::VersionBuildString(); - -#ifdef _WIN32 - if (!SupportedWindowsVersion()) - throw CoreException(GetWindowsVersion() + " is not a supported version of Windows"); -#endif - -#ifdef _WIN32 - Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "\\" << ServicesConf.GetName(); -#else - Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "/" << ServicesConf.GetName(); + Log(LOG_TERMINAL) << "Using configuration file " << Anope::ExpandConfig(ServicesConf.GetName()); +#ifndef _WIN32 /* Fork to background */ if (!Anope::NoFork) { @@ -453,7 +466,7 @@ void Anope::Init(int ac, char **av) sigemptyset(&mask); sigsuspend(&mask); - exit(Anope::ReturnValue); + return false; } else if (i == -1) { @@ -479,7 +492,7 @@ void Anope::Init(int ac, char **av) catch (const ConfigException &ex) { Log(LOG_TERMINAL) << ex.GetReason(); - Log(LOG_TERMINAL) << "*** Support resources: Read through the services.conf self-contained"; + Log(LOG_TERMINAL) << "*** Support resources: Read through the anope.conf self-contained"; Log(LOG_TERMINAL) << "*** documentation. Read the documentation files found in the 'docs'"; Log(LOG_TERMINAL) << "*** folder. Visit our portal located at https://www.anope.org/. Join"; Log(LOG_TERMINAL) << "*** our support channel on /server irc.teranova.net channel #anope."; @@ -487,11 +500,11 @@ void Anope::Init(int ac, char **av) } /* Create me */ - Configuration::Block *block = Config->GetBlock("serverinfo"); - Me = new Server(NULL, block->Get<const Anope::string>("name"), 0, block->Get<const Anope::string>("description"), block->Get<const Anope::string>("id")); - for (botinfo_map::const_iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it) + Configuration::Block &block = Config->GetBlock("serverinfo"); + Me = new Server(NULL, block.Get<const Anope::string>("name"), 0, block.Get<const Anope::string>("description"), block.Get<const Anope::string>("id")); + for (const auto &[_, bi] : *BotListByNick) { - it->second->server = Me; + bi->server = Me; ++Me->users; } @@ -503,27 +516,23 @@ void Anope::Init(int ac, char **av) /* Initialize multi-language support */ Language::InitLanguages(); - /* Initialize random number generator */ - block = Config->GetBlock("options"); - srand(block->Get<unsigned>("seed") ^ time(NULL)); - /* load modules */ Log() << "Loading modules..."; for (int i = 0; i < Config->CountBlock("module"); ++i) - ModuleManager::LoadModule(Config->GetBlock("module", i)->Get<const Anope::string>("name"), NULL); + ModuleManager::LoadModule(Config->GetBlock("module", i).Get<const Anope::string>("name"), NULL); #ifndef _WIN32 /* If we're root, issue a warning now */ if (!getuid() && !getgid()) { /* If we are configured to setuid later, don't issue a warning */ - Configuration::Block *options = Config->GetBlock("options"); - if (options->Get<const Anope::string>("user").empty()) + Configuration::Block &options = Config->GetBlock("options"); + if (options.Get<const Anope::string>("user").empty()) { std::cerr << "WARNING: You are currently running Anope as the root superuser. Anope does not" << std::endl; std::cerr << " require root privileges to run, and it is discouraged that you run Anope" << std::endl; std::cerr << " as the root superuser." << std::endl; - sleep(3); + std::this_thread::sleep_for(std::chrono::seconds(3)); } } @@ -532,14 +541,17 @@ void Anope::Init(int ac, char **av) setuidgid(); #endif - Module *protocol = ModuleManager::FindFirstOf(PROTOCOL); - if (protocol == NULL) + auto *encryption = ModuleManager::FindFirstOf(ENCRYPTION); + if (!encryption) + throw CoreException("You must load a non-deprecated encryption module!"); + + if (!IRCD) throw CoreException("You must load a protocol module!"); /* Write our PID to the PID file. */ write_pidfile(); - Log() << "Using IRCd protocol " << protocol->name; + Log(LOG_TERMINAL) << "Using IRCd protocol " << IRCD->GetProtocolName() << " (" << IRCD->owner->name << ")"; /* Auto assign sid if applicable */ if (IRCD->RequiresID) @@ -547,8 +559,8 @@ void Anope::Init(int ac, char **av) Anope::string sid = IRCD->SID_Retrieve(); if (Me->GetSID() == Me->GetName()) Me->SetSID(sid); - for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it) - it->second->GenerateUID(); + for (const auto &[_, bi] : *BotListByNick) + bi->GenerateUID(); } /* Load up databases */ @@ -560,8 +572,9 @@ void Anope::Init(int ac, char **av) FOREACH_MOD(OnPostInit, ()); - for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it) - it->second->Sync(); + for (const auto &[_, ci] : ChannelList) + ci->Sync(); Serialize::CheckTypes(); + return true; } diff --git a/src/language.cpp b/src/language.cpp index cdcf4afa0..440461133 100644 --- a/src/language.cpp +++ b/src/language.cpp @@ -15,7 +15,7 @@ #include "config.h" #include "language.h" -#if GETTEXT_FOUND +#if HAVE_LOCALIZATION # include <libintl.h> #endif @@ -24,7 +24,7 @@ std::vector<Anope::string> Language::Domains; void Language::InitLanguages() { -#if GETTEXT_FOUND +#if HAVE_LOCALIZATION Log(LOG_DEBUG) << "Initializing Languages..."; Languages.clear(); @@ -36,7 +36,7 @@ void Language::InitLanguages() setlocale(LC_ALL, ""); - spacesepstream sep(Config->GetBlock("options")->Get<const Anope::string>("languages")); + spacesepstream sep(Config->GetBlock("options").Get<const Anope::string>("languages")); Anope::string language; while (sep.GetToken(language)) { @@ -73,12 +73,67 @@ const char *Language::Translate(const NickCore *nc, const char *string) return Translate(nc ? nc->language.c_str() : "", string); } -#if GETTEXT_FOUND +const char *Language::Translate(int count, const char *singular, const char *plural) +{ + return Translate("", count, singular, plural); +} + +const char *Language::Translate(User *u, int count, const char *singular, const char *plural) +{ + if (u && u->IsIdentified()) + return Translate(u->Account(), count, singular, plural); + else + return Translate("", count, singular, plural); +} + +const char *Language::Translate(const NickCore *nc, int count, const char *singular, const char *plural) +{ + return Translate(nc ? nc->language.c_str() : "", count, singular, plural); +} + +#if HAVE_LOCALIZATION #if defined(__GLIBC__) && defined(__USE_GNU_GETTEXT) extern "C" int _nl_msg_cat_cntr; #endif +namespace +{ + void PreTranslate(const char* lang) + { +#if defined(__GLIBC__) && defined(__USE_GNU_GETTEXT) + ++_nl_msg_cat_cntr; +#endif + +#ifdef _WIN32 + SetThreadLocale(MAKELCID(MAKELANGID(WindowsGetLanguage(lang), SUBLANG_DEFAULT), SORT_DEFAULT)); +#else + /* First, set LANG and LANGUAGE env variables. + * Some systems (Debian) don't care about this, so we must setlocale LC_ALL as well. + * BUT if this call fails because the LANGUAGE env variable is set, setlocale resets + * the locale to "C", which short circuits gettext and causes it to fail on systems that + * use the LANGUAGE env variable. We must reset the locale to en_US (or, anything not + * C or POSIX) then. + */ + setenv("LANG", lang, 1); + setenv("LANGUAGE", lang, 1); + if (setlocale(LC_ALL, lang) == NULL) + setlocale(LC_ALL, "en_US"); +#endif + } + + void PostTranslate() + { +#ifdef _WIN32 + SetThreadLocale(MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), SORT_DEFAULT)); +#else + unsetenv("LANGUAGE"); + unsetenv("LANG"); + setlocale(LC_ALL, ""); +#endif + } +} + const char *Language::Translate(const char *lang, const char *string) { if (!string || !*string) @@ -87,36 +142,31 @@ const char *Language::Translate(const char *lang, const char *string) if (!lang || !*lang) lang = Config->DefLanguage.c_str(); -#if defined(__GLIBC__) && defined(__USE_GNU_GETTEXT) - ++_nl_msg_cat_cntr; -#endif + PreTranslate(lang); -#ifdef _WIN32 - SetThreadLocale(MAKELCID(MAKELANGID(WindowsGetLanguage(lang), SUBLANG_DEFAULT), SORT_DEFAULT)); -#else - /* First, set LANG and LANGUAGE env variables. - * Some systems (Debian) don't care about this, so we must setlocale LC_ALL as well. - * BUT if this call fails because the LANGUAGE env variable is set, setlocale resets - * the locale to "C", which short circuits gettext and causes it to fail on systems that - * use the LANGUAGE env variable. We must reset the locale to en_US (or, anything not - * C or POSIX) then. - */ - setenv("LANG", lang, 1); - setenv("LANGUAGE", lang, 1); - if (setlocale(LC_ALL, lang) == NULL) - setlocale(LC_ALL, "en_US"); -#endif const char *translated_string = dgettext("anope", string); for (unsigned i = 0; translated_string == string && i < Domains.size(); ++i) translated_string = dgettext(Domains[i].c_str(), string); -#ifdef _WIN32 - SetThreadLocale(MAKELCID(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), SORT_DEFAULT)); -#else - unsetenv("LANGUAGE"); - unsetenv("LANG"); - setlocale(LC_ALL, ""); -#endif + PostTranslate(); + return translated_string; +} + +const char *Language::Translate(const char *lang, int count, const char *singular, const char *plural) +{ + if (!singular || !*singular || !plural || !*plural) + return ""; + + if (!lang || !*lang) + lang = Config->DefLanguage.c_str(); + + PreTranslate(lang); + + const char *translated_string = dngettext("anope", singular, plural, count); + for (unsigned i = 0; (translated_string == singular || translated_string == plural) && i < Domains.size(); ++i) + translated_string = dngettext(Domains[i].c_str(), singular, plural, count); + + PostTranslate(); return translated_string; } #else @@ -124,4 +174,10 @@ const char *Language::Translate(const char *lang, const char *string) { return string != NULL ? string : ""; } +const char *Language::Translate(const char *lang, int count, const char *singular, const char *plural) +{ + return Language::Translate("", count == 1 ? singular : plural); +} #endif + + diff --git a/src/logger.cpp b/src/logger.cpp index c2e250831..28614d9f1 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -29,20 +29,15 @@ static Anope::string GetTimeStamp() { char tbuf[256]; - time_t t; - if (time(&t) < 0) - t = Anope::CurTime; - - tm tm = *localtime(&t); + Anope::UpdateTime(); + auto tm = *localtime(&Anope::CurTime); if (Anope::Debug) { char *s; - struct timeval tv; - gettimeofday(&tv, NULL); strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S", &tm); s = tbuf + strlen(tbuf); - s += snprintf(s, sizeof(tbuf) - (s - tbuf), ".%06d", static_cast<int>(tv.tv_usec)); + s += snprintf(s, sizeof(tbuf) - (s - tbuf), ".%06lld", static_cast<long long>(Anope::CurTimeNs / 1000)); strftime(s, sizeof(tbuf) - (s - tbuf) - 1, " %Y]", &tm); } else @@ -57,7 +52,7 @@ static inline Anope::string CreateLogName(const Anope::string &file, time_t t = tm *tm = localtime(&t); strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm); - return Anope::LogDir + "/" + file + "." + timestamp; + return Anope::ExpandLog(file + "." + timestamp); } LogFile::LogFile(const Anope::string &name) : filename(name), stream(name.c_str(), std::ios_base::out | std::ios_base::app) @@ -74,11 +69,11 @@ const Anope::string &LogFile::GetName() const return this->filename; } -Log::Log(LogType t, const Anope::string &cat, BotInfo *b) : bi(b), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(t), category(cat) +Log::Log(LogType t, const Anope::string &cat, BotInfo *b) : bi(b), type(t), category(cat) { } -Log::Log(LogType t, CommandSource &src, Command *_c, ChannelInfo *_ci) : u(src.GetUser()), nc(src.nc), c(_c), source(&src), chan(NULL), ci(_ci), s(NULL), m(NULL), type(t) +Log::Log(LogType t, CommandSource &src, Command *_c, ChannelInfo *_ci) : u(src.GetUser()), nc(src.nc), c(_c), source(&src), ci(_ci), type(t) { if (!c) throw CoreException("Invalid pointers passed to Log::Log"); @@ -87,35 +82,34 @@ Log::Log(LogType t, CommandSource &src, Command *_c, ChannelInfo *_ci) : u(src.G throw CoreException("This constructor does not support this log type"); size_t sl = c->name.find('/'); - this->bi = NULL; if (sl != Anope::string::npos) this->bi = Config->GetClient(c->name.substr(0, sl)); this->category = c->name; } -Log::Log(User *_u, Channel *ch, const Anope::string &cat) : bi(NULL), u(_u), nc(NULL), c(NULL), source(NULL), chan(ch), ci(chan ? *chan->ci : NULL), s(NULL), m(NULL), type(LOG_CHANNEL), category(cat) +Log::Log(User *_u, Channel *ch, const Anope::string &cat) : u(_u), chan(ch), ci(chan ? *chan->ci : nullptr), type(LOG_CHANNEL), category(cat) { if (!chan) throw CoreException("Invalid pointers passed to Log::Log"); } -Log::Log(User *_u, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(_u), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_USER), category(cat) +Log::Log(User *_u, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(_u), type(LOG_USER), category(cat) { if (!u) throw CoreException("Invalid pointers passed to Log::Log"); } -Log::Log(Server *serv, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(serv), m(NULL), type(LOG_SERVER), category(cat) +Log::Log(Server *serv, const Anope::string &cat, BotInfo *_bi) : bi(_bi), s(serv), type(LOG_SERVER), category(cat) { if (!s) throw CoreException("Invalid pointer passed to Log::Log"); } -Log::Log(BotInfo *b, const Anope::string &cat) : bi(b), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_NORMAL), category(cat) +Log::Log(BotInfo *b, const Anope::string &cat) : bi(b), type(LOG_NORMAL), category(cat) { } -Log::Log(Module *mod, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(mod), type(LOG_MODULE), category(cat) +Log::Log(Module *mod, const Anope::string &cat, BotInfo *_bi) : bi(_bi), m(mod), type(LOG_MODULE), category(cat) { } @@ -131,9 +125,13 @@ Log::~Log() FOREACH_MOD(OnLog, (this)); if (Config) - for (unsigned i = 0; i < Config->LogInfos.size(); ++i) - if (Config->LogInfos[i].HasType(this->type, this->category)) - Config->LogInfos[i].ProcessMessage(this); + { + for (auto &li : Config->LogInfos) + { + if (li.HasType(this->type, this->category)) + li.ProcessMessage(this); + } + } } Anope::string Log::FormatSource() const @@ -227,14 +225,14 @@ Anope::string Log::BuildPrefix() const return buffer; } -LogInfo::LogInfo(int la, bool rio, bool ldebug) : bot(NULL), last_day(0), log_age(la), raw_io(rio), debug(ldebug) +LogInfo::LogInfo(int la, bool rio, bool ldebug) : log_age(la), raw_io(rio), debug(ldebug) { } LogInfo::~LogInfo() { - for (unsigned i = 0; i < this->logfiles.size(); ++i) - delete this->logfiles[i]; + for (const auto *logfile : this->logfiles) + delete logfile; this->logfiles.clear(); } @@ -281,16 +279,15 @@ bool LogInfo::HasType(LogType ltype, const Anope::string &type) const if (list == NULL) return false; - for (unsigned i = 0; i < list->size(); ++i) + for (auto value : *list) { - Anope::string cat = list->at(i); bool inverse = false; - if (cat[0] == '~') + if (value[0] == '~') { - cat.erase(cat.begin()); + value.erase(value.begin()); inverse = true; } - if (Anope::Match(type, cat)) + if (Anope::Match(type, value)) { return !inverse; } @@ -301,18 +298,16 @@ bool LogInfo::HasType(LogType ltype, const Anope::string &type) const void LogInfo::OpenLogFiles() { - for (unsigned i = 0; i < this->logfiles.size(); ++i) - delete this->logfiles[i]; + for (const auto *logfile : this->logfiles) + delete logfile; this->logfiles.clear(); - for (unsigned i = 0; i < this->targets.size(); ++i) + for (const auto &target : this->targets) { - const Anope::string &target = this->targets[i]; - if (target.empty() || target[0] == '#' || target == "globops" || target.find(":") != Anope::string::npos) continue; - LogFile *lf = new LogFile(CreateLogName(target)); + auto *lf = new LogFile(CreateLogName(target)); if (!lf->stream.is_open()) { Log() << "Unable to open logfile " << lf->GetName(); @@ -353,10 +348,8 @@ void LogInfo::ProcessMessage(const Log *l) FOREACH_MOD(OnLogMessage, (this, l, buffer)); - for (unsigned i = 0; i < this->targets.size(); ++i) + for (const auto &target : this->targets) { - const Anope::string &target = this->targets[i]; - if (!target.empty() && target[0] == '#') { if (UplinkSock && l->type <= LOG_NORMAL && Me && Me->IsSynced()) @@ -371,7 +364,7 @@ void LogInfo::ProcessMessage(const Log *l) if (!bi) bi = c->WhoSends(); if (bi) - IRCD->SendPrivmsg(bi, c->name, "%s", buffer.c_str()); + IRCD->SendPrivmsg(bi, c->name, buffer); } } else if (target == "globops") @@ -382,7 +375,7 @@ void LogInfo::ProcessMessage(const Log *l) if (!bi) bi = this->bot; if (bi) - IRCD->SendGlobops(bi, "%s", buffer.c_str()); + IRCD->SendGlobops(bi, buffer); } } } @@ -394,11 +387,10 @@ void LogInfo::ProcessMessage(const Log *l) this->OpenLogFiles(); if (this->log_age) - for (unsigned i = 0; i < this->targets.size(); ++i) + { + for (const auto &target : this->targets) { - const Anope::string &target = this->targets[i]; - - if (target.empty() || target[0] == '#' || target == "globops" || target.find(":") != Anope::string::npos) + if (target.empty() || target[0] == '#' || target == "globops" || target.find(":") != Anope::string::npos) continue; Anope::string oldlog = CreateLogName(target, Anope::CurTime - 86400 * this->log_age); @@ -408,11 +400,11 @@ void LogInfo::ProcessMessage(const Log *l) Log(LOG_DEBUG) << "Deleted old logfile " << oldlog; } } + } } - for (unsigned i = 0; i < this->logfiles.size(); ++i) + for (auto *lf : this->logfiles) { - LogFile *lf = this->logfiles[i]; lf->stream << GetTimeStamp() << " " << buffer << std::endl; } } diff --git a/src/mail.cpp b/src/mail.cpp index bdad2447f..3aa15c99f 100644 --- a/src/mail.cpp +++ b/src/mail.cpp @@ -17,13 +17,14 @@ Mail::Message::Message(const Anope::string &sf, const Anope::string &mailto, const Anope::string &a, const Anope::string &s, const Anope::string &m) : Thread() - , sendmail_path(Config->GetBlock("mail")->Get<const Anope::string>("sendmailpath")) - , send_from(sf), mail_to(mailto) + , sendmail_path(Config->GetBlock("mail").Get<const Anope::string>("sendmailpath", "/usr/sbin/sendmail -it")) + , send_from(sf) + , mail_to(mailto) , addr(a) , subject(s) , message(m) - , content_type(Config->GetBlock("mail")->Get<const Anope::string>("content_type", "text/plain; charset=UTF-8")) - , dont_quote_addresses(Config->GetBlock("mail")->Get<bool>("dontquoteaddresses")) + , content_type(Config->GetBlock("mail").Get<const Anope::string>("content_type", "text/plain; charset=UTF-8")) + , dont_quote_addresses(Config->GetBlock("mail").Get<bool>("dontquoteaddresses")) { } @@ -38,8 +39,7 @@ Mail::Message::~Message() void Mail::Message::Run() { errno = 0; - FILE *pipe = popen(sendmail_path.c_str(), "w"); - + auto *pipe = popen(sendmail_path.c_str(), "w"); if (!pipe) { error = strerror(errno); @@ -56,13 +56,16 @@ void Mail::Message::Run() fprintf(pipe, "Content-Type: %s\r\n", content_type.c_str()); fprintf(pipe, "Content-Transfer-Encoding: 8bit\r\n"); fprintf(pipe, "\r\n"); - fprintf(pipe, "%s", message.c_str()); - fprintf(pipe, "\r\n.\r\n"); - int result = pclose(pipe); + std::stringstream stream(message.str()); + for (Anope::string line; std::getline(stream, line.str()); ) + fprintf(pipe, "%s\r\n", line.c_str()); + fprintf(pipe, "\r\n"); + auto result = pclose(pipe); if (result > 0) - error = "Sendmail exited with code " + stringify(result); + error = "Sendmail exited with code " + Anope::ToString(result); + SetExitState(); } @@ -71,32 +74,32 @@ bool Mail::Send(User *u, NickCore *nc, BotInfo *service, const Anope::string &su if (!nc || !service || subject.empty() || message.empty()) return false; - Configuration::Block *b = Config->GetBlock("mail"); + Configuration::Block &b = Config->GetBlock("mail"); if (!u) { - if (!b->Get<bool>("usemail") || b->Get<const Anope::string>("sendfrom").empty()) + if (!b.Get<bool>("usemail") || b.Get<const Anope::string>("sendfrom").empty()) return false; else if (nc->email.empty()) return false; nc->lastmail = Anope::CurTime; - Thread *t = new Mail::Message(b->Get<const Anope::string>("sendfrom"), nc->display, nc->email, subject, message); + Thread *t = new Mail::Message(b.Get<const Anope::string>("sendfrom"), nc->display, nc->email, subject, message); t->Start(); return true; } else { - if (!b->Get<bool>("usemail") || b->Get<const Anope::string>("sendfrom").empty()) + if (!b.Get<bool>("usemail") || b.Get<const Anope::string>("sendfrom").empty()) u->SendMessage(service, _("Services have been configured to not send mail.")); - else if (Anope::CurTime - u->lastmail < b->Get<time_t>("delay")) - u->SendMessage(service, _("Please wait \002%d\002 seconds and retry."), b->Get<time_t>("delay") - (Anope::CurTime - u->lastmail)); + else if (Anope::CurTime - u->lastmail < b.Get<time_t>("delay")) + u->SendMessage(service, _("Please wait \002%lu\002 seconds and retry."), (unsigned long)b.Get<time_t>("delay") - (Anope::CurTime - u->lastmail)); else if (nc->email.empty()) - u->SendMessage(service, _("E-mail for \002%s\002 is invalid."), nc->display.c_str()); + u->SendMessage(service, _("Email for \002%s\002 is invalid."), nc->display.c_str()); else { u->lastmail = nc->lastmail = Anope::CurTime; - Thread *t = new Mail::Message(b->Get<const Anope::string>("sendfrom"), nc->display, nc->email, subject, message); + Thread *t = new Mail::Message(b.Get<const Anope::string>("sendfrom"), nc->display, nc->email, subject, message); t->Start(); return true; } @@ -107,21 +110,21 @@ bool Mail::Send(User *u, NickCore *nc, BotInfo *service, const Anope::string &su bool Mail::Send(NickCore *nc, const Anope::string &subject, const Anope::string &message) { - Configuration::Block *b = Config->GetBlock("mail"); - if (!b->Get<bool>("usemail") || b->Get<const Anope::string>("sendfrom").empty() || !nc || nc->email.empty() || subject.empty() || message.empty()) + Configuration::Block &b = Config->GetBlock("mail"); + if (!b.Get<bool>("usemail") || b.Get<const Anope::string>("sendfrom").empty() || !nc || nc->email.empty() || subject.empty() || message.empty()) return false; nc->lastmail = Anope::CurTime; - Thread *t = new Mail::Message(b->Get<const Anope::string>("sendfrom"), nc->display, nc->email, subject, message); + Thread *t = new Mail::Message(b.Get<const Anope::string>("sendfrom"), nc->display, nc->email, subject, message); t->Start(); return true; } /** - * Checks whether we have a valid, common e-mail address. + * Checks whether we have a valid, common email address. * This is NOT entirely RFC compliant, and won't be so, because I said - * *common* cases. ;) It is very unlikely that e-mail addresses that + * *common* cases. ;) It is very unlikely that email addresses that * are really being used will fail the check. * * @param email Email to Validate @@ -148,13 +151,15 @@ bool Mail::Validate(const Anope::string &email) return false; /* Check for forbidden characters in the name */ - for (unsigned i = 0, end = copy.length(); i < end; ++i) + for (auto chr : copy) { - if (copy[i] <= 31 || copy[i] >= 127) + if (chr <= 31 || chr >= 127) return false; - for (unsigned int j = 0; j < 13; ++j) - if (copy[i] == specials[j]) + for (auto special : specials) + { + if (chr == special) return false; + } } /* Check for forbidden characters in the domain */ @@ -162,9 +167,11 @@ bool Mail::Validate(const Anope::string &email) { if (domain[i] <= 31 || domain[i] >= 127) return false; - for (unsigned int j = 0; j < 13; ++j) - if (domain[i] == specials[j]) + for (auto special : specials) + { + if (domain[i] == special) return false; + } if (domain[i] == '.') { if (!i || i == end - 1) diff --git a/src/main.cpp b/src/main.cpp index 37e039fd2..ad4a72443 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,4 @@ -/* Services -- main source file. +/* Anope -- main source file. * * (C) 2003-2025 Anope Team * Contact us at team@anope.org @@ -17,14 +17,14 @@ #include "uplink.h" #ifndef _WIN32 -#include <limits.h> +#include <climits> #else #include <process.h> #endif /* Command-line options: */ int Anope::Debug = 0; -bool Anope::ReadOnly = false, Anope::NoFork = false, Anope::NoThird = false, Anope::NoExpire = false, Anope::ProtocolDebug = false; +bool Anope::ReadOnly = false, Anope::NoFork = false, Anope::NoThird = false, Anope::NoPID = false, Anope::NoExpire = false, Anope::ProtocolDebug = false; Anope::string Anope::ServicesDir; Anope::string Anope::ServicesBin; @@ -36,28 +36,37 @@ Anope::string Anope::QuitReason; static Anope::string BinaryDir; /* Full path to services bin directory */ -time_t Anope::StartTime = time(NULL); -time_t Anope::CurTime = time(NULL); +time_t Anope::StartTime = 0; +time_t Anope::CurTime = 0; +long long Anope::CurTimeNs = 0; -int Anope::CurrentUplink = -1; +size_t Anope::CurrentUplink = -1; -class UpdateTimer : public Timer +class UpdateTimer final + : public Timer { - public: - UpdateTimer(time_t timeout) : Timer(timeout, Anope::CurTime, true) { } +public: + UpdateTimer(time_t timeout) + : Timer(timeout, true) + { + } - void Tick(time_t) anope_override + void Tick() override { Anope::SaveDatabases(); } }; -class ExpireTimer : public Timer +class ExpireTimer final + : public Timer { - public: - ExpireTimer(time_t timeout) : Timer(timeout, Anope::CurTime, true) { } +public: + ExpireTimer(time_t timeout) + : Timer(timeout, true) + { + } - void Tick(time_t) anope_override + void Tick() override { FOREACH_MOD(OnExpireTick, ()); } @@ -76,13 +85,13 @@ void Anope::SaveDatabases() */ static Anope::string GetFullProgDir(const Anope::string &argv0) { - char buffer[PATH_MAX]; #ifdef _WIN32 /* Windows has specific API calls to get the EXE path that never fail. * For once, Windows has something of use, compared to the POSIX code * for this, this is positively neato. */ - if (GetModuleFileName(NULL, buffer, PATH_MAX)) + char buffer[MAX_PATH]; + if (GetModuleFileName(NULL, buffer, MAX_PATH)) { Anope::string fullpath = buffer; Anope::string::size_type n = fullpath.rfind("\\"); @@ -91,6 +100,7 @@ static Anope::string GetFullProgDir(const Anope::string &argv0) } #else // Get the current working directory + char buffer[PATH_MAX]; if (getcwd(buffer, PATH_MAX)) { Anope::string remainder = argv0; @@ -137,12 +147,13 @@ int main(int ac, char **av, char **envp) try { /* General initialization first */ - Anope::Init(ac, av); + if (!Anope::Init(ac, av)) + return Anope::ReturnValue; } catch (const CoreException &ex) { Log() << ex.GetReason(); - return -1; + return EXIT_FAILURE; } try @@ -151,13 +162,13 @@ int main(int ac, char **av, char **envp) } catch (const SocketException &ex) { - Log(LOG_TERMINAL) << "Unable to connect to uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].host << ":" << Config->Uplinks[Anope::CurrentUplink].port << "): " << ex.GetReason(); + Log(LOG_TERMINAL) << "Unable to connect to uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].str() << "): " << ex.GetReason(); } /* Set up timers */ time_t last_check = Anope::CurTime; - UpdateTimer updateTimer(Config->GetBlock("options")->Get<time_t>("updatetimeout", "5m")); - ExpireTimer expireTimer(Config->GetBlock("options")->Get<time_t>("expiretimeout", "30m")); + UpdateTimer updateTimer(Config->GetBlock("options").Get<time_t>("updatetimeout", "2m")); + ExpireTimer expireTimer(Config->GetBlock("options").Get<time_t>("expiretimeout", "30m")); /*** Main loop. ***/ while (!Anope::Quitting) @@ -167,7 +178,7 @@ int main(int ac, char **av, char **envp) /* Process timers */ if (Anope::CurTime - last_check >= Config->TimeoutCheck) { - TimerManager::TickTimers(Anope::CurTime); + TimerManager::TickTimers(); last_check = Anope::CurTime; } @@ -206,11 +217,13 @@ int main(int ac, char **av, char **envp) if (Anope::Restarting) { - chdir(BinaryDir.c_str()); - Anope::string sbin = "./" + Anope::ServicesBin; - av[0] = const_cast<char *>(sbin.c_str()); - execve(Anope::ServicesBin.c_str(), av, envp); - Log() << "Restart failed"; + if (chdir(BinaryDir.c_str()) == 0) + { + Anope::string sbin = "./" + Anope::ServicesBin; + av[0] = const_cast<char *>(sbin.c_str()); + execve(Anope::ServicesBin.c_str(), av, envp); + } + Log() << "Restart failed: " << strerror(errno); Anope::ReturnValue = -1; } diff --git a/src/memos.cpp b/src/memos.cpp index 53177135d..809f895db 100644 --- a/src/memos.cpp +++ b/src/memos.cpp @@ -17,7 +17,8 @@ #include "account.h" #include "regchannel.h" -Memo::Memo() : Serializable("Memo") +Memo::Memo() + : Serializable(MEMO_TYPE) { mi = NULL; unread = receipt = false; @@ -34,17 +35,23 @@ Memo::~Memo() } } -void Memo::Serialize(Serialize::Data &data) const +Memo::Type::Type() + : Serialize::Type(MEMO_TYPE) { - data["owner"] << this->owner; - data.SetType("time", Serialize::Data::DT_INT); data["time"] << this->time; - data["sender"] << this->sender; - data["text"] << this->text; - data["unread"] << this->unread; - data["receipt"] << this->receipt; } -Serializable* Memo::Unserialize(Serializable *obj, Serialize::Data &data) +void Memo::Type::Serialize(const Serializable *obj, Serialize::Data &data) const +{ + const auto *m = static_cast<const Memo *>(obj); + data.Store("owner", m->owner); + data.Store("time", m->time); + data.Store("sender", m->sender); + data.Store("text", m->text); + data.Store("unread", m->unread); + data.Store("receipt", m->receipt); +} + +Serializable *Memo::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { Anope::string owner; @@ -76,7 +83,8 @@ Serializable* Memo::Unserialize(Serializable *obj, Serialize::Data &data) return m; } -MemoInfo::MemoInfo() : memomax(0), memos("Memo") +MemoInfo::MemoInfo() + : memos(MEMO_TYPE) { } @@ -113,9 +121,11 @@ void MemoInfo::Del(unsigned index) bool MemoInfo::HasIgnore(User *u) { - for (unsigned i = 0; i < this->ignores.size(); ++i) - if (u->nick.equals_ci(this->ignores[i]) || (u->IsIdentified() && u->Account()->display.equals_ci(this->ignores[i])) || Anope::Match(u->GetMask(), Anope::string(this->ignores[i]))) + for (const auto &ignore : this->ignores) + { + if (u->nick.equals_ci(ignore) || (u->IsIdentified() && u->Account()->display.equals_ci(ignore)) || Anope::Match(u->GetMask(), Anope::string(ignore))) return true; + } return false; } diff --git a/src/messages.cpp b/src/messages.cpp index b2f65a5c2..273ab827b 100644 --- a/src/messages.cpp +++ b/src/messages.cpp @@ -19,10 +19,11 @@ #include "messages.h" #include "servers.h" #include "channels.h" +#include "numeric.h" using namespace Message; -void Away::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Away::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { const Anope::string &msg = !params.empty() ? params[0] : ""; @@ -33,7 +34,7 @@ void Away::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) Log(source.GetUser(), "away") << "is no longer away"; } -void Capab::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Capab::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { if (params.size() == 1) { @@ -43,18 +44,20 @@ void Capab::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) Servers::Capab.insert(token); } else - for (unsigned i = 0; i < params.size(); ++i) - Servers::Capab.insert(params[i]); + { + for (const auto ¶m : params) + Servers::Capab.insert(param); + } } -void Error::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Error::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { Log(LOG_TERMINAL) << "ERROR: " << params[0]; Anope::QuitReason = "Received ERROR from uplink: " + params[0]; Anope::Quitting = true; } -void Invite::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Invite::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { User *targ = User::Find(params[0]); Channel *c = Channel::Find(params[1]); @@ -65,7 +68,7 @@ void Invite::Run(MessageSource &source, const std::vector<Anope::string> ¶ms FOREACH_MOD(OnInvite, (source.GetUser(), c, targ)); } -void Join::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Join::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { User *user = source.GetUser(); const Anope::string &channels = params[0]; @@ -92,14 +95,14 @@ void Join::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) } std::list<SJoinUser> users; - users.push_back(std::make_pair(ChannelStatus(), user)); + users.emplace_back(ChannelStatus(), user); Channel *chan = Channel::Find(channel); - SJoin(source, channel, chan ? chan->creation_time : Anope::CurTime, "", users); + SJoin(source, channel, chan ? chan->creation_time : Anope::CurTime, "", {}, users); } } -void Join::SJoin(MessageSource &source, const Anope::string &chan, time_t ts, const Anope::string &modes, const std::list<SJoinUser> &users) +void Join::SJoin(MessageSource &source, const Anope::string &chan, time_t ts, const Anope::string &modes, const std::vector<Anope::string> &modeparams, const std::list<SJoinUser> &users) { bool created; Channel *c = Channel::FindOrCreate(chan, created, ts ? ts : Anope::CurTime); @@ -125,12 +128,10 @@ void Join::SJoin(MessageSource &source, const Anope::string &chan, time_t ts, co /* If we are syncing, mlock is checked later in Channel::Sync. It is important to not check it here * so that Channel::SetCorrectModes can correctly detect the presence of channel mode +r. */ - c->SetModesInternal(source, modes, ts, !c->syncing); + c->SetModesInternal(source, modes, modeparams, ts, !c->syncing); - for (std::list<SJoinUser>::const_iterator it = users.begin(), it_end = users.end(); it != it_end; ++it) + for (const auto &[status, u] : users) { - const ChannelStatus &status = it->first; - User *u = it->second; keep_their_modes = ts <= c->creation_time; // OnJoinChannel can call modules which can modify this channel's ts if (c->FindUser(u)) @@ -167,7 +168,7 @@ void Join::SJoin(MessageSource &source, const Anope::string &chan, time_t ts, co } } -void Kick::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Kick::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { const Anope::string &channel = params[0]; const Anope::string &users = params[1]; @@ -184,7 +185,7 @@ void Kick::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) c->KickInternal(source, user, reason); } -void Kill::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Kill::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { User *u = User::Find(params[0]); BotInfo *bi; @@ -199,7 +200,7 @@ void Kill::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) if (last_time == Anope::CurTime) { - Anope::QuitReason = "Kill loop detected. Are Services U:Lined?"; + Anope::QuitReason = "Kill loop detected. Is Anope U:Lined?"; Anope::Quitting = true; return; } @@ -211,53 +212,46 @@ void Kill::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) u->KillInternal(source, params[1]); } -void Message::Mode::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Message::Mode::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { - Anope::string buf; - for (unsigned i = 1; i < params.size(); ++i) - buf += " " + params[i]; - if (IRCD->IsChannelValid(params[0])) { Channel *c = Channel::Find(params[0]); if (c) - c->SetModesInternal(source, buf.substr(1), 0); + c->SetModesInternal(source, params[1], { params.begin() + 2, params.end() }); } else { User *u = User::Find(params[0]); if (u) - u->SetModesInternal(source, "%s", buf.substr(1).c_str()); + u->SetModesInternal(source, params[1], { params.begin() + 2, params.end() }); } } /* XXX We should cache the file somewhere not open/read/close it on every request */ -void MOTD::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void MOTD::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { Server *s = Server::Find(params[0]); if (s != Me) return; - FILE *f = fopen(Config->GetBlock("serverinfo")->Get<const Anope::string>("motd").c_str(), "r"); - if (f) + auto motdfile = Anope::ExpandConfig(Config->GetBlock("serverinfo").Get<const Anope::string>("motd")); + std::ifstream stream(motdfile.str()); + if (!stream.is_open()) { - IRCD->SendNumeric(375, source.GetSource(), ":- %s Message of the Day", s->GetName().c_str()); - char buf[BUFSIZE]; - while (fgets(buf, sizeof(buf), f)) - { - buf[strlen(buf) - 1] = 0; - IRCD->SendNumeric(372, source.GetSource(), ":- %s", buf); - } - fclose(f); - IRCD->SendNumeric(376, source.GetSource(), ":End of /MOTD command."); + IRCD->SendNumeric(ERR_NOSUCHNICK, source.GetSource(), "- MOTD file not readable! Please contact your IRC administrator."); + return; } - else - IRCD->SendNumeric(422, source.GetSource(), ":- MOTD file not found! Please contact your IRC administrator."); + + IRCD->SendNumeric(RPL_MOTDSTART, source.GetSource(), "- " + s->GetName() + " Message of the Day"); + for (Anope::string line; std::getline(stream, line.str()); ) + IRCD->SendNumeric(RPL_MOTD, source.GetSource(), Anope::printf("- %s", line.c_str())); + IRCD->SendNumeric(RPL_ENDOFMOTD, source.GetSource(), "End of /MOTD command."); } -void Notice::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Notice::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { Anope::string message = params[1]; @@ -269,11 +263,11 @@ void Notice::Run(MessageSource &source, const std::vector<Anope::string> ¶ms BotInfo *bi = BotInfo::Find(params[0]); if (!bi) return; - FOREACH_MOD(OnBotNotice, (u, bi, message)); + FOREACH_MOD(OnBotNotice, (u, bi, message, tags)); } } -void Part::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Part::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { User *u = source.GetUser(); const Anope::string &reason = params.size() > 1 ? params[1] : ""; @@ -295,12 +289,12 @@ void Part::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) } } -void Ping::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Ping::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { IRCD->SendPong(params.size() > 1 ? params[1] : Me->GetSID(), params[0]); } -void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { const Anope::string &receiver = params[0]; Anope::string message = params[1]; @@ -312,7 +306,7 @@ void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> ¶m Channel *c = Channel::Find(receiver); if (c) { - FOREACH_MOD(OnPrivmsg, (u, c, message)); + FOREACH_MOD(OnPrivmsg, (u, c, message, tags)); } } else @@ -330,50 +324,39 @@ void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> ¶m if (!servername.equals_ci(Me->GetName())) return; } - else if (!IRCD->RequiresID && Config->UseStrictPrivmsg) - { - BotInfo *bi = BotInfo::Find(receiver); - if (!bi) - return; - Log(LOG_DEBUG) << "Ignored PRIVMSG without @ from " << u->nick; - u->SendMessage(bi, _("\"/msg %s\" is no longer supported. Use \"/msg %s@%s\" or \"/%s\" instead."), bi->nick.c_str(), bi->nick.c_str(), Me->GetName().c_str(), bi->nick.c_str()); - return; - } BotInfo *bi = BotInfo::Find(botname, nick_only); - if (bi) { - if (message[0] == '\1' && message[message.length() - 1] == '\1') + Anope::string ctcpname, ctcpbody; + if (Anope::ParseCTCP(message, ctcpname, ctcpbody)) { - if (message.substr(0, 6).equals_ci("\1PING ")) + if (ctcpname.equals_ci("PING")) { - Anope::string buf = message; - buf.erase(buf.begin()); - buf.erase(buf.end() - 1); - IRCD->SendCTCP(bi, u->nick, "%s", buf.c_str()); + IRCD->SendNotice(bi, u->nick, Anope::FormatCTCP("PING", ctcpbody)); } - else if (message.substr(0, 9).equals_ci("\1VERSION\1")) + else if (ctcpname.equals_ci("VERSION")) { Module *enc = ModuleManager::FindFirstOf(ENCRYPTION); - IRCD->SendCTCP(bi, u->nick, "VERSION Anope-%s %s :%s - (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)", Anope::VersionBuildString().c_str()); + IRCD->SendNotice(bi, u->nick, Anope::FormatCTCP("VERSION", Anope::printf("Anope-%s %s -- %s -- %s", Anope::Version().c_str(), + Anope::VersionBuildString().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)"))); } return; } EventReturn MOD_RESULT; - FOREACH_RESULT(OnBotPrivmsg, MOD_RESULT, (u, bi, message)); + FOREACH_RESULT(OnBotPrivmsg, MOD_RESULT, (u, bi, message, tags)); if (MOD_RESULT == EVENT_STOP) return; - bi->OnMessage(u, message); + bi->OnMessage(u, message, tags); } } return; } -void Quit::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Quit::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { const Anope::string &reason = params[0]; User *user = source.GetUser(); @@ -383,7 +366,7 @@ void Quit::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) user->Quit(reason); } -void SQuit::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void SQuit::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { Server *s = Server::Find(params[0]); @@ -404,7 +387,7 @@ void SQuit::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) s->Delete(s->GetName() + " " + s->GetUplink()->GetName()); } -void Stats::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Stats::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { User *u = source.GetUser(); @@ -413,60 +396,56 @@ void Stats::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) case 'l': if (u->HasMode("OPER")) { - IRCD->SendNumeric(211, source.GetSource(), "Server SendBuf SentBytes SentMsgs RecvBuf RecvBytes RecvMsgs ConnTime"); - IRCD->SendNumeric(211, source.GetSource(), "%s %d %d %d %d %d %d %ld", Config->Uplinks[Anope::CurrentUplink].host.c_str(), UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, static_cast<long>(Anope::CurTime - Anope::StartTime)); + IRCD->SendNumeric(RPL_STATSLINKINFO, source.GetSource(), "Server SendBuf SentBytes SentMsgs RecvBuf RecvBytes RecvMsgs ConnTime"); + IRCD->SendNumeric(RPL_STATSLINKINFO, source.GetSource(), Config->Uplinks[Anope::CurrentUplink].host, UplinkSock->WriteBufferLen(), TotalWritten, -1, UplinkSock->ReadBufferLen(), TotalRead, -1, Anope::CurTime - Anope::StartTime); } - IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]); + IRCD->SendNumeric(RPL_STATSLINKINFO, source.GetSource(), params[0][0], "End of /STATS report."); break; case 'o': case 'O': /* Check whether the user is an operator */ - if (!u->HasMode("OPER") && Config->GetBlock("options")->Get<bool>("hidestatso")) - IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]); + if (!u->HasMode("OPER") && Config->GetBlock("options").Get<bool>("hidestatso")) + IRCD->SendNumeric(RPL_STATSLINKINFO, source.GetSource(), params[0][0], "End of /STATS report."); else { - for (unsigned i = 0; i < Oper::opers.size(); ++i) + for (auto *o : Oper::opers) { - Oper *o = Oper::opers[i]; - const NickAlias *na = NickAlias::Find(o->name); if (na) - IRCD->SendNumeric(243, source.GetSource(), "O * * %s %s 0", o->name.c_str(), o->ot->GetName().replace_all_cs(" ", "_").c_str()); + IRCD->SendNumeric(RPL_STATSOLINE, source.GetSource(), 'O', '*', '*', o->name, o->ot->GetName().replace_all_cs(" ", "_"), '0'); } - IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]); + IRCD->SendNumeric(RPL_STATSLINKINFO, source.GetSource(), params[0][0], "End of /STATS report."); } break; case 'u': { long uptime = static_cast<long>(Anope::CurTime - Anope::StartTime); - IRCD->SendNumeric(242, source.GetSource(), ":Services up %ld day%s, %02ld:%02ld:%02ld", uptime / 86400, uptime / 86400 == 1 ? "" : "s", (uptime / 3600) % 24, (uptime / 60) % 60, uptime % 60); - IRCD->SendNumeric(250, source.GetSource(), ":Current users: %lu (%d ops); maximum %u", static_cast<unsigned long>(UserListByNick.size()), OperCount, MaxUserCount); - IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]); + IRCD->SendNumeric(RPL_STATSUPTIME, source.GetSource(), Anope::printf("Services up %ld day%s, %02ld:%02ld:%02ld", uptime / 86400, uptime / 86400 == 1 ? "" : "s", (uptime / 3600) % 24, (uptime / 60) % 60, uptime % 60)); + IRCD->SendNumeric(RPL_STATSCONN, source.GetSource(), Anope::printf("Current users: %zu (%d ops); maximum %u", UserListByNick.size(), OperCount, MaxUserCount)); + IRCD->SendNumeric(RPL_STATSLINKINFO, source.GetSource(), params[0][0], "End of /STATS report."); break; } /* case 'u' */ default: - IRCD->SendNumeric(219, source.GetSource(), "%c :End of /STATS report.", params[0][0]); + IRCD->SendNumeric(RPL_STATSLINKINFO, source.GetSource(), params[0][0], "End of /STATS report."); } return; } -void Time::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Time::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { - time_t t; - time(&t); - struct tm *tm = localtime(&t); - char buf[64]; - strftime(buf, sizeof(buf), "%a %b %d %H:%M:%S %Y %Z", tm); - IRCD->SendNumeric(391, source.GetSource(), "%s :%s", Me->GetName().c_str(), buf); - return; + const auto *tm = localtime(&Anope::CurTime); + char timebuf[64]; + strftime(timebuf, sizeof(timebuf), "%A, %d %B %Y @ %H:%M:%S %Z", tm); + const auto timestr = Anope::printf("%s (%lu)", timebuf, Anope::CurTime); + IRCD->SendNumeric(RPL_TIME, source.GetSource(), Me->GetName(), timestr); } -void Topic::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Topic::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { Channel *c = Channel::Find(params[0]); if (c) @@ -475,28 +454,29 @@ void Topic::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) return; } -void Version::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Version::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { Module *enc = ModuleManager::FindFirstOf(ENCRYPTION); - IRCD->SendNumeric(351, source.GetSource(), "Anope-%s %s :%s -(%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)", Anope::VersionBuildString().c_str()); + IRCD->SendNumeric(RPL_VERSION, source.GetSource(), "Anope-" + Anope::Version(), Me->GetName(), Anope::printf("%s -(%s) -- %s", + IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)", Anope::VersionBuildString().c_str())); } -void Whois::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) +void Whois::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) { User *u = User::Find(params[0]); if (u && u->server == Me) { const BotInfo *bi = BotInfo::Find(u->GetUID()); - IRCD->SendNumeric(311, source.GetSource(), "%s %s %s * :%s", u->nick.c_str(), u->GetIdent().c_str(), u->host.c_str(), u->realname.c_str()); + IRCD->SendNumeric(RPL_WHOISUSER, source.GetSource(), u->nick, u->GetIdent(), u->host, '*', u->realname); if (bi) - IRCD->SendNumeric(307, source.GetSource(), "%s :is a registered nick", bi->nick.c_str()); - IRCD->SendNumeric(312, source.GetSource(), "%s %s :%s", u->nick.c_str(), Me->GetName().c_str(), Config->GetBlock("serverinfo")->Get<const Anope::string>("description").c_str()); + IRCD->SendNumeric(RPL_WHOISREGNICK, source.GetSource(), bi->nick, "is a registered nick"); + IRCD->SendNumeric(RPL_WHOISSERVER, source.GetSource(), u->nick, Me->GetName(), Config->GetBlock("serverinfo").Get<const Anope::string>("description")); if (bi) - IRCD->SendNumeric(317, source.GetSource(), "%s %ld %ld :seconds idle, signon time", bi->nick.c_str(), static_cast<long>(Anope::CurTime - bi->lastmsg), static_cast<long>(bi->signon)); - IRCD->SendNumeric(313, source.GetSource(), "%s :is a Network Service", u->nick.c_str()); - IRCD->SendNumeric(318, source.GetSource(), "%s :End of /WHOIS list.", u->nick.c_str()); + IRCD->SendNumeric(RPL_WHOISIDLE, source.GetSource(), bi->nick, Anope::CurTime - bi->lastmsg, bi->signon, "seconds idle, signon time"); + IRCD->SendNumeric(RPL_WHOISOPERATOR, source.GetSource(), u->nick, "is a Network Service"); + IRCD->SendNumeric(RPL_ENDOFWHOIS, source.GetSource(), u->nick, "End of /WHOIS list."); } else - IRCD->SendNumeric(401, source.GetSource(), "%s :No such user.", params[0].c_str()); + IRCD->SendNumeric(ERR_NOSUCHNICK, source.GetSource(), params[0], "No such user."); } diff --git a/src/misc.cpp b/src/misc.cpp index 3ffab7199..a472f5b15 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -19,15 +19,19 @@ #include "regexpr.h" #include "sockets.h" -#include <errno.h> -#include <sys/types.h> +#include <cerrno> +#include <climits> +#include <numeric> +#include <random> +#include <filesystem> #include <sys/stat.h> +#include <sys/types.h> #ifndef _WIN32 #include <sys/socket.h> #include <netdb.h> #endif -NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(true), desc(descending) +NumberList::NumberList(const Anope::string &list, bool descending) : desc(descending) { Anope::string error; commasepstream sep(list); @@ -42,13 +46,12 @@ NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(tr if (t == Anope::string::npos) { - try + if (auto num = Anope::TryConvert<unsigned>(token, &error)) { - unsigned num = convertTo<unsigned>(token, error, false); if (error.empty()) - numbers.insert(num); + numbers.insert(num.value()); } - catch (const ConvertException &) + else { error = "1"; } @@ -65,15 +68,17 @@ NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(tr else { Anope::string error2; - try + auto n1 = Anope::TryConvert<unsigned>(token.substr(0, t), &error); + auto n2 = Anope::TryConvert<unsigned>(token.substr(t + 1), &error); + if (n1.has_value() && n2.has_value()) { - unsigned num1 = convertTo<unsigned>(token.substr(0, t), error, false); - unsigned num2 = convertTo<unsigned>(token.substr(t + 1), error2, false); + auto num1 = n1.value(); + auto num2 = n2.value(); if (error.empty() && error2.empty()) for (unsigned i = num1; i <= num2; ++i) numbers.insert(i); } - catch (const ConvertException &) + else { error = "1"; } @@ -90,10 +95,6 @@ NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(tr } while (sep.GetToken(token)); } -NumberList::~NumberList() -{ -} - void NumberList::Process() { if (!is_valid) @@ -106,8 +107,8 @@ void NumberList::Process() } else { - for (std::set<unsigned>::iterator it = numbers.begin(), it_end = numbers.end(); it != it_end; ++it) - this->HandleNumber(*it); + for (unsigned int number : numbers) + this->HandleNumber(number); } } @@ -145,29 +146,31 @@ void ListFormatter::Process(std::vector<Anope::string> &buffer) std::vector<Anope::string> tcolumns; std::map<Anope::string, size_t> lengths; std::set<Anope::string> breaks; - for (unsigned i = 0; i < this->columns.size(); ++i) + for (const auto &column : this->columns) { - tcolumns.push_back(Language::Translate(this->nc, this->columns[i].c_str())); - lengths[this->columns[i]] = tcolumns[i].length(); + tcolumns.emplace_back(Language::Translate(this->nc, column.c_str())); + lengths[column] = column.length(); } - for (unsigned i = 0; i < this->entries.size(); ++i) + for (auto &entry : this->entries) { - ListEntry &e = this->entries[i]; - for (unsigned j = 0; j < this->columns.size(); ++j) - if (e[this->columns[j]].length() > lengths[this->columns[j]]) - lengths[this->columns[j]] = e[this->columns[j]].length(); + for (const auto &column : this->columns) + { + if (entry[column].length() > lengths[column]) + lengths[column] = entry[column].length(); + } } - unsigned length = 0; - for (std::map<Anope::string, size_t>::iterator it = lengths.begin(), it_end = lengths.end(); it != it_end; ++it) + const auto max_length = Config->GetBlock("options").Get<size_t>("linelength", "120"); + unsigned total_length = 0; + for (const auto &[column, length] : lengths) { - /* Break lines at 80 chars */ - if (length > 80) + // Break lines that are getting too long. + if (total_length > max_length) { - breaks.insert(it->first); - length = 0; + breaks.insert(column); + total_length = 0; } else - length += it->second; + total_length += length; } /* Only put a list header if more than 1 column */ @@ -191,10 +194,8 @@ void ListFormatter::Process(std::vector<Anope::string> &buffer) buffer.push_back(s); } - for (unsigned i = 0; i < this->entries.size(); ++i) + for (auto &entry : this->entries) { - ListEntry &e = this->entries[i]; - Anope::string s; for (unsigned j = 0; j < this->columns.size(); ++j) { @@ -205,16 +206,16 @@ void ListFormatter::Process(std::vector<Anope::string> &buffer) } else if (!s.empty()) s += " "; - s += e[this->columns[j]]; + s += entry[this->columns[j]]; if (j + 1 != this->columns.size()) - for (unsigned k = e[this->columns[j]].length(); k < lengths[this->columns[j]]; ++k) + for (unsigned k = entry[this->columns[j]].length(); k < lengths[this->columns[j]]; ++k) s += " "; } buffer.push_back(s); } } -InfoFormatter::InfoFormatter(NickCore *acc) : nc(acc), longest(0) +InfoFormatter::InfoFormatter(NickCore *acc) : nc(acc) { } @@ -222,23 +223,23 @@ void InfoFormatter::Process(std::vector<Anope::string> &buffer) { buffer.clear(); - for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it) + for (const auto &[key, value] : this->replies) { Anope::string s; - for (unsigned i = it->first.length(); i < this->longest; ++i) + for (unsigned i = key.length(); i < this->longest; ++i) s += " "; - s += it->first + ": " + Language::Translate(this->nc, it->second.c_str()); + s += key + ": " + Language::Translate(this->nc, value.c_str()); buffer.push_back(s); } } -Anope::string& InfoFormatter::operator[](const Anope::string &key) +Anope::string &InfoFormatter::operator[](const Anope::string &key) { Anope::string tkey = Language::Translate(this->nc, key.c_str()); if (tkey.length() > this->longest) this->longest = tkey.length(); - this->replies.push_back(std::make_pair(tkey, "")); + this->replies.emplace_back(tkey, ""); return this->replies.back().second; } @@ -246,11 +247,11 @@ void InfoFormatter::AddOption(const Anope::string &opt) { Anope::string options = Language::Translate(this->nc, "Options"); Anope::string *optstr = NULL; - for (std::vector<std::pair<Anope::string, Anope::string> >::iterator it = this->replies.begin(), it_end = this->replies.end(); it != it_end; ++it) + for (auto &[option, value] : this->replies) { - if (it->first == options) + if (option == options) { - optstr = &it->second; + optstr = &value; break; } } @@ -265,10 +266,7 @@ void InfoFormatter::AddOption(const Anope::string &opt) bool Anope::IsFile(const Anope::string &filename) { struct stat fileinfo; - if (!stat(filename.c_str(), &fileinfo)) - return true; - - return false; + return stat(filename.c_str(), &fileinfo) == 0; } time_t Anope::DoTime(const Anope::string &s) @@ -276,37 +274,28 @@ time_t Anope::DoTime(const Anope::string &s) if (s.empty()) return 0; - int amount = 0; Anope::string end; - - try + auto amount = Anope::Convert<int>(s, -1, &end); + if (!end.empty()) { - amount = convertTo<int>(s, end, false); - if (!end.empty()) + switch (end[0]) { - switch (end[0]) - { - case 's': - return amount; - case 'm': - return amount * 60; - case 'h': - return amount * 3600; - case 'd': - return amount * 86400; - case 'w': - return amount * 86400 * 7; - case 'y': - return amount * 86400 * 365; - default: - break; - } + case 's': + return amount; + case 'm': + return amount * 60; + case 'h': + return amount * 3600; + case 'd': + return amount * 86400; + case 'w': + return amount * 86400 * 7; + case 'y': + return amount * 86400 * 365; + default: + break; } } - catch (const ConvertException &) - { - amount = -1; - } return amount; } @@ -320,43 +309,39 @@ Anope::string Anope::Duration(time_t t, const NickCore *nc) time_t minutes = (t / 60) % 60; time_t seconds = (t) % 60; - if (!years && !days && !hours && !minutes) - return stringify(seconds) + " " + (seconds != 1 ? Language::Translate(nc, _("seconds")) : Language::Translate(nc, _("second"))); - else + Anope::string buffer; + if (years) { - bool need_comma = false; - Anope::string buffer; - if (years) - { - buffer = stringify(years) + " " + (years != 1 ? Language::Translate(nc, _("years")) : Language::Translate(nc, _("year"))); - need_comma = true; - } - if (days) - { - buffer += need_comma ? ", " : ""; - buffer += stringify(days) + " " + (days != 1 ? Language::Translate(nc, _("days")) : Language::Translate(nc, _("day"))); - need_comma = true; - } - if (hours) - { - buffer += need_comma ? ", " : ""; - buffer += stringify(hours) + " " + (hours != 1 ? Language::Translate(nc, _("hours")) : Language::Translate(nc, _("hour"))); - need_comma = true; - } - if (minutes) - { - buffer += need_comma ? ", " : ""; - buffer += stringify(minutes) + " " + (minutes != 1 ? Language::Translate(nc, _("minutes")) : Language::Translate(nc, _("minute"))); - } - return buffer; + buffer = Anope::printf(Language::Translate(nc, years, N_("%lld year", "%lld years")), (long long)years); + } + if (days) + { + buffer += buffer.empty() ? "" : ", "; + buffer += Anope::printf(Language::Translate(nc, days, N_("%lld day", "%lld days")), (long long)days); + } + if (hours) + { + buffer += buffer.empty() ? "" : ", "; + buffer += Anope::printf(Language::Translate(nc, hours, N_("%lld hour", "%lld hours")), (long long)hours); + } + if (minutes) + { + buffer += buffer.empty() ? "" : ", "; + buffer += Anope::printf(Language::Translate(nc, minutes, N_("%lld minute", "%lld minutes")), (long long)minutes); + } + if (seconds || buffer.empty()) + { + buffer += buffer.empty() ? "" : ", "; + buffer += Anope::printf(Language::Translate(nc, seconds, N_("%lld second", "%lld seconds")), (long long)seconds); } + return buffer; } Anope::string Anope::strftime(time_t t, const NickCore *nc, bool short_output) { tm tm = *localtime(&t); char buf[BUFSIZE]; - strftime(buf, sizeof(buf), Language::Translate(nc, _("%b %d %H:%M:%S %Y %Z")), &tm); + strftime(buf, sizeof(buf), Language::Translate(nc, _("%b %d %Y %H:%M:%S %Z")), &tm); if (short_output) return buf; if (t < Anope::CurTime) @@ -371,36 +356,28 @@ Anope::string Anope::Expires(time_t expires, const NickCore *nc) { if (!expires) return Language::Translate(nc, NO_EXPIRE); - else if (expires <= Anope::CurTime) + + if (expires <= Anope::CurTime) return Language::Translate(nc, _("expires momentarily")); - else - { - char buf[256]; - time_t diff = expires - Anope::CurTime + 59; - if (diff >= 86400) - { - int days = diff / 86400; - snprintf(buf, sizeof(buf), Language::Translate(nc, days == 1 ? _("expires in %d day") : _("expires in %d days")), days); - } - else - { - if (diff <= 3600) - { - int minutes = diff / 60; - snprintf(buf, sizeof(buf), Language::Translate(nc, minutes == 1 ? _("expires in %d minute") : _("expires in %d minutes")), minutes); - } - else - { - int hours = diff / 3600, minutes; - diff -= hours * 3600; - minutes = diff / 60; - snprintf(buf, sizeof(buf), Language::Translate(nc, hours == 1 && minutes == 1 ? _("expires in %d hour, %d minute") : (hours == 1 && minutes != 1 ? _("expires in %d hour, %d minutes") : (hours != 1 && minutes == 1 ? _("expires in %d hours, %d minute") : _("expires in %d hours, %d minutes")))), hours, minutes); - } - } + // This will get inlined when compiled with optimisations. + auto nearest = [](auto timeleft, auto roundto) { + if ((timeleft % roundto) <= (roundto / 2)) + return timeleft - (timeleft % roundto); + return timeleft - (timeleft % roundto) + roundto; + }; - return buf; - } + // In order to get a shorter result we round to the nearest period. + auto timeleft = expires - Anope::CurTime; + if (timeleft >= 31536000) + timeleft = nearest(timeleft, 86400); // Nearest day if its more than a year + else if (timeleft >= 86400) + timeleft = nearest(timeleft, 3600); // Nearest hour if its more than a day + else if (timeleft >= 3600) + timeleft = nearest(timeleft, 60); // Nearest minute if its more than an hour + + auto duration = Anope::Duration(timeleft, nc); + return Anope::printf(Language::Translate(nc, _("expires in %s")), duration.c_str()); } bool Anope::Match(const Anope::string &str, const Anope::string &mask, bool case_sensitive, bool use_regex) @@ -415,7 +392,7 @@ bool Anope::Match(const Anope::string &str, const Anope::string &mask, bool case if (r == NULL || r->GetExpression() != stripped_mask) { - ServiceReference<RegexProvider> provider("Regex", Config->GetBlock("options")->Get<const Anope::string>("regexengine")); + ServiceReference<RegexProvider> provider("Regex", Config->GetBlock("options").Get<const Anope::string>("regexengine")); if (provider) { try @@ -507,29 +484,11 @@ bool Anope::Match(const Anope::string &str, const Anope::string &mask, bool case return m == mask_len; } -void Anope::Encrypt(const Anope::string &src, Anope::string &dest) +bool Anope::Encrypt(const Anope::string &src, Anope::string &dest) { EventReturn MOD_RESULT; FOREACH_RESULT(OnEncrypt, MOD_RESULT, (src, dest)); - static_cast<void>(MOD_RESULT); -} - -bool Anope::Decrypt(const Anope::string &src, Anope::string &dest) -{ - size_t pos = src.find(':'); - if (pos == Anope::string::npos) - { - Log() << "Error: Anope::Decrypt() called with invalid password string (" << src << ")"; - return false; - } - Anope::string hashm(src.begin(), src.begin() + pos); - - EventReturn MOD_RESULT; - FOREACH_RESULT(OnDecrypt, MOD_RESULT, (hashm, src, dest)); - if (MOD_RESULT == EVENT_ALLOW) - return true; - - return false; + return MOD_RESULT == EVENT_ALLOW &&!dest.empty(); } Anope::string Anope::printf(const char *fmt, ...) @@ -602,7 +561,7 @@ int Anope::LastErrorCode() #endif } -const Anope::string Anope::LastError() +Anope::string Anope::LastError() { #ifndef _WIN32 return strerror(errno); @@ -619,32 +578,35 @@ const Anope::string Anope::LastError() Anope::string Anope::Version() { #ifdef VERSION_GIT - return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA + " (" + VERSION_GIT + ")"; + return Anope::ToString(VERSION_MAJOR) + "." + Anope::ToString(VERSION_MINOR) + "." + Anope::ToString(VERSION_PATCH) + VERSION_EXTRA + " (" + VERSION_GIT + ")"; #else - return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH) + VERSION_EXTRA; + return Anope::ToString(VERSION_MAJOR) + "." + Anope::ToString(VERSION_MINOR) + "." + Anope::ToString(VERSION_PATCH) + VERSION_EXTRA; #endif } Anope::string Anope::VersionShort() { - return stringify(VERSION_MAJOR) + "." + stringify(VERSION_MINOR) + "." + stringify(VERSION_PATCH); + return Anope::ToString(VERSION_MAJOR) + "." + Anope::ToString(VERSION_MINOR) + "." + Anope::ToString(VERSION_PATCH); } Anope::string Anope::VersionBuildString() { -#ifdef REPRODUCIBLE_BUILD - Anope::string s = "build #" + stringify(BUILD); +#if REPRODUCIBLE_BUILD + Anope::string s = "build #" + Anope::ToString(BUILD); #else - Anope::string s = "build #" + stringify(BUILD) + ", compiled " + Anope::compiled; + Anope::string s = "build #" + Anope::ToString(BUILD) + ", compiled " + Anope::compiled; #endif Anope::string flags; -#ifdef DEBUG_BUILD +#if DEBUG_BUILD flags += "D"; #endif #ifdef VERSION_GIT flags += "G"; #endif +#if REPRODUCIBLE_BUILD + flags += "R" +#endif #ifdef _WIN32 flags += "W"; #endif @@ -770,6 +732,145 @@ Anope::string Anope::Random(size_t len) }; Anope::string buf; for (size_t i = 0; i < len; ++i) - buf.append(chars[rand() % sizeof(chars)]); + buf.append(chars[Anope::RandomNumber() % sizeof(chars)]); return buf; } + +int Anope::RandomNumber() +{ + static std::random_device device; + static std::mt19937 engine(device()); + static std::uniform_int_distribution<int> dist(INT_MIN, INT_MAX); + return dist(engine); +} + +// Implementation of https://en.wikipedia.org/wiki/Levenshtein_distance +size_t Anope::Distance(const Anope::string &s1, const Anope::string &s2) +{ + if (s1.empty()) + return s2.length(); + if (s2.empty()) + return s1.length(); + + std::vector<size_t> costs(s2.length() + 1); + std::iota(costs.begin(), costs.end(), 0); + + size_t i = 0; + for (const auto c1 : s1) + { + costs[0] = i + 1; + size_t corner = i; + size_t j = 0; + for (const auto &c2 : s2) + { + size_t upper = costs[j + 1]; + if (c1 == c2) + costs[j + 1] = corner; + else + { + size_t t = upper < corner ? upper : corner; + costs[j + 1] = (costs[j] < t ? costs[j] : t) + 1; + } + corner = upper; + j++; + } + i++; + } + return costs[s2.length()]; +} + +void Anope::UpdateTime() +{ +#ifdef _WIN32 + SYSTEMTIME st; + GetSystemTime(&st); + + CurTime = time(nullptr); + CurTimeNs = st.wMilliseconds; +#elif HAVE_CLOCK_GETTIME + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + + CurTime = ts.tv_sec; + CurTimeNs = ts.tv_nsec; +#else + struct timeval tv; + gettimeofday(&tv, nullptr); + + CurTime = tv.tv_sec; + CurTimeNs = tv.tv_usec * 1000; +#endif +} + +Anope::string Anope::Expand(const Anope::string &base, const Anope::string &fragment) +{ + if (fragment.empty()) + return ""; // We can't expand an empty fragment. + + // The fragment is an absolute path, don't modify it. + if (std::filesystem::path(fragment.str()).is_absolute()) + return fragment; + +#ifdef _WIN32 + static constexpr const char separator = '\\'; +#else + static constexpr const char separator = '/'; +#endif + + // The fragment is relative to a home directory, expand that. + if (!fragment.compare(0, 2, "~/", 2)) + { + const auto *homedir = getenv("HOME"); + if (homedir && *homedir) + return Anope::printf("%s%c%s", homedir, separator, fragment.c_str() + 2); + } + + return Anope::printf("%s%c%s", base.c_str(), separator, fragment.c_str()); +} + +Anope::string Anope::FormatCTCP(const Anope::string &name, const Anope::string &value) +{ + if (value.empty()) + return Anope::printf("\1%s\1", name.c_str()); + + return Anope::printf("\1%s %s\1", name.c_str(), value.c_str()); +} + +bool Anope::ParseCTCP(const Anope::string &text, Anope::string &name, Anope::string &body) +{ + // According to draft-oakley-irc-ctcp-02 a valid CTCP must begin with SOH and + // contain at least one octet which is not NUL, SOH, CR, LF, or SPACE. As most + // of these are restricted at the protocol level we only need to check for SOH + // and SPACE. + if (text.length() < 2 || text[0] != '\x1' || text[1] == '\x1' || text[1] == ' ') + { + name.clear(); + body.clear(); + return false; + } + + auto end_of_name = text.find(' ', 2); + auto end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0; + if (end_of_name == std::string::npos) + { + // The CTCP only contains a name. + name = text.substr(1, text.length() - 1 - end_of_ctcp); + body.clear(); + return true; + } + + // The CTCP contains a name and a body. + name = text.substr(1, end_of_name - 1); + + auto start_of_body = text.find_first_not_of(' ', end_of_name + 1); + if (start_of_body == std::string::npos) + { + // The CTCP body is provided but empty. + body.clear(); + return true; + } + + // The CTCP body provided was non-empty. + body = text.substr(start_of_body, text.length() - start_of_body - end_of_ctcp); + return true; +} diff --git a/src/modes.cpp b/src/modes.cpp index 0bd2019c7..fdaae7716 100644 --- a/src/modes.cpp +++ b/src/modes.cpp @@ -39,16 +39,14 @@ static std::vector<ChannelModeStatus *> ChannelModesByStatus; /* Number of generic modes we support */ unsigned ModeManager::GenericChannelModes = 0, ModeManager::GenericUserModes = 0; -struct StackerInfo +struct StackerInfo final { /* Modes to be added */ std::list<std::pair<Mode *, Anope::string> > AddModes; /* Modes to be deleted */ std::list<std::pair<Mode *, Anope::string> > DelModes; /* Bot this is sent from */ - BotInfo *bi; - - StackerInfo() : bi(NULL) { } + BotInfo *bi = nullptr; /** Add a mode to this object * @param mode The mode @@ -58,10 +56,6 @@ struct StackerInfo void AddMode(Mode *mode, bool set, const Anope::string ¶m); }; -ChannelStatus::ChannelStatus() -{ -} - ChannelStatus::ChannelStatus(const Anope::string &m) : modes(m) { } @@ -101,9 +95,9 @@ Anope::string ChannelStatus::BuildModePrefixList() const { Anope::string ret; - for (size_t i = 0; i < modes.length(); ++i) + for (auto mode : modes) { - ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]); + ChannelMode *cm = ModeManager::FindChannelModeByChar(mode); if (cm != NULL && cm->type == MODE_STATUS) { ChannelModeStatus *cms = anope_dynamic_static_cast<ChannelModeStatus *>(cm); @@ -118,10 +112,6 @@ Mode::Mode(const Anope::string &mname, ModeClass mcl, char mch, ModeType mt) : n { } -Mode::~Mode() -{ -} - bool Mode::CanSet(User *u) const { return true; @@ -154,9 +144,9 @@ ChannelMode *ChannelMode::Wrap(Anope::string ¶m) ChannelMode *ChannelMode::Unwrap(Anope::string ¶m) { - for (unsigned i = 0; i < listeners.size(); ++i) + for (auto *listener : listeners) { - ChannelMode *cm = listeners[i]->Unwrap(this, param); + ChannelMode *cm = listener->Unwrap(this, param); if (cm != this) return cm; } @@ -243,10 +233,7 @@ bool UserModeNoone::CanSet(User *u) const bool ChannelModeKey::IsValid(Anope::string &value) const { - if (!value.empty() && value.find(':') == Anope::string::npos && value.find(',') == Anope::string::npos) - return true; - - return false; + return !value.empty() && value.find(':') == Anope::string::npos && value.find(',') == Anope::string::npos; } bool ChannelModeOperOnly::CanSet(User *u) const @@ -307,13 +294,14 @@ void StackerInfo::AddMode(Mode *mode, bool set, const Anope::string ¶m) } /* Add this mode and its param to our list */ - list->push_back(std::make_pair(mode, param)); + list->emplace_back(mode, param); } -static class ModePipe : public Pipe +static class ModePipe final + : public Pipe { - public: - void OnNotify() +public: + void OnNotify() override { ModeManager::ProcessModes(); } @@ -330,7 +318,7 @@ static StackerInfo *GetInfo(List &l, Object *o) if (it != l.end()) return it->second; - StackerInfo *s = new StackerInfo(); + auto *s = new StackerInfo(); l[o] = s; return s; } @@ -339,27 +327,33 @@ static StackerInfo *GetInfo(List &l, Object *o) * @param info The stacker info for a channel or user * @return a list of strings */ -static std::list<Anope::string> BuildModeStrings(StackerInfo *info) +static auto BuildModeStrings(StackerInfo *info) { - std::list<Anope::string> ret; + std::list<std::pair<Anope::string, std::vector<Anope::string>>> ret; std::list<std::pair<Mode *, Anope::string> >::iterator it, it_end; - Anope::string buf = "+", parambuf; + Anope::string buf = "+"; + std::vector<Anope::string> parambuf; unsigned NModes = 0; + size_t paramlen = 0; for (it = info->AddModes.begin(), it_end = info->AddModes.end(); it != it_end; ++it) { - if (++NModes > IRCD->MaxModes || (buf.length() + parambuf.length() > IRCD->MaxLine - 100)) // Leave room for command, channel, etc + if ((IRCD->MaxModes && ++NModes > IRCD->MaxModes) || (IRCD->MaxLine && buf.length() + paramlen > IRCD->MaxLine - 100)) // Leave room for command, channel, etc { - ret.push_back(buf + parambuf); + ret.push_back({buf, parambuf}); buf = "+"; parambuf.clear(); + paramlen = 0; NModes = 1; } buf += it->first->mchar; if (!it->second.empty()) - parambuf += " " + it->second; + { + parambuf.push_back(it->second); + paramlen += it->second.length() + 1; + } } if (buf[buf.length() - 1] == '+') @@ -368,25 +362,29 @@ static std::list<Anope::string> BuildModeStrings(StackerInfo *info) buf += "-"; for (it = info->DelModes.begin(), it_end = info->DelModes.end(); it != it_end; ++it) { - if (++NModes > IRCD->MaxModes || (buf.length() + parambuf.length() > IRCD->MaxLine - 100)) // Leave room for command, channel, etc + if ((IRCD->MaxModes && ++NModes > IRCD->MaxModes) || (IRCD->MaxLine && buf.length() + paramlen > IRCD->MaxLine - 100)) // Leave room for command, channel, etc { - ret.push_back(buf + parambuf); + ret.push_back({buf, parambuf}); buf = "-"; parambuf.clear(); + paramlen = 0; NModes = 1; } buf += it->first->mchar; if (!it->second.empty()) - parambuf += " " + it->second; + { + parambuf.push_back(it->second); + paramlen += it->second.length() + 1; + } } if (buf[buf.length() - 1] == '-') buf.erase(buf.length() - 1); if (!buf.empty()) - ret.push_back(buf + parambuf); + ret.push_back({buf, parambuf}); return ret; } @@ -400,7 +398,7 @@ bool ModeManager::AddUserMode(UserMode *um) if (um->name.empty()) { - um->name = stringify(++GenericUserModes); + um->name = Anope::ToString(++GenericUserModes); Log() << "ModeManager: Added generic support for user mode " << um->mchar; } @@ -427,7 +425,7 @@ bool ModeManager::AddChannelMode(ChannelMode *cm) if (cm->name.empty()) { - cm->name = stringify(++GenericChannelModes); + cm->name = Anope::ToString(++GenericChannelModes); Log() << "ModeManager: Added generic support for channel mode " << cm->mchar; } @@ -456,8 +454,8 @@ bool ModeManager::AddChannelMode(ChannelMode *cm) FOREACH_MOD(OnChannelModeAdd, (cm)); - for (unsigned int i = 0; i < ChannelModes.size(); ++i) - ChannelModes[i]->Check(); + for (auto *cmode : ChannelModes) + cmode->Check(); return true; } @@ -589,7 +587,7 @@ const std::vector<ChannelModeStatus *> &ModeManager::GetStatusChannelModesByRank return ChannelModesByStatus; } -static struct StatusSort +static struct StatusSort final { bool operator()(ChannelModeStatus *cm1, ChannelModeStatus *cm2) const { @@ -600,10 +598,8 @@ static struct StatusSort void ModeManager::RebuildStatusModes() { ChannelModesByStatus.clear(); - for (unsigned j = 0; j < ChannelModesIdx.size(); ++j) + for (auto *cm : ChannelModesIdx) { - ChannelMode *cm = ChannelModesIdx[j]; - if (cm && cm->type == MODE_STATUS && std::find(ChannelModesByStatus.begin(), ChannelModesByStatus.end(), cm) == ChannelModesByStatus.end()) ChannelModesByStatus.push_back(anope_dynamic_static_cast<ChannelModeStatus *>(cm)); } @@ -640,30 +636,22 @@ void ModeManager::ProcessModes() { if (!UserStackerObjects.empty()) { - for (std::map<User *, StackerInfo *>::const_iterator it = UserStackerObjects.begin(), it_end = UserStackerObjects.end(); it != it_end; ++it) + for (const auto &[u, s] : UserStackerObjects) { - User *u = it->first; - StackerInfo *s = it->second; - - std::list<Anope::string> ModeStrings = BuildModeStrings(s); - for (std::list<Anope::string>::iterator lit = ModeStrings.begin(), lit_end = ModeStrings.end(); lit != lit_end; ++lit) - IRCD->SendMode(s->bi, u, "%s", lit->c_str()); - delete it->second; + for (const auto &modestr : BuildModeStrings(s)) + IRCD->SendModeInternal(s->bi, u, modestr.first, modestr.second); + delete s; } UserStackerObjects.clear(); } if (!ChannelStackerObjects.empty()) { - for (std::map<Channel *, StackerInfo *>::const_iterator it = ChannelStackerObjects.begin(), it_end = ChannelStackerObjects.end(); it != it_end; ++it) + for (const auto &[c, s] : ChannelStackerObjects) { - Channel *c = it->first; - StackerInfo *s = it->second; - - std::list<Anope::string> ModeStrings = BuildModeStrings(s); - for (std::list<Anope::string>::iterator lit = ModeStrings.begin(), lit_end = ModeStrings.end(); lit != lit_end; ++lit) - IRCD->SendMode(s->bi, c, "%s", lit->c_str()); - delete it->second; + for (const auto &modestr : BuildModeStrings(s)) + IRCD->SendModeInternal(s->bi, c, modestr.first, modestr.second); + delete s; } ChannelStackerObjects.clear(); } @@ -676,9 +664,8 @@ static void StackerDel(std::map<T *, StackerInfo *> &map, T *obj) if (it != map.end()) { StackerInfo *si = it->second; - std::list<Anope::string> ModeStrings = BuildModeStrings(si); - for (std::list<Anope::string>::iterator lit = ModeStrings.begin(), lit_end = ModeStrings.end(); lit != lit_end; ++lit) - IRCD->SendMode(si->bi, obj, "%s", lit->c_str()); + for (const auto &modestr : BuildModeStrings(si)) + IRCD->SendModeInternal(si->bi, obj, modestr.first, modestr.second); delete si; map.erase(it); @@ -742,7 +729,7 @@ void ModeManager::StackerDel(Mode *m) } } -Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh), cidr_len(0), family(0) +Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh) { Anope::string n, u, h; @@ -796,22 +783,15 @@ Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh &cidr_range = this->host.substr(sl + 1); sockaddrs addr(cidr_ip); - - try + auto range = Anope::TryConvert<unsigned short>(cidr_range); + if (addr.valid() && range.has_value()) { - 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. */ - - this->host = cidr_ip; - this->family = addr.family(); + this->cidr_len = range.value(); + this->host = cidr_ip; + this->family = addr.family(); - Log(LOG_DEBUG) << "Ban " << mask << " has cidr " << this->cidr_len; - } + Log(LOG_DEBUG) << "Ban " << mask << " has cidr " << this->cidr_len; } - catch (const ConvertException &) { } } } @@ -819,12 +799,12 @@ Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh this->real.clear(); } -const Anope::string Entry::GetMask() const +Anope::string Entry::GetMask() const { return this->mask; } -const Anope::string Entry::GetNUHMask() const +Anope::string Entry::GetNUHMask() const { Anope::string n = nick.empty() ? "*" : nick, u = user.empty() ? "*" : user, @@ -835,11 +815,11 @@ const Anope::string Entry::GetNUHMask() const { case AF_INET: if (cidr_len <= 32) - c = "/" + stringify(cidr_len); + c = "/" + Anope::ToString(cidr_len); break; case AF_INET6: if (cidr_len <= 128) - c = "/" + stringify(cidr_len); + c = "/" + Anope::ToString(cidr_len); break; } @@ -855,8 +835,7 @@ bool Entry::Matches(User *u, bool full) const if (cm != NULL && cm->type == MODE_LIST) { ChannelModeList *cml = anope_dynamic_static_cast<ChannelModeList *>(cm); - if (cml->Matches(u, this)) - return true; + return cml->Matches(u, this); } } diff --git a/src/module.cpp b/src/module.cpp index ba4076306..6b6a4e36c 100644 --- a/src/module.cpp +++ b/src/module.cpp @@ -11,7 +11,7 @@ #include "language.h" #include "account.h" -#ifdef GETTEXT_FOUND +#if HAVE_LOCALIZATION # include <libintl.h> #endif @@ -39,14 +39,14 @@ Module::Module(const Anope::string &modname, const Anope::string &, ModType modt ModuleManager::Modules.push_back(this); -#if GETTEXT_FOUND - for (unsigned i = 0; i < Language::Languages.size(); ++i) +#if HAVE_LOCALIZATION + for (const auto &language : Language::Languages) { /* Remove .UTF-8 or any other suffix */ Anope::string lang; - sepstream(Language::Languages[i], '.').GetToken(lang); + sepstream(language, '.').GetToken(lang); - if (Anope::IsFile(Anope::LocaleDir + "/" + lang + "/LC_MESSAGES/" + modname + ".mo")) + if (Anope::IsFile(Anope::ExpandLocale(lang + "/LC_MESSAGES/" + modname + ".mo"))) { if (!bindtextdomain(this->name.c_str(), Anope::LocaleDir.c_str())) Log() << "Error calling bindtextdomain, " << Anope::LastError(); @@ -75,7 +75,7 @@ Module::~Module() if (it != ModuleManager::Modules.end()) ModuleManager::Modules.erase(it); -#if GETTEXT_FOUND +#if HAVE_LOCALIZATION std::vector<Anope::string>::iterator dit = std::find(Language::Domains.begin(), Language::Domains.end(), this->name); if (dit != Language::Domains.end()) Language::Domains.erase(dit); diff --git a/src/modulemanager.cpp b/src/modulemanager.cpp index 3ea0b2fa4..11a00e363 100644 --- a/src/modulemanager.cpp +++ b/src/modulemanager.cpp @@ -15,39 +15,33 @@ #include <sys/types.h> #include <sys/stat.h> #ifndef _WIN32 -#include <dirent.h> #include <sys/types.h> #include <dlfcn.h> #endif +#include <filesystem> + std::list<Module *> ModuleManager::Modules; std::vector<Module *> ModuleManager::EventHandlers[I_SIZE]; #ifdef _WIN32 void ModuleManager::CleanupRuntimeDirectory() { - Anope::string dirbuf = Anope::DataDir + "/runtime"; + Anope::string dirbuf = Anope::ExpandData("runtime"); Log(LOG_DEBUG) << "Cleaning out Module run time directory (" << dirbuf << ") - this may take a moment, please wait"; - - DIR *dirp = opendir(dirbuf.c_str()); - if (!dirp) + try { - Log(LOG_DEBUG) << "Cannot open directory (" << dirbuf << ")"; - return; + for (const auto &entry : std::filesystem::directory_iterator(dirbuf.str())) + { + if (entry.is_regular_file()) + std::filesystem::remove(entry); + } } - - for (dirent *dp; (dp = readdir(dirp));) + catch (const std::filesystem::filesystem_error &err) { - if (!dp->d_ino) - continue; - if (Anope::string(dp->d_name).equals_cs(".") || Anope::string(dp->d_name).equals_cs("..")) - continue; - Anope::string filebuf = dirbuf + "/" + dp->d_name; - unlink(filebuf.c_str()); + Log(LOG_DEBUG) << "Cannot open directory (" << dirbuf << "): " << err.what(); } - - closedir(dirp); } /** @@ -61,7 +55,7 @@ void ModuleManager::CleanupRuntimeDirectory() */ static ModuleReturn moduleCopyFile(const Anope::string &name, Anope::string &output) { - Anope::string input = Anope::ModuleDir + "/modules/" + name + ".so"; + const auto input = Anope::ExpandModule(name + DLL_EXT); struct stat s; if (stat(input.c_str(), &s) == -1) @@ -139,7 +133,7 @@ ModuleReturn ModuleManager::LoadModule(const Anope::string &modname, User *u) #ifdef _WIN32 /* Generate the filename for the temporary copy of the module */ - Anope::string pbuf = Anope::DataDir + "/runtime/" + modname + ".so.XXXXXX"; + auto pbuf = Anope::ExpandData("runtime/" + modname + DLL_EXT ".XXXXXX"); /* Don't skip return value checking! -GD */ ModuleReturn ret = moduleCopyFile(modname, pbuf); @@ -152,7 +146,7 @@ ModuleReturn ModuleManager::LoadModule(const Anope::string &modname, User *u) return ret; } #else - Anope::string pbuf = Anope::ModuleDir + "/modules/" + modname + ".so"; + const auto pbuf = Anope::ExpandModule(modname + DLL_EXT); #endif dlerror(); @@ -248,7 +242,7 @@ ModuleReturn ModuleManager::LoadModule(const Anope::string &modname, User *u) /* Initialize config */ try { - m->OnReload(Config); + m->OnReload(*Config); } catch (const ModuleException &ex) { @@ -273,8 +267,8 @@ ModuleReturn ModuleManager::LoadModule(const Anope::string &modname, User *u) Log(LOG_DEBUG) << "Module " << modname << " loaded."; /* Attach module to all events */ - for (unsigned i = 0; i < I_SIZE; ++i) - EventHandlers[i].push_back(m); + for (auto &mods : EventHandlers) + mods.push_back(m); m->Prioritize(); @@ -313,10 +307,8 @@ ModuleReturn ModuleManager::UnloadModule(Module *m, User *u) Module *ModuleManager::FindModule(const Anope::string &name) { - for (std::list<Module *>::const_iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it) + for (auto *m : Modules) { - Module *m = *it; - if (m->name.equals_ci(name)) return m; } @@ -326,10 +318,8 @@ Module *ModuleManager::FindModule(const Anope::string &name) Module *ModuleManager::FindFirstOf(ModType type) { - for (std::list<Module *>::const_iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it) + for (auto *m : Modules) { - Module *m = *it; - if (m->type & type) return m; } @@ -358,7 +348,7 @@ void ModuleManager::RequireVersion(int major, int minor, int patch) } } - throw ModuleException("This module requires version " + stringify(major) + "." + stringify(minor) + "." + stringify(patch) + " - this is " + Anope::VersionShort()); + throw ModuleException("This module requires version " + Anope::ToString(major) + "." + Anope::ToString(minor) + "." + Anope::ToString(patch) + " - this is " + Anope::VersionShort()); } ModuleReturn ModuleManager::DeleteModule(Module *m) @@ -395,9 +385,8 @@ ModuleReturn ModuleManager::DeleteModule(Module *m) void ModuleManager::DetachAll(Module *mod) { - for (unsigned i = 0; i < I_SIZE; ++i) + for (auto &mods : EventHandlers) { - std::vector<Module *> &mods = EventHandlers[i]; std::vector<Module *>::iterator it2 = std::find(mods.begin(), mods.end(), mod); if (it2 != mods.end()) mods.erase(it2); @@ -446,43 +435,62 @@ bool ModuleManager::SetPriority(Module *mod, Implementation i, Priority s, Modul { /* Dummy value */ case PRIORITY_DONTCARE: + { swap = false; break; + } /* Module wants to be first, sod everything else */ case PRIORITY_FIRST: + { swap_pos = 0; break; + } /* Module is submissive and wants to be last... awww. */ case PRIORITY_LAST: + { if (EventHandlers[i].empty()) swap_pos = 0; else swap_pos = EventHandlers[i].size() - 1; break; + } /* Place this module after a set of other modules */ case PRIORITY_AFTER: + { /* Find the latest possible position */ swap_pos = 0; swap = false; for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x) + { for (size_t n = 0; n < sz; ++n) + { if (modules[n] && EventHandlers[i][x] == modules[n] && x >= swap_pos && source <= swap_pos) { swap_pos = x; swap = true; } + } + } break; + } /* Place this module before a set of other modules */ case PRIORITY_BEFORE: + { swap_pos = EventHandlers[i].size() - 1; swap = false; for (size_t x = 0, end = EventHandlers[i].size(); x != end; ++x) + { for (size_t n = 0; n < sz; ++n) + { if (modules[n] && EventHandlers[i][x] == modules[n] && x <= swap_pos && source >= swap_pos) { swap = true; swap_pos = x; } + } + } + break; + } } /* Do we need to swap? */ @@ -510,16 +518,17 @@ void ModuleManager::UnloadAll() { std::vector<Anope::string> modules; for (size_t i = 1, j = 0; i != MT_END; j |= i, i <<= 1) - for (std::list<Module *>::iterator it = Modules.begin(), it_end = Modules.end(); it != it_end; ++it) + { + for (auto *m : Modules) { - Module *m = *it; if ((m->type & j) == m->type) modules.push_back(m->name); } + } - for (unsigned i = 0; i < modules.size(); ++i) + for (auto &module : modules) { - Module *m = FindModule(modules[i]); + Module *m = FindModule(module); if (m != NULL) UnloadModule(m, NULL); } diff --git a/src/nickalias.cpp b/src/nickalias.cpp index 68cf165e6..ac33e836a 100644 --- a/src/nickalias.cpp +++ b/src/nickalias.cpp @@ -18,24 +18,24 @@ #include "servers.h" #include "config.h" -Serialize::Checker<nickalias_map> NickAliasList("NickAlias"); +Serialize::Checker<nickalias_map> NickAliasList(NICKALIAS_TYPE); -NickAlias::NickAlias(const Anope::string &nickname, NickCore* nickcore) : Serializable("NickAlias") +NickAlias::NickAlias(const Anope::string &nickname, NickCore *nickcore) + : Serializable(NICKALIAS_TYPE) + , nick(nickname) + , nc(nickcore) { if (nickname.empty()) throw CoreException("Empty nick passed to NickAlias constructor"); else if (!nickcore) throw CoreException("Empty nickcore passed to NickAlias constructor"); - this->time_registered = this->last_seen = Anope::CurTime; - this->nick = nickname; - this->nc = nickcore; nickcore->aliases->push_back(this); + if (this->nick.equals_ci(nickcore->display)) + nickcore->na = this; - size_t old = NickAliasList->size(); - (*NickAliasList)[this->nick] = this; - if (old == NickAliasList->size()) - Log(LOG_DEBUG) << "Duplicate nick " << nickname << " in nickalias table"; + if (!NickAliasList->insert_or_assign(this->nick, this).second) + Log(LOG_DEBUG) << "Duplicate nick " << this->nick << " in NickAlias table"; if (this->nc->o == NULL) { @@ -78,7 +78,7 @@ NickAlias::~NickAlias() NickAliasList->erase(this->nick); } -void NickAlias::SetVhost(const Anope::string &ident, const Anope::string &host, const Anope::string &creator, time_t created) +void NickAlias::SetVHost(const Anope::string &ident, const Anope::string &host, const Anope::string &creator, time_t created) { this->vhost_ident = ident; this->vhost_host = host; @@ -86,7 +86,7 @@ void NickAlias::SetVhost(const Anope::string &ident, const Anope::string &host, this->vhost_created = created; } -void NickAlias::RemoveVhost() +void NickAlias::RemoveVHost() { this->vhost_ident.clear(); this->vhost_host.clear(); @@ -94,27 +94,35 @@ void NickAlias::RemoveVhost() this->vhost_created = 0; } -bool NickAlias::HasVhost() const +bool NickAlias::HasVHost() const { return !this->vhost_host.empty(); } -const Anope::string &NickAlias::GetVhostIdent() const +const Anope::string &NickAlias::GetVHostIdent() const { return this->vhost_ident; } -const Anope::string &NickAlias::GetVhostHost() const +const Anope::string &NickAlias::GetVHostHost() const { return this->vhost_host; } -const Anope::string &NickAlias::GetVhostCreator() const +Anope::string NickAlias::GetVHostMask() const +{ + if (this->GetVHostIdent().empty()) + return this->GetVHostHost(); + + return this->GetVHostIdent() + "@" + this->GetVHostHost(); +} + +const Anope::string &NickAlias::GetVHostCreator() const { return this->vhost_creator; } -time_t NickAlias::GetVhostCreated() const +time_t NickAlias::GetVHostCreated() const { return this->vhost_created; } @@ -131,36 +139,50 @@ NickAlias *NickAlias::Find(const Anope::string &nick) return NULL; } -void NickAlias::Serialize(Serialize::Data &data) const +NickAlias *NickAlias::FindId(uint64_t id) { - data["nick"] << this->nick; - data["last_quit"] << this->last_quit; - data["last_realname"] << this->last_realname; - data["last_usermask"] << this->last_usermask; - data["last_realhost"] << this->last_realhost; - data.SetType("time_registered", Serialize::Data::DT_INT); data["time_registered"] << this->time_registered; - data.SetType("last_seen", Serialize::Data::DT_INT); data["last_seen"] << this->last_seen; - data["nc"] << this->nc->display; - - if (this->HasVhost()) + const auto *nc = NickCore::FindId(id); + return nc ? nc->na : nullptr; +} + +NickAlias::Type::Type() + : Serialize::Type(NICKALIAS_TYPE) +{ +} + +void NickAlias::Type::Serialize(const Serializable *obj, Serialize::Data &data) const +{ + const auto *na = static_cast<const NickAlias *>(obj); + data.Store("nick", na->nick); + data.Store("last_quit", na->last_quit); + data.Store("last_realname", na->last_realname); + data.Store("last_usermask", na->last_usermask); + data.Store("last_realhost", na->last_realhost); + data.Store("time_registered", na->time_registered); + data.Store("last_seen", na->last_seen); + data.Store("ncid", na->nc->GetId()); + + if (na->HasVHost()) { - data["vhost_ident"] << this->GetVhostIdent(); - data["vhost_host"] << this->GetVhostHost(); - data["vhost_creator"] << this->GetVhostCreator(); - data["vhost_time"] << this->GetVhostCreated(); + data.Store("vhost_ident", na->GetVHostIdent()); + data.Store("vhost_host", na->GetVHostHost()); + data.Store("vhost_creator", na->GetVHostCreator()); + data.Store("vhost_time", na->GetVHostCreated()); } - Extensible::ExtensibleSerialize(this, this, data); + Extensible::ExtensibleSerialize(na, na, data); } -Serializable* NickAlias::Unserialize(Serializable *obj, Serialize::Data &data) +Serializable *NickAlias::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { Anope::string snc, snick; + uint64_t sncid = 0; - data["nc"] >> snc; + data["nc"] >> snc; // Deprecated 2.0 field + data["ncid"] >> sncid; data["nick"] >> snick; - NickCore *core = NickCore::Find(snc); + auto *core = sncid ? NickCore::FindId(sncid) : NickCore::Find(snc); if (core == NULL) return NULL; @@ -200,7 +222,7 @@ Serializable* NickAlias::Unserialize(Serializable *obj, Serialize::Data &data) data["vhost_creator"] >> vhost_creator; data["vhost_time"] >> vhost_time; - na->SetVhost(vhost_ident, vhost_host, vhost_creator, vhost_time); + na->SetVHost(vhost_ident, vhost_host, vhost_creator, vhost_time); Extensible::ExtensibleUnserialize(na, na, data); @@ -210,6 +232,9 @@ Serializable* NickAlias::Unserialize(Serializable *obj, Serialize::Data &data) data["extensible:NO_EXPIRE"] >> b; if (b) na->Extend<bool>("NS_NO_EXPIRE"); + + if (na->time_registered < na->nc->time_registered) + na->nc->time_registered = na->time_registered; /* end compat */ return na; diff --git a/src/nickcore.cpp b/src/nickcore.cpp index 5bc15cf50..7ca338aff 100644 --- a/src/nickcore.cpp +++ b/src/nickcore.cpp @@ -15,28 +15,25 @@ #include "config.h" #include <climits> -Serialize::Checker<nickcore_map> NickCoreList("NickCore"); -nickcoreid_map NickCoreIdList; - -NickCore::NickCore(const Anope::string &coredisplay, uint64_t coreid) : Serializable("NickCore"), chanaccess("ChannelInfo"), aliases("NickAlias") +Serialize::Checker<nickcore_map> NickCoreList(NICKCORE_TYPE); +Serialize::Checker<nickcoreid_map> NickCoreIdList(NICKCORE_TYPE); + +NickCore::NickCore(const Anope::string &coredisplay, uint64_t coreid) + : Serializable(NICKCORE_TYPE) + , chanaccess(CHANNELINFO_TYPE) + , id(coreid) + , display(coredisplay) + , aliases(NICKALIAS_TYPE) { if (coredisplay.empty()) throw CoreException("Empty display passed to NickCore constructor"); - this->o = NULL; - this->channelcount = 0; - this->lastmail = 0; - - this->display = coredisplay; - this->id = coreid; + if (!NickCoreList->insert_or_assign(this->display, this).second) + Log(LOG_DEBUG) << "Duplicate account " << this->display << " in NickCore table"; - size_t old = NickCoreList->size(); - (*NickCoreList)[this->display] = this; - if (old == NickCoreList->size()) - Log(LOG_DEBUG) << "Duplicate account " << coredisplay << " in nickcore table?"; - - if (this->id) - NickCoreIdList[this->id] = this; + // Upgrading users may not have an account identifier. + if (this->id && !NickCoreIdList->insert_or_assign(this->id, this).second) + Log(LOG_DEBUG) << "Duplicate account id " << this->id << " in NickCore table"; FOREACH_MOD(OnNickCoreCreate, (this)); } @@ -59,9 +56,7 @@ NickCore::~NickCore() NickCoreList->erase(this->display); if (this->id) - NickCoreIdList.erase(this->id); - - this->ClearAccess(); + NickCoreIdList->erase(this->id); if (!this->memos.memos->empty()) { @@ -71,22 +66,32 @@ NickCore::~NickCore() } } -void NickCore::Serialize(Serialize::Data &data) const +NickCore::Type::Type() + : Serialize::Type(NICKCORE_TYPE) +{ +} + +void NickCore::Type::Serialize(const Serializable *obj, Serialize::Data &data) const { - data["display"] << this->display; - data["uniqueid"] << this->id; - data["pass"] << this->pass; - data["email"] << this->email; - data["language"] << this->language; - for (unsigned i = 0; i < this->access.size(); ++i) - data["access"] << this->access[i] << " "; - data["memomax"] << this->memos.memomax; - for (unsigned i = 0; i < this->memos.ignores.size(); ++i) - data["memoignores"] << this->memos.ignores[i] << " "; - Extensible::ExtensibleSerialize(this, this, data); + const auto *nc = static_cast<const NickCore *>(obj); + data.Store("display", nc->display); + data.Store("uniqueid", nc->id); + data.Store("pass", nc->pass); + data.Store("email", nc->email); + data.Store("language", nc->language); + data.Store("lastmail", nc->lastmail); + data.Store("time_registered", nc->time_registered); + data.Store("memomax", nc->memos.memomax); + + std::ostringstream oss; + for (const auto &ignore : nc->memos.ignores) + oss << ignore << " "; + data.Store("memoignores", oss.str()); + + Extensible::ExtensibleSerialize(nc, nc, data); } -Serializable* NickCore::Unserialize(Serializable *obj, Serialize::Data &data) +Serializable *NickCore::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { NickCore *nc; @@ -104,14 +109,8 @@ Serializable* NickCore::Unserialize(Serializable *obj, Serialize::Data &data) data["pass"] >> nc->pass; data["email"] >> nc->email; data["language"] >> nc->language; - { - Anope::string buf; - data["access"] >> buf; - spacesepstream sep(buf); - nc->access.clear(); - while (sep.GetToken(buf)) - nc->access.push_back(buf); - } + data["lastmail"] >> nc->lastmail; + data["time_registered"] >> nc->time_registered; data["memomax"] >> nc->memos.memomax; { Anope::string buf; @@ -119,7 +118,7 @@ Serializable* NickCore::Unserialize(Serializable *obj, Serialize::Data &data) spacesepstream sep(buf); nc->memos.ignores.clear(); while (sep.GetToken(buf)) - nc->memos.ignores.push_back(buf); + nc->memos.ignores.insert(buf); } Extensible::ExtensibleUnserialize(nc, nc, data); @@ -127,10 +126,6 @@ Serializable* NickCore::Unserialize(Serializable *obj, Serialize::Data &data) /* compat */ bool b; b = false; - data["extensible:SECURE"] >> b; - if (b) - nc->Extend<bool>("NS_SECURE"); - b = false; data["extensible:PRIVATE"] >> b; if (b) nc->Extend<bool>("NS_PRIVATE"); @@ -157,13 +152,35 @@ Serializable* NickCore::Unserialize(Serializable *obj, Serialize::Data &data) b = false; data["extensible:KILLPROTECT"] >> b; if (b) - nc->Extend<bool>("KILLPROTECT"); + nc->Extend<bool>("PROTECT"); + + b = false; + data["KILLPROTECT"] >> b; + if (b) + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", Config->GetModule("nickserv").Get<time_t>("kill", "60s")); + } + b = false; + data["KILL_QUICK"] >> b; + if (b) + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", Config->GetModule("nickserv").Get<time_t>("killquick", "20s")); + } + b = false; + data["KILL_IMMED"] >> b; + if (b) + { + nc->Extend<bool>("PROTECT"); + nc->Extend("PROTECT_AFTER", 0); + } /* end compat */ return nc; } -void NickCore::SetDisplay(const NickAlias *na) +void NickCore::SetDisplay(NickAlias *na) { if (na->nc != this || na->nick == this->display) return; @@ -171,13 +188,14 @@ void NickCore::SetDisplay(const NickAlias *na) FOREACH_MOD(OnChangeCoreDisplay, (this, na->nick)); /* this affects the serialized aliases */ - for (unsigned i = 0; i < aliases->size(); ++i) - aliases->at(i)->QueueUpdate(); + for (auto *alias : *aliases) + alias->QueueUpdate(); /* Remove the core from the list */ NickCoreList->erase(this->display); this->display = na->nick; + this->na = na; (*NickCoreList)[this->display] = this; } @@ -187,67 +205,6 @@ bool NickCore::IsServicesOper() const return this->o != NULL; } -void NickCore::AddAccess(const Anope::string &entry) -{ - this->access.push_back(entry); - FOREACH_MOD(OnNickAddAccess, (this, entry)); -} - -Anope::string NickCore::GetAccess(unsigned entry) const -{ - if (this->access.empty() || entry >= this->access.size()) - return ""; - return this->access[entry]; -} - -unsigned NickCore::GetAccessCount() const -{ - return this->access.size(); -} - -bool NickCore::FindAccess(const Anope::string &entry) -{ - for (unsigned i = 0, end = this->access.size(); i < end; ++i) - if (this->access[i] == entry) - return true; - - return false; -} - -void NickCore::EraseAccess(const Anope::string &entry) -{ - for (unsigned i = 0, end = this->access.size(); i < end; ++i) - if (this->access[i] == entry) - { - FOREACH_MOD(OnNickEraseAccess, (this, entry)); - this->access.erase(this->access.begin() + i); - break; - } -} - -void NickCore::ClearAccess() -{ - FOREACH_MOD(OnNickClearAccess, (this)); - this->access.clear(); -} - -bool NickCore::IsOnAccess(const User *u) const -{ - Anope::string buf = u->GetIdent() + "@" + u->host, buf2, buf3; - if (!u->vhost.empty()) - buf2 = u->GetIdent() + "@" + u->vhost; - if (!u->GetCloakedHost().empty()) - buf3 = u->GetIdent() + "@" + u->GetCloakedHost(); - - for (unsigned i = 0, end = this->access.size(); i < end; ++i) - { - Anope::string a = this->GetAccess(i); - if (Anope::Match(buf, a) || (!buf2.empty() && Anope::Match(buf2, a)) || (!buf3.empty() && Anope::Match(buf3, a))) - return true; - } - return false; -} - void NickCore::AddChannelReference(ChannelInfo *ci) { ++(*this->chanaccess)[ci]; @@ -255,7 +212,7 @@ void NickCore::AddChannelReference(ChannelInfo *ci) void NickCore::RemoveChannelReference(ChannelInfo *ci) { - int& i = (*this->chanaccess)[ci]; + int &i = (*this->chanaccess)[ci]; if (--i <= 0) this->chanaccess->erase(ci); } @@ -263,11 +220,11 @@ void NickCore::RemoveChannelReference(ChannelInfo *ci) void NickCore::GetChannelReferences(std::deque<ChannelInfo *> &queue) { queue.clear(); - for (std::map<ChannelInfo *, int>::iterator it = this->chanaccess->begin(), it_end = this->chanaccess->end(); it != it_end; ++it) - queue.push_back(it->first); + for (const auto &[ci, _] : *this->chanaccess) + queue.push_back(ci); } -NickCore* NickCore::Find(const Anope::string &nick) +NickCore *NickCore::Find(const Anope::string &nick) { nickcore_map::const_iterator it = NickCoreList->find(nick); if (it != NickCoreList->end()) @@ -279,36 +236,34 @@ NickCore* NickCore::Find(const Anope::string &nick) return NULL; } -uint64_t NickCore::GetId() +NickCore *NickCore::FindId(uint64_t id) { - if (this->id) - return this->id; - - NickAlias *na = NickAlias::Find(this->display); - if (!na) + auto it = NickCoreIdList->find(id); + if (it != NickCoreIdList->end()) { - Log(LOG_DEBUG) << "Unable to find the display NickAlias for NickCore: " << this->display; - return 0; + it->second->QueueUpdate(); + return it->second; } + return nullptr; +} - Anope::string secretid = this->display + "\0" + stringify(na->time_registered); +uint64_t NickCore::GetId() +{ + if (this->id) + return this->id; - // Generate the account id. This should almost always only have one - // iteration but in the rare case that we generate a duplicate id we try - // again with a new key. + // We base the account identifier on the account display at registration and + // when the account was first registered. This should be unique enough that + // it never collides. In the extremely rare case that it does generate a + // duplicate id we try with a new suffix. + uint64_t attempt = 0; while (!this->id) { - // Generate a random key for SipHash. - char key[16]; - for (size_t i = 0; i < sizeof(key); ++i) - key[i] = rand() % CHAR_MAX; - - uint64_t newid = Anope::SipHash24(secretid.c_str(), secretid.length(), key); - nickcoreid_map::const_iterator it = NickCoreIdList.find(newid); - if (it == NickCoreIdList.end()) + const auto newidstr = this->display + "\0" + Anope::ToString(this->time_registered) + "\0" + Anope::ToString(attempt++); + const auto newid = Anope::hash_cs()(newidstr); + if (NickCoreIdList->emplace(newid, this).second) { this->id = newid; - NickCoreIdList[this->id] = this; this->QueueUpdate(); break; } diff --git a/src/opertype.cpp b/src/opertype.cpp index 026e5ae3d..d1a1256b3 100644 --- a/src/opertype.cpp +++ b/src/opertype.cpp @@ -13,7 +13,7 @@ std::vector<Oper *> Oper::opers; -Oper::Oper(const Anope::string &n, OperType *o) : name(n), ot(o), require_oper(true) +Oper::Oper(const Anope::string &n, OperType *o) : name(n), ot(o) { opers.push_back(this); } @@ -27,10 +27,8 @@ Oper::~Oper() Oper *Oper::Find(const Anope::string &name) { - for (unsigned i = 0; i < opers.size(); ++i) + for (auto *o : opers) { - Oper *o = opers[i]; - if (o->name.equals_ci(name)) return o; } @@ -40,10 +38,8 @@ Oper *Oper::Find(const Anope::string &name) OperType *OperType::Find(const Anope::string &name) { - for (unsigned i = 0; i < Config->MyOperTypes.size(); ++i) + for (auto *ot : Config->MyOperTypes) { - OperType *ot = Config->MyOperTypes[i]; - if (ot->GetName() == name) return ot; } @@ -57,19 +53,15 @@ OperType::OperType(const Anope::string &nname) : name(nname) bool OperType::HasCommand(const Anope::string &cmdstr) const { - for (std::list<Anope::string>::const_iterator it = this->commands.begin(), it_end = this->commands.end(); it != it_end; ++it) + for (const auto &command : this->commands) { - const Anope::string &s = *it; - - if (!s.find('~') && Anope::Match(cmdstr, s.substr(1))) + if (!command.find('~') && Anope::Match(cmdstr, command.substr(1))) return false; - else if (Anope::Match(cmdstr, s)) + else if (Anope::Match(cmdstr, command)) return true; } - for (std::set<OperType *>::const_iterator iit = this->inheritances.begin(), iit_end = this->inheritances.end(); iit != iit_end; ++iit) + for (auto *ot : this->inheritances) { - OperType *ot = *iit; - if (ot->HasCommand(cmdstr)) return true; } @@ -79,19 +71,15 @@ bool OperType::HasCommand(const Anope::string &cmdstr) const bool OperType::HasPriv(const Anope::string &privstr) const { - for (std::list<Anope::string>::const_iterator it = this->privs.begin(), it_end = this->privs.end(); it != it_end; ++it) + for (const auto &priv : this->privs) { - const Anope::string &s = *it; - - if (!s.find('~') && Anope::Match(privstr, s.substr(1))) + if (!priv.find('~') && Anope::Match(privstr, priv.substr(1))) return false; - else if (Anope::Match(privstr, s)) + else if (Anope::Match(privstr, priv)) return true; } - for (std::set<OperType *>::const_iterator iit = this->inheritances.begin(), iit_end = this->inheritances.end(); iit != iit_end; ++iit) + for (auto *ot : this->inheritances) { - OperType *ot = *iit; - if (ot->HasPriv(privstr)) return true; } @@ -120,28 +108,24 @@ void OperType::Inherits(OperType *ot) this->inheritances.insert(ot); } -const std::list<Anope::string> OperType::GetCommands() const +std::list<Anope::string> OperType::GetCommands() const { std::list<Anope::string> cmd_list = this->commands; - for (std::set<OperType *>::const_iterator it = this->inheritances.begin(), it_end = this->inheritances.end(); it != it_end; ++it) + for (auto *ot : this->inheritances) { - OperType *ot = *it; - std::list<Anope::string> cmds = ot->GetCommands(); - for (std::list<Anope::string>::const_iterator it2 = cmds.begin(), it2_end = cmds.end(); it2 != it2_end; ++it2) - cmd_list.push_back(*it2); + for (const auto &cmd : ot->GetCommands()) + cmd_list.push_back(cmd); } return cmd_list; } -const std::list<Anope::string> OperType::GetPrivs() const +std::list<Anope::string> OperType::GetPrivs() const { std::list<Anope::string> priv_list = this->privs; - for (std::set<OperType *>::const_iterator it = this->inheritances.begin(), it_end = this->inheritances.end(); it != it_end; ++it) + for (auto *ot : this->inheritances) { - OperType *ot = *it; - std::list<Anope::string> priv = ot->GetPrivs(); - for (std::list<Anope::string>::const_iterator it2 = priv.begin(), it2_end = priv.end(); it2 != it2_end; ++it2) - priv_list.push_back(*it2); + for (const auto &priv : ot->GetPrivs()) + priv_list.push_back(priv); } return priv_list; } diff --git a/src/pipeengine.cpp b/src/pipeengine.cpp index 4072b6df0..5bca3d1ab 100644 --- a/src/pipeengine.cpp +++ b/src/pipeengine.cpp @@ -60,7 +60,7 @@ void Pipe::Write(const char *data, size_t sz) write(this->write_pipe, data, sz); } -int Pipe::Read(char *data, size_t sz) +ssize_t Pipe::Read(char *data, size_t sz) { return read(this->GetFD(), data, sz); } diff --git a/src/process.cpp b/src/process.cpp index fdc854f47..9f2794ad6 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -18,61 +18,81 @@ void Anope::Process(const Anope::string &buffer) { - /* If debugging, log the buffer */ - Log(LOG_RAWIO) << "Received: " << buffer; - if (buffer.empty()) return; Anope::map<Anope::string> tags; Anope::string source, command; std::vector<Anope::string> params; - if (!IRCD->Parse(buffer, tags, source, command, params)) return; + Log(LOG_RAWIO) << "Received " << buffer; if (Anope::ProtocolDebug) { if (tags.empty()) - Log() << "No tags"; + Log() << "\tNo tags"; else - for (Anope::map<Anope::string>::const_iterator it = tags.begin(); it != tags.end(); ++it) - Log() << "tags " << it->first << ": " << it->second; + { + for (const auto &[tname, tvalue] : tags) + Log() << "\tTag " << tname << ": " << tvalue; + } - Log() << "Source : " << (source.empty() ? "No source" : source); - Log() << "Command: " << command; + if (source.empty()) + Log() << "\tNo source"; + else + Log() << "\tSource: " << source; + + Log() << "\tCommand: " << command; if (params.empty()) - Log() << "No params"; + Log() << "\tNo params"; else - for (unsigned i = 0; i < params.size(); ++i) - Log() << "params " << i << ": " << params[i]; + { + for (size_t i = 0; i < params.size(); ++i) + Log() << "\tParam " << i << ": " << params[i]; + } } - static const Anope::string proto_name = ModuleManager::FindFirstOf(PROTOCOL) ? ModuleManager::FindFirstOf(PROTOCOL)->name : ""; - MessageSource src(source); - EventReturn MOD_RESULT; - FOREACH_RESULT(OnMessage, MOD_RESULT, (src, command, params)); + FOREACH_RESULT(OnMessage, MOD_RESULT, (src, command, params, tags)); if (MOD_RESULT == EVENT_STOP) return; + ProcessInternal(src, command, params, tags); +} + +void Anope::ProcessInternal(MessageSource &src, const Anope::string &command, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> & tags) +{ + static const Anope::string proto_name = ModuleManager::FindFirstOf(PROTOCOL) ? ModuleManager::FindFirstOf(PROTOCOL)->name : ""; ServiceReference<IRCDMessage> m("IRCDMessage", proto_name + "/" + command.lower()); if (!m) { - Log(LOG_DEBUG) << "unknown message from server (" << buffer << ")"; + Log(LOG_DEBUG) << "unknown message from server: " << command; return; } - if (m->HasFlag(IRCDMESSAGE_SOFT_LIMIT) ? (params.size() < m->GetParamCount()) : (params.size() != m->GetParamCount())) + if (m->HasFlag(IRCDMessage::FLAG_SOFT_LIMIT) ? (params.size() < m->GetParamCount()) : (params.size() != m->GetParamCount())) Log(LOG_DEBUG) << "invalid parameters for " << command << ": " << params.size() << " != " << m->GetParamCount(); - else if (m->HasFlag(IRCDMESSAGE_REQUIRE_USER) && !src.GetUser()) - Log(LOG_DEBUG) << "unexpected non-user source " << source << " for " << command; - else if (m->HasFlag(IRCDMESSAGE_REQUIRE_SERVER) && !source.empty() && !src.GetServer()) - Log(LOG_DEBUG) << "unexpected non-server source " << source << " for " << command; + else if (m->HasFlag(IRCDMessage::FLAG_REQUIRE_USER) && !src.GetUser()) + Log(LOG_DEBUG) << "unexpected non-user source " << src.GetSource() << " for " << command; + else if (m->HasFlag(IRCDMessage::FLAG_REQUIRE_SERVER) && !src.GetSource().empty() && !src.GetServer()) + Log(LOG_DEBUG) << "unexpected non-server source " << src.GetSource() << " for " << command; else - m->Run(src, params, tags); + { + try + { + m->Run(src, params, tags); + } + catch (const ProtocolException &err) + { + IRCD->SendError(err.GetReason()); + Anope::QuitReason = "Protocol error: " + err.GetReason(); + Anope::Quitting = true; + Anope::ReturnValue = EXIT_FAILURE; + } + } } bool IRCDProto::Parse(const Anope::string &buffer, Anope::map<Anope::string> &tags, Anope::string &source, Anope::string &command, std::vector<Anope::string> ¶ms) @@ -123,17 +143,76 @@ bool IRCDProto::Parse(const Anope::string &buffer, Anope::map<Anope::string> &ta return true; } -Anope::string IRCDProto::Format(const Anope::string &source, const Anope::string &message) +bool IRCDProto::Format(Anope::string &message, const Anope::map<Anope::string> &tags, const MessageSource &source, const Anope::string &command, const std::vector<Anope::string> ¶ms) { - if (!source.empty()) - return ":" + source + " " + message; - else - return message; + std::stringstream buffer; + if (!tags.empty()) + { + char separator = '@'; + for (const auto &[tname, tvalue] : tags) + { + if (IRCD->IsTagValid(tname, tvalue)) + { + buffer << separator << tname; + if (!tvalue.empty()) + buffer << '=' << tvalue; + separator = ';'; + } + } + if (separator != '@') + buffer << ' '; + } + + if (source.GetServer()) + { + const auto *s = source.GetServer(); + if (s != Me && !s->IsJuped()) + { + Log(LOG_DEBUG) << "Attempted to send \"" << command << "\" from " << s->GetName() << " who is not from me"; + return false; + } + + buffer << ':' << s->GetSID() << ' '; + } + else if (source.GetUser()) + { + const auto *u = source.GetUser(); + if (u->server != Me && !u->server->IsJuped()) + { + Log(LOG_DEBUG) << "Attempted to send \"" << command << "\" from " << u->nick << " who is not from me"; + return false; + } + + const auto *bi = source.GetBot(); + if (bi && !bi->introduced) + { + Log(LOG_DEBUG) << "Attempted to send \"" << command << "\" from " << bi->nick << " when not introduced"; + return false; + } + + buffer << ':' << u->GetUID() << ' '; + } + + buffer << command; + if (!params.empty()) + { + buffer << ' '; + for (auto it = params.begin(); it != params.end() - 1; ++it) + buffer << *it << ' '; + + + const auto &last = params.back(); + if (last.empty() || last[0] == ':' || last.find(' ') != Anope::string::npos) + buffer << ':'; + buffer << last; + } + + message = buffer.str(); + return true; } MessageTokenizer::MessageTokenizer(const Anope::string &msg) : message(msg) - , position(0) { } diff --git a/src/protocol.cpp b/src/protocol.cpp index 26a248a95..7a167582a 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -18,17 +18,18 @@ #include "uplink.h" #include "bots.h" #include "channels.h" +#include "numeric.h" IRCDProto *IRCD = NULL; -IRCDProto::IRCDProto(Module *creator, const Anope::string &p) : Service(creator, "IRCDProto", creator->name), proto_name(p) +IRCDProto::IRCDProto(Module *creator, const Anope::string &p) + : Service(creator, "IRCDProto", creator->name) + , proto_name(p) + , MaxChannel(Config->GetBlock("networkinfo").Get<unsigned>("chanlen", "32")) + , MaxHost(Config->GetBlock("networkinfo").Get<unsigned>("hostlen", "64")) + , MaxNick(Config->GetBlock("networkinfo").Get<unsigned>("nicklen", "31")) + , MaxUser(Config->GetBlock("networkinfo").Get<unsigned>("userlen", "10")) { - DefaultPseudoclientModes = "+io"; - CanSVSNick = CanSVSJoin = CanSetVHost = CanSetVIdent = CanSNLine = CanSQLine = CanSQLineChannel - = CanSZLine = CanSVSHold = CanSVSO = CanCertFP = RequiresID = AmbiguousID = false; - MaxModes = 3; - MaxLine = 512; - if (IRCD == NULL) IRCD = this; } @@ -39,11 +40,6 @@ IRCDProto::~IRCDProto() IRCD = NULL; } -const Anope::string &IRCDProto::GetProtocolName() -{ - return this->proto_name; -} - static inline char nextID(int pos, Anope::string &buf) { char &c = buf[pos]; @@ -80,7 +76,7 @@ Anope::string IRCDProto::SID_Retrieve() if (!IRCD || !IRCD->RequiresID) return ""; - static Anope::string current_sid = Config->GetBlock("serverinfo")->Get<const Anope::string>("id"); + static Anope::string current_sid = Config->GetBlock("serverinfo").Get<const Anope::string>("id"); if (current_sid.empty()) current_sid = "00A"; @@ -94,179 +90,112 @@ Anope::string IRCDProto::SID_Retrieve() return current_sid; } +time_t IRCDProto::ExtractTimestamp(const Anope::string &str) +{ + auto ts = Anope::TryConvert<time_t>(str); + if (!ts.has_value()) + throw ProtocolException("Invalid timestamp: " + str); + return ts.value(); +} + +void IRCDProto::SendError(const Anope::string &reason) +{ + Uplink::Send("ERROR", reason); +} + void IRCDProto::SendKill(const MessageSource &source, const Anope::string &target, const Anope::string &reason) { - UplinkSocket::Message(source) << "KILL " << target << " :" << reason; + Uplink::Send(source, "KILL", target, reason); } -void IRCDProto::SendSVSKillInternal(const MessageSource &source, User *user, const Anope::string &buf) +void IRCDProto::SendSVSKill(const MessageSource &source, User *user, const Anope::string &buf) { - UplinkSocket::Message(source) << "KILL " << user->GetUID() << " :" << buf; + Uplink::Send(source, "KILL", user->GetUID(), buf); } -void IRCDProto::SendModeInternal(const MessageSource &source, const Channel *dest, const Anope::string &buf) +void IRCDProto::SendModeInternal(const MessageSource &source, Channel *chan, const Anope::string &modes, const std::vector<Anope::string> &values) { - UplinkSocket::Message(source) << "MODE " << dest->name << " " << buf; + auto params = values; + params.insert(params.begin(), { chan->name, modes }); + Uplink::SendInternal({}, source, "MODE", params); } -void IRCDProto::SendModeInternal(const MessageSource &source, User *dest, const Anope::string &buf) +void IRCDProto::SendModeInternal(const MessageSource &source, User *dest, const Anope::string &modes, const std::vector<Anope::string> &values) { - UplinkSocket::Message(source) << "MODE " << dest->GetUID() << " " << buf; + auto params = values; + params.insert(params.begin(), { dest->GetUID(), modes }); + Uplink::SendInternal({}, source, "MODE", params); } -void IRCDProto::SendKickInternal(const MessageSource &source, const Channel *c, User *u, const Anope::string &r) +void IRCDProto::SendKick(const MessageSource &source, const Channel *c, User *u, const Anope::string &r) { if (!r.empty()) - UplinkSocket::Message(source) << "KICK " << c->name << " " << u->GetUID() << " :" << r; + Uplink::Send(source, "KICK", c->name, u->GetUID(), r); else - UplinkSocket::Message(source) << "KICK " << c->name << " " << u->GetUID(); + Uplink::Send(source, "KICK", c->name, u->GetUID()); } -void IRCDProto::SendNoticeInternal(const MessageSource &source, const Anope::string &dest, const Anope::string &msg) +void IRCDProto::SendNotice(const MessageSource &source, const Anope::string &dest, const Anope::string &msg, const Anope::map<Anope::string> &tags) { - UplinkSocket::Message(source) << "NOTICE " << dest << " :" << msg; + Uplink::Send(tags, source, "NOTICE", dest, msg.empty() ? " " : msg); } -void IRCDProto::SendPrivmsgInternal(const MessageSource &source, const Anope::string &dest, const Anope::string &buf) +void IRCDProto::SendPrivmsg(const MessageSource &source, const Anope::string &dest, const Anope::string &msg, const Anope::map<Anope::string> &tags) { - UplinkSocket::Message(source) << "PRIVMSG " << dest << " :" << buf; + Uplink::Send(tags, source, "PRIVMSG", dest, msg.empty() ? " " : msg); } -void IRCDProto::SendQuitInternal(User *u, const Anope::string &buf) +void IRCDProto::SendTagmsg(const MessageSource &source, const Anope::string &dest, const Anope::map<Anope::string> &tags) { - if (!buf.empty()) - UplinkSocket::Message(u) << "QUIT :" << buf; - else - UplinkSocket::Message(u) << "QUIT"; + if (CanTagMessage) + Uplink::Send(tags, source, "TAGMSG", dest); } -void IRCDProto::SendPartInternal(User *u, const Channel *chan, const Anope::string &buf) +void IRCDProto::SendQuit(User *u, const Anope::string &buf, const Anope::string &operbuf) { if (!buf.empty()) - UplinkSocket::Message(u) << "PART " << chan->name << " :" << buf; + Uplink::Send(u, "QUIT", buf); else - UplinkSocket::Message(u) << "PART " << chan->name; + Uplink::Send(u, "QUIT"); } -void IRCDProto::SendGlobopsInternal(const MessageSource &source, const Anope::string &buf) +void IRCDProto::SendPart(User *u, const Channel *chan, const Anope::string &buf) { - UplinkSocket::Message(source) << "GLOBOPS :" << buf; + if (!buf.empty()) + Uplink::Send(u, "PART", chan->name, buf); + else + Uplink::Send(u, "PART", chan->name); } -void IRCDProto::SendCTCPInternal(const MessageSource &source, const Anope::string &dest, const Anope::string &buf) +void IRCDProto::SendGlobops(const MessageSource &source, const Anope::string &message) { - Anope::string s = Anope::NormalizeBuffer(buf); - this->SendNoticeInternal(source, dest, "\1" + s + "\1"); + Uplink::Send(source, "GLOBOPS", message); } -void IRCDProto::SendNumericInternal(int numeric, const Anope::string &dest, const Anope::string &buf) +void IRCDProto::SendNumericInternal(int numeric, const Anope::string &dest, const std::vector<Anope::string> ¶ms) { - Anope::string n = stringify(numeric); + Anope::string n = Anope::ToString(numeric); if (numeric < 10) n = "0" + n; if (numeric < 100) n = "0" + n; - UplinkSocket::Message(Me) << n << " " << dest << " " << buf; -} - -void IRCDProto::SendTopic(const MessageSource &source, Channel *c) -{ - UplinkSocket::Message(source) << "TOPIC " << c->name << " :" << c->topic; -} -void IRCDProto::SendSVSKill(const MessageSource &source, User *user, const char *fmt, ...) -{ - if (!user || !fmt) - return; - - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendSVSKillInternal(source, user, buf); -} - -void IRCDProto::SendMode(const MessageSource &source, const Channel *dest, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendModeInternal(source, dest, buf); -} - -void IRCDProto::SendMode(const MessageSource &source, User *u, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendModeInternal(source, u, buf); -} - -void IRCDProto::SendKick(const MessageSource &source, const Channel *chan, User *user, const char *fmt, ...) -{ - if (!chan || !user) - return; - - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendKickInternal(source, chan, user, buf); -} - -void IRCDProto::SendNotice(const MessageSource &source, const Anope::string &dest, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendNoticeInternal(source, dest, buf); -} - -void IRCDProto::SendAction(const MessageSource &source, const Anope::string &dest, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - Anope::string actionbuf = Anope::string("\1ACTION ") + buf + '\1'; - SendPrivmsgInternal(source, dest, actionbuf); + auto newparams = params; + newparams.insert(newparams.begin(), dest); + Uplink::SendInternal({}, Me, n, newparams); } -void IRCDProto::SendPrivmsg(const MessageSource &source, const Anope::string &dest, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendPrivmsgInternal(source, dest, buf); -} - -void IRCDProto::SendQuit(User *u, const char *fmt, ...) +void IRCDProto::SendTopic(const MessageSource &source, Channel *c) { - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendQuitInternal(u, buf); + Uplink::Send(source, "TOPIC", c->name, c->topic); } void IRCDProto::SendPing(const Anope::string &servname, const Anope::string &who) { if (servname.empty()) - UplinkSocket::Message(Me) << "PING " << who; + Uplink::Send("PING", who); else - UplinkSocket::Message(Me) << "PING " << servname << " " << who; + Uplink::Send("PING", servname, who); } /** @@ -278,74 +207,29 @@ void IRCDProto::SendPing(const Anope::string &servname, const Anope::string &who void IRCDProto::SendPong(const Anope::string &servname, const Anope::string &who) { if (servname.empty()) - UplinkSocket::Message(Me) << "PONG " << who; + Uplink::Send("PONG", who); else - UplinkSocket::Message(Me) << "PONG " << servname << " " << who; + Uplink::Send("PONG", servname, who); } void IRCDProto::SendInvite(const MessageSource &source, const Channel *c, User *u) { - UplinkSocket::Message(source) << "INVITE " << u->GetUID() << " " << c->name; -} - -void IRCDProto::SendPart(User *user, const Channel *chan, const char *fmt, ...) -{ - if (fmt) - { - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendPartInternal(user, chan, buf); - } - else - SendPartInternal(user, chan, ""); -} - -void IRCDProto::SendGlobops(const MessageSource &source, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendGlobopsInternal(source, buf); + Uplink::Send(source, "INVITE", u->GetUID(), c->name); } void IRCDProto::SendSquit(Server *s, const Anope::string &message) { - UplinkSocket::Message() << "SQUIT " << s->GetSID() << " :" << message; + Uplink::Send("SQUIT", s->GetSID(), message); } void IRCDProto::SendNickChange(User *u, const Anope::string &newnick) { - UplinkSocket::Message(u) << "NICK " << newnick << " " << Anope::CurTime; + Uplink::Send(u, "NICK", newnick, Anope::CurTime); } void IRCDProto::SendForceNickChange(User *u, const Anope::string &newnick, time_t when) { - UplinkSocket::Message() << "SVSNICK " << u->GetUID() << " " << newnick << " " << when; -} - -void IRCDProto::SendCTCP(const MessageSource &source, const Anope::string &dest, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendCTCPInternal(source, dest, buf); -} - -void IRCDProto::SendNumeric(int numeric, const Anope::string &dest, const char *fmt, ...) -{ - va_list args; - char buf[BUFSIZE] = ""; - va_start(args, fmt); - vsnprintf(buf, BUFSIZE - 1, fmt, args); - va_end(args); - SendNumericInternal(numeric, dest, buf); + Uplink::Send("SVSNICK", u->GetUID(), newnick, when); } bool IRCDProto::IsNickValid(const Anope::string &nick) @@ -364,10 +248,10 @@ bool IRCDProto::IsNickValid(const Anope::string &nick) Anope::string special = "[]\\`_^{|}"; for (unsigned i = 0; i < nick.length(); ++i) - if (!(nick[i] >= 'A' && nick[i] <= 'Z') && !(nick[i] >= 'a' && nick[i] <= 'z') + if ((nick[i] < 'A' || nick[i] > 'Z') && (nick[i] < 'a' || nick[i] > 'z') && special.find(nick[i]) == Anope::string::npos && (Config && Config->NickChars.find(nick[i]) == Anope::string::npos) - && (!i || (!(nick[i] >= '0' && nick[i] <= '9') && nick[i] != '-'))) + && (!i || ((nick[i] < '0' || nick[i] > '9') && nick[i] != '-'))) return false; return true; @@ -375,7 +259,7 @@ bool IRCDProto::IsNickValid(const Anope::string &nick) bool IRCDProto::IsChannelValid(const Anope::string &chan) { - if (chan.empty() || chan[0] != '#' || chan.length() > Config->GetBlock("networkinfo")->Get<unsigned>("chanlen")) + if (chan.empty() || chan[0] != '#' || chan.length() > IRCD->MaxChannel) return false; if (chan.find_first_of(" ,") != Anope::string::npos) @@ -386,13 +270,11 @@ bool IRCDProto::IsChannelValid(const Anope::string &chan) bool IRCDProto::IsIdentValid(const Anope::string &ident) { - if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen")) + if (ident.empty() || ident.length() > IRCD->MaxUser) return false; - for (unsigned i = 0; i < ident.length(); ++i) + for (auto c : ident) { - const char &c = ident[i]; - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '-') continue; @@ -404,11 +286,11 @@ bool IRCDProto::IsIdentValid(const Anope::string &ident) bool IRCDProto::IsHostValid(const Anope::string &host) { - if (host.empty() || host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen")) + if (host.empty() || host.length() > IRCD->MaxHost) return false; - const Anope::string &vhostdisablebe = Config->GetBlock("networkinfo")->Get<const Anope::string>("disallow_start_or_end"), - vhostchars = Config->GetBlock("networkinfo")->Get<const Anope::string>("vhost_chars"); + const Anope::string &vhostdisablebe = Config->GetBlock("networkinfo").Get<const Anope::string>("disallow_start_or_end"), + vhostchars = Config->GetBlock("networkinfo").Get<const Anope::string>("vhost_chars"); if (vhostdisablebe.find_first_of(host[0]) != Anope::string::npos) return false; @@ -416,31 +298,26 @@ bool IRCDProto::IsHostValid(const Anope::string &host) return false; int dots = 0; - for (unsigned i = 0; i < host.length(); ++i) + for (auto chr : host) { - if (host[i] == '.') + if (chr == '.') ++dots; - if (vhostchars.find_first_of(host[i]) == Anope::string::npos) + if (vhostchars.find_first_of(chr) == Anope::string::npos) return false; } - return dots > 0 || Config->GetBlock("networkinfo")->Get<bool>("allow_undotted_vhosts"); + return dots > 0 || Config->GetBlock("networkinfo").Get<bool>("allow_undotted_vhosts"); } void IRCDProto::SendOper(User *u) { - SendNumericInternal(381, u->GetUID(), ":You are now an IRC operator (set by services)"); + SendNumeric(RPL_YOUREOPER, u->GetUID(), "You are now an IRC operator (set by services)"); u->SetMode(NULL, "OPER"); } -unsigned IRCDProto::GetMaxListFor(Channel *c) -{ - return c->HasMode("LBAN") ? 0 : Config->GetBlock("networkinfo")->Get<int>("modelistsize"); -} - -unsigned IRCDProto::GetMaxListFor(Channel *c, ChannelMode *cm) +size_t IRCDProto::GetMaxListFor(Channel *c, ChannelMode *cm) { - return GetMaxListFor(c); + return c->HasMode("LBAN") ? 0 : Config->GetBlock("networkinfo").Get<size_t>("modelistsize", "100"); } Anope::string IRCDProto::NormalizeMask(const Anope::string &mask) @@ -450,7 +327,21 @@ Anope::string IRCDProto::NormalizeMask(const Anope::string &mask) return Entry("", mask).GetNUHMask(); } -MessageSource::MessageSource(const Anope::string &src) : source(src), u(NULL), s(NULL) +void IRCDProto::SendContextNotice(BotInfo *bi, User *target, Channel *context, const Anope::string &msg) +{ + IRCD->SendNotice(bi, target->GetUID(), Anope::printf("[%s] %s", context->name.c_str(), msg.c_str()), { + { "+draft/channel-context", context->name }, + }); +} + +void IRCDProto::SendContextPrivmsg(BotInfo *bi, User *target, Channel *context, const Anope::string &msg) +{ + IRCD->SendPrivmsg(bi, target->GetUID(), Anope::printf("[%s] %s", context->name.c_str(), msg.c_str()), { + { "+draft/channel-context", context->name }, + }); +} + +MessageSource::MessageSource(const Anope::string &src) : source(src) { /* no source for incoming message is our uplink */ if (src.empty()) @@ -461,11 +352,11 @@ MessageSource::MessageSource(const Anope::string &src) : source(src), u(NULL), s this->u = User::Find(src); } -MessageSource::MessageSource(User *_u) : source(_u ? _u->nick : ""), u(_u), s(NULL) +MessageSource::MessageSource(User *_u) : source(_u ? _u->nick : ""), u(_u) { } -MessageSource::MessageSource(Server *_s) : source(_s ? _s->GetName() : ""), u(NULL), s(_s) +MessageSource::MessageSource(Server *_s) : source(_s ? _s->GetName() : ""), s(_s) { } @@ -499,18 +390,9 @@ Server *MessageSource::GetServer() const return this->s; } -IRCDMessage::IRCDMessage(Module *o, const Anope::string &n, unsigned p) : Service(o, "IRCDMessage", o->name + "/" + n.lower()), name(n), param_count(p) -{ -} - -unsigned IRCDMessage::GetParamCount() const +IRCDMessage::IRCDMessage(Module *o, const Anope::string &n, size_t pc) + : Service(o, "IRCDMessage", o->name + "/" + n.lower()) + , name(n) + , param_count(pc) { - return this->param_count; } - -void IRCDMessage::Run(MessageSource &source, const std::vector<Anope::string> ¶ms, const Anope::map<Anope::string> &tags) -{ - // Most IRCds don't support message tags yet so use the tagless variant. - Run(source, params); -} - diff --git a/src/regchannel.cpp b/src/regchannel.cpp index 6dbd9df55..f45803e06 100644 --- a/src/regchannel.cpp +++ b/src/regchannel.cpp @@ -19,9 +19,10 @@ #include "bots.h" #include "servers.h" -Serialize::Checker<registered_channel_map> RegisteredChannelList("ChannelInfo"); +Serialize::Checker<registered_channel_map> RegisteredChannelList(CHANNELINFO_TYPE); -AutoKick::AutoKick() : Serializable("AutoKick") +AutoKick::AutoKick() + : Serializable(AUTOKICK_TYPE) { } @@ -38,32 +39,40 @@ AutoKick::~AutoKick() } } -void AutoKick::Serialize(Serialize::Data &data) const +AutoKick::Type::Type() + : Serialize::Type(AUTOKICK_TYPE) { - data["ci"] << this->ci->name; - if (this->nc) - data["nc"] << this->nc->display; +} + +void AutoKick::Type::Serialize(const Serializable *obj, Serialize::Data &data) const +{ + const auto *ak = static_cast<const AutoKick *>(obj); + data.Store("ci", ak->ci->name); + if (ak->nc) + data.Store("ncid", ak->nc->GetId()); else - data["mask"] << this->mask; - data["reason"] << this->reason; - data["creator"] << this->creator; - data.SetType("addtime", Serialize::Data::DT_INT); data["addtime"] << this->addtime; - data.SetType("last_used", Serialize::Data::DT_INT); data["last_used"] << this->last_used; + data.Store("mask", ak->mask); + data.Store("reason", ak->reason); + data.Store("creator", ak->creator); + data.Store("addtime", ak->addtime); + data.Store("last_used", ak->last_used); } -Serializable* AutoKick::Unserialize(Serializable *obj, Serialize::Data &data) +Serializable *AutoKick::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { Anope::string sci, snc; + uint64_t sncid = 0; data["ci"] >> sci; - data["nc"] >> snc; + data["nc"] >> snc; // Deprecated 2.0 field + data["ncid"] >> sncid; ChannelInfo *ci = ChannelInfo::Find(sci); if (!ci) return NULL; AutoKick *ak; - NickCore *nc = NickCore::Find(snc); + auto *nc = sncid ? NickCore::FindId(sncid) : NickCore::Find(snc); if (obj) { ak = anope_dynamic_static_cast<AutoKick *>(obj); @@ -95,37 +104,31 @@ Serializable* AutoKick::Unserialize(Serializable *obj, Serialize::Data &data) return ak; } -ChannelInfo::ChannelInfo(const Anope::string &chname) : Serializable("ChannelInfo"), - access("ChanAccess"), akick("AutoKick") +ChannelInfo::ChannelInfo(const Anope::string &chname) + : Serializable(CHANNELINFO_TYPE) + , access(CHANACCESS_TYPE) + , akick(AUTOKICK_TYPE) + , name(chname) + , time_registered(Anope::CurTime) + , last_used(Anope::CurTime) { if (chname.empty()) throw CoreException("Empty channel passed to ChannelInfo constructor"); - this->founder = NULL; - this->successor = NULL; this->c = Channel::Find(chname); if (this->c) this->c->ci = this; - this->banexpire = 0; - this->bi = NULL; - this->last_topic_time = 0; - this->name = chname; - - this->bantype = 2; - this->memos.memomax = 0; - this->last_used = this->time_registered = Anope::CurTime; - - size_t old = RegisteredChannelList->size(); - (*RegisteredChannelList)[this->name] = this; - if (old == RegisteredChannelList->size()) + if (!RegisteredChannelList->insert_or_assign(this->name, this).second) Log(LOG_DEBUG) << "Duplicate channel " << this->name << " in registered channel table?"; FOREACH_MOD(OnCreateChan, (this)); } -ChannelInfo::ChannelInfo(const ChannelInfo &ci) : Serializable("ChannelInfo"), - access("ChanAccess"), akick("AutoKick") +ChannelInfo::ChannelInfo(const ChannelInfo &ci) + : Serializable(CHANNELINFO_TYPE) + , access(CHANACCESS_TYPE) + , akick(AUTOKICK_TYPE) { *this = ci; @@ -178,43 +181,56 @@ ChannelInfo::~ChannelInfo() } } -void ChannelInfo::Serialize(Serialize::Data &data) const +ChannelInfo::Type::Type() + : Serialize::Type(CHANNELINFO_TYPE) { - data["name"] << this->name; - if (this->founder) - data["founder"] << this->founder->display; - if (this->successor) - data["successor"] << this->successor->display; - data["description"] << this->desc; - data.SetType("time_registered", Serialize::Data::DT_INT); data["time_registered"] << this->time_registered; - data.SetType("last_used", Serialize::Data::DT_INT); data["last_used"] << this->last_used; - data["last_topic"] << this->last_topic; - data["last_topic_setter"] << this->last_topic_setter; - data.SetType("last_topic_time", Serialize::Data::DT_INT); data["last_topic_time"] << this->last_topic_time; - data.SetType("bantype", Serialize::Data::DT_INT); data["bantype"] << this->bantype; +} + +void ChannelInfo::Type::Serialize(const Serializable *obj, Serialize::Data &data) const +{ + const auto *ci = static_cast<const ChannelInfo *>(obj); + + data.Store("name", ci->name); + if (ci->founder) + data.Store("founderid", ci->founder->GetId()); + if (ci->successor) + data.Store("successorid", ci->successor->GetId()); + data.Store("description", ci->desc); + data.Store("time_registered", ci->time_registered); + data.Store("last_used", ci->last_used); + data.Store("last_topic", ci->last_topic); + data.Store("last_topic_setter", ci->last_topic_setter); + data.Store("last_topic_time", ci->last_topic_time); + data.Store("bantype", ci->bantype); { Anope::string levels_buffer; - for (Anope::map<int16_t>::const_iterator it = this->levels.begin(), it_end = this->levels.end(); it != it_end; ++it) - levels_buffer += it->first + " " + stringify(it->second) + " "; - data["levels"] << levels_buffer; + for (const auto &[name, level] : ci->levels) + levels_buffer += name + " " + Anope::ToString(level) + " "; + data.Store("levels", levels_buffer); } - if (this->bi) - data["bi"] << this->bi->nick; - data.SetType("banexpire", Serialize::Data::DT_INT); data["banexpire"] << this->banexpire; - data["memomax"] << this->memos.memomax; - for (unsigned i = 0; i < this->memos.ignores.size(); ++i) - data["memoignores"] << this->memos.ignores[i] << " "; + if (ci->bi) + data.Store("bi", ci->bi->nick); + data.Store("banexpire", ci->banexpire); + data.Store("memomax", ci->memos.memomax); + + std::ostringstream oss; + for (const auto &ignore : ci->memos.ignores) + oss << ignore << " "; + data.Store("memoignores", oss.str()); - Extensible::ExtensibleSerialize(this, this, data); + Extensible::ExtensibleSerialize(ci, ci, data); } -Serializable* ChannelInfo::Unserialize(Serializable *obj, Serialize::Data &data) +Serializable *ChannelInfo::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { Anope::string sname, sfounder, ssuccessor, slevels, sbi; + uint64_t sfounderid = 0, ssuccessorid = 0; data["name"] >> sname; - data["founder"] >> sfounder; - data["successor"] >> ssuccessor; + data["founder"] >> sfounder; // Deprecated 2.0 field + data["founderid"] >> sfounderid; + data["successor"] >> ssuccessor; // Deprecated 2.0 field + data["successorid"] >> ssuccessorid; data["levels"] >> slevels; data["bi"] >> sbi; @@ -224,8 +240,8 @@ Serializable* ChannelInfo::Unserialize(Serializable *obj, Serialize::Data &data) else ci = new ChannelInfo(sname); - ci->SetFounder(NickCore::Find(sfounder)); - ci->SetSuccessor(NickCore::Find(ssuccessor)); + ci->SetFounder(sfounderid ? NickCore::FindId(sfounderid) : NickCore::Find(sfounder)); + ci->SetSuccessor(ssuccessorid ? NickCore::FindId(ssuccessorid) : NickCore::Find(ssuccessor)); data["description"] >> ci->desc; data["time_registered"] >> ci->time_registered; @@ -238,11 +254,14 @@ Serializable* ChannelInfo::Unserialize(Serializable *obj, Serialize::Data &data) std::vector<Anope::string> v; spacesepstream(slevels).GetTokens(v); for (unsigned i = 0; i + 1 < v.size(); i += 2) - try - { - ci->levels[v[i]] = convertTo<int16_t>(v[i + 1]); - } - catch (const ConvertException &) { } + { + // Begin 2.0 database compatibility. + if (v[i] == "FANTASIA") + v[i] = "FANTASY"; + // End 2.0 database compatibility. + if (auto level = Anope::TryConvert<int16_t>(v[i + 1])) + ci->levels[v[i]] = level.value(); + } } BotInfo *bi = BotInfo::Find(sbi, true); if (*ci->bi != bi) @@ -260,7 +279,7 @@ Serializable* ChannelInfo::Unserialize(Serializable *obj, Serialize::Data &data) spacesepstream sep(buf); ci->memos.ignores.clear(); while (sep.GetToken(buf)) - ci->memos.ignores.push_back(buf); + ci->memos.ignores.insert(buf); } Extensible::ExtensibleUnserialize(ci, ci, data); @@ -268,10 +287,6 @@ Serializable* ChannelInfo::Unserialize(Serializable *obj, Serialize::Data &data) /* compat */ bool b; b = false; - data["extensible:SECURE"] >> b; - if (b) - ci->Extend<bool>("CS_SECURE"); - b = false; data["extensible:PRIVATE"] >> b; if (b) ci->Extend<bool>("CS_PRIVATE"); @@ -423,18 +438,10 @@ AccessGroup ChannelInfo::AccessFor(const User *u, bool updateLastUsed) if (u == NULL) return group; - const NickCore *nc = u->Account(); - if (nc == NULL && !this->HasExt("NS_SECURE") && u->IsRecognized()) - { - const NickAlias *na = NickAlias::Find(u->nick); - if (na != NULL) - nc = na->nc; - } - group.super_admin = u->super_admin; group.founder = IsFounder(u, this); group.ci = this; - group.nc = nc; + group.nc = u->Account(); FindMatches(group, this, u, u->Account()); @@ -443,12 +450,10 @@ AccessGroup ChannelInfo::AccessFor(const User *u, bool updateLastUsed) if (updateLastUsed) this->last_used = Anope::CurTime; - for (unsigned i = 0; i < group.paths.size(); ++i) + for (auto &p : group.paths) { - ChanAccess::Path &p = group.paths[i]; - - for (unsigned int j = 0; j < p.size(); ++j) - p[j]->last_seen = Anope::CurTime; + for (auto *ca : p) + ca->last_seen = Anope::CurTime; } } @@ -527,7 +532,7 @@ void ChannelInfo::ClearAccess() AutoKick *ChannelInfo::AddAkick(const Anope::string &user, NickCore *akicknc, const Anope::string &reason, time_t t, time_t lu) { - AutoKick *autokick = new AutoKick(); + auto *autokick = new AutoKick(); autokick->ci = this; autokick->nc = akicknc; autokick->reason = reason; @@ -544,7 +549,7 @@ AutoKick *ChannelInfo::AddAkick(const Anope::string &user, NickCore *akicknc, co AutoKick *ChannelInfo::AddAkick(const Anope::string &user, const Anope::string &mask, const Anope::string &reason, time_t t, time_t lu) { - AutoKick *autokick = new AutoKick(); + auto *autokick = new AutoKick(); autokick->ci = this; autokick->mask = mask; autokick->nc = NULL; @@ -646,7 +651,7 @@ Anope::string ChannelInfo::GetIdealBan(User *u) const } } -ChannelInfo* ChannelInfo::Find(const Anope::string &name) +ChannelInfo *ChannelInfo::Find(const Anope::string &name) { registered_channel_map::const_iterator it = RegisteredChannelList->find(name); if (it != RegisteredChannelList->end()) @@ -688,6 +693,6 @@ void ChannelInfo::RemoveChannelReference(const Anope::string &what) void ChannelInfo::GetChannelReferences(std::deque<Anope::string> &chans) { chans.clear(); - for (Anope::map<int>::iterator it = references.begin(); it != references.end(); ++it) - chans.push_back(it->first); + for (auto &[chan, _] : references) + chans.push_back(chan); } diff --git a/src/serialize.cpp b/src/serialize.cpp index faf8d0e4a..d820157b4 100644 --- a/src/serialize.cpp +++ b/src/serialize.cpp @@ -27,21 +27,25 @@ std::list<Serializable *> *Serializable::SerializableItems; void Serialize::RegisterTypes() { - static Type nc("NickCore", NickCore::Unserialize), na("NickAlias", NickAlias::Unserialize), bi("BotInfo", BotInfo::Unserialize), - ci("ChannelInfo", ChannelInfo::Unserialize), access("ChanAccess", ChanAccess::Unserialize), - akick("AutoKick", AutoKick::Unserialize), memo("Memo", Memo::Unserialize), xline("XLine", XLine::Unserialize); + static NickCore::Type nc; + static NickAlias::Type na; + static BotInfo::Type bi; + static ChannelInfo::Type ci; + static ChanAccess::Type access; + static AutoKick::Type akick; + static Memo::Type memo; + static XLine::Type xline; } void Serialize::CheckTypes() { - for (std::map<Anope::string, Serialize::Type *>::const_iterator it = Serialize::Type::GetTypes().begin(), it_end = Serialize::Type::GetTypes().end(); it != it_end; ++it) + for (const auto &[_, t] : Serialize::Type::GetTypes()) { - Serialize::Type *t = it->second; t->Check(); } } -Serializable::Serializable(const Anope::string &serialize_type) : last_commit(0), last_commit_time(0), id(0), redis_ignore(0) +Serializable::Serializable(const Anope::string &serialize_type) { if (SerializableItems == NULL) SerializableItems = new std::list<Serializable *>(); @@ -55,7 +59,7 @@ Serializable::Serializable(const Anope::string &serialize_type) : last_commit(0) FOREACH_MOD(OnSerializableConstruct, (this)); } -Serializable::Serializable(const Serializable &other) : last_commit(0), last_commit_time(0), id(0), redis_ignore(0) +Serializable::Serializable(const Serializable &other) { SerializableItems->push_back(this); this->s_iter = SerializableItems->end(); @@ -112,7 +116,22 @@ const std::list<Serializable *> &Serializable::GetItems() return *SerializableItems; } -Type::Type(const Anope::string &n, unserialize_func f, Module *o) : name(n), unserialize(f), owner(o), timestamp(0) +Serialize::DataType Serialize::Data::GetType(const Anope::string &key) const +{ + auto it = this->types.find(key); + if (it != this->types.end()) + return it->second; + return Serialize::DataType::TEXT; +} + +void Serialize::Data::SetType(const Anope::string &key, Serialize::DataType dt) +{ + this->types[key] = dt; +} + +Type::Type(const Anope::string &n, Module *o) + : name(n) + , owner(o) { TypeOrder.push_back(this->name); Types[this->name] = this; @@ -124,13 +143,13 @@ Type::~Type() { /* null the type of existing serializable objects of this type */ if (Serializable::SerializableItems != NULL) - for (std::list<Serializable *>::iterator it = Serializable::SerializableItems->begin(); it != Serializable::SerializableItems->end(); ++it) + { + for (auto *s : *Serializable::SerializableItems) { - Serializable *s = *it; - if (s->s_type == this) s->s_type = NULL; } + } std::vector<Anope::string>::iterator it = std::find(TypeOrder.begin(), TypeOrder.end(), this->name); if (it != TypeOrder.end()) @@ -138,21 +157,11 @@ Type::~Type() Types.erase(this->name); } -Serializable *Type::Unserialize(Serializable *obj, Serialize::Data &data) -{ - return this->unserialize(obj, data); -} - void Type::Check() { FOREACH_MOD(OnSerializeCheck, (this)); } -time_t Type::GetTimestamp() const -{ - return this->timestamp; -} - void Type::UpdateTimestamp() { this->timestamp = Anope::CurTime; @@ -165,13 +174,3 @@ Type *Serialize::Type::Find(const Anope::string &name) return it->second; return NULL; } - -const std::vector<Anope::string> &Type::GetTypeOrder() -{ - return TypeOrder; -} - -const std::map<Anope::string, Serialize::Type *>& Type::GetTypes() -{ - return Types; -} diff --git a/src/servers.cpp b/src/servers.cpp index a945d1642..9f98aeb07 100644 --- a/src/servers.cpp +++ b/src/servers.cpp @@ -27,7 +27,7 @@ Anope::map<Server *> Servers::ByID; std::set<Anope::string> Servers::Capab; -Server::Server(Server *up, const Anope::string &sname, unsigned shops, const Anope::string &desc, const Anope::string &ssid, bool jupe) : name(sname), hops(shops), description(desc), sid(ssid), uplink(up), users(0) +Server::Server(Server *up, const Anope::string &sname, unsigned shops, const Anope::string &desc, const Anope::string &ssid, bool jupe) : name(sname), hops(shops), description(desc), sid(ssid), uplink(up) { syncing = true; juped = jupe; @@ -48,27 +48,32 @@ Server::Server(Server *up, const Anope::string &sname, unsigned shops, const Ano if (Me == this->uplink && !juped) { /* Now do mode related stuff as we know what modes exist .. */ - for (botinfo_map::iterator it = BotListByNick->begin(), it_end = BotListByNick->end(); it != it_end; ++it) + for (auto &[_, bi] : *BotListByNick) { - BotInfo *bi = it->second; - Anope::string modes = !bi->botmodes.empty() ? ("+" + bi->botmodes) : IRCD->DefaultPseudoclientModes; + spacesepstream modesep(bi->botmodes.empty() ? IRCD->DefaultPseudoclientModes : "+" + bi->botmodes); - bi->SetModesInternal(bi, modes.c_str()); - for (unsigned i = 0; i < bi->botchannels.size(); ++i) + Anope::string modechars; + modesep.GetToken(modechars); + + std::vector<Anope::string> modeparams; + modesep.GetTokens(modeparams); + + bi->SetModesInternal(bi, modechars, modeparams); + for (const auto &botchannel : bi->botchannels) { - size_t h = bi->botchannels[i].find('#'); + size_t h = botchannel.find('#'); if (h == Anope::string::npos) continue; - Anope::string chname = bi->botchannels[i].substr(h); + Anope::string chname = botchannel.substr(h); Channel *c = Channel::Find(chname); if (c && c->FindUser(bi)) { - Anope::string want_modes = bi->botchannels[i].substr(0, h); - for (unsigned j = 0; j < want_modes.length(); ++j) + Anope::string want_modes = botchannel.substr(0, h); + for (char want_mode : want_modes) { - ChannelMode *cm = ModeManager::FindChannelModeByChar(want_modes[j]); + ChannelMode *cm = ModeManager::FindChannelModeByChar(want_mode); if (cm == NULL) - cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_modes[j])); + cm = ModeManager::FindChannelModeByChar(ModeManager::GetStatusChar(want_mode)); if (cm && cm->type == MODE_STATUS) { MessageSource ms = bi; @@ -81,19 +86,15 @@ Server::Server(Server *up, const Anope::string &sname, unsigned shops, const Ano IRCD->SendBOB(); - for (unsigned i = 0; i < Me->GetLinks().size(); ++i) + for (auto *link : Me->GetLinks()) { - Server *s = Me->GetLinks()[i]; - - if (s->juped) - IRCD->SendServer(s); + if (link->juped) + IRCD->SendServer(link); } /* We make the bots go online */ - for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + for (const auto &[_, u] : UserListByNick) { - User *u = it->second; - BotInfo *bi = BotInfo::Find(u->GetUID()); if (bi) { @@ -106,22 +107,22 @@ Server::Server(Server *up, const Anope::string &sname, unsigned shops, const Ano bi->introduced = true; } - for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it) + for (const auto &[_, c] : ChannelList) { - Channel *c = it->second; - if (c->users.empty()) IRCD->SendChannel(c); else - for (Channel::ChanUserList::const_iterator cit = c->users.begin(), cit_end = c->users.end(); cit != cit_end; ++cit) - IRCD->SendJoin(cit->second->user, c, &cit->second->status); + { + for (const auto &[_, uc] : c->users) + IRCD->SendJoin(uc->user, c, &uc->status); + } - for (Channel::ModeList::const_iterator it2 = c->GetModes().begin(); it2 != c->GetModes().end(); ++it2) + for (const auto &[mode, value] : c->GetModes()) { - ChannelMode *cm = ModeManager::FindChannelModeByName(it2->first); + ChannelMode *cm = ModeManager::FindChannelModeByName(mode); if (!cm || cm->type != MODE_LIST) continue; - ModeManager::StackerAdd(c->WhoSends(), c, cm, true, it2->second); + ModeManager::StackerAdd(c->WhoSends(), c, cm, true, value); } if (!c->topic.empty() && !c->topic_setter.empty()) @@ -139,10 +140,8 @@ Server::~Server() { Log(this, "quit") << "quit from " << (this->uplink ? this->uplink->GetName() : "no uplink") << " for " << this->quit_reason; - for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + for (const auto &[_, u] : UserListByNick) { - User *u = it->second; - if (u->server == this) { u->Quit(this->quit_reason); @@ -259,8 +258,8 @@ void Server::Sync(bool sync_links) if (sync_links && !this->links.empty()) { - for (unsigned i = 0, j = this->links.size(); i < j; ++i) - this->links[i]->Sync(true); + for (auto *link : this->links) + link->Sync(true); } bool me = this->GetUplink() && this->GetUplink() == Me; @@ -309,9 +308,11 @@ bool Server::IsULined() const if (this == Me) return true; - for (unsigned i = 0; i < Config->Ulines.size(); ++i) - if (Config->Ulines[i].equals_ci(this->GetName())) + for (const auto &uline : Config->Ulines) + { + if (uline.equals_ci(this->GetName())) return true; + } return false; } @@ -327,7 +328,7 @@ bool Server::IsQuitting() const void Server::Notice(BotInfo *source, const Anope::string &message) { - if (Config->UsePrivmsg && Config->DefPrivmsg) + if (Config->DefPrivmsg) IRCD->SendGlobalPrivmsg(source, this, message); else IRCD->SendGlobalNotice(source, this, message); @@ -351,10 +352,12 @@ Server *Server::Find(const Anope::string &name, bool name_only) return NULL; } -Server* Servers::GetUplink() +Server *Servers::GetUplink() { - for (unsigned i = 0; Me && i < Me->GetLinks().size(); ++i) - if (!Me->GetLinks()[i]->IsJuped()) - return Me->GetLinks()[i]; + for (auto *link : Me->GetLinks()) + { + if (!link->IsJuped()) + return link; + } return NULL; } diff --git a/src/siphash.cpp b/src/siphash.cpp deleted file mode 100644 index 78571c390..000000000 --- a/src/siphash.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/* SipHash-2-4 routines. - * - * (C) 2003-2025 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - */ - -/* <MIT License> - Copyright (c) 2013 Marek Majkowski <marek@popcount.org> - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - </MIT License> - Original location: - https://github.com/majek/csiphash/ - Solution inspired by code from: - Samuel Neves (supercop/crypto_auth/siphash24/little) - djb (supercop/crypto_auth/siphash24/little2) - Jean-Philippe Aumasson (https://131002.net/siphash/siphash24.c) -*/ - -#include "services.h" -#include "anope.h" - -// WARNING: This ifdef maze could be a lot simpler but unfortunately -// that will cause find_includes to be unable to parse it. - -#ifdef __APPLE__ -# include <libkern/OSByteOrder.h> -# define _le64toh(x) OSSwapLittleToHostInt64(x) -#endif - -#ifdef __FreeBSD__ -# include <sys/endian.h> -# define _le64toh(x) le64toh(x) -#endif - -#ifdef __linux__ -# include <endian.h> -#endif - -#ifdef __NetBSD__ -# include <sys/endian.h> -# define _le64toh(x) le64toh(x) -#endif - -#ifdef __OpenBSD__ -# include <sys/endian.h> -# define _le64toh(x) le64toh(x) -#endif - -// Windows is always little endian. -#ifdef _WIN32 -# define _le64toh(x) ((uint64_t)(x)) -#endif - -// Attempt to work on unenumerated platforms. -#if defined(le64toh) && !defined(_le64toh) -# define _le64toh le64toh -#endif - -// We can't do anything about this. -#ifndef _le64toh -# error Please define _le64toh for your platform! -#endif - -#define ROTATE(x, b) (uint64_t)( ((x) << (b)) | ( (x) >> (64 - (b))) ) - -#define HALF_ROUND(a,b,c,d,s,t) \ - a += b; c += d; \ - b = ROTATE(b, s) ^ a; \ - d = ROTATE(d, t) ^ c; \ - a = ROTATE(a, 32); - -#define DOUBLE_ROUND(v0,v1,v2,v3) \ - HALF_ROUND(v0,v1,v2,v3,13,16); \ - HALF_ROUND(v2,v1,v0,v3,17,21); \ - HALF_ROUND(v0,v1,v2,v3,13,16); \ - HALF_ROUND(v2,v1,v0,v3,17,21); - - -uint64_t Anope::SipHash24(const void *src, unsigned long src_sz, const char key[16]) -{ - const uint64_t *_key = (uint64_t *)key; - uint64_t k0 = _le64toh(_key[0]); - uint64_t k1 = _le64toh(_key[1]); - uint64_t b = (uint64_t)src_sz << 56; - const uint64_t *in = (uint64_t*)src; - - uint64_t v0 = k0 ^ 0x736f6d6570736575ULL; - uint64_t v1 = k1 ^ 0x646f72616e646f6dULL; - uint64_t v2 = k0 ^ 0x6c7967656e657261ULL; - uint64_t v3 = k1 ^ 0x7465646279746573ULL; - - while (src_sz >= 8) - { - uint64_t mi = _le64toh(*in); - in += 1; src_sz -= 8; - v3 ^= mi; - DOUBLE_ROUND(v0,v1,v2,v3); - v0 ^= mi; - } - - uint64_t t = 0; uint8_t *pt = (uint8_t *)&t; uint8_t *m = (uint8_t *)in; - switch (src_sz) - { - case 7: pt[6] = m[6]; - case 6: pt[5] = m[5]; - case 5: pt[4] = m[4]; - case 4: *((uint32_t*)&pt[0]) = *((uint32_t*)&m[0]); break; - case 3: pt[2] = m[2]; - case 2: pt[1] = m[1]; - case 1: pt[0] = m[0]; - } - b |= _le64toh(t); - - v3 ^= b; - DOUBLE_ROUND(v0,v1,v2,v3); - v0 ^= b; v2 ^= 0xff; - DOUBLE_ROUND(v0,v1,v2,v3); - DOUBLE_ROUND(v0,v1,v2,v3); - return (v0 ^ v1) ^ (v2 ^ v3); -} diff --git a/src/socket_clients.cpp b/src/socket_clients.cpp index 5316be226..1df727efc 100644 --- a/src/socket_clients.cpp +++ b/src/socket_clients.cpp @@ -14,7 +14,7 @@ #include "logger.h" #include "sockets.h" -#include <errno.h> +#include <cerrno> void ConnectionSocket::Connect(const Anope::string &TargetHost, int Port) { diff --git a/src/socket_transport.cpp b/src/socket_transport.cpp index 287e60450..621b2bce4 100644 --- a/src/socket_transport.cpp +++ b/src/socket_transport.cpp @@ -13,14 +13,6 @@ #include "sockets.h" #include "socketengine.h" -BufferedSocket::BufferedSocket() -{ -} - -BufferedSocket::~BufferedSocket() -{ -} - bool BufferedSocket::ProcessRead() { char tbuffer[NET_BUFSIZE]; @@ -55,7 +47,7 @@ bool BufferedSocket::ProcessWrite() return true; } -const Anope::string BufferedSocket::GetLine() +Anope::string BufferedSocket::GetLine() { size_t s = this->read_buffer.find('\n'); if (s == Anope::string::npos) @@ -115,14 +107,6 @@ BinarySocket::DataBlock::~DataBlock() delete [] this->orig; } -BinarySocket::BinarySocket() -{ -} - -BinarySocket::~BinarySocket() -{ -} - bool BinarySocket::ProcessRead() { char tbuffer[NET_BUFSIZE]; diff --git a/src/socketengines/socketengine_epoll.cpp b/src/socketengines/epoll.cpp index 93f558299..f5cd7c090 100644 --- a/src/socketengines/socketengine_epoll.cpp +++ b/src/socketengines/epoll.cpp @@ -15,9 +15,9 @@ #include "socketengine.h" #include "config.h" +#include <cerrno> #include <sys/epoll.h> #include <ulimit.h> -#include <errno.h> static int EngineHandle; static std::vector<epoll_event> events; @@ -53,7 +53,7 @@ void SocketEngine::Change(Socket *s, bool set, SocketFlag flag) memset(&ev, 0, sizeof(ev)); - ev.events = (s->flags[SF_READABLE] ? EPOLLIN : 0) | (s->flags[SF_WRITABLE] ? EPOLLOUT : 0); + ev.events = (s->flags[SF_READABLE] ? EPOLLIN : 0u) | (s->flags[SF_WRITABLE] ? EPOLLOUT : 0u); ev.data.fd = s->GetFD(); int mod; @@ -67,7 +67,7 @@ void SocketEngine::Change(Socket *s, bool set, SocketFlag flag) return; if (epoll_ctl(EngineHandle, mod, ev.data.fd, &ev) == -1) - throw SocketException("Unable to epoll_ctl() fd " + stringify(ev.data.fd) + " to epoll: " + Anope::LastError()); + throw SocketException("Unable to epoll_ctl() fd " + Anope::ToString(ev.data.fd) + " to epoll: " + Anope::LastError()); } void SocketEngine::Process() @@ -76,7 +76,7 @@ void SocketEngine::Process() events.resize(events.size() * 2); int total = epoll_wait(EngineHandle, &events.front(), events.size(), Config->ReadTimeout * 1000); - Anope::CurTime = time(NULL); + Anope::UpdateTime(); /* EINTR can be given if the read timeout expires */ if (total == -1) diff --git a/src/socketengines/socketengine_kqueue.cpp b/src/socketengines/kqueue.cpp index 9b3943d72..271f5a49d 100644 --- a/src/socketengines/socketengine_kqueue.cpp +++ b/src/socketengines/kqueue.cpp @@ -77,7 +77,7 @@ void SocketEngine::Process() static timespec kq_timespec = { Config->ReadTimeout, 0 }; int total = kevent(kq_fd, &change_events.front(), change_count, &event_events.front(), event_events.size(), &kq_timespec); change_count = 0; - Anope::CurTime = time(NULL); + Anope::UpdateTime(); /* EINTR can be given if the read timeout expires */ if (total == -1) diff --git a/src/socketengines/socketengine_poll.cpp b/src/socketengines/poll.cpp index 0c728a055..05b1f3ef5 100644 --- a/src/socketengines/socketengine_poll.cpp +++ b/src/socketengines/poll.cpp @@ -71,7 +71,7 @@ void SocketEngine::Change(Socket *s, bool set, SocketFlag flag) { std::map<int, unsigned>::iterator pos = socket_positions.find(s->GetFD()); if (pos == socket_positions.end()) - throw SocketException("Unable to remove fd " + stringify(s->GetFD()) + " from poll, it does not exist?"); + throw SocketException("Unable to remove fd " + Anope::ToString(s->GetFD()) + " from poll, it does not exist?"); if (pos->second != events.size() - 1) { @@ -90,7 +90,7 @@ void SocketEngine::Change(Socket *s, bool set, SocketFlag flag) { std::map<int, unsigned>::iterator pos = socket_positions.find(s->GetFD()); if (pos == socket_positions.end()) - throw SocketException("Unable to modify fd " + stringify(s->GetFD()) + " in poll, it does not exist?"); + throw SocketException("Unable to modify fd " + Anope::ToString(s->GetFD()) + " in poll, it does not exist?"); pollfd &ev = events[pos->second]; ev.events = (s->flags[SF_READABLE] ? POLLIN : 0) | (s->flags[SF_WRITABLE] ? POLLOUT : 0); @@ -100,7 +100,7 @@ void SocketEngine::Change(Socket *s, bool set, SocketFlag flag) void SocketEngine::Process() { int total = poll(&events.front(), events.size(), Config->ReadTimeout * 1000); - Anope::CurTime = time(NULL); + Anope::UpdateTime(); /* EINTR can be given if the read timeout expires */ if (total < 0) diff --git a/src/socketengines/socketengine_select.cpp b/src/socketengines/select.cpp index c29d971c5..5b895cd9d 100644 --- a/src/socketengines/socketengine_select.cpp +++ b/src/socketengines/select.cpp @@ -16,10 +16,7 @@ #include "logger.h" #include "config.h" -#ifdef _AIX -# undef FD_ZERO -# define FD_ZERO(p) memset((p), 0, sizeof(*(p))) -#endif /* _AIX */ +#include <thread> static int MaxFD; static unsigned FDCount; @@ -97,13 +94,13 @@ void SocketEngine::Process() */ if (FDCount == 0) { - sleep(tval.tv_sec); + std::this_thread::sleep_for(std::chrono::seconds(tval.tv_sec)); return; } #endif int sresult = select(MaxFD + 1, &rfdset, &wfdset, &efdset, &tval); - Anope::CurTime = time(NULL); + Anope::UpdateTime(); if (sresult == -1) { diff --git a/src/sockets.cpp b/src/sockets.cpp index 2227ba607..908bf34f6 100644 --- a/src/sockets.cpp +++ b/src/sockets.cpp @@ -16,7 +16,7 @@ #ifndef _WIN32 #include <arpa/inet.h> -#include <errno.h> +#include <cerrno> #include <fcntl.h> #endif @@ -52,6 +52,8 @@ size_t sockaddrs::size() const return sizeof(sa4); case AF_INET6: return sizeof(sa6); + case AF_UNIX: + return sizeof(saun); default: break; } @@ -67,6 +69,8 @@ int sockaddrs::port() const return ntohs(sa4.sin_port); case AF_INET6: return ntohs(sa6.sin6_port); + case AF_UNIX: + return 0; default: break; } @@ -76,18 +80,51 @@ int sockaddrs::port() const Anope::string sockaddrs::addr() const { - char address[INET6_ADDRSTRLEN]; - switch (sa.sa_family) { case AF_INET: - if (inet_ntop(AF_INET, &sa4.sin_addr, address, sizeof(address))) - return address; + { + char v4address[INET_ADDRSTRLEN]; + if (inet_ntop(AF_INET, &sa4.sin_addr, v4address, sizeof(v4address))) + return v4address; break; + } case AF_INET6: - if (inet_ntop(AF_INET6, &sa6.sin6_addr, address, sizeof(address))) - return address; + { + char v6address[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &sa6.sin6_addr, v6address, sizeof(v6address))) + return v6address; break; + } + case AF_UNIX: + return saun.sun_path; + default: + break; + } + + return ""; +} + +Anope::string sockaddrs::str() const +{ + switch (sa.sa_family) + { + case AF_INET: + { + char v4address[INET_ADDRSTRLEN]; + if (!inet_ntop(AF_INET, &sa4.sin_addr, v4address, sizeof(v4address))) + strcpy(v4address, "0.0.0.0"); + return Anope::printf("%s:%u", v4address, sa4.sin_port); + } + case AF_INET6: + { + char v6address[INET6_ADDRSTRLEN]; + if (!inet_ntop(AF_INET6, &sa6.sin6_addr, v6address, sizeof(v6address))) + strcpy(v6address, "0:0:0:0:0:0:0:0"); + return Anope::printf("[%s]:%u", v6address, sa6.sin6_port); + } + case AF_UNIX: + return saun.sun_path; default: break; } @@ -187,6 +224,15 @@ void sockaddrs::pton(int type, const Anope::string &address, int pport) } break; } + case AF_UNIX: + { + if (address.length() < sizeof(saun.sun_path)) + { + saun.sun_family = AF_UNIX; + memcpy(&saun.sun_path, address.c_str(), address.length() + 1); + } + break; + } default: break; } @@ -236,13 +282,7 @@ cidr::cidr(const Anope::string &ip) Anope::string cidr_range = ip.substr(sl + 1); this->cidr_ip = real_ip; - this->cidr_len = ipv6 ? 128 : 32; - try - { - if (cidr_range.is_pos_number_only()) - this->cidr_len = convertTo<unsigned int>(cidr_range); - } - catch (const ConvertException &) { } + this->cidr_len = Anope::Convert<unsigned int>(cidr_range, ipv6 ? 128 : 32); this->addr.pton(ipv6 ? AF_INET6 : AF_INET, real_ip); } } @@ -306,7 +346,7 @@ bool cidr::match(const sockaddrs &other) byte = len % 8; if (byte) { - uint8_t m = ~0 << (8 - byte); + uint8_t m = ~0u << (8 - byte); return (*ip & m) == (*their_ip & m); } @@ -386,23 +426,23 @@ size_t cidr::hash::operator()(const cidr &s) const } } -int SocketIO::Recv(Socket *s, char *buf, size_t sz) +ssize_t SocketIO::Recv(Socket *s, char *buf, size_t sz) { - int i = recv(s->GetFD(), buf, sz, 0); + ssize_t i = recv(s->GetFD(), buf, sz, 0); if (i > 0) TotalRead += i; return i; } -int SocketIO::Send(Socket *s, const char *buf, size_t sz) +ssize_t SocketIO::Send(Socket *s, const char *buf, size_t sz) { - int i = send(s->GetFD(), buf, sz, 0); + ssize_t i = send(s->GetFD(), buf, sz, 0); if (i > 0) TotalWritten += i; return i; } -int SocketIO::Send(Socket *s, const Anope::string &buf) +ssize_t SocketIO::Send(Socket *s, const Anope::string &buf) { return this->Send(s, buf.c_str(), buf.length()); } @@ -432,7 +472,7 @@ SocketFlag SocketIO::FinishAccept(ClientSocket *cs) void SocketIO::Bind(Socket *s, const Anope::string &ip, int port) { - s->bindaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, ip, port); + s->bindaddr.pton(s->GetFamily(), ip, port); if (bind(s->GetFD(), &s->bindaddr.sa, s->bindaddr.size()) == -1) throw SocketException("Unable to bind to address: " + Anope::LastError()); } @@ -440,7 +480,7 @@ void SocketIO::Bind(Socket *s, const Anope::string &ip, int port) void SocketIO::Connect(ConnectionSocket *s, const Anope::string &target, int port) { s->flags[SF_CONNECTING] = s->flags[SF_CONNECTED] = false; - s->conaddr.pton(s->IsIPv6() ? AF_INET6 : AF_INET, target, port); + s->conaddr.pton(s->GetFamily(), target, port); int c = connect(s->GetFD(), &s->conaddr.sa, s->conaddr.size()); if (c == -1) { @@ -488,12 +528,12 @@ Socket::Socket() throw CoreException("Socket::Socket() ?"); } -Socket::Socket(int s, bool i, int type) +Socket::Socket(int s, int f, int type) { this->io = &NormalSocketIO; - this->ipv6 = i; + this->family = f; if (s == -1) - this->sock = socket(this->ipv6 ? AF_INET6 : AF_INET, type, 0); + this->sock = socket(this->family, type, 0); else this->sock = s; this->SetBlocking(false); @@ -510,14 +550,14 @@ Socket::~Socket() SocketEngine::Sockets.erase(this->sock); } -int Socket::GetFD() const +int Socket::GetFamily() const { - return sock; + return family; } -bool Socket::IsIPv6() const +int Socket::GetFD() const { - return ipv6; + return sock; } bool Socket::SetBlocking(bool state) @@ -567,10 +607,6 @@ ListenSocket::ListenSocket(const Anope::string &bindip, int port, bool i) throw SocketException("Unable to listen: " + Anope::LastError()); } -ListenSocket::~ListenSocket() -{ -} - bool ListenSocket::ProcessRead() { try diff --git a/src/threadengine.cpp b/src/threadengine.cpp index b26463f53..729f3e5ec 100644 --- a/src/threadengine.cpp +++ b/src/threadengine.cpp @@ -13,49 +13,21 @@ #include "threadengine.h" #include "anope.h" -#ifndef _WIN32 -#include <pthread.h> -#endif - -static inline pthread_attr_t *get_engine_attr() -{ - /* Threadengine attributes used by this thread engine */ - static pthread_attr_t attr; - static bool inited = false; - - if (inited == false) - { - if (pthread_attr_init(&attr)) - throw CoreException("Error calling pthread_attr_init"); - if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE)) - throw CoreException("Unable to mark threads as joinable"); - inited = true; - } - - return &attr; -} +#include <stdexcept> static void *entry_point(void *parameter) { Thread *thread = static_cast<Thread *>(parameter); thread->Run(); thread->SetExitState(); - pthread_exit(0); return NULL; } -Thread::Thread() : exit(false) -{ -} - -Thread::~Thread() -{ -} - void Thread::Join() { this->SetExitState(); - pthread_join(handle, NULL); + if (this->handle) + this->handle->join(); } void Thread::SetExitState() @@ -67,15 +39,19 @@ void Thread::SetExitState() void Thread::Exit() { this->SetExitState(); - pthread_exit(0); } void Thread::Start() { - if (pthread_create(&this->handle, get_engine_attr(), entry_point, this)) + try + { + if (!this->handle) + this->handle = std::make_unique<std::thread>(entry_point, this); + } + catch (const std::system_error &err) { this->flags[SF_DEAD] = true; - throw CoreException("Unable to create thread: " + Anope::LastError()); + throw CoreException("Unable to create thread: " + Anope::string(err.what())); } } @@ -89,48 +65,3 @@ void Thread::OnNotify() this->Join(); this->flags[SF_DEAD] = true; } - -Mutex::Mutex() -{ - pthread_mutex_init(&mutex, NULL); -} - -Mutex::~Mutex() -{ - pthread_mutex_destroy(&mutex); -} - -void Mutex::Lock() -{ - pthread_mutex_lock(&mutex); -} - -void Mutex::Unlock() -{ - pthread_mutex_unlock(&mutex); -} - -bool Mutex::TryLock() -{ - return pthread_mutex_trylock(&mutex) == 0; -} - -Condition::Condition() : Mutex() -{ - pthread_cond_init(&cond, NULL); -} - -Condition::~Condition() -{ - pthread_cond_destroy(&cond); -} - -void Condition::Wakeup() -{ - pthread_cond_signal(&cond); -} - -void Condition::Wait() -{ - pthread_cond_wait(&cond, &mutex); -} diff --git a/src/timers.cpp b/src/timers.cpp index db20dcd8b..e280434ab 100644 --- a/src/timers.cpp +++ b/src/timers.cpp @@ -11,26 +11,23 @@ std::multimap<time_t, Timer *> TimerManager::Timers; -Timer::Timer(long time_from_now, time_t now, bool repeating) +Timer::Timer(time_t time_from_now, bool repeating) + : trigger(Anope::CurTime + std::abs(time_from_now)) + , secs(time_from_now) + , repeat(repeating) { - owner = NULL; - trigger = now + time_from_now; - secs = time_from_now; - repeat = repeating; - settime = now; - - TimerManager::AddTimer(this); + if (time_from_now) + TimerManager::AddTimer(this); } -Timer::Timer(Module *creator, long time_from_now, time_t now, bool repeating) +Timer::Timer(Module *creator, time_t time_from_now, bool repeating) + : owner(creator) + , trigger(Anope::CurTime + std::abs(time_from_now)) + , secs(time_from_now) + , repeat(repeating) { - owner = creator; - trigger = now + time_from_now; - secs = time_from_now; - repeat = repeating; - settime = now; - - TimerManager::AddTimer(this); + if (time_from_now) + TimerManager::AddTimer(this); } Timer::~Timer() @@ -55,11 +52,6 @@ bool Timer::GetRepeat() const return repeat; } -time_t Timer::GetSetTime() const -{ - return settime; -} - void Timer::SetSecs(time_t t) { TimerManager::DelTimer(this); @@ -80,7 +72,7 @@ Module *Timer::GetOwner() const void TimerManager::AddTimer(Timer *t) { - Timers.insert(std::make_pair(t->GetTimer(), t)); + Timers.emplace(t->GetTimer(), t); } void TimerManager::DelTimer(Timer *t) @@ -96,20 +88,20 @@ void TimerManager::DelTimer(Timer *t) } } -void TimerManager::TickTimers(time_t ctime) +void TimerManager::TickTimers() { while (!Timers.empty()) { std::multimap<time_t, Timer *>::iterator it = Timers.begin(); Timer *t = it->second; - if (t->GetTimer() > ctime) + if (t->GetTimer() > Anope::CurTime) break; - t->Tick(ctime); + t->Tick(); if (t->GetRepeat()) - t->SetTimer(ctime + t->GetSecs()); + t->SetTimer(Anope::CurTime + t->GetSecs()); else delete t; } diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index 29421aeeb..249a036ad 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -1,6 +1,6 @@ # Find all the *.cpp files within the current source directory, and sort the list file(GLOB TOOLS_SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp") -sort_list(TOOLS_SRCS) +list(SORT TOOLS_SRCS) # Set all the files to use C++ as well as set their compile flags set_source_files_properties(${TOOLS_SRCS} PROPERTIES LANGUAGE CXX COMPILE_FLAGS "${CXXFLAGS}") @@ -9,34 +9,34 @@ set_source_files_properties(${TOOLS_SRCS} PROPERTIES LANGUAGE CXX COMPILE_FLAGS foreach(SRC ${TOOLS_SRCS}) # Convert the source file extension to have no extension string(REGEX REPLACE "\\.cpp$" "" EXE ${SRC}) - # Calculate the header file dependencies for the given source file - calculate_depends(${SRC}) # Only continue if this file isn't skipped if(NOT SKIP) # Generate the executable and set its linker flags, also set it to depend on the main Anope executable to be built beforehand add_executable(${EXE} ${SRC}) set_target_properties(${EXE} PROPERTIES LINKER_LANGUAGE CXX LINK_FLAGS "${LDFLAGS}") add_dependencies(${EXE} ${PROGRAM_NAME}) - # Only for Windows, set anopesmtp to require the wsock32 library - if(WIN32 AND ${EXE} STREQUAL anopesmtp) - target_link_libraries(${EXE} wsock32) - endif(WIN32 AND ${EXE} STREQUAL anopesmtp) - if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS" AND ${EXE} STREQUAL anopesmtp) - target_link_libraries(${EXE} socket nsl) - endif(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS" AND ${EXE} STREQUAL anopesmtp) # Set the executable to be installed to the bin directory under the main directory install(TARGETS ${EXE} DESTINATION ${BIN_DIR} + RUNTIME ) # Add the executable to the list of files for CPack to ignore set(EXE_BINARY "$<TARGET_FILE:${EXE}>") get_filename_component(EXE_BINARY ${EXE_BINARY} NAME) add_to_cpack_ignored_files("${EXE_BINARY}$" TRUE) - endif(NOT SKIP) -endforeach(SRC) + endif() +endforeach() -# If not on Windows, generate anoperc and install it along with mydbgen +# If not on Windows, generate anope.service and anoperc if(NOT WIN32) + if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + configure_file(${Anope_SOURCE_DIR}/src/tools/anope.service.in ${Anope_BINARY_DIR}/src/tools/anope.service) + install( + PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/anope.service + DESTINATION ${BIN_DIR} + ) + endif() + configure_file(${Anope_SOURCE_DIR}/src/tools/anoperc.in ${Anope_BINARY_DIR}/src/tools/anoperc) install (PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/anoperc DESTINATION ${BIN_DIR} @@ -44,9 +44,9 @@ if(NOT WIN32) install (PROGRAMS geoipupdate.sh DESTINATION ${BIN_DIR} ) -endif(NOT WIN32) +endif() # 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\")") -endif(NOT WIN32 AND RUNGROUP) +endif() diff --git a/src/tools/anope.service.in b/src/tools/anope.service.in new file mode 100644 index 000000000..6fe8f652d --- /dev/null +++ b/src/tools/anope.service.in @@ -0,0 +1,17 @@ +[Unit] +After=network.target +Description=Anope IRC Services +Documentation=https://wiki.anope.org/ +After=network-online.target +Wants=network-online.target + +[Service] +ExecReload=/bin/kill -HUP $MAINPID +ExecStart=@BIN_DIR@/@PROGRAM_NAME@ --nofork +Restart=on-failure +Type=simple +WorkingDirectory=@CMAKE_INSTALL_PREFIX@ + +[Install] +WantedBy=multi-user.target + diff --git a/src/tools/anoperc.in b/src/tools/anoperc.in index 0d3b2d07d..24fc77e83 100644 --- a/src/tools/anoperc.in +++ b/src/tools/anoperc.in @@ -1,8 +1,8 @@ #!/bin/sh # -# Configuration script for Services +# Configuration script for Anope # -# (C) 2003-2024 Anope Team +# (C) 2003-2025 Anope Team # Contact us at team@anope.org # # Please read COPYING and README for further details. @@ -14,8 +14,8 @@ -ANOPEPID="@INSTDIR@/data/services.pid" -ANOPROG="@INSTDIR@/bin/services" +ANOPEPID="@INSTDIR@/data/anope.pid" +ANOPROG="@INSTDIR@/bin/anope" LOG="@INSTDIR@/logs/" ARCVERSION="2" diff --git a/src/tools/anopesmtp.cpp b/src/tools/anopesmtp.cpp deleted file mode 100644 index d1c3f7b64..000000000 --- a/src/tools/anopesmtp.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* smtp stuff handler for win32. - * - * (C) 2003-2025 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. - * - * Written by Dominick Meglio <codemastr@unrealircd.com> - * *nix port by Trystan Scott Lee <trystan@nomadirc.net> - */ - -#include "sysconf.h" - -/* Some Linux boxes (or maybe glibc includes) require this for the - * prototype of strsignal(). */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif - -#include <string> -#include <vector> -#include <cstdarg> -#include <cstdio> -#include <cstdlib> -#include <cstring> -#include <ctime> -#include <cerrno> -#include <iostream> -#include <fstream> - -#ifndef _WIN32 -# include <unistd.h> -# include <netdb.h> -# include <netinet/in.h> -# include <sys/socket.h> -# include <arpa/inet.h> -# include <sys/time.h> -#else -# include <winsock.h> -# define WIN32_LEAN_AND_MEAN -# include <windows.h> -#endif - -#include <sys/types.h> - -#ifdef _AIX -extern int strcasecmp(const char *, const char *); -extern int strncasecmp(const char *, const char *, size_t); -# if 0 /* These break on some AIX boxes (4.3.1 reported). */ -extern int socket(int, int, int); -extern int connect(int, struct sockaddr *, int); -# endif -#endif /* _AIX */ - -/* Some SUN fixs */ -#ifdef __sun -/* Solaris specific code, types that do not exist in Solaris' - * * sys/types.h - * **/ -# ifndef INADDR_NONE -# define INADDR_NONE (-1) -# endif -#endif - -#ifdef _WIN32 -typedef SOCKET ano_socket_t; -#define ano_sockclose(fd) closesocket(fd) -#define ano_sockread(fd, buf, len) recv(fd, buf, len, 0) -#define ano_sockwrite(fd, buf, len) send(fd, buf, len, 0) -#else -typedef int ano_socket_t; -#define ano_sockclose(fd) close(fd) -#define ano_sockread(fd, buf, len) read(fd, buf, len) -#define ano_sockwrite(fd, buf, len) write(fd, buf, len) -#define SOCKET_ERROR -1 -#endif - -/* Data structures */ -struct smtp_message -{ - std::vector<std::string> smtp_headers; - std::vector<std::string> smtp_body; - std::string from; - std::string to; - ano_socket_t sock; -}; - -int smtp_debug = 0; - -struct smtp_message smail; - -static std::string get_logname(struct tm *tm = NULL) -{ - char timestamp[32]; - - if (!tm) - { - time_t t = time(NULL); - tm = localtime(&t); - } - - strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm); - std::string name = std::string("anopesmtp.") + timestamp; - return name; -} - -/* TimeStamp for Email Header */ -static std::string GetTimeStamp() -{ - char tbuf[256]; - time_t t = time(NULL); - struct tm *tm = gmtime(&t); - - strftime(tbuf, sizeof(tbuf) - 1, "%a, %d %b %Y %H:%M:%S +0000", tm); - - return tbuf; -} - -/* Log stuff to the log file with a datestamp. Note that errno is - * preserved by this routine and log_perror(). - */ - -void alog(const char *fmt, ...) -{ - if (!smtp_debug || !fmt) - return; - - std::fstream file; - file.open(get_logname().c_str(), std::ios_base::out | std::ios_base::app); - - if (!file.is_open()) - return; - - va_list args; - va_start(args, fmt); - - time_t t = time(NULL); - struct tm *tm = localtime(&t); - - char buf[256]; - strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", tm); - file << buf; - vsnprintf(buf, sizeof(buf), fmt, args); - file << buf << std::endl; - va_end(args); - va_end(args); - - file.close(); -} - -/* Remove a trailing \r\n */ -std::string strip(const std::string &buf) -{ - std::string newbuf = buf; - char c = newbuf[newbuf.size() - 1]; - while (c == '\n' || c == '\r') - { - newbuf.erase(newbuf.end() - 1); - c = newbuf[newbuf.size() - 1]; - } - return newbuf; -} - -/* Is the buffer a header? */ -bool smtp_is_header(const std::string &buf) -{ - size_t tmp = buf.find(' '); - - if (tmp == std::string::npos) - return false; - - if (tmp > 0 && buf[tmp - 1] == ':') - return true; - return false; -} - -/* Parse a header into a name and value */ -void smtp_parse_header(const std::string &buf, std::string &header, std::string &value) -{ - std::string newbuf = strip(buf); - - size_t space = newbuf.find(' '); - if (space != std::string::npos) - { - header = newbuf.substr(0, space); - value = newbuf.substr(space + 1); - } - else - { - header = newbuf; - value = ""; - } -} - -/* Have we reached the end of input? */ -bool smtp_is_end(const std::string &buf) -{ - if (buf[0] == '.') - if (buf[1] == '\r' || buf[1] == '\n') - return true; - - return false; -} - -/* Set who the email is to */ -void smtp_set_to(const std::string &to) -{ - smail.to = to; - size_t c = smail.to.rfind('<'); - if (c != std::string::npos && c + 1 < smail.to.size()) - { - smail.to = smail.to.substr(c + 1); - smail.to.erase(smail.to.end() - 1); - } -} - -/* Establish a connection to the SMTP server */ -int smtp_connect(const char *host, unsigned short port) -{ - struct sockaddr_in addr; - - if ((smail.sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) - return 0; - - if ((addr.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) - { - struct hostent *hent; - if (!(hent = gethostbyname(host))) - return 0; - memcpy(&addr.sin_addr, hent->h_addr, hent->h_length); - } - addr.sin_family = AF_INET; - addr.sin_port = htons(port ? port : 25); - if (connect(smail.sock, reinterpret_cast<struct sockaddr *>(&addr), sizeof(struct sockaddr_in)) == SOCKET_ERROR) - { - ano_sockclose(smail.sock); - return 0; - } - - return 1; -} - -/* Send a line of text */ -int smtp_send(const char *text) -{ - int result = ano_sockwrite(smail.sock, text, strlen(text)); - - alog("SMTP: sent %s",text); - - if (result == SOCKET_ERROR) - ano_sockclose(smail.sock); - - return result; -} - -/* Read a line of text */ -int smtp_read(char *buf, int len) -{ - int result; - - memset(buf, 0, len); - result = ano_sockread(smail.sock, buf, len); - - if (result == SOCKET_ERROR) - ano_sockclose(smail.sock); - - return result; -} - -/* Retrieve a response code */ -int smtp_get_code(const std::string &text) -{ - size_t tmp = text.find(' '); - - if (tmp == std::string::npos) - return 0; - - return atol(text.substr(0, tmp).c_str()); -} - -/* Send the email */ -int smtp_send_email() -{ - char buf[1024]; - if (!smtp_read(buf, 1024)) - { - alog("SMTP: error reading buffer"); - return 0; - } - - int code = smtp_get_code(buf); - if (code != 220) - { - alog("SMTP: error expected code 220 got %d",code); - return 0; - } - - if (!smtp_send("HELO anope\r\n")) - { - alog("SMTP: error writing to socket"); - return 0; - } - - if (!smtp_read(buf, 1024)) - { - alog("SMTP: error reading buffer"); - return 0; - } - - code = smtp_get_code(buf); - if (code != 250) - { - alog("SMTP: error expected code 250 got %d",code); - return 0; - } - - strcpy(buf, "MAIL FROM: <"); - strcat(buf, smail.from.c_str()); - strcat(buf, ">\r\n"); - - if (!smtp_send(buf)) - { - alog("SMTP: error writing to socket"); - return 0; - } - - if (!smtp_read(buf, 1024)) - { - alog("SMTP: error reading buffer"); - return 0; - } - - code = smtp_get_code(buf); - if (code != 250) - return 0; - - strcpy(buf, "RCPT TO: <"); - strcat(buf, smail.to.c_str()); - strcat(buf, ">\r\n"); - - if (!smtp_send(buf)) - { - alog("SMTP: error writing to socket"); - return 0; - } - - if (!smtp_read(buf, 1024)) - { - alog("SMTP: error reading buffer"); - return 0; - } - - code = smtp_get_code(buf); - if (smtp_get_code(buf) != 250) - { - alog("SMTP: error expected code 250 got %d",code); - return 0; - } - - if (!smtp_send("DATA\r\n")) - { - alog("SMTP: error writing to socket"); - return 0; - } - - if (!smtp_read(buf, 1024)) - { - alog("SMTP: error reading buffer"); - return 0; - } - - code = smtp_get_code(buf); - if (code != 354) - { - alog("SMTP: error expected code 354 got %d",code); - return 0; - } - - for (std::vector<std::string>::const_iterator it = smail.smtp_headers.begin(), it_end = smail.smtp_headers.end(); it != it_end; ++it) - if (!smtp_send(it->c_str())) - { - alog("SMTP: error writing to socket"); - return 0; - } - - if (!smtp_send("\r\n")) - { - alog("SMTP: error writing to socket"); - return 0; - } - - bool skip_done = false; - for (std::vector<std::string>::const_iterator it = smail.smtp_body.begin(), it_end = smail.smtp_body.end(); it != it_end; ++it) - if (skip_done) - { - if (!smtp_send(it->c_str())) - { - alog("SMTP: error writing to socket"); - return 0; - } - } - else - skip_done = true; - - if (!smtp_send("\r\n.\r\n")) - { - alog("SMTP: error writing to socket"); - return 0; - } - - if (!smtp_read(buf, 1024)) - { - alog("SMTP: error reading buffer"); - return 0; - } - - code = smtp_get_code(buf); - if (code != 250) - { - alog("SMTP: error expected code 250 got %d",code); - return 0; - } - - return 1; -} - -void smtp_disconnect() -{ - char buf[1024]; - - if (!smtp_send("QUIT\r\n")) - { - alog("SMTP: error writing to socket"); - } - - if (!smtp_read(buf, 1024)) - { - alog("SMTP: error reading buffer"); - } - - int code = smtp_get_code(buf); - if (code != 221) - { - alog("SMTP: error expected code 221 got %d",code); - } - - ano_sockclose(smail.sock); -} - -int main(int argc, char *argv[]) -{ - /* Win32 stuff */ -#ifdef _WIN32 - WSADATA wsa; -#endif - - if (argc == 1) - return 0; - - if (argc == 3 && !strcmp(argv[2], "--debug")) - smtp_debug = 1; - - char *server = strtok(argv[1], ":"), *aport; - short port; - if ((aport = strtok(NULL, ""))) - port = atoi(aport); - else - port = 25; - - if (!server) - { - alog("No Server"); - /* Bad, bad, bad. This was a return from main with no value! -GD */ - return 0; - } - else - alog("SMTP: server %s port %d",server,port); - - /* The WSAStartup function initiates use of WS2_32.DLL by a process. */ - /* guessing we can skip it under *nix */ -#ifdef _WIN32 - if (WSAStartup(MAKEWORD(1, 1), &wsa)) - return 0; -#endif - - char buf[8192]; - bool headers_done = false; - /* Read the message and parse it */ - while (fgets(buf, 8192, stdin)) - { - if (smtp_is_header(buf) && !headers_done) - { - smail.smtp_headers.push_back(strip(buf) + "\r\n"); - std::string header, value; - smtp_parse_header(buf, header, value); - if (header == "From:") - { - alog("SMTP: from: %s", value.c_str()); - smail.from = value; - } - else if (header == "To:") - { - alog("SMTP: to: %s", value.c_str()); - smtp_set_to(value); - } - else if (smtp_is_end(buf)) - break; - else - { - smail.smtp_headers.push_back("Date: " + GetTimeStamp() + "\r\n"); - headers_done = true; - smail.smtp_body.push_back(strip(buf) + "\r\n"); - } - } - else - smail.smtp_body.push_back(strip(buf) + "\r\n"); - } - - if (!smtp_connect(server, port)) - { - alog("SMTP: failed to connect to %s:%d", server, port); - return 0; - } - if (!smtp_send_email()) - { - alog("SMTP: error during sending of mail"); - return 0; - } - smtp_disconnect(); - - return 1; -} diff --git a/src/tools/mkauthors b/src/tools/mkauthors new file mode 100755 index 000000000..1a864374d --- /dev/null +++ b/src/tools/mkauthors @@ -0,0 +1,57 @@ +#!/usr/bin/env perl +# +# (C) 2003-2025 Anope Team +# Contact us at team@anope.org +# +# Please read COPYING and README for further details. +# +# Based on the original code of Epona by Lara. +# Based on the original code of Services by Andy Church. + +use v5.26.0; +use strict; +use warnings FATAL => qw(all); + +use File::Basename qw(dirname); +use File::Spec::Functions qw(catfile); +use FindBin qw($RealDir); + +my %committers; +for my $committer (split /\n+/, `git log --pretty='%an <%ae>%n%(trailers:key=Co-Authored-By,valueonly)' HEAD`) { + $committers{$committer} ||= 0; + $committers{$committer} += 1; +} + +my %authors; +for my $committer (keys %committers) { + open(my $fh, '-|', 'git', 'check-mailmap', $committer); + chomp(my $author = <$fh>); + close $fh; + + $author = $1 if $author =~ /^(.+) <(?:\S+\@localhost|\S+\@users.noreply.github.com)>$/; + next if $author =~ /\[bot\]$/; + next if $author =~ /^\(svnadmin\)$/; + + $authors{$author} ||= 0; + $authors{$author} += $committers{$committer}; +} + + +my $author_file = catfile dirname(dirname($RealDir)), 'docs', 'AUTHORS.txt'; +open(my $fh, '>', $author_file) or die "Unable to write $author_file: $!"; +say $fh <<"EOH"; +Since the first commit in March 2004 ${\scalar %authors} people have submitted patches, commits, +and other useful contributions to Anope. These people, ordered by the number of +contributions they have made, are: +EOH + +for my $author (sort { $authors{$b} <=> $authors{$a} or lc($a) cmp lc($b) } keys %authors) { + say $fh " * $author"; +} +close $fh; + +if ($ENV{MKAUTHORS_COMMIT} // 1) { + system 'git', 'commit', + '--message', 'Update author list.', + '--', $author_file; +} diff --git a/src/uplink.cpp b/src/uplink.cpp index f1b524dd1..c8bfc0b3b 100644 --- a/src/uplink.cpp +++ b/src/uplink.cpp @@ -17,12 +17,16 @@ UplinkSocket *UplinkSock = NULL; -class ReconnectTimer : public Timer +class ReconnectTimer final + : public Timer { - public: - ReconnectTimer(int wait) : Timer(wait) { } +public: + ReconnectTimer(time_t wait) + : Timer(wait) + { + } - void Tick(time_t) + void Tick() override { try { @@ -30,7 +34,7 @@ class ReconnectTimer : public Timer } catch (const SocketException &ex) { - Log(LOG_TERMINAL) << "Unable to connect to uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].host << ":" << Config->Uplinks[Anope::CurrentUplink].port << "): " << ex.GetReason(); + Log(LOG_TERMINAL) << "Unable to connect to uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].str() << "): " << ex.GetReason(); } } }; @@ -43,21 +47,63 @@ void Uplink::Connect() return; } - if (static_cast<unsigned>(++Anope::CurrentUplink) >= Config->Uplinks.size()) + if (++Anope::CurrentUplink >= Config->Uplinks.size()) Anope::CurrentUplink = 0; Configuration::Uplink &u = Config->Uplinks[Anope::CurrentUplink]; new UplinkSocket(); - if (!Config->GetBlock("serverinfo")->Get<const Anope::string>("localhost").empty()) - UplinkSock->Bind(Config->GetBlock("serverinfo")->Get<const Anope::string>("localhost")); + if (!Config->GetBlock("serverinfo").Get<const Anope::string>("localhost").empty()) + UplinkSock->Bind(Config->GetBlock("serverinfo").Get<const Anope::string>("localhost")); FOREACH_MOD(OnPreServerConnect, ()); - Anope::string ip = Anope::Resolve(u.host, u.ipv6 ? AF_INET6 : AF_INET); - Log(LOG_TERMINAL) << "Attempting to connect to uplink #" << (Anope::CurrentUplink + 1) << " " << u.host << " (" << ip << '/' << u.port << ") with protocol " << IRCD->GetProtocolName(); + Anope::string ip = Anope::Resolve(u.host, u.protocol); + Log(LOG_TERMINAL) << "Attempting to connect to uplink #" << (Anope::CurrentUplink + 1) << " " << ip << " (" << u.str() << ")"; UplinkSock->Connect(ip, u.port); } -UplinkSocket::UplinkSocket() : Socket(-1, Config->Uplinks[Anope::CurrentUplink].ipv6), ConnectionSocket(), BufferedSocket() +void Uplink::SendInternal(const Anope::map<Anope::string> &tags, const MessageSource &source, const Anope::string &command, const std::vector<Anope::string> ¶ms) +{ + if (!UplinkSock) + { + Log(LOG_DEBUG) << "Attempted to send \"" << command << "\" from " << source.GetName() << " with a null uplink socket"; + return; + } + + Anope::string message; + if (!IRCD->Format(message, tags, source, command, params)) + return; + + UplinkSock->Write(message); + + Log(LOG_RAWIO) << "Sent " << message; + if (Anope::ProtocolDebug) + { + if (tags.empty()) + Log() << "\tNo tags"; + else + { + for (const auto &[tname, tvalue] : tags) + Log() << "\tTag " << tname << ": " << tvalue; + } + + if (source.GetSource().empty()) + Log() << "\tNo source"; + else + Log() << "\tSource: " << source.GetSource(); + + Log() << "\tCommand: " << command; + + if (params.empty()) + Log() << "\tNo params"; + else + { + for (size_t i = 0; i < params.size(); ++i) + Log() << "\tParam " << i << ": " << params[i]; + } + } +} + +UplinkSocket::UplinkSocket() : Socket(-1, Config->Uplinks[Anope::CurrentUplink].protocol), ConnectionSocket(), BufferedSocket() { error = false; UplinkSock = this; @@ -79,15 +125,14 @@ UplinkSocket::~UplinkSocket() { FOREACH_MOD(OnServerDisconnect, ()); - for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) + for (const auto &[_, u] : UserListByNick) { - User *u = it->second; - if (u->server == Me) { /* Don't use quitmsg here, it may contain information you don't want people to see */ - IRCD->SendQuit(u, "Shutting down"); - BotInfo* bi = BotInfo::Find(u->GetUID()); + const auto *reason = Anope::Restarting ? "Restarting" : "Shutting down"; + IRCD->SendQuit(u, reason, Anope::QuitReason); + BotInfo *bi = BotInfo::Find(u->GetUID()); if (bi != NULL) bi->introduced = false; } @@ -120,7 +165,7 @@ UplinkSocket::~UplinkSocket() } else if (!Anope::Quitting) { - time_t retry = Config->GetBlock("options")->Get<time_t>("retrywait"); + time_t retry = Config->GetBlock("options").Get<time_t>("retrywait"); Log() << "Disconnected, retrying in " << retry << " seconds"; new ReconnectTimer(retry); @@ -130,7 +175,7 @@ UplinkSocket::~UplinkSocket() bool UplinkSocket::ProcessRead() { bool b = BufferedSocket::ProcessRead(); - for (Anope::string buf; (buf = this->GetLine()).empty() == false;) + for (Anope::string buf; !(buf = this->GetLine()).empty();) { Anope::Process(buf); User::QuitUsers(); @@ -141,7 +186,7 @@ bool UplinkSocket::ProcessRead() void UplinkSocket::OnConnect() { - Log(LOG_TERMINAL) << "Successfully connected to uplink #" << (Anope::CurrentUplink + 1) << " " << Config->Uplinks[Anope::CurrentUplink].host << ":" << Config->Uplinks[Anope::CurrentUplink].port; + Log(LOG_TERMINAL) << "Successfully connected to uplink #" << (Anope::CurrentUplink + 1) << " " << Config->Uplinks[Anope::CurrentUplink].str(); IRCD->SendConnect(); FOREACH_MOD(OnServerConnect, ()); } @@ -149,64 +194,6 @@ void UplinkSocket::OnConnect() void UplinkSocket::OnError(const Anope::string &err) { Anope::string what = !this->flags[SF_CONNECTED] ? "Unable to connect to" : "Lost connection from"; - Log(LOG_TERMINAL) << what << " uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].host << ":" << Config->Uplinks[Anope::CurrentUplink].port << ")" << (!err.empty() ? (": " + err) : ""); + Log(LOG_TERMINAL) << what << " uplink #" << (Anope::CurrentUplink + 1) << " (" << Config->Uplinks[Anope::CurrentUplink].str() << ")" << (!err.empty() ? (": " + err) : ""); error |= !err.empty(); } - -UplinkSocket::Message::Message() : source(Me) -{ -} - -UplinkSocket::Message::Message(const MessageSource &src) : source(src) -{ -} - -UplinkSocket::Message::~Message() -{ - Anope::string message_source; - - if (this->source.GetServer() != NULL) - { - const Server *s = this->source.GetServer(); - - if (s != Me && !s->IsJuped()) - { - Log(LOG_DEBUG) << "Attempted to send \"" << this->buffer.str() << "\" from " << s->GetName() << " who is not from me?"; - return; - } - - message_source = s->GetSID(); - } - else if (this->source.GetUser() != NULL) - { - const User *u = this->source.GetUser(); - - if (u->server != Me && !u->server->IsJuped()) - { - Log(LOG_DEBUG) << "Attempted to send \"" << this->buffer.str() << "\" from " << u->nick << " who is not from me?"; - return; - } - - const BotInfo *bi = this->source.GetBot(); - if (bi != NULL && bi->introduced == false) - { - Log(LOG_DEBUG) << "Attempted to send \"" << this->buffer.str() << "\" from " << bi->nick << " when not introduced"; - return; - } - - message_source = u->GetUID(); - } - - if (!UplinkSock) - { - if (!message_source.empty()) - Log(LOG_DEBUG) << "Attempted to send \"" << message_source << " " << this->buffer.str() << "\" with UplinkSock NULL"; - else - Log(LOG_DEBUG) << "Attempted to send \"" << this->buffer.str() << "\" with UplinkSock NULL"; - return; - } - - Anope::string sent = IRCD->Format(message_source, this->buffer.str()); - UplinkSock->Write(sent); - Log(LOG_RAWIO) << "Sent: " << sent; -} diff --git a/src/users.cpp b/src/users.cpp index 13a19c0ef..8f8e9570d 100644 --- a/src/users.cpp +++ b/src/users.cpp @@ -31,7 +31,8 @@ time_t MaxUserTime = 0; std::list<User *> User::quitting_users; -User::User(const Anope::string &snick, const Anope::string &sident, const Anope::string &shost, const Anope::string &svhost, const Anope::string &uip, Server *sserver, const Anope::string &srealname, time_t ts, const Anope::string &smodes, const Anope::string &suid, NickCore *account) : ip(uip) +User::User(const Anope::string &snick, const Anope::string &sident, const Anope::string &shost, const Anope::string &svhost, const Anope::string &uip, Server *sserver, const Anope::string &srealname, time_t ts, const Anope::string &smodes, const std::vector<Anope::string> &smodeparams, const Anope::string &suid, NickCore *account) + : ip(uip) { if (snick.empty() || sident.empty() || shost.empty()) throw CoreException("Bad args passed to User::User"); @@ -40,7 +41,6 @@ User::User(const Anope::string &snick, const Anope::string &sident, const Anope: quit = false; server = NULL; invalid_pw_count = invalid_pw_time = lastmemosend = lastnickreg = lastmail = 0; - on_access = false; this->nick = snick; this->ident = sident; @@ -50,7 +50,7 @@ User::User(const Anope::string &snick, const Anope::string &sident, const Anope: this->server = sserver; this->realname = srealname; this->timestamp = this->signon = ts; - this->SetModesInternal(sserver, "%s", smodes.c_str()); + this->SetModesInternal(sserver, smodes, smodeparams); this->uid = suid; this->super_admin = false; this->nc = NULL; @@ -93,7 +93,7 @@ static void CollideKill(User *target, const Anope::string &reason) else { // Be sure my user is really dead - IRCD->SendQuit(target, "%s", reason.c_str()); + IRCD->SendQuit(target, reason); // Reintroduce my client if (BotInfo *bi = dynamic_cast<BotInfo *>(target)) @@ -111,7 +111,7 @@ static void Collide(User *u, const Anope::string &id, const Anope::string &type) CollideKill(u, type); } -User* User::OnIntroduce(const Anope::string &snick, const Anope::string &sident, const Anope::string &shost, const Anope::string &svhost, const Anope::string &sip, Server *sserver, const Anope::string &srealname, time_t ts, const Anope::string &smodes, const Anope::string &suid, NickCore *nc) +User *User::OnIntroduce(const Anope::string &snick, const Anope::string &sident, const Anope::string &shost, const Anope::string &svhost, const Anope::string &sip, Server *sserver, const Anope::string &srealname, time_t ts, const Anope::string &smodes, const Anope::string &suid, NickCore *nc, const std::vector<Anope::string> &smodeparams) { // How IRCds handle collisions varies a lot, for safety well just always kill both sides // With properly set qlines, this can almost never happen anyway @@ -133,7 +133,7 @@ User* User::OnIntroduce(const Anope::string &snick, const Anope::string &sident, } } - return new User(snick, sident, shost, svhost, sip, sserver, srealname, ts, smodes, suid, nc); + return new User(snick, sident, shost, svhost, sip, sserver, srealname, ts, smodes, smodeparams, suid, nc); } void User::ChangeNick(const Anope::string &newnick, time_t ts) @@ -153,14 +153,14 @@ void User::ChangeNick(const Anope::string &newnick, time_t ts) else { NickAlias *old_na = NickAlias::Find(this->nick); - if (old_na && (this->IsIdentified(true) || this->IsRecognized())) + if (old_na && this->IsIdentified(true)) old_na->last_seen = Anope::CurTime; UserListByNick.erase(this->nick); this->nick = newnick; - User* &other = UserListByNick[this->nick]; + User *&other = UserListByNick[this->nick]; if (other) { CollideKill(this, "Nick collision"); @@ -169,11 +169,7 @@ void User::ChangeNick(const Anope::string &newnick, time_t ts) } other = this; - on_access = false; NickAlias *na = NickAlias::Find(this->nick); - if (na) - on_access = na->nc->IsOnAccess(this); - if (na && na->nc == this->Account()) { na->last_seen = Anope::CurTime; @@ -282,7 +278,7 @@ void User::SetRealname(const Anope::string &srealname) this->realname = srealname; NickAlias *na = NickAlias::Find(this->nick); - if (na && (this->IsIdentified(true) || this->IsRecognized())) + if (na && this->IsIdentified(true)) na->last_realname = srealname; Log(this, "realname") << "changed realname to " << srealname; @@ -332,26 +328,52 @@ void User::SendMessage(BotInfo *source, const char *fmt, ...) va_end(args); } -void User::SendMessage(BotInfo *source, const Anope::string &msg) +void User::SendMessage(BotInfo *source, int count, const char *singular, const char *plural, ...) { - const char *translated_message = Language::Translate(this, msg.c_str()); + va_list args; + char buf[BUFSIZE] = ""; + + const char *translated_message = Language::Translate(this, count, singular, plural); - /* Send privmsg instead of notice if: - * - UsePrivmsg is enabled - * - The user is not registered and NSDefMsg is enabled - * - The user is registered and has set /ns set msg on - */ - bool send_privmsg = Config->UsePrivmsg && ((!this->nc && Config->DefPrivmsg) || (this->nc && this->nc->HasExt("MSG"))); - sepstream sep(translated_message, '\n', true); - for (Anope::string tok; sep.GetToken(tok);) + va_start(args, plural); + vsnprintf(buf, BUFSIZE - 1, translated_message, args); + + this->SendMessage(source, Anope::string(buf)); + + va_end(args); +} + +namespace +{ + void SendMessageInternal(BotInfo *source, User *target, const Anope::string &msg, const Anope::map<Anope::string> &tags) { - if (send_privmsg) - IRCD->SendPrivmsg(source, this->GetUID(), "%s", tok.c_str()); - else - IRCD->SendNotice(source, this->GetUID(), "%s", tok.c_str()); + const char *translated_message = Language::Translate(target, msg.c_str()); + + sepstream sep(translated_message, '\n', true); + for (Anope::string tok; sep.GetToken(tok);) + { + if (target->ShouldPrivmsg()) + IRCD->SendPrivmsg(source, target->GetUID(), tok, tags); + else + IRCD->SendNotice(source, target->GetUID(), tok, tags); + } } } +void User::SendMessage(BotInfo *source, const Anope::string &msg) +{ + SendMessageInternal(source, this, msg, {}); +} + +void User::SendMessage(CommandSource &source, const Anope::string &msg) +{ + Anope::map<Anope::string> tags; + if (!source.msgid.empty()) + tags["+draft/reply"] = source.msgid; + + SendMessageInternal(*source.service, this, msg, tags); +} + void User::Identify(NickAlias *na) { if (this->nick.equals_ci(na->nick)) @@ -372,17 +394,18 @@ void User::Identify(NickAlias *na) { if (!this->nc->o->ot->modes.empty()) { - this->SetModes(NULL, "%s", this->nc->o->ot->modes.c_str()); - this->SendMessage(NULL, "Changing your usermodes to \002%s\002", this->nc->o->ot->modes.c_str()); - UserMode *um = ModeManager::FindUserModeByName("OPER"); + auto *um = ModeManager::FindUserModeByName("OPER"); if (um && !this->HasMode("OPER") && this->nc->o->ot->modes.find(um->mchar) != Anope::string::npos) IRCD->SendOper(this); + + this->SetModes(NULL, this->nc->o->ot->modes); + this->SendMessage(NULL, _("Changing your usermodes to \002%s\002"), this->nc->o->ot->modes.c_str()); } if (IRCD->CanSetVHost && !this->nc->o->vhost.empty()) { - this->SendMessage(NULL, "Changing your vhost to \002%s\002", this->nc->o->vhost.c_str()); + this->SendMessage(NULL, _("Changing your vhost to \002%s\002"), this->nc->o->vhost.c_str()); this->SetDisplayedHost(this->nc->o->vhost); - IRCD->SendVhost(this, "", this->nc->o->vhost); + IRCD->SendVHost(this, "", this->nc->o->vhost); } } } @@ -424,6 +447,11 @@ NickCore *User::Account() const return this->nc; } +NickAlias *User::AccountNick() const +{ + return this->nc ? this->nc->na : nullptr; +} + bool User::IsIdentified(bool check_nick) const { if (check_nick && this->nc) @@ -432,56 +460,58 @@ bool User::IsIdentified(bool check_nick) const return na && *na->nc == *this->nc; } - return this->nc ? true : false; + return this->nc; } -bool User::IsRecognized(bool check_secure) const +bool User::IsSecurelyConnected() const { - if (check_secure && on_access) - { - const NickAlias *na = NickAlias::Find(this->nick); - - if (!na || na->nc->HasExt("NS_SECURE")) - return false; - } - - return on_access; + return HasMode("SSL") || HasExt("ssl"); } bool User::IsServicesOper() { if (!this->nc || !this->nc->IsServicesOper()) - // No opertype. - return false; - else if (this->nc->o->require_oper && !this->HasMode("OPER")) - return false; - else if (!this->nc->o->certfp.empty() && this->fingerprint != this->nc->o->certfp) - // Certfp mismatch - return false; - else if (!this->nc->o->hosts.empty()) + return false; // Account isn't a services oper. + + auto *oper = this->nc->o; + if (oper->require_oper && !this->HasMode("OPER")) + return false; // User isn't an ircd oper. + + if (!oper->certfp.empty()) + { + bool match = false; + for (const auto &certfp : oper->certfp) + { + if (this->fingerprint == certfp) + { + match = true; + break; + } + } + if (!match) + return false; // Wrong TLS fingerprint. + } + + if (!oper->hosts.empty()) { bool match = false; Anope::string match_host = this->GetIdent() + "@" + this->host; Anope::string match_ip = this->GetIdent() + "@" + this->ip.addr(); - for (unsigned i = 0; i < this->nc->o->hosts.size(); ++i) + for (const auto &userhost : oper->hosts) { - const Anope::string &userhost = this->nc->o->hosts[i]; if (Anope::Match(match_host, userhost) || Anope::Match(match_ip, userhost)) { match = true; break; } } - if (match == false) - return false; + if (!match) + return false; // Wrong user@host/ip. } EventReturn MOD_RESULT; FOREACH_RESULT(IsServicesOper, MOD_RESULT, (this)); - if (MOD_RESULT == EVENT_STOP) - return false; - - return true; + return MOD_RESULT != EVENT_STOP; } bool User::HasCommand(const Anope::string &command) @@ -504,11 +534,7 @@ void User::UpdateHost() return; NickAlias *na = NickAlias::Find(this->nick); - on_access = false; - if (na) - on_access = na->nc->IsOnAccess(this); - - if (na && (this->IsIdentified(true) || this->IsRecognized())) + if (na && this->IsIdentified(true)) { Anope::string last_usermask = this->GetIdent() + "@" + this->GetDisplayedHost(); Anope::string last_realhost = this->GetIdent() + "@" + this->host; @@ -539,17 +565,18 @@ void User::SetModeInternal(const MessageSource &source, UserMode *um, const Anop { if (!this->nc->o->ot->modes.empty()) { - this->SetModes(NULL, "%s", this->nc->o->ot->modes.c_str()); - this->SendMessage(NULL, "Changing your usermodes to \002%s\002", this->nc->o->ot->modes.c_str()); - UserMode *oper = ModeManager::FindUserModeByName("OPER"); + auto *oper = ModeManager::FindUserModeByName("OPER"); if (oper && !this->HasMode("OPER") && this->nc->o->ot->modes.find(oper->mchar) != Anope::string::npos) IRCD->SendOper(this); + + this->SetModes(NULL, this->nc->o->ot->modes); + this->SendMessage(NULL, _("Changing your usermodes to \002%s\002"), this->nc->o->ot->modes.c_str()); } if (IRCD->CanSetVHost && !this->nc->o->vhost.empty()) { - this->SendMessage(NULL, "Changing your vhost to \002%s\002", this->nc->o->vhost.c_str()); + this->SendMessage(NULL, _("Changing your vhost to \002%s\002"), this->nc->o->vhost.c_str()); this->SetDisplayedHost(this->nc->o->vhost); - IRCD->SendVhost(this, "", this->nc->o->vhost); + IRCD->SendVHost(this, "", this->nc->o->vhost); } } } @@ -616,19 +643,24 @@ void User::SetModes(BotInfo *bi, const char *umodes, ...) { char buf[BUFSIZE] = ""; va_list args; - Anope::string modebuf, sbuf; - int add = -1; va_start(args, umodes); vsnprintf(buf, BUFSIZE - 1, umodes, args); va_end(args); - spacesepstream sep(buf); + SetModes(bi, Anope::string(buf)); +} + +void User::SetModes(BotInfo *bi, const Anope::string &umodes) +{ + Anope::string modebuf, sbuf; + int add = -1; + spacesepstream sep(umodes); sep.GetToken(modebuf); - for (unsigned i = 0, end = modebuf.length(); i < end; ++i) + for (auto mode : modebuf) { UserMode *um; - switch (modebuf[i]) + switch (mode) { case '+': add = 1; @@ -639,7 +671,7 @@ void User::SetModes(BotInfo *bi, const char *umodes, ...) default: if (add == -1) continue; - um = ModeManager::FindUserModeByChar(modebuf[i]); + um = ModeManager::FindUserModeByChar(mode); if (!um) continue; } @@ -656,26 +688,18 @@ void User::SetModes(BotInfo *bi, const char *umodes, ...) } } -void User::SetModesInternal(const MessageSource &source, const char *umodes, ...) +void User::SetModesInternal(const MessageSource &source, const Anope::string &umodes, const std::vector<Anope::string> &umodeparams) { - char buf[BUFSIZE] = ""; - va_list args; - Anope::string modebuf, sbuf; - int add = -1; - va_start(args, umodes); - vsnprintf(buf, BUFSIZE - 1, umodes, args); - va_end(args); - - if (this->server && this->server->IsSynced() && Anope::string(buf) != "+") - Log(this, "mode") << "changes modes to " << buf; + if (this->server && this->server->IsSynced() && Anope::string(umodes) != "+") + Log(this, "mode") << "changes modes to " << umodes; - spacesepstream sep(buf); - sep.GetToken(modebuf); - for (unsigned i = 0, end = modebuf.length(); i < end; ++i) + int add = -1; + auto paramit = umodeparams.begin(); + for (const auto mode : umodes) { UserMode *um; - switch (modebuf[i]) + switch (mode) { case '+': add = 1; @@ -686,15 +710,16 @@ void User::SetModesInternal(const MessageSource &source, const char *umodes, ... default: if (add == -1) continue; - um = ModeManager::FindUserModeByChar(modebuf[i]); + um = ModeManager::FindUserModeByChar(mode); if (!um) continue; } if (add) { - if (um->type == MODE_PARAM && sep.GetToken(sbuf)) - this->SetModeInternal(source, um, sbuf); + Anope::string sbuf; + if (um->type == MODE_PARAM && paramit != umodeparams.end()) + this->SetModeInternal(source, um, *paramit++); else this->SetModeInternal(source, um); } @@ -707,16 +732,16 @@ Anope::string User::GetModes() const { Anope::string m, params; - for (ModeList::const_iterator it = this->modes.begin(), it_end = this->modes.end(); it != it_end; ++it) + for (const auto &[mode, value] : this->modes) { - UserMode *um = ModeManager::FindUserModeByName(it->first); + UserMode *um = ModeManager::FindUserModeByName(mode); if (um == NULL) continue; m += um->mchar; - if (!it->second.empty()) - params += " " + it->second; + if (!value.empty()) + params += " " + value; } return m + params; @@ -744,7 +769,7 @@ void User::Kill(const MessageSource &source, const Anope::string &reason) { Anope::string real_reason = source.GetName() + " (" + reason + ")"; - IRCD->SendSVSKill(source, this, "%s", real_reason.c_str()); + IRCD->SendSVSKill(source, this, real_reason); } void User::KillInternal(const MessageSource &source, const Anope::string &reason) @@ -810,14 +835,14 @@ Anope::string User::Mask() const bool User::BadPassword() { - if (!Config->GetBlock("options")->Get<int>("badpasslimit")) + if (!Config->GetBlock("options").Get<unsigned int>("badpasslimit")) return false; - if (Config->GetBlock("options")->Get<time_t>("badpasstimeout") > 0 && this->invalid_pw_time > 0 && this->invalid_pw_time < Anope::CurTime - Config->GetBlock("options")->Get<time_t>("badpasstimeout")) + if (Config->GetBlock("options").Get<time_t>("badpasstimeout") > 0 && this->invalid_pw_time > 0 && this->invalid_pw_time < Anope::CurTime - Config->GetBlock("options").Get<time_t>("badpasstimeout")) this->invalid_pw_count = 0; ++this->invalid_pw_count; this->invalid_pw_time = Anope::CurTime; - if (this->invalid_pw_count >= Config->GetBlock("options")->Get<int>("badpasslimit")) + if (this->invalid_pw_count >= Config->GetBlock("options").Get<unsigned int>("badpasslimit")) { this->Kill(Me, "Too many invalid passwords"); return true; @@ -826,7 +851,16 @@ bool User::BadPassword() return false; } -User* User::Find(const Anope::string &name, bool nick_only) +bool User::ShouldPrivmsg() const +{ + // Send a PRIVMSG instead of a NOTICE if: + // 1. The user is not registered and msg is in nickserv:defaults. + // 2. The user is registered and has set /ns set message on. + static ExtensibleRef<bool> msg("MSG"); + return (!nc && Config->DefPrivmsg) || (nc && msg && msg->HasExt(nc)); +} + +User *User::Find(const Anope::string &name, bool nick_only) { if (!nick_only && IRCD && IRCD->RequiresID) { @@ -847,7 +881,7 @@ User* User::Find(const Anope::string &name, bool nick_only) void User::QuitUsers() { - for (std::list<User *>::iterator it = quitting_users.begin(), it_end = quitting_users.end(); it != it_end; ++it) - delete *it; + for (const auto *quitting_user : quitting_users) + delete quitting_user; quitting_users.clear(); } diff --git a/src/version.sh b/src/version.sh index 5be2b116d..d5fbe4a97 100644 --- a/src/version.sh +++ b/src/version.sh @@ -1,6 +1,6 @@ #!/bin/sh VERSION_MAJOR=2 -VERSION_MINOR=0 -VERSION_PATCH=18 +VERSION_MINOR=1 +VERSION_PATCH=14 VERSION_EXTRA="-git" diff --git a/src/win32/Config.cs b/src/win32/Config.cs index 812700b5e..fc0b5c78f 100644 --- a/src/win32/Config.cs +++ b/src/win32/Config.cs @@ -1,7 +1,7 @@ /*
* Config.cs - Windows Configuration
*
- * (C) 2003-2024 Anope Team
+ * (C) 2003-2025 Anope Team
* Contact us at team@anope.org
*
* This program is free but copyrighted software; see the file COPYING for
diff --git a/src/win32/anope_windows.h b/src/win32/anope_windows.h index 274667bb6..71e896992 100644 --- a/src/win32/anope_windows.h +++ b/src/win32/anope_windows.h @@ -29,9 +29,7 @@ # define DllExport __declspec(dllimport) #endif -#define MARK_DEPRECATED - -#if GETTEXT_FOUND +#if HAVE_LOCALIZATION /* Undefine some functions libintl defines */ # undef snprintf # undef vsnprintf @@ -53,13 +51,11 @@ #define EINPROGRESS WSAEWOULDBLOCK #include "socket.h" -#include "dir/dir.h" #include "dl/dl.h" #include "pipe/pipe.h" -#include "pthread/pthread.h" #include "sigaction/sigaction.h" -typedef int ssize_t; +typedef SSIZE_T ssize_t; namespace Anope { @@ -69,9 +65,6 @@ namespace Anope extern CoreExport void OnStartup(); extern CoreExport void OnShutdown(); extern CoreExport USHORT WindowsGetLanguage(const Anope::string &lang); -extern CoreExport int gettimeofday(timeval *tv, void *); -extern CoreExport Anope::string GetWindowsVersion(); -extern CoreExport bool SupportedWindowsVersion(); extern int setenv(const char *name, const char *value, int overwrite); extern int unsetenv(const char *name); extern int mkstemp(char *input); diff --git a/src/win32/conanfile.txt b/src/win32/conanfile.txt index ac72eee2a..d0ef69167 100644 --- a/src/win32/conanfile.txt +++ b/src/win32/conanfile.txt @@ -1,4 +1,5 @@ [requires]
+argon2/20190702
libmysqlclient/8.1.0
openssl/3.2.1
pcre2/10.42
@@ -7,6 +8,7 @@ gettext/0.21 libgettext/0.22
[options]
+argon2/*:shared=True
libmysqlclient/*:shared=True
openssl/*:shared=True
pcre2/*:shared=True
diff --git a/src/win32/dir/dir.cpp b/src/win32/dir/dir.cpp deleted file mode 100644 index 944fd92b1..000000000 --- a/src/win32/dir/dir.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* POSIX emulation layer for Windows. - * - * (C) 2008-2024 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - */ - -#include "dir.h" -#include <stdio.h> - -DIR *opendir(const char *path) -{ - char real_path[MAX_PATH]; - _snprintf(real_path, sizeof(real_path), "%s/*", path); - - DIR *d = new DIR(); - d->handle = FindFirstFile(real_path, &d->data); - d->read_first = false; - - if (d->handle == INVALID_HANDLE_VALUE) - { - delete d; - return NULL; - } - - return d; -} - -dirent *readdir(DIR *d) -{ - if (d->read_first == false) - d->read_first = true; - else if (!FindNextFile(d->handle, &d->data)) - return NULL; - - d->ent.d_ino = 1; - d->ent.d_name = d->data.cFileName; - - return &d->ent; -} - -int closedir(DIR *d) -{ - FindClose(d->handle); - delete d; - return 0; -} diff --git a/src/win32/dir/dir.h b/src/win32/dir/dir.h deleted file mode 100644 index 4f444f672..000000000 --- a/src/win32/dir/dir.h +++ /dev/null @@ -1,27 +0,0 @@ -/* POSIX emulation layer for Windows. - * - * (C) 2008-2024 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - */ - -#include <windows.h> - -struct dirent -{ - int d_ino; - char *d_name; -}; - -struct DIR -{ - dirent ent; - HANDLE handle; - WIN32_FIND_DATA data; - bool read_first; -}; - -DIR *opendir(const char *); -dirent *readdir(DIR *); -int closedir(DIR *); diff --git a/src/win32/dl/dl.cpp b/src/win32/dl/dl.cpp index 9006c9cc6..464809578 100644 --- a/src/win32/dl/dl.cpp +++ b/src/win32/dl/dl.cpp @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. diff --git a/src/win32/dl/dl.h b/src/win32/dl/dl.h index 2c3509020..1907554dd 100644 --- a/src/win32/dl/dl.h +++ b/src/win32/dl/dl.h @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. diff --git a/src/win32/pipe/pipe.cpp b/src/win32/pipe/pipe.cpp index d9e81e8eb..fd198e932 100644 --- a/src/win32/pipe/pipe.cpp +++ b/src/win32/pipe/pipe.cpp @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. diff --git a/src/win32/pipe/pipe.h b/src/win32/pipe/pipe.h index 81d366d8d..80fbe2a7b 100644 --- a/src/win32/pipe/pipe.h +++ b/src/win32/pipe/pipe.h @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. diff --git a/src/win32/pthread/pthread.cpp b/src/win32/pthread/pthread.cpp deleted file mode 100644 index 7b705c9d6..000000000 --- a/src/win32/pthread/pthread.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* POSIX emulation layer for Windows. - * - * (C) 2008-2024 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - */ - -#include "pthread.h" - -struct ThreadInfo -{ - void *(*entry)(void *); - void *param; -}; - -static DWORD WINAPI entry_point(void *parameter) -{ - ThreadInfo *ti = static_cast<ThreadInfo *>(parameter); - ti->entry(ti->param); - delete ti; - return 0; -} - -int pthread_attr_init(pthread_attr_t *) -{ - /* No need for this */ - return 0; -} - -int pthread_attr_setdetachstate(pthread_attr_t *, int) -{ - /* No need for this */ - return 0; -} - -int pthread_create(pthread_t *thread, const pthread_attr_t *, void *(*entry)(void *), void *param) -{ - ThreadInfo *ti = new ThreadInfo; - ti->entry = entry; - ti->param = param; - - *thread = CreateThread(NULL, 0, entry_point, ti, 0, NULL); - if (!*thread) - { - delete ti; - return -1; - } - - return 0; -} - -int pthread_join(pthread_t thread, void **) -{ - if (WaitForSingleObject(thread, INFINITE) == WAIT_FAILED) - return -1; - CloseHandle(thread); - return 0; -} - -void pthread_exit(int i) -{ - ExitThread(i); -} - -int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *) -{ - InitializeCriticalSection(mutex); - return 0; -} - -int pthread_mutex_destroy(pthread_mutex_t *mutex) -{ - DeleteCriticalSection(mutex); - return 0; -} - -int pthread_mutex_lock(pthread_mutex_t *mutex) -{ - EnterCriticalSection(mutex); - return 0; -} - -int pthread_mutex_trylock(pthread_mutex_t *mutex) -{ - return !TryEnterCriticalSection(mutex); -} - -int pthread_mutex_unlock(pthread_mutex_t *mutex) -{ - LeaveCriticalSection(mutex); - return 0; -} - -int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *) -{ - *cond = CreateEvent(NULL, false, false, NULL); - if (*cond == NULL) - return -1; - return 0; -} - -int pthread_cond_destroy(pthread_cond_t *cond) -{ - return !CloseHandle(*cond); -} - -int pthread_cond_signal(pthread_cond_t *cond) -{ - return !PulseEvent(*cond); -} - -int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - LeaveCriticalSection(mutex); - WaitForSingleObject(*cond, INFINITE); - EnterCriticalSection(mutex); - return 0; -} diff --git a/src/win32/pthread/pthread.h b/src/win32/pthread/pthread.h deleted file mode 100644 index 072d66c93..000000000 --- a/src/win32/pthread/pthread.h +++ /dev/null @@ -1,35 +0,0 @@ -/* POSIX emulation layer for Windows. - * - * (C) 2008-2024 Anope Team - * Contact us at team@anope.org - * - * Please read COPYING and README for further details. - */ - -#include <Windows.h> - -typedef HANDLE pthread_t; -typedef CRITICAL_SECTION pthread_mutex_t; -typedef HANDLE pthread_cond_t; -typedef int pthread_attr_t; -typedef void pthread_mutexattr_t; -typedef void pthread_condattr_t; - -#define PTHREAD_CREATE_JOINABLE 0 - -extern int pthread_attr_init(pthread_attr_t *); -extern int pthread_attr_setdetachstate(pthread_attr_t *, int); -extern int pthread_create(pthread_t *, const pthread_attr_t *, void *(*)(void *), void *); -extern int pthread_join(pthread_t, void **); -extern void pthread_exit(int); - -extern int pthread_mutex_init(pthread_mutex_t *, const pthread_mutexattr_t *); -extern int pthread_mutex_destroy(pthread_mutex_t *); -extern int pthread_mutex_lock(pthread_mutex_t *); -extern int pthread_mutex_trylock(pthread_mutex_t *); -extern int pthread_mutex_unlock(pthread_mutex_t *); - -extern int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *); -extern int pthread_cond_destroy(pthread_cond_t *); -extern int pthread_cond_signal(pthread_cond_t *); -extern int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *); diff --git a/src/win32/resource.h b/src/win32/resource.h index 4c9af7ff2..3d2dfc60b 100644 --- a/src/win32/resource.h +++ b/src/win32/resource.h @@ -1,6 +1,6 @@ /* * - * (C) 2005-2024 Anope Team + * (C) 2005-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. diff --git a/src/win32/sigaction/sigaction.cpp b/src/win32/sigaction/sigaction.cpp index 829ce6c66..4a3346aaa 100644 --- a/src/win32/sigaction/sigaction.cpp +++ b/src/win32/sigaction/sigaction.cpp @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. diff --git a/src/win32/sigaction/sigaction.h b/src/win32/sigaction/sigaction.h index 0492ca0e0..272b82bbe 100644 --- a/src/win32/sigaction/sigaction.h +++ b/src/win32/sigaction/sigaction.h @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. @@ -18,7 +18,7 @@ # define SIGPIPE -1 #endif -struct sigaction +struct sigaction final { void (*sa_handler)(int); int sa_flags; diff --git a/src/win32/socket.cpp b/src/win32/socket.cpp index 3a7a112d8..972cdbcc6 100644 --- a/src/win32/socket.cpp +++ b/src/win32/socket.cpp @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. @@ -48,87 +48,6 @@ int windows_accept(int fd, struct sockaddr *addr, int *addrlen) return i; } -/** This is inet_pton, but it works on Windows - * @param af The protocol type, AF_INET or AF_INET6 - * @param src The address - * @param dst Struct to put results in - * @return 1 on success, -1 on error - */ -int windows_inet_pton(int af, const char *src, void *dst) -{ - int address_length; - sockaddr_storage sa; - sockaddr_in *sin = reinterpret_cast<sockaddr_in *>(&sa); - sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6 *>(&sa); - - switch (af) - { - case AF_INET: - address_length = sizeof(sockaddr_in); - break; - case AF_INET6: - address_length = sizeof(sockaddr_in6); - break; - default: - return -1; - } - - if (!WSAStringToAddress(static_cast<LPSTR>(const_cast<char *>(src)), af, NULL, reinterpret_cast<LPSOCKADDR>(&sa), &address_length)) - { - switch (af) - { - case AF_INET: - memcpy(dst, &sin->sin_addr, sizeof(in_addr)); - break; - case AF_INET6: - memcpy(dst, &sin6->sin6_addr, sizeof(in6_addr)); - break; - } - return 1; - } - - return 0; -} - -/** This is inet_ntop, but it works on Windows - * @param af The protocol type, AF_INET or AF_INET6 - * @param src Network address structure - * @param dst After converting put it here - * @param size sizeof the dest - * @return dst - */ -const char *windows_inet_ntop(int af, const void *src, char *dst, size_t size) -{ - int address_length; - DWORD string_length = size; - sockaddr_storage sa; - sockaddr_in *sin = reinterpret_cast<sockaddr_in *>(&sa); - sockaddr_in6 *sin6 = reinterpret_cast<sockaddr_in6 *>(&sa); - - memset(&sa, 0, sizeof(sa)); - - switch (af) - { - case AF_INET: - address_length = sizeof(sockaddr_in); - sin->sin_family = af; - memcpy(&sin->sin_addr, src, sizeof(in_addr)); - break; - case AF_INET6: - address_length = sizeof(sockaddr_in6); - sin6->sin6_family = af; - memcpy(&sin6->sin6_addr, src, sizeof(in6_addr)); - break; - default: - return NULL; - } - - if (!WSAAddressToString(reinterpret_cast<LPSOCKADDR>(&sa), address_length, NULL, dst, &string_length)) - return dst; - - return NULL; -} - int fcntl(int fd, int cmd, int arg) { if (cmd == F_GETFL) diff --git a/src/win32/socket.h b/src/win32/socket.h index 7c20a65ad..1125774a8 100644 --- a/src/win32/socket.h +++ b/src/win32/socket.h @@ -1,6 +1,6 @@ /* POSIX emulation layer for Windows. * - * (C) 2008-2024 Anope Team + * (C) 2008-2025 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. @@ -21,12 +21,8 @@ extern CoreExport int read(int fd, char *buf, size_t count); extern CoreExport int write(int fd, const char *buf, size_t count); extern CoreExport int windows_close(int fd); extern CoreExport int windows_accept(int fd, struct sockaddr *addr, int *addrlen); -extern CoreExport int windows_inet_pton(int af, const char *src, void *dst); -extern CoreExport const char *windows_inet_ntop(int af, const void *src, char *dst, size_t size); extern CoreExport int fcntl(int fd, int cmd, int arg); #ifndef WIN32_NO_OVERRIDE # define accept windows_accept -# define inet_pton windows_inet_pton -# define inet_ntop windows_inet_ntop #endif diff --git a/src/win32/win32.rc.cmake b/src/win32/win32.rc.cmake index c72fe2b49..f0f3f8759 100644 --- a/src/win32/win32.rc.cmake +++ b/src/win32/win32.rc.cmake @@ -34,9 +34,7 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US VER_ANOPE VERSIONINFO FILEVERSION @VERSION_COMMA@ PRODUCTVERSION @VERSION_COMMA@ -#ifndef MINGW FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#endif #ifdef _DEBUG FILEFLAGS VS_FF_DEBUG #else @@ -54,7 +52,7 @@ BEGIN VALUE "FileDescription", "Anope IRC Services" VALUE "FileVersion", "@VERSION_FULL@" VALUE "InternalName", "Anope" - VALUE "LegalCopyright", "Copyright (C) 2003-2024 Anope Team" + VALUE "LegalCopyright", "Copyright (C) 2003-2025 Anope Team" VALUE "OriginalFilename", "anope.exe" VALUE "ProductName", "Anope" VALUE "ProductVersion", "@VERSION_DOTTED@" diff --git a/src/win32/windows.cpp b/src/win32/windows.cpp index 54ce44202..73045f408 100644 --- a/src/win32/windows.cpp +++ b/src/win32/windows.cpp @@ -1,7 +1,7 @@ /* POSIX emulation layer for Windows. * * (C) 2008-2011 Robin Burchell <w00t@inspircd.org> - * (C) 2008-2024 Anope Team <team@anope.org> + * (C) 2008-2025 Anope Team <team@anope.org> * * Please read COPYING and README for further details. * @@ -18,23 +18,20 @@ #include <sys/types.h> #include <fcntl.h> -static struct WindowsLanguage +static struct WindowsLanguage final { Anope::string languageName; USHORT windowsLanguageName; } WindowsLanguages[] = { - {"ca_ES", LANG_CATALAN}, {"de_DE", LANG_GERMAN}, {"el_GR", LANG_GREEK}, {"en_US", LANG_ENGLISH}, {"es_ES", LANG_SPANISH}, {"fr_FR", LANG_FRENCH}, - {"hu_HU", LANG_HUNGARIAN}, {"it_IT", LANG_ITALIAN}, {"nl_NL", LANG_DUTCH}, {"pl_PL", LANG_POLISH}, {"pt_PT", LANG_PORTUGUESE}, - {"ru_RU", LANG_RUSSIAN}, {"tr_TR", LANG_TURKISH}, }; @@ -64,172 +61,6 @@ USHORT WindowsGetLanguage(const Anope::string &lang) return LANG_NEUTRAL; } -/** Like gettimeofday(), but it works on Windows. - * @param tv A timeval struct - * @param tz Should be NULL, it is not used - * @return 0 on success - */ -int gettimeofday(timeval *tv, void *) -{ - SYSTEMTIME st; - GetSystemTime(&st); - - tv->tv_sec = Anope::CurTime; - tv->tv_usec = st.wMilliseconds; - - return 0; -} - -Anope::string GetWindowsVersion() -{ - OSVERSIONINFOEX osvi; - SYSTEM_INFO si; - - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - ZeroMemory(&si, sizeof(SYSTEM_INFO)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - - BOOL bOsVersionInfoEx = GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osvi)); - if (!bOsVersionInfoEx) - { - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osvi))) - return ""; - } - GetSystemInfo(&si); - - Anope::string buf, extra, cputype; - /* Determine CPU type 32 or 64 */ - if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - cputype = " 64-bit"; - else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) - cputype = " 32-bit"; - else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) - cputype = " Itanium 64-bit"; - - switch (osvi.dwPlatformId) - { - /* test for the Windows NT product family. */ - case VER_PLATFORM_WIN32_NT: - /* Windows Vista or Windows Server 2008 */ - if (osvi.dwMajorVersion == 6 && !osvi.dwMinorVersion) - { - if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - extra = " Enterprise Edition"; - else if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - extra = " Datacenter Edition"; - else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) - extra = " Home Premium/Basic"; - if (osvi.dwMinorVersion == 0) - { - if (osvi.wProductType & VER_NT_WORKSTATION) - buf = "Microsoft Windows Vista" + cputype + extra; - else - buf = "Microsoft Windows Server 2008" + cputype + extra; - } - else if (osvi.dwMinorVersion == 1) - { - if (osvi.wProductType & VER_NT_WORKSTATION) - buf = "Microsoft Windows 7" + cputype + extra; - else - buf = "Microsoft Windows Server 2008 R2" + cputype + extra; - } - } - /* Windows 2003 or Windows XP Pro 64 */ - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) - { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - extra = " Datacenter Edition"; - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - extra = " Enterprise Edition"; -#ifdef VER_SUITE_COMPUTE_SERVER - else if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER) - extra = " Compute Cluster Edition"; -#endif - else if (osvi.wSuiteMask == VER_SUITE_BLADE) - extra = " Web Edition"; - else - extra = " Standard Edition"; - if (osvi.wProductType & VER_NT_WORKSTATION && si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) - buf = "Microsoft Windows XP Professional x64 Edition" + extra; - else - buf = "Microsoft Windows Server 2003 Family" + cputype + extra; - } - if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) - { - if (osvi.wSuiteMask & VER_SUITE_EMBEDDEDNT) - extra = " Embedded"; - else if (osvi.wSuiteMask & VER_SUITE_PERSONAL) - extra = " Home Edition"; - buf = "Microsoft Windows XP" + extra; - } - if (osvi.dwMajorVersion == 5 && !osvi.dwMinorVersion) - { - if (osvi.wSuiteMask & VER_SUITE_DATACENTER) - extra = " Datacenter Server"; - else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - extra = " Advanced Server"; - else - extra = " Server"; - buf = "Microsoft Windows 2000" + extra; - } - if (osvi.dwMajorVersion <= 4) - { - if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) - extra = " Enterprise Edition"; - buf = "Microsoft Windows NT Server 4.0" + extra; - } - break; - case VER_PLATFORM_WIN32_WINDOWS: - if (osvi.dwMajorVersion == 4 && !osvi.dwMinorVersion) - { - if (osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B') - extra = " OSR2"; - buf = "Microsoft Windows 95" + extra; - } - if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) - { - if (osvi.szCSDVersion[1] == 'A') - extra = "SE"; - buf = "Microsoft Windows 98" + extra; - } - if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) - buf = "Microsoft Windows Millennium Edition"; - } - return buf; -} - -bool SupportedWindowsVersion() -{ - OSVERSIONINFOEX osvi; - - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - - BOOL bOsVersionInfoEx = GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osvi)); - if (!bOsVersionInfoEx) - { - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (!GetVersionEx(reinterpret_cast<OSVERSIONINFO *>(&osvi))) - return false; - } - - switch (osvi.dwPlatformId) - { - /* test for the Windows NT product family. */ - case VER_PLATFORM_WIN32_NT: - /* win nt4 */ - if (osvi.dwMajorVersion <= 4) - return false; - /* the rest */ - return true; - /* win95 win98 winME */ - case VER_PLATFORM_WIN32_WINDOWS: - return false; - } - return false; -} - int setenv(const char *name, const char *value, int overwrite) { return SetEnvironmentVariable(name, value); diff --git a/src/xline.cpp b/src/xline.cpp index 87279b412..2b6a93e9a 100644 --- a/src/xline.cpp +++ b/src/xline.cpp @@ -21,15 +21,15 @@ /* List of XLine managers we check users against in XLineManager::CheckAll */ std::list<XLineManager *> XLineManager::XLineManagers; -Serialize::Checker<std::multimap<Anope::string, XLine *, ci::less> > XLineManager::XLinesByUID("XLine"); +Serialize::Checker<std::multimap<Anope::string, XLine *, ci::less> > XLineManager::XLinesByUID(XLINE_TYPE); void XLine::Init() { - if (this->mask.length() >= 2 && this->mask[0] == '/' && this->mask[this->mask.length() - 1] == '/' && !Config->GetBlock("options")->Get<const Anope::string>("regexengine").empty()) + if (this->mask.length() >= 2 && this->mask[0] == '/' && this->mask[this->mask.length() - 1] == '/' && !Config->GetBlock("options").Get<const Anope::string>("regexengine").empty()) { Anope::string stripped_mask = this->mask.substr(1, this->mask.length() - 2); - ServiceReference<RegexProvider> provider("Regex", Config->GetBlock("options")->Get<const Anope::string>("regexengine")); + ServiceReference<RegexProvider> provider("Regex", Config->GetBlock("options").Get<const Anope::string>("regexengine")); if (provider) { try @@ -86,7 +86,12 @@ void XLine::Init() } } -XLine::XLine(const Anope::string &ma, const Anope::string &r, const Anope::string &uid) : Serializable("XLine"), mask(ma), by(Me->GetName()), created(0), expires(0), reason(r), id(uid) +XLine::XLine(const Anope::string &ma, const Anope::string &r, const Anope::string &uid) + : Serializable(XLINE_TYPE) + , mask(ma) + , by(Me->GetName()) + , reason(r) + , id(uid) { regex = NULL; manager = NULL; @@ -95,7 +100,14 @@ XLine::XLine(const Anope::string &ma, const Anope::string &r, const Anope::strin this->Init(); } -XLine::XLine(const Anope::string &ma, const Anope::string &b, const time_t ex, const Anope::string &r, const Anope::string &uid) : Serializable("XLine"), mask(ma), by(b), created(Anope::CurTime), expires(ex), reason(r), id(uid) +XLine::XLine(const Anope::string &ma, const Anope::string &b, const time_t ex, const Anope::string &r, const Anope::string &uid) + : Serializable(XLINE_TYPE) + , mask(ma) + , by(b) + , created(Anope::CurTime) + , expires(ex) + , reason(r) + , id(uid) { regex = NULL; manager = NULL; @@ -151,19 +163,26 @@ bool XLine::IsRegex() const return !this->mask.empty() && this->mask[0] == '/' && this->mask[this->mask.length() - 1] == '/'; } -void XLine::Serialize(Serialize::Data &data) const +XLine::Type::Type() + : Serialize::Type(XLINE_TYPE) { - data["mask"] << this->mask; - data["by"] << this->by; - data["created"] << this->created; - data["expires"] << this->expires; - data["reason"] << this->reason; - data["uid"] << this->id; - if (this->manager) - data["manager"] << this->manager->name; } -Serializable* XLine::Unserialize(Serializable *obj, Serialize::Data &data) +void XLine::Type::Serialize(const Serializable *obj, Serialize::Data &data) const +{ + const auto *xl = static_cast<const XLine *>(obj); + + data.Store("mask", xl->mask); + data.Store("by", xl->by); + data.Store("created", xl->created); + data.Store("expires", xl->expires); + data.Store("reason", xl->reason); + data.Store("uid", xl->id); + if (xl->manager) + data.Store("manager", xl->manager->name); +} + +Serializable *XLine::Type::Unserialize(Serializable *obj, Serialize::Data &data) const { Anope::string smanager; @@ -224,10 +243,8 @@ void XLineManager::UnregisterXLineManager(XLineManager *xlm) void XLineManager::CheckAll(User *u) { - for (std::list<XLineManager *>::iterator it = XLineManagers.begin(), it_end = XLineManagers.end(); it != it_end; ++it) + for (auto *xlm : XLineManagers) { - XLineManager *xlm = *it; - if (xlm->CheckAllXLines(u)) break; } @@ -251,7 +268,7 @@ Anope::string XLineManager::GenerateUID() { char c; do - c = (rand() % 75) + 48; + c = (Anope::RandomNumber() % 75) + 48; while (!isupper(c) && !isdigit(c)); id += c; } @@ -261,7 +278,10 @@ Anope::string XLineManager::GenerateUID() return id; } -XLineManager::XLineManager(Module *creator, const Anope::string &xname, char t) : Service(creator, "XLineManager", xname), type(t), xlines("XLine") +XLineManager::XLineManager(Module *creator, const Anope::string &xname, char t) + : Service(creator, "XLineManager", xname) + , type(t) + , xlines(XLINE_TYPE) { } @@ -288,7 +308,7 @@ const std::vector<XLine *> &XLineManager::GetList() const void XLineManager::AddXLine(XLine *x) { if (!x->id.empty()) - XLinesByUID->insert(std::make_pair(x->id, x)); + XLinesByUID->emplace(x->id, x); this->xlines->push_back(x); x->manager = this; } @@ -346,7 +366,7 @@ bool XLineManager::DelXLine(XLine *x) return false; } -XLine* XLineManager::GetEntry(unsigned index) +XLine *XLineManager::GetEntry(unsigned index) { if (index >= this->xlines->size()) return NULL; @@ -361,9 +381,8 @@ void XLineManager::Clear() std::vector<XLine *> xl; this->xlines->swap(xl); - for (unsigned i = 0; i < xl.size(); ++i) + for (auto *x : xl) { - XLine *x = xl[i]; if (!x->id.empty()) XLinesByUID->erase(x->id); delete x; @@ -417,7 +436,7 @@ bool XLineManager::CanAdd(CommandSource &source, const Anope::string &mask, time return true; } -XLine* XLineManager::HasEntry(const Anope::string &mask) +XLine *XLineManager::HasEntry(const Anope::string &mask) { std::multimap<Anope::string, XLine *, ci::less>::iterator it = XLinesByUID->find(mask); if (it != XLinesByUID->end()) @@ -427,10 +446,8 @@ XLine* XLineManager::HasEntry(const Anope::string &mask) it->second->QueueUpdate(); return it->second; } - for (unsigned i = 0, end = this->xlines->size(); i < end; ++i) + for (auto *x : *this->xlines) { - XLine *x = this->xlines->at(i); - if (x->mask.equals_ci(mask)) { x->QueueUpdate(); |