void Kill::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) { User *u = User::Find(params[0]); ServiceBot *bi; if (!u) return; /* Recover if someone kills us. */ if (u->server == Me && (bi = dynamic_cast<ServiceBot *>(u))) { static time_t last_time = 0; if (last_time == Anope::CurTime) { Anope::QuitReason = "Kill loop detected. Are Services U:Lined?"; Anope::Quitting = true; return; } last_time = Anope::CurTime; bi->OnKill(); } else u->KillInternal(source, params[1]); }
void OnUserKicked(const MessageSource &source, User *target, const Anope::string &channel, ChannelStatus &status, const Anope::string &kickmsg) override { ServiceBot *bi = ServiceBot::Find(target->GetUID()); if (bi) /* Bots get rejoined */ bi->Join(channel, &status); }
void OnLeaveChannel(User *u, Channel *c) override { /* Channel is persistent, it shouldn't be deleted and the service bot should stay */ if (c->ci && c->ci->HasFieldS("PERSIST")) return; /* Channel is syncing from a netburst, don't destroy it as more users are probably wanting to join immediately * We also don't part the bot here either, if necessary we will part it after the sync */ if (c->syncing) return; /* Additionally, do not delete this channel if ChanServ/a BotServ bot is inhabiting it */ if (inhabit && inhabit->HasExt(c)) return; if (c->ci) { ServiceBot *bot = c->ci->GetBot(); /* This is called prior to removing the user from the channnel, so c->users.size() - 1 should be safe */ if (bot && u != bot && c->users.size() - 1 <= Config->GetModule(this)->Get<unsigned>("minusers") && c->FindUser(bot)) bot->Part(c); } }
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override { const Anope::string &chan = params[0]; const Anope::string &nick = params[1]; if (Anope::ReadOnly) { source.Reply(_("Sorry, bot assignment is temporarily disabled.")); return; } ChanServ::Channel *ci = ChanServ::Find(chan); if (ci == NULL) { source.Reply(_("Channel \002{0}\002 isn't registered."), chan); return; } ServiceBot *bi = ServiceBot::Find(nick, true); if (!bi) { source.Reply(_("Bot \002{0}\002 does not exist."), nick); return; } ChanServ::AccessGroup access = source.AccessFor(ci); if (!access.HasPriv("ASSIGN") && !source.HasPriv("botserv/administration")) { source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "ASSIGN", ci->GetName()); return; } if (ci->HasFieldS("BS_NOBOT")) { source.Reply(_("Access denied. \002{0}\002 may not have a bot assigned to it because a Services Operator has disallowed it."), ci->GetName()); return; } if (bi->bi->GetOperOnly() && !source.HasPriv("botserv/administration")) { source.Reply(_("Access denied. Bot \002{0}\002 is for operators only."), bi->nick); return; } if (ci->GetBot() == bi) { source.Reply(_("Bot \002{0}\002 is already assigned to \002{1}\002."), ci->GetBot()->nick, ci->GetName()); return; } bool override = !access.HasPriv("ASSIGN"); Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << bi->nick; bi->Assign(source.GetUser(), ci); source.Reply(_("Bot \002{0}\002 has been assigned to \002{1}\002."), bi->nick, ci->GetName()); }
void OnChanRegistered(ChanServ::Channel *ci) override { const Anope::string &bot = Config->GetModule(this)->Get<Anope::string>("bot"); if (bot.empty()) return; ServiceBot *bi = ServiceBot::Find(bot, true); if (bi == NULL) { Log(this) << "bs_autoassign is configured to assign bot " << bot << ", but it does not exist?"; return; } bi->Assign(NULL, ci); }
void SendTopic(const MessageSource &source, Channel *c) override { ServiceBot *bi = source.GetBot(); bool needjoin = c->FindUser(bi) == NULL; if (needjoin) { ChannelStatus status; status.AddMode('o'); bi->Join(c, &status); } IRCDProto::SendTopic(source, c); if (needjoin) bi->Part(c); }
void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> ¶ms) { const Anope::string &receiver = params[0]; Anope::string message = params[1]; User *u = source.GetUser(); if (IRCD->IsChannelValid(receiver)) { Channel *c = Channel::Find(receiver); if (c) { EventManager::Get()->Dispatch(&Event::Privmsg::OnPrivmsg, u, c, message); } } else { /* If a server is specified (nick@server format), make sure it matches * us, and strip it off. */ Anope::string botname = receiver; size_t s = receiver.find('@'); bool nick_only = false; if (s != Anope::string::npos) { Anope::string servername(receiver.begin() + s + 1, receiver.end()); botname = botname.substr(0, s); nick_only = true; if (!servername.equals_ci(Me->GetName())) return; } else if (!IRCD->RequiresID && Config->UseStrictPrivmsg) { ServiceBot *bi = ServiceBot::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; } ServiceBot *bi = ServiceBot::Find(botname, nick_only); if (bi) { if (message[0] == '\1' && message[message.length() - 1] == '\1') { if (message.substr(0, 6).equals_ci("\1PING ")) { Anope::string buf = message; buf.erase(buf.begin()); buf.erase(buf.end() - 1); IRCD->SendCTCP(bi, u->nick, "%s", buf.c_str()); } else if (message.substr(0, 9).equals_ci("\1VERSION\1")) { 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()); } return; } EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::BotPrivmsg::OnBotPrivmsg, u, bi, message); if (MOD_RESULT == EVENT_STOP) return; bi->OnMessage(u, message); } } return; }
void Anope::Init(int ac, char **av) { /* Set file creation mask and group ID. */ #if defined(DEFUMASK) && HAVE_UMASK umask(DEFUMASK); #endif /* Parse command line arguments */ ParseCommandLineArguments(ac, av); if (GetCommandLineArgument("version", 'v')) { Log(LOG_TERMINAL) << "Anope-" << Anope::Version() << " -- " << Anope::VersionBuildString(); throw CoreException(); } if (GetCommandLineArgument("help", 'h')) { Log(LOG_TERMINAL) << "Anope-" << Anope::Version() << " -- " << Anope::VersionBuildString(); Log(LOG_TERMINAL) << "Anope IRC Services (http://www.anope.org)"; Log(LOG_TERMINAL) << "Usage ./" << Anope::ServicesBin << " [options] ..."; Log(LOG_TERMINAL) << "-c, --config=filename.conf"; Log(LOG_TERMINAL) << " --confdir=conf file direcory"; Log(LOG_TERMINAL) << " --dbdir=database directory"; 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) << "-e, --noexpire"; Log(LOG_TERMINAL) << "-n, --nofork"; Log(LOG_TERMINAL) << " --nothird"; Log(LOG_TERMINAL) << " --protocoldebug"; Log(LOG_TERMINAL) << "-r, --readonly"; Log(LOG_TERMINAL) << "-s, --support"; Log(LOG_TERMINAL) << "-v, --version"; Log(LOG_TERMINAL) << ""; Log(LOG_TERMINAL) << "Further support is available from http://www.anope.org"; Log(LOG_TERMINAL) << "Or visit us on IRC at irc.anope.org #anope"; throw CoreException(); } if (GetCommandLineArgument("nofork", 'n')) Anope::NoFork = true; if (GetCommandLineArgument("support", 's')) { Anope::NoFork = Anope::NoThird = true; ++Anope::Debug; } if (GetCommandLineArgument("readonly", 'r')) Anope::ReadOnly = true; if (GetCommandLineArgument("nothird")) Anope::NoThird = true; if (GetCommandLineArgument("noexpire", 'e')) Anope::NoExpire = true; if (GetCommandLineArgument("protocoldebug")) Anope::ProtocolDebug = true; Anope::string arg; if (GetCommandLineArgument("debug", 'd', arg)) { if (!arg.empty()) { int level = arg.is_number_only() ? convertTo<int>(arg) : -1; if (level > 0) Anope::Debug = level; else throw CoreException("Invalid option given to --debug"); } else ++Anope::Debug; } if (GetCommandLineArgument("config", 'c', arg)) { if (arg.empty()) throw CoreException("The --config option requires a file name"); ServicesConf = Configuration::File(arg, false); } if (GetCommandLineArgument("confdir", 0, arg)) { if (arg.empty()) throw CoreException("The --confdir option requires a path"); Anope::ConfigDir = arg; } if (GetCommandLineArgument("dbdir", 0, arg)) { if (arg.empty()) throw CoreException("The --dbdir option requires a path"); Anope::DataDir = arg; } if (GetCommandLineArgument("localedir", 0, arg)) { if (arg.empty()) throw CoreException("The --localedir option requires a path"); Anope::LocaleDir = arg; } if (GetCommandLineArgument("modulesdir", 0, arg)) { if (arg.empty()) throw CoreException("The --modulesdir option requires a path"); Anope::ModuleDir = arg; } if (GetCommandLineArgument("logdir", 0, arg)) { if (arg.empty()) throw CoreException("The --logdir option requires a path"); Anope::LogDir = arg; } /* Chdir to Services data directory. */ 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"); #else /* 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<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); } } #endif #ifdef _WIN32 Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "\\" << ServicesConf.GetName(); #else Log(LOG_TERMINAL) << "Using configuration file " << Anope::ConfigDir << "/" << ServicesConf.GetName(); /* Fork to background */ if (!Anope::NoFork) { /* Install these before fork() - it is possible for the child to * connect and kill() the parent before it is able to install the * handler. */ struct sigaction sa, old_sigusr2, old_sigchld; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); sa.sa_handler = parent_signal_handler; sigaction(SIGUSR2, &sa, &old_sigusr2); sigaction(SIGCHLD, &sa, &old_sigchld); int i = fork(); if (i > 0) { sigset_t mask; sigemptyset(&mask); sigsuspend(&mask); exit(Anope::ReturnValue); } else if (i == -1) { Log() << "Error, unable to fork: " << Anope::LastError(); Anope::NoFork = true; } /* Child doesn't need these */ sigaction(SIGUSR2, &old_sigusr2, NULL); sigaction(SIGCHLD, &old_sigchld, NULL); } #endif /* Initialize the socket engine. Note that some engines can not survive a fork(), so this must be here. */ SocketEngine::Init(); ServiceManager::Init(); EventManager::Init(); new BotInfoType(); new XLineType(nullptr); new OperBlockType(); /* Read configuration file; exit if there are problems. */ try { Config = new Configuration::Conf(); } catch (const ConfigException &ex) { Log(LOG_TERMINAL) << ex.GetReason(); 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 http://www.anope.org/. Join"; Log(LOG_TERMINAL) << "*** our support channel on /server irc.anope.org channel #anope."; throw CoreException("Configuration file failed to validate"); } /* Create me */ Configuration::Block *block = Config->GetBlock("serverinfo"); Me = new Server(NULL, block->Get<Anope::string>("name"), 0, block->Get<Anope::string>("description"), block->Get<Anope::string>("id")); for (std::pair<Anope::string, User *> p : UserListByNick) { User *u = p.second; if (u->type != UserType::BOT) continue; ServiceBot *bi = anope_dynamic_static_cast<ServiceBot *>(u); bi->server = Me; ++Me->users; } /* Announce ourselves to the logfile. */ Log() << "Anope " << Anope::Version() << " starting up" << (Anope::Debug || Anope::ReadOnly ? " (options:" : "") << (Anope::Debug ? " debug" : "") << (Anope::ReadOnly ? " readonly" : "") << (Anope::Debug || Anope::ReadOnly ? ")" : ""); InitSignals(); /* Initialize multi-language support */ Language::InitLanguages(); /* Initialize random number generator */ block = Config->GetBlock("options"); srand(block->Get<unsigned>("seed") ^ time(NULL)); ModeManager::Apply(nullptr); /* load modules */ Log() << "Loading modules..."; for (int i = 0; i < Config->CountBlock("module"); ++i) ModuleManager::LoadModule(Config->GetBlock("module", i)->Get<Anope::string>("name"), NULL); #ifndef _WIN32 /* We won't background later, so we should setuid now */ if (Anope::NoFork) setuidgid(); #endif Module *protocol = ModuleManager::FindFirstOf(PROTOCOL); if (protocol == NULL) throw CoreException("You must load a protocol module!"); /* Write our PID to the PID file. */ write_pidfile(); Log() << "Using IRCd protocol " << protocol->name; /* Auto assign sid if applicable */ if (IRCD->RequiresID) { Anope::string sid = IRCD->SID_Retrieve(); if (Me->GetSID() == Me->GetName()) Me->SetSID(sid); for (std::pair<Anope::string, User *> p : UserListByNick) { User *u = p.second; if (u->type != UserType::BOT) continue; ServiceBot *bi = anope_dynamic_static_cast<ServiceBot *>(u); bi->GenerateUID(); } } /* Load up databases */ Log() << "Loading databases..."; EventReturn MOD_RESULT = EventManager::Get()->Dispatch(&Event::LoadDatabase::OnLoadDatabase);; static_cast<void>(MOD_RESULT); Log() << "Databases loaded"; for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end; ++it) it->second->Sync(); }
void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) override { const Anope::string &query = params[0]; ServiceBot *bi = ServiceBot::Find(query, true); ChanServ::Channel *ci = ChanServ::Find(query); InfoFormatter info(source.nc); if (bi) { source.Reply(_("Information for bot \002%s\002:"), bi->nick.c_str()); info[_("Mask")] = bi->GetIdent() + "@" + bi->host; info[_("Real name")] = bi->realname; if (bi->bi) { info[_("Created")] = Anope::strftime(bi->bi->GetCreated(), source.GetAccount()); info[_("Options")] = bi->bi->GetOperOnly() ? _("Private") : _("None"); } info[_("Used on")] = stringify(bi->GetChannelCount()) + " channel(s)"; EventManager::Get()->Dispatch(&Event::ServiceBotEvent::OnServiceBot, source, bi, ci, info); std::vector<Anope::string> replies; info.Process(replies); for (unsigned i = 0; i < replies.size(); ++i) source.Reply(replies[i]); if (source.HasPriv("botserv/administration")) { Anope::string buf; for (ChanServ::Channel *ci2 : bi->GetChannels()) buf += " " + ci2->GetName(); source.Reply(buf); } } else if (ci) { if (!source.AccessFor(ci).HasPriv("INFO") && !source.HasPriv("botserv/administration")) { source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "INFO", ci->GetName()); return; } source.Reply(_("Information for channel \002{0}\002:"), ci->GetName()); info[_("Bot nick")] = ci->GetBot() ? ci->GetBot()->nick : _("not assigned yet"); EventManager::Get()->Dispatch(&Event::ServiceBotEvent::OnServiceBot, source, bi, ci, info); std::vector<Anope::string> replies; info.Process(replies); for (unsigned i = 0; i < replies.size(); ++i) source.Reply(replies[i]); } else { source.Reply(_("\002{0}\002 is not a valid bot or registered channel."), query); } }