[WIP/ADD] Try to separate discovery, make links nicer

This commit is contained in:
LDA 2024-12-19 20:59:12 +01:00
parent 2e566c73fc
commit 5ddc5d3e5c
15 changed files with 409 additions and 118 deletions

View file

@ -90,22 +90,6 @@ string_cat(char *in1, char *in2)
return out;
}
static char *
trim_nl(char *in)
{
char *tc;
if (!in)
{
return NULL;
}
while ((tc = strrchr(in, '\n')))
{
*tc = '\0';
}
return in;
}
typedef struct str_array {
char **values;
@ -115,6 +99,10 @@ static str_array_t *
str_array_create(void)
{
str_array_t *ret = malloc(sizeof(*ret));
if (!ret)
{
return NULL;
}
ret->values = NULL;
ret->quantity = 0;
@ -216,36 +204,6 @@ failure:
free(line);
return NULL;
}
static int
exec_code(char *program, char *argv[])
{
pid_t forkRet;
if (!program || !argv)
{
return -1;
}
forkRet = fork();
if (forkRet == 0)
{
/* Child */
execvp(program, argv);
exit(0);
}
else
{
/* Parent */
int status;
if (waitpid(forkRet, &status, 0) == -1)
{
return -1;
}
return status;
}
/* We're not meant to ever be there, but TCC is stupid. */
return -1;
}
static char *
strchrn(char *s, char c)
@ -253,17 +211,6 @@ strchrn(char *s, char c)
s = strchr(s, c);
return s ? s+1 : NULL;
}
static char *
strchrl(char *s, char c)
{
char *s1 = NULL;
while ((s = strchr(s, c)))
{
s1 = s;
s++;
}
return s1;
}
static str_array_t *
split(char *dir)
{
@ -512,8 +459,7 @@ main_build(int argc, char *argv[])
{
FILE *makefile;
char *repo = cmd_stdout("git remote get-url origin");
size_t size, i;
ssize_t nread;
size_t i;
bool with_static = false, with_lmdb = false;
int opt;
str_array_t *sources, *images, *utils, *aya;
@ -546,6 +492,13 @@ main_build(int argc, char *argv[])
}
makefile = fopen("Makefile", "w");
if (!makefile)
{
fprintf(stderr, "Couldn't create Makefile.\n");
fprintf(stderr, "This isn't good, actually.\n");
return EXIT_FAILURE;
}
fprintf(makefile, "# Autogenerated POSIX Makefile from Parsee\n");
fprintf(makefile, "# Ideally do not touch, unless you have a very ");
fprintf(makefile, "good reason to do it. \n\n");

View file

@ -72,12 +72,13 @@ ParseeCheckMatrix(void *datp)
Log(LOG_ERR, "Please check if your homeserver is active.");
Log(LOG_ERR, "%s shall now exit...", NAME);
/* TODO: SEGV! */
pthread_mutex_lock(&data->halt_lock);
data->halted = true;
pthread_mutex_unlock(&data->halt_lock);
XMPPFinishCompStream(data->jabber);
pthread_join(xmpp_thr, NULL);
//pthread_join(xmpp_thr, NULL);
Log(LOG_INFO, "Stopping server...");
HttpServerStop(data->server);
}

View file

@ -1,6 +1,9 @@
#include <Matrix.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Http.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <string.h>
@ -32,3 +35,34 @@ MatrixParseID(char *user)
return ret;
}
UserID *
MatrixParseIDFromMTO(Uri *uri)
{
UserID *id = NULL;
char *path, *params, *decoded;
if (!uri)
{
return NULL;
}
if (!StrEquals(uri->proto, "https") || !StrEquals(uri->host, "matrix.to"))
{
return NULL;
}
if (strncmp(uri->path, "/#/", 3))
{
return NULL;
}
path = StrDuplicate(uri->path + 3);
params = path ? strchr(path, '?') : NULL;
if (params)
{
*params = '\0';
}
decoded = HttpUrlDecode(path);
id = MatrixParseID(decoded);
Free(decoded);
Free(path);
return id;
}

View file

@ -27,6 +27,7 @@ ParseeInitData(XMPPComponent *comp)
data->config = ParseeConfigGet();
data->router = HttpRouterCreate();
data->jabber = comp;
data->muc = CreateMUCServer(data);
data->handler = CommandCreateRouter();
data->oid_servers = HashMapCreate();
@ -116,6 +117,7 @@ ParseeFreeData(ParseeData *data)
pthread_mutex_destroy(&data->halt_lock);
Free(data->id);
XMPPEndCompStream(data->jabber);
FreeMUCServer(data->muc);
DbClose(data->db);
HttpRouterFree(data->router);
CommandFreeRouter(data->handler);

View file

@ -5,11 +5,13 @@
#include <Cytoplasm/Http.h>
#include <Cytoplasm/Json.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Uri.h>
#include <stdbool.h>
#include <string.h>
#include <StringStream.h>
#include <Matrix.h>
#include <XML.h>
#include <AS.h>
@ -151,20 +153,42 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
else if (StrEquals(elem->name, "a"))
{
char *href = HashMapGet(elem->attrs, "href");
/* TODO: Check if the element here is a Matrix.TO
* pointing to a Parsee user. */
Concat("(");
for (i = 0; i < ArraySize(elem->children); i++)
Uri *pref = UriParse(href);
if (StrEquals(pref->host, "matrix.to"))
{
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
/* TODO: Check if the element here is a Matrix.TO
* pointing to a Parsee user. */
UserID *id = MatrixParseIDFromMTO(pref);
if (id)
{
/* TODO: Detect if it already is a Parsee user */
Concat("@");
Concat(id->localpart);
Concat(":");
Concat(id->server);
}
else
{
Concat(href);
}
Free(id);
}
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);
}
Concat(" < ");
Concat(href);
Concat(" >");
}
UriFree(pref);
}
else
{

View file

@ -10,6 +10,37 @@
#include <string.h>
static HttpClientContext *
TryDownload(ParseeData *data, char *server, char *identi)
{
HttpClientContext *cctx;
char *path;
server = HttpUrlEncode(server);
identi = HttpUrlEncode(identi);
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
ASAuthenticateRequest(data->config, cctx);
Free(path);
HttpRequestSendHeaders(cctx);
if (HttpRequestSend(cctx) != HTTP_OK)
{
Log(LOG_WARNING, "Failing back.");
HttpClientContextFree(cctx);
path = StrConcat(4, "/_matrix/client/v1/media/download/", server, "/", identi);
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
ASAuthenticateRequest(data->config, cctx);
Free(path);
HttpRequestSendHeaders(cctx);
HttpRequestSend(cctx);
}
Free(server);
Free(identi);
return cctx;
}
RouteHead(RouteMedia, arr, argp)
{
ParseeHttpArg *args = argp;
@ -44,15 +75,7 @@ RouteHead(RouteMedia, arr, argp)
/* Proxy the media through an authenticated endpoint if the HMAC
* is valid. */
server = HttpUrlEncode(server);
identi = HttpUrlEncode(identi);
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
cctx = ParseeCreateRequest(args->data->config, HTTP_GET, path);
ASAuthenticateRequest(args->data->config, cctx);
Free(path);
HttpRequestSendHeaders(cctx);
HttpRequestSend(cctx);
cctx = TryDownload(args->data, server, identi);
reqh = HttpResponseHeaders(cctx);
while (HashMapIterate(reqh, &key, (void **) &val))
{
@ -65,8 +88,6 @@ RouteHead(RouteMedia, arr, argp)
}
HttpClientContextFree(cctx);
Free(server);
Free(identi);
return NULL;
}

View file

@ -192,7 +192,7 @@ XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool
XMLAddChild(x, history);
XMLAddChild(presence, x);
features = CreateIQFeatures();
features = LookupJIDFeatures(from);
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
IQ_IDENTITY
#undef IdentitySimple
@ -258,7 +258,7 @@ XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
XMLAddChild(presence, status);
}
features = CreateIQFeatures();
features = LookupJIDFeatures(from);
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
IQ_IDENTITY
#undef IdentitySimple

153
src/XMPP/MUCServ.c Normal file
View file

@ -0,0 +1,153 @@
#include <MUCServ.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <pthread.h>
#include <string.h>
#define MUCNS "http://jabber.org/protocol/muc"
struct MUCServer {
pthread_mutex_t mut;
ParseeData *data;
};
static char *
GetMUCName(char *jid)
{
char *name, *at;
size_t len;
if (!jid || *jid != '#')
{
return NULL;
}
jid++;
at = strchr(jid, '@');
if (!at)
{
return StrDuplicate(jid);
}
len = at - jid;
name = Malloc(len + 1);
memset(name, '\0', len + 1);
memcpy(name, jid, len);
return name;
}
MUCServer *
CreateMUCServer(ParseeData *data)
{
MUCServer *server;
if (!data)
{
return NULL;
}
server = Malloc(sizeof(*server));
pthread_mutex_init(&server->mut, NULL);
server->data = data;
return server;
}
static bool
MUCManagePresence(MUCServer *serv, XMLElement *stanza, char *from, char *to)
{
char *name;
XMLElement *x = XMLookForTKV(stanza, "x", "xmlns", MUCNS);
char *type = HashMapGet(stanza->attrs, "type");
if (x && !type)
{
/* The user is trying to join the MUC */
name = GetMUCName(to);
Log(LOG_WARNING, "%s is trying to join MUC '%s'", from, name);
/* TODO: Check if the user should be joining. If so, make them.
* Implementing MUCs is gonna be fun. */
/* TODO: Presence broadcast hell. */
Free(name);
}
return false;
}
static bool
MUCManageMessage(MUCServer *serv, XMLElement *stanza, char *from, char *to)
{
Log(LOG_WARNING, "MUCSERV: got a message %s->%s", from, to);
return false;
}
bool
ManageMUCStanza(MUCServer *serv, XMLElement *stanza)
{
char *stype, *from, *to;
bool ret;
if (!serv || !stanza)
{
return false;
}
from = HashMapGet(stanza->attrs, "from");
to = HashMapGet(stanza->attrs, "to");
stype = stanza->name;
if (to && *to != '#')
{
/* We aren't interacting with a MUC at all. Don't do anything. */
return false;
}
if (StrEquals(stype, "iq"))
{
/* TODO: Worry about IQ later */
return false;
}
ret = false;
pthread_mutex_lock(&serv->mut);
if (StrEquals(stype, "presence"))
{
ret = MUCManagePresence(serv, stanza, from, to);
}
else if (StrEquals(stype, "message"))
{
ret = MUCManageMessage(serv, stanza, from, to);
}
/* TODO: Do stuff while locked */
pthread_mutex_unlock(&serv->mut);
/* TODO: Verify the destination, and make sure we aren't doing
* anything stupid(especially with discovery) */
return false;
}
bool
MUCServerExists(MUCServer *serv, char *muc)
{
if (!serv || !muc)
{
return false;
}
return false;
}
void
FreeMUCServer(MUCServer *serv)
{
if (!serv)
{
return;
}
pthread_mutex_lock(&serv->mut);
/* TODO */
pthread_mutex_unlock(&serv->mut);
pthread_mutex_destroy(&serv->mut);
Free(serv);
}

View file

@ -3,6 +3,7 @@
#include <Cytoplasm/Util.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Sha.h>
#include <Cytoplasm/Log.h>
#include <StringStream.h>
#include <XMPPCommand.h>
@ -142,3 +143,39 @@ XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features)
XMLAddChild(presence, c);
}
IQFeatures *
LookupJIDFeatures(char *jid)
{
IQFeatures *features;
if (!jid)
{
return NULL;
}
features = CreateIQFeatures();
if (*jid == '#')
{
/* This is a MUC. As such, we need to advertise MUCs */
#define ID(...) AddIQIdentity(features, __VA_ARGS__)
#define AD(var) AdvertiseIQFeature(features, var)
ID("gateway", NULL, "matrix", "Parsee MUC gateway");
ID("conference", NULL, "text", "Parsee MUC gateway");
ID("component", NULL, "generic", "Parsee component");
AD("http://jabber.org/protocol/muc");
#undef AD
#undef ID
}
else
{
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
IQ_ADVERT
#undef AdvertiseSimple
}
return features;
}

View file

@ -65,6 +65,12 @@ XMPPDispatcher(void *argp)
Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name);
}
if (ManageMUCStanza(args->muc, stanza))
{
XMLFreeElement(stanza);
continue;
}
if (StrEquals(stanza->name, "presence"))
{
PresenceStanza(args, stanza, thread);

View file

@ -115,33 +115,37 @@ end:
#define DISCO "http://jabber.org/protocol/disco#info"
static XMLElement *
IQGenerateQuery(void)
IQGenerateQuery(IQFeatures *features)
{
XMLElement *query = XMLCreateTag("query");
XMLElement *query;
if (!features)
{
return NULL;
}
query = XMLCreateTag("query");
XMLAddAttr(query, "xmlns", DISCO);
{
XMLElement *feature;
#define IdentitySimple(c,t,n) do \
{ \
feature = XMLCreateTag("identity"); \
XMLAddAttr(feature, "category", c); \
XMLAddAttr(feature, "type", t); \
XMLAddAttr(feature, "name", n); \
XMLAddChild(query, feature); \
} \
while (0);
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(f) do \
{ \
feature = XMLCreateTag("feature"); \
XMLAddAttr(feature, "var", f); \
XMLAddChild(query, feature); \
} \
while (0);
size_t i;
for (i = 0; i < ArraySize(features->identity); i++)
{
XMPPIdentity *identity = ArrayGet(features->identity, i);
IQ_ADVERT
#undef AdvertiseSimple
feature = XMLCreateTag("identity");
XMLAddAttr(feature, "category", identity->category);
XMLAddAttr(feature, "type", identity->type);
XMLAddAttr(feature, "name", identity->name);
XMLAddChild(query, feature);
}
for (i = 0; i < ArraySize(features->adverts); i++)
{
char *var = ArrayGet(features->adverts, i);
feature = XMLCreateTag("feature");
XMLAddAttr(feature, "var", var);
XMLAddChild(query, feature);
}
}
return query;
@ -151,7 +155,7 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
{
char *from, *to, *id;
XMLElement *iq_reply, *query;
IQFeatures *features = CreateIQFeatures();
IQFeatures *features;
from = HashMapGet(stanza->attrs, "from");
to = HashMapGet(stanza->attrs, "to");
@ -164,13 +168,8 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
XMLAddAttr(iq_reply, "type", "result");
XMLAddAttr(iq_reply, "id", id);
query = IQGenerateQuery();
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
IQ_ADVERT
#undef AdvertiseSimple
features = LookupJIDFeatures(to);
query = IQGenerateQuery(features);
{
char *ver = XMPPGenerateVer(features);
char *node = StrConcat(3, REPOSITORY, "#", ver);
@ -599,10 +598,14 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
}
else
{
char *buf = NULL;
Stream *s = StrStreamWriter(&buf);
Log(LOG_WARNING, "Unknown I/Q received:");
XMLEncode(StreamStdout(), stanza);
StreamPrintf(StreamStdout(),"\n");
StreamFlush(StreamStdout());
XMLEncode(s, stanza);
StreamFlush(s);
StreamClose(s);
Log(LOG_WARNING, "%s", buf);
Free(buf);
}
}

View file

@ -74,6 +74,14 @@ void AddIQIdentity(IQFeatures *, char *category, char *lang, char *type, char *n
void AdvertiseIQFeature(IQFeatures *, char *feature);
void FreeIQFeatures(IQFeatures *);
/**
* Looks up the features supported by a JID
* ----------------------
* Returns: an IQ featureset[LA:HEAP] | NULL
* Thrasher: FreeIQFeatures
* Modifies: NOTHING */
IQFeatures * LookupJIDFeatures(char *jid);
/** Generate the B64-encoded SHA-256 hash for the 'ver' field in caps.
* --------
* Returns: A base64 encoded ver hash[LA:HEAP]

39
src/include/MUCServ.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef PARSEE_MUCSERV_H
#define PARSEE_MUCSERV_H
/*-* An easy interface for handling MUCs.
*/
typedef struct MUCServer MUCServer;
#include <Parsee.h>
#include <stdbool.h>
/** Creates a MUC server handle given a ParseeData handle.
* This handle shall be used for all MUC-related operations.
* ----------------------------------------------------------
* Returns: a MUC server handle[HEAP] | NULL
* Thrasher: FreeMUCServer
* See-Also: https://xmpp.org/extensions/xep-0045.html */
extern MUCServer * CreateMUCServer(ParseeData *data);
/** Manages a stanza from a MUC, and returns true if the stanza
* was actually processed by the MUC server.
* -------------------------------------------------------------
* See-Also: CreateMUCServer */
extern bool ManageMUCStanza(MUCServer *serv, XMLElement *stanza);
/** Checks if a MUC(from its localpart, so
* <code>'#foobar@bridge.blow.hole' -> 'foobar'</code>) exists.
* --------------
* Returns: whenever the MUC exists */
extern bool MUCServerExists(MUCServer *serv, char *muc);
/** Destroys all memory and references from a MUC server handle.
* --------------
* Thrashes: CreateMUCServer
* See-Also: https://xmpp.org/extensions/xep-0045.html */
extern void FreeMUCServer(MUCServer *serv);
#endif

View file

@ -2,6 +2,7 @@
#define PARSEE_MATRIX_H
#include <Cytoplasm/Json.h>
#include <Cytoplasm/Uri.h>
#include "FileInfo.h"
@ -20,6 +21,12 @@ typedef struct UserID {
* Thrasher: Free */
extern UserID * MatrixParseID(char *user);
/** Attempts to parse a Matrix ID from a matrix.to URL.
* -------------------------------------------------
* Returns: a valid user ID[HEAP] | NULL
* Thrasher: Free */
extern UserID * MatrixParseIDFromMTO(Uri *uri);
/* Creates an error message JSON, with the specified code and message. */
extern HashMap * MatrixCreateError(char *err, char *msg);

View file

@ -18,6 +18,8 @@ typedef struct ParseeData ParseeData;
#include <Command.h>
#include <XMPP.h>
#include <MUCServ.h>
#define PARSEE_VERBOSE_NONE 0
#define PARSEE_VERBOSE_LOG 1
#define PARSEE_VERBOSE_TIMINGS 2
@ -62,6 +64,7 @@ typedef struct ParseeData {
HttpServer *server;
XMPPComponent *jabber;
MUCServer *muc;
Db *db;
char *id;