Example #1
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		const Anope::string &chan = params[0];
		const Anope::string &param = params[1];

		if (Anope::ReadOnly)
		{
			source.Reply(_("Services are in read-only mode."));
			return;
		}

		ChanServ::Channel *ci = ChanServ::Find(chan);
		if (ci == NULL)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
			return;
		}

		EventReturn MOD_RESULT;
		MOD_RESULT = EventManager::Get()->Dispatch(&Event::SetChannelOption::OnSetChannelOption, source, this, ci, param);
		if (MOD_RESULT == EVENT_STOP)
			return;

		if (MOD_RESULT != EVENT_ALLOW && !source.AccessFor(ci).HasPriv("SET") && source.permission.empty() && !source.HasPriv("chanserv/administration"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
			return;
		}

		Anope::string scommand = GetAttribute(source.command);

		/* remove existing */
		for (CSMiscData *data : ci->GetRefs<CSMiscData *>())
			if (data->GetName() == scommand)
			{
				data->Delete();
				break;
			}

		if (!param.empty())
		{
			CSMiscData *data = Serialize::New<CSMiscData *>();
			data->SetChannel(ci);
			data->SetName(scommand);
			data->SetData(param);

			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to change it to " << param;
			source.Reply(_("\002{0}\002 for \002{1}\002 set to \002{2}\002."), scommand, ci->GetName(), param);
		}
		else
		{
			Log(source.AccessFor(ci).HasPriv("SET") ? LOG_COMMAND : LOG_OVERRIDE, source, this, ci) << "to unset it";
			source.Reply(_("\002{0}\002 for \002{1}\002 unset."), scommand, ci->GetName());
		}
	}
Example #2
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		ChanServ::Channel *ci = ChanServ::Find(params[0]);
		const Anope::string &value = params[1];

		if (ci == NULL)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), params[0]);
			return;
		}

		if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET"))
		{
			source.Reply(_("Access denied. You do not have the \002{0}\002 privilege on \002{1}\002."), "SET", ci->GetName());
			return;
		}

		if (Anope::ReadOnly)
		{
			source.Reply(_("Sorry, bot option setting is temporarily disabled."));
			return;
		}

		if (value.equals_ci("ON"))
		{
			bool override = !source.AccessFor(ci).HasPriv("SET");
			Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable fantasy";

			ci->SetS<bool>("BS_FANTASY", true);
			source.Reply(_("Fantasy mode is now \002on\002 on channel \002{0}\002."), ci->GetName());
		}
Example #3
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		const Anope::string &chan = params[0];
		const Anope::string &value = params[1];

		if (Anope::ReadOnly)
		{
			source.Reply(_("Services are in read-only mode."));
			return;
		}

		ChanServ::Channel *ci = ChanServ::Find(chan);
		if (ci == NULL)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
			return;
		}

		if (!source.HasPriv("botserv/administration") && !source.AccessFor(ci).HasPriv("SET"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
			return;
		}

		if (value.equals_ci("ON"))
		{
			bool override = !source.AccessFor(ci).HasPriv("SET");
			Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to enable greets";

			ci->SetS<bool>("BS_GREET", true);
			source.Reply(_("Greet mode for \002{0}\002 is now \002on\002."), ci->GetName());
		}
Example #4
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) 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());
	}
Example #5
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		const Anope::string &chan = params[0];
		const Anope::string &text = params[1];

		ChanServ::Channel *ci = ChanServ::Find(chan);
		if (ci == NULL)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
			return;
		}

		if (!source.AccessFor(ci).HasPriv("SAY") && !source.HasPriv("botserv/administration"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SAY", ci->GetName());
			return;
		}

		if (!ci->GetBot())
		{
			source.Reply(_("There is no bot assigned to \002{0}\002. One must be assigned to the channel before this command can be used."), ci->GetName());
			ServiceBot *bi;
			Anope::string name;
			Command::FindCommandFromService("botserv/assign", bi, name);
			CommandInfo *help = source.service->FindCommand("generic/help");
			if (bi && help)
				source.Reply(_("See \002{msg}{service} {help} {command}\002 for information on assigning bots."),
				                "msg"_kw = Config->StrictPrivmsg, "service"_kw = bi->nick, "help"_kw = help->cname, "command"_kw = name);
			return;
		}

		if (!ci->c || !ci->c->FindUser(ci->GetBot()))
		{
			source.Reply(_("Bot \002{0}\002 is not on channel \002{1}\002."), ci->GetBot()->nick, ci->GetName());
			return;
		}

		if (text[0] == '\001')
		{
			this->OnSyntaxError(source, "");
			return;
		}

		IRCD->SendPrivmsg(ci->GetBot(), ci->GetName(), "%s", text.c_str());
		ci->GetBot()->lastmsg = Anope::CurTime;

		bool override = !source.AccessFor(ci).HasPriv("SAY");
		Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to say: " << text;
	}
Example #6
0
File: log.cpp Project: Robby-/anope
	void OnLog(Logger *logger) override
	{
		User *user = logger->GetUser();
		ChanServ::Channel *channel = logger->GetCi();
		Command *command = logger->GetCommand();
		CommandSource *source = logger->GetSource();

		if (logger->GetType() != LogType::COMMAND || user == nullptr || command == nullptr || channel == nullptr || !Me || !Me->IsSynced())
			return;

		Channel *c = channel->GetChannel();

		for (LogSetting *log : channel->GetRefs<LogSetting *>())
		{
			/* wrong command */
			if (log->GetServiceName() != command->GetName())
				continue;

			/* if a command name is given check the service and the command */
			if (!log->GetCommandName().empty())
			{
				/* wrong service (only check if not a fantasy command, though) */
				if (!source->c && log->GetCommandService() != source->service->nick)
					continue;

				if (!log->GetCommandName().equals_ci(source->GetCommand()))
					continue;
			}

			const Anope::string &buffer = logger->GetMaskedMessage();

			if (log->GetMethod().equals_ci("MEMO") && memoserv && channel->WhoSends() != NULL)
				memoserv->Send(channel->WhoSends()->nick, channel->GetName(), buffer, true);
			else if (source->c)
				/* Sending a channel message or notice in response to a fantasy command */;
			else if (log->GetMethod().equals_ci("MESSAGE") && c)
			{
				IRCD->SendPrivmsg(channel->WhoSends(), log->GetExtra() + c->name, "{0}", buffer);
#warning "fix idletimes"
				//l->ci->WhoSends()->lastmsg = Anope::CurTime;
			}
			else if (log->GetMethod().equals_ci("NOTICE") && c)
				IRCD->SendNotice(channel->WhoSends(), log->GetExtra() + c->name, "{0}", buffer);
		}
	}
Example #7
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		const Anope::string &chan = params[0];

		if (Anope::ReadOnly && !source.HasPriv("chanserv/administration"))
		{
			source.Reply(_("Sorry, channel de-registration is temporarily disabled."));
			return;
		}

		ChanServ::Channel *ci = ChanServ::Find(chan);
		if (ci == NULL)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
			return;
		}

		if (params.size() < 2 || !chan.equals_ci(params[1]))
		{
			source.Reply(_("You must enter the channel name twice as a confirmation that you wish to drop \002{0}\002."), ci->GetName());
			return;
		}

		if ((ci->HasFieldS("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER")) && !source.HasCommand("chanserv/drop"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "FOUNDER", ci->GetName());
			return;
		}

		EventReturn MOD_RESULT = this->onchandrop(&Event::ChanDrop::OnChanDrop, source, ci);
		if (MOD_RESULT == EVENT_STOP)
			return;

		bool override = (ci->HasFieldS("SECUREFOUNDER") ? !source.IsFounder(ci) : !source.AccessFor(ci).HasPriv("FOUNDER"));
		Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "(founder was: " << (ci->GetFounder() ? ci->GetFounder()->GetDisplay() : "none") << ")";

		Reference<Channel> c = ci->c;
		ci->Delete();

		source.Reply(_("Channel \002{0}\002 has been dropped."), chan);

		if (c)
			c->CheckModes();
	}
Example #8
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{

		Anope::string param = !params.empty() ? params[0] : "", chan;
		ChanServ::Channel *ci = NULL;
		MemoServ::MemoInfo *mi;

		if (!param.empty() && param[0] == '#')
		{
			chan = param;
			param = params.size() > 1 ? params[1] : "";

			ci = ChanServ::Find(chan);
			if (!ci)
			{
				source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
				return;
			}

			if (!source.AccessFor(ci).HasPriv("MEMO"))
			{
				source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
				return;
			}

			mi = ci->GetMemos();
		}
		else
		{
			mi = source.nc->GetMemos();
		}

		if (!param.empty() && !isdigit(param[0]) && !param.equals_ci("NEW"))
		{
			this->OnSyntaxError(source);
			return;
		}

		if (!mi)
			return;

		auto memos = mi->GetMemos();

		if (!memos.size())
		{
			if (!chan.empty())
				source.Reply(_("\002{0}\002 has no memos."), chan);
			else
				source.Reply(_("You have no memos."));
			return;
		}

		ListFormatter list(source.GetAccount());

		list.AddColumn(_("Number")).AddColumn(_("Sender")).AddColumn(_("Date/Time"));

		if (!param.empty() && isdigit(param[0]))
		{
			NumberList(param, false,
				[&](unsigned int number)
				{
					if (!number || number > memos.size())
						return;

					MemoServ::Memo *m = mi->GetMemo(number - 1);

					ListFormatter::ListEntry entry;
					entry["Number"] = (m->GetUnread() ? "* " : "  ") + stringify(number);
					entry["Sender"] = m->GetSender();
					entry["Date/Time"] = Anope::strftime(m->GetTime(), source.GetAccount());
					list.AddEntry(entry);
				},
				[]{});
		}
		else
		{
			if (!param.empty())
			{
				unsigned i, end;
				for (i = 0, end = memos.size(); i < end; ++i)
					if (mi->GetMemo(i)->GetUnread())
						break;
				if (i == end)
				{
					if (!chan.empty())
						source.Reply(_("\002{0}\002 has no new memos."), chan);
					else
						source.Reply(_("You have no new memos."));
					return;
				}
			}

			for (unsigned i = 0, end = memos.size(); i < end; ++i)
			{
				if (!param.empty() && !mi->GetMemo(i)->GetUnread())
					continue;

				MemoServ::Memo *m = mi->GetMemo(i);

				ListFormatter::ListEntry entry;
				entry["Number"] = (m->GetUnread() ? "* " : "  ") + stringify(i + 1);
				entry["Sender"] = m->GetSender();
				entry["Date/Time"] = Anope::strftime(m->GetTime(), source.GetAccount());
				list.AddEntry(entry);
			}
		}

		std::vector<Anope::string> replies;
		list.Process(replies);

		source.Reply(_("Memos for \002{0}\002:"), ci ? ci->GetName().c_str() : source.GetNick().c_str());
		for (unsigned i = 0; i < replies.size(); ++i)
			source.Reply(replies[i]);
	}
Example #9
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		if (!this->fs)
			return;

		const Anope::string &command = params[0];
		const Anope::string &subcommand = params.size() > 1 ? params[1] : "";

		ForbidType ftype = FT_SIZE;
		if (subcommand.equals_ci("NICK"))
			ftype = FT_NICK;
		else if (subcommand.equals_ci("CHAN"))
			ftype = FT_CHAN;
		else if (subcommand.equals_ci("EMAIL"))
			ftype = FT_EMAIL;
		else if (subcommand.equals_ci("REGISTER"))
			ftype = FT_REGISTER;

		if (command.equals_ci("ADD") && params.size() > 3 && ftype != FT_SIZE)
		{
			const Anope::string &expiry = params[2][0] == '+' ? params[2] : "";
			const Anope::string &entry = !expiry.empty() ? params[3] : params[2];
			Anope::string reason;
			if (expiry.empty())
				reason = params[3] + " ";
			if (params.size() > 4)
				reason += params[4];
			reason.trim();

			if (entry.replace_all_cs("?*", "").empty())
			{
				source.Reply(_("The mask must contain at least one non wildcard character."));
				return;
			}

			time_t expiryt = 0;

			if (!expiry.empty())
			{
				expiryt = Anope::DoTime(expiry);
				if (expiryt == -1)
				{
					source.Reply(_("Invalid expiry time \002{0}\002."), expiry);
					return;
				}
				else if (expiryt)
					expiryt += Anope::CurTime;
			}

			NickServ::Nick *target = NickServ::FindNick(entry);
			if (target != NULL && Config->GetModule("nickserv")->Get<bool>("secureadmins", "yes") && target->GetAccount()->IsServicesOper())
			{
				source.Reply(_("Access denied."));
				return;
			}

			ForbidData *d = this->fs->FindForbid(entry, ftype);
			bool created = false;
			if (d == NULL)
			{
				d = Serialize::New<ForbidData *>();
				created = true;
			}

			d->SetMask(entry);
			d->SetCreator(source.GetNick());
			d->SetReason(reason);
			d->SetCreated(Anope::CurTime);
			d->SetExpires(expiryt);
			d->SetType(ftype);

			if (Anope::ReadOnly)
				source.Reply(_("Services are in read-only mode. Any changes made may not persist."));

			Log(LOG_ADMIN, source, this) << "to add a forbid on " << entry << " of type " << subcommand;
			source.Reply(_("Added a forbid on \002{0}\002 of type \002{1}\002 to expire on \002{2}\002."), entry, subcommand.lower(), expiryt ? Anope::strftime(expiryt, source.GetAccount()) : "never");

			/* apply forbid */
			switch (ftype)
			{
				case FT_NICK:
				{
					int na_matches = 0;

					for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
						this->OnUserNickChange(it->second);

					for (auto it = NickServ::service->GetNickList().begin(); it != NickServ::service->GetNickList().end();)
					{
						NickServ::Nick *na = *it;
						++it;

						d = this->fs->FindForbid(na->GetNick(), FT_NICK);
						if (d == NULL)
							continue;

						++na_matches;

						delete na;
					}

					source.Reply(_("\002{0}\002 nickname(s) dropped."), na_matches);
					break;
				}
				case FT_CHAN:
				{
					int chan_matches = 0, ci_matches = 0;

					for (channel_map::const_iterator it = ChannelList.begin(), it_end = ChannelList.end(); it != it_end;)
					{
						Channel *c = it->second;
						++it;

						d = this->fs->FindForbid(c->name, FT_CHAN);
						if (d == NULL)
							continue;

						ServiceBot *OperServ = Config->GetClient("OperServ");
						if (IRCD->CanSQLineChannel && OperServ)
						{
							time_t inhabit = Config->GetModule("chanserv")->Get<time_t>("inhabit", "15s");
#warning "xline allocated on stack"
#if 0
							XLine x(c->name, OperServ->nick, Anope::CurTime + inhabit, d->GetReason());
							IRCD->SendSQLine(NULL, &x);
#endif
						}
						else if (ChanServ::service)
						{
							ChanServ::service->Hold(c);
						}

						++chan_matches;

						for (Channel::ChanUserList::const_iterator cit = c->users.begin(), cit_end = c->users.end(); cit != cit_end;)
						{
							User *u = cit->first;
							++cit;

							if (u->server == Me || u->HasMode("OPER"))
								continue;

							reason = Anope::printf(Language::Translate(u, _("This channel has been forbidden: \002%s\002")), d->GetReason().c_str());

							c->Kick(source.service, u, "%s", reason.c_str());
						}
					}

					for (auto it = ChanServ::service->GetChannels().begin(); it != ChanServ::service->GetChannels().end();)
					{
						ChanServ::Channel *ci = it->second;
						++it;

						d = this->fs->FindForbid(ci->GetName(), FT_CHAN);
						if (d == NULL)
							continue;

						++ci_matches;

						delete ci;
					}

					source.Reply(_("\002{0}\002 channel(s) cleared, and \002{1}\002 channel(s) dropped."), chan_matches, ci_matches);

					break;
				}
				default:
					break;
			}

		}
		else if (command.equals_ci("DEL") && params.size() > 2 && ftype != FT_SIZE)
		{
			const Anope::string &entry = params[2];

			ForbidData *d = this->fs->FindForbid(entry, ftype);
			if (d == nullptr)
			{
				source.Reply(_("Forbid on \002{0}\002 was not found."), entry);
				return;
			}

			if (Anope::ReadOnly)
				source.Reply(_("Services are in read-only mode. Any changes made may not persist."));

			Log(LOG_ADMIN, source, this) << "to remove forbid on " << d->GetMask() << " of type " << subcommand;
			source.Reply(_("\002{0}\002 deleted from the \002{1}\002 forbid list."), d->GetMask(), subcommand);
			d->Delete();
		}
		else if (command.equals_ci("LIST"))
		{
			const std::vector<ForbidData *> &forbids = this->fs->GetForbids();
			if (forbids.empty())
			{
				source.Reply(_("Forbid list is empty."));
				return;
			}

			ListFormatter list(source.GetAccount());
			list.AddColumn(_("Mask")).AddColumn(_("Type")).AddColumn(_("Creator")).AddColumn(_("Expires")).AddColumn(_("Reason"));

			unsigned shown = 0;
			for (unsigned i = 0; i < forbids.size(); ++i)
			{
				ForbidData *d = forbids[i];

				if (ftype != FT_SIZE && ftype != d->GetType())
					continue;

				Anope::string stype;
				if (d->GetType() == FT_NICK)
					stype = "NICK";
				else if (d->GetType() == FT_CHAN)
					stype = "CHAN";
				else if (d->GetType() == FT_EMAIL)
					stype = "EMAIL";
				else if (d->GetType() == FT_REGISTER)
					stype = "REGISTER";
				else
					continue;

				ListFormatter::ListEntry entry;
				entry["Mask"] = d->GetMask();
				entry["Type"] = stype;
				entry["Creator"] = d->GetCreator();
				entry["Expires"] = d->GetExpires() ? Anope::strftime(d->GetExpires(), NULL, true).c_str() : Language::Translate(source.GetAccount(), _("Never"));
				entry["Reason"] = d->GetReason();
				list.AddEntry(entry);
				++shown;
			}

			if (!shown)
			{
				source.Reply(_("There are no forbids of type \002{0}\002."), subcommand.upper());
				return;
			}

			source.Reply(_("Forbid list:"));

			std::vector<Anope::string> replies;
			list.Process(replies);

			for (unsigned i = 0; i < replies.size(); ++i)
				source.Reply(replies[i]);

			if (shown >= forbids.size())
				source.Reply(_("End of forbid list."));
			else
				source.Reply(_("End of forbid list - \002{0}\002/\002{1}\002 entries shown."), shown, forbids.size());
		}
		else
			this->OnSyntaxError(source, command);
	}
Example #10
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		const Anope::string &chan = params[0];

		User *u = source.GetUser();
		Channel *c = Channel::Find(chan);

		if (!c)
		{
			source.Reply(_("Channel \002{0}\002 doesn't exist."), chan);
			return;
		}

		ChanServ::Channel *ci = c->ci;
		if (!ci)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), c->name);
			return;
		}

		if (!source.AccessFor(ci).HasPriv("INVITE") && !source.HasCommand("chanserv/invite"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "INVITE", ci->GetName());
			return;
		}

		User *u2;
		if (params.size() == 1)
			u2 = u;
		else
			u2 = User::Find(params[1], true);

		if (!u2)
		{
			source.Reply(_("\002{0}\002 isn't currently online."), params.size() > 1 ? params[1] : source.GetNick());
			return;
		}

		if (c->FindUser(u2))
		{
			if (u2 == u)
				source.Reply(_("You are already in \002{0}\002!"), c->name);
			else
				source.Reply(_("\002{0}\002 is already in \002{1}\002!"), u2->nick, c->name);

			return;
		}

		bool override = !source.AccessFor(ci).HasPriv("INVITE");

		IRCD->SendInvite(ci->WhoSends(), c, u2);
		if (u2 != u)
		{
			source.Reply(_("\002{0}\002 has been invited to \002{1}\002."), u2->nick, c->name);
			u2->SendMessage(ci->WhoSends(), _("You have been invited to \002{0}\002 by \002{1}\002."), c->name, source.GetNick());
			Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "for " << u2->nick;
		}
Example #11
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		if (Anope::ReadOnly)
		{
			source.Reply(_("Services are in read-only mode."));
			return;
		}

		MemoServ::MemoInfo *mi;
		ChanServ::Channel *ci = NULL;
		Anope::string numstr = !params.empty() ? params[0] : "", chan;

		if (!numstr.empty() && numstr[0] == '#')
		{
			chan = numstr;
			numstr = params.size() > 1 ? params[1] : "";

			ci = ChanServ::Find(chan);
			if (!ci)
			{
				source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
				return;
			}

			if (!source.AccessFor(ci).HasPriv("MEMO"))
			{
				source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
				return;
			}

			mi = ci->GetMemos();
		}
		else
		{
			mi = source.nc->GetMemos();
		}

		if (numstr.empty() || (!isdigit(numstr[0]) && !numstr.equals_ci("ALL") && !numstr.equals_ci("LAST")))
		{
			this->OnSyntaxError(source, numstr);
			return;
		}

		if (!mi || mi->GetMemos().empty())
		{
			if (!chan.empty())
				source.Reply(_("\002{0}\002 has no memos."), chan);
			else
				source.Reply(_("You have no memos."));
			return;
		}

		auto memos = mi->GetMemos();

		if (isdigit(numstr[0]))
		{
			NumberList(numstr, true,
				[&](unsigned int number)
				{
					if (!number || number > memos.size())
						return;

					EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, mi->GetMemo(number - 1));

					mi->Del(number - 1);
					source.Reply(_("Memo \002{0}\002 has been deleted."), number);
				},
				[](){});
		}
		else if (numstr.equals_ci("LAST"))
		{
			/* Delete last memo. */
			EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, mi->GetMemo(memos.size() - 1));
			mi->Del(memos.size() - 1);
			source.Reply(_("Memo \002{0}\002 has been deleted."), memos.size() + 1);
		}
		else
		{
			/* Delete all memos. */
			std::for_each(memos.begin(), memos.end(),
			[&](MemoServ::Memo *m)
			{
				EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ci ? ci->GetName() : source.nc->GetDisplay(), mi, m);
				delete m;
			});
			if (!chan.empty())
				source.Reply(_("All memos for channel \002{0}\002 have been deleted."), chan);
			else
				source.Reply(_("All of your memos have been deleted."));
		}
	}
Example #12
0
	MemoResult Send(const Anope::string &source, const Anope::string &target, const Anope::string &message, bool force) override
	{
		bool ischan, isregistered;
		MemoServ::MemoInfo *mi = GetMemoInfo(target, ischan, isregistered, true);

		if (mi == NULL)
			return MEMO_INVALID_TARGET;

		User *sender = User::Find(source);
		if (sender != NULL && !sender->HasPriv("memoserv/no-limit") && !force)
		{
			time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
			if (send_delay > 0 && sender->lastmemosend + send_delay > Anope::CurTime)
				return MEMO_TOO_FAST;
			else if (!mi->GetMemoMax())
				return MEMO_TARGET_FULL;
			else if (mi->GetMemoMax() > 0 && mi->GetMemos().size() >= static_cast<unsigned>(mi->GetMemoMax()))
				return MEMO_TARGET_FULL;
			else if (mi->HasIgnore(sender))
				return MEMO_SUCCESS;
		}

		if (sender != NULL)
			sender->lastmemosend = Anope::CurTime;

		MemoServ::Memo *m = Serialize::New<MemoServ::Memo *>();
		m->SetMemoInfo(mi);
		m->SetSender(source);
		m->SetTime(Anope::CurTime);
		m->SetText(message);
		m->SetUnread(true);

		EventManager::Get()->Dispatch(&MemoServ::Event::MemoSend::OnMemoSend, source, target, mi, m);

		if (ischan)
		{
			ChanServ::Channel *ci = ChanServ::Find(target);

			if (ci->c)
			{
				for (Channel::ChanUserList::iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
				{
					ChanUserContainer *cu = it->second;

					if (ci->AccessFor(cu->user).HasPriv("MEMO"))
					{
						if (cu->user->Account() && cu->user->Account()->HasFieldS("MEMO_RECEIVE"))
							cu->user->SendMessage(*MemoServ, _("There is a new memo on channel \002{0}\002. Type \002{1}{2} READ {3} {4}\002 to read it."), ci->GetName(), Config->StrictPrivmsg, MemoServ->nick, ci->GetName(), mi->GetMemos().size()); // XXX
					}
				}
			}
		}
		else
		{
			NickServ::Account *nc = NickServ::FindNick(target)->GetAccount();

			if (nc->HasFieldS("MEMO_RECEIVE"))
				for (User *u : nc->users)
					u->SendMessage(*MemoServ, _("You have a new memo from \002{0}\002. Type \002{1}{2} READ {3}\002 to read it."), source, Config->StrictPrivmsg, MemoServ->nick, mi->GetMemos().size());//XXX

			/* let's get out the mail if set in the nickcore - certus */
			if (nc->HasFieldS("MEMO_MAIL"))
				SendMemoMail(nc, mi, m);
		}

		return MEMO_SUCCESS;
	}
Example #13
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		if (Anope::ReadOnly)
		{
			source.Reply(_("Services are in read-only mode."));
			return;
		}

		const Anope::string &nname = params[0];

		bool ischan, isregistered;
		MemoServ::MemoInfo *mi = MemoServ::service->GetMemoInfo(nname, ischan, isregistered, false);

		if (!isregistered)
		{
			if (ischan)
				source.Reply(_("Channel \002{0}\002 isn't registered."), nname);
			else
				source.Reply(_("\002{0}\002 isn't registered."), nname);

			return;
		}

		if (mi == nullptr)
			return;

		ChanServ::Channel *ci = NULL;
		NickServ::Nick *na = NULL;
		if (ischan)
		{
			ci = ChanServ::Find(nname);
			if (ci == nullptr)
				return;
		}
		else
		{
			na = NickServ::FindNick(nname);
			if (na == nullptr)
				return;
		}

		auto memos = mi->GetMemos();
		for (int i = memos.size() - 1; i >= 0; --i)
		{
			MemoServ::Memo *m = memos[i];

			if (!m->GetUnread())
				continue;

			NickServ::Nick *sender = NickServ::FindNick(m->GetSender());

			if (sender && sender->GetAccount() == source.GetAccount())
			{
				EventManager::Get()->Dispatch(&MemoServ::Event::MemoDel::OnMemoDel, ischan ? ci->GetName() : na->GetAccount()->GetDisplay(), mi, m);
				mi->Del(i);
				source.Reply(_("Your last memo to \002{0}\002 has been cancelled."), ischan ? ci->GetName() : na->GetAccount()->GetDisplay());
				return;
			}
		}

		source.Reply(_("No memo was cancelable."));
	}
Example #14
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		const Anope::string &chan = params[0];

		User *u = source.GetUser();
		Channel *c = Channel::Find(chan);

		if (!c)
		{
			source.Reply(_("Channel \002{0}\002 doesn't exist."), chan);
			return;
		}

		ChanServ::Channel *ci = c->GetChannel();
		if (!ci)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), c->name);
			return;
		}

		if (!source.AccessFor(ci).HasPriv("INVITE") && !source.HasOverrideCommand("chanserv/invite"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "INVITE", ci->GetName());
			return;
		}

		User *u2;
		if (params.size() == 1)
			u2 = u;
		else
			u2 = User::Find(params[1], true);

		if (!u2)
		{
			source.Reply(_("\002{0}\002 isn't currently online."), params.size() > 1 ? params[1] : source.GetNick());
			return;
		}

		if (c->FindUser(u2))
		{
			if (u2 == u)
				source.Reply(_("You are already in \002{0}\002!"), c->name);
			else
				source.Reply(_("\002{0}\002 is already in \002{1}\002!"), u2->nick, c->name);

			return;
		}

		IRCD->Send<messages::Invite>(ci->WhoSends(), c, u2);
		if (u2 != u)
		{
			source.Reply(_("\002{0}\002 has been invited to \002{1}\002."), u2->nick, c->name);
			u2->SendMessage(ci->WhoSends(), _("You have been invited to \002{0}\002 by \002{1}\002."), c->name, source.GetNick());
			logger.Command(source, ci, _("{source} used {command} on {channel} to invite {0}"), u2->nick);
		}
		else
		{
			u2->SendMessage(ci->WhoSends(), _("You have been invited to \002{0}\002."), c->name);
			logger.Command(source, ci, _("{source} used {command} on {channel}"));
		}
	}
Example #15
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{

		MemoServ::MemoInfo *mi;
		ChanServ::Channel *ci = NULL;
		Anope::string numstr = params[0], chan;

		if (!numstr.empty() && numstr[0] == '#')
		{
			chan = numstr;
			numstr = params.size() > 1 ? params[1] : "";

			ci = ChanServ::Find(chan);
			if (!ci)
			{
				source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
				return;
			}

			if (!source.AccessFor(ci).HasPriv("MEMO"))
			{
				source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
				return;
			}

			mi = ci->GetMemos();
		}
		else
			mi = source.nc->GetMemos();

		if (numstr.empty() || (!numstr.equals_ci("LAST") && !numstr.equals_ci("NEW") && !numstr.is_number_only()))
		{
			this->OnSyntaxError(source, numstr);
			return;
		}

		if (!mi)
			return;

		auto memos = mi->GetMemos();

		if (memos.empty())
		{
			if (!chan.empty())
				source.Reply(_("\002{0}\002 has no memos."), chan);
			else
				source.Reply(_("You have no memos."));
			return;
		}

		int i, end;

		if (numstr.equals_ci("NEW"))
		{
			int readcount = 0;
			for (i = 0, end = memos.size(); i < end; ++i)
				if (mi->GetMemo(i)->GetUnread())
				{
					DoRead(source, mi, ci, i);
					++readcount;
				}
			if (!readcount)
			{
				if (!chan.empty())
					source.Reply(_("\002{0}\002 has no new memos."), chan);
				else
					source.Reply(_("You have no new memos."));
			}
		}
		else if (numstr.equals_ci("LAST"))
		{
			for (i = 0, end = memos.size() - 1; i < end; ++i);
			DoRead(source, mi, ci, i);
		}
		else /* number[s] */
		{
			NumberList(numstr, false,
				[&](unsigned int number)
				{
					if (!number || number > memos.size())
						return;

					DoRead(source, mi, ci, number - 1);
				},
				[]{});
		}
	}
Example #16
0
File: log.cpp Project: Robby-/anope
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		const Anope::string &channel = params[0];

		ChanServ::Channel *ci = ChanServ::Find(channel);
		if (ci == NULL)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), channel);
			return;
		}

		if (!source.AccessFor(ci).HasPriv("SET") && !source.HasOverridePriv("chanserv/administration"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "SET", ci->GetName());
			return;
		}

		if (params.size() == 1)
		{
			std::vector<LogSetting *> ls = ci->GetRefs<LogSetting *>();
			if (ls.empty())
			{
				source.Reply(_("There currently are no logging configurations for \002{0}\002."), ci->GetName());
				return;
			}

			ListFormatter list(source.GetAccount());
			list.AddColumn(_("Number")).AddColumn(_("Service")).AddColumn(_("Command")).AddColumn(_("Method")).AddColumn("");

			for (unsigned i = 0; i < ls.size(); ++i)
			{
				LogSetting *log = ls[i];

				ListFormatter::ListEntry entry;
				entry["Number"] = stringify(i + 1);
				entry["Service"] = log->GetCommandService();
				entry["Command"] = !log->GetCommandName().empty() ? log->GetCommandName() : log->GetServiceName();
				entry["Method"] = log->GetMethod();
				entry[""] = log->GetExtra();
				list.AddEntry(entry);
			}

			source.Reply(_("Log list for \002{0}\002:"), ci->GetName());

			std::vector<Anope::string> replies;
			list.Process(replies);

			for (unsigned i = 0; i < replies.size(); ++i)
				source.Reply(replies[i]);
		}
		else if (params.size() > 2)
		{
			if (Anope::ReadOnly)
			{
				source.Reply(_("Services are in read-only mode."));
				return;
			}

			const Anope::string &command = params[1];
			const Anope::string &method = params[2];
			const Anope::string &extra = params.size() > 3 ? params[3] : "";

			size_t sl = command.find('/');
			if (sl == Anope::string::npos)
			{
				source.Reply(_("\002{0}\002 is not a valid command."), command);
				return;
			}

			Anope::string service = command.substr(0, sl),
				command_name = command.substr(sl + 1);
			ServiceBot *bi = ServiceBot::Find(service, true);

			Anope::string service_name;

			/* Allow either a command name or a service name. */
			if (bi && bi->commands.count(command_name))
			{
				/* Get service name from command */
				service_name = bi->commands[command_name].name;
			}
			else if (ServiceReference<Command>(command.lower()))
			{
				/* This is the service name, don't use any specific command */
				service_name = command;
				bi = NULL;
				command_name.clear();
			}
			else
			{
				source.Reply(_("\002{0}\002 is not a valid command."), command);
				return;
			}

			if (!method.equals_ci("MESSAGE") && !method.equals_ci("NOTICE") && !method.equals_ci("MEMO"))
			{
				source.Reply(_("\002%s\002 is not a valid logging method."));
				return;
			}

			for (unsigned i = 0; i < extra.length(); ++i)
				if (ModeManager::GetStatusChar(extra[i]) == 0)
				{
					source.Reply(_("\002%c\002 is an unknown status mode."), extra[i]);
					return;
				}

			std::vector<LogSetting *> ls = ci->GetRefs<LogSetting *>();
			for (unsigned i = ls.size(); i > 0; --i)
			{
				LogSetting *log = ls[i - 1];

				if (log->GetServiceName() == service_name && log->GetMethod().equals_ci(method) && command_name.equals_ci(log->GetCommandName()))
				{
					if (log->GetExtra() == extra)
					{
						logger.Command(source, ci, _("{source} used {command} on {channel} to remove logging for {0} with method {1}"),
							command, method + (extra.empty() ? "" : (" " + extra)));

						source.Reply(_("Logging for command \002{0}\002 on \002{1}\002 with log method \002{2}{3}{4}\002 has been removed."),
								!log->GetCommandName().empty() ? log->GetCommandName() : log->GetServiceName(),
								!log->GetCommandService().empty() ? log->GetCommandService() : "any service", method, extra.empty() ? "" : " ", extra);
						log->Delete();
					}
					else
					{
						log->SetExtra(extra);

						logger.Command(source, ci, _("{source} used {command} on {channel} to change logging for {0} to method {1}"),
							command, method + (extra.empty() ? "" : (" " + extra)));

						source.Reply(_("Logging changed for command \002{0}\002 on \002{1}\002, now using log method \002{2}{3}{4]\002."),
								!log->GetCommandName().empty() ? log->GetCommandName() : log->GetServiceName(),
								!log->GetCommandService().empty() ? log->GetCommandService() : "any service", method, extra.empty() ? "" : " ", extra);
					}
					return;
				}
			}

			LogSetting *log = Serialize::New<LogSetting *>();
			log->SetChannel(ci);
			log->SetServiceName(service_name);
			if (bi)
				log->SetCommandService(bi->nick);
			log->SetCommandName(command_name);
			log->SetMethod(method);
			log->SetExtra(extra);
			log->SetCreated(Anope::CurTime);
			log->SetCreator(source.GetNick());

			logger.Command(source, ci,
					_("{source} used {command} on {channel} to log {0} to method {1}"), command, method + (extra.empty() ? "" : (" " + extra)));

			source.Reply(_("Logging is now active for command \002{0}\002 on \002{1}\002, using log method \002{2}{3}{4}\002."),
					!command_name.empty() ? command_name : service_name, bi ? bi->nick : "any service", method, extra.empty() ? "" : " ", extra);
		}
		else
		{
			this->OnSyntaxError(source, "");
		}
	}
Example #17
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
	{
		ChannelMode *cm = ModeManager::FindChannelModeByName("BAN");
		if (!cm)
			return;

		std::vector<ChannelMode *> modes = cm->listeners;
		modes.push_back(cm);

		if (params.empty())
		{
			if (!source.GetUser())
				return;

			unsigned count = 0;

			for (ChanServ::Channel *ci : source.GetAccount()->GetRefs<ChanServ::Channel *>())
			{
				if (!ci->c || !source.AccessFor(ci).HasPriv("UNBAN"))
					continue;

				for (unsigned j = 0; j < modes.size(); ++j)
					if (ci->c->Unban(source.GetUser(), modes[j]->name, true))
						++count;
			}

			Log(LOG_COMMAND, source, this, NULL) << "on all channels";
			source.Reply(_("You have been unbanned from %d channels."), count);

			return;
		}

		const Anope::string &chan = params[0];
		ChanServ::Channel *ci = ChanServ::Find(chan);
		if (ci == NULL)
		{
			source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
			return;
		}

		if (ci->c == NULL)
		{
			source.Reply(_("Channel \002{0}\002 doesn't exist."), ci->GetName());
			return;
		}

		if (!source.AccessFor(ci).HasPriv("UNBAN") && !source.HasPriv("chanserv/kick"))
		{
			source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "UNBAN", ci->GetName());
			return;
		}

		User *u2 = source.GetUser();
		if (params.size() > 1)
			u2 = User::Find(params[1], true);

		if (!u2)
		{
			if (params.size() > 1)
				source.Reply(_("User \002{0}\002 isn't currently online."), params[1]);
			return;
		}

		bool override = !source.AccessFor(ci).HasPriv("UNBAN") && source.HasPriv("chanserv/kick");
		Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to unban " << u2->nick;

		for (unsigned i = 0; i < modes.size(); ++i)
			ci->c->Unban(u2, modes[i]->name, source.GetUser() == u2);
		if (u2 == source.GetUser())
			source.Reply(_("You have been unbanned from \002{0}\002."), ci->c->name);
		else
			source.Reply(_("\002{0}\002 has been unbanned from \002{1}\002."), u2->nick, ci->c->name);
	}
Example #18
0
	void DoLimit(CommandSource &source, const std::vector<Anope::string> &params, MemoServ::MemoInfo *mi)
	{

		Anope::string p1 = params[1];
		Anope::string p2 = params.size() > 2 ? params[2] : "";
		Anope::string p3 = params.size() > 3 ? params[3] : "";
		Anope::string user, chan;
		int16_t limit;
		NickServ::Account *nc = source.nc;
		ChanServ::Channel *ci = NULL;
		bool is_servadmin = source.HasPriv("memoserv/set-limit");

		if (p1[0] == '#')
		{
			chan = p1;
			p1 = p2;
			p2 = p3;
			p3 = params.size() > 4 ? params[4] : "";

			ci = ChanServ::Find(chan);
			if (!ci)
			{
				source.Reply(_("Channel \002{0}\002 isn't registered."), chan);
				return;
			}

			if (!is_servadmin && !source.AccessFor(ci).HasPriv("MEMO"))
			{
				source.Reply(_("Access denied. You do not have privilege \002{0}\002 on \002{1}\002."), "MEMO", ci->GetName());
				return;
			}
			mi = ci->GetMemos();
		}
		if (is_servadmin)
		{
			if (!p2.empty() && !p2.equals_ci("HARD") && chan.empty())
			{
				NickServ::Nick *na;
				if (!(na = NickServ::FindNick(p1)))
				{
					source.Reply(_("\002{0}\002 isn't registered."), p1);
					return;
				}
				user = p1;
				mi = na->GetAccount()->GetMemos();
				nc = na->GetAccount();
				p1 = p2;
				p2 = p3;
			}
			else if (p1.empty() || (!p1.is_pos_number_only() && !p1.equals_ci("NONE")) || (!p2.empty() && !p2.equals_ci("HARD")))
			{
				this->OnSyntaxError(source, "");
				return;
			}
			if (!chan.empty())
			{
				if (!p2.empty())
					ci->SetS<bool>("MEMO_HARDMAX", true);
				else
					ci->UnsetS<bool>("MEMO_HARDMAX");
			}
			else
			{
				if (!p2.empty())
					nc->SetS<bool>("MEMO_HARDMAX", true);
				else
					nc->UnsetS<bool>("MEMO_HARDMAX");
			}
			limit = -1;
			try
			{
				limit = convertTo<int16_t>(p1);
			}
			catch (const ConvertException &) { }
		}
		else
		{
			if (p1.empty() || !p2.empty() || !isdigit(p1[0]))
			{
				this->OnSyntaxError(source, "");
				return;
			}
			if (!chan.empty() && ci->HasFieldS("MEMO_HARDMAX"))
			{
				source.Reply(_("The memo limit for \002{0}\002 may not be changed."), chan);
				return;
			}
			if (chan.empty() && nc->HasFieldS("MEMO_HARDMAX"))
			{
				source.Reply(_("You are not permitted to change your memo limit."));
				return;
			}
			int max_memos = Config->GetModule("memoserv")->Get<int>("maxmemos");
			limit = -1;
			try
			{
				limit = convertTo<int16_t>(p1);
			}
			catch (const ConvertException &) { }
			/* The first character is a digit, but we could still go negative
			 * from overflow... watch out! */
			if (limit < 0 || (max_memos > 0 && limit > max_memos))
			{
				if (!chan.empty())
					source.Reply(_("You cannot set the memo limit for \002{0}\002 higher than \002{1}\002."), chan, max_memos);
				else
					source.Reply(_("You cannot set your memo limit higher than \002{0}\002."), max_memos);
				return;
			}
		}
		mi->SetMemoMax(limit);
		if (limit > 0)
		{
			if (chan.empty() && nc == source.nc)
				source.Reply(_("Your memo limit has been set to \002{0}\002."), limit);
			else
				source.Reply(_("Memo limit for \002{0}\002 set to \002{1}\002."), !chan.empty() ? chan : user, limit);
		}
		else if (!limit)
		{
			if (chan.empty() && nc == source.nc)
				source.Reply(_("You will no longer be able to receive memos."));
			else
				source.Reply(_("Memo limit for \002{0}\002 set to \0020\002."), !chan.empty() ? chan : user);
		}
		else
		{
			if (chan.empty() && nc == source.nc)
				source.Reply(_("Your memo limit has been disabled."));
			else
				source.Reply(_("Memo limit \002disabled\002 for \002{0}\002."), !chan.empty() ? chan : user);
		}
	}
Example #19
0
	void Execute(CommandSource &source, const std::vector<Anope::string> &params) 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);
		}
	}