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:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
@@ -342,19 +342,17 @@ void Privmsg::Run(MessageSource &source, const std::vector<Anope::string> ¶m
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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> ¶ms)
|
||||
{
|
||||
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)
|
||||
{
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user