diff --git a/src/AS.c b/src/AS.c index 6705f21..7046d39 100644 --- a/src/AS.c +++ b/src/AS.c @@ -54,3 +54,133 @@ ASAuthenticateRequest(ParseeConfig *data, HttpClientContext *ctx) HttpRequestHeader(ctx, "Authorization", bearer); Free(bearer); } +bool +ASRegisterUser(ParseeConfig *conf, char *user) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + HttpStatus status; + if (!conf || !user) + { + return false; + } + + /* Create user. We don't actually care about the value as we can + * masquerade, as long as it exists. */ + ctx = ParseeCreateRequest( + conf, + HTTP_POST, "/_matrix/client/v3/register" + ); + json = HashMapCreate(); + + HashMapSet(json,"type",JsonValueString("m.login.application_service")); + HashMapSet(json,"username",JsonValueString(user)); + + ASAuthenticateRequest(conf, ctx); + status = ParseeSetRequestJSON(ctx, json); + HttpClientContextFree(ctx); + JsonFree(json); + + return status == HTTP_OK; +} + +void +ASPing(ParseeConfig *conf) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL; + char *path; + if (!conf) + { + return; + } + + Log(LOG_NOTICE, "Pinging..."); + path = StrConcat(3, + "/_matrix/client/v1/appservice/", + "Parsee%20XMPP", + "/ping" + ); + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + HttpClientContextFree(ctx); + JsonFree(json); +} +void +ASJoin(ParseeConfig *conf, char *id, char *masquerade) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL, *params_obj; + char *path, *params; + if (!conf || !id) + { + return; + } + + params_obj = HashMapCreate(); + if (masquerade) + { + HashMapSet(params_obj, "user_id", masquerade); + } + params = HttpParamEncode(params_obj); + HashMapFree(params_obj); + path = StrConcat(4, + "/_matrix/client/v3/rooms/", id, "/join?", + params + ); + Free(params); + + ctx = ParseeCreateRequest( + conf, + HTTP_POST, path + ); + Free(path); + json = HashMapCreate(); + ASAuthenticateRequest(conf, ctx); + ParseeSetRequestJSON(ctx, json); + HttpClientContextFree(ctx); + JsonFree(json); +} +void +ASSend(ParseeConfig *conf, char *id, char *user, char *type, HashMap *c) +{ + HttpClientContext *ctx = NULL; + HashMap *json = NULL, *params_obj; + char *path, *params; + char *txn; + if (!conf || !id || !type || !c) + { + return; + } + + txn = StrRandom(16); + params_obj = HashMapCreate(); + if (user) + { + HashMapSet(params_obj, "user_id", user); + } + params = HttpParamEncode(params_obj); + HashMapFree(params_obj); + path = StrConcat(8, + "/_matrix/client/v3/rooms/", + id, "/send/", type, "/", txn, "?", + params + ); + Log(LOG_INFO, "Sending %s", path); + Free(params); + Free(txn); + + ctx = ParseeCreateRequest(conf, HTTP_PUT, path); + Free(path); + json = HashMapCreate(); + ASAuthenticateRequest(conf, ctx); + Log(LOG_INFO, "%d", ParseeSetRequestJSON(ctx, c)); + HttpClientContextFree(ctx); + JsonFree(c); +} diff --git a/src/Events.c b/src/Events.c new file mode 100644 index 0000000..9e4a620 --- /dev/null +++ b/src/Events.c @@ -0,0 +1,17 @@ +#include + +HashMap * +MatrixCreateNotice(char *body) +{ + HashMap *map; + if (!body) + { + return NULL; + } + + map = HashMapCreate(); + HashMapSet(map, "msgtype", JsonValueString("m.notice")); + HashMapSet(map, "body", JsonValueString(body)); + + return map; +} diff --git a/src/Main.c b/src/Main.c index 80e56cf..b7367db 100644 --- a/src/Main.c +++ b/src/Main.c @@ -4,6 +4,7 @@ #include #include +#include #include int @@ -12,7 +13,7 @@ Main(void) HttpServer *server = NULL; HttpServerConfig conf; ParseeData *data; - const ParseeConfig *parseeConf; + const ParseeConfig *parsee_conf; Stream *yaml; Log(LOG_INFO, "%s - v%s", NAME, VERSION); @@ -23,29 +24,14 @@ Main(void) ParseeExportConfigYAML(yaml); StreamClose(yaml); - parseeConf = ParseeConfigGet(); - Log(LOG_INFO, "HS token: %s", parseeConf->hs_token); + parsee_conf = ParseeConfigGet(); + Log(LOG_INFO, "HS token: %s", parsee_conf->hs_token); + + ASRegisterUser(parsee_conf, parsee_conf->sender_localpart); - { - /* Create user. We don't actually care about the value as we can - * masquerade, as long as it exists. */ - HttpClientContext *ctx = ParseeCreateRequest( - parseeConf, - HTTP_POST, "/_matrix/client/v3/register" - ); - HashMap *json = HashMapCreate(); - - HashMapSet(json,"type",JsonValueString("m.login.application_service")); - HashMapSet(json,"username",JsonValueString(parseeConf->sender_localpart)); - - ASAuthenticateRequest(parseeConf, ctx); - Log(LOG_INFO, "Got %d", ParseeSetRequestJSON(ctx, json)); - HttpClientContextFree(ctx); - JsonFree(json); - } memset(&conf, 0, sizeof(conf)); - conf.port = parseeConf->port; + conf.port = parsee_conf->port; conf.threads = 4; conf.maxConnections = 32; conf.handlerArgs = ParseeInitData(); @@ -54,6 +40,8 @@ Main(void) /* TODO: The rest of Parsee. */ server = HttpServerCreate(&conf); HttpServerStart(server); + + /* TODO: Manage signals(^C) with finesse */ HttpServerJoin(server); HttpServerFree(server); diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c new file mode 100644 index 0000000..503872c --- /dev/null +++ b/src/MatrixEventHandler.c @@ -0,0 +1,69 @@ +#include + +#include +#include +#include + +#include +#include + +#define GrabString(obj, ...) JsonValueAsString(JsonGet(obj, __VA_ARGS__)) +static void +ParseeMemberHandler(const ParseeConfig *conf, HashMap *event) +{ + char *state_key = GrabString(event, 1, "state_key"); + char *membership = GrabString(event, 2, "content", "membership"); + char *room_id = GrabString(event, 1, "room_id"); + Log(LOG_INFO, "Membership '%s'->'%s'", state_key, membership); + + if (StrEquals(membership, "invite") && ParseeIsPuppet(conf, state_key)) + { + Log(LOG_INFO, "Looks like %s was invited to %s", + state_key, + GrabString(event, 1, "room_id") + ); + ASJoin(conf, room_id, state_key); + } +} +static void +ParseeMessageHandler(const ParseeConfig *conf, HashMap *event) +{ + char *msgtype = GrabString(event, 2, "content", "msgtype"); + char *body = GrabString(event, 2, "content", "body"); + char *id = GrabString(event, 1, "room_id"); + if (StrEquals(body, "!help")) + { + Log(LOG_ERR, "Not implemented!"); + ASSend(conf, id, NULL, "m.room.message", + MatrixCreateNotice("No help, pal.") + ); + } +} + +void +ParseeEventHandler(const ParseeConfig *conf, HashMap *event) +{ + char *event_type; + if (!conf || !event) + { + return; + } + + Log(LOG_INFO, "Event by '%s', with type '%s'", + JsonValueAsString(HashMapGet(event, "sender")), + JsonValueAsString(HashMapGet(event, "type")) + ); + + event_type = GrabString(event, 1, "type"); + if (StrEquals(event_type, "m.room.member")) + { + ParseeMemberHandler(conf, event); + return; + } + if (StrEquals(event_type, "m.room.message")) + { + ParseeMessageHandler(conf, event); + return; + } +} + diff --git a/src/ParseeUser.c b/src/ParseeUser.c new file mode 100644 index 0000000..526ecef --- /dev/null +++ b/src/ParseeUser.c @@ -0,0 +1,32 @@ +#include + +#include + +bool +ParseeIsPuppet(const ParseeConfig *conf, char *user) +{ + char *localpart; + bool flag = true; + size_t len; + if (!user || !conf || *user != '@') + { + return false; + } + + localpart = user + 1; + len = strlen(conf->namespace_base); + if (strncmp(localpart, conf->namespace_base, len)) + { + flag = false; + } + + len = strlen(conf->sender_localpart); + if (!strncmp(localpart, conf->sender_localpart, len)) + { + flag = true; + } + + /* TODO: Check serverpart. 2 Parsee instances may be running in the same + * room. */ + return flag; +} diff --git a/src/Routes/Ping.c b/src/Routes/Ping.c new file mode 100644 index 0000000..88dc533 --- /dev/null +++ b/src/Routes/Ping.c @@ -0,0 +1,37 @@ +#include + +#include + +#include +#include + +RouteHead(RoutePing, arr, argp) +{ + ParseeHttpArg *args = argp; + HashMap *request = NULL; + HashMap *response = NULL; + + response = ASVerifyRequest(args); + if (response) + { + goto end; + } + if (HttpRequestMethodGet(args->ctx) != HTTP_POST) + { + HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED); + response = MatrixCreateError( + "M_UNRECOGNIZED", + "Path /ping only accepts POST as a valid method." + ); + goto end; + } + Log(LOG_INFO, "Pong!"); + + RequestJSON(); + + /* TODO: Load ping info */ + response = HashMapCreate(); +end: + JsonFree(request); + return response; +} diff --git a/src/Routes/Root.c b/src/Routes/Root.c index 1d47bdf..74fcecb 100644 --- a/src/Routes/Root.c +++ b/src/Routes/Root.c @@ -1,9 +1,13 @@ #include +#include + RouteHead(RouteRoot, arr, argp) { ParseeHttpArg *args = argp; + ASPing(args->data->config); + HttpResponseHeader(args->ctx, "Content-Type", "text/html"); HttpSendHeaders(args->ctx); StreamPrintf(args->stream, ""); diff --git a/src/Routes/Transactions.c b/src/Routes/Transactions.c index 61524a5..182dba1 100644 --- a/src/Routes/Transactions.c +++ b/src/Routes/Transactions.c @@ -38,11 +38,11 @@ RouteHead(RouteTxns, arr, argp) for (i = 0; i < ArraySize(events); i++) { HashMap *event = JsonValueAsObject(ArrayGet(events, i)); - Log(LOG_INFO, "Event by '%s', with type '%s'", - JsonValueAsString(HashMapGet(event, "sender")), - JsonValueAsString(HashMapGet(event, "type")) - ); + ParseeEventHandler(args->data->config, event); } + + /* TODO: Store TXN ID somewhere so that we can commit + * "Epic Matrix Idempotency" */ Log(LOG_INFO, "OK!"); response = HashMapCreate(); diff --git a/src/XML/SAX.c b/src/XML/SAX.c new file mode 100644 index 0000000..82c101c --- /dev/null +++ b/src/XML/SAX.c @@ -0,0 +1,702 @@ +#include + +#include +#include +#include +#include +#include + +#include +#include + +struct XMLexer { + Stream *stream; + bool autofree; + + /* A temporary buffer */ + int *buffer; + ssize_t length; + ssize_t top; + + enum { + XML_STATE_NONE = 0, + XML_STATE_COMMENT, + + XML_STATE_ATTR, + XML_STATE_ATTRHEAD_PROP, + XML_STATE_ATTRTAIL + } state; + + struct { + Array *elements; + char *str; + } data; +}; + +/* "Looks ahead" in the XML stream for a NUL-terminated string. + * If it was found and skip is set, then also skips over it. */ +static bool XMLookahead(XMLexer *lexer, const char *str, bool skip); + +/* Parses an XML "name" */ +static bool XMLIsStart(XMLexer *lexer); +static char * XMLParseName(XMLexer *lexer); +static bool XMLSkipSpace(XMLexer *lexer); +static char * XMLParseAttValue(XMLexer *lexer); + +static ssize_t XMLInitialiseBuffer(XMLexer *lexer); +static int XMLGetc(XMLexer *lexer); +static void XMLUngetc(XMLexer *lexer, int ch); +static void XMLEndBuffer(XMLexer *lexer); +static void XMLRollback(XMLexer *lexer); +static void XMLReset(XMLexer *lexer, ssize_t loc); +static char * XMLStringify(XMLexer *lexer, ssize_t point); +static HashMap * XMLReadProps(XMLexer *l); +static void XMLPushElement(XMLexer *lexer, char *e); +static char * XMLPopElement(XMLexer *lexer); + +static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs); +static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs); +static XMLEvent * XMLCreateRelax(XMLexer *lexer); +static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end); +static XMLEvent * XMLCreateData(XMLexer *lexer); + +XMLexer * +XMLCreateLexer(Stream *stream, bool autofree) +{ + XMLexer *lexer; + if (!stream) + { + return NULL; + } + + lexer = Malloc(sizeof(*lexer)); + lexer->stream = stream; + lexer->autofree = autofree; + lexer->state = XML_STATE_NONE; + + lexer->buffer = NULL; + lexer->length = -1; + lexer->top = -1; + + lexer->data.elements = ArrayCreate(); + lexer->data.str = NULL; + + return lexer; +} +void +XMLFreeLexer(XMLexer *lexer) +{ + if (!lexer) + { + return; + } + + if (lexer->autofree) + { + StreamClose(lexer->stream); + } + if (lexer->buffer) + { + Free(lexer->buffer); + } + if (lexer->data.str) + { + Free(lexer->data.str); + } + if (lexer->data.elements) + { + size_t i; + Array *elems = lexer->data.elements; + for (i = 0; i < ArraySize(elems); i++) + { + Free(ArrayGet(elems, i)); + } + ArrayFree(lexer->data.elements); + } + Free(lexer); +} + + +XMLEvent * +XMLCrank(XMLexer *lexer) +{ + XMLEvent *event = NULL; + char c; + char *attrname; + HashMap *props; + char *key, *val; + char cbuf[2] = { 0 }; + char *tmp; + if (!lexer) + { + return NULL; + } + if (StreamEof(lexer->stream)) + { + return NULL; + } + event = XMLCreateRelax(lexer); + switch (lexer->state) + { + case XML_STATE_NONE: + if (XMLookahead(lexer, "", true)) + { + lexer->state = XML_STATE_NONE; + } + else if (XMLookahead(lexer, "--", false)) + { + /* Throw error */ + return NULL; + } + break; + case XML_STATE_ATTR: + attrname = XMLParseName(lexer); + if (!attrname) + { + /* TODO: Throw error */ + } + XMLPushElement(lexer, attrname); + + props = XMLReadProps(lexer); + + XMLSkipSpace(lexer); + if (XMLookahead(lexer, "/>", true)) + { + lexer->state = XML_STATE_NONE; + XMLFreeEvent(event); + event = XMLCreateEmptyElem(lexer, props); + + Free(XMLPopElement(lexer)); + break; + } + else if (XMLookahead(lexer, ">", true)) + { + lexer->state = XML_STATE_NONE; + XMLFreeEvent(event); + event = XMLCreateStart(lexer, props); + break; + } + break; + case XML_STATE_ATTRTAIL: + attrname = XMLParseName(lexer); + Free(XMLPopElement(lexer)); + if (!XMLookahead(lexer, ">", true)) + { + /* TODO: Throw error. */ + break; + } + lexer->state = XML_STATE_NONE; + XMLFreeEvent(event); + event = XMLCreateEnd(lexer, attrname); + break; + } + /* TODO: Crank our XML parser. */ + return event; +} +void +XMLFreeEvent(XMLEvent *event) +{ + if (!event) + { + return; + } + if (event->element) + { + Free(event->element); + } + if (event->attrs) + { + char *key; + void *val; + while (HashMapIterate(event->attrs, &key, &val)) + { + Log(LOG_INFO, "Trying to free %s", val); + Free(val); + } + HashMapFree(event->attrs); + } + if (event->data) + { + Free(event->data); + } + Free(event); +} + +static bool +XMLookahead(XMLexer *lexer, const char *str, bool skip) +{ + int *stack; + size_t top, i; + ssize_t ntop; + bool ret = false; + if (!lexer || !str) + { + return false; + } + + top = 0; + stack = Malloc(strlen(str) * sizeof(*stack)); + + for (i = 0; i < strlen(str); i++) + { + char c = str[i]; + int getc = XMLGetc(lexer); + + stack[top++] = getc; + if (getc != c || getc == EOF) + { + goto seekback; + } + } + + /* We have been able to seek the string properly */ + ret = true; + if (!skip) + { + goto seekback; + } + Free(stack); + return ret; + +seekback: + for (ntop = top - 1; ntop >= 0; ntop--) + { + XMLUngetc(lexer, stack[ntop]); + } + Free(stack); + return ret; +} + +#define IsNamestart(c) ((c == ':') || isalpha(c) || (c == '_')) +#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit(c)) +static char * +XMLParseName(XMLexer *lexer) +{ + int c; + ssize_t point; + + point = XMLInitialiseBuffer(lexer); + c = XMLGetc(lexer); + if (!IsNamestart(c)) + { + XMLRollback(lexer); + return NULL; + } + + while ((c = XMLGetc(lexer))) + { + if (!IsNamepart(c)) + { + break; + } + } + XMLUngetc(lexer, c); + + return XMLStringify(lexer, point); +} + +static ssize_t +XMLInitialiseBuffer(XMLexer *lexer) +{ + if (!lexer) + { + return -1; + } + if (lexer->length != -1) + { + return lexer->top; + } + + lexer->length = 0; + lexer->top = 0; + lexer->buffer = NULL; + return 0; +} +static int +XMLGetc(XMLexer *lexer) +{ + int ch; + if (lexer->length == -1) + { + return StreamGetc(lexer->stream); + } + + ch = StreamGetc(lexer->stream); + if (lexer->top >= lexer->length) + { + lexer->length += 8; + lexer->buffer = Realloc( + lexer->buffer, + lexer->length * sizeof(*lexer->buffer) + ); + } + lexer->buffer[lexer->top++] = ch; + return ch; +} +static void +XMLUngetc(XMLexer *lexer, int ch) +{ + if (!lexer || lexer->length == -1) + { + StreamUngetc(lexer->stream, ch); + return; + } + if (lexer->top == 0) + { + return; + } + + StreamUngetc(lexer->stream, ch); + lexer->top--; +} +static void +XMLEndBuffer(XMLexer *lexer) +{ + if (!lexer || lexer->length == -1) + { + return; + } + lexer->top = -1; + lexer->length = -1; + + Free(lexer->buffer); + lexer->buffer = NULL; +} +static void +XMLRollback(XMLexer *lexer) +{ + ssize_t i; + if (!lexer || lexer->length == -1) + { + return; + } + for (i = lexer->top - 1; i >= 0; i--) + { + StreamUngetc(lexer->stream, lexer->buffer[i]); + } + XMLEndBuffer(lexer); +} +void +XMLReset(XMLexer *lexer, ssize_t loc) +{ + ssize_t i; + if (!lexer || lexer->length == -1) + { + return; + } + for (i = lexer->top - 1; i >= loc; i--) + { + StreamUngetc(lexer->stream, lexer->buffer[i]); + } + lexer->top = loc; +} +char * +XMLStringify(XMLexer *lexer, ssize_t point) +{ + ssize_t i; + char *str; + if (!lexer || lexer->length == -1) + { + return NULL; + } + str = Malloc(lexer->top + 1); + memset(str, '\0', lexer->top + 1); + for (i = point; i < lexer->top; i++) + { + str[i - point] = (char) lexer->buffer[i]; + } + XMLEndBuffer(lexer); + + return str; +} + +#define IsSpace(c) ((c == ' ') || (c == 0x09) || (c == 0x0D) || (c == 0x0A)) +bool +XMLSkipSpace(XMLexer *lexer) +{ + int c; + bool r = false; + while ((c = XMLGetc(lexer)) != EOF) + { + if (!IsSpace(c)) + { + break; + } + r = true; + } + XMLUngetc(lexer, c); + + return r; +} +HashMap * +XMLReadProps(XMLexer *lexer) +{ + ssize_t point = XMLInitialiseBuffer(lexer); + HashMap *map = HashMapCreate(); + bool error = false; + while (true) + { + char *name; + char *value = NULL; + + if (!XMLSkipSpace(lexer)) + { + /* A lack of space is totally excepted */ + break; + } + + name = XMLParseName(lexer); + if (!name) + { + /* A lack of name is totally excepted */ + break; + } + + value = StrDuplicate(""); + if (XMLookahead(lexer, "=", true)) + { + /* TODO: Values aren't names. */ + Free(value); + value = XMLParseAttValue(lexer); + if (!value) + { + error = true; + Free(name); + break; + } + } + HashMapSet(map, name, value); + Free(name); + } + if (error) + { + XMLReset(lexer, point); + } + return map; +} +static XMLEvent * +XMLCreateStart(XMLexer *lexer, HashMap *attrs) +{ + XMLEvent *event = Malloc(sizeof(*event)); + size_t elements = ArraySize(lexer->data.elements) - 1; + char *h_element = StrDuplicate(ArrayGet(lexer->data.elements, elements)); + + event->type = XML_LEXER_STARTELEM; + event->element = h_element; + event->attrs = attrs; + event->data = NULL; + + /* TODO */ + event->line = 0; + event->col = 0; + event->offset = 0; + + return event; +} +XMLEvent * +XMLCreateEnd(XMLexer *lexer, char *end) +{ + XMLEvent *event = Malloc(sizeof(*event)); + + event->type = XML_LEXER_ENDELEM; + event->element = end; + event->attrs = NULL; + event->data = NULL; + + /* TODO */ + event->line = 0; + event->col = 0; + event->offset = 0; + + return event; +} +static XMLEvent * +XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs) +{ + XMLEvent *event = Malloc(sizeof(*event)); + size_t elements = ArraySize(lexer->data.elements) - 1; + char *h_element = StrDuplicate(ArrayGet(lexer->data.elements, elements)); + + event->type = XML_LEXER_ELEM; + event->element = h_element; + event->attrs = attrs; + event->data = NULL; + + /* TODO */ + event->line = 0; + event->col = 0; + event->offset = 0; + + return event; +} +XMLEvent * +XMLCreateData(XMLexer *lexer) +{ + XMLEvent *event = Malloc(sizeof(*event)); + size_t elements = ArraySize(lexer->data.elements); + + event->type = XML_LEXER_DATA; + event->element = elements ? + StrDuplicate(ArrayGet(lexer->data.elements, elements - 1)) : + NULL; + event->attrs = NULL; + event->data = lexer->data.str; + + /* TODO */ + event->line = 0; + event->col = 0; + event->offset = 0; + + lexer->data.str = NULL; + + return event; +} +static XMLEvent * +XMLCreateRelax(XMLexer *lexer) +{ + XMLEvent *event = Malloc(sizeof(*event)); + size_t elements = ArraySize(lexer->data.elements); + + event->type = XML_RELAX; + event->element = elements ? + StrDuplicate(ArrayGet(lexer->data.elements, elements - 1)) : + NULL; + event->attrs = NULL; + event->data = NULL; + + /* TODO */ + event->line = 0; + event->col = 0; + event->offset = 0; + + return event; +} + +static void +XMLPushElement(XMLexer *lexer, char *e) +{ + ArrayAdd(lexer->data.elements, e); +} +static char * +XMLPopElement(XMLexer *lexer) +{ + size_t n = ArraySize(lexer->data.elements); + if (n == 0) + { + return NULL; + } + + return ArrayDelete(lexer->data.elements, n - 1); +} + +#define IsNormalQ(c) ((c != '<') && (c != '&') && (c != '\'')) +#define IsNormalD(c) ((c != '<') && (c != '&') && (c != '"')) +static char * +XMLParseAttQuote(XMLexer *lexer) +{ + int c; + ssize_t point; + char *str; + + point = XMLInitialiseBuffer(lexer); + + while ((c = XMLGetc(lexer))) + { + if (!IsNormalQ(c)) + { + break; + } + } + if (c != '\'') + { + XMLUngetc(lexer, c); + return NULL; + } + + /* TODO: Decode the string */ + str = XMLStringify(lexer, point); + str[strlen(str) - 1] = '\0'; /* Trim the quote. */ + return str; +} +static char * +XMLParseAttDouble(XMLexer *lexer) +{ + int c; + ssize_t point; + char *str; + + point = XMLInitialiseBuffer(lexer); + + while ((c = XMLGetc(lexer))) + { + if (!IsNormalD(c)) + { + break; + } + } + if (c != '"') + { + XMLUngetc(lexer, c); + return NULL; + } + + /* TODO: Decode the string */ + str = XMLStringify(lexer, point); + str[strlen(str) - 1] = '\0'; /* Trim the quote. */ + return str; +} +static char * +XMLParseAttValue(XMLexer *lexer) +{ + ssize_t point = XMLInitialiseBuffer(lexer); + + if (XMLookahead(lexer, "'", true)) + { + return XMLParseAttQuote(lexer); + } + else if (XMLookahead(lexer, "\"", true)) + { + return XMLParseAttDouble(lexer); + } + return NULL; +} diff --git a/src/include/AS.h b/src/include/AS.h index 7c174ec..240d82e 100644 --- a/src/include/AS.h +++ b/src/include/AS.h @@ -15,4 +15,19 @@ extern HashMap * ASVerifyRequest(ParseeHttpArg *); * It does not send the request, however. */ extern void ASAuthenticateRequest(ParseeConfig *, HttpClientContext *); +/* Registers an user through the Application Service API. Returns + * true if the user was created. */ +extern bool ASRegisterUser(ParseeConfig *, char *); + +/* Pings the homeserver to get attention. */ +extern void ASPing(ParseeConfig *); + +/* Joins a room from an ID and a given user we want to masquerade + * as. */ +extern void ASJoin(ParseeConfig *, char *, char *); + +/* Sends a message event with a specific type and body. + * Said body is freed during the function's execution. */ +extern void ASSend(ParseeConfig *, char *, char *, char *, HashMap *); + #endif diff --git a/src/include/Matrix.h b/src/include/Matrix.h index a0bef57..47943b8 100644 --- a/src/include/Matrix.h +++ b/src/include/Matrix.h @@ -5,4 +5,7 @@ /* Creates an error message JSON, with the specified code and message. */ extern HashMap * MatrixCreateError(char *err, char *msg); + +/* Creates the content for a notice message. */ +extern HashMap * MatrixCreateNotice(char *body); #endif diff --git a/src/include/Parsee.h b/src/include/Parsee.h index f025a33..71e9acf 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -52,4 +52,10 @@ extern HttpClientContext * ParseeCreateRequest(ParseeConfig *, HttpRequestMethod /* Sends headers, and writes the JSON object. */ extern HttpStatus ParseeSetRequestJSON(HttpClientContext *, HashMap *); + +/* This function is called on every event received, and routes/manages it. */ +extern void ParseeEventHandler(const ParseeConfig *, HashMap *); + +/* Verifies if a user is a Parsee puppet user. */ +extern bool ParseeIsPuppet(const ParseeConfig *, char *); #endif diff --git a/src/include/Routes.h b/src/include/Routes.h index b07e5d7..ba8f223 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -10,7 +10,8 @@ typedef struct ParseeHttpArg { /* A list of all routes. */ #define ROUTES X_ROUTE("/", RouteRoot) \ - X_ROUTE("/_matrix/app/v1/transactions/(.*)", RouteTxns) + X_ROUTE("/_matrix/app/v1/transactions/(.*)", RouteTxns) \ + X_ROUTE("/_matrix/app/v1/ping", RoutePing) #define X_ROUTE(path, name) extern void * name(Array *, void *); ROUTES diff --git a/src/include/XML.h b/src/include/XML.h new file mode 100644 index 0000000..608daf3 --- /dev/null +++ b/src/include/XML.h @@ -0,0 +1,36 @@ +#ifndef PARSEE_XML_H +#define PARSEE_XML_H + +#include +#include + +typedef struct XMLexer XMLexer; +typedef struct XMLEvent { + enum { + XML_LEXER_UNKNOWN = 0, + XML_LEXER_STARTELEM, + XML_LEXER_ELEM, /* Empty attr */ + XML_LEXER_DATA, + XML_LEXER_ENDELEM, + + XML_RELAX + } type; + + int line, col; + size_t offset; + + /* The element name */ + char *element; + + /* Attributes hashmap */ + HashMap *attrs; + + /* The decoded data */ + char *data; +} XMLEvent; + +extern XMLexer * XMLCreateLexer(Stream *, bool); +extern XMLEvent * XMLCrank(XMLexer *); +extern void XMLFreeEvent(XMLEvent *); +extern void XMLFreeLexer(XMLexer *); +#endif