Exemple #1
0
	static Anope::string Unescape(const Anope::string &string)
	{
		Anope::string ret = string;
		for (int i = 0; special[i].character.empty() == false; ++i)
			if (!special[i].replace.empty())
				ret = ret.replace_all_cs(special[i].replace, special[i].character);

		for (size_t i, last = 0; (i = string.find("&#", last)) != Anope::string::npos;)
		{
			last = i + 1;

			size_t end = string.find(';', i);
			if (end == Anope::string::npos)
				break;

			Anope::string ch = string.substr(i + 2, end - (i + 2));

			if (ch.empty())
				continue;

			long l;
			if (!ch.empty() && ch[0] == 'x')
				l = strtol(ch.substr(1).c_str(), NULL, 16);
			else
				l = strtol(ch.c_str(), NULL, 10);

			if (l > 0 && l < 256)
				ret = ret.replace_all_cs("&#" + ch + ";", Anope::string(l));
		}

		return ret;
	}
Exemple #2
0
	Anope::string RealMask(const Anope::string &mask)
	{
		/* If it s an existing user, we ignore the hostmask. */
		User *u = User::Find(mask, true);
		if (u)
			return "*!*@" + u->host;

		size_t host = mask.find('@');
		/* Determine whether we get a nick or a mask. */
		if (host != Anope::string::npos)
		{
			size_t user = mask.find('!');
			/* Check whether we have a nick too.. */
			if (user != Anope::string::npos)
			{
				if (user > host)
					/* this should never happen */
					return "";
				else
					return mask;
			}
			else
				/* We have user@host. Add nick wildcard. */
				return "*!" + mask;
		}

		/* We only got a nick.. */
		return mask + "!*@*";
	}
Exemple #3
0
bool ChannelModeKey::IsValid(Anope::string &value) const
{
	if (!value.empty() && value.find(':') == Anope::string::npos && value.find(',') == Anope::string::npos)
		return true;

	return false;
}
Exemple #4
0
	MyRedisService(Module *c, const Anope::string &n, const Anope::string &h, int p, unsigned d) : Provider(c, n), host(h), port(p), db(d), sock(NULL), sub(NULL),
		ti(c), in_transaction(false)
	{
		sock = new RedisSocket(this, host.find(':') != Anope::string::npos);
		sock->Connect(host, port);

		sub = new RedisSocket(this, host.find(':') != Anope::string::npos);
		sub->Connect(host, port);
	}
Exemple #5
0
bool AccessGroup::HasPriv(const Anope::string &name) const
{
	if (this->super_admin)
		return true;
	else if (!ci || ci->GetLevel(name) == ACCESS_INVALID)
		return false;

	/* Privileges prefixed with auto are understood to be given
	 * automatically. Sometimes founders want to not automatically
	 * obtain privileges, so we will let them */
	bool auto_mode = !name.find("AUTO");

	/* Only grant founder privilege if this isn't an auto mode or if they don't match any entries in this group */
	if ((!auto_mode || this->empty()) && this->founder)
		return true;

	EventReturn MOD_RESULT;
	FOREACH_RESULT(OnGroupCheckPriv, MOD_RESULT, (this, name));
	if (MOD_RESULT != EVENT_CONTINUE)
		return MOD_RESULT == EVENT_ALLOW;

	for (unsigned i = this->size(); i > 0; --i)
	{
		ChanAccess *access = this->at(i - 1);

		if (::HasPriv(*this, access, name))
			return true;
	}

	return false;
}
Exemple #6
0
cidr::cidr(const Anope::string &ip, unsigned char len)
{
	bool ipv6 = ip.find(':') != Anope::string::npos;
	this->addr.pton(ipv6 ? AF_INET6 : AF_INET, ip);
	this->cidr_ip = ip;
	this->cidr_len = len;
}
Exemple #7
0
cidr::cidr(const Anope::string &ip)
{
	bool ipv6 = ip.find(':') != Anope::string::npos;
	size_t sl = ip.find_last_of('/');

	if (sl == Anope::string::npos)
	{
		this->cidr_ip = ip;
		this->cidr_len = ipv6 ? 128 : 32;
		this->addr.pton(ipv6 ? AF_INET6 : AF_INET, ip);
	}
	else
	{
		Anope::string real_ip = ip.substr(0, sl);
		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->addr.pton(ipv6 ? AF_INET6 : AF_INET, real_ip);
	}
}
Exemple #8
0
void User::Identify(NickServ::Nick *na)
{
	if (this->nick.equals_ci(na->GetNick()))
	{
		na->SetLastUsermask(this->GetIdent() + "@" + this->GetDisplayedHost());
		na->SetLastRealhost(this->GetIdent() + "@" + this->host);
		na->SetLastRealname(this->realname);
		na->SetLastSeen(Anope::CurTime);
	}

	IRCD->SendLogin(this, na);

	this->Login(na->GetAccount());

	EventManager::Get()->Dispatch(&Event::NickIdentify::OnNickIdentify, this);

	if (this->IsServicesOper())
	{
		Anope::string m = this->nc->o->GetType()->modes;
		if (!m.empty())
		{
			this->SetModes(NULL, "%s", m.c_str());
			this->SendMessage(Me, "Changing your usermodes to \002{0}\002", m.c_str());
			UserMode *um = ModeManager::FindUserModeByName("OPER");
			if (um && !this->HasMode("OPER") && m.find(um->mchar) != Anope::string::npos)
				IRCD->SendOper(this);
		}
		if (IRCD->CanSetVHost && !this->nc->o->GetVhost().empty())
		{
			this->SendMessage(Me, "Changing your vhost to \002{0}\002", this->nc->o->GetVhost());
 			this->SetDisplayedHost(this->nc->o->GetVhost());
			IRCD->SendVhost(this, "", this->nc->o->GetVhost());
		}
	}
}
Exemple #9
0
	/* strip dots from username, and remove anything after the first + */
	Anope::string CleanMail(const Anope::string &email)
	{
		size_t host = email.find('@');
		if (host == Anope::string::npos)
			return email;

		Anope::string username = email.substr(0, host);
		username = username.replace_all_cs(".", "");

		size_t sz = username.find('+');
		if (sz != Anope::string::npos)
			username = username.substr(0, sz);

		Anope::string cleaned = username + email.substr(host);
		logger.Debug("cleaned {0} to {1}", email, cleaned);
		return cleaned;
	}
Exemple #10
0
MessageSource::MessageSource(const Anope::string &src) : source(src)
{
	/* no source for incoming message is our uplink */
	if (src.empty())
		this->s = Servers::GetUplink();
	else if (IRCD->RequiresID || src.find('.') != Anope::string::npos)
		this->s = Server::Find(src);
	if (this->s == NULL)
		this->u = User::Find(src);
}
Exemple #11
0
	void Send(Interface *i, const std::vector<std::pair<const char *, size_t> > &args)
	{
		if (!sock)
		{
			sock = new RedisSocket(this, host.find(':') != Anope::string::npos);
			sock->Connect(host, port);
		}

		this->Send(sock, i, args);
	}
Exemple #12
0
/**
 * Get the token
 * @param str String to search in
 * @param dilim Character to search for
 * @param token_number the token number
 * @return token
 */
Anope::string myStrGetToken(const Anope::string &str, char dilim, int token_number)
{
	if (str.empty() || str.find(dilim) == Anope::string::npos)
		return token_number ? "" : str;

	Anope::string substring;
	sepstream sep(str, dilim);

	for (int i = 0; i < token_number + 1 && !sep.StreamEnd() && sep.GetToken(substring); ++i);

	return substring;
}
Exemple #13
0
	void Subscribe(Interface *i, const Anope::string &ch) override
	{
		if (sub == NULL)
		{
			sub = new RedisSocket(this, host.find(':') != Anope::string::npos);
			sub->Connect(host, port);
		}

		std::vector<Anope::string> args = { "SUBSCRIBE", ch };
		this->SendCommand(sub, NULL, args);

		sub->subinterfaces[ch] = i;
	}
Exemple #14
0
NumberList::NumberList(const Anope::string &list, bool descending) : is_valid(true), desc(descending)
{
	Anope::string error;
	commasepstream sep(list);
	Anope::string token;

	sep.GetToken(token);
	if (token.empty())
		token = list;
	do
	{
		size_t t = token.find('-');

		if (t == Anope::string::npos)
		{
			unsigned num = convertTo<unsigned>(token, error, false);
			if (error.empty())
				numbers.insert(num);
			else
			{
				if (!this->InvalidRange(list))
				{
					is_valid = false;
					return;
				}
			}
		}
		else
		{
			Anope::string error2;
			unsigned num1 = convertTo<unsigned>(token.substr(0, t), error, false);
			unsigned num2 = convertTo<unsigned>(token.substr(t + 1), error2, false);
			if (error.empty() && error2.empty())
			{
				for (unsigned i = num1; i <= num2; ++i)
					numbers.insert(i);
			}
			else
			{
				if (!this->InvalidRange(list))
				{
					is_valid = false;
					return;
				}
			}
		}
	} while (sep.GetToken(token));
}
Exemple #15
0
/**
 * Checks whether we have a valid, common e-mail 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
 * are really being used will fail the check.
 *
 * @param email Email to Validate
 * @return bool
 */
bool MailValidate(const Anope::string &email)
{
	bool has_period = false;

	static char specials[] = {'(', ')', '<', '>', '@', ',', ';', ':', '\\', '\"', '[', ']', ' '};

	if (email.empty())
		return false;
	Anope::string copy = email;

	size_t at = copy.find('@');
	if (at == Anope::string::npos)
		return false;
	Anope::string domain = copy.substr(at + 1);
	copy = copy.substr(0, at);

	/* Don't accept empty copy or domain. */
	if (copy.empty() || domain.empty())
		return false;

	/* Check for forbidden characters in the name */
	for (unsigned i = 0, end = copy.length(); i < end; ++i)
	{
		if (copy[i] <= 31 || copy[i] >= 127)
			return false;
		for (unsigned int j = 0; j < 13; ++j)
			if (copy[i] == specials[j])
				return false;
	}

	/* Check for forbidden characters in the domain */
	for (unsigned i = 0, end = domain.length(); i < end; ++i)
	{
		if (domain[i] <= 31 || domain[i] >= 127)
			return false;
		for (unsigned int j = 0; j < 13; ++j)
			if (domain[i] == specials[j])
				return false;
		if (domain[i] == '.')
		{
			if (!i || i == end - 1)
				return false;
			has_period = true;
		}
	}

	return has_period;
}
Exemple #16
0
/**
 * Is the given nick a network service
 * @param nick to check
 * @param int Check if botserv bots
 * @return int
 */
bool nickIsServices(const Anope::string &tempnick, bool bot)
{
	if (tempnick.empty())
		return false;

	Anope::string nick = tempnick;

	size_t at = nick.find('@');
	if (at != Anope::string::npos)
	{
		Anope::string servername = nick.substr(at + 1);
		if (!servername.equals_ci(Config->ServerName))
			return false;
		nick = nick.substr(0, at);
	}

	BotInfo *bi = findbot(nick);
	if (bi)
		return bot ? true : bi->HasFlag(BI_CORE);
	return false;
}
Exemple #17
0
/** Called on startup to organize our starting arguments in a better way
 * and check for errors
 * @param ac number of args
 * @param av args
 */
static void ParseCommandLineArguments(int ac, char **av)
{
	for (int i = 1; i < ac; ++i)
	{
		Anope::string option = av[i];
		Anope::string param;
		while (!option.empty() && option[0] == '-')
			option.erase(option.begin());
		size_t t = option.find('=');
		if (t != Anope::string::npos)
		{
			param = option.substr(t + 1);
			option.erase(t);
		}

		if (option.empty())
			continue;

		CommandLineArguments.push_back(std::make_pair(option, param));
	}
}
Exemple #18
0
	bool IsIdentValid(const Anope::string &ident) override
	{
		if (ident.empty() || ident.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
			return false;

		Anope::string chars = "~}|{ `_^]\\[ .-$";

		for (unsigned i = 0; i < ident.length(); ++i)
		{
			const char &c = ident[i];

			if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
				continue;

			if (chars.find(c) != Anope::string::npos)
				continue;

			return false;
		}

		return true;
	}
Exemple #19
0
void User::SetModeInternal(const MessageSource &source, UserMode *um, const Anope::string &param)
{
	if (!um)
		return;

	this->modes[um->name] = param;

	if (um->name == "OPER")
	{
		++OperCount;

		if (this->IsServicesOper())
		{
			Anope::string m = this->nc->o->GetType()->modes;
			if (!m.empty())
			{
				this->SetModes(NULL, "%s", m.c_str());
				this->SendMessage(Me, "Changing your usermodes to \002{0}\002", m);
				UserMode *oper = ModeManager::FindUserModeByName("OPER");
				if (oper && !this->HasMode("OPER") && m.find(oper->mchar) != Anope::string::npos)
					IRCD->SendOper(this);
			}
			if (IRCD->CanSetVHost && !this->nc->o->GetVhost().empty())
			{
				this->SendMessage(Me, "Changing your vhost to \002{0}\002", this->nc->o->GetVhost());
				this->SetDisplayedHost(this->nc->o->GetVhost());
				IRCD->SendVhost(this, "", this->nc->o->GetVhost());
			}
		}
	}

	if (um->name == "CLOAK" || um->name == "VHOST")
		this->UpdateHost();

	EventManager::Get()->Dispatch(&Event::UserModeSet::OnUserModeSet, source, this, um->name);
}
Exemple #20
0
bool IRCDProto::IsNickValid(const Anope::string &nick)
{
	/**
	 * RFC: defination of a valid nick
	 * nickname =  ( letter / special ) ( letter / digit / special / "-" )
	 * letter   =  A-Z / a-z
	 * digit    =  0-9
	 * special  =  [, ], \, `, _, ^, {, |, }
	 **/

	 if (nick.empty())
	 	return false;

	Anope::string special = "[]\\`_^{|}";

	for (unsigned int i = 0; i < nick.length(); ++i)
		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] != '-')))
			return false;

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

		User *u = source.GetUser();
		NickServ::Nick *na = NickServ::FindNick(source.GetNick());
		if (!na || na->GetAccount() != source.GetAccount())
		{
			source.Reply(_("Access denied.")); //XXX with nonickownership this should be allowed.
			return;
		}

		if (source.GetAccount()->HasFieldS("UNCONFIRMED"))
		{
			source.Reply(_("You must confirm your account before you may request a vhost."));
			return;
		}

		Anope::string rawhostmask = params[0];

		Anope::string user, host;
		size_t a = rawhostmask.find('@');

		if (a == Anope::string::npos)
			host = rawhostmask;
		else
		{
			user = rawhostmask.substr(0, a);
			host = rawhostmask.substr(a + 1);
		}

		if (host.empty())
		{
			this->OnSyntaxError(source, "");
			return;
		}

		if (!user.empty())
		{
			if (user.length() > Config->GetBlock("networkinfo")->Get<unsigned>("userlen"))
			{
				source.Reply(_("The username \002{0}\002 is too long, please use a username shorter than %d characters."), Config->GetBlock("networkinfo")->Get<unsigned>("userlen"));
				return;
			}

			if (!IRCD->CanSetVIdent)
			{
				source.Reply(_("Vhosts may not contain a username."));
				return;
			}

			if (!IRCD->IsIdentValid(user))
			{
				source.Reply(_("The requested username is not valid."));
				return;
			}
		}

		if (host.length() > Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"))
		{
			source.Reply(_("The requested vhost is too long, please use a hostname no longer than {0} characters."), Config->GetBlock("networkinfo")->Get<unsigned>("hostlen"));
			return;
		}

		if (!IRCD->IsHostValid(host))
		{
			source.Reply(_("The requested hostname is not valid."));
			return;
		}

		time_t send_delay = Config->GetModule("memoserv")->Get<time_t>("senddelay");
		if (Config->GetModule(this->GetOwner())->Get<bool>("memooper") && send_delay > 0 && u && u->lastmemosend + send_delay > Anope::CurTime)
		{
			source.Reply(_("Please wait %d seconds before requesting a new vHost."), send_delay);
			u->lastmemosend = Anope::CurTime;
			return;
		}

		HostRequest *req = Serialize::New<HostRequest *>();
		req->SetNick(na);
		req->SetIdent(user);
		req->SetHost(host);
		req->SetTime(Anope::CurTime);

		source.Reply(_("Your vhost has been requested."));
		this->SendMemos(source, user, host);
		Log(LOG_COMMAND, source, this) << "to request new vhost " << (!user.empty() ? user + "@" : "") << host;
	}
Exemple #22
0
bool IRCdMessage::OnPrivmsg(const Anope::string &source, const std::vector<Anope::string> &params)
{
	const Anope::string &receiver = params.size() > 0 ? params[0] : "";
	Anope::string message = params.size() > 1 ? params[1] : "";

	/* Messages from servers can happen on some IRCds, check for . */
	if (source.empty() || receiver.empty() || message.empty() || source.find('.') != Anope::string::npos)
		return true;

	User *u = finduser(source);

	if (!u)
	{
		Log() << message << ": user record for " << source << " not found";

		BotInfo *bi = findbot(receiver);
		if (bi)
			ircdproto->SendMessage(bi, source, "%s", "Internal error - unable to process request.");

		return true;
	}

	if (receiver[0] == '#')
	{
		Channel *c = findchan(receiver);
		if (c)
		{
			FOREACH_MOD(I_OnPrivmsg, 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('@');
		if (s != Anope::string::npos)
		{
			Anope::string servername(receiver.begin() + s + 1, receiver.end());
			botname = botname.substr(0, s);
			if (!servername.equals_ci(Config->ServerName))
				return true;
		}
		else if (Config->UseStrictPrivMsg)
		{
			BotInfo *bi = findbot(receiver);
			if (!bi)
				return true;
			Log(LOG_DEBUG) << "Ignored PRIVMSG without @ from " << source;
			u->SendMessage(bi, _("\"/msg %s\" is no longer supported.  Use \"/msg %s@%s\" or \"/%s\" instead."), receiver.c_str(), receiver.c_str(), Config->ServerName.c_str(), receiver.c_str());
			return true;
		}

		BotInfo *bi = findbot(botname);

		if (bi)
		{
			EventReturn MOD_RESULT;
			FOREACH_RESULT(I_OnBotPrivmsg, OnBotPrivmsg(u, bi, message));
			if (MOD_RESULT == EVENT_STOP)
				return true;

			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);
					ircdproto->SendCTCP(bi, u->nick, "%s", buf.c_str());
				}
				else if (message.substr(0, 9).equals_ci("\1VERSION\1"))
				{
					Module *enc = ModuleManager::FindFirstOf(ENCRYPTION);
					ircdproto->SendCTCP(bi, u->nick, "VERSION Anope-%s %s :%s - (%s) -- %s", Anope::Version().c_str(), Config->ServerName.c_str(), ircd->name, enc ? enc->name.c_str() : "unknown", Anope::VersionBuildString().c_str());
				}
				return true;
			}
			
			bi->OnMessage(u, message);
		}
	}

	return true;
}
Exemple #23
0
	EventReturn OnLoadDatabase() override
	{
		const Anope::string &db_name = Anope::DataDir + "/" + Config->GetModule(this)->Get<Anope::string>("database", "anope.db");

		std::fstream fd(db_name.c_str(), std::ios_base::in | std::ios_base::binary);
		if (!fd.is_open())
		{
			Log(this) << "Unable to open " << db_name << " for reading!";
			return EVENT_STOP;
		}

		Serialize::TypeBase *type = nullptr;
		Serialize::Object *obj = nullptr;
		for (Anope::string buf; std::getline(fd, buf.str());)
		{
			if (buf.find("OBJECT ") == 0)
			{
				Anope::string t = buf.substr(7);
				if (obj)
					Log(LOG_DEBUG) << "obj != null but got OBJECT";
				if (type)
					Log(LOG_DEBUG) << "type != null but got OBJECT";
				type = Serialize::TypeBase::Find(t);
				obj = nullptr;
			}
			else if (buf.find("ID ") == 0)
			{
				if (!type || obj)
					continue;

				try
				{
					Serialize::ID id = convertTo<Serialize::ID>(buf.substr(3));
					obj = type->Require(id);
				}
				catch (const ConvertException &)
				{
					Log(LOG_DEBUG) << "Unable to parse object id " << buf.substr(3);
				}
			}
			else if (buf.find("DATA ") == 0)
			{
				if (!type)
					continue;

				if (!obj)
					obj = type->Create();

				size_t sp = buf.find(' ', 5); // Skip DATA
				if (sp == Anope::string::npos)
					continue;

				Anope::string key = buf.substr(5, sp - 5), value = buf.substr(sp + 1);

				Serialize::FieldBase *field = type->GetField(key);
				if (field)
					field->UnserializeFromString(obj, value);
			}
			else if (buf.find("END") == 0)
			{
				type = nullptr;
				obj = nullptr;
			}
		}

		fd.close();

		loaded = true;
		return EVENT_STOP;
	}
Exemple #24
0
Entry::Entry(const Anope::string &m, const Anope::string &fh) : name(m), mask(fh), cidr_len(0)
{
	Anope::string n, u, h;

	size_t at = fh.find('@');
	if (at != Anope::string::npos)
	{
		this->host = fh.substr(at + 1);

		const Anope::string &nu = fh.substr(0, at);

		size_t ex = nu.find('!');
		if (ex != Anope::string::npos)
		{
			this->user = nu.substr(ex + 1);
			this->nick = nu.substr(0, ex);
		}
		else
			this->user = nu;
	}
	else
	{
		if (fh.find('.') != Anope::string::npos || fh.find(':') != Anope::string::npos)
			this->host = fh;
		else
			this->nick = fh;
	}
	
	at = this->host.find('#');
	if (at != Anope::string::npos)
	{
		this->real = this->host.substr(at + 1);
		this->host = this->host.substr(0, at);
	}
	
	/* If the mask is all *'s it will match anything, so just clear it */
	if (this->nick.find_first_not_of("*") == Anope::string::npos)
		this->nick.clear();
	
	if (this->user.find_first_not_of("*") == Anope::string::npos)
		this->user.clear();
	
	if (this->host.find_first_not_of("*") == Anope::string::npos)
		this->host.clear();
	else
	{
		/* Host might be a CIDR range */
		size_t sl = this->host.find_last_of('/');
		if (sl != Anope::string::npos)
		{
			const Anope::string &cidr_ip = this->host.substr(0, sl),
						&cidr_range = this->host.substr(sl + 1);

			sockaddrs addr(cidr_ip);

			try
			{
				if (addr.valid() && cidr_range.is_pos_number_only())
				{
					this->cidr_len = convertTo<unsigned short>(cidr_range);
					/* If we got here, cidr_len is a valid number.
					 * If cidr_len is >32 (or 128) it is handled later in
					 * cidr::match
					 */

					this->host = cidr_ip;

					Log(LOG_DEBUG) << "Ban " << m << " has cidr " << this->cidr_len;
				}
			}
			catch (const ConvertException &) { }
		}
	}

	if (this->real.find_first_not_of("*") == Anope::string::npos)
		this->real.clear();
}
Exemple #25
0
sockaddrs::sockaddrs(const Anope::string &address)
{
	this->clear();
	if (!address.empty() && address.find_first_not_of_ci("0123456789abcdef.:") == Anope::string::npos)
		this->pton(address.find(':') != Anope::string::npos ? AF_INET6 : AF_INET, address);
}