[ADD/FIX/WIP] "Fix" concurrency, prepare XEP-0421

I'll need to break down my commits more...
This commit is contained in:
LDA 2024-07-18 15:50:19 +02:00
commit 63c1bc819e
14 changed files with 356 additions and 162 deletions

View file

@ -1,6 +1,10 @@
#include "XMPPThread/internal.h"
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <string.h>
char *
ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza)
@ -111,3 +115,70 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
return ret;
}
bool
ServerHasXEP421(ParseeData *data, char *from)
{
char *server = NULL, *parsee;
XMLElement *disco;
bool ret;
if (!data || !from)
{
return false;
}
server = strchr(from, '@');
if (!server)
{
server = from;
}
else
{
server++;
}
server = StrDuplicate(server);
if (strchr(server, '/'))
{
*(strchr(server, '/')) = '\0';
}
parsee = ParseeJID(data);
disco = XMPPSendDisco(data->jabber, parsee, server);
ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0");
XMLFreeElement(disco);
Free(parsee);
Free(server);
return ret;
}
char *
ParseeGetBridgedUser(ParseeData *data, XMLElement *stanza)
{
char *user, *xmpp_from, *type;
char *decode_from;
if (!data || !stanza)
{
return NULL;
}
xmpp_from = HashMapGet(stanza->attrs, "from");
type = HashMapGet(stanza->attrs, "type");
decode_from = ParseeLookupJID(xmpp_from);
/* TODO: On semi-anonymous MUCs, it might be preferable to use a
* form of the occupant ID as the base, as it is more unique, and
* less prone to trigger the character limit on Matrix.
* I'll need to detect these first....
*
* See: https://xmpp.org/extensions/xep-0421.html */
user = ParseeEncodeJID(
data->config,
decode_from,
StrEquals(type, "chat")
);
Free(decode_from);
return user;
}

79
src/XMPPThread/Caps.c Normal file
View file

@ -0,0 +1,79 @@
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Base64.h>
#include <Cytoplasm/Util.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Sha.h>
#include <StringStream.h>
#include <XMPPCommand.h>
#include <Matrix.h>
#include <XMPP.h>
#include <XML.h>
#include "XMPPThread/internal.h"
/* Generates a SHA-256 hash of the ver field. */
char *
XMPPGenerateVer(void)
{
char *S = NULL;
unsigned char *Sha = NULL;
Array *identities = ArrayCreate();
Array *features = ArrayCreate();
size_t i;
/* Initialise identity table, to be sorted */
#define IdentitySimple(cat, Type, Name) { \
XMPPIdentity *id = Malloc(sizeof(*id)); \
id->category = cat; \
id->lang = NULL; \
id->type = Type; \
id->name = Name; \
ArrayAdd(identities, id); }
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(feature) ArrayAdd(features, feature);
IQ_ADVERT
#undef AdvertiseSimple
ArraySort(identities, IdentitySort);
for (i = 0; i < ArraySize(identities); i++)
{
XMPPIdentity *identity = ArrayGet(identities, i);
char *id_chunk = StrConcat(7,
identity->category, "/",
identity->type, "/",
identity->lang, "/",
identity->name);
char *tmp = S;
S = StrConcat(3, S, id_chunk, "<");
Free(tmp);
Free(id_chunk);
}
ArraySort(features, ((int (*) (void *, void *)) ICollate));
for (i = 0; i < ArraySize(features); i++)
{
char *feature = ArrayGet(features, i);
char *tmp = S;
S = StrConcat(3, S, feature, "<");
Free(tmp);
}
Sha = Sha1(S);
Free(S);
S = Base64Encode((const char *) Sha, 20);
Free(Sha);
ArrayFree(features);
for (i = 0; i < ArraySize(identities); i++)
{
XMPPIdentity *identity = ArrayGet(identities, i);
/* We don't have to do anything here. */
Free(identity);
}
ArrayFree(identities);
return S;
}

View file

@ -3,7 +3,10 @@
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Base64.h>
@ -21,70 +24,6 @@
#include "XMPPThread/internal.h"
/* Generates a SHA-256 hash of the ver field. */
char *
XMPPGenerateVer(void)
{
char *S = NULL;
unsigned char *Sha = NULL;
Array *identities = ArrayCreate();
Array *features = ArrayCreate();
size_t i;
/* Initialise identity table, to be sorted */
#define IdentitySimple(cat, Type, Name) { \
XMPPIdentity *id = Malloc(sizeof(*id)); \
id->category = cat; \
id->lang = NULL; \
id->type = Type; \
id->name = Name; \
ArrayAdd(identities, id); }
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(feature) ArrayAdd(features, feature);
IQ_ADVERT
#undef AdvertiseSimple
ArraySort(identities, IdentitySort);
for (i = 0; i < ArraySize(identities); i++)
{
XMPPIdentity *identity = ArrayGet(identities, i);
char *id_chunk = StrConcat(7,
identity->category, "/",
identity->type, "/",
identity->lang, "/",
identity->name);
char *tmp = S;
S = StrConcat(3, S, id_chunk, "<");
Free(tmp);
Free(id_chunk);
}
ArraySort(features, ((int (*) (void *, void *)) ICollate));
for (i = 0; i < ArraySize(features); i++)
{
char *feature = ArrayGet(features, i);
char *tmp = S;
S = StrConcat(3, S, feature, "<");
Free(tmp);
}
Sha = Sha1(S);
Free(S);
S = Base64Encode((const char *) Sha, 20);
Free(Sha);
ArrayFree(features);
for (i = 0; i < ArraySize(identities); i++)
{
XMPPIdentity *identity = ArrayGet(identities, i);
/* We don't have to do anything here. */
Free(identity);
}
ArrayFree(identities);
return S;
}
XMLElement *
RetrieveStanza(XMPPThread *thread)
{
@ -152,11 +91,11 @@ XMPPDispatcher(void *argp)
}
typedef struct XMPPAwait {
pthread_mutex_t cond_lock;
pthread_cond_t condition;
XMLElement *stanza;
bool usable;
XMLElement *stanza;
pthread_mutex_t cond_lock;
pthread_cond_t condition;
} XMPPAwait;
static pthread_mutex_t await_lock = PTHREAD_MUTEX_INITIALIZER;
@ -243,7 +182,7 @@ ParseeXMPPThread(void *argp)
{
pthread_mutex_lock(&await->cond_lock);
await->stanza = stanza;
pthread_cond_broadcast(&await->condition);
pthread_cond_signal(&await->condition);
pthread_mutex_unlock(&await->cond_lock);
HashMapDelete(await_table, id);
@ -286,25 +225,37 @@ ParseeXMPPThread(void *argp)
return NULL;
}
#include <time.h>
#include <errno.h>
/* TODO: This function does NIT behave well. */
XMLElement *
ParseeAwaitStanza(char *identifier, int64_t timeout)
{
/* TODO: Pthreads HATE me using Malloc here, so I'm abusing stackspace.
* Not *too much* of a problem, just a weird oddity. If anyone has a clue
* on why that happens (at least on ARM64 with a Pi4 running Debian), let
* me know! */
XMPPAwait awa;
XMPPAwait *awa;
XMLElement *stanza;
struct timespec ts;
if (!identifier)
{
return NULL;
}
/* XXX: You may be wondering _why_ I am using stdlib's malloc, instead of
* Cytoplasm's Malloc wrapper(which are essentially the same thing, plus
* some memchecking as of now), or the stack, as done previously.
* The reasons are that:
* - Cytoplasm's heap causes a fun bus error on ARM64. I have _no clue_ why,
* since, as I said, it is as of now just malloc with extra metadata prepended
* to it. A _guess_ may be that the metadata makes everything unaligned, which,
* just sounds silly. pthreads should be able to cope fine with that, I think.
* - The stack seems to be a prime candidate to cause a stack corruption, which
* puzzled me for a comically long while("why is the value being swapped for
* the key?" "why does duplicating twice cause the former problem to disappear,
* but still obviously creating another one?", ...).
*
* I am open to issues talking about such fun behaviour and ideas to fix this.
* This is a bodge, and potentially a problem, or vulnerability with Cytoplasm/
* Parsee. As long as it isn't "use Rust". Please don't say that. */
awa = malloc(sizeof(XMPPAwait));
/* Convert into an absolute timeout.
* =================================
* XXX: For anyone using timespecs: MAKE ABSOLUTELY FUCKING SURE YOUR NANOS
@ -327,30 +278,33 @@ ParseeAwaitStanza(char *identifier, int64_t timeout)
ts.tv_nsec = nsc_delta;
ts.tv_sec += sec_delta;
}
(void) msecond;
}
pthread_mutex_lock(&await_lock);
pthread_cond_init(&awa.condition, NULL);
pthread_mutex_init(&awa.cond_lock, NULL);
awa.stanza = NULL;
pthread_cond_init(&awa->condition, NULL);
pthread_mutex_init(&awa->cond_lock, NULL);
awa->stanza = NULL;
HashMapSet(await_table, identifier, &awa);
if (HashMapGet(await_table, identifier))
{
HashMapDelete(await_table, identifier);
}
HashMapSet(await_table, identifier, awa);
pthread_mutex_unlock(&await_lock);
pthread_mutex_lock(&awa.cond_lock);
while (!awa.stanza)
pthread_mutex_lock(&awa->cond_lock);
while (!awa->stanza)
{
int code;
if (timeout <= 0)
{
pthread_cond_wait(&awa.condition, &awa.cond_lock);
pthread_cond_wait(&awa->condition, &awa->cond_lock);
continue;
}
code = pthread_cond_timedwait(&awa.condition, &awa.cond_lock, &ts);
code = pthread_cond_timedwait(&awa->condition, &awa->cond_lock, &ts);
if (code == ETIMEDOUT)
{
/* Timeout detected, give up regardless of the status of our
@ -361,22 +315,15 @@ ParseeAwaitStanza(char *identifier, int64_t timeout)
}
if (code == EINVAL)
{
Log(LOG_ERR, "=========== Achievement GET! ===========");
Log(LOG_ERR, "%s: TIMEDWAIT RETURNED EINVAL.", __func__);
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! ===========");
Achievement("TIMEDWAIT RETURNED WEIRD EINVAL", true);
}
}
stanza = awa.stanza;
pthread_mutex_unlock(&awa.cond_lock);
stanza = awa->stanza;
pthread_mutex_unlock(&awa->cond_lock);
pthread_cond_destroy(&awa.condition);
pthread_mutex_destroy(&awa.cond_lock);
pthread_cond_destroy(&awa->condition);
pthread_mutex_destroy(&awa->cond_lock);
free(awa);
return stanza;
}

View file

@ -148,11 +148,11 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
return false;
}
/* TODO: On semi-anonymous MUCs, it might be preferable to use a
* form of the occupant ID as the base, as it is more unique, and
* less prone to trigger the character limit on Matrix.
*
* See: https://xmpp.org/extensions/xep-0421.html */
if (ServerHasXEP421(args, HashMapGet(stanza->attrs, "from")))
{
}
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
decode_from = ParseeLookupJID(from);
from_matrix = ParseeEncodeJID(args->config, decode_from, true);
@ -198,16 +198,9 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
if (mroom_id && !XMPPIsParseeStanza(stanza))
{
char *res = ParseeGetResource(from);
char *encoded = ParseeEncodeJID(args->config, decode_from, false);
char *encoded = ParseeGetBridgedUser(args, stanza);
char *event_id = NULL;
bool chat = false;
if (StrEquals(HashMapGet(stanza->attrs, "type"), "chat"))
{
Free(encoded);
encoded = StrDuplicate(from_matrix);
chat = true;
}
bool chat = StrEquals(HashMapGet(stanza->attrs, "type"), "chat");
{
char *parsee = ParseeJID(args);

View file

@ -58,20 +58,24 @@ struct XMPPThread {
XMPPThreadInfo *info;
};
extern int ICollate(unsigned char *cata, unsigned char *catb);
extern int IdentitySort(void *idap, void *idbp);
int ICollate(unsigned char *cata, unsigned char *catb);
int IdentitySort(void *idap, void *idbp);
extern char * ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza);
extern char * ParseeGetEventFromID(ParseeData *data, XMLElement *stanza, char *id);
extern char * ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza);
extern void ParseePushAllStanza(ParseeData *args, XMLElement *stanza, char *event);
extern bool ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza);
char * ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza);
char * ParseeGetEventFromID(ParseeData *data, XMLElement *stanza, char *id);
char * ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza);
void ParseePushAllStanza(ParseeData *args, XMLElement *stanza, char *event);
bool ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza);
extern HashMap * ShoveStanza(HashMap *content, XMLElement *stanza);
extern void ManageProfileItem(ParseeData *args, XMLElement *item, XMLElement *stanza, XMPPThread *thr);
extern XMLElement * CreatePubsubRequest(char *from, char *to, char *node);
HashMap * ShoveStanza(HashMap *content, XMLElement *stanza);
void ManageProfileItem(ParseeData *args, XMLElement *item, XMLElement *stanza, XMPPThread *thr);
XMLElement * CreatePubsubRequest(char *from, char *to, char *node);
extern bool MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
extern void IQStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
extern void PresenceStanza(ParseeData *args, XMLElement *stanza);
bool MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
void IQStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
void PresenceStanza(ParseeData *args, XMLElement *stanza);
bool ServerHasXEP421(ParseeData *data, char *from);
char * ParseeGetBridgedUser(ParseeData *data, XMLElement *stanza);