mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 16:55:10 +00:00
[ADD/WIP] Baisic VCards, worst XEP-0393 parser
Blocks coming soon. Took me a day to get a system I remotely liked, and now I feel ashamed of that a bit. It's truly over...
This commit is contained in:
parent
c349b37f60
commit
4007232716
6 changed files with 409 additions and 12 deletions
|
|
@ -23,14 +23,11 @@ For future XEPs:
|
||||||
federation.
|
federation.
|
||||||
As such, if _any_ client devs hear this, please consider adding these,
|
As such, if _any_ client devs hear this, please consider adding these,
|
||||||
(especially if you're a smElement employee!)
|
(especially if you're a smElement employee!)
|
||||||
|
|
||||||
- https://xmpp.org/extensions/xep-0080.html
|
- https://xmpp.org/extensions/xep-0080.html
|
||||||
Doxxing people over two protocols is great!
|
Doxxing people over two protocols is great!
|
||||||
|
|
||||||
- https://xmpp.org/extensions/xep-0449.html
|
- https://xmpp.org/extensions/xep-0449.html
|
||||||
Stickers are great. Matrix and XMPP somewhat has support for them, so
|
Stickers are great. Matrix and XMPP somewhat has support for them, so
|
||||||
might be a nice-to-have, and also to push over XMPP support.
|
might be a nice-to-have, and also to push over XMPP support.
|
||||||
|
|
||||||
- https://xmpp.org/extensions/xep-0050.html
|
- https://xmpp.org/extensions/xep-0050.html
|
||||||
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
||||||
also a nice to have.
|
also a nice to have.
|
||||||
|
|
@ -39,10 +36,9 @@ For future XEPs:
|
||||||
|
|
||||||
ON STANDBY BECAUSE THESE HAVE BEEN TERRIBLE TO DEAL WITH AND WHO KEEPS WRITING
|
ON STANDBY BECAUSE THESE HAVE BEEN TERRIBLE TO DEAL WITH AND WHO KEEPS WRITING
|
||||||
THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT:
|
THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT:
|
||||||
(x) https://xmpp.org/extensions/xep-0084.html
|
x https://xmpp.org/extensions/xep-0084.html
|
||||||
Avatar support would be extremely useful, if just a QoL improvment.
|
Avatar support would be extremely useful, if just a QoL improvment.
|
||||||
Matrix and XMPP both have support for these.
|
Matrix and XMPP both have support for these.
|
||||||
|
|
||||||
XEP-0084 is a pain in the ass to implement and seems generally just
|
XEP-0084 is a pain in the ass to implement and seems generally just
|
||||||
unreliable, however.
|
unreliable, however.
|
||||||
|
|
||||||
|
|
|
||||||
14
src/Events.c
14
src/Events.c
|
|
@ -3,6 +3,8 @@
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <XEP393.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
HashMap *
|
HashMap *
|
||||||
|
|
@ -25,6 +27,7 @@ HashMap *
|
||||||
MatrixCreateMessage(char *body)
|
MatrixCreateMessage(char *body)
|
||||||
{
|
{
|
||||||
HashMap *map;
|
HashMap *map;
|
||||||
|
char *text = NULL;
|
||||||
if (!body)
|
if (!body)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -33,6 +36,17 @@ MatrixCreateMessage(char *body)
|
||||||
map = HashMapCreate();
|
map = HashMapCreate();
|
||||||
HashMapSet(map, "msgtype", JsonValueString("m.text"));
|
HashMapSet(map, "msgtype", JsonValueString("m.text"));
|
||||||
HashMapSet(map, "body", JsonValueString(body));
|
HashMapSet(map, "body", JsonValueString(body));
|
||||||
|
{
|
||||||
|
/* TODO */
|
||||||
|
XEP393Element *e = XEP393(body);
|
||||||
|
text = XEP393ToXMLString(e);
|
||||||
|
XEP393FreeElement(e);
|
||||||
|
|
||||||
|
|
||||||
|
HashMapSet(map, "formatted_body", JsonValueString(text));
|
||||||
|
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
||||||
|
Free(text);
|
||||||
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <StringStream.h>
|
#include <StringStream.h>
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
|
#include <XEP393.h>
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
#include <Glob.h>
|
#include <Glob.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
|
||||||
291
src/XEP-0393.c
Normal file
291
src/XEP-0393.c
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
#include <XEP393.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Array.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
static XEP393Element *
|
||||||
|
CreateElementVessel(XEP393Element *parent, XEP393Type type)
|
||||||
|
{
|
||||||
|
XEP393Element *ret = Malloc(sizeof(*ret));
|
||||||
|
ret->parent = parent;
|
||||||
|
ret->type = type;
|
||||||
|
ret->children = ArrayCreate();
|
||||||
|
ret->text_data = NULL;
|
||||||
|
|
||||||
|
if (parent)
|
||||||
|
{
|
||||||
|
ArrayAdd(parent->children, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
XEP393FreeElementBase(XEP393Element *element, bool unlink)
|
||||||
|
{
|
||||||
|
size_t i, len;
|
||||||
|
if (!element)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = ArraySize(element->children);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
XEP393FreeElement(ArrayGet(element->children, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (element->parent && unlink)
|
||||||
|
{
|
||||||
|
XEP393Element *parent = element->parent;
|
||||||
|
len = ArraySize(parent->children);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
XEP393Element *c = ArrayGet(parent->children, i);
|
||||||
|
if (c == element)
|
||||||
|
{
|
||||||
|
ArrayDelete(parent->children, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayFree(element->children);
|
||||||
|
Free(element->text_data);
|
||||||
|
Free(element);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
XEP393FreeElement(XEP393Element *element)
|
||||||
|
{
|
||||||
|
XEP393FreeElementBase(element, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct StrView {
|
||||||
|
char *start;
|
||||||
|
char *end;
|
||||||
|
|
||||||
|
bool heap_free;
|
||||||
|
} StrView;
|
||||||
|
#define ViewLength(v) ((size_t) ((v.end) - (v.start)))
|
||||||
|
static char *
|
||||||
|
StringifyView(StrView v)
|
||||||
|
{
|
||||||
|
char *r;
|
||||||
|
size_t len;
|
||||||
|
if (!v.start || v.start > v.end)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = ViewLength(v);
|
||||||
|
r = Malloc(len + 1);
|
||||||
|
memcpy(r, v.start, len);
|
||||||
|
r[len] = '\0';
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
static StrView
|
||||||
|
CreateStaticView(char *str)
|
||||||
|
{
|
||||||
|
StrView view = {
|
||||||
|
.start = str,
|
||||||
|
.end = str + strlen(str),
|
||||||
|
.heap_free = false
|
||||||
|
};
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
static bool
|
||||||
|
IdentifySpan(char span_tag, StrView in, StrView *view)
|
||||||
|
{
|
||||||
|
size_t length;
|
||||||
|
bool found = false;
|
||||||
|
char prev = '\0';
|
||||||
|
if (in.start >= in.end)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (ViewLength(in) < 2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*in.start != span_tag || isspace(*(in.start + 1)))
|
||||||
|
{
|
||||||
|
/* The opening styling directive MUST NOT be followed
|
||||||
|
* by a whitespace character */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
view->start = in.start + 1;
|
||||||
|
in.start += 1;
|
||||||
|
|
||||||
|
for (length = 0; ViewLength(in) > 0; length++, in.start++)
|
||||||
|
{
|
||||||
|
if (*in.start == span_tag)
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = *in.start;
|
||||||
|
}
|
||||||
|
if (!found || !length || (prev && isspace(prev)))
|
||||||
|
{
|
||||||
|
/* the closing styling directive MUST NOT be preceeded
|
||||||
|
* by a whitespace character. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
view->end = in.start;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
XEP393Decode(StrView view, XEP393Element *root)
|
||||||
|
{
|
||||||
|
StrView subview = view;
|
||||||
|
StrView textview = view;
|
||||||
|
XEP393Element *text, *span;
|
||||||
|
size_t i;
|
||||||
|
bool managed = false;
|
||||||
|
|
||||||
|
textview.end = subview.start;
|
||||||
|
for (i = 0; subview.start < subview.end; subview.start++)
|
||||||
|
{
|
||||||
|
StrView span_view;
|
||||||
|
managed = false;
|
||||||
|
#define Spanify(xep_symbol) \
|
||||||
|
managed = true; \
|
||||||
|
textview.end = subview.start; \
|
||||||
|
text = CreateElementVessel( \
|
||||||
|
root, XEP393_TEXT \
|
||||||
|
); \
|
||||||
|
text->text_data = StringifyView(textview); \
|
||||||
|
\
|
||||||
|
/* Found a span. */ \
|
||||||
|
span = CreateElementVessel( \
|
||||||
|
root, xep_symbol \
|
||||||
|
); \
|
||||||
|
\
|
||||||
|
XEP393Decode(span_view, span); \
|
||||||
|
\
|
||||||
|
/* Update subview */ \
|
||||||
|
subview.start = span_view.end + 1; \
|
||||||
|
\
|
||||||
|
/* Update textview */ \
|
||||||
|
textview.start = subview.start; \
|
||||||
|
textview.end = subview.start
|
||||||
|
if (IdentifySpan('_', subview, &span_view))
|
||||||
|
{
|
||||||
|
Spanify(XEP393_ITALIC);
|
||||||
|
}
|
||||||
|
else if (IdentifySpan('*', subview, &span_view))
|
||||||
|
{
|
||||||
|
Spanify(XEP393_EMPH);
|
||||||
|
}
|
||||||
|
else if (IdentifySpan('`', subview, &span_view))
|
||||||
|
{
|
||||||
|
Spanify(XEP393_MONO);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Text character: update end */
|
||||||
|
textview.end = subview.start;
|
||||||
|
}
|
||||||
|
(void) i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!managed)
|
||||||
|
{
|
||||||
|
textview.end = subview.start;
|
||||||
|
text = CreateElementVessel(
|
||||||
|
root, XEP393_TEXT
|
||||||
|
);
|
||||||
|
text->text_data = StringifyView(textview);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XEP393Element *
|
||||||
|
XEP393(char *message)
|
||||||
|
{
|
||||||
|
StrView view = CreateStaticView(message);
|
||||||
|
XEP393Element *root = CreateElementVessel(NULL, XEP393_ROOT);
|
||||||
|
|
||||||
|
/* TODO: Parse blocks first, *then* spans. Considering the
|
||||||
|
* current architecture, this shouldn't be too hard to integrate,
|
||||||
|
* given how string views already manage boundaries, and elements
|
||||||
|
* can already be used to contain blocks I think. */
|
||||||
|
XEP393Decode(view, root);
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <XML.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
||||||
|
{
|
||||||
|
XMLElement *head = xmlparent;
|
||||||
|
size_t i;
|
||||||
|
if (!element || !xmlparent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (element->type)
|
||||||
|
{
|
||||||
|
case XEP393_ITALIC:
|
||||||
|
head = XMLCreateTag("i");
|
||||||
|
XMLAddChild(xmlparent, head);
|
||||||
|
break;
|
||||||
|
case XEP393_EMPH:
|
||||||
|
head = XMLCreateTag("strong");
|
||||||
|
XMLAddChild(xmlparent, head);
|
||||||
|
break;
|
||||||
|
case XEP393_MONO:
|
||||||
|
head = XMLCreateTag("code");
|
||||||
|
XMLAddChild(xmlparent, head);
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
if (ArraySize(element->children) == 0 && element->text_data)
|
||||||
|
{
|
||||||
|
XMLElement *text = XMLCreateText(element->text_data);
|
||||||
|
XMLAddChild(head, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ArraySize(element->children); i++)
|
||||||
|
{
|
||||||
|
XEP393Element *sub = ArrayGet(element->children, i);
|
||||||
|
ShoveXML(sub, head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <StringStream.h>
|
||||||
|
char *
|
||||||
|
XEP393ToXMLString(XEP393Element *xepd)
|
||||||
|
{
|
||||||
|
XMLElement *root;
|
||||||
|
|
||||||
|
Stream *writer;
|
||||||
|
char *ret = NULL;
|
||||||
|
size_t i;
|
||||||
|
if (!xepd)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = XMLCreateTag("ROOT");
|
||||||
|
ShoveXML(xepd, root);
|
||||||
|
|
||||||
|
writer = StrStreamWriter(&ret);
|
||||||
|
for (i = 0; i < ArraySize(root->children); i++)
|
||||||
|
{
|
||||||
|
XMLEncode(writer, ArrayGet(root->children, i));
|
||||||
|
}
|
||||||
|
XMLFreeElement(root);
|
||||||
|
StreamFlush(writer);
|
||||||
|
StreamClose(writer);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
@ -17,8 +17,6 @@
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
/* TODO: Rewrite this avatar code.
|
|
||||||
* XEP-0084 sucks. */
|
|
||||||
#define IQ_ADVERT \
|
#define IQ_ADVERT \
|
||||||
AdvertiseSimple("http://jabber.org/protocol/chatstates") \
|
AdvertiseSimple("http://jabber.org/protocol/chatstates") \
|
||||||
AdvertiseSimple("http://jabber.org/protocol/caps") \
|
AdvertiseSimple("http://jabber.org/protocol/caps") \
|
||||||
|
|
@ -42,7 +40,6 @@
|
||||||
AdvertiseSimple("urn:parsee:x-parsee:0") \
|
AdvertiseSimple("urn:parsee:x-parsee:0") \
|
||||||
AdvertiseSimple("urn:parsee:jealousy:0")
|
AdvertiseSimple("urn:parsee:jealousy:0")
|
||||||
|
|
||||||
/* TODO: More identities */
|
|
||||||
#define IQ_IDENTITY \
|
#define IQ_IDENTITY \
|
||||||
IdentitySimple("gateway", "matrix", "Parsee Matrix Gateway") \
|
IdentitySimple("gateway", "matrix", "Parsee Matrix Gateway") \
|
||||||
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
|
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
|
||||||
|
|
@ -286,7 +283,11 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
|
||||||
}
|
}
|
||||||
struct XMPPThread;
|
struct XMPPThread;
|
||||||
typedef struct XMPPThreadInfo {
|
typedef struct XMPPThreadInfo {
|
||||||
/* A FIFO of stanzas */
|
/* A FIFO of stanzas inbound, to be read by dispatcher
|
||||||
|
* threads.
|
||||||
|
*
|
||||||
|
* TODO: Using it's length in !stats can be useful
|
||||||
|
* for having a "congestion" metric for Parsee admins... */
|
||||||
Array *stanzas;
|
Array *stanzas;
|
||||||
pthread_mutex_t lock;
|
pthread_mutex_t lock;
|
||||||
|
|
||||||
|
|
@ -1022,6 +1023,16 @@ IQResult(ParseeData *args, XMLElement *stanza)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static XMLElement *
|
||||||
|
CreateTagWithText(const char *tn, char *text)
|
||||||
|
{
|
||||||
|
XMLElement *tag = XMLCreateTag((char *) tn);
|
||||||
|
XMLElement *tex = XMLCreateText(text);
|
||||||
|
|
||||||
|
XMLAddChild(tag, tex);
|
||||||
|
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
static void
|
static void
|
||||||
IQGet(ParseeData *args, XMLElement *stanza)
|
IQGet(ParseeData *args, XMLElement *stanza)
|
||||||
{
|
{
|
||||||
|
|
@ -1033,6 +1044,44 @@ IQGet(ParseeData *args, XMLElement *stanza)
|
||||||
if (XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"))
|
if (XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"))
|
||||||
{
|
{
|
||||||
Log(LOG_INFO, "vCard information GET for %s", to);
|
Log(LOG_INFO, "vCard information GET for %s", to);
|
||||||
|
|
||||||
|
/* TODO: "a compliant server MUST respond on behalf of the
|
||||||
|
* requestor and not forward the IQ to the requestee's
|
||||||
|
* connected resource". */
|
||||||
|
if (!strncmp(to, "parsee@", 7))
|
||||||
|
{
|
||||||
|
XMLElement *iqVCard = XMLCreateTag("iq");
|
||||||
|
XMLAddAttr(iqVCard, "from", to);
|
||||||
|
XMLAddAttr(iqVCard, "to", from);
|
||||||
|
XMLAddAttr(iqVCard, "id", id);
|
||||||
|
XMLAddAttr(iqVCard, "type", "result");
|
||||||
|
{
|
||||||
|
XMLElement *vCard = XMLCreateTag("vCard");
|
||||||
|
XMLAddAttr(vCard, "xmlns", "vcard-temp");
|
||||||
|
{
|
||||||
|
XMLElement *fn = CreateTagWithText(
|
||||||
|
"FN", "Parsee Mizuhashi"
|
||||||
|
);
|
||||||
|
XMLElement *nick = CreateTagWithText(
|
||||||
|
"NICKNAME", "Parsee"
|
||||||
|
);
|
||||||
|
XMLElement *url = CreateTagWithText(
|
||||||
|
"URL", REPOSITORY
|
||||||
|
);
|
||||||
|
/* TODO: Maybe abstract the vCard code. */
|
||||||
|
XMLAddChild(vCard, nick);
|
||||||
|
XMLAddChild(vCard, url);
|
||||||
|
XMLAddChild(vCard, fn);
|
||||||
|
}
|
||||||
|
XMLAddChild(iqVCard, vCard);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&jabber->write_lock);
|
||||||
|
XMLEncode(jabber->stream, iqVCard);
|
||||||
|
StreamFlush(jabber->stream);
|
||||||
|
pthread_mutex_unlock(&jabber->write_lock);
|
||||||
|
XMLFreeElement(iqVCard);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (XMLookForTKV(stanza, "query", "xmlns", DISCO))
|
else if (XMLookForTKV(stanza, "query", "xmlns", DISCO))
|
||||||
{
|
{
|
||||||
|
|
@ -1228,7 +1277,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
||||||
JsonValue *val = JsonValueInteger(power_level);
|
JsonValue *val = JsonValueInteger(power_level);
|
||||||
JsonValueFree(JsonSet(users, val, 1, matrix_user_pl));
|
JsonValueFree(JsonSet(users, val, 1, matrix_user_pl));
|
||||||
ASSetPL(args->config, room, powers);
|
ASSetPL(args->config, room, powers);
|
||||||
JsonValueFree(val);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1283,8 +1331,11 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
||||||
status_str = status_data->data;
|
status_str = status_data->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Verify whenever this code works as soon as I can get
|
/* TODO: "The server will automatically set a user's presence to
|
||||||
* my own instance (kappach.at) with presence enabled. */
|
* unavailable if their last active time was over a threshold value
|
||||||
|
* (e.g. 5 minutes)."
|
||||||
|
* We _will_ need to manage those cases properly(cronjob?) if we want
|
||||||
|
* XMPP presences to sync properly */
|
||||||
ASSetStatus(
|
ASSetStatus(
|
||||||
args->config, from_matrix,
|
args->config, from_matrix,
|
||||||
GuessStatus(stanza), status_str
|
GuessStatus(stanza), status_str
|
||||||
|
|
@ -1547,6 +1598,7 @@ ParseeAwaitStanza(char *identifier, int64_t timeout)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Convert into an absolute timeout */
|
||||||
if (timeout > 0)
|
if (timeout > 0)
|
||||||
{
|
{
|
||||||
int64_t seconds = timeout / (1 SECONDS);
|
int64_t seconds = timeout / (1 SECONDS);
|
||||||
|
|
|
||||||
43
src/include/XEP393.h
Normal file
43
src/include/XEP393.h
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
#ifndef PARSEE_XEP393_H
|
||||||
|
#define PARSEE_XEP393_H
|
||||||
|
|
||||||
|
#include <Cytoplasm/Array.h>
|
||||||
|
|
||||||
|
typedef enum XEP393Type {
|
||||||
|
XEP393_ROOT,
|
||||||
|
XEP393_ITALIC,
|
||||||
|
XEP393_EMPH,
|
||||||
|
XEP393_MONO,
|
||||||
|
XEP393_TEXT
|
||||||
|
} XEP393Type;
|
||||||
|
typedef struct XEP393Element {
|
||||||
|
struct XEP393Element *parent;
|
||||||
|
|
||||||
|
XEP393Type type;
|
||||||
|
|
||||||
|
char *text_data;
|
||||||
|
Array *children;
|
||||||
|
} XEP393Element;
|
||||||
|
|
||||||
|
/** Tries to decode a XEP-393 message into a formatted structure.
|
||||||
|
* --------
|
||||||
|
* Returns: A root XEP393Element[LA:HEAP] | NULL
|
||||||
|
* Modifies: NOTHING
|
||||||
|
* See-Also: https://xmpp.org/extensions/xep-0393.html, XEP393FreeElement */
|
||||||
|
extern XEP393Element * XEP393(char *message);
|
||||||
|
|
||||||
|
/** Frees an XEP-0393 {element}, alongside all it's children
|
||||||
|
* --------
|
||||||
|
* Returns: NOTHING
|
||||||
|
* Modifies: {element}
|
||||||
|
* See-Also: https://xmpp.org/extensions/xep-0393.html, XEP393 */
|
||||||
|
extern void XEP393FreeElement(XEP393Element *element);
|
||||||
|
|
||||||
|
/** Converts a XEP-0393 element to a XMLd string
|
||||||
|
* --------
|
||||||
|
* Returns: A string representing a XML'ified version of {xepd}[LA:HEAP]
|
||||||
|
* Modifies: NOTHING
|
||||||
|
* See-Also: https://xmpp.org/extensions/xep-0393.html, XEP393FreeElement, XEP393 */
|
||||||
|
extern char * XEP393ToXMLString(XEP393Element *xepd);
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Add a link
Reference in a new issue