mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 18:15:11 +00:00
542 lines
14 KiB
C
542 lines
14 KiB
C
#include <XMPP.h>
|
|
|
|
#include <Cytoplasm/Memory.h>
|
|
#include <Cytoplasm/Str.h>
|
|
#include <Cytoplasm/Log.h>
|
|
|
|
#include <Parsee.h>
|
|
#include <XML.h>
|
|
|
|
void
|
|
XMPPSendPlain(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *edit)
|
|
{
|
|
char *ident = StrRandom(32);
|
|
XMPPSendPlainID(comp, fr, to, msg, type, rst, rse, event_id, oob, edit, ident);
|
|
Free(ident);
|
|
}
|
|
|
|
void
|
|
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
|
{
|
|
XMLElement *message;
|
|
char *ident = StrRandom(32), *from;
|
|
if (!comp || !fr || !to || !type || !redact)
|
|
{
|
|
Free(ident);
|
|
return;
|
|
}
|
|
|
|
message = XMLCreateTag("message");
|
|
XMLAddAttr(message, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
XMLAddAttr(message, "to", to);
|
|
XMLAddAttr(message, "type", type);
|
|
XMLAddAttr(message, "id", ident);
|
|
|
|
{
|
|
XMLElement *apply_to, *retract, *body, *txt, *fallback;
|
|
|
|
apply_to = XMLCreateTag("apply-to");
|
|
XMLAddAttr(apply_to, "xmlns", "urn:xmpp:fasten:0");
|
|
XMLAddAttr(apply_to, "id", redact);
|
|
{
|
|
retract = XMLCreateTag("retract");
|
|
XMLAddAttr(retract, "xmlns", "urn:xmpp:message-retract:0");
|
|
XMLAddChild(apply_to, retract);
|
|
}
|
|
XMLAddChild(message, apply_to);
|
|
|
|
retract = XMLCreateTag("retract");
|
|
XMLAddAttr(retract, "xmlns", "urn:xmpp:message-retract:1");
|
|
XMLAddAttr(retract, "id", redact);
|
|
XMLAddChild(message, retract);
|
|
|
|
fallback = XMLCreateTag("fallback");
|
|
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
|
XMLAddAttr(fallback, "for", "urn:xmpp:message-retract:0");
|
|
XMLAddChild(message, fallback);
|
|
|
|
fallback = XMLCreateTag("fallback");
|
|
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
|
XMLAddAttr(fallback, "for", "urn:xmpp:message-retract:1");
|
|
XMLAddChild(message, fallback);
|
|
|
|
body = XMLCreateTag("body");
|
|
txt = XMLCreateText(
|
|
"This person attempted to retract a previous message, "
|
|
"but your client does not yet support the 0.4.1 version of "
|
|
"XEP-0424.\n\n"
|
|
|
|
"(_Parsee is jealous of being able to read such messages._)"
|
|
);
|
|
XMLAddChild(body, txt);
|
|
XMLAddChild(message, body);
|
|
|
|
}
|
|
|
|
pthread_mutex_lock(&comp->write_lock);
|
|
XMLEncode(comp->stream, message);
|
|
StreamFlush(comp->stream);
|
|
XMLFreeElement(message);
|
|
|
|
pthread_mutex_unlock(&comp->write_lock);
|
|
|
|
Free(from);
|
|
Free(ident);
|
|
}
|
|
void
|
|
XMPPSendPlainID(XMPPComponent *comp, char *fr, char *to, char *msg, char *type, char *rst, char *rse, char *event_id, char *oob, char *edit, char *ident)
|
|
{
|
|
XMLElement *message, *body, *data, *parsee;
|
|
char *from;
|
|
if (!comp || !fr || !to || !msg || !ident)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&comp->write_lock);
|
|
|
|
message = XMLCreateTag("message");
|
|
XMLAddAttr(message, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
XMLAddAttr(message, "to", to);
|
|
XMLAddAttr(message, "type", type);
|
|
XMLAddAttr(message, "id", ident);
|
|
|
|
body = XMLCreateTag("body");
|
|
data = XMLCreateText(oob ? oob : msg);
|
|
|
|
/* TODO: Add Parsee specific fields here */
|
|
parsee = XMLCreateTag("x-parsee");
|
|
{
|
|
XMLElement *parsee_version, *ver_elem;
|
|
XMLElement *parsee_link, *link_elem;
|
|
XMLElement *parsee_text, *text_elem;
|
|
XMLElement *parsee_event, *event_elem;
|
|
|
|
parsee_version = XMLCreateTag("version");
|
|
ver_elem = XMLCreateText(VERSION);
|
|
XMLAddChild(parsee_version, ver_elem);
|
|
XMLAddChild(parsee, parsee_version);
|
|
|
|
parsee_link = XMLCreateTag("repository");
|
|
link_elem = XMLCreateText(REPOSITORY);
|
|
XMLAddChild(parsee_link, link_elem);
|
|
XMLAddChild(parsee, parsee_link);
|
|
|
|
parsee_text = XMLCreateTag("zayds-note");
|
|
text_elem = XMLCreateText("\"LDA HANG YOURSELF\" - Zayd");
|
|
XMLAddChild(parsee_text, text_elem);
|
|
XMLAddChild(parsee, parsee_text);
|
|
|
|
parsee_text = XMLCreateTag("mcnebs-note");
|
|
text_elem = XMLCreateText("LDA will never beat the allegations");
|
|
XMLAddChild(parsee_text, text_elem);
|
|
XMLAddChild(parsee, parsee_text);
|
|
|
|
if (event_id)
|
|
{
|
|
parsee_event = XMLCreateTag("event-id");
|
|
event_elem = XMLCreateText(event_id);
|
|
XMLAddChild(parsee_event, event_elem);
|
|
XMLAddChild(parsee, parsee_event);
|
|
}
|
|
/* TODO: Add custom fields depending on the caller's wishes */
|
|
}
|
|
|
|
if (edit)
|
|
{
|
|
XMLElement *xedit = XMLCreateTag("replace");
|
|
XMLAddAttr(xedit, "xmlns", "urn:xmpp:message-correct:0");
|
|
XMLAddAttr(xedit, "id", edit);
|
|
XMLAddChild(message, xedit);
|
|
}
|
|
|
|
{
|
|
XMLElement *request = XMLCreateTag("request");
|
|
XMLAddAttr(request, "xmlns", "urn:xmpp:receipts");
|
|
XMLAddChild(message, request);
|
|
}
|
|
{
|
|
XMLElement *markable = XMLCreateTag("markable");
|
|
XMLAddAttr(markable, "xmlns", "urn:xmpp:chat-markers:0");
|
|
XMLAddChild(message, markable);
|
|
}
|
|
|
|
if (oob)
|
|
{
|
|
XMLElement *xoob, *oob_data, *oob_url;
|
|
|
|
xoob = XMLCreateTag("x");
|
|
XMLAddAttr(xoob, "xmlns", "jabber:x:oob");
|
|
oob_url = XMLCreateTag("url");
|
|
oob_data = XMLCreateText(oob);
|
|
XMLAddChild(oob_url, oob_data);
|
|
XMLAddChild(xoob, oob_url);
|
|
XMLAddChild(message, xoob);
|
|
}
|
|
if (rst && rse)
|
|
{
|
|
int off = ParseeFindDatastart(msg);
|
|
char *ostr = StrInt(off);
|
|
XMLElement *reply = XMLCreateTag("reply");
|
|
XMLElement *fallback = XMLCreateTag("fallback");
|
|
XMLElement *fall_body = XMLCreateTag("body");
|
|
|
|
XMLAddAttr(reply, "to", rse);
|
|
XMLAddAttr(reply, "id", rst);
|
|
XMLAddAttr(reply, "xmlns", "urn:xmpp:reply:0");
|
|
|
|
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
|
XMLAddAttr(fallback, "for", "urn:xmpp:reply:0");
|
|
|
|
XMLAddAttr(fall_body, "start", "0");
|
|
XMLAddAttr(fall_body, "end", ostr);
|
|
XMLAddChild(fallback, fall_body);
|
|
|
|
XMLAddChild(message, reply);
|
|
XMLAddChild(message, fallback);
|
|
|
|
Free(ostr);
|
|
}
|
|
|
|
XMLAddChild(message, body);
|
|
XMLAddChild(message, parsee);
|
|
XMLAddChild(body, data);
|
|
|
|
XMLEncode(comp->stream, message);
|
|
StreamFlush(comp->stream);
|
|
XMLFreeElement(message);
|
|
Free(from);
|
|
|
|
pthread_mutex_unlock(&comp->write_lock);
|
|
}
|
|
void
|
|
XMPPSendMUC(XMPPComponent *comp, char *fr, char *as, char *to, char *msg, char *type)
|
|
{
|
|
XMLElement *message, *body, *data;
|
|
char *from, *id;
|
|
if (!comp || !fr || !to || !as || !msg)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&comp->write_lock);
|
|
|
|
message = XMLCreateTag("message");
|
|
XMLAddAttr(message, "from",
|
|
(from = StrConcat(5, fr, "@", comp->host, "/", as)));
|
|
XMLAddAttr(message, "to", to);
|
|
XMLAddAttr(message, "type", type);
|
|
XMLAddAttr(message, "id", (id = StrRandom(8)));
|
|
|
|
body = XMLCreateTag("body");
|
|
data = XMLCreateText(msg);
|
|
|
|
XMLAddChild(message, body);
|
|
XMLAddChild(body, data);
|
|
|
|
XMLEncode(comp->stream, message);
|
|
StreamFlush(comp->stream);
|
|
|
|
XMLFreeElement(message);
|
|
Free(from);
|
|
Free(id);
|
|
|
|
pthread_mutex_unlock(&comp->write_lock);
|
|
}
|
|
void
|
|
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
|
{
|
|
XMLElement *presence;
|
|
char *from, *id;
|
|
if (!comp || !fr || !muc)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pthread_mutex_lock(&comp->write_lock);
|
|
|
|
presence = XMLCreateTag("presence");
|
|
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
XMLAddAttr(presence, "to", muc);
|
|
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
|
XMLAddAttr(presence, "type", "unavailable");
|
|
|
|
if (reason)
|
|
{
|
|
XMLElement *status = XMLCreateTag("status");
|
|
XMLElement *string = XMLCreateText(reason);
|
|
|
|
XMLAddChild(status, string);
|
|
XMLAddChild(presence, status);
|
|
}
|
|
|
|
XMPPAnnotatePresence(presence);
|
|
|
|
XMLEncode(comp->stream, presence);
|
|
StreamFlush(comp->stream);
|
|
|
|
XMLFreeElement(presence);
|
|
Free(from);
|
|
Free(id);
|
|
|
|
pthread_mutex_unlock(&comp->write_lock);
|
|
}
|
|
|
|
bool
|
|
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
|
|
{
|
|
XMLElement *presence, *x, *reply;
|
|
char *from, *id;
|
|
if (!comp || !fr || !muc)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
pthread_mutex_lock(&comp->write_lock);
|
|
|
|
presence = XMLCreateTag("presence");
|
|
x = XMLCreateTag("x");
|
|
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
XMLAddAttr(presence, "to", muc);
|
|
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
|
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
|
|
|
XMLAddChild(presence, x);
|
|
XMPPAnnotatePresence(presence);
|
|
|
|
XMLEncode(comp->stream, presence);
|
|
StreamFlush(comp->stream);
|
|
|
|
XMLFreeElement(presence);
|
|
Free(from);
|
|
|
|
pthread_mutex_unlock(&comp->write_lock);
|
|
|
|
/*if ((reply = ParseeAwaitStanza(id, 500)))
|
|
{
|
|
bool exit_code = true;
|
|
|
|
if (XMPPHasError(reply, "conflict"))
|
|
{
|
|
Log(LOG_WARNING, "UNIMPLEMENTED NAMING CONFLICT.");
|
|
exit_code = false;
|
|
}
|
|
|
|
XMLFreeElement(reply);
|
|
Free(id);
|
|
return exit_code;
|
|
}*/
|
|
(void) reply;
|
|
Free(id);
|
|
return true;
|
|
}
|
|
bool
|
|
XMPPIsParseeStanza(XMLElement *stanza)
|
|
{
|
|
if (!stanza)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return !!XMLookForUnique(stanza, "x-parsee");
|
|
}
|
|
|
|
|
|
char *
|
|
XMPPGetStanzaID(XMLElement *stanza)
|
|
{
|
|
XMLElement *stanza_id;
|
|
if (!stanza)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
stanza_id = XMLookForTKV(stanza, "stanza-id", "xmlns", "urn:xmpp:sid:0");
|
|
return stanza_id ? HashMapGet(stanza_id->attrs, "id") : NULL;
|
|
}
|
|
char *
|
|
XMPPGetOriginID(XMLElement *stanza)
|
|
{
|
|
XMLElement *origin_id;
|
|
if (!stanza)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
origin_id = XMLookForTKV(stanza, "origin-id", "xmlns", "urn:xmpp:sid:0");
|
|
return origin_id ? HashMapGet(origin_id->attrs, "id") : NULL;
|
|
}
|
|
char *
|
|
XMPPGetReplacedID(XMLElement *stanza)
|
|
{
|
|
XMLElement *replace = XMLookForTKV(
|
|
stanza, "replace",
|
|
"xmlns", "urn:xmpp:message-correct:0"
|
|
);
|
|
return replace ? HashMapGet(replace->attrs, "id") : NULL;
|
|
}
|
|
char *
|
|
XMPPGetRetractedID(XMLElement *stanza)
|
|
{
|
|
XMLElement *retract = XMLookForTKV(
|
|
stanza, "retract",
|
|
"xmlns", "urn:xmpp:message-retract:1"
|
|
);
|
|
char *id = retract ? HashMapGet(retract->attrs, "id") : NULL;
|
|
if (!id)
|
|
{
|
|
/* Pretend its fastened */
|
|
XMLElement *fasten = XMLookForTKV(
|
|
stanza, "apply-to",
|
|
"xmlns", "urn:xmpp:fasten:0"
|
|
);
|
|
retract = XMLookForTKV(
|
|
fasten, "retract",
|
|
"xmlns", "urn:xmpp:message-retract:0"
|
|
);
|
|
|
|
if (retract)
|
|
{
|
|
id = HashMapGet(fasten->attrs, "id");
|
|
}
|
|
}
|
|
|
|
return id;
|
|
}
|
|
char *
|
|
XMPPGetReply(XMLElement *elem)
|
|
{
|
|
XMLElement *rep = XMLookForTKV(elem, "reply", "xmlns", "urn:xmpp:reply:0");
|
|
if (!rep)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return HashMapGet(rep->attrs, "id");
|
|
}
|
|
void
|
|
XMPPAnnotatePresence(XMLElement *presence)
|
|
{
|
|
XMLElement *c;
|
|
char *ver;
|
|
if (!presence)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ver = XMPPGenerateVer();
|
|
c = XMLCreateTag("c");
|
|
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
|
XMLAddAttr(c, "hash", "sha-1");
|
|
XMLAddAttr(c, "node", REPOSITORY);
|
|
XMLAddAttr(c, "ver", ver);
|
|
|
|
Free(ver);
|
|
XMLAddChild(presence, c);
|
|
}
|
|
|
|
char *
|
|
XMPPGetModeration(XMLElement *stanza)
|
|
{
|
|
#define MOD_BASE "urn:xmpp:message-moderate"
|
|
XMLElement *moderate;
|
|
XMLElement *fasten;
|
|
if (!stanza)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* 0.3.0 version of XEP-0425: Bare look at the moderate element */
|
|
moderate = XMLookForTKV(stanza, "moderate", "xmlns", MOD_BASE ":1");
|
|
if (moderate)
|
|
{
|
|
return HashMapGet(moderate->attrs, "id");
|
|
}
|
|
|
|
/* Earlier versions, which still depend on fastening. */
|
|
fasten = XMLookForTKV(
|
|
stanza, "apply-to",
|
|
"xmlns", "urn:xmpp:fasten:0"
|
|
);
|
|
moderate = XMLookForTKV(
|
|
fasten, "moderated",
|
|
"xmlns", MOD_BASE ":0"
|
|
);
|
|
if (moderate)
|
|
{
|
|
return HashMapGet(fasten->attrs, "id");
|
|
}
|
|
return NULL;
|
|
#undef MOD_BASE
|
|
}
|
|
|
|
bool
|
|
XMPPHasError(XMLElement *stanza, char *type)
|
|
{
|
|
XMLElement *err;
|
|
if (!stanza || !type)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
err = XMLookForUnique(stanza, "error");
|
|
if (!err)
|
|
{
|
|
return false;
|
|
}
|
|
return XMLookForUnique(err, type);
|
|
}
|
|
|
|
XMLElement *
|
|
XMPPSendDisco(XMPPComponent *jabber, char *from, char *to)
|
|
{
|
|
XMLElement *ret, *iq;
|
|
char *identifier;
|
|
|
|
if (!jabber || !from || !to)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
iq = XMLCreateTag("iq");
|
|
XMLAddAttr(iq, "type", "get");
|
|
XMLAddAttr(iq, "from", from);
|
|
XMLAddAttr(iq, "to", to);
|
|
XMLAddAttr(iq, "id", (identifier = StrRandom(69)));
|
|
{
|
|
XMLElement *query = XMLCreateTag("query");
|
|
XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info");
|
|
XMLAddChild(iq, query);
|
|
}
|
|
|
|
pthread_mutex_lock(&jabber->write_lock);
|
|
XMLEncode(jabber->stream, iq);
|
|
StreamFlush(jabber->stream);
|
|
pthread_mutex_unlock(&jabber->write_lock);
|
|
XMLFreeElement(iq);
|
|
|
|
ret = ParseeAwaitStanza(identifier, 250);
|
|
Free(identifier);
|
|
return ret;
|
|
}
|
|
bool
|
|
XMPPDiscoAdvertises(XMLElement *disco, char *var)
|
|
{
|
|
XMLElement *query;
|
|
if (!disco || !var)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
query = XMLookForTKV(
|
|
disco,
|
|
"query", "xmlns",
|
|
"http://jabber.org/protocol/disco#info"
|
|
);
|
|
if (!query)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return !!XMLookForTKV(query, "feature", "var", var);
|
|
}
|