[ADD/WIP] Congestion, basic ad-hoc commands

Woah, took me a while, eh? Next up, getting forms!
This commit is contained in:
LDA 2024-07-13 16:26:33 +02:00
commit 408888ef67
9 changed files with 911 additions and 14 deletions

View file

@ -13,6 +13,7 @@ CommandHead(CmdStats, cmd, argp)
ParseeCmdArg *args = argp;
ParseeData *data = args->data;
HashMap *event = args->event;
size_t congestion = ParseeCongestion();
size_t alloc = MemoryAllocated();
size_t kb = alloc >> 10;
size_t mb = kb >> 10;
@ -31,6 +32,9 @@ CommandHead(CmdStats, cmd, argp)
ReplySprintf("- Memory used: %d%s (reported by Cytoplasm)",
(int) min, unit
);
ReplySprintf("- Unprocessed stanzas: %d",
(int) congestion
);
ReplySprintf("- Source code and licensing information: %s",
REPOSITORY
);

View file

@ -141,20 +141,27 @@ IdentifySpan(char span_tag, StrView in, StrView *view)
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;
char prev = '\0', curr = '\0';
textview.end = subview.start;
for (i = 0; subview.start < subview.end; subview.start++)
for (; subview.start < subview.end; subview.start++)
{
StrView span_view;
managed = false;
curr = *subview.start;
if (prev == '\0' || prev == '\n')
{
/* TODO: Start of line, start parsing blocks. */
}
#define Spanify(xep_symbol) \
managed = true; \
textview.end = subview.start; \
@ -193,7 +200,8 @@ XEP393Decode(StrView view, XEP393Element *root)
/* Text character: update end */
textview.end = subview.start;
}
(void) i;
prev = curr;
}
if (!managed)
@ -215,7 +223,10 @@ XEP393(char *message)
/* 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. */
* can already be used to contain blocks I think.
*
* Actually, nevermind, these would be pure pain. Nested blocks,
* unterminated ones, QUOTES. Just hell. I hate parsing this shit. */
XEP393Decode(view, root);
return root;
}

121
src/XMPPCommand/Commands.c Normal file
View file

@ -0,0 +1,121 @@
#include <XMPPCommand.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Array.h>
#include <Cytoplasm/Str.h>
struct XMPPCommand {
XMPPCmdCallback callback;
char *node, *name;
Array *options;
};
XMPPCommand *
XMPPBasicCmd(char *node, char *name, XMPPCmdCallback callback_funct)
{
XMPPCommand *cmd;
if (!node || !name || !callback_funct)
{
return NULL;
}
cmd = Malloc(sizeof(*cmd));
cmd->callback = callback_funct;
cmd->node = StrDuplicate(node);
cmd->name = StrDuplicate(name);
/* No options -> no form required */
cmd->options = NULL;
return cmd;
}
void
XMPPFreeCommand(XMPPCommand *cmd)
{
size_t i;
if (!cmd)
{
return;
}
for (i = 0; i < ArraySize(cmd->options); i++)
{
XMPPOption *opt = ArrayGet(cmd->options, i);
XMPPFreeOption(opt);
}
ArrayFree(cmd->options);
Free(cmd->node);
Free(cmd->name);
Free(cmd);
}
void
XMPPAddOption(XMPPCommand *cmd, XMPPOption *opt)
{
if (!cmd || !opt)
{
return;
}
if (!cmd->options)
{
cmd->options = ArrayCreate();
}
ArrayAdd(cmd->options, opt);
}
XMLElement *
XMPPFormifyCommand(XMPPCommand *cmd)
{
XMLElement *x, *field;
size_t i;
if (!cmd || !cmd->options)
{
return NULL;
}
x = XMLCreateTag("x");
XMLAddAttr(x, "xmlns", "jabber:x:data");
XMLAddAttr(x, "type", "form");
/* TODO: Other fields */
for (i = 0; i < ArraySize(cmd->options); i++)
{
XMPPOption *opt = ArrayGet(cmd->options, i);
field = XMPPOptionToXML(opt);
XMLAddChild(x, field);
}
return x;
}
char *
XMPPGetCommandNode(XMPPCommand *cmd)
{
return cmd ? cmd->node : NULL;
}
char *
XMPPGetCommandDesc(XMPPCommand *cmd)
{
return cmd ? cmd->name : NULL;
}
bool
XMPPCommandRequiresForm(XMPPCommand *cmd)
{
return cmd ? !!cmd->options : false;
}
void
XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, XMLElement *to, HashMap *arg_table)
{
if (!m || !cmd || !to)
{
return;
}
cmd->callback(m, arg_table, to);
/* TODO Free arg_table's strings */
HashMapFree(arg_table);
}

260
src/XMPPCommand/Manager.c Normal file
View file

@ -0,0 +1,260 @@
#include <XMPPCommand.h>
#include <Cytoplasm/HashMap.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Util.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <pthread.h>
typedef struct XMPPSession {
char *identifier;
/* "Each session [...] SHOULD be valid only between one
* requester/responder pair." */
/* The Parsee-managed entity managing the session. */
char *receiver;
/* The command's issuer entity. */
char *issuer;
/* The issued node */
char *node;
/* Timestamp of the session's creation, from Parsee's
* point-of-view */
uint64_t creation;
} XMPPSession;
struct XMPPCommandManager {
pthread_mutex_t lock;
/* A hashmap of XMPPCommands. */
HashMap *commands;
HashMap *sessions;
};
static void
XMPPDestroySession(XMPPSession *session)
{
if (!session)
{
return;
}
Free(session->identifier);
Free(session->receiver);
Free(session->issuer);
Free(session->node);
Free(session);
}
static void
InvalidateSession(XMPPCommandManager *m, char *session)
{
XMPPSession *s = HashMapDelete(m->sessions, session);
XMPPDestroySession(s);
}
static char *
NewSession(XMPPCommandManager *m, char *node)
{
char *session_id = NULL;
do
{
int period = (UtilTsMillis() / (10 SECONDS));
char *rand = StrRandom(16);
char *pstr = StrInt(period);
session_id = StrConcat(5, node, "_", pstr, "_", rand);
Free(rand);
Free(pstr);
}
while (HashMapGet(m->sessions, session_id));
return session_id;
}
/* NOTE: The manager MUST be locked. */
static char *
XMPPRegisterSession(XMPPCommandManager *m, char *p_jid, char *s_jid, char *node)
{
XMPPSession *session;
char *session_ident;
if (!m || !p_jid || !s_jid)
{
return NULL;
}
session = Malloc(sizeof(*session));
session->node = StrDuplicate(node);
session->issuer = StrDuplicate(s_jid);
session->receiver = StrDuplicate(p_jid);
session->creation = UtilTsMillis();
session_ident = NewSession(m, node);
session->identifier = session_ident;
HashMapSet(m->sessions, session_ident, session);
return session_ident;
}
XMPPCommandManager *
XMPPCreateManager(void)
{
XMPPCommandManager *ret = Malloc(sizeof(*ret));
pthread_mutex_init(&ret->lock, NULL);
ret->commands = HashMapCreate();
ret->sessions = HashMapCreate();
return ret;
}
void
XMPPRegisterCommand(XMPPCommandManager *m, XMPPCommand *cmd)
{
if (!m || !cmd)
{
return;
}
pthread_mutex_lock(&m->lock);
HashMapSet(m->commands, XMPPGetCommandNode(cmd), cmd);
pthread_mutex_unlock(&m->lock);
}
void
XMPPFreeManager(XMPPCommandManager *manager)
{
char *node_name, *session_id;
XMPPCommand *val;
XMPPSession *session;
if (!manager)
{
return;
}
while (HashMapIterate(manager->commands, &node_name, (void **) &val))
{
XMPPFreeCommand(val);
}
while (HashMapIterate(manager->sessions, &session_id, (void **) &session))
{
XMPPDestroySession(session);
}
pthread_mutex_destroy(&manager->lock);
HashMapFree(manager->commands);
HashMapFree(manager->sessions);
Free(manager);
}
void
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
{
char *node_name;
XMPPCommand *val;
XMLElement *item;
if (!m || !p || !jid)
{
return;
}
pthread_mutex_lock(&m->lock);
while (HashMapIterate(m->commands, &node_name, (void **) &val))
{
item = XMLCreateTag("item");
XMLAddAttr(item, "jid", jid);
XMLAddAttr(item, "node", node_name);
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
XMLAddChild(p, item);
}
pthread_mutex_unlock(&m->lock);
}
bool
XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
{
XMLElement *command;
XMPPComponent *jabber;
XMPPCommand *cmd;
char *from, *to, *id;
char *session_id = NULL;
bool good = false;
if (!m || !stanza || !data)
{
return false;
}
jabber = data->jabber;
from = HashMapGet(stanza->attrs, "from");
to = HashMapGet(stanza->attrs, "to");
id = HashMapGet(stanza->attrs, "id");
pthread_mutex_lock(&m->lock);
/* TODO */
#define CMD_NS "http://jabber.org/protocol/commands"
if ((command = XMLookForTKV(stanza, "command", "xmlns", CMD_NS)))
{
char *action = HashMapGet(command->attrs, "action");
char *node = HashMapGet(command->attrs, "node");
if (!action || StrEquals(action, "execute"))
{
XMLElement *command_xml, *iq;
/* This is an execution. */
cmd = HashMapGet(m->commands, node);
if (!cmd)
{
Log(LOG_WARNING,
"User %s asked to execute '%s', but it doesn't exist.",
from, node
);
goto end;
}
if (XMPPCommandRequiresForm(cmd))
{
Log(LOG_WARNING,
"User %s asked to execute '%s', but it requires a form, "
"which is currently UNIMPLEMENTED", from, node
);
goto end;
}
/* Doesn't need to be freed, as it lives with m. */
session_id = XMPPRegisterSession(m, to, from, node);
/* No forms, we good. */
iq = XMLCreateTag("iq");
XMLAddAttr(iq, "type", "result");
XMLAddAttr(iq, "from", to);
XMLAddAttr(iq, "to", from);
XMLAddAttr(iq, "id", id);
command_xml = XMLCreateTag("command");
XMLAddAttr(command_xml, "xmlns", CMD_NS);
XMLAddAttr(command_xml, "node", node);
XMLAddAttr(command_xml, "status", "completed");
XMLAddAttr(command_xml, "sessionid", session_id);
XMPPExecuteCommand(m, cmd, command_xml, NULL);
XMLAddChild(iq, command_xml);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq);
InvalidateSession(m, session_id);
good = true;
goto end;
}
}
#undef CMD_NS
end:
pthread_mutex_unlock(&m->lock);
return good;
}

251
src/XMPPCommand/Options.c Normal file
View file

@ -0,0 +1,251 @@
#include <XMPPCommand.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Str.h>
struct XMPPOption {
enum {
XMPP_OPTION_TEXT,
XMPP_OPTION_BOOL,
XMPP_OPTION_LIST,
XMPP_OPTION_FIXED
} type;
union {
/* Default text/value for string/fixed */
struct {
char *text;
} as_string;
/* Default choice for bool */
struct {
bool choice;
} as_bool;
struct {
Array *elements;
char *def;
bool single;
} as_arr;
} data;
char *id, *desc;
bool required;
};
void
XMPPFreeOption(XMPPOption *opt)
{
size_t i;
Array *elements;
if (!opt)
{
return;
}
switch (opt->type)
{
case XMPP_OPTION_TEXT:
case XMPP_OPTION_FIXED:
Free(opt->data.as_string.text);
break;
case XMPP_OPTION_LIST:
elements = opt->data.as_arr.elements;
for (i = 0; i < ArraySize(elements); i++)
{
char *element = ArrayGet(elements, i);
Free(element);
}
ArrayFree(elements);
Free(opt->data.as_arr.def);
break;
default:
break;
}
Free(opt->desc);
Free(opt->id);
Free(opt);
}
XMPPOption *
XMPPCreateText(bool req, char *id, char *def)
{
XMPPOption *opt;
if (!id)
{
return NULL;
}
opt = Malloc(sizeof(*opt));
opt->desc = NULL;
opt->type = XMPP_OPTION_TEXT;
opt->id = StrDuplicate(id);
opt->data.as_string.text = StrDuplicate(def);
opt->required = req;
return opt;
}
XMPPOption *
XMPPCreateBool(bool req, char *id, bool def)
{
XMPPOption *opt;
if (!id)
{
return NULL;
}
opt = Malloc(sizeof(*opt));
opt->desc = NULL;
opt->type = XMPP_OPTION_BOOL;
opt->id = StrDuplicate(id);
opt->data.as_bool.choice = def;
opt->required = req;
return opt;
}
XMPPOption *
XMPPCreateList(bool req, bool single, char *id, char *def)
{
XMPPOption *opt;
if (!id)
{
return NULL;
}
opt = Malloc(sizeof(*opt));
opt->desc = NULL;
opt->type = XMPP_OPTION_LIST;
opt->id = StrDuplicate(id);
opt->data.as_arr.def = StrDuplicate(def);
opt->data.as_arr.single = single;
opt->data.as_arr.elements = ArrayCreate();
opt->required = req;
ArrayAdd(opt->data.as_arr.elements, StrDuplicate(def));
return opt;
}
XMPPOption *
XMPPCreateFixed(char *id, char *text)
{
XMPPOption *opt;
if (!id)
{
return NULL;
}
opt = Malloc(sizeof(*opt));
opt->desc = NULL;
opt->type = XMPP_OPTION_TEXT;
opt->id = StrDuplicate(id);
opt->data.as_string.text = StrDuplicate(text);
opt->required = false;
return opt;
}
XMLElement *
XMPPOptionToXML(XMPPOption *opt)
{
XMLElement *elem, *req, *desc, *data;
XMLElement *value;
if (!opt)
{
return NULL;
}
elem = XMLCreateTag("field");
switch (opt->type)
{
case XMPP_OPTION_TEXT: XMLAddAttr(elem, "type", "text-single"); break;
case XMPP_OPTION_BOOL: XMLAddAttr(elem, "type", "boolean"); break;
/* TODO */
case XMPP_OPTION_LIST: XMLAddAttr(elem, "type", "list-single"); break;
case XMPP_OPTION_FIXED: XMLAddAttr(elem, "type", "fixed"); break;
}
XMLAddAttr(elem, "var", opt->id);
if (opt->required)
{
req = XMLCreateTag("required");
XMLAddChild(elem, req);
}
if (opt->desc)
{
desc = XMLCreateTag("desc");
data = XMLCreateText(opt->desc);
XMLAddChild(desc, data);
XMLAddChild(elem, desc);
}
if (opt->type == XMPP_OPTION_LIST)
{
size_t i;
Array *elements = opt->data.as_arr.elements;
XMLElement *option, *value;
for (i = 0; i < ArraySize(elements); i++)
{
char *element = ArrayGet(elements, i);
option = XMLCreateTag("option");
value = XMLCreateTag("value");
data = XMLCreateText(element);
XMLAddChild(value, data);
XMLAddChild(option, value);
XMLAddChild(elem, option);
}
}
switch (opt->type)
{
case XMPP_OPTION_TEXT:
value = XMLCreateTag("value");
data = XMLCreateText(opt->data.as_string.text);
XMLAddChild(value, data);
XMLAddChild(elem, value);
break;
case XMPP_OPTION_LIST:
value = XMLCreateTag("value");
data = XMLCreateText(opt->data.as_arr.def);
XMLAddChild(value, data);
XMLAddChild(elem, value);
break;
case XMPP_OPTION_BOOL:
value = XMLCreateTag("value");
data = XMLCreateText(opt->data.as_bool.choice ? "true" : "false");
XMLAddChild(value, data);
XMLAddChild(elem, value);
break;
default:
break;
}
/* TODO */
return elem;
}
void
XMPPAddListOption(XMPPOption *list, char *option)
{
Array *elements;
if (!list || !option || list->type != XMPP_OPTION_LIST)
{
return;
}
elements = list->data.as_arr.elements;
ArrayAdd(elements, StrDuplicate(option));
}
void
XMPPSetDescription(XMPPOption *opt, char *desc)
{
if (!opt || !desc)
{
return;
}
Free(opt->desc);
opt->desc = StrDuplicate(desc);
}

View file

@ -12,6 +12,7 @@
#include <Cytoplasm/Sha.h>
#include <StringStream.h>
#include <XMPPCommand.h>
#include <Matrix.h>
#include <XMPP.h>
#include <XML.h>
@ -19,6 +20,7 @@
#define IQ_ADVERT \
AdvertiseSimple("http://jabber.org/protocol/chatstates") \
AdvertiseSimple("http://jabber.org/protocol/commands") \
AdvertiseSimple("http://jabber.org/protocol/caps") \
AdvertiseSimple("urn:xmpp:avatar:metadata+notify") \
AdvertiseSimple("urn:xmpp:avatar:data+notify") \
@ -293,6 +295,7 @@ typedef struct XMPPThreadInfo {
ParseeData *args;
XMPPComponent *jabber;
XMPPCommandManager *m;
struct XMPPThread *dispatchers;
size_t available_dispatchers;
@ -864,13 +867,16 @@ TrimBase64(char *b64)
return ret;
}
static void
IQResult(ParseeData *args, XMLElement *stanza)
IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
XMLElement *vcard = XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp");
XMLElement *event = XMLookForTKV(stanza, "pubsub",
"xmlns", "http://jabber.org/protocol/pubsub"
);
(void) thr;
if (event)
{
size_t i;
@ -1033,15 +1039,60 @@ CreateTagWithText(const char *tn, char *text)
return tag;
}
static bool
IQIsCommandList(ParseeData *args, XMLElement *stanza)
{
char *parsee = NULL;
XMLElement *query = XMLookForTKV(
stanza, "query", "xmlns",
"http://jabber.org/protocol/disco#items"
);
bool ret = false;
if (!query ||
!StrEquals(HashMapGet(query->attrs, "node"),
"http://jabber.org/protocol/commands"))
{
return false;
}
parsee = ParseeJID(args);
ret = StrEquals(HashMapGet(stanza->attrs, "to"), parsee);
Free(parsee);
return ret;
}
static void
IQGet(ParseeData *args, XMLElement *stanza)
IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
XMPPComponent *jabber = args->jabber;
char *from = HashMapGet(stanza->attrs, "from");
char *to = HashMapGet(stanza->attrs, "to");
char *id = HashMapGet(stanza->attrs, "id");
if (XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"))
(void) thr;
if (IQIsCommandList(args, stanza))
{
XMLElement *iq_reply = XMLCreateTag("iq");
XMLAddAttr(iq_reply, "type", "result");
XMLAddAttr(iq_reply, "from", to);
XMLAddAttr(iq_reply, "to", from);
XMLAddAttr(iq_reply, "id", id);
{
XMLElement *q = XMLCreateTag("query");
XMLAddAttr(q, "xmlns", "http://jabber.org/protocol/disco#items");
XMLAddAttr(q, "node", "http://jabber.org/protocol/commands");
XMPPShoveCommandList(thr->info->m, to, q);
XMLAddChild(iq_reply, q);
}
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq_reply);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq_reply);
}
else if (XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"))
{
Log(LOG_INFO, "vCard information GET for %s", to);
@ -1127,13 +1178,22 @@ IQGet(ParseeData *args, XMLElement *stanza)
}
static void
IQError(ParseeData *args, XMLElement *stanza)
IQError(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
/* TODO */
}
static void
IQSet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
XMPPCommandManager *manager = thr->info->m;
if (!XMPPManageCommand(manager, stanza, args))
{
Log(LOG_WARNING, "NOT A COMMAND");
}
}
#undef DISCO
static void
IQStanza(ParseeData *args, XMLElement *stanza)
IQStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
char *type;
type = HashMapGet(stanza->attrs, "type");
@ -1141,13 +1201,14 @@ IQStanza(ParseeData *args, XMLElement *stanza)
{ \
if (StrEquals(type, #ctyp)) \
{ \
callback(args, stanza); \
callback(args, stanza, thr); \
return; \
} \
} \
while (0)
OnType(get, IQGet);
OnType(set, IQSet);
OnType(error, IQError);
OnType(result, IQResult);
#undef OnType
@ -1467,7 +1528,7 @@ XMPPDispatcher(void *argp)
}
else if (StrEquals(stanza->name, "iq"))
{
IQStanza(args, stanza);
IQStanza(args, stanza, thread);
}
else
{
@ -1488,21 +1549,117 @@ typedef struct XMPPAwait {
XMLElement *stanza;
} XMPPAwait;
static pthread_mutex_t await_lock = PTHREAD_MUTEX_INITIALIZER;
static HashMap *await_table = NULL;
static XMPPThreadInfo info;
size_t
ParseeCongestion(void)
{
size_t congestion;
pthread_mutex_lock(&info.lock);
congestion = ArraySize(info.stanzas);
pthread_mutex_unlock(&info.lock);
return congestion;
}
static void
StatusCallback(XMPPCommandManager *m, HashMap *data, XMLElement *out)
{
size_t alloc = MemoryAllocated();
size_t kb = alloc >> 10;
size_t mb = kb >> 10;
size_t gb = mb >> 10;
size_t min = gb ? gb : (mb ? mb : (kb ? kb : alloc));
char *unit = gb ? "GB" : (mb ? "MB" : (kb ? "KB" : "B"));
XMLElement *x = XMLCreateTag("x");
XMLElement *title = XMLCreateTag("title");
{
XMLElement *title_text = XMLCreateText("Parsee statistics");
XMLAddChild(title, title_text);
}
XMLAddChild(x, title);
XMLAddAttr(x, "xmlns", "jabber:x:data");
XMLAddAttr(x, "type", "result");
{
XMLElement *reported, *item, *field, *value, *txt;
reported = XMLCreateTag("reported");
XMLAddChild(x, reported);
#define Report(id, label) do \
{ \
field = XMLCreateTag("field"); \
XMLAddAttr(field, "var", id); \
XMLAddAttr(field, "label", label); \
XMLAddChild(reported, field); \
} \
while(0)
#define BeginItem() item = XMLCreateTag("item")
#define EndItem() XMLAddChild(x, item)
#define SetField(id, val) do \
{ \
field = XMLCreateTag("field"); \
value = XMLCreateTag("value"); \
txt = XMLCreateText(val); \
XMLAddAttr(field, "var", id); \
XMLAddChild(value, txt); \
XMLAddChild(field, value); \
XMLAddChild(item, field); \
} \
while(0)
/* Report */
Report("mem-alloc", "Heap allocated with Cytoplasm");
Report("xml-congest", "Unprocessed stanzas(congestion)");
/* Set */
BeginItem();
{
char *min_str = StrInt(min);
char *congest = StrInt(ParseeCongestion());
char *alloc = StrConcat(3, min_str, " ", unit);
SetField("mem-alloc", alloc);
SetField("xml-congest", congest);
Free(congest);
Free(min_str);
Free(alloc);
}
EndItem();
#undef SetField
#undef EndItem
#undef BeginItem
#undef Report
}
XMLAddChild(out, x);
}
void *
ParseeXMPPThread(void *argp)
{
ParseeData *args = argp;
XMPPComponent *jabber = args->jabber;
XMLElement *stanza = NULL;
XMPPThreadInfo info;
size_t i;
/* Initialise the await table */
await_table = HashMapCreate();
/* Initialise the command manager, and add all ad-hoc commands */
info.m = XMPPCreateManager();
{
XMPPCommand *cmd;
cmd = XMPPBasicCmd(
"status", "Get status about Parsee",
StatusCallback
);
XMPPRegisterCommand(info.m, cmd);
}
/* Initialise the FIFO */
info.stanzas = ArrayCreate();
pthread_mutex_init(&info.lock, NULL);
@ -1578,6 +1735,8 @@ ParseeXMPPThread(void *argp)
HashMapFree(await_table);
pthread_mutex_destroy(&info.lock);
XMPPFreeManager(info.m);
return NULL;
}

View file

@ -123,6 +123,14 @@ extern void * ParseeXMPPThread(void *data);
* Modifies: NONE */
extern XMLElement * ParseeAwaitStanza(char *identifier, int64_t ts);
/** Returns the amount of unprocessed stanzas in the XMPP thread, which
* can be used by admins to guess load.
* --------
* UB-If: called in the XMPP dispatcher thread itself
* Returns: amount of stanzas in the FIFO
* Modifies: NONE */
extern size_t ParseeCongestion(void);
/* Finds the room a DM is associated to, from a Matrix user and a Jabber
* ID. */
extern char * ParseeFindDMRoom(ParseeData *data, char *mxid, char *jid);

83
src/include/XMPPCommand.h Normal file
View file

@ -0,0 +1,83 @@
#ifndef PARSEE_XMPPCOMMAND_H
#define PARSEE_XMPPCOMMAND_H
#include <Cytoplasm/HashMap.h>
#include <stdbool.h>
#include <Parsee.h>
#include <XML.h>
typedef struct XMPPCommandManager XMPPCommandManager;
typedef struct XMPPCommand XMPPCommand;
typedef struct XMPPOption XMPPOption;
typedef void (*XMPPCmdCallback)(XMPPCommandManager *, HashMap *, XMLElement *);
/** Creates a simple XMPP command manager, which routes commands
* with a single-stage form system.
* -------------------------------------------
* Returns: An opaque command manager[LA:HEAP]
* Modifies: NOTHING
* See-Also: XMPPFreeManager */
extern XMPPCommandManager * XMPPCreateManager(void);
/** Create a basic command with a node and name description
* -----------------------------------------------------
* Returns: A command to be used with {XMPPRegisterCommand}[LA:HEAP]
* Modifies: NOTHING
* See-Also: XMPPRegisterCommand */
extern XMPPCommand * XMPPBasicCmd(char *node, char *name, XMPPCmdCallback cb);
extern void XMPPAddOption(XMPPCommand *cmd, XMPPOption *opt);
extern XMLElement * XMPPFormifyCommand(XMPPCommand *cmd);
extern char * XMPPGetCommandNode(XMPPCommand *cmd);
extern char * XMPPGetCommandDesc(XMPPCommand *cmd);
extern bool XMPPCommandRequiresForm(XMPPCommand *cmd);
extern void XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, XMLElement *to, HashMap *arg_table);
/** Create a basic option.
* -----------------------------------------------------
* Returns: A form option of the right type[LA:HEAP]
* Modifies: NOTHING
* See-Also: https://xmpp.org/extensions/xep-0004.html */
extern XMPPOption * XMPPCreateText(bool req, char *id, char *def);
extern XMPPOption * XMPPCreateBool(bool req, char *id, bool def);
extern XMPPOption * XMPPCreateList(bool req, bool single, char *id, char *def);
extern XMPPOption * XMPPCreateFixed(char *id, char *text);
extern void XMPPAddListOption(XMPPOption *list, char *option);
extern void XMPPSetDescription(XMPPOption *opt, char *descri);
extern XMLElement * XMPPOptionToXML(XMPPOption *opt);
extern void XMPPFreeOption(XMPPOption *opt);
extern void XMPPFreeCommand(XMPPCommand *cmd);
/** Registers a {cmd} to the {m}anager, with no extra metadata or callback.
* It also makes {cmd} belong to {m}anager, and therefore does not belong to
* its creator anymore.
* -----------------------------------------------------
* Returns: NOTHING
* Modifies: {m}anager
* See-Also: XMPPCreateManager, XMPPFreeManager */
extern void XMPPRegisterCommand(XMPPCommandManager *m, XMPPCommand *cmd);
/** Shoves all {m} commands into XML as children of {p}, and a {jid}
* -----------------------------------------------------
* Returns: NOTHING
* Modifies: {p}
* See-Also: XMPPCreateManager */
extern void XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p);
/** Destroys all memory related to the command {manager}.
* -----------------------------------------------------
* Returns: NOTHING
* Modifies: {manager}
* See-Also: XMPPCreateManager */
extern void XMPPFreeManager(XMPPCommandManager *manager);
/** Manages a command given a IQ stanza and a Parsee data field
* -----------------------------------------------------
* Returns: Whenever the command was managed properly and completed.
* Modifies: {manager}
* See-Also: XMPPCreateManager */
extern bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data);
#endif