mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 18:25:10 +00:00
896 lines
22 KiB
C
896 lines
22 KiB
C
#include <Parsee.h>
|
|
|
|
#include <Cytoplasm/Memory.h>
|
|
#include <Cytoplasm/Json.h>
|
|
#include <Cytoplasm/Util.h>
|
|
#include <Cytoplasm/Log.h>
|
|
#include <Cytoplasm/Str.h>
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <Routes.h>
|
|
#include <Glob.h>
|
|
#include <AS.h>
|
|
|
|
ParseeData *
|
|
ParseeInitData(XMPPComponent *comp)
|
|
{
|
|
ParseeData *data;
|
|
if (!ParseeConfigGet())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
data = Malloc(sizeof(*data));
|
|
data->config = ParseeConfigGet();
|
|
data->router = HttpRouterCreate();
|
|
data->jabber = comp;
|
|
data->handler = CommandCreateRouter();
|
|
data->db = DbOpen(data->config->db_path, 0);
|
|
|
|
#define X_ROUTE(path, func) do {\
|
|
if (!HttpRouterAdd(data->router, path, func))\
|
|
{\
|
|
Log(LOG_ERR, "Can't register %s", path);\
|
|
}\
|
|
}\
|
|
while (0);
|
|
ROUTES
|
|
#undef X_ROUTE
|
|
#define X_COMMAND(path,n,d) CommandAddCommand(data->handler, path, n);
|
|
COMMANDS
|
|
#undef X_COMMAND
|
|
return data;
|
|
}
|
|
void
|
|
ParseeFreeData(ParseeData *data)
|
|
{
|
|
if (!data)
|
|
{
|
|
return;
|
|
}
|
|
|
|
XMPPEndCompStream(data->jabber);
|
|
DbClose(data->db);
|
|
HttpRouterFree(data->router);
|
|
CommandFreeRouter(data->handler);
|
|
Free(data);
|
|
}
|
|
|
|
void
|
|
ParseeCleanup(void *datp)
|
|
{
|
|
ParseeData *data = datp;
|
|
Array *chats;
|
|
char *chat;
|
|
size_t i;
|
|
uint64_t ts = UtilTsMillis();
|
|
|
|
Log(LOG_NOTICE, "Cleaning up...");
|
|
chats = DbList(data->db, 1, "chats");
|
|
|
|
for (i = 0; i < ArraySize(chats); i++)
|
|
{
|
|
DbRef *ref;
|
|
HashMap *json, *stanzas, *events, *ids;
|
|
char *stanza, *event, *id;
|
|
JsonValue *val;
|
|
Array *to_delete;
|
|
size_t j;
|
|
|
|
chat = ArrayGet(chats, i);
|
|
ref = DbLock(data->db, 2, "chats", chat);
|
|
json = DbJson(ref);
|
|
|
|
#define CleanupField(field, timeout, threshold) do \
|
|
{ \
|
|
size_t cleaned = 0; \
|
|
field##s = JsonValueAsObject(HashMapGet(json, #field"s")); \
|
|
to_delete = ArrayCreate(); \
|
|
while (HashMapIterate(field##s, &field, (void **) &val)) \
|
|
{ \
|
|
HashMap *obj = JsonValueAsObject(val); \
|
|
uint64_t age = JsonValueAsInteger(HashMapGet(obj, "age")); \
|
|
uint64_t dur = ts - age; \
|
|
\
|
|
if ((dur > (timeout))) \
|
|
{ \
|
|
ArrayAdd(to_delete, StrDuplicate(field)); \
|
|
cleaned++; \
|
|
} \
|
|
} \
|
|
\
|
|
for (j = 0; j < ArraySize(to_delete); j++) \
|
|
{ \
|
|
field = ArrayGet(to_delete, j); \
|
|
if (cleaned > threshold) \
|
|
{ \
|
|
JsonValueFree(HashMapDelete(field##s, field)); \
|
|
} \
|
|
Free(field); \
|
|
} \
|
|
ArrayFree(to_delete); \
|
|
} \
|
|
while (0)
|
|
|
|
CleanupField(stanza, 30 MINUTES, 50);
|
|
CleanupField(event, 30 MINUTES, 50);
|
|
CleanupField(id, 30 MINUTES, 50);
|
|
|
|
DbUnlock(data->db, ref);
|
|
}
|
|
DbListFree(chats);
|
|
/* TODO */
|
|
chats = DbList(data->db, 1, "rooms");
|
|
|
|
for (i = 0; i < ArraySize(chats); i++)
|
|
{
|
|
DbRef *ref;
|
|
HashMap *json, *stanzas, *events, *ids;
|
|
char *stanza, *event, *id;
|
|
JsonValue *val;
|
|
Array *to_delete;
|
|
size_t j;
|
|
|
|
chat = ArrayGet(chats, i);
|
|
ref = DbLock(data->db, 3, "rooms", chat, "data");
|
|
json = DbJson(ref);
|
|
|
|
#define CleanupField(field, timeout, threshold) do \
|
|
{ \
|
|
size_t cleaned = 0; \
|
|
field##s = JsonValueAsObject(HashMapGet(json, #field"s")); \
|
|
to_delete = ArrayCreate(); \
|
|
while (HashMapIterate(field##s, &field, (void **) &val)) \
|
|
{ \
|
|
HashMap *obj = JsonValueAsObject(val); \
|
|
uint64_t age = JsonValueAsInteger(HashMapGet(obj, "age")); \
|
|
uint64_t dur = ts - age; \
|
|
\
|
|
if ((dur > (timeout))) \
|
|
{ \
|
|
ArrayAdd(to_delete, StrDuplicate(field)); \
|
|
cleaned++; \
|
|
} \
|
|
} \
|
|
\
|
|
for (j = 0; j < ArraySize(to_delete); j++) \
|
|
{ \
|
|
field = ArrayGet(to_delete, j); \
|
|
if (cleaned > threshold) \
|
|
{ \
|
|
JsonValueFree(HashMapDelete(field##s, field)); \
|
|
} \
|
|
Free(field); \
|
|
} \
|
|
ArrayFree(to_delete); \
|
|
} \
|
|
while (0)
|
|
|
|
CleanupField(stanza, 3 HOURS, 50);
|
|
CleanupField(event, 3 HOURS, 50);
|
|
CleanupField(id, 3 HOURS, 50);
|
|
|
|
DbUnlock(data->db, ref);
|
|
}
|
|
DbListFree(chats);
|
|
}
|
|
int
|
|
ParseeFindDatastart(char *data)
|
|
{
|
|
char *startline;
|
|
bool found = false;
|
|
if (!data)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
startline = data;
|
|
while (startline)
|
|
{
|
|
char *endline = strchr(startline, '\n');
|
|
|
|
if (*startline != '>')
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
startline = endline ? endline + 1 : NULL;
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return (int) (startline - data);
|
|
}
|
|
|
|
#include <StringStream.h>
|
|
typedef struct XMPPFlags {
|
|
bool quote;
|
|
} XMPPFlags;
|
|
static char *
|
|
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|
{
|
|
char *xepd = NULL, *tmp = NULL;
|
|
|
|
size_t i;
|
|
XMLElement *child;
|
|
char *reply_id = JsonValueAsString(
|
|
JsonGet(event, 4,
|
|
"content", "m.relates_to", "m.in_reply_to", "event_id"
|
|
));
|
|
char *room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
|
HashMap *referenced;
|
|
char *subxep;
|
|
#define Concat(strp) do \
|
|
{ \
|
|
size_t cidx; \
|
|
size_t len = strp ? strlen(strp) : 0; \
|
|
for (cidx = 0; cidx < len; cidx++) \
|
|
{ \
|
|
char cch[2] = { strp[cidx], 0 }; \
|
|
char nch = *cch ? strp[cidx+1] : '\0'; \
|
|
bool c = *cch == '\n' && nch != '>'; \
|
|
if (c && flags.quote) \
|
|
{ \
|
|
tmp = xepd; \
|
|
xepd = StrConcat(2, xepd, "\n>"); \
|
|
Free(tmp); \
|
|
continue; \
|
|
} \
|
|
tmp = xepd; \
|
|
xepd = StrConcat(2, xepd, cch); \
|
|
Free(tmp); \
|
|
} \
|
|
} \
|
|
while (0)
|
|
switch (elem->type)
|
|
{
|
|
case XML_ELEMENT_DATA:
|
|
Concat(elem->data);
|
|
break;
|
|
case XML_ELEMENT_TAG:
|
|
if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong"))
|
|
{
|
|
Concat("*");
|
|
for (i = 0; i < ArraySize(elem->children); i++)
|
|
{
|
|
child = ArrayGet(elem->children, i);
|
|
subxep = XMPPifyElement(event, child, flags);
|
|
|
|
Concat(subxep);
|
|
Free(subxep);
|
|
}
|
|
Concat("*");
|
|
}
|
|
else if (StrEquals(elem->name, "em"))
|
|
{
|
|
Concat("_");
|
|
for (i = 0; i < ArraySize(elem->children); i++)
|
|
{
|
|
child = ArrayGet(elem->children, i);
|
|
subxep = XMPPifyElement(event, child, flags);
|
|
|
|
Concat(subxep);
|
|
Free(subxep);
|
|
}
|
|
Concat("_");
|
|
}
|
|
else if (StrEquals(elem->name, "code"))
|
|
{
|
|
Concat("`");
|
|
for (i = 0; i < ArraySize(elem->children); i++)
|
|
{
|
|
child = ArrayGet(elem->children, i);
|
|
subxep = XMPPifyElement(event, child, flags);
|
|
|
|
Concat(subxep);
|
|
Free(subxep);
|
|
}
|
|
Concat("`");
|
|
}
|
|
else if (StrEquals(elem->name, "mx-reply"))
|
|
{
|
|
char *str;
|
|
referenced = ASFind(ParseeConfigGet(), room_id, reply_id);
|
|
str = JsonValueAsString(
|
|
JsonGet(referenced, 2, "content", "body")
|
|
);
|
|
if (!str)
|
|
{
|
|
JsonFree(referenced);
|
|
return xepd;
|
|
}
|
|
Concat(">");
|
|
flags.quote = true;
|
|
Concat(str);
|
|
flags.quote = false;
|
|
Concat("\n");
|
|
JsonFree(referenced);
|
|
}
|
|
else if (StrEquals(elem->name, "blockquote"))
|
|
{
|
|
Concat(">");
|
|
flags.quote = true;
|
|
for (i = 0; i < ArraySize(elem->children); i++)
|
|
{
|
|
child = ArrayGet(elem->children, i);
|
|
subxep = XMPPifyElement(event, child, flags);
|
|
|
|
Concat(subxep);
|
|
Free(subxep);
|
|
}
|
|
flags.quote = false;
|
|
Concat("\n");
|
|
}
|
|
else if (StrEquals(elem->name, "br"))
|
|
{
|
|
Concat("\n");
|
|
/* HTML fucking SUCKS */
|
|
for (i = 0; i < ArraySize(elem->children); i++)
|
|
{
|
|
child = ArrayGet(elem->children, i);
|
|
subxep = XMPPifyElement(event, child, flags);
|
|
|
|
Concat(subxep);
|
|
Free(subxep);
|
|
}
|
|
Concat("\n");
|
|
}
|
|
else if (StrEquals(elem->name, "a"))
|
|
{
|
|
char *href = HashMapGet(elem->attrs, "href");
|
|
Concat("(");
|
|
for (i = 0; i < ArraySize(elem->children); i++)
|
|
{
|
|
child = ArrayGet(elem->children, i);
|
|
subxep = XMPPifyElement(event, child, flags);
|
|
|
|
Concat(subxep);
|
|
Free(subxep);
|
|
}
|
|
Concat(" points to ");
|
|
Concat(href);
|
|
Concat(" )");
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < ArraySize(elem->children); i++)
|
|
{
|
|
child = ArrayGet(elem->children, i);
|
|
subxep = XMPPifyElement(event, child, flags);
|
|
|
|
Concat(subxep);
|
|
Free(subxep);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return xepd;
|
|
}
|
|
char *
|
|
ParseeXMPPify(HashMap *event)
|
|
{
|
|
char *type, *format, *html;
|
|
char *xepd = NULL;
|
|
XMLElement *elem;
|
|
|
|
XMPPFlags flags;
|
|
if (!event)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/* Check if it is a message event. */
|
|
type = JsonValueAsString(HashMapGet(event, "type"));
|
|
if (!StrEquals(type, "m.room.message"))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
|
|
if (!StrEquals(format, "org.matrix.custom.html"))
|
|
{
|
|
/* Settle for the raw body instead. */
|
|
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
|
|
return StrDuplicate(body);
|
|
}
|
|
|
|
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
|
|
html = StrConcat(3, "<html>", html, "</html>");
|
|
elem = XMLCDecode(StrStreamReader(html), true, true);
|
|
|
|
flags.quote = false;
|
|
xepd = XMPPifyElement(event, elem, flags);
|
|
|
|
XMLFreeElement(elem);
|
|
Free(html);
|
|
|
|
return xepd;
|
|
}
|
|
void
|
|
ParseePushDMStanza(ParseeData *data, char *room_id, char *stanza_id, char *id, char *ev, char *sender)
|
|
{
|
|
DbRef *ref;
|
|
HashMap *j;
|
|
HashMap *stanzas, *obj, *events, *ids;
|
|
bool new_stanzas = false, new_events = false;
|
|
bool new_ids = false;
|
|
uint64_t age = UtilTsMillis();
|
|
if (!data || !room_id || !ev || !sender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ref = DbLock(data->db, 3, "rooms", room_id, "data");
|
|
if (!ref)
|
|
{
|
|
return;
|
|
}
|
|
j = DbJson(ref);
|
|
|
|
|
|
if (stanza_id)
|
|
{
|
|
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
|
|
if (!stanzas)
|
|
{
|
|
stanzas = HashMapCreate();
|
|
new_stanzas = true;
|
|
}
|
|
|
|
obj = HashMapCreate();
|
|
HashMapSet(obj, "age", JsonValueInteger(age));
|
|
HashMapSet(obj, "event", JsonValueString(ev));
|
|
JsonValueFree(HashMapSet(stanzas, stanza_id, JsonValueObject(obj)));
|
|
|
|
if (new_stanzas)
|
|
{
|
|
HashMapSet(j, "stanzas", JsonValueObject(stanzas));
|
|
}
|
|
}
|
|
|
|
events = JsonValueAsObject(HashMapGet(j, "events"));
|
|
if (!events)
|
|
{
|
|
events = HashMapCreate();
|
|
new_events = true;
|
|
}
|
|
obj = HashMapCreate();
|
|
HashMapSet(obj, "stanza", JsonValueString(stanza_id));
|
|
HashMapSet(obj, "origin", JsonValueString(id));
|
|
HashMapSet(obj, "sender", JsonValueString(sender));
|
|
HashMapSet(obj, "age", JsonValueInteger(age));
|
|
JsonValueFree(HashMapSet(events, ev, JsonValueObject(obj)));
|
|
if (new_events)
|
|
{
|
|
HashMapSet(j, "events", JsonValueObject(events));
|
|
}
|
|
|
|
if (id)
|
|
{
|
|
ids = JsonValueAsObject(HashMapGet(j, "ids"));
|
|
if (!ids)
|
|
{
|
|
ids = HashMapCreate();
|
|
new_ids = true;
|
|
}
|
|
obj = HashMapCreate();
|
|
HashMapSet(obj, "stanza", JsonValueString(stanza_id));
|
|
HashMapSet(obj, "event", JsonValueString(ev));
|
|
HashMapSet(obj, "sender", JsonValueString(sender));
|
|
HashMapSet(obj, "age", JsonValueInteger(age));
|
|
JsonValueFree(HashMapSet(ids, id, JsonValueObject(obj)));
|
|
if (new_ids)
|
|
{
|
|
HashMapSet(j, "ids", JsonValueObject(ids));
|
|
}
|
|
}
|
|
DbUnlock(data->db, ref);
|
|
}
|
|
void
|
|
ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, char *ev, char *sender)
|
|
{
|
|
DbRef *ref;
|
|
HashMap *j;
|
|
HashMap *stanzas, *obj, *events, *ids;
|
|
bool new_stanzas = false, new_events = false;
|
|
bool new_ids = false;
|
|
uint64_t age = UtilTsMillis();
|
|
if (!data || !chat_id || !stanza_id || !ev || !sender)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ref = DbLock(data->db, 2, "chats", chat_id);
|
|
j = DbJson(ref);
|
|
if (!ref)
|
|
{
|
|
return;
|
|
}
|
|
|
|
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
|
|
if (!stanzas)
|
|
{
|
|
stanzas = HashMapCreate();
|
|
new_stanzas = true;
|
|
}
|
|
|
|
obj = HashMapCreate();
|
|
HashMapSet(obj, "age", JsonValueInteger(age));
|
|
HashMapSet(obj, "event", JsonValueString(ev));
|
|
JsonValueFree(HashMapSet(stanzas, stanza_id, JsonValueObject(obj)));
|
|
|
|
if (new_stanzas)
|
|
{
|
|
HashMapSet(j, "stanzas", JsonValueObject(stanzas));
|
|
}
|
|
|
|
events = JsonValueAsObject(HashMapGet(j, "events"));
|
|
if (!events)
|
|
{
|
|
events = HashMapCreate();
|
|
new_events = true;
|
|
}
|
|
obj = HashMapCreate();
|
|
HashMapSet(obj, "stanza", JsonValueString(stanza_id));
|
|
HashMapSet(obj, "origin", JsonValueString(id));
|
|
HashMapSet(obj, "sender", JsonValueString(sender));
|
|
HashMapSet(obj, "age", JsonValueInteger(age));
|
|
JsonValueFree(HashMapSet(events, ev, JsonValueObject(obj)));
|
|
if (new_events)
|
|
{
|
|
HashMapSet(j, "events", JsonValueObject(events));
|
|
}
|
|
|
|
if (id)
|
|
{
|
|
ids = JsonValueAsObject(HashMapGet(j, "ids"));
|
|
if (!ids)
|
|
{
|
|
ids = HashMapCreate();
|
|
new_ids = true;
|
|
}
|
|
obj = HashMapCreate();
|
|
HashMapSet(obj, "stanza", JsonValueString(stanza_id));
|
|
HashMapSet(obj, "event", JsonValueString(ev));
|
|
HashMapSet(obj, "sender", JsonValueString(sender));
|
|
HashMapSet(obj, "age", JsonValueInteger(age));
|
|
JsonValueFree(HashMapSet(ids, id, JsonValueObject(obj)));
|
|
if (new_ids)
|
|
{
|
|
HashMapSet(j, "ids", JsonValueObject(ids));
|
|
}
|
|
}
|
|
DbUnlock(data->db, ref);
|
|
}
|
|
|
|
bool
|
|
ParseeVerifyDMStanza(ParseeData *data, char *room_id, char *id)
|
|
{
|
|
DbRef *ref = NULL;
|
|
HashMap *j = NULL;
|
|
HashMap *stanzas = NULL;
|
|
bool ret = true;
|
|
if (!data || !room_id || !id)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
ref = DbLock(data->db, 3, "rooms", room_id, "data");
|
|
j = DbJson(ref);
|
|
if (!ref)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
stanzas = JsonValueAsObject(HashMapGet(j, "ids"));
|
|
if (!stanzas)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ret = !HashMapGet(stanzas, id);
|
|
end:
|
|
DbUnlock(data->db, ref);
|
|
return ret;
|
|
}
|
|
bool
|
|
ParseeVerifyStanza(ParseeData *data, char *chat_id, char *stanza_id)
|
|
{
|
|
DbRef *ref = NULL;
|
|
HashMap *j = NULL;
|
|
HashMap *stanzas = NULL;
|
|
bool ret = true;
|
|
if (!data || !chat_id || !stanza_id)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
ref = DbLock(data->db, 2, "chats", chat_id);
|
|
j = DbJson(ref);
|
|
if (!ref)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
stanzas = JsonValueAsObject(HashMapGet(j, "stanzas"));
|
|
if (!stanzas)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ret = !HashMapGet(stanzas, stanza_id);
|
|
end:
|
|
DbUnlock(data->db, ref);
|
|
return ret;
|
|
}
|
|
char *
|
|
ParseeEventFromID(ParseeData *data, char *chat_id, char *id)
|
|
{
|
|
DbRef *ref = NULL;
|
|
HashMap *j = NULL;
|
|
HashMap *ids = NULL;
|
|
char *ret = NULL;
|
|
if (!data || !chat_id || !id)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ref = DbLock(data->db, 2, "chats", chat_id);
|
|
j = DbJson(ref);
|
|
if (!ref)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ids = JsonValueAsObject(HashMapGet(j, "ids"));
|
|
if (!ids)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ret = JsonValueAsString(JsonGet(ids, 2, id, "event"));
|
|
ret = StrDuplicate(ret);
|
|
end:
|
|
DbUnlock(data->db, ref);
|
|
return ret;
|
|
}
|
|
char *
|
|
ParseeDMEventFromID(ParseeData *data, char *room_id, char *id)
|
|
{
|
|
DbRef *ref = NULL;
|
|
HashMap *j = NULL;
|
|
HashMap *ids = NULL;
|
|
char *ret = NULL;
|
|
if (!data || !room_id || !id)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ref = DbLock(data->db, 3, "rooms", room_id, "data");
|
|
j = DbJson(ref);
|
|
if (!ref)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ids = JsonValueAsObject(HashMapGet(j, "ids"));
|
|
if (!ids)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ret = JsonValueAsString(JsonGet(ids, 2, id, "event"));
|
|
ret = StrDuplicate(ret);
|
|
end:
|
|
DbUnlock(data->db, ref);
|
|
return ret;
|
|
}
|
|
|
|
char *
|
|
ParseeEventFromSID(ParseeData *data, char *chat_id, char *id)
|
|
{
|
|
DbRef *ref = NULL;
|
|
HashMap *j = NULL;
|
|
HashMap *ids = NULL;
|
|
char *ret = NULL;
|
|
if (!data || !chat_id || !id)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ref = DbLock(data->db, 2, "chats", chat_id);
|
|
j = DbJson(ref);
|
|
if (!ref)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ids = JsonValueAsObject(HashMapGet(j, "stanzas"));
|
|
if (!ids)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ret = JsonValueAsString(JsonGet(ids, 2, id, "event"));
|
|
ret = StrDuplicate(ret);
|
|
end:
|
|
DbUnlock(data->db, ref);
|
|
return ret;
|
|
}
|
|
char *
|
|
ParseeDMEventFromSID(ParseeData *data, char *room_id, char *id)
|
|
{
|
|
DbRef *ref = NULL;
|
|
HashMap *j = NULL;
|
|
HashMap *ids = NULL;
|
|
char *ret = NULL;
|
|
if (!data || !room_id || !id)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ref = DbLock(data->db, 3, "rooms", room_id, "data");
|
|
j = DbJson(ref);
|
|
if (!ref)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ids = JsonValueAsObject(HashMapGet(j, "stanzas"));
|
|
if (!ids)
|
|
{
|
|
goto end;
|
|
}
|
|
|
|
ret = JsonValueAsString(JsonGet(ids, 2, id, "event"));
|
|
ret = StrDuplicate(ret);
|
|
end:
|
|
DbUnlock(data->db, ref);
|
|
return ret;
|
|
|
|
}
|
|
void
|
|
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
|
{
|
|
DbRef *ref;
|
|
HashMap *j, *obj;
|
|
if (!data || !glob)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ref = DbLock(data->db, 1, "global_bans");
|
|
if (!ref)
|
|
{
|
|
ref = DbCreate(data->db, 1, "global_bans");
|
|
}
|
|
|
|
j = DbJson(ref);
|
|
|
|
obj = HashMapCreate();
|
|
if (reason)
|
|
{
|
|
HashMapSet(obj, "reason", JsonValueString(reason));
|
|
}
|
|
HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis()));
|
|
JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj)));
|
|
|
|
DbUnlock(data->db, ref);
|
|
}
|
|
bool
|
|
ParseeManageBan(ParseeData *data, char *user, char *room)
|
|
{
|
|
DbRef *ref;
|
|
HashMap *j;
|
|
char *key;
|
|
JsonValue *val;
|
|
bool banned = false , matches = false;
|
|
if (!data || !user)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ref = DbLock(data->db, 1, "global_bans");
|
|
j = DbJson(ref);
|
|
while (HashMapIterate(j, &key, (void **) &val))
|
|
{
|
|
HashMap *obj = JsonValueAsObject(val);
|
|
if (matches)
|
|
{
|
|
continue;
|
|
}
|
|
if (GlobMatches(key, user))
|
|
{
|
|
banned = true;
|
|
matches = true;
|
|
if (room)
|
|
{
|
|
/* TODO: Use the object to set the reason */
|
|
ASBan(data->config, room, user);
|
|
(void) obj;
|
|
}
|
|
}
|
|
}
|
|
DbUnlock(data->db, ref);
|
|
|
|
return banned;
|
|
}
|
|
char *
|
|
ParseeStringifyDate(uint64_t millis)
|
|
{
|
|
uint64_t rest = millis;
|
|
uint64_t hours, minutes, seconds;
|
|
char *hs, *ms, *ss, *out;
|
|
|
|
hours = rest / (1 HOURS);
|
|
rest = rest % (1 HOURS);
|
|
|
|
minutes = rest / (1 MINUTES);
|
|
rest = rest % (1 MINUTES);
|
|
|
|
seconds = rest / (1 SECONDS);
|
|
|
|
hs = StrInt(hours);
|
|
ms = StrInt(minutes);
|
|
ss = StrInt(seconds);
|
|
|
|
out = StrConcat(8,
|
|
hours ? hs : "",
|
|
hours ? " hours" : "",
|
|
(hours && minutes) ? ", " : "",
|
|
|
|
minutes ? ms : "",
|
|
minutes ? " minutes" : "",
|
|
(minutes && seconds) ? ", " : "",
|
|
|
|
seconds ? ss : "",
|
|
seconds ? " seconds" : ""
|
|
);
|
|
Free(hs);
|
|
Free(ms);
|
|
Free(ss);
|
|
|
|
return out;
|
|
}
|
|
|
|
void
|
|
ParseeAchievement(const char *func, const char *msg, bool die)
|
|
{
|
|
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
|
Log(LOG_ERR, "%s: %s.", func, msg);
|
|
Log(LOG_ERR, "THIS IS, LET'S SAY, NOT GOOD, AND A SIGN OF ");
|
|
Log(LOG_ERR, "A PROGRAMMER ERROR. PLEASE KILLALL -9 PARSEE.");
|
|
Log(LOG_ERR, "");
|
|
Log(LOG_ERR, "YOU, HOWEVER, GET TO WIN AN AWARD FOR THIS.");
|
|
Log(LOG_ERR, "I AM SIMPLY JEALOUS OF YOU GETTING SUCH A ");
|
|
Log(LOG_ERR, "GOOD ERROR MESSAGE.");
|
|
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
|
|
|
if (die)
|
|
{
|
|
abort();
|
|
}
|
|
}
|
|
char *
|
|
ParseeGenerateMTO(char *common_id)
|
|
{
|
|
char *matrix_to;
|
|
if (!common_id)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
common_id = HttpUrlEncode(common_id);
|
|
matrix_to = StrConcat(2, "https://matrix.to/#/", common_id);
|
|
Free(common_id);
|
|
|
|
return matrix_to;
|
|
}
|