[MOD/WIP] Start adding the server parsing code

Might need some clean-up, and also we'll need to refactor the
User API to use CommonIDs instead
This commit is contained in:
lda 2023-11-29 01:17:29 +01:00
parent 4e7554d241
commit 8eab884289
3 changed files with 231 additions and 20 deletions

View file

@ -24,12 +24,18 @@
#include <Parser.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Int.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
/* Iterate through a char **. */
#define Iterate(s) (*(*s)++)
/* Parse an extended localpart */
static int
ParseUserLocalpart(char **str, char **out)
@ -45,21 +51,187 @@ ParseUserLocalpart(char **str, char **out)
/* An extended localpart contains every ASCII printable character,
* except an ':'. */
start = *str;
while (isascii((c = (*(*str)++))) && c != ':' && c)
while (isascii((c = Iterate(str))) && c != ':' && c)
{
/* Do nothing */
}
length = (size_t) (*str - start) - 1;
if (c != ':' || length < 1)
if (length < 1)
{
*str = start;
return 0;
}
if (c == ':')
{
--(*str);
}
*out = Malloc(length + 1);
memcpy(*out, start, length);
*out[length] = '\0';
(*out)[length] = '\0';
return 1;
}
/* Parses an IPv4 address. */
static int
ParseIPv4(char **str, char **out)
{
/* Be *very* careful with this buffer */
char buffer[4];
char *start;
size_t length;
char c;
int digit = 0;
int digits = 0;
memset(buffer, '\0', 4);
start = *str;
/* An IPv4 address is made of 4 blocks between 1-3 digits, like so:
* (1-3)*DIGIT.(1-3)*DIGIT.(1-3)*DIGIT.(1-3)*DIGIT */
while ((isdigit(c = Iterate(str)) || c == '.') && c && digits < 4)
{
if (isdigit(c))
{
digit++;
continue;
}
if (digit < 1 || digit > 3)
{
/* Current digit is too long for the spec! */
*str = start;
return 0;
}
memcpy(buffer, *str - digit - 1, digit);
if (atoi(buffer) > 255)
{
/* Current digit is too large for the spec! */
*str = start;
return 0;
}
memset(buffer, '\0', 4);
digit = 0;
digits++; /* We have parsed a digit. */
}
if (c == '.' || digits != 3)
{
*str = start;
return 0;
}
length = (size_t) (*str - start) - 1;
*out = Malloc(length + 1);
memcpy(*out, start, length);
(*str)--;
return 1;
}
static int
ParseIPv6(char **str, char **out)
{
/* TODO */
(void) str;
(void) out;
return 0;
}
static int
ParseHostname(char **str, char **out)
{
char *start;
size_t length = 0;
char c;
start = *str;
while ((c = Iterate(str)) &&
(isalnum(c) || c == '.' || c == '-') &&
++length < 256)
{
/* Do nothing. */
}
if (length < 1 || length > 255)
{
*str = start;
return 0;
}
length = (size_t) (*str - start) - 1;
*out = Malloc(length + 1);
memcpy(*out, start, length);
(*str)--;
return 1;
}
static int
ParseServerName(char **str, ServerPart *out)
{
char c;
char *start;
char *startPort;
size_t chars = 0;
char *host = NULL;
char *port = NULL;
if (!str || !out)
{
return 0;
}
start = *str;
if (!host)
{
/* If we can parse an IPv4 address, use that. */
ParseIPv4(str, &host);
}
if (!host)
{
/* If we can parse an IPv6 address, use that. */
ParseIPv6(str, &host);
}
if (!host)
{
/* If we can parse an hostname, use that. */
ParseHostname(str, &host);
}
if (!host)
{
/* Can't parse a valid server name. */
return 0;
}
/* Now, there's only 2 options: a ':', or the end(everything else.) */
if (**str != ':')
{
/* We're done. */
out->hostname = host;
out->port = NULL;
return 1;
}
/* TODO: Separate this out */
startPort = ++(*str);
while(isdigit(c = Iterate(str)) && c && ++chars < 5)
{
/* Do nothing. */
}
if (chars < 1 || chars > 5)
{
*str = start;
Free(host);
host = NULL;
return 0;
}
port = Malloc(chars + 1);
memcpy(port, startPort, chars);
port[chars] = '\0';
if (atol(port) > 65535)
{
Free(port);
Free(host);
*str = start;
return 0;
}
out->hostname = host;
out->port = port;
return 1;
}
@ -89,6 +261,9 @@ ParseCommonID(char *str, CommonID *id)
return 0;
}
id->sigil = sigil;
id->local = NULL;
id->server.hostname = NULL;
id->server.port = NULL;
switch (sigil)
{
@ -97,9 +272,37 @@ ParseCommonID(char *str, CommonID *id)
{
return 0;
}
if (*str++ != ':')
{
Free(id->local);
id->local = NULL;
return 0;
}
/* TODO: Match whenever str is valid. */
id->server = StrDuplicate(str);
if (!ParseServerName(&str, &id->server))
{
Free(id->local);
id->local = NULL;
return 0;
}
break;
}
return 1;
}
void
CommonIDFree(CommonID id)
{
if (id.local)
{
Free(id.local);
}
if (id.server.hostname)
{
Free(id.server.hostname);
}
if (id.server.port)
{
Free(id.server.port);
}
}

View file

@ -909,29 +909,30 @@ UserIdParse(char *id, char *defaultServer)
/* Fully-qualified user ID */
if (*id == '@')
{
/* TODO: Just use the CommonID. */
CommonID commonID;
commonID.sigil = '\0';
commonID.local = NULL;
commonID.server = NULL;
if (!ParseCommonID(id, &commonID) || !commonID.server)
commonID.server.hostname = NULL;
commonID.server.port = NULL;
if (!ParseCommonID(id, &commonID) || !commonID.server.hostname)
{
Free(userId);
Free(commonID.local);
Free(commonID.server);
userId = NULL;
goto finish;
if (commonID.server.hostname)
{
Free(commonID.server.hostname);
}
if (*commonID.server == '\0')
{
Free(userId);
Free(commonID.local);
Free(commonID.server);
userId = NULL;
goto finish;
}
userId->localpart = commonID.local;
userId->server = commonID.server;
userId->server = commonID.server.hostname;
if (commonID.server.port)
{
Free(commonID.server.port);
}
}
else
{

View file

@ -36,6 +36,13 @@
* Matrix specification
*/
/**
* The host[:port] format in a servername.
*/
typedef struct ServerPart {
char *hostname;
char *port;
} ServerPart;
/**
* A common identifier in the form '&local[:server]', where
* & determines the *type* of the identifier.
@ -43,7 +50,7 @@
typedef struct CommonID {
char sigil;
char *local;
char *server; /* Might be NULL for some sigils(e.g: room IDs >= v3) */
ServerPart server;
} CommonID;
/**