1
0
mirror of https://github.com/anope/anope.git synced 2026-06-12 19:14:47 +02:00

Rework how CTCP messages are sent and received.

This commit is contained in:
Sadie Powell
2024-06-24 14:29:55 +01:00
parent 6e5713d64a
commit 693eeed762
8 changed files with 90 additions and 69 deletions
+14
View File
@@ -601,6 +601,20 @@ namespace Anope
/** Expands a module path. */
inline auto ExpandModule(const Anope::string &path) { return Expand(ModuleDir, path); }
/** Formats a CTCP message for sending to a client.
* @param name The name of the CTCP.
* @param body If present then the body of the CTCP.
* @return A formatted CTCP ready to send to a client.
*/
extern CoreExport Anope::string FormatCTCP(const Anope::string &name, const Anope::string &body = "");
/** Parses a CTCP message received from a client.
* @param text The raw message to parse.
* @param name The location to store the name of the CTCP.
* @param body The location to store body of the CTCP if one is present.
* @return True if the message was a well formed CTCP; otherwise, false.
*/
extern CoreExport bool ParseCTCP(const Anope::string &text, Anope::string &name, Anope::string &body);
}
/** sepstream allows for splitting token separated lists.
-4
View File
@@ -43,7 +43,6 @@ public:
virtual void SendNotice(const MessageSource &source, const Anope::string &dest, const Anope::string &msg, const Anope::map<Anope::string> &tags = {});
virtual void SendPrivmsg(const MessageSource &source, const Anope::string &dest, const Anope::string &msg, const Anope::map<Anope::string> &tags = {});
virtual void SendCTCPInternal(const MessageSource &, const Anope::string &dest, const Anope::string &buf);
/** Parses an incoming message from the IRC server.
* @param message The message to parse.
@@ -213,9 +212,6 @@ public:
virtual void SendKick(const MessageSource &source, const Channel *chan, User *user, const Anope::string &msg);
virtual void SendAction(const MessageSource &source, const Anope::string &dest, const char *fmt, ...) ATTR_FORMAT(4, 5);
virtual void SendCTCP(const MessageSource &source, const Anope::string &dest, const char *fmt, ...) ATTR_FORMAT(4, 5);
virtual void SendGlobalNotice(BotInfo *bi, const Server *dest, const Anope::string &msg) = 0;
virtual void SendGlobalPrivmsg(BotInfo *bi, const Server *desc, const Anope::string &msg) = 0;
+1 -5
View File
@@ -111,11 +111,7 @@ public:
return;
}
message = message.replace_all_cs("\1", "");
if (message.empty())
return;
IRCD->SendAction(*ci->bi, ci->name, "%s", message.c_str());
IRCD->SendPrivmsg(*ci->bi, ci->name, Anope::FormatCTCP("ACTION", message));
ci->bi->lastmsg = Anope::CurTime;
bool override = !source.AccessFor(ci).HasPriv("SAY");
+3 -5
View File
@@ -1215,11 +1215,9 @@ public:
/* If it's a /me, cut the CTCP part because the ACTION will cause
* problems with the caps or badwords kicker
*/
if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1')
{
realbuf.erase(0, 8);
realbuf.erase(realbuf.length() - 1);
}
Anope::string ctcpname, ctcpbody;
if (Anope::ParseCTCP(msg, ctcpname, ctcpbody) && ctcpname.equals_ci("ACTION"))
realbuf = ctcpbody;
if (realbuf.empty())
return;
+19 -20
View File
@@ -117,7 +117,7 @@ void IRC2SQL::OnUserConnect(User *u, bool &exempt)
this->RunQuery(query);
if (ctcpuser && (Me->IsSynced() || ctcpeob) && u->server != Me)
IRCD->SendPrivmsg(StatServ, u->GetUID(), "\1VERSION\1");
IRCD->SendPrivmsg(StatServ, u->GetUID(), Anope::FormatCTCP("VERSION"));
}
@@ -299,28 +299,27 @@ void IRC2SQL::OnTopicUpdated(User *source, Channel *c, const Anope::string &user
void IRC2SQL::OnBotNotice(User *u, BotInfo *bi, Anope::string &message, const Anope::map<Anope::string> &tags)
{
Anope::string versionstr;
if (bi != StatServ)
return;
if (message[0] == '\1' && message[message.length() - 1] == '\1')
{
if (message.substr(0, 9).equals_ci("\1VERSION "))
{
if (u->HasExt("CTCPVERSION"))
return;
u->Extend<bool>("CTCPVERSION");
versionstr = Anope::NormalizeBuffer(message.substr(9, message.length() - 10));
if (versionstr.empty())
return;
query = "UPDATE `" + prefix + "user` "
"SET version=@version@ "
"WHERE nick=@nick@";
query.SetValue("version", versionstr);
query.SetValue("nick", u->nick);
this->RunQuery(query);
}
}
Anope::string ctcpname, ctcpbody;
if (!Anope::ParseCTCP(message, ctcpname, ctcpbody) || ctcpname != "VERSION")
return;
if (u->HasExt("CTCPVERSION"))
return;
u->Extend<bool>("CTCPVERSION");
auto versionstr = Anope::NormalizeBuffer(message.substr(9, message.length() - 10));
if (versionstr.empty())
return;
query = "UPDATE `" + prefix + "user` "
"SET version=@version@ "
"WHERE nick=@nick@";
query.SetValue("version", versionstr);
query.SetValue("nick", u->nick);
this->RunQuery(query);
}
MODULE_INIT(IRC2SQL)
+6 -8
View File
@@ -342,19 +342,17 @@ void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> &param
if (bi)
{
if (message[0] == '\1' && message[message.length() - 1] == '\1')
Anope::string ctcpname, ctcpbody;
if (Anope::ParseCTCP(message, ctcpname, ctcpbody))
{
if (message.substr(0, 6).equals_ci("\1PING "))
if (ctcpname.equals_ci("PING"))
{
Anope::string buf = message;
buf.erase(buf.begin());
buf.erase(buf.end() - 1);
IRCD->SendCTCP(bi, u->nick, "%s", buf.c_str());
IRCD->SendNotice(bi, u->nick, Anope::FormatCTCP("PING", ctcpbody));
}
else if (message.substr(0, 9).equals_ci("\1VERSION\1"))
else if (ctcpname.equals_ci("VERSION"))
{
Module *enc = ModuleManager::FindFirstOf(ENCRYPTION);
IRCD->SendCTCP(bi, u->nick, "VERSION Anope-%s %s :%s - (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)", Anope::VersionBuildString().c_str());
IRCD->SendNotice(bi, u->nick, Anope::FormatCTCP("VERSION", Anope::printf("Anope-%s %s :%s - (%s) -- %s", Anope::Version().c_str(), Me->GetName().c_str(), IRCD->GetProtocolName().c_str(), enc ? enc->name.c_str() : "(none)", Anope::VersionBuildString().c_str())));
}
return;
}
+47
View File
@@ -835,3 +835,50 @@ Anope::string Anope::Expand(const Anope::string &base, const Anope::string &frag
return Anope::printf("%s%c%s", base.c_str(), separator, fragment.c_str());
}
Anope::string Anope::FormatCTCP(const Anope::string &name, const Anope::string &value)
{
if (value.empty())
return Anope::printf("\1%s\1", name.c_str());
return Anope::printf("\1%s %s\1", name.c_str(), value.c_str());
}
bool Anope::ParseCTCP(const Anope::string &text, Anope::string &name, Anope::string &body)
{
// According to draft-oakley-irc-ctcp-02 a valid CTCP must begin with SOH and
// contain at least one octet which is not NUL, SOH, CR, LF, or SPACE. As most
// of these are restricted at the protocol level we only need to check for SOH
// and SPACE.
if (text.length() < 2 || text[0] != '\x1' || text[1] == '\x1' || text[1] == ' ')
{
name.clear();
body.clear();
return false;
}
auto end_of_name = text.find(' ', 2);
auto end_of_ctcp = *text.rbegin() == '\x1' ? 1 : 0;
if (end_of_name == std::string::npos)
{
// The CTCP only contains a name.
name = text.substr(1, text.length() - 1 - end_of_ctcp);
body.clear();
return true;
}
// The CTCP contains a name and a body.
name = text.substr(1, end_of_name - 1);
auto start_of_body = text.find_first_not_of(' ', end_of_name + 1);
if (start_of_body == std::string::npos)
{
// The CTCP body is provided but empty.
body.clear();
return true;
}
// The CTCP body provided was non-empty.
body = text.substr(start_of_body, text.length() - start_of_body - end_of_ctcp);
return true;
}
-27
View File
@@ -166,12 +166,6 @@ void IRCDProto::SendGlobops(const MessageSource &source, const Anope::string &me
Uplink::Send(source, "GLOBOPS", message);
}
void IRCDProto::SendCTCPInternal(const MessageSource &source, const Anope::string &dest, const Anope::string &buf)
{
Anope::string s = Anope::NormalizeBuffer(buf);
this->SendNotice(source, dest, "\1" + s + "\1");
}
void IRCDProto::SendNumericInternal(int numeric, const Anope::string &dest, const std::vector<Anope::string> &params)
{
Anope::string n = Anope::ToString(numeric);
@@ -190,17 +184,6 @@ void IRCDProto::SendTopic(const MessageSource &source, Channel *c)
Uplink::Send(source, "TOPIC", c->name, c->topic);
}
void IRCDProto::SendAction(const MessageSource &source, const Anope::string &dest, const char *fmt, ...)
{
va_list args;
char buf[BUFSIZE] = "";
va_start(args, fmt);
vsnprintf(buf, BUFSIZE - 1, fmt, args);
va_end(args);
Anope::string actionbuf = Anope::string("\1ACTION ") + buf + '\1';
SendPrivmsg(source, dest, actionbuf);
}
void IRCDProto::SendPing(const Anope::string &servname, const Anope::string &who)
{
if (servname.empty())
@@ -243,16 +226,6 @@ void IRCDProto::SendForceNickChange(User *u, const Anope::string &newnick, time_
Uplink::Send("SVSNICK", u->GetUID(), newnick, when);
}
void IRCDProto::SendCTCP(const MessageSource &source, const Anope::string &dest, const char *fmt, ...)
{
va_list args;
char buf[BUFSIZE] = "";
va_start(args, fmt);
vsnprintf(buf, BUFSIZE - 1, fmt, args);
va_end(args);
SendCTCPInternal(source, dest, buf);
}
bool IRCDProto::IsNickValid(const Anope::string &nick)
{
/**