mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 17:05:11 +00:00
[ADD/WIP] Congestion, basic ad-hoc commands
Woah, took me a while, eh? Next up, getting forms!
This commit is contained in:
parent
4007232716
commit
408888ef67
9 changed files with 911 additions and 14 deletions
4
Makefile
4
Makefile
|
|
@ -19,8 +19,8 @@ SOURCE=src
|
||||||
OBJECT=build
|
OBJECT=build
|
||||||
INCLUDES=src/include
|
INCLUDES=src/include
|
||||||
CC=cc
|
CC=cc
|
||||||
CFLAGS=-I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -O3 -g -ggdb -Wall -Werror
|
CFLAGS=-I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -O3 -g -ggdb -Wall -Werror -flto
|
||||||
LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -Wl,--export-dynamic -O3 -g -ggdb
|
LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -Wl,--export-dynamic -O3 -g -ggdb -flto
|
||||||
BINARY=parsee
|
BINARY=parsee
|
||||||
# ============================ Compilation =================================
|
# ============================ Compilation =================================
|
||||||
SRC_FILES:=$(shell find $(SOURCE) -name '*.c')
|
SRC_FILES:=$(shell find $(SOURCE) -name '*.c')
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ CommandHead(CmdStats, cmd, argp)
|
||||||
ParseeCmdArg *args = argp;
|
ParseeCmdArg *args = argp;
|
||||||
ParseeData *data = args->data;
|
ParseeData *data = args->data;
|
||||||
HashMap *event = args->event;
|
HashMap *event = args->event;
|
||||||
|
size_t congestion = ParseeCongestion();
|
||||||
size_t alloc = MemoryAllocated();
|
size_t alloc = MemoryAllocated();
|
||||||
size_t kb = alloc >> 10;
|
size_t kb = alloc >> 10;
|
||||||
size_t mb = kb >> 10;
|
size_t mb = kb >> 10;
|
||||||
|
|
@ -31,6 +32,9 @@ CommandHead(CmdStats, cmd, argp)
|
||||||
ReplySprintf("- Memory used: %d%s (reported by Cytoplasm)",
|
ReplySprintf("- Memory used: %d%s (reported by Cytoplasm)",
|
||||||
(int) min, unit
|
(int) min, unit
|
||||||
);
|
);
|
||||||
|
ReplySprintf("- Unprocessed stanzas: %d",
|
||||||
|
(int) congestion
|
||||||
|
);
|
||||||
ReplySprintf("- Source code and licensing information: %s",
|
ReplySprintf("- Source code and licensing information: %s",
|
||||||
REPOSITORY
|
REPOSITORY
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -141,20 +141,27 @@ IdentifySpan(char span_tag, StrView in, StrView *view)
|
||||||
view->end = in.start;
|
view->end = in.start;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
XEP393Decode(StrView view, XEP393Element *root)
|
XEP393Decode(StrView view, XEP393Element *root)
|
||||||
{
|
{
|
||||||
StrView subview = view;
|
StrView subview = view;
|
||||||
StrView textview = view;
|
StrView textview = view;
|
||||||
XEP393Element *text, *span;
|
XEP393Element *text, *span;
|
||||||
size_t i;
|
|
||||||
bool managed = false;
|
bool managed = false;
|
||||||
|
char prev = '\0', curr = '\0';
|
||||||
|
|
||||||
textview.end = subview.start;
|
textview.end = subview.start;
|
||||||
for (i = 0; subview.start < subview.end; subview.start++)
|
for (; subview.start < subview.end; subview.start++)
|
||||||
{
|
{
|
||||||
StrView span_view;
|
StrView span_view;
|
||||||
managed = false;
|
managed = false;
|
||||||
|
curr = *subview.start;
|
||||||
|
if (prev == '\0' || prev == '\n')
|
||||||
|
{
|
||||||
|
/* TODO: Start of line, start parsing blocks. */
|
||||||
|
}
|
||||||
#define Spanify(xep_symbol) \
|
#define Spanify(xep_symbol) \
|
||||||
managed = true; \
|
managed = true; \
|
||||||
textview.end = subview.start; \
|
textview.end = subview.start; \
|
||||||
|
|
@ -193,7 +200,8 @@ XEP393Decode(StrView view, XEP393Element *root)
|
||||||
/* Text character: update end */
|
/* Text character: update end */
|
||||||
textview.end = subview.start;
|
textview.end = subview.start;
|
||||||
}
|
}
|
||||||
(void) i;
|
|
||||||
|
prev = curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!managed)
|
if (!managed)
|
||||||
|
|
@ -215,7 +223,10 @@ XEP393(char *message)
|
||||||
/* TODO: Parse blocks first, *then* spans. Considering the
|
/* TODO: Parse blocks first, *then* spans. Considering the
|
||||||
* current architecture, this shouldn't be too hard to integrate,
|
* current architecture, this shouldn't be too hard to integrate,
|
||||||
* given how string views already manage boundaries, and elements
|
* 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);
|
XEP393Decode(view, root);
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
121
src/XMPPCommand/Commands.c
Normal file
121
src/XMPPCommand/Commands.c
Normal 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
260
src/XMPPCommand/Manager.c
Normal 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
251
src/XMPPCommand/Options.c
Normal 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);
|
||||||
|
}
|
||||||
175
src/XMPPThread.c
175
src/XMPPThread.c
|
|
@ -12,6 +12,7 @@
|
||||||
#include <Cytoplasm/Sha.h>
|
#include <Cytoplasm/Sha.h>
|
||||||
|
|
||||||
#include <StringStream.h>
|
#include <StringStream.h>
|
||||||
|
#include <XMPPCommand.h>
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
|
|
||||||
#define IQ_ADVERT \
|
#define IQ_ADVERT \
|
||||||
AdvertiseSimple("http://jabber.org/protocol/chatstates") \
|
AdvertiseSimple("http://jabber.org/protocol/chatstates") \
|
||||||
|
AdvertiseSimple("http://jabber.org/protocol/commands") \
|
||||||
AdvertiseSimple("http://jabber.org/protocol/caps") \
|
AdvertiseSimple("http://jabber.org/protocol/caps") \
|
||||||
AdvertiseSimple("urn:xmpp:avatar:metadata+notify") \
|
AdvertiseSimple("urn:xmpp:avatar:metadata+notify") \
|
||||||
AdvertiseSimple("urn:xmpp:avatar:data+notify") \
|
AdvertiseSimple("urn:xmpp:avatar:data+notify") \
|
||||||
|
|
@ -293,6 +295,7 @@ typedef struct XMPPThreadInfo {
|
||||||
|
|
||||||
ParseeData *args;
|
ParseeData *args;
|
||||||
XMPPComponent *jabber;
|
XMPPComponent *jabber;
|
||||||
|
XMPPCommandManager *m;
|
||||||
|
|
||||||
struct XMPPThread *dispatchers;
|
struct XMPPThread *dispatchers;
|
||||||
size_t available_dispatchers;
|
size_t available_dispatchers;
|
||||||
|
|
@ -864,13 +867,16 @@ TrimBase64(char *b64)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
static void
|
static void
|
||||||
IQResult(ParseeData *args, XMLElement *stanza)
|
IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
{
|
{
|
||||||
XMLElement *vcard = XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp");
|
XMLElement *vcard = XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp");
|
||||||
|
|
||||||
XMLElement *event = XMLookForTKV(stanza, "pubsub",
|
XMLElement *event = XMLookForTKV(stanza, "pubsub",
|
||||||
"xmlns", "http://jabber.org/protocol/pubsub"
|
"xmlns", "http://jabber.org/protocol/pubsub"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
(void) thr;
|
||||||
|
|
||||||
if (event)
|
if (event)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
@ -1033,15 +1039,60 @@ CreateTagWithText(const char *tn, char *text)
|
||||||
|
|
||||||
return tag;
|
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
|
static void
|
||||||
IQGet(ParseeData *args, XMLElement *stanza)
|
IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
{
|
{
|
||||||
XMPPComponent *jabber = args->jabber;
|
XMPPComponent *jabber = args->jabber;
|
||||||
char *from = HashMapGet(stanza->attrs, "from");
|
char *from = HashMapGet(stanza->attrs, "from");
|
||||||
char *to = HashMapGet(stanza->attrs, "to");
|
char *to = HashMapGet(stanza->attrs, "to");
|
||||||
char *id = HashMapGet(stanza->attrs, "id");
|
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);
|
Log(LOG_INFO, "vCard information GET for %s", to);
|
||||||
|
|
||||||
|
|
@ -1127,13 +1178,22 @@ IQGet(ParseeData *args, XMLElement *stanza)
|
||||||
|
|
||||||
}
|
}
|
||||||
static void
|
static void
|
||||||
IQError(ParseeData *args, XMLElement *stanza)
|
IQError(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
{
|
{
|
||||||
/* TODO */
|
/* 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
|
#undef DISCO
|
||||||
static void
|
static void
|
||||||
IQStanza(ParseeData *args, XMLElement *stanza)
|
IQStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
{
|
{
|
||||||
char *type;
|
char *type;
|
||||||
type = HashMapGet(stanza->attrs, "type");
|
type = HashMapGet(stanza->attrs, "type");
|
||||||
|
|
@ -1141,13 +1201,14 @@ IQStanza(ParseeData *args, XMLElement *stanza)
|
||||||
{ \
|
{ \
|
||||||
if (StrEquals(type, #ctyp)) \
|
if (StrEquals(type, #ctyp)) \
|
||||||
{ \
|
{ \
|
||||||
callback(args, stanza); \
|
callback(args, stanza, thr); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
OnType(get, IQGet);
|
OnType(get, IQGet);
|
||||||
|
OnType(set, IQSet);
|
||||||
OnType(error, IQError);
|
OnType(error, IQError);
|
||||||
OnType(result, IQResult);
|
OnType(result, IQResult);
|
||||||
#undef OnType
|
#undef OnType
|
||||||
|
|
@ -1467,7 +1528,7 @@ XMPPDispatcher(void *argp)
|
||||||
}
|
}
|
||||||
else if (StrEquals(stanza->name, "iq"))
|
else if (StrEquals(stanza->name, "iq"))
|
||||||
{
|
{
|
||||||
IQStanza(args, stanza);
|
IQStanza(args, stanza, thread);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -1488,21 +1549,117 @@ typedef struct XMPPAwait {
|
||||||
|
|
||||||
XMLElement *stanza;
|
XMLElement *stanza;
|
||||||
} XMPPAwait;
|
} XMPPAwait;
|
||||||
|
|
||||||
static pthread_mutex_t await_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t await_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static HashMap *await_table = NULL;
|
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 *
|
void *
|
||||||
ParseeXMPPThread(void *argp)
|
ParseeXMPPThread(void *argp)
|
||||||
{
|
{
|
||||||
ParseeData *args = argp;
|
ParseeData *args = argp;
|
||||||
XMPPComponent *jabber = args->jabber;
|
XMPPComponent *jabber = args->jabber;
|
||||||
XMLElement *stanza = NULL;
|
XMLElement *stanza = NULL;
|
||||||
XMPPThreadInfo info;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
/* Initialise the await table */
|
/* Initialise the await table */
|
||||||
await_table = HashMapCreate();
|
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 */
|
/* Initialise the FIFO */
|
||||||
info.stanzas = ArrayCreate();
|
info.stanzas = ArrayCreate();
|
||||||
pthread_mutex_init(&info.lock, NULL);
|
pthread_mutex_init(&info.lock, NULL);
|
||||||
|
|
@ -1578,6 +1735,8 @@ ParseeXMPPThread(void *argp)
|
||||||
HashMapFree(await_table);
|
HashMapFree(await_table);
|
||||||
|
|
||||||
pthread_mutex_destroy(&info.lock);
|
pthread_mutex_destroy(&info.lock);
|
||||||
|
|
||||||
|
XMPPFreeManager(info.m);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,14 @@ extern void * ParseeXMPPThread(void *data);
|
||||||
* Modifies: NONE */
|
* Modifies: NONE */
|
||||||
extern XMLElement * ParseeAwaitStanza(char *identifier, int64_t ts);
|
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
|
/* Finds the room a DM is associated to, from a Matrix user and a Jabber
|
||||||
* ID. */
|
* ID. */
|
||||||
extern char * ParseeFindDMRoom(ParseeData *data, char *mxid, char *jid);
|
extern char * ParseeFindDMRoom(ParseeData *data, char *mxid, char *jid);
|
||||||
|
|
|
||||||
83
src/include/XMPPCommand.h
Normal file
83
src/include/XMPPCommand.h
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue