コード例 #1
0
void bahamut::SJoin::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
	Anope::string modes;
	if (params.size() >= 4)
		for (unsigned i = 2; i < params.size(); ++i)
			modes += " " + params[i];
	if (!modes.empty())
		modes.erase(modes.begin());

	std::list<rfc1459::Join::SJoinUser> users;

	/* For some reason, bahamut will send a SJOIN from the user joining a channel
	 * if the channel already existed
	 */
	if (source.GetUser())
	{
		rfc1459::Join::SJoinUser sju;
		sju.second = source.GetUser();
		users.push_back(sju);
	}
	else
	{
		spacesepstream sep(params[params.size() - 1]);
		Anope::string buf;

		while (sep.GetToken(buf))
		{
			rfc1459::Join::SJoinUser sju;

			/* Get prefixes from the nick */
			for (char ch; !buf.empty() && (ch = ModeManager::GetStatusChar(buf[0]));)
			{
				buf.erase(buf.begin());
				sju.first.AddMode(ch);
			}

			sju.second = User::Find(buf);
			if (!sju.second)
			{
				Log(LOG_DEBUG) << "SJOIN for non-existent user " << buf << " on " << params[1];
				continue;
			}

			users.push_back(sju);
		}
	}

	time_t ts = Anope::CurTime;

	try
	{
		ts = convertTo<time_t>(params[0]);
	}
	catch (const ConvertException &) { }

	rfc1459::Join::SJoin(source, params[1], ts, modes, users);
}
コード例 #2
0
ファイル: modes.cpp プロジェクト: RanadeepPolavarapu/IRCd
/** Build a list of mode strings to send to the IRCd from the mode stacker
 * @param info The stacker info for a channel or user
 * @return a list of strings
 */
static std::list<Anope::string> BuildModeStrings(StackerInfo *info)
{
	std::list<Anope::string> ret;
	std::list<std::pair<Mode *, Anope::string> >::iterator it, it_end;
	Anope::string buf = "+", parambuf;
	unsigned NModes = 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
		{
			ret.push_back(buf + parambuf);
			buf = "+";
			parambuf.clear();
			NModes = 1;
		}

		buf += it->first->mchar;

		if (!it->second.empty())
			parambuf += " " + it->second;
	}

	if (buf[buf.length() - 1] == '+')
		buf.erase(buf.length() - 1);

	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
		{
			ret.push_back(buf + parambuf);
			buf = "-";
			parambuf.clear();
			NModes = 1;
		}

		buf += it->first->mchar;

		if (!it->second.empty())
			parambuf += " " + it->second;
	}

	if (buf[buf.length() - 1] == '-')
		buf.erase(buf.length() - 1);

	if (!buf.empty())
		ret.push_back(buf + parambuf);

	return ret;
}
コード例 #3
0
ファイル: xmlrpc.cpp プロジェクト: gerrit-rws/anope
	static bool GetData(Anope::string &content, Anope::string &tag, Anope::string &data)
	{
		if (content.empty())
			return false;

		Anope::string prev, cur;
		bool istag;

		do
		{
			prev = cur;
			cur.clear();

			int len = 0;
			istag = false;

			if (content[0] == '<')
			{
				len = content.find_first_of('>');
				istag = true;
			}
			else if (content[0] != '>')
			{
				len = content.find_first_of('<');
			}

			if (len)
			{
				if (istag)
				{
					cur = content.substr(1, len - 1);
					content.erase(0, len + 1);
					while (!content.empty() && content[0] == ' ')
						content.erase(content.begin());
				}
				else
				{
					cur = content.substr(0,len);
					content.erase(0, len);
				}
			}
		}
		while (istag && !content.empty());

		tag = Unescape(prev);
		data = Unescape(cur);
		return !istag && !data.empty();
	}
コード例 #4
0
ファイル: ngircd.cpp プロジェクト: bonnedav/anope
	/*
	 * RFC 2813, 4.2.2: Njoin Message:
	 * The NJOIN message is used between servers only.
	 * It is used when two servers connect to each other to exchange
	 * the list of channel members for each channel.
	 *
	 * Even though the same function can be performed by using a succession
	 * of JOIN, this message SHOULD be used instead as it is more efficient.
	 *
	 * Received: :dev.anope.de NJOIN #test :DukeP2,@DukeP,%test,+test2
	 */
	void Run(MessageSource &source, const std::vector<Anope::string> &params) override
	{
		std::list<Message::Join::SJoinUser> users;

		commasepstream sep(params[1]);
		Anope::string buf;
		while (sep.GetToken(buf))
		{

			Message::Join::SJoinUser sju;

			/* Get prefixes from the nick */
			for (char ch; (ch = ModeManager::GetStatusChar(buf[0]));)
			{
				buf.erase(buf.begin());
				sju.first.AddMode(ch);
			}

			sju.second = User::Find(buf);
			if (!sju.second)
			{
				Log(LOG_DEBUG) << "NJOIN for non-existent user " << buf << " on " << params[0];
				continue;
			}
			users.push_back(sju);
		}

		Message::Join::SJoin(source, params[0], 0, "", users);
	}
コード例 #5
0
ファイル: process.cpp プロジェクト: bonnedav/anope
void IRCDProto::Parse(const Anope::string &buffer, Anope::string &source, Anope::string &command, std::vector<Anope::string> &params)
{
	spacesepstream sep(buffer);

	if (buffer[0] == ':')
	{
		sep.GetToken(source);
		source.erase(0, 1);
	}

	sep.GetToken(command);
	
	for (Anope::string token; sep.GetToken(token);)
	{
		if (token[0] == ':')
		{
			if (!sep.StreamEnd())
				params.push_back(token.substr(1) + " " + sep.GetRemaining());
			else
				params.push_back(token.substr(1));
			break;
		}
		else
			params.push_back(token);
	}
}
コード例 #6
0
	void DoStatsUplink(CommandSource &source)
	{
		Anope::string buf;
		for (std::set<Anope::string>::iterator it = Servers::Capab.begin(); it != Servers::Capab.end(); ++it)
			buf += " " + *it;
		if (!buf.empty())
			buf.erase(buf.begin());

		source.Reply(_("Uplink server: \002{0}\002"), Me->GetLinks().front()->GetName());
		source.Reply(_("Uplink capab: {0}"), buf);
		source.Reply(_("Servers found: {0}"), stats_count_servers(Me->GetLinks().front()));
	}
コード例 #7
0
ファイル: init.cpp プロジェクト: SaberUK/anope
/** 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));
	}
}
コード例 #8
0
ファイル: xmlrpc_main.cpp プロジェクト: SaberUK/anope
    void DoChannel(XMLRPCServiceInterface *iface, HTTPClient *client, XMLRPCRequest &request)
    {
        if (request.data.empty())
            return;

        Channel *c = Channel::Find(request.data[0]);

        request.reply("name", iface->Sanitize(c ? c->name : request.data[0]));

        if (c)
        {
            request.reply("bancount", stringify(c->HasMode("BAN")));
            int count = 0;
            std::vector<Anope::string> v = c->GetModeList("BAN");
            for (unsigned int i = 0; i < v.size(); ++i)
                request.reply("ban" + stringify(++count), iface->Sanitize(v[i]));

            request.reply("exceptcount", stringify(c->HasMode("EXCEPT")));
            count = 0;
            v = c->GetModeList("EXCEPT");
            for (unsigned int i = 0; i < v.size(); ++i)
                request.reply("except" + stringify(++count), iface->Sanitize(v[i]));

            request.reply("invitecount", stringify(c->HasMode("INVITEOVERRIDE")));
            count = 0;
            v = c->GetModeList("INVITEOVERRIDE");
            for (unsigned int i = 0; i < v.size(); ++i)
                request.reply("invite" + stringify(++count), iface->Sanitize(v[i]));

            Anope::string users;
            for (Channel::ChanUserList::const_iterator it = c->users.begin(); it != c->users.end(); ++it)
            {
                ChanUserContainer *uc = it->second;
                users += uc->status.BuildModePrefixList() + uc->user->nick + " ";
            }
            if (!users.empty())
            {
                users.erase(users.length() - 1);
                request.reply("users", iface->Sanitize(users));
            }

            if (!c->topic.empty())
                request.reply("topic", iface->Sanitize(c->topic));

            if (!c->topic_setter.empty())
                request.reply("topicsetter", iface->Sanitize(c->topic_setter));

            request.reply("topictime", stringify(c->topic_time));
            request.reply("topicts", stringify(c->topic_ts));
        }
    }
コード例 #9
0
ファイル: hybrid.cpp プロジェクト: SaberUK/anope
void hybrid::SJoin::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
	Anope::string modes;
	if (params.size() >= 3)
		for (unsigned i = 2; i < params.size() - 1; ++i)
			modes += " " + params[i];
	if (!modes.empty())
		modes.erase(modes.begin());

	std::list<Message::Join::SJoinUser> users;

	spacesepstream sep(params[params.size() - 1]);
	Anope::string buf;

	while (sep.GetToken(buf))
	{
		Message::Join::SJoinUser sju;

		/* Get prefixes from the nick */
		for (char ch; (ch = ModeManager::GetStatusChar(buf[0]));)
		{
			buf.erase(buf.begin());
			sju.first.AddMode(ch);
		}

		sju.second = User::Find(buf);
		if (!sju.second)
		{
			Log(LOG_DEBUG) << "SJOIN for non-existent user " << buf << " on " << params[1];
			continue;
		}

		users.push_back(sju);
	}

	time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : Anope::CurTime;
	Message::Join::SJoin(source, params[1], ts, modes, users);
}
コード例 #10
0
ファイル: m_xmlrpc_main.cpp プロジェクト: xxgrunge/anope196
	void DoChannel(XMLRPCServiceInterface *iface, XMLRPCClientSocket *source, XMLRPCRequest *request)
	{
		if (request->data.empty())
			return;

		Channel *c = findchan(request->data[0]);

		request->reply("name", iface->Sanitize(c ? c->name : request->data[0]));

		if (c)
		{
			request->reply("bancount", stringify(c->HasMode(CMODE_BAN)));
			int count = 0;
			std::pair<Channel::ModeList::iterator, Channel::ModeList::iterator> its = c->GetModeList(CMODE_BAN);
			for (; its.first != its.second; ++its.first)
				request->reply("ban" + stringify(++count), iface->Sanitize(its.first->second));

			request->reply("exceptcount", stringify(c->HasMode(CMODE_EXCEPT)));
			count = 0;
			its = c->GetModeList(CMODE_EXCEPT);
			for (; its.first != its.second; ++its.first)
				request->reply("except" + stringify(++count), iface->Sanitize(its.first->second));

			request->reply("invitecount", stringify(c->HasMode(CMODE_INVITEOVERRIDE)));
			count = 0;
			its = c->GetModeList(CMODE_INVITEOVERRIDE);
			for (; its.first != its.second; ++its.first)
				request->reply("invite" + stringify(++count), iface->Sanitize(its.first->second));

			Anope::string users;
			for (CUserList::const_iterator it = c->users.begin(); it != c->users.end(); ++it)
			{
				UserContainer *uc = *it;
				users += uc->Status->BuildModePrefixList() + uc->user->nick + " ";
			}
			if (!users.empty())
			{
				users.erase(users.length() - 1);
				request->reply("users", iface->Sanitize(users));
			}

			if (!c->topic.empty())
				request->reply("topic", iface->Sanitize(c->topic));

			if (!c->topic_setter.empty())
				request->reply("topicsetter", iface->Sanitize(c->topic_setter));

			request->reply("topictime", stringify(c->topic_time));
		}
	}
コード例 #11
0
ファイル: m_xmlrpc_main.cpp プロジェクト: xxgrunge/anope196
	void DoStats(XMLRPCServiceInterface *iface, XMLRPCClientSocket *source, XMLRPCRequest *request)
	{
		request->reply("uptime", stringify(Anope::CurTime - start_time));
		request->reply("uplinkname", Me->GetLinks().front()->GetName());
		{
			Anope::string buf;
			for (std::set<Anope::string>::iterator it = Capab.begin(); it != Capab.end(); ++it)
				buf += " " + *it;
			if (!buf.empty())
				buf.erase(buf.begin());
			request->reply("uplinkcapab", buf);
		}
		request->reply("usercount", stringify(usercnt));
		request->reply("maxusercount", stringify(maxusercnt));
		request->reply("channelcount", stringify(ChannelList.size()));
	}
コード例 #12
0
ファイル: xmlrpc_main.cpp プロジェクト: SaberUK/anope
    void DoStats(XMLRPCServiceInterface *iface, HTTPClient *client, XMLRPCRequest &request)
    {
        Stats *stats = Serialize::GetObject<Stats *>();

        request.reply("uptime", stringify(Anope::CurTime - Anope::StartTime));
        request.reply("uplinkname", Me->GetLinks().front()->GetName());
        {
            Anope::string buf;
            for (std::set<Anope::string>::iterator it = Servers::Capab.begin(); it != Servers::Capab.end(); ++it)
                buf += " " + *it;
            if (!buf.empty())
                buf.erase(buf.begin());
            request.reply("uplinkcapab", buf);
        }
        request.reply("usercount", stringify(UserListByNick.size()));
        request.reply("maxusercount", stringify(stats ? stats->GetMaxUserCount() : 0));
        request.reply("channelcount", stringify(ChannelList.size()));
    }
コード例 #13
0
ファイル: m_xmlrpc_main.cpp プロジェクト: xxgrunge/anope196
	void DoUser(XMLRPCServiceInterface *iface, XMLRPCClientSocket *source, XMLRPCRequest *request)
	{
		if (request->data.empty())
			return;

		User *u = finduser(request->data[0]);

		request->reply("nick", iface->Sanitize(u ? u->nick : request->data[0]));

		if (u)
		{
			request->reply("ident", iface->Sanitize(u->GetIdent()));
			request->reply("vident", iface->Sanitize(u->GetVIdent()));
			request->reply("host", iface->Sanitize(u->host));
			if (!u->vhost.empty())
				request->reply("vhost", iface->Sanitize(u->vhost));
			if (!u->chost.empty())
				request->reply("chost", iface->Sanitize(u->chost));
			if (u->ip())
				request->reply("ip", u->ip.addr());
			request->reply("timestamp", stringify(u->timestamp));
			request->reply("signon", stringify(u->my_signon));
			if (u->Account())
			{
				request->reply("account", iface->Sanitize(u->Account()->display));
				if (u->Account()->o)
					request->reply("opertype", iface->Sanitize(u->Account()->o->ot->GetName()));
			}

			Anope::string channels;
			for (UChannelList::const_iterator it = u->chans.begin(); it != u->chans.end(); ++it)
			{
				ChannelContainer *cc = *it;
				channels += cc->Status->BuildModePrefixList() + cc->chan->name + " ";
			}
			if (!channels.empty())
			{
				channels.erase(channels.length() - 1);
				request->reply("channels", channels);
			}
		}
	}
コード例 #14
0
ファイル: xmlrpc_main.cpp プロジェクト: SaberUK/anope
    void DoUser(XMLRPCServiceInterface *iface, HTTPClient *client, XMLRPCRequest &request)
    {
        if (request.data.empty())
            return;

        User *u = User::Find(request.data[0]);

        request.reply("nick", iface->Sanitize(u ? u->nick : request.data[0]));

        if (u)
        {
            request.reply("ident", iface->Sanitize(u->GetIdent()));
            request.reply("vident", iface->Sanitize(u->GetVIdent()));
            request.reply("host", iface->Sanitize(u->host));
            if (!u->vhost.empty())
                request.reply("vhost", iface->Sanitize(u->vhost));
            if (!u->chost.empty())
                request.reply("chost", iface->Sanitize(u->chost));
            request.reply("ip", u->ip.addr());
            request.reply("timestamp", stringify(u->timestamp));
            request.reply("signon", stringify(u->signon));
            if (u->Account())
            {
                request.reply("account", iface->Sanitize(u->Account()->GetDisplay()));
                if (u->Account()->o)
                    request.reply("opertype", iface->Sanitize(u->Account()->o->GetType()->GetName()));
            }

            Anope::string channels;
            for (User::ChanUserList::const_iterator it = u->chans.begin(); it != u->chans.end(); ++it)
            {
                ChanUserContainer *cc = it->second;
                channels += cc->status.BuildModePrefixList() + cc->chan->name + " ";
            }
            if (!channels.empty())
            {
                channels.erase(channels.length() - 1);
                request.reply("channels", channels);
            }
        }
    }
コード例 #15
0
ファイル: bahamut.cpp プロジェクト: xxgrunge/anope196
	bool OnSJoin(const Anope::string &source, const std::vector<Anope::string> &params)
	{
		Channel *c = findchan(params[1]);
		time_t ts = Anope::string(params[0]).is_pos_number_only() ? convertTo<time_t>(params[0]) : 0;
		bool keep_their_modes = true;

		if (!c)
		{
			c = new Channel(params[1], ts);
			c->SetFlag(CH_SYNCING);
		}
		/* Our creation time is newer than what the server gave us */
		else if (c->creation_time > ts)
		{
			c->creation_time = ts;
			c->Reset();
		}
		/* Their TS is newer than ours, our modes > theirs, unset their modes if need be */
		else if (ts > c->creation_time)
			keep_their_modes = false;

		/* If we need to keep their modes, and this SJOIN string contains modes */
		if (keep_their_modes && params.size() >= 4)
		{
			/* Set the modes internally */
			Anope::string modes;
			for (unsigned i = 2; i < params.size(); ++i)
				modes += " " + params[i];
			if (!modes.empty())
				modes.erase(modes.begin());
			c->SetModesInternal(NULL, modes);
		}

		/* For some reason, bahamut will send a SJOIN from the user joining a channel
		 * if the channel already existed
		 */
		if (!c->HasFlag(CH_SYNCING) && params.size() == 2)
		{
			User *u = finduser(source);
			if (!u)
				Log(LOG_DEBUG) << "SJOIN for nonexistant user " << source << " on " << c->name;
			else
			{
				EventReturn MOD_RESULT;
				FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c));

				/* Add the user to the channel */
				c->JoinUser(u);

				/* Now set whatever modes this user is allowed to have on the channel */
				chan_set_correct_modes(u, c, 1);

				/* Check to see if modules want the user to join, if they do
				 * check to see if they are allowed to join (CheckKick will kick/ban them)
				 * Don't trigger OnJoinChannel event then as the user will be destroyed
				 */
				if (MOD_RESULT == EVENT_STOP && (!c->ci || !c->ci->CheckKick(u)))
				{
					FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c));
				}
			}
		}
		else
		{
			spacesepstream sep(params[params.size() - 1]);
			Anope::string buf;
			while (sep.GetToken(buf))
			{
				std::list<ChannelMode *> Status;
				char ch;

				/* Get prefixes from the nick */
				while ((ch = ModeManager::GetStatusChar(buf[0])))
				{
					buf.erase(buf.begin());
					ChannelMode *cm = ModeManager::FindChannelModeByChar(ch);
					if (!cm)
					{
						Log() << "Received unknown mode prefix " << cm << " in SJOIN string";
						continue;
					}

					if (keep_their_modes)
						Status.push_back(cm);
				}

				User *u = finduser(buf);
				if (!u)
				{
					Log(LOG_DEBUG) << "SJOIN for nonexistant user " << buf << " on " << c->name;
					continue;
				}

				EventReturn MOD_RESULT;
				FOREACH_RESULT(I_OnPreJoinChannel, OnPreJoinChannel(u, c));

				/* Add the user to the channel */
				c->JoinUser(u);

				/* Update their status internally on the channel
				 * This will enforce secureops etc on the user
				 */
				for (std::list<ChannelMode *>::iterator it = Status.begin(), it_end = Status.end(); it != it_end; ++it)
					c->SetModeInternal(*it, buf);

				/* Now set whatever modes this user is allowed to have on the channel */
				chan_set_correct_modes(u, c, 1);

				/* Check to see if modules want the user to join, if they do
				 * check to see if they are allowed to join (CheckKick will kick/ban them)
				 * Don't trigger OnJoinChannel event then as the user will be destroyed
				 */
				if (MOD_RESULT != EVENT_STOP && c->ci && c->ci->CheckKick(u))
					continue;

				FOREACH_MOD(I_OnJoinChannel, OnJoinChannel(u, c));
			}
		}

		/* Channel is done syncing */
		if (c->HasFlag(CH_SYNCING))
		{
			/* Unset the syncing flag */
			c->UnsetFlag(CH_SYNCING);
			c->Sync();
		}

		return true;
	}
コード例 #16
0
ファイル: messages.cpp プロジェクト: SaberUK/anope
void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> &params)
{
	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;
}
コード例 #17
0
ファイル: protocol.cpp プロジェクト: xxgrunge/anope196
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;
}