diff --git a/.gitignore b/.gitignore index 01d033d..09b6fd1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,11 @@ parsee* parsee *.swp .* -data -data/* +data* +data*/* Makefile configure +gmon.out tools/out tools/out/* @@ -25,10 +26,7 @@ tags !.forgejo/* !.forgejo/** -# Janet -janet/* -janet/** -janet -extensions -extensions/* -extensions/** +!.guix +!.guix/* +!.guix/** + diff --git a/.guix/modules/parsee.scm b/.guix/modules/parsee.scm new file mode 100644 index 0000000..75b8b64 --- /dev/null +++ b/.guix/modules/parsee.scm @@ -0,0 +1,82 @@ +(define-module (parsee) + #:use-module (guix packages) + #:use-module (guix git-download) + #:use-module (guix build-system gnu) + #:use-module (guix gexp) + #:use-module (guix utils) + #:use-module (gnu packages tls) + #:use-module (gnu packages databases) + #:use-module ((guix licenses) #:prefix license:)) + +(define vcs-file? + (or (git-predicate + (dirname (dirname (current-source-directory)))) + (const #t))) + +(define-public cytoplasm + (let ((commit "32f31fe6d61583630995d956ed7bd7566c4dc14f") + (revision "0")) + (package + (name "cytoplasm") + (version (git-version "0.4.1" revision commit)) + (source + (origin + (method git-fetch) + (uri (git-reference + (url "https://git.telodendria.io/Telodendria/Cytoplasm") + (commit commit))) + (file-name (git-file-name name version)) + (sha256 + (base32 "09x5xfswryf3wjs1synh972yr2fmpjmffi7pjyjdzb4asqh4whrv")))) + (build-system gnu-build-system) + (arguments + (list + #:tests? #f + #:configure-flags #~'("--with-lmdb") + #:phases #~(modify-phases %standard-phases + (add-before 'configure 'add-ld-flags + (lambda _ + (substitute* "./configure" + (("(LDFLAGS=\"\\$\\{LIBS\\} \\$\\{LDFLAGS\\})\"" all flags) + (string-append flags " -Wl,-rpath=" #$output "/lib\""))) + (mkdir-p (string-append #$output "/lib")))) + (replace 'configure + (lambda* (#:key configure-flags #:allow-other-keys) + (apply invoke `("./configure" + ,(string-append "--prefix=" #$output) + ,@configure-flags))))))) + (inputs (list openssl lmdb)) + (home-page "https://git.telodendria.io/Telodendria/Cytoplasm") + (synopsis "General-purpose high-level networked C library") + (description "Cytoplasm is a general-purpose C library for creating +high-level (particularly networked and multi-threaded) C applications.") + (license license:expat)))) + +(define-public parsee + (package + (name "parsee") + (version "0.0.1-git") + (source + (local-file "../.." "parsee-checkout" + #:recursive? #t + #:select? vcs-file?)) + (build-system gnu-build-system) + (arguments + (list + #:tests? #f + #:make-flags #~(list "CC=gcc" + (string-append "CYTO_INC=" #$cytoplasm "/include") + (string-append "CYTO_LIB=" #$cytoplasm "/lib") + (string-append "PREFIX=" #$output)) + #:phases #~(modify-phases %standard-phases + (replace 'configure + (lambda* (#:key inputs #:allow-other-keys) + (let ((gcc (string-append (assoc-ref inputs "gcc") "/bin/gcc"))) + (invoke gcc "configure.c" "-o" "configure") + (invoke "./configure"))))))) + (home-page "https://git.kappach.at/lda/Parsee") + (synopsis "Jealous Matrix to XMPP bridge") + (description "Parsee is a Matrix-XMPP bridge written in C99 with Cytoplasm.") + (license license:agpl3+))) + +parsee diff --git a/CHANGELOG.md b/CHANGELOG.md index 5999d7f..f4ffcde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,22 @@ commit done between releases. *There is currently no beta releases of Parsee* ## Alpha + +### v0.3.0[lunar-rainbow] +This is the first release of 2025! +TBD +#### New things +- Allow admins to remove users from the nofly list. +- Parsee can now access the homeserver/component locally (rather than over the network) +- The endpoint for media has been changed to '/media/[SERV]/[ID]?hmac=...' +- Add parsee-plumb tool to manage plumbing from outside Parsee if it is not running. +- Add packaging for Guix (see #18) +- Parsee is now dependent on authenticated media to function. + (Please, Matrix, do _not_ touch anything, I do _not_ want to mess with this anymore) +#### Bugfixes +- Fix potential infinite loops when processing some messages. +- Parsee now handles pinging puppets from Matrix more sanely. + ### v0.2.0[star-of-hope] <8/11/2024> Fixes some media metadata things, replaces the build system, tries out avatar support some more, MUC contexts, and speeds diff --git a/README.MD b/README.MD index 9a7fc6f..f9522e8 100644 --- a/README.MD +++ b/README.MD @@ -18,12 +18,11 @@ A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solutio and maybe as a testing ground for Cytoplasm features I sometimes add. (Well, I'm *trying* to do that, at least. -Please scream at me if that fails(or just doesn't run on a overclocked Raspberry -Pi 4B, which, by the way, is literally where Parsee+XMPP is running for now.)) +Please scream at me if that fails(or just doesn't run on a overclocked Raspberry Pi 4B)) ### "Why not just use Matrix lol" ### "Why not just use XMPP lol" -These two having the same answer should be enough information. Also can I *just* have fun? +These two having the same answer should be enough information. One could also argue that both sides need to migrate(onboard) the other side, so a bridge may be a good way to start. @@ -44,6 +43,13 @@ Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendri Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but you can get away without those if you're adventurous). +### BUILDING WITH GUIX +If you have [Guix](https://guix.gnu.org/) installed, you can build Parsee using `guix package -f guix.scm`, or test it +using `guix shell -f guix.scm`. You can also generate a Docker/OCI image. +```sh +guix pack -f docker -S /bin=bin -L.guix/modules parsee +``` + ## RUNNING First off, you may want to configure Parsee by running the `config` tool(generally named `parsee-config` in most cases), with the correct flags, like here. diff --git a/build.conf b/build.conf index 9ebf253..a2e17c1 100644 --- a/build.conf +++ b/build.conf @@ -1,6 +1,6 @@ -CODE=star-of-hope +CODE=lunar-rainbow NAME=Parsee -VERSION=0.2.0 +VERSION=0.3.0 BINARY=parsee SOURCE=src INCLUDES=src/include diff --git a/configure.c b/configure.c index 2f59bb5..d3a01d0 100644 --- a/configure.c +++ b/configure.c @@ -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) { @@ -368,7 +315,7 @@ write_objects(FILE *makefile, str_array_t *sources) { char *src = str_array_get(sources, i); char *ofl = string_rep_ext(src, ".c", ".o"); - char *obj = string_cat("build", strchr(ofl, '/')); + char *obj = string_cat("build", ofl + 3); fprintf(makefile, " %s", obj); free(ofl); @@ -512,12 +459,10 @@ 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, *janet = NULL, *images, *utils, *aya; - char *janet_dir = NULL; + str_array_t *sources, *images, *utils, *aya; if (repo) { @@ -532,7 +477,7 @@ main_build(int argc, char *argv[]) repo = strdup("N/A"); } - while ((opt = getopt(argc, argv, "slJ:")) != -1) + while ((opt = getopt(argc, argv, "sl")) != -1) { switch (opt) { @@ -543,13 +488,17 @@ main_build(int argc, char *argv[]) with_lmdb = with_static; with_static = true; break; - case 'J': - janet_dir = strdup(optarg); - break; } } 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"); @@ -564,22 +513,13 @@ main_build(int argc, char *argv[]) /* Create all objects */ sources = collect_sources("src", true, ".c"); - if (janet_dir) - { - janet = collect_sources(janet_dir, true, ".c"); - for (i = 0; i < str_array_len(janet); i++) - { - str_array_add(sources, str_array_get(janet, i)); - } - str_array_free(janet); - } images = collect_sources("etc/media", true, ".png"); fprintf(makefile, "binary:"); write_objects(makefile, sources); write_images(makefile, images); fprintf(makefile, "\n\t"); { - fprintf(makefile, "$(CC) -lm -o $(BINARY)"); + fprintf(makefile, "$(CC) -o $(BINARY)"); if (with_static) { fprintf(makefile, " -static"); @@ -592,7 +532,7 @@ main_build(int argc, char *argv[]) fprintf(makefile, " -lm -lpthread -lmbedtls -lmbedx509 -lmbedcrypto -lCytoplasm -lmbedtls -lmbedx509 -lmbedcrypto"); if (with_lmdb) fprintf(makefile, " -llmdb"); } - fprintf(makefile, " -lCytoplasm -lm $(LDFLAGS)\n"); + fprintf(makefile, " -lCytoplasm $(LDFLAGS)\n"); } /* Write rules for every source */ @@ -600,7 +540,7 @@ main_build(int argc, char *argv[]) { char *src = str_array_get(sources, i); char *ofl = string_rep_ext(src, ".c", ".o"); - char *obj = string_cat("build", strchr(ofl, '/')); + char *obj = string_cat("build", ofl + 3); fprintf(makefile, "%s: %s", obj, src); analyse_dependencies(makefile, src); @@ -618,10 +558,6 @@ main_build(int argc, char *argv[]) str_array_free(s); fprintf(makefile, "\t$(CC) -c -Isrc -Isrc/include -I$(CYTO_INC) "); - if (janet_dir) - { - fprintf(makefile, "-DJANET -I %s ", janet_dir); - } fprintf(makefile, "-DVERSION=\"\\\"$(VERSION)\\\"\" "); fprintf(makefile, "-DNAME=\"\\\"$(NAME)\\\"\" "); fprintf(makefile, "-DCODE=\"\\\"$(CODE)\\\"\" "); @@ -692,7 +628,7 @@ main_build(int argc, char *argv[]) fprintf(makefile, " -lCytoplasm -lmbedtls -lmbedx509 -lmbedcrypto"); if (with_lmdb) fprintf(makefile, " -llmdb"); } - fprintf(makefile, " -lCytoplasm -lm $(CFLAGS)\n"); + fprintf(makefile, " -lCytoplasm $(CFLAGS)\n"); } free(ofl); @@ -780,7 +716,6 @@ main_build(int argc, char *argv[]) str_array_free(aya); fflush(makefile); fclose(makefile); - free(janet_dir); free(repo); return EXIT_SUCCESS; } diff --git a/etc/man/man1/parsee-config.1 b/etc/man/man1/parsee-config.1 index a297075..5dc258b 100644 --- a/etc/man/man1/parsee-config.1 +++ b/etc/man/man1/parsee-config.1 @@ -1,6 +1,6 @@ ." The last field is the codename, by the way. ." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN -.TH parsee-config 1 "Parsee Utility" "star-of-hope" +.TH parsee-config 1 "Parsee Utility" "lunar-rainbow" .SH NAME parsee-config - generate a basic configuration file @@ -11,6 +11,7 @@ parsee-config .B [-s SHARED_SECRET] .B [-m MEDIA_URL] .B [-J JABBER_HOST] +.B [-j JABBER_ADDR] .B [-p JABBER_PORT] .B [-d DATABASE] .B [-M MAX_STANZA] @@ -34,6 +35,7 @@ $ parsee-config \\ -H 'blow.hole' \\ -s 'The Dark Shared Secret' \\ -J 'xmpp.blow.hole' \\ + -j 'localhost' \\ -S 128 .fi .if n \{\ @@ -68,6 +70,12 @@ media. It must be publicly accessible (behind a reverse proxy to HTTP:7642) .I JABBER_HOST is used as the component host for Parsee. .TP +.BR -j JABBER_ADDR +.I JABBER_ADDR +can optionally be used to change the hostname Parsee will try to contact +for XMPP. Users should ideally use localhost (or a hostname pointing to +the server itself), as XMPP component streams are not encrypted. +.TP .BR -p JABBER_PORT .I JABBER_PORT is used as the component post for Parsee. Parsee uses it alongside diff --git a/etc/man/man7/.parsee-bridge-guidebook.7.swp b/etc/man/man7/.parsee-bridge-guidebook.7.swp new file mode 100644 index 0000000..50976ea Binary files /dev/null and b/etc/man/man7/.parsee-bridge-guidebook.7.swp differ diff --git a/etc/man/man7/.parsee-cmd-syntax.7.swp b/etc/man/man7/.parsee-cmd-syntax.7.swp new file mode 100644 index 0000000..64990b7 Binary files /dev/null and b/etc/man/man7/.parsee-cmd-syntax.7.swp differ diff --git a/etc/man/man7/parsee-bridge-guidebook.7 b/etc/man/man7/parsee-bridge-guidebook.7 new file mode 100644 index 0000000..b5932f2 --- /dev/null +++ b/etc/man/man7/parsee-bridge-guidebook.7 @@ -0,0 +1,70 @@ +." The last field is the codename, by the way. +." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN +.TH parsee-bridge-guidebook 7 "Parsee Utility" "star-of-hope" + +.SH NAME +parsee-bridge-guidebook - A short guidebook on running a Parsee bridge + +.SH INTRODUCTION +.P +This manpage is intended to be a guidebook for Parsee administrators. It +is meant to show how to create an instance with an XMPP-Matrix server +(though it cannot be specific, due to their ecosystem diversity), how to +plumb rooms, and moderate them through. +.P +It also assumes Parsee is properly built and installed, in which case you +are seeing this from +.I man +itself. + +.SH CONVENTIONS +This page shall assume a few things that +.B must +be changed to fit your configuration. Please read those carefully, or it +will come and bite at you! + +.P +First off, it assumes that you have a domain at +.I blow.hole +and that any other domains related to Parsee are it's subdomains, as +you'll see. + +.P +We also assume you're planning on making the XMPP component available at +.I j.blow.hole , +and that Parsee can reach it by using +.I localhost:1234 . +It is highly recommended that Parsee can reach the component locally, as +the stream cannot be encrypted! + +.P +The Parsee HTTP server (which is used for media and the appservice) shall +be reached through +.I https://p.blow.hole/ +via a reverse proxy. This manual shall only show you what port to make +available, as there are many reverse proxy options available. + +.P +Finally, the Matrix server will be publicly known as +.I m.blow.hole +. + +That is, if +.B bob +is in it, then they shall be known as +.I @bob:m.blow.hole . + +.SH SETTING UP +Setting up Parsee mainly involves creating a valid configuration file +and the database. Most of this however is dealt with by +.I parsee-config(1) +TODO + +.P + +.SH LICENSE +This document is under public domain, or CC0 if not allowed by local law. + +.SH SEE ALSO +.B parsee(1), parsee-cmd-syntax(7) + diff --git a/guix.scm b/guix.scm new file mode 120000 index 0000000..021acc5 --- /dev/null +++ b/guix.scm @@ -0,0 +1 @@ +.guix/modules/parsee.scm \ No newline at end of file diff --git a/src/AS/Media.c b/src/AS/Media.c index 65f049c..5fbc659 100644 --- a/src/AS/Media.c +++ b/src/AS/Media.c @@ -150,7 +150,7 @@ ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha) return false; } - path = StrConcat(3, "/_matrix/media/v3/download/", uri->host, uri->path); + path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path); cctx = ParseeCreateRequest(c, HTTP_GET, path); ASAuthenticateRequest(c, cctx); HttpRequestSendHeaders(cctx); @@ -195,7 +195,7 @@ ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len) return false; } - path = StrConcat(3, "/_matrix/media/v3/download/", uri->host, uri->path); + path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path); cctx = ParseeCreateRequest(c, HTTP_GET, path); ASAuthenticateRequest(c, cctx); HttpRequestSendHeaders(cctx); diff --git a/src/AS/Room.c b/src/AS/Room.c index 9e10579..e9691e1 100644 --- a/src/AS/Room.c +++ b/src/AS/Room.c @@ -25,11 +25,13 @@ ASInvite(const ParseeConfig *conf, char *id, char *invited) "@", conf->sender_localpart, ":", conf->server_base ); + id = HttpUrlEncode(id); path = StrConcat(5, "/_matrix/client/v3/rooms/", id, "/invite", "?user_id=", bridge ); Free(bridge); + Free(id); ctx = ParseeCreateRequest( conf, @@ -60,11 +62,13 @@ ASBan(const ParseeConfig *conf, char *id, char *banned) "@", conf->sender_localpart, ":", conf->server_base ); + id = HttpUrlEncode(id); path = StrConcat(5, "/_matrix/client/v3/rooms/", id, "/ban", "?user_id=", bridge ); Free(bridge); + Free(id); ctx = ParseeCreateRequest( conf, @@ -95,11 +99,13 @@ ASKick(const ParseeConfig *conf, char *id, char *banned) "@", conf->sender_localpart, ":", conf->server_base ); + id = HttpUrlEncode(id); path = StrConcat(5, "/_matrix/client/v3/rooms/", id, "/kick", "?user_id=", bridge ); Free(bridge); + Free(id); ctx = ParseeCreateRequest( conf, @@ -120,7 +126,8 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade) { HttpClientContext *ctx = NULL; HashMap *json = NULL; - char *path, *ret; + char *path, *ret, *serv; + int status; if (!conf || !id) { return NULL; @@ -139,6 +146,11 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade) { masquerade = HttpUrlEncode(masquerade); } + serv = strchr(id, ':'); + if (serv) + { + serv = serv + 1; + } id = HttpUrlEncode(id); path = StrConcat(5, "/_matrix/client/v3/join/", id, "?", @@ -152,7 +164,7 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade) Free(path); json = HashMapCreate(); ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, json); + status = ParseeSetRequestJSON(ctx, json); JsonFree(json); json = JsonDecode(HttpClientStream(ctx)); @@ -163,6 +175,8 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade) Free(masquerade); Free(id); + (void) serv; // TODO + return ret; } void diff --git a/src/AS/Send.c b/src/AS/Send.c index 766f393..6811548 100644 --- a/src/AS/Send.c +++ b/src/AS/Send.c @@ -33,6 +33,11 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c, u char *path; char *txn, *ret; char *ts_str; + HttpStatus status; + if (!ret) + { + Log(LOG_ERR, "%", ret); + } HashMap *reply; if (!conf || !id || !type || !user || !c) { @@ -47,18 +52,20 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c, u ts_str = TSToStr(ts); txn = StrRandom(16); + id = HttpUrlEncode(id); path = StrConcat(11, "/_matrix/client/v3/rooms/", id, "/send/", type, "/", txn, "?", "user_id=", user, "&ts=", ts_str ); + Free(id); Free(txn); Free(ts_str); ctx = ParseeCreateRequest(conf, HTTP_PUT, path); Free(path); ASAuthenticateRequest(conf, ctx); - ParseeSetRequestJSON(ctx, c); + status = ParseeSetRequestJSON(ctx, c); reply = JsonDecode(HttpClientStream(ctx)); ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id"))); diff --git a/src/Commands/BanUser.c b/src/Commands/BanUser.c index adc505e..dd380ed 100644 --- a/src/Commands/BanUser.c +++ b/src/Commands/BanUser.c @@ -28,6 +28,25 @@ CommandHead(CmdBanUser, cmd, argp) BotDestroy(); } +CommandHead(CmdNoFlyListDel, cmd, argp) +{ + ParseeCmdArg *args = argp; + ParseeData *data = args->data; + HashMap *event = args->event; + char *user = HashMapGet(cmd->arguments, "user"); + BotInitialise(); + + if (!user) + { + BotDestroy(); + return; + } + + ReplySprintf("Unbanning %s", user); + ParseeGlobalUnban(data, user); + + BotDestroy(); +} CommandHead(CmdNoFlyList, cmd, argp) { ParseeCmdArg *args = argp; diff --git a/src/Commands/SetPL.c b/src/Commands/SetPL.c index 95a2f04..ef2fdb6 100644 --- a/src/Commands/SetPL.c +++ b/src/Commands/SetPL.c @@ -12,7 +12,7 @@ CommandHead(CmdSetPL, cmd, argp) char *user = HashMapGet(cmd->arguments, "user"); char *room = HashMapGet(cmd->arguments, "room"); char *pl_str = HashMapGet(cmd->arguments, "pl"); - long pl = pl_str ? strtol(pl_str, NULL, 10) : 0; + long pl = strtol(pl_str, NULL, 10); HashMap *map; diff --git a/src/Extensions.c b/src/Extensions.c deleted file mode 100644 index 987ec91..0000000 --- a/src/Extensions.c +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef JANET -#include -#include - -bool -ExtensionEnabled(void) -{ - return false; -} -Extensions * -ExtensionCreateContext(ParseeData *data) -{ - (void) data; - return NULL; -} -void -ExtensionDestroyContext(Extensions *ctx) -{ - (void) ctx; -} -Extension * -ExtensionLoadBasic(Extensions *ctx, char *id, char *file) -{ - (void) file; - (void) ctx; - (void) id; - return NULL; -} -void -ExtensionLoadDir(Extensions *ctx, char *dir) -{ - (void) ctx; - (void) dir; -} -bool -ExtensionPushStanza(Extensions *ctx, XMLElement *stanza, StanzaType type) -{ - (void) ctx; - (void) stanza; - - return false; -} -bool -ExtensionPushEvent(Extensions *ctx, HashMap *obj) -{ - (void) ctx; - (void) obj; -} -void -ExtensionManageCommands(Extensions *ctx, XMLElement *stanza) -{ - (void) ctx; - (void) stanza; -} -void -ExtensionRequestCommands(Extensions *ctx) -{ - (void) ctx; -} -void -ExtensionShoveCommands(Extensions *ctx, char *jid, XMLElement *query, XMLElement *stanza) -{ - (void) ctx; - (void) jid; - (void) query; - (void) stanza; -} -void -ExtensionReload(Extensions *ctx, char *id) -{ - (void) ctx; - (void) id; -} -#endif diff --git a/src/Extensions/DB.c b/src/Extensions/DB.c deleted file mode 100644 index dd15f0c..0000000 --- a/src/Extensions/DB.c +++ /dev/null @@ -1,366 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" - -static int JanetDbGet(void *data0, Janet key, Janet *out); -static void JanetMarshalDb(void *data0, JanetMarshalContext *ctx); -static void *JanetUnmarshalDb(JanetMarshalContext *ctx); -static void JanetDbToString(void *data0, JanetBuffer *buf); -static int32_t JanetHashDb(void *data0, size_t size); -static int JanetCloseDb(void *data0, size_t size); -static Janet JanetDbNext(void *data0, Janet next); - -static Janet JanetRefClose(int32_t argc, Janet *argv); -static Janet JanetRefGet(int32_t argc, Janet *argv); -static Janet JanetRefSet(int32_t argc, Janet *argv); -static const JanetMethod db_methods[] = { - { "get", JanetRefGet }, - { "set", JanetRefSet }, - { "close", JanetRefClose }, - - { NULL, NULL } -}; -const JanetAbstractType janet_db_type = { - .name = "db-ref", - .gc = JanetCloseDb, .gcmark = NULL, - - .get = JanetDbGet, .put = NULL, - .marshal = JanetMarshalDb, .unmarshal = JanetUnmarshalDb, - .tostring = JanetDbToString, - - .compare = NULL, - .hash = JanetHashDb, - - .next = JanetDbNext, - JANET_ATEND_NEXT -}; - -typedef struct DbAndRef { - Db *database; - DbRef *ref; - - bool valid; -} DbAndRef; - -static Janet -CreateFromDb(Db *db, DbRef *ref) -{ - JanetAbstract *abs; - - abs = janet_abstract(&janet_db_type, sizeof(DbAndRef)); - ((DbAndRef *)(abs))->database = db; - ((DbAndRef *)(abs))->ref = ref; - ((DbAndRef *)(abs))->valid = true; - - return janet_wrap_abstract(abs); -} - -static void -CloseFromJanet(Janet v) -{ - DbAndRef *indirect = janet_checkabstract(v, &janet_db_type); - if (indirect) - { - indirect->valid = false; - indirect->database = NULL; - indirect->ref = NULL; - } -} -static DbRef * -RefFromJanet(Janet v) -{ - DbAndRef *indirect = janet_checkabstract(v, &janet_db_type); - return (indirect && indirect->valid) ? indirect->ref : NULL; -} -static Db * -DbFromJanet(Janet v) -{ - DbAndRef *indirect = janet_checkabstract(v, &janet_db_type); - return (indirect && indirect->valid) ? indirect->database : NULL; -} - -static int -JanetDbGet(void *data0, Janet key, Janet *out) -{ - JanetKeyword kw; - if (!janet_checktype(key, JANET_KEYWORD)) - { - return 0; - } - - kw = janet_unwrap_keyword(key); - (void) data0; - return janet_getmethod(kw, db_methods, out); -} -static void -JanetMarshalDb(void *data0, JanetMarshalContext *ctx) -{ - janet_marshal_abstract(ctx, data0); -} -static void * -JanetUnmarshalDb(JanetMarshalContext *ctx) -{ - DbAndRef *data = janet_unmarshal_abstract(ctx, sizeof(DbAndRef)); - return data; -} -static int -JanetCloseDb(void *data0, size_t size) -{ - DbAndRef *data = data0; - if (data && data->valid) - { - data->valid = false; - DbUnlock(data->database, data->ref); - data->database = NULL; - data->ref = NULL; - } - return 0; -} -static void -JanetDbToString(void *data0, JanetBuffer *buf) -{ - janet_buffer_push_cstring(buf, "[" NAME " database reference]"); -} -static int32_t -JanetHashDb(void *data0, size_t size) -{ - uintptr_t datap = (uintptr_t) data0; - - (void) size; - /* Hm. */ - return (int32_t) datap; -} -static Janet -JanetDbNext(void *data0, Janet next) -{ - (void) data0; - return janet_nextmethod(db_methods, next); -} - -static Janet -JanetRefClose(int32_t argc, Janet *argv) -{ - Db *db; - DbRef *ref; - janet_fixarity(argc, 1); - - if (!(ref = RefFromJanet(argv[0])) || - !(db = DbFromJanet(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("self isnt a DbRef *") - ); - return janet_wrap_nil(); - } - - DbUnlock(db, ref); - CloseFromJanet(argv[0]); - return janet_wrap_true(); - -} -static Janet -JanetRefGet(int32_t argc, Janet *argv) -{ - DbRef *ref; - HashMap *json; - janet_fixarity(argc, 1); - - if (!(ref = RefFromJanet(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("self isnt a DbRef *") - ); - return janet_wrap_nil(); - } - - json = DbJson(ref); - return JsonToJanet(json); -} -static Janet -JanetRefSet(int32_t argc, Janet *argv) -{ - DbRef *ref; - JanetTable *table; - HashMap *json; - bool ret; - - janet_fixarity(argc, 2); - - if (!(ref = RefFromJanet(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("self isnt a DbRef *") - ); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_TABLE)) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("object to replace isnt JSON") - ); - return janet_wrap_nil(); - } - - table = janet_unwrap_table(argv[1]); - if (!(json = JanetToJson(table))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("object to replace isnt JSON") - ); - return janet_wrap_nil(); - } - - ret = DbJsonSet(ref, json); - JsonFree(json); - - return janet_wrap_boolean(ret); -} - - -Janet -JanetDbExists(int32_t argc, Janet *argv) -{ - ParseeData *data; - Array *args; - int32_t i; - bool ret; - janet_arity(argc, 2, -1); - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("self isnt a ParseeData *") - ); - return janet_wrap_nil(); - } - - args = ArrayCreate(); - for (i = 1; i < argc; i++) - { - char *string = (char *) janet_to_string(argv[i]); - ArrayAdd(args, string); - } - - ret = DbExistsArgs(data->db, args); - ArrayFree(args); - - return janet_wrap_boolean(ret); -} -Janet -JanetDbList(int32_t argc, Janet *argv) -{ - ParseeData *data; - Array *args, *list; - JanetArray *arr; - int32_t i; - janet_arity(argc, 1, -1); - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("self isnt a ParseeData *") - ); - return janet_wrap_nil(); - } - - args = ArrayCreate(); - for (i = 1; i < argc; i++) - { - char *string = (char *) janet_to_string(argv[i]); - ArrayAdd(args, string); - } - - list = DbListArgs(data->db, args); - arr = janet_array(0); - for (i = 0; i < (int32_t) ArraySize(list); i++) - { - char *ent = ArrayGet(list, i); - janet_array_push(arr, janet_cstringv(ent)); - } - - ArrayFree(args); - DbListFree(list); - return janet_wrap_array(arr); -} -Janet -JanetDbLock(int32_t argc, Janet *argv) -{ - ParseeData *data; - Array *args; - DbRef *ref; - int32_t i; - janet_arity(argc, 2, -1); - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("self isnt a ParseeData *") - ); - return janet_wrap_nil(); - } - - args = ArrayCreate(); - for (i = 1; i < argc; i++) - { - char *string = (char *) janet_to_string(argv[i]); - ArrayAdd(args, string); - } - - ref = DbLockArgs(data->db, args); - ArrayFree(args); - - if (!ref) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("couldn't lock database") - ); - return janet_wrap_nil(); - } - - return CreateFromDb(data->db, ref); -} -Janet -JanetDbCreate(int32_t argc, Janet *argv) -{ - ParseeData *data; - Array *args; - DbRef *ref; - int32_t i; - janet_arity(argc, 2, -1); - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("self isnt a ParseeData *") - ); - return janet_wrap_nil(); - } - - args = ArrayCreate(); - for (i = 1; i < argc; i++) - { - char *string = (char *) janet_to_string(argv[i]); - ArrayAdd(args, string); - } - - ref = DbCreateArgs(data->db, args); - ArrayFree(args); - - if (!ref) - { - janet_signalv( - JANET_SIGNAL_ERROR, - janet_cstringv("couldn't create database") - ); - return janet_wrap_nil(); - } - - return CreateFromDb(data->db, ref); -} -#endif diff --git a/src/Extensions/Data.c b/src/Extensions/Data.c deleted file mode 100644 index 71b8d0f..0000000 --- a/src/Extensions/Data.c +++ /dev/null @@ -1,222 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" -#include "Extensions/Functions.h" - -static int JanetDataGet(void *data0, Janet key, Janet *out); -static void JanetMarshalData(void *data0, JanetMarshalContext *ctx); -static void *JanetUnmarshalData(JanetMarshalContext *ctx); -static void JanetDataToString(void *data0, JanetBuffer *buf); -static int32_t JanetHashData(void *data0, size_t size); -static Janet JanetDataNext(void *data0, Janet next); - -static Janet JanetShimMXC(int32_t argc, Janet *argv); -static Janet JanetMTO(int32_t argc, Janet *argv); -static Janet JanetIsAdmin(int32_t argc, Janet *argv); -static Janet JanetUptime(int32_t argc, Janet *argv); -static Janet JanetGlobalBan(int32_t argc, Janet *argv); -static const JanetMethod data_methods[] = { - /* Matrix */ - { "matrix/invite", JanetMatrixInvite }, - { "matrix/find", JanetMatrixFind }, - { "matrix/send", JanetMatrixSend }, - { "matrix/shim-mxc", JanetShimMXC }, - { "matrix/matrix-to", JanetMTO }, - - /* Bridge */ - { "bridge/get", JanetGetKey }, - { "bridge/set", JanetSetKey }, - { "bridge/stanza-info", JanetStanzaInfo }, - - /* XMPP */ - { "xmpp/write", JanetWriteStanza }, - - /* Database */ - { "db/lock", JanetDbLock }, - { "db/exists?", JanetDbExists }, - { "db/create", JanetDbCreate }, - { "db/list", JanetDbList }, - - /* General */ - { "admin?", JanetIsAdmin }, - { "uptime", JanetUptime }, - { "global-ban", JanetGlobalBan }, - - { NULL, NULL } -}; - -const JanetAbstractType janet_data_type = { - .name = "parsee-ctx", - .gc = NULL, .gcmark = NULL, - - .get = JanetDataGet, .put = NULL, - .marshal = JanetMarshalData, .unmarshal = JanetUnmarshalData, - .tostring = JanetDataToString, - - .compare = NULL, - .hash = JanetHashData, - - .next = JanetDataNext, - JANET_ATEND_NEXT -}; - -ParseeData * -GetParseeData(Janet v) -{ - ParseeData **indirect = janet_checkabstract(v, &janet_data_type); - return indirect ? *indirect : NULL; -} -Janet -FromParseeData(ParseeData *data) -{ - JanetAbstract abs; - if (!data) - { - return janet_wrap_nil(); - } - - abs = janet_abstract(&janet_data_type, sizeof(ParseeData *)); - *((ParseeData **) abs) = data; - return janet_wrap_abstract(abs); -} - -static int -JanetDataGet(void *data0, Janet key, Janet *out) -{ - JanetKeyword kw; - if (!janet_checktype(key, JANET_KEYWORD)) - { - return 0; - } - - kw = janet_unwrap_keyword(key); - (void) data0; - return janet_getmethod(kw, data_methods, out); -} -static void -JanetMarshalData(void *data0, JanetMarshalContext *ctx) -{ - janet_marshal_abstract(ctx, data0); -} -static void * -JanetUnmarshalData(JanetMarshalContext *ctx) -{ - ParseeData **data = janet_unmarshal_abstract(ctx, sizeof(ParseeData *)); - return data ? *data : NULL; -} -static void -JanetDataToString(void *data0, JanetBuffer *buf) -{ - janet_buffer_push_cstring(buf, "[" NAME " context type]"); -} -static int32_t -JanetHashData(void *data0, size_t size) -{ - uintptr_t datap = (uintptr_t) data0; - - (void) size; - /* We're casting down, but in a context where there is really - * just one ParseeData, is that really such a crime? */ - return (int32_t) datap; -} -static Janet -JanetDataNext(void *data0, Janet next) -{ - (void) data0; - return janet_nextmethod(data_methods, next); -} -static Janet -JanetIsAdmin(int32_t argc, Janet *argv) -{ - ParseeData *data; - char *str; - janet_fixarity(argc, 2); - if (!(data = GetParseeData(argv[0]))) - { - return janet_wrap_nil(); - } - str = (char *) janet_to_string(argv[1]); - - return janet_wrap_boolean(ParseeIsAdmin(data, str)); -} -static Janet -JanetShimMXC(int32_t argc, Janet *argv) -{ - ParseeData *data; - Janet v; - char *mxc, *shimmed; - janet_fixarity(argc, 2); - if (!(data = GetParseeData(argv[0]))) - { - return janet_wrap_nil(); - } - mxc = (char *) janet_to_string(argv[1]); - shimmed = ParseeToUnauth(data, mxc); - if (!shimmed) - { - return janet_wrap_nil(); - } - v = janet_cstringv(shimmed); - Free(shimmed); - - return v; -} -static Janet -JanetMTO(int32_t argc, Janet *argv) -{ - ParseeData *data; - Janet v; - char *common, *mto; - janet_fixarity(argc, 2); - if (!(data = GetParseeData(argv[0]))) - { - return janet_wrap_nil(); - } - common = (char *) janet_to_string(argv[1]); - mto = ParseeGenerateMTO(common); - if (!mto) - { - return janet_wrap_nil(); - } - v = janet_cstringv(mto); - Free(mto); - - (void) data; - return v; -} -static Janet -JanetUptime(int32_t argc, Janet *argv) -{ - ParseeData *data; - janet_fixarity(argc, 1); - if (!(data = GetParseeData(argv[0]))) - { - return janet_wrap_nil(); - } - - (void) data; - return janet_wrap_number(ParseeUptime()/1000.); -} -static Janet -JanetGlobalBan(int32_t argc, Janet *argv) -{ - ParseeData *data; - const char *user, *reason = NULL; - janet_arity(argc, 2, 3); - if (!(data = GetParseeData(argv[0]))) - { - return janet_wrap_nil(); - } - if (!(user = janet_getcstring(argv, 1))) - { - return janet_wrap_nil(); - } - if (argc == 3 && janet_checktype(argv[2], JANET_STRING)) - { - reason = janet_getcstring(argv, 2); - } - - ParseeGlobalBan(data, (char *) user, (char *) reason); - return janet_wrap_true(); -} -#endif diff --git a/src/Extensions/Functions.c b/src/Extensions/Functions.c deleted file mode 100644 index d159f99..0000000 --- a/src/Extensions/Functions.c +++ /dev/null @@ -1,207 +0,0 @@ -#ifdef JANET -#include "Extensions/Internal.h" - -#include "Extensions/Functions.h" - -Janet -JanetMilliseconds(int32_t argc, Janet *argv) -{ - janet_fixarity(argc, 0); - return janet_wrap_number(UtilTsMillis()/1000.); -} -Janet -JanetCytoVersion(int32_t argc, Janet *argv) -{ - janet_fixarity(argc, 0); - return janet_cstringv(CytoplasmGetVersionStr()); -} -static const JanetReg functions[] = { - /* Cytoplasm */ - { - "cyto/version", JanetCytoVersion, - "(cyto/version)\n\n" - "Returns the Cytoplasm version as a string." - }, - { - "cyto/ts", JanetMilliseconds, - "(cyto/ts)\n\n" - "Returns the timestamp as seconds since 01-01-1970." - }, - - /* XML */ - { - "xml/data", JanetCreateXMLData, - "(xml/data [PARENT?] [TEXT])\n\n" - "Creates a data XML element with freeform text." - }, - { - "xml/tag", JanetCreateXMLTag, - "(xml/tag [PARENT?] [NAME])\n\n" - "Creates a simple XML tag with a name" - }, - - /* JSON */ - { - "json/parse", JanetParseJSON, - "(json/parse [INPUT:buffer]) -> table|NIL\n\n" - "Parses a JSON buffer into a table." - }, - - /* HTTP */ - { - "http/request", JanetHttpRequest, - "(http/request " - "[METHOD:string] " - "[URL:string] " - "[HEADERS:table?] " - "[DATA:buffer])\n\n" - "Creates and sends an HTTP request, and returns an HTTP response" - }, - - {NULL, NULL, NULL} -}; -void -SetupBasicExtensionContext(Extension *ext, JanetTable *env) -{ - char *parsee; - /* Constants */ - janet_def(ext->env, - "parsee/ctx", FromParseeData(ext->source->data), - "The Parsee context used, which maps directly to a ParseeData " - "structure pointer in C99land. Used in various functions." - ); - - parsee = ParseeJID(ext->source->data); - janet_def(ext->env, - "parsee/jid", janet_cstringv(parsee), - "The Parsee bot's Jabber ID as a string" - ); - Free(parsee); - - parsee = ParseeMXID(ext->source->data); - janet_def(ext->env, - "parsee/mxid", janet_cstringv(parsee), - "The Parsee bot's MXID as a string" - ); - Free(parsee); - - janet_def(ext->env, - "parsee/id", janet_cstringv(ext->id), - "The extension's 'identifier'(reverse DNS) notation." - ); - - /* Functions */ - janet_cfuns(env, "parsee", functions); - - /* "Why bother with C99?" */ -#define CheckType(var, type, str) \ - "(if (not= (type " #var ") :" #type ") (error `" str "`))" - janet_dostring(ext->env, - "(defn xml/get-attr[xml key] " - CheckType(xml, table, "XML is not a table") - CheckType(key, string, "KEY is not a string") - "(get (get xml :xml-attrs) key) " - ")" - , "embed:" __FILE__, NULL - ); - janet_dostring(ext->env, - "(defn xml/set-attr[xml key val] " - CheckType(xml, table, "XML is not a table") - CheckType(key, string, "KEY is not a string") - CheckType(val, string, "VAL is not a string") - "(put (get xml :xml-attrs) key val) " - - /* Just to make sure this function returns NIL */ - "nil" - ")" - , "embed:" __FILE__, NULL - ); - janet_dostring(ext->env, - "(defn xmppcmd/create-note [type msg] " - "(def note (xml/tag `note`)) " - "(def mesg (xml/data note msg)) " - - "(xml/set-attr note `type` type) " - - "note" - ")" - , "embed:" __FILE__, NULL - ); - janet_dostring(ext->env, - "(defn xmppcmd/create-form [] " - "(def x (xml/tag `x`)) " - "(xml/set-attr x `xmlns` `jabber:x:data`) " - - "x" - ")" - , "embed:" __FILE__, NULL - ); - janet_dostring(ext->env, - "(defn xmppcmd/set-form-title [x title] " - "(def title-xml (xml/tag x `title`)) " - "(def title-txt (xml/data title-xml title)) " - - "title-xml" - ")" - , "embed:" __FILE__, NULL - ); - janet_dostring(ext->env, - "(defn xmppcmd/generate-table [x fields elems] " - CheckType(x, table, "Form is not a table") - CheckType(fields, table, "Fields is not a table") - CheckType(elems, array, "Elems is not an array") - - "(def reported (xml/tag x `reported`)) " - - "(loop [[id name] :pairs fields] " - "(def field (xml/tag reported `field`)) " - "(xml/set-attr field `var` id) " - "(xml/set-attr field `label` name) " - ") " - - "(loop [entry :in elems] " - CheckType(entry, table, "Entry is not a table.") - "(def xml-entry (xml/tag x `item`)) " - - "(loop [[id val] :pairs entry] " - "(def xml-field (xml/tag xml-entry `field`)) " - "(def xml-value (xml/tag xml-field `value`)) " - "(xml/data xml-value val) " - "(xml/set-attr xml-field `var` id) " - ") " - ") " - ")" - , "embed:" __FILE__, NULL - ); - janet_dostring(ext->env, - "(defn xmppcmd/find-tag [xml tag] " - "(var ret nil) " - "(loop [xml-field :in (get xml :xml-children)] " - "(if (= (get xml-field :xml-name) tag) (do" - "(set ret xml-field) " - "(break)" - ")) " - ") " - "ret " - ")" - , "embed:" __FILE__, NULL - ); - janet_dostring(ext->env, - "(defn xmppcmd/get-field [submit key] " - CheckType(submit, table, "Input is not a table") - "(var value nil) " - "(loop [xml-field :in (get submit :xml-children)] " - "(if (and (= (get xml-field :xml-name) `field`) (= (get (get xml-field :xml-attrs) `var`) key)) (do" - "(set value (xmppcmd/find-tag xml-field `value`)) " - "(set value (get (get (get value :xml-children) 0) :xml-data)) " - "(break)" - ")) " - ") " - "value " - ") " - , "embed:" __FILE__, NULL - ); - -#undef CheckType -} -#endif diff --git a/src/Extensions/Functions.h b/src/Extensions/Functions.h deleted file mode 100644 index 0d0f4a6..0000000 --- a/src/Extensions/Functions.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifdef JANET - -#include - -#define Bind(name) Janet Janet##name(int32_t argc, Janet *argv) - - -Bind(Milliseconds); -Bind(CytoVersion); - -Bind(WriteStanza); - -Bind(CreateXMLData); -Bind(CreateXMLTag); -Bind(SetXMLAttr); - -Bind(ParseJSON); - -Bind(MatrixInvite); -Bind(MatrixFind); -Bind(MatrixSend); - -Bind(StanzaInfo); -Bind(GetKey); -Bind(SetKey); - -Bind(DbLock); -Bind(DbCreate); -Bind(DbList); -Bind(DbExists); - -Bind(HttpRequest); - -#endif diff --git a/src/Extensions/Http.c b/src/Extensions/Http.c deleted file mode 100644 index ffd95b8..0000000 --- a/src/Extensions/Http.c +++ /dev/null @@ -1,258 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" -#include "Extensions/Functions.h" - -#include -#include - -static int JanetHttpGet(void *http0, Janet key, Janet *out); -static void JanetMarshalHttp(void *http0, JanetMarshalContext *ctx); -static void *JanetUnmarshalHttp(JanetMarshalContext *ctx); -static void JanetHttpToString(void *http0, JanetBuffer *buf); -static int32_t JanetHashHttp(void *http0, size_t size); -static Janet JanetHttpNext(void *http0, Janet next); -static int JanetCloseHttp(void *data0, size_t size); - -static Janet JanetHttpCode(int32_t argc, Janet *argv); -static Janet JanetHttpHeaders(int32_t argc, Janet *argv); -static Janet JanetHttpResponse(int32_t argc, Janet *argv); -static const JanetMethod http_methods[] = { - { "code", JanetHttpCode }, - { "headers", JanetHttpHeaders }, - { "response", JanetHttpResponse }, - - { NULL, NULL } -}; - -typedef struct HttpStruct { - int code; - HashMap *headers; - - uint8_t *data; - int32_t size; -} HttpStruct; - -const JanetAbstractType janet_http_type = { - .name = "http-response", - .gc = JanetCloseHttp, .gcmark = NULL, - - .get = JanetHttpGet, .put = NULL, - .marshal = JanetMarshalHttp, .unmarshal = JanetUnmarshalHttp, - .tostring = JanetHttpToString, - - .compare = NULL, - .hash = JanetHashHttp, - - .next = JanetHttpNext, - JANET_ATEND_NEXT -}; -Janet -JanetHttpRequest(int32_t argc, Janet *argv) -{ - const char *method_str = NULL; - const char *path_str = NULL; - JanetTable *headers = NULL; - JanetAbstract *abs = NULL; - JanetBuffer *data = NULL; - HttpClientContext *ctx; - HashMap *raw_headers; - char *header, *value; - HttpStruct *http; - int code, ch; - int32_t i; - Uri *url; - - janet_arity(argc, 2, 4); - if (!(method_str = janet_getcstring(argv, 0)) || - !(path_str = janet_getcstring(argv, 1))) - { - return janet_wrap_nil(); - } - if (argc >= 3) - { - headers = janet_gettable(argv, 2); - } - if (argc >= 4) - { - data = janet_getbuffer(argv, 3); - } - - url = UriParse(path_str); - if (!url || - (!StrEquals(url->proto, "http") && !StrEquals(url->proto, "https"))) - { - UriFree(url); - return janet_wrap_nil(); - } - if (!url->port || StrEquals(url->proto, "http")) - { - url->port = 80; - } - if (!url->port || StrEquals(url->proto, "https")) - { - url->port = 443; - } - ctx = HttpRequest( - HttpRequestMethodFromString(method_str), - StrEquals(url->proto, "https") ? HTTP_FLAG_TLS : HTTP_FLAG_NONE, - url->port, url->host, url->path - ); - UriFree(url); - if (!ctx) - { - return janet_wrap_nil(); - } - - for (i = 0; headers && i < headers->capacity; i++) - { - JanetKV kv = headers->data[i]; - char *key, *value; - if (!janet_checktype(kv.key, JANET_STRING)) - { - continue; - } - - key = (char *) janet_unwrap_string(kv.key); - value = (char *) janet_to_string(kv.value); - HttpRequestHeader(ctx, key, value); - } - if (data) - { - char *len_str = StrInt((int) data->count); - HttpRequestHeader(ctx, "Content-Length", len_str); - Free(len_str); - } - HttpRequestSendHeaders(ctx); - if (data) - { - for (i = 0; i < data->count; i++) - { - uint8_t byte = data->data[i]; - StreamPutc(HttpClientStream(ctx), byte); - } - } - code = HttpRequestSend(ctx); - raw_headers = HttpResponseHeaders(ctx); - - abs = janet_abstract(&janet_http_type, sizeof(HttpStruct)); - http = (HttpStruct *) abs; - http->code = code; - http->headers = HashMapCreate(); - http->size = 0; - http->data = NULL; - while (HashMapIterate(raw_headers, &header, (void **) &value)) - { - HashMapSet(http->headers, header, StrDuplicate(value)); - } - - while ((ch = StreamGetc(HttpClientStream(ctx))) != EOF) - { - http->data = Realloc(http->data, ++(http->size)); - http->data[http->size-1] = ch; - } - HttpClientContextFree(ctx); - - return janet_wrap_abstract(abs); -} - -static int -JanetHttpGet(void *http0, Janet key, Janet *out) -{ - JanetKeyword kw; - if (!janet_checktype(key, JANET_KEYWORD)) - { - return 0; - } - - kw = janet_unwrap_keyword(key); - (void) http0; - return janet_getmethod(kw, http_methods, out); -} -static void -JanetMarshalHttp(void *http0, JanetMarshalContext *ctx) -{ - janet_marshal_abstract(ctx, http0); -} -static void * -JanetUnmarshalHttp(JanetMarshalContext *ctx) -{ - HttpStruct *http = janet_unmarshal_abstract(ctx, sizeof(HttpStruct)); - return http; -} -static void -JanetHttpToString(void *http0, JanetBuffer *buf) -{ - janet_buffer_push_cstring(buf, "[" NAME " HTTP object]"); -} -static int32_t -JanetHashHttp(void *http0, size_t size) -{ - uintptr_t httpp = (uintptr_t) http0; - - (void) size; - return (int32_t) httpp; -} -static Janet -JanetHttpNext(void *http0, Janet next) -{ - (void) http0; - return janet_nextmethod(http_methods, next); -} -static int -JanetCloseHttp(void *data0, size_t size) -{ - HttpStruct *http = data0; - char *key; - void *val; - - while (HashMapIterate(http->headers, &key, &val)) - { - Free(val); - } - HashMapFree(http->headers); - Free(http->data); - return 0; -} -static Janet -JanetHttpCode(int32_t argc, Janet *argv) -{ - JanetAbstract *abs; - HttpStruct *http; - janet_fixarity(argc, 1); - - abs = janet_getabstract(argv, 0, &janet_http_type); - if (!abs) - { - return janet_wrap_nil(); - } - http = (HttpStruct *) abs; - - return janet_wrap_integer(http->code); -} -static Janet -JanetHttpHeaders(int32_t argc, Janet *argv) -{ - return janet_wrap_nil(); -} -static Janet -JanetHttpResponse(int32_t argc, Janet *argv) -{ - JanetBuffer *buffer; - JanetAbstract *abs; - HttpStruct *http; - janet_fixarity(argc, 1); - - abs = janet_getabstract(argv, 0, &janet_http_type); - if (!abs) - { - return janet_wrap_nil(); - } - http = (HttpStruct *) abs; - - buffer = janet_buffer(http->size + 1); - janet_buffer_push_bytes(buffer, (const uint8_t *) http->data, http->size); - - return janet_wrap_buffer(buffer); -} -#endif diff --git a/src/Extensions/Internal.h b/src/Extensions/Internal.h deleted file mode 100644 index db0af88..0000000 --- a/src/Extensions/Internal.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef PARSEE_IEXTENSIONS_H -#define PARSEE_IEXTENSIONS_H -#ifdef JANET - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include - -#define JANET_STANZA_PUSH 0 -#define JANET_REQUEST_XMPPCMD 1 -#define JANET_REQUEST_XMPPCMD_A 2 -#define JANET_REQUEST_XMPPCMD_F 3 - -struct Extensions { - HashMap *extensions; - size_t count; - - ParseeData *data; - - pthread_mutex_t mtx; -}; -struct Extension { - Extensions *source; - - JanetTable *env; - JanetVM *vm; - char *id; - - uint8_t *bytes; - size_t len; - char *file; - - JanetFiber *stanzas; - - pthread_t thr; - volatile bool halted; - - XMPPCommandManager *manager; - JanetTable *functions; -}; - -/** Creates a Janet table from an XML element. For all sensical - * usecases, set in to NULL. - * ----------------------- - * Returns: a wrapped Janet table[janet] */ -Janet ExtensionsFromXML(JanetArray *in, XMLElement *e); - -/** Takes a Janet table and tries to encode it as XML. - * ---------------- - * Returns: an XML element[HEAP] | NULL */ -XMLElement * ExtensionsToXML(JanetTable *t); - -/** Creates a Janet table from a JSON object. - * ---------- - * Returns: a wrapped Janet table[janet] | NULL */ -Janet JsonToJanet(HashMap *obj); - -/** Takes a Janet table and tries to encode it as JSON. - * ---------------- - * Returns: a JSON object[HEAP] | NULL */ -HashMap * JanetToJson(JanetTable *table); - -/** Configures some basic context for the embedded Janet environment. - * --------------------------- - * Returns: NOTHING - * Modifies: env */ -void SetupBasicExtensionContext(Extension *ext, JanetTable *env); - -/** Tries to get a ParseeData from a Janet value(see 'parsee/ctx'). - * ------------------ - * Returns: the data object | NULL */ -ParseeData * GetParseeData(Janet v); - -/** Generates a Janet value from a ParseeData. - * ------------ - * See-Also; GetParseeData */ -Janet FromParseeData(ParseeData *data); - -bool -TryCall(Extension *ext, char *func, int32_t argc, Janet *argv, Janet *out); -#endif -#endif diff --git a/src/Extensions/JSON.c b/src/Extensions/JSON.c deleted file mode 100644 index 1996b9d..0000000 --- a/src/Extensions/JSON.c +++ /dev/null @@ -1,170 +0,0 @@ -#ifdef JANET -#include - -#include -#include -#include - -#include -#include - -Janet JsonToJanet(HashMap *obj); -static Janet -JsonValueToJanet(JsonValue *val) -{ - Janet jVal; - switch (JsonValueType(val)) - { - case JSON_STRING: - jVal = janet_cstringv(JsonValueAsString(val)); - break; - case JSON_NULL: - jVal = janet_wrap_nil(); - break; - case JSON_FLOAT: - jVal = janet_wrap_number(JsonValueAsFloat(val)); - break; - case JSON_INTEGER: - jVal = janet_wrap_s64(JsonValueAsInteger(val)); - break; - case JSON_BOOLEAN: - jVal = janet_wrap_boolean(JsonValueAsBoolean(val)); - break; - case JSON_OBJECT: - jVal = JsonToJanet(JsonValueAsObject(val)); - break; - case JSON_ARRAY: - { - JanetArray *arr = janet_array(0); - Array *jArr = JsonValueAsArray(val); - size_t i, len; - for (i = 0, len = ArraySize(jArr); i < len; i++) - { - janet_array_push(arr, JsonValueToJanet(ArrayGet(jArr, i))); - } - jVal = janet_wrap_array(arr); - } - break; - } - - return jVal; -} - -Janet -JsonToJanet(HashMap *obj) -{ - JanetTable *table; - char *key; - JsonValue *val; - if (!obj) - { - return janet_wrap_nil(); - } - - table = janet_table(0); - while (HashMapIterate(obj, &key, (void **) &val)) - { - Janet jVal = JsonValueToJanet(val); - janet_table_put(table, janet_cstringv(key), jVal); - } - - return janet_wrap_table(table); -} - -HashMap * JanetToJson(JanetTable *table); -static JsonValue * -JanetToJsonValue(Janet janet) -{ - JsonValue *ret = NULL; - if (janet_type(janet) == JANET_STRING) - { - ret = JsonValueString((char *) janet_unwrap_string(janet)); - } - else if (janet_type(janet) == JANET_BOOLEAN) - { - ret = JsonValueBoolean(janet_unwrap_boolean(janet)); - } - else if (janet_type(janet) == JANET_NIL) - { - ret = JsonValueNull(); - } - else if (janet_type(janet) == JANET_ARRAY) - { - JanetArray *arr = janet_unwrap_array(janet); - Array *jArr = ArrayCreate(); - size_t i; - - for (i = 0; i < arr->count; i++) - { - Janet child = arr->data[i]; - ArrayAdd(jArr, JanetToJsonValue(child)); - } - - ret = JsonValueArray(jArr); - } - else if (janet_type(janet) == JANET_TABLE) - { - ret = JsonValueObject(JanetToJson(janet_unwrap_table(janet))); - } - else if (janet_is_int(janet)) - { - ret = JsonValueInteger(janet_unwrap_s64(janet)); - } - else if (janet_type(janet) == JANET_NUMBER) - { - ret = JsonValueFloat(janet_unwrap_number(janet)); - } - - return ret; -} -HashMap * -JanetToJson(JanetTable *table) -{ - HashMap *ret; - size_t i; - if (!table) - { - return NULL; - } - - ret = HashMapCreate(); - for (i = 0; i < table->capacity; i++) - { - JanetKV *kv = &table->data[i]; - char *key; - if (!janet_checktype(kv->key, JANET_STRING)) - { - continue; - } - - key = (char *) janet_unwrap_string(kv->key); - - JsonValueFree(HashMapSet(ret, key, JanetToJsonValue(kv->value))); - } - - return ret; -} - -Janet -JanetParseJSON(int32_t argc, Janet *argv) -{ - JanetBuffer *buf = NULL; - Stream *buffer_stream; - HashMap *json; - Janet ret; - - janet_fixarity(argc, 1); - if (!(buf = janet_getbuffer(argv, 0))) - { - return janet_wrap_nil(); - } - - buffer_stream = StrStreamReaderN((char *) buf->data, buf->count); - json = JsonDecode(buffer_stream); - StreamClose(buffer_stream); - ret = JsonToJanet(json); - JsonFree(json); - - return ret; -} -#endif diff --git a/src/Extensions/Jabber.c b/src/Extensions/Jabber.c deleted file mode 100644 index 9adcaec..0000000 --- a/src/Extensions/Jabber.c +++ /dev/null @@ -1,30 +0,0 @@ -#ifdef JANET -#include "Extensions/Internal.h" - -#include "Extensions/Functions.h" - -Janet -JanetWriteStanza(int32_t argc, Janet *argv) -{ - XMLElement *xml; - ParseeData *data; - - janet_fixarity(argc, 2); - if (janet_type(argv[0]) != JANET_POINTER) - { - return janet_wrap_nil(); - } - if (janet_type(argv[1]) != JANET_TABLE) - { - return janet_wrap_nil(); - } - - data = janet_unwrap_pointer(argv[0]); - xml = ExtensionsToXML(janet_unwrap_table(argv[1])); - - XMPPSendStanza(data->jabber, xml, data->config->max_stanza_size); - - XMLFreeElement(xml); - return janet_wrap_nil(); -} -#endif diff --git a/src/Extensions/Janet.c b/src/Extensions/Janet.c deleted file mode 100644 index 0442688..0000000 --- a/src/Extensions/Janet.c +++ /dev/null @@ -1,304 +0,0 @@ -#ifdef JANET -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include "Extensions/Internal.h" - -bool -ExtensionEnabled(void) -{ - return true; -} - -Extensions * -ExtensionCreateContext(ParseeData *data) -{ - Extensions *ctx = Malloc(sizeof(*ctx)); - - ctx->extensions = HashMapCreate(); - ctx->data = data; - ctx->count = 0; - - pthread_mutex_init(&ctx->mtx, NULL); - return ctx; -} -void -ExtensionDestroyContext(Extensions *ctx) -{ - char *key; - Extension *ext; - if (!ctx) - { - return; - } - - while (HashMapIterate(ctx->extensions, &key, (void **) &ext)) - { - /* TODO */ - ext->halted = true; - janet_loop1_interrupt(ext->vm); - - pthread_join(ext->thr, NULL); - XMPPFreeManager(ext->manager); - Free(ext->bytes); - Free(ext->file); - Free(ext->id); - Free(ext); - } - pthread_mutex_destroy(&ctx->mtx); - HashMapFree(ctx->extensions); - Free(ctx); -} - -Janet -GetGlobal(Extension *ext, char *key) -{ - Janet v; - - v = janet_table_get(ext->env, janet_csymbolv(key)); - if (janet_type(v) == JANET_NIL) - { - return janet_wrap_nil(); - } - return janet_table_get(janet_unwrap_table(v), janet_ckeywordv("value")); -} -bool -TryCall(Extension *ext, char *func, int32_t argc, Janet *argv, Janet *out) -{ - Janet v = GetGlobal(ext, func); - JanetFiber *fib = NULL; - JanetSignal sig = JANET_SIGNAL_OK; - if (janet_type(v) == JANET_FUNCTION) - { - sig = janet_pcall( - janet_unwrap_function(v), - argc, argv, - out, &fib - ); - } - if (sig != JANET_SIGNAL_OK) - { - janet_stacktrace(fib, *out); - } - - return sig == JANET_SIGNAL_OK; -} - -static void * -ExtensionLoop(void *p) -{ - Extension *ext = p; - Janet f_table; - - janet_init(); - ext->env = janet_core_env(NULL); - ext->functions = janet_table(0); - ext->vm = janet_local_vm(); - - f_table = janet_wrap_table(ext->functions); - janet_gcroot(f_table); - - /* Load the Janet environment */ - SetupBasicExtensionContext(ext, ext->env); - janet_dobytes(ext->env, ext->bytes, ext->len, ext->file, NULL); - - while (!ext->halted) - { - janet_loop(); - UtilSleepMillis(1); - } - - janet_gcunroot(f_table); - - //janet_table_deinit(ext->env); - janet_deinit(); - return NULL; -} - -void -ExtensionReload(Extensions *ctx, char *id) -{ - Extension *ext; - Log(LOG_INFO, "ctx=%p id=%s", ctx, id); - if (!ctx || !id) - { - return; - } - - pthread_mutex_lock(&ctx->mtx); - Log(LOG_INFO, "Reloading %s", id); - ext = HashMapGet(ctx->extensions, id); - if (!ext) - { - pthread_mutex_unlock(&ctx->mtx); - Log(LOG_INFO, ":("); - return; - } - ext->halted = true; - janet_loop1_interrupt(ext->vm); - - pthread_join(ext->thr, NULL); - XMPPFreeManager(ext->manager); - Free(ext->bytes); - Free(ext->id); - HashMapDelete(ctx->extensions, id); - - ExtensionLoadBasic(ctx, id, ext->file); - Free(ext->file); - Free(ext); - pthread_mutex_unlock(&ctx->mtx); -} -Extension * -ExtensionLoadBasic(Extensions *ctx, char *id, char *file) -{ - Extension *ext; - Stream *stream; - uint8_t *bytes = NULL; - size_t len = 0; - int c; - - if (!ctx || !id || !file) - { - return NULL; - } - - if (!(stream = StreamOpen(file, "r"))) - { - return NULL; - } - while ((c = StreamGetc(stream)) != EOF) - { - bytes = Realloc(bytes, len + 1); - bytes[len++] = c; - } - StreamClose(stream); - - ext = Malloc(sizeof(*ext)); - - /* will be set by the thread */ - ext->vm = NULL; - ext->stanzas = NULL; - - ext->file = StrDuplicate(file); - ext->id = StrDuplicate(id); - ext->bytes = bytes; - ext->source = ctx; - ext->len = len; - ext->halted = false; - ext->manager = XMPPCreateManager(ext); - HashMapSet(ctx->extensions, id, ext); - - pthread_create( - &ext->thr, - NULL, - ExtensionLoop, - ext - ); - return ext; -} - -static bool -EndsWith(char *str, const char *ext) -{ - size_t len_str, len_ext; - if (!str || !ext) - { - return false; - } - - len_str = strlen(str); - len_ext = strlen(ext); - if (len_ext > len_str) - { - return false; - } - - return StrEquals(str + (len_str - len_ext), ext); -} -void -ExtensionLoadDir(Extensions *ctx, char *dir) -{ - struct dirent *ent; - DIR *handle; - if (!ctx || !dir) - { - return; - } - if (!(handle = opendir(dir))) - { - return; - } - while ((ent = readdir(handle))) - { - const char *extension = ".janet"; - char *name = ent->d_name; - char *actual_path; - char *id = name; - if (!EndsWith(name, extension)) - { - continue; - } - id = StrDuplicate(id); - actual_path = StrConcat(3, dir, "/", name); - id[strlen(name) - strlen(extension)] = '\0'; - - ExtensionLoadBasic(ctx, id, actual_path); - - Free(actual_path); - Free(id); - } - - closedir(handle); -} -void -ExtensionShoveCommands(Extensions *ctx, char *jid, XMLElement *query, XMLElement *stanza) -{ - size_t i; - char *key; - Extension *val; - if (!ctx || !jid || !query || !stanza) - { - return; - } - pthread_mutex_lock(&ctx->mtx); - for (i = 0; IterateReentrant(ctx->extensions, key, val, i); ) - { - XMPPShoveCommandList(val->manager, jid, query, stanza); - } - pthread_mutex_unlock(&ctx->mtx); -} -void -ExtensionManageCommands(Extensions *ctx, XMLElement *stanza) -{ - size_t i; - char *key; - Extension *val; - if (!ctx || !stanza) - { - return; - } - pthread_mutex_lock(&ctx->mtx); - for (i = 0; IterateReentrant(ctx->extensions, key, val, i); ) - { - XMPPManageCommand(val->manager, stanza, ctx->data); - } - pthread_mutex_unlock(&ctx->mtx); -} -#endif diff --git a/src/Extensions/KV.c b/src/Extensions/KV.c deleted file mode 100644 index 69819c5..0000000 --- a/src/Extensions/KV.c +++ /dev/null @@ -1,86 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" - -Janet -JanetGetKey(int32_t argc, Janet *argv) -{ - ParseeData *data; - char *chat_id = NULL; - char *key = NULL, *val = NULL; - Janet ret; - - janet_fixarity(argc, 3); - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("CTX is not a ptr")); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_STRING)) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("CHAT ID is not a string")); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[2], JANET_STRING)) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("KEY is not a string")); - return janet_wrap_nil(); - } - - if (!(data = janet_unwrap_pointer(argv[0]))) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("CTX is NULL..")); - return janet_wrap_nil(); - } - chat_id = (char *) janet_unwrap_string(argv[1]); - key = (char *) janet_unwrap_string(argv[2]); - - if ((val = ParseeGetChatSetting(data, chat_id, key))) - { - ret = janet_cstringv(val); - Free(val); - } - else - { - ret = janet_wrap_nil(); - } - return ret; -} -Janet -JanetSetKey(int32_t argc, Janet *argv) -{ - ParseeData *data; - char *chat_id = NULL; - char *key = NULL, *val = NULL; - - janet_fixarity(argc, 4); - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("CTX is not a ptr")); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_STRING)) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("CHAT ID is not a string")); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[2], JANET_STRING)) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("KEY is not a string")); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[3], JANET_STRING)) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("VAL is not a string")); - return janet_wrap_nil(); - } - - chat_id = (char *) janet_unwrap_string(argv[1]); - key = (char *) janet_unwrap_string(argv[2]); - val = (char *) janet_unwrap_string(argv[3]); - - ParseeSetChatSetting(data, chat_id, key, val); - return janet_wrap_nil(); -} -#endif - diff --git a/src/Extensions/Matrix.c b/src/Extensions/Matrix.c deleted file mode 100644 index e0d33a8..0000000 --- a/src/Extensions/Matrix.c +++ /dev/null @@ -1,149 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" - -#include - -Janet -JanetMatrixInvite(int32_t argc, Janet *argv) -{ - ParseeData *data; - char *invitee, *room; - janet_fixarity(argc, 3); - - if (!(data = GetParseeData(argv[0]))) - { - return janet_wrap_nil(); - } - if (janet_type(argv[1]) != JANET_STRING) - { - return janet_wrap_nil(); - } - if (janet_type(argv[2]) != JANET_STRING) - { - return janet_wrap_nil(); - } - - invitee = (char *) janet_unwrap_string(argv[1]); - room = (char *) janet_unwrap_string(argv[2]); - if (!data) - { - return janet_wrap_nil(); - } - - ASInvite(data->config, room, invitee); - return janet_wrap_nil(); -} -Janet -JanetMatrixFind(int32_t argc, Janet *argv) -{ - ParseeData *data; - char *id, *room; - Janet returned; - HashMap *e; - janet_fixarity(argc, 3); - - if (!(data = GetParseeData(argv[0]))) - { - return janet_wrap_nil(); - } - if (janet_type(argv[1]) != JANET_STRING) - { - return janet_wrap_nil(); - } - if (janet_type(argv[2]) != JANET_STRING) - { - return janet_wrap_nil(); - } - - room = (char *) janet_unwrap_string(argv[1]); - id = (char *) janet_unwrap_string(argv[2]); - if (!data) - { - return janet_wrap_nil(); - } - - if (!(e = ASFind(data->config, room, id))) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("couldnt find event")); - return janet_wrap_nil(); - } - returned = JsonToJanet(e); - JsonFree(e); - return returned; -} -Janet -JanetMatrixSend(int32_t argc, Janet *argv) -{ - ParseeData *data; - char *from, *room, *type, *id; - JanetTable *json; - Janet returned; - HashMap *e; - janet_fixarity(argc, 5); - - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("CTX is not a ptr.") - ); - return janet_wrap_nil(); - } - if (janet_type(argv[1]) != JANET_STRING) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("FROM is not a string.") - ); - return janet_wrap_nil(); - } - if (janet_type(argv[2]) != JANET_STRING) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("ROOM is not a string.") - ); - return janet_wrap_nil(); - } - if (janet_type(argv[3]) != JANET_STRING) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("TYPE is not a string.") - ); - return janet_wrap_nil(); - } - if (janet_type(argv[4]) != JANET_TABLE) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("JSON is not a table.") - ); - return janet_wrap_nil(); - } - - from = (char *) janet_unwrap_string(argv[1]); - room = (char *) janet_unwrap_string(argv[2]); - type = (char *) janet_unwrap_string(argv[3]); - json = janet_unwrap_table(argv[4]); - if (!data) - { - return janet_wrap_nil(); - } - - if (!(e = JanetToJson(json))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("JSON is not valid JSON.") - ); - return janet_wrap_nil(); - } - - if (!(id = ASSend(data->config, room, from, type, e, 0))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Could not send event") - ); - return janet_wrap_nil(); - } - returned = janet_cstringv(id); - Free(id); - return returned; -} -#endif diff --git a/src/Extensions/PushEvents.c b/src/Extensions/PushEvents.c deleted file mode 100644 index 40ae858..0000000 --- a/src/Extensions/PushEvents.c +++ /dev/null @@ -1,89 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" - -typedef struct ExtensionEventReply { - pthread_mutex_t lock; - pthread_cond_t cond; - - HashMap *obj; - - Extension *ext; - bool reply, ack; -} ExtensionEventReply; - -static void -OnEventResponse(JanetEVGenericMessage msg) -{ - ExtensionEventReply *reply = msg.argp; - Extension *ext = reply->ext; - Janet args[1] = { - JsonToJanet(reply->obj), - }; - Janet ret; - - if (TryCall(ext, "on-event", 1, args, &ret)) - { - pthread_mutex_lock(&reply->lock); - reply->ack = true; - reply->reply = janet_truthy(ret); - pthread_cond_signal(&reply->cond); - pthread_mutex_unlock(&reply->lock); - return; - } - pthread_mutex_lock(&reply->lock); - reply->ack = true; - reply->reply = false; - pthread_cond_signal(&reply->cond); - pthread_mutex_unlock(&reply->lock); -} -bool -ExtensionPushEvent(Extensions *ctx, HashMap *obj) -{ - bool veto = false; - size_t i; - - char *key; - Extension *val; - if (!ctx || !obj) - { - return false; - } - - pthread_mutex_lock(&ctx->mtx); - for (i = 0; IterateReentrant(ctx->extensions, key, val, i); ) - { - ExtensionEventReply *reply = Malloc(sizeof(*reply)); - JanetEVGenericMessage msg = { - /* We except a veto boolean. */ - .tag = JANET_STANZA_PUSH, - }; - - pthread_mutex_init(&reply->lock, NULL); - pthread_cond_init(&reply->cond, NULL); - reply->ext = val; - reply->reply = false; - reply->ack = false; - reply->obj = obj; - msg.argp = reply; - - janet_ev_post_event(val->vm, OnEventResponse, msg); - pthread_mutex_lock(&reply->lock); - while (!reply->ack) - { - pthread_cond_wait(&reply->cond, &reply->lock); - } - pthread_mutex_unlock(&reply->lock); - pthread_mutex_destroy(&reply->lock); - pthread_cond_destroy(&reply->cond); - if (reply->reply) - { - veto = true; - } - Free(reply); - } - pthread_mutex_unlock(&ctx->mtx); - - return veto; -} -#endif diff --git a/src/Extensions/PushStanza.c b/src/Extensions/PushStanza.c deleted file mode 100644 index 4f61961..0000000 --- a/src/Extensions/PushStanza.c +++ /dev/null @@ -1,98 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" - -typedef struct ExtensionStanzaReply { - pthread_mutex_t lock; - pthread_cond_t cond; - - XMLElement *stanza; - - Extension *ext; - bool reply, ack; - char *type; -} ExtensionStanzaReply; - -static void -OnStanzaResponse(JanetEVGenericMessage msg) -{ - ExtensionStanzaReply *reply = msg.argp; - Extension *ext = reply->ext; - Janet args[2] = { - ExtensionsFromXML(NULL, reply->stanza), - janet_cstringv(reply->type) - }; - Janet ret; - - if (TryCall(ext, "on-stanza", 2, args, &ret)) - { - pthread_mutex_lock(&reply->lock); - reply->ack = true; - reply->reply = janet_truthy(ret); - pthread_cond_signal(&reply->cond); - pthread_mutex_unlock(&reply->lock); - return; - } - pthread_mutex_lock(&reply->lock); - reply->ack = true; - reply->reply = false; - pthread_cond_signal(&reply->cond); - pthread_mutex_unlock(&reply->lock); -} -bool -ExtensionPushStanza(Extensions *ctx, XMLElement *stanza, StanzaType t) -{ - bool veto = false; - size_t i; - - char *table[STANZA_TYPE_COUNT] = { - "raw", - "message", - "iq", - "presence" - }; - char *key; - Extension *val; - if (!ctx || !stanza) - { - return false; - } - - pthread_mutex_lock(&ctx->mtx); - for (i = 0; IterateReentrant(ctx->extensions, key, val, i); ) - { - ExtensionStanzaReply *reply = Malloc(sizeof(*reply)); - JanetEVGenericMessage msg = { - /* We except a veto boolean. */ - .tag = JANET_STANZA_PUSH, - }; - - pthread_mutex_init(&reply->lock, NULL); - pthread_cond_init(&reply->cond, NULL); - reply->ext = val; - reply->reply = false; - reply->ack = false; - reply->type = table[t]; - reply->stanza = stanza; - msg.argp = reply; - - janet_ev_post_event(val->vm, OnStanzaResponse, msg); - pthread_mutex_lock(&reply->lock); - while (!reply->ack) - { - pthread_cond_wait(&reply->cond, &reply->lock); - } - pthread_mutex_unlock(&reply->lock); - pthread_mutex_destroy(&reply->lock); - pthread_cond_destroy(&reply->cond); - if (reply->reply) - { - veto = true; - } - Free(reply); - } - pthread_mutex_unlock(&ctx->mtx); - - return veto; -} -#endif diff --git a/src/Extensions/RequestCmds.c b/src/Extensions/RequestCmds.c deleted file mode 100644 index 61a1bca..0000000 --- a/src/Extensions/RequestCmds.c +++ /dev/null @@ -1,580 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" - -#include -#include - -typedef struct ExtensionCommandReply { - pthread_mutex_t lock; - pthread_cond_t cond; - - XMLElement *form; - char *from; - char *node; - - XMLElement *out; - - XMPPCommand *cmd; - - Extension *ext; - bool ack; -} ExtensionCommandReply; - -static void OnCommandResponse(JanetEVGenericMessage msg); - -static int JanetCmdGet(void *data0, Janet key, Janet *out); -static void JanetMarshalCmd(void *data0, JanetMarshalContext *ctx); -static void *JanetUnmarshalCmd(JanetMarshalContext *ctx); -static void JanetCmdToString(void *data0, JanetBuffer *buf); -static int32_t JanetHashCmd(void *data0, size_t size); -static Janet JanetCmdNext(void *data0, Janet next); - -static Janet JanetCmdCreateText(int32_t argc, Janet *argv); -static Janet JanetCmdCreateBool(int32_t argc, Janet *argv); -static Janet JanetCmdCreateFixed(int32_t argc, Janet *argv); -static Janet JanetCmdSetTitle(int32_t argc, Janet *argv); -static const JanetMethod cmd_methods[] = { - { "create-text", JanetCmdCreateText }, - { "create-bool", JanetCmdCreateBool }, - { "create-fixed", JanetCmdCreateFixed }, - { "set-title", JanetCmdSetTitle }, - { NULL, NULL } -}; -const JanetAbstractType janet_cmd_type = { - .name = "parsee-xmppcmd", - .gc = NULL, .gcmark = NULL, - - .get = JanetCmdGet, .put = NULL, - .marshal = JanetMarshalCmd, .unmarshal = JanetUnmarshalCmd, - .tostring = JanetCmdToString, - - .compare = NULL, - .hash = JanetHashCmd, - - .next = JanetCmdNext, - JANET_ATEND_NEXT -}; - - -static XMPPCommand * -JanetToCommand(Janet v) -{ - XMPPCommand **indirect = janet_checkabstract(v, &janet_cmd_type); - return indirect ? *indirect : NULL; -} -static Janet -CommandToJanet(XMPPCommand *cmd) -{ - JanetAbstract abs; - if (!cmd) - { - return janet_wrap_nil(); - } - - abs = janet_abstract(&janet_cmd_type, sizeof(XMPPCommand *)); - *((XMPPCommand **) abs) = cmd; - return janet_wrap_abstract(abs); -} - -static void -PutFunction(JanetTable *functions, char *node, char *kind, JanetFunction *f) -{ - JanetTable *kinds; - Janet kindsv; - if (!functions || !node || !kind || !f) - { - return; - } - - kindsv = janet_table_get(functions, janet_cstringv(node)); - if (janet_checktype(kindsv, JANET_NIL)) - { - kinds = janet_table(0); - janet_table_put( - functions, - janet_cstringv(node), janet_wrap_table(kinds) - ); - } - else - { - kinds = janet_unwrap_table(kindsv); - } - - janet_table_put(kinds, janet_cstringv(kind), janet_wrap_function(f)); - -} -static JanetFunction * -FetchFunction(JanetTable *functions, char *node, char *kind) -{ - Janet f_tablev, fv; - JanetTable *ftable; - if (!functions || !node || !kind) - { - return NULL; - } - - f_tablev = janet_table_get( - functions, - janet_cstringv(node) - ); - if (!janet_checktype(f_tablev, JANET_TABLE)) - { - return NULL; - } - ftable = janet_unwrap_table(f_tablev); - - fv = janet_table_get( - ftable, - janet_cstringv(kind) - ); - if (!janet_checktype(fv, JANET_FUNCTION)) - { - return NULL; - } - - return janet_unwrap_function(fv); -} - -extern void -JanetFormCb(XMPPCommandManager *manager, XMPPCommand *cmd, char *from, char *node) -{ - Extension *ext = XMPPGetManagerCookie(manager); - ExtensionCommandReply *reply; - - reply = Malloc(sizeof(*reply)); - JanetEVGenericMessage msg = { - .tag = JANET_REQUEST_XMPPCMD_F - }; - - pthread_mutex_init(&reply->lock, NULL); - pthread_cond_init(&reply->cond, NULL); - reply->ext = ext; - reply->node = node; - reply->from = from; - reply->ack = false; - reply->cmd = cmd; - msg.argp = reply; - - janet_ev_post_event(ext->vm, OnCommandResponse, msg); - pthread_mutex_lock(&reply->lock); - while (!reply->ack) - { - pthread_cond_wait(&reply->cond, &reply->lock); - } - pthread_mutex_unlock(&reply->lock); - pthread_mutex_destroy(&reply->lock); - pthread_cond_destroy(&reply->cond); - Free(reply); - -} -static void -JanetCmdCb(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) -{ - Extension *ext = XMPPGetManagerCookie(m); - ExtensionCommandReply *reply; - - reply = Malloc(sizeof(*reply)); - JanetEVGenericMessage msg = { - .tag = JANET_REQUEST_XMPPCMD_A - }; - - pthread_mutex_init(&reply->lock, NULL); - pthread_cond_init(&reply->cond, NULL); - reply->ext = ext; - reply->node = node; - reply->form = form; - reply->from = from; - reply->ack = false; - reply->out = out; - msg.argp = reply; - - janet_ev_post_event(ext->vm, OnCommandResponse, msg); - pthread_mutex_lock(&reply->lock); - while (!reply->ack) - { - pthread_cond_wait(&reply->cond, &reply->lock); - } - pthread_mutex_unlock(&reply->lock); - pthread_mutex_destroy(&reply->lock); - pthread_cond_destroy(&reply->cond); - Free(reply); -} -static Janet -JanetRegisterCommand(int32_t argc, Janet *argv) -{ - JanetTable *self; - JanetFunction *func; - JanetFunction *form; - char *node, *name; - Janet v; - - Extension *ext; - XMPPCommand *cmd; - - janet_arity(argc, 4, 5); - - if (!janet_checktype(argv[0], JANET_TABLE) || - !janet_checktype(argv[1], JANET_STRING) || - !janet_checktype(argv[2], JANET_STRING) || - !janet_checktype(argv[3], JANET_FUNCTION) || - (argc == 5 && !janet_checktype(argv[4], JANET_FUNCTION))) - { - return janet_wrap_nil(); - } - self = janet_unwrap_table(argv[0]); - node = (char *) janet_unwrap_string(argv[1]); - name = (char *) janet_unwrap_string(argv[2]); - func = janet_unwrap_function(argv[3]); - form = argc == 5 ? janet_unwrap_function(argv[4]) : NULL; - - v = janet_table_get(self, janet_ckeywordv("ptr")); - if (!janet_checktype(v, JANET_POINTER)) - { - return janet_wrap_nil(); - } - ext = janet_unwrap_pointer(v); - - PutFunction(ext->functions, node, "act", func); - PutFunction(ext->functions, node, "form", form); - cmd = XMPPBasicCmd(XMPPCMD_ADMINS, node, name, JanetCmdCb); - if (form) - { - XMPPCmdOptionsCreator(cmd, JanetFormCb); - } - XMPPRegisterCommand(ext->manager, cmd); - - return janet_wrap_nil(); -} - -static void -OnCommandResponse(JanetEVGenericMessage msg) -{ - ExtensionCommandReply *reply = msg.argp; - Extension *ext = reply->ext; - Janet ret, in; - - if (msg.tag == JANET_REQUEST_XMPPCMD) - { - JanetTable *cmd = janet_table(0); - janet_table_put(cmd, - janet_ckeywordv("ptr"), - janet_wrap_pointer(reply->ext) - ); - janet_table_put(cmd, - janet_ckeywordv("register"), - janet_wrap_cfunction(JanetRegisterCommand) - ); - in = janet_wrap_table(cmd); - - TryCall(ext, "on-xmppcmd", 1, &in, &ret); - } - else if (msg.tag == JANET_REQUEST_XMPPCMD_F) - { - Janet in[2] = { - janet_cstringv(reply->from), - CommandToJanet(reply->cmd) - }; - JanetFiber *fib = NULL; - JanetFunction *func; - JanetSignal sig; - - func = FetchFunction(reply->ext->functions, reply->node, "form"); - if (func) - { - sig = janet_pcall(func, sizeof(in)/sizeof(*in), in, &ret, &fib); - if (sig != JANET_SIGNAL_OK) - { - janet_stacktrace(fib, ret); - } - } - } - else if (msg.tag == JANET_REQUEST_XMPPCMD_A) - { - JanetFunction *func; - - func = FetchFunction(reply->ext->functions, reply->node, "act"); - if (func) - { - Janet in[2] = { - janet_cstringv(reply->from), - ExtensionsFromXML(NULL, reply->form) - }; - JanetFiber *fib = NULL; - JanetSignal sig; - XMLElement *out = reply->out; - sig = janet_pcall(func, sizeof(in)/sizeof(*in), in, &ret, &fib); - if (sig != JANET_SIGNAL_OK) - { - janet_stacktrace(fib, ret); - SetNote("error", "Internal error in Janet extension."); - } - - if (janet_checktype(ret, JANET_ARRAY)) - { - JanetArray *arr = janet_unwrap_array(ret); - size_t i, added; - for (i = 0, added = 0; i < arr->count; i++) - { - Janet v = arr->data[i]; - JanetTable *source; - XMLElement *child = NULL; - - if (!reply->out || !janet_checktype(v, JANET_TABLE)) - { - continue; - } - source = janet_unwrap_table(v); - child = ExtensionsToXML(source); - XMLAddChild(reply->out, child); - if (child) - { - added++; - } - } - if (added == 0) - { - SetNote("error", "No values returned in Janet extension."); - } - } - else - { - SetNote("error", "Invalid output in Janet extension."); - } - } - } - - pthread_mutex_lock(&reply->lock); - reply->ack = true; - pthread_cond_signal(&reply->cond); - pthread_mutex_unlock(&reply->lock); -} - -void -ExtensionRequestCommands(Extensions *ctx) -{ - size_t i; - char *key; - Extension *val; - if (!ctx) - { - return; - } - - pthread_mutex_lock(&ctx->mtx); - for (i = 0; IterateReentrant(ctx->extensions, key, val, i); ) - { - ExtensionCommandReply *reply = Malloc(sizeof(*reply)); - JanetEVGenericMessage msg = { - .tag = JANET_REQUEST_XMPPCMD - }; - - pthread_mutex_init(&reply->lock, NULL); - pthread_cond_init(&reply->cond, NULL); - reply->ext = val; - reply->ack = false; - msg.argp = reply; - - janet_ev_post_event(val->vm, OnCommandResponse, msg); - pthread_mutex_lock(&reply->lock); - while (!reply->ack) - { - pthread_cond_wait(&reply->cond, &reply->lock); - } - pthread_mutex_unlock(&reply->lock); - pthread_mutex_destroy(&reply->lock); - pthread_cond_destroy(&reply->cond); - Free(reply); - } - pthread_mutex_unlock(&ctx->mtx); -} - -static int -JanetCmdGet(void *data0, Janet key, Janet *out) -{ - JanetKeyword kw; - if (!janet_checktype(key, JANET_KEYWORD)) - { - return 0; - } - - kw = janet_unwrap_keyword(key); - (void) data0; - return janet_getmethod(kw, cmd_methods, out); -} -static void -JanetMarshalCmd(void *data0, JanetMarshalContext *ctx) -{ - janet_marshal_abstract(ctx, data0); -} -static void * -JanetUnmarshalCmd(JanetMarshalContext *ctx) -{ - XMPPCommand **data = janet_unmarshal_abstract(ctx, sizeof(XMPPCommand *)); - return data ? *data : NULL; -} -static void -JanetCmdToString(void *data0, JanetBuffer *buf) -{ - janet_buffer_push_cstring(buf, "[" NAME " XMPP command type]"); -} -static int32_t -JanetHashCmd(void *data0, size_t size) -{ - uintptr_t datap = (uintptr_t) data0; - - (void) size; - /* We're casting down, but in a context where there is really - * just one ParseeData, is that really such a crime? */ - return (int32_t) datap; -} -static Janet -JanetCmdNext(void *data0, Janet next) -{ - (void) data0; - return janet_nextmethod(cmd_methods, next); -} - - -static Janet -JanetCmdCreateText(int32_t argc, Janet *argv) -{ - XMPPCommand *cmd; - XMPPOption *opt; - - bool req; - char *id, *def, *desc; - - janet_arity(argc, 4, 5); - if (!(cmd = JanetToCommand(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Object is not a command") - ); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_BOOLEAN) || - !janet_checktype(argv[2], JANET_STRING) || - !janet_checktype(argv[3], JANET_STRING) || - ((argc == 5) && !janet_checktype(argv[4], JANET_STRING))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Invalid argument types") - ); - return janet_wrap_nil(); - } - - req = janet_unwrap_boolean(argv[1]); - id = (char *) janet_unwrap_string(argv[2]); - def = (char *) janet_unwrap_string(argv[3]); - desc = argc == 5 ? (char *) janet_unwrap_string(argv[4]) : NULL; - - opt = XMPPCreateText(req, id, def); - XMPPSetDescription(opt, desc); - - XMPPAddOption(cmd, opt); - return janet_wrap_nil(); -} - -static Janet -JanetCmdCreateBool(int32_t argc, Janet *argv) -{ - XMPPCommand *cmd; - XMPPOption *opt; - - bool req, def; - char *id, *desc; - - janet_arity(argc, 4, 5); - if (!(cmd = JanetToCommand(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Object is not a command") - ); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_BOOLEAN) || - !janet_checktype(argv[2], JANET_STRING) || - !janet_checktype(argv[3], JANET_BOOLEAN) || - ((argc == 5) && !janet_checktype(argv[4], JANET_STRING))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Invalid argument types") - ); - return janet_wrap_nil(); - } - - req = janet_unwrap_boolean(argv[1]); - id = (char *) janet_unwrap_string(argv[2]); - def = janet_unwrap_boolean(argv[3]); - desc = argc == 5 ? (char *) janet_unwrap_string(argv[4]) : NULL; - - opt = XMPPCreateBool(req, id, def); - XMPPSetDescription(opt, desc); - - XMPPAddOption(cmd, opt); - return janet_wrap_nil(); -} -static Janet -JanetCmdCreateFixed(int32_t argc, Janet *argv) -{ - XMPPCommand *cmd; - XMPPOption *opt; - - char *id, *desc; - - janet_fixarity(argc, 3); - if (!(cmd = JanetToCommand(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Object is not a command") - ); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_STRING) || - !janet_checktype(argv[2], JANET_STRING)) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Invalid argument types") - ); - return janet_wrap_nil(); - } - - id = (char *) janet_unwrap_string(argv[1]); - desc = (char *) janet_unwrap_string(argv[2]); - - opt = XMPPCreateFixed(id, desc); - XMPPSetDescription(opt, desc); - - XMPPAddOption(cmd, opt); - return janet_wrap_nil(); -} -static Janet -JanetCmdSetTitle(int32_t argc, Janet *argv) -{ - XMPPCommand *cmd; - - char *title; - - janet_fixarity(argc, 2); - if (!(cmd = JanetToCommand(argv[0]))) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Object is not a command") - ); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_STRING)) - { - janet_signalv( - JANET_SIGNAL_ERROR, janet_cstringv("Invalid argument types") - ); - return janet_wrap_nil(); - } - - title = (char *) janet_unwrap_string(argv[1]); - - XMPPSetFormTitle(cmd, title); - return janet_wrap_nil(); -} -#endif diff --git a/src/Extensions/StanzaInfo.c b/src/Extensions/StanzaInfo.c deleted file mode 100644 index 1400aaa..0000000 --- a/src/Extensions/StanzaInfo.c +++ /dev/null @@ -1,98 +0,0 @@ -#ifdef JANET - -#include "Extensions/Internal.h" - -Janet -JanetStanzaInfo(int32_t argc, Janet *argv) -{ - ParseeData *data; - XMLElement *stanza; - - JanetTable *ret; - - char *trimmed = NULL; - char *chat_id = NULL; - char *from = NULL; - char *room = NULL; - char *mxid = NULL; - - janet_fixarity(argc, 2); - if (!(data = GetParseeData(argv[0]))) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("CTX is not a ptr")); - return janet_wrap_nil(); - } - if (!janet_checktype(argv[1], JANET_TABLE)) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("STANZA is not a table")); - return janet_wrap_nil(); - } - - if (!(stanza = ExtensionsToXML(janet_unwrap_table(argv[1])))) - { - janet_signalv(JANET_SIGNAL_ERROR, janet_cstringv("STANZA is not XML.")); - return janet_wrap_nil(); - } - - from = HashMapGet(stanza->attrs, "from"); - trimmed = ParseeTrimJID(from); - chat_id = ParseeGetFromMUCID(data, trimmed); - - ret = janet_table(0); - janet_table_put(ret, - janet_ckeywordv("in-muc"), - janet_wrap_boolean(ParseeIsMUC(data, trimmed)) - ); - janet_table_put(ret, - janet_ckeywordv("in-dm"), - janet_wrap_boolean(StrEquals(HashMapGet(stanza->attrs, "type"), "chat")) - ); - if (chat_id && trimmed) - { - room = ParseeGetRoomID(data, chat_id); - janet_table_put(ret, - janet_ckeywordv("muc-jid"), janet_cstringv(trimmed) - ); - janet_table_put(ret, - janet_ckeywordv("id"), janet_cstringv(chat_id) - ); - } - else - { - char *to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to")); - char *dmid = ParseeGetDMID(to, from); - - room = ParseeFindDMRoom(data, to, from); - if (dmid) - { - janet_table_put(ret, - janet_ckeywordv("id"), janet_cstringv(dmid) - ); - } - - Free(dmid); - Free(to); - } - - if (room) - { - janet_table_put(ret, - janet_ckeywordv("room-id"), janet_cstringv(room) - ); - } - - if ((mxid = ParseeGetBridgedUser(data, stanza))) - { - janet_table_put(ret, - janet_ckeywordv("bridged-sender"), janet_cstringv(mxid) - ); - } - - Free(trimmed); - Free(chat_id); - Free(room); - Free(mxid); - XMLFreeElement(stanza); - return janet_wrap_table(ret); -} -#endif diff --git a/src/Extensions/XML.c b/src/Extensions/XML.c deleted file mode 100644 index 2567f82..0000000 --- a/src/Extensions/XML.c +++ /dev/null @@ -1,357 +0,0 @@ -#ifdef JANET -#include - -#include -#include - -#include -#include - -static Janet XMLToString(int32_t argc, Janet *argv); -static Janet JanetXMLAddAttr(int32_t argc, Janet *argv); -static void InitialiseBasicXML(JanetTable *table, char *type); - -XMLElement * -ExtensionsToXML(JanetTable *t) -{ - XMLElement *ret = NULL; - - char *type = NULL, *name, *data; - JanetArray *children; - JanetTable *attrs; - int32_t i; - Janet v; - - if (!t) - { - return NULL; - } -#define ChkType(ent, type) (janet_type((v = janet_table_get(t, janet_ckeywordv(ent)))) == type) - if (!ChkType("xml-type", JANET_STRING)) - { - return NULL; - } - type = (char *) janet_unwrap_string(v); - if (StrEquals(type, "data")) - { - if (!ChkType("xml-data", JANET_STRING)) - { - return NULL; - } - data = (char *) janet_unwrap_string(v); - - ret = XMLCreateText(data); - } - else if (StrEquals(type, "tag")) - { - if (!ChkType("xml-name", JANET_STRING)) - { - return NULL; - } - name = (char *) janet_unwrap_string(v); - - if (!ChkType("xml-attrs", JANET_TABLE)) - { - return NULL; - } - - ret = XMLCreateTag(name); - attrs = janet_unwrap_table(v); - for (i = 0; i < attrs->capacity; i++) - { - JanetKV *kv = &attrs->data[i]; - if (!janet_checktype(kv->key, JANET_STRING) || - !janet_checktype(kv->value, JANET_STRING)) - { - continue; - } - XMLAddAttr(ret, - (char *) janet_unwrap_string(kv->key), - (char *) janet_unwrap_string(kv->value) - ); - } - - if (!ChkType("xml-children", JANET_ARRAY)) - { - return ret; - } - children = janet_unwrap_array(v); - - for (i = 0; i < children->capacity; i++) - { - Janet cJanet = children->data[i]; - XMLElement *cXML = NULL; - if (!janet_checktype(cJanet, JANET_TABLE)) - { - continue; - } - - cXML = ExtensionsToXML(janet_unwrap_table(cJanet)); - XMLAddChild(ret, cXML); - } - } - - return ret; -} -Janet -ExtensionsFromXML(JanetArray *in, XMLElement *e) -{ - JanetArray *children = NULL; - JanetTable *table; - size_t i, len; - char *type = NULL; - if (!e) - { - return janet_wrap_nil(); - } - - table = janet_table(0); - switch (e->type) - { - case XML_ELEMENT_TAG: - type = "tag"; - break; - case XML_ELEMENT_DATA: - case XML_ELEMENT_CDATA: - type = "data"; - break; - case XML_ELEMENT_PI: - type = "pi"; - break; - case XML_ELEMENT_UNKNOWN: - type = "unknown"; - break; - } - - InitialiseBasicXML(table, type); - if (e->type == XML_ELEMENT_DATA || e->type == XML_ELEMENT_PI) - { - janet_table_put(table, - janet_ckeywordv("xml-data"), janet_cstringv(e->data) - ); - } - else if (e->type == XML_ELEMENT_TAG) - { - char *attr, *val; - JanetTable *attrs; - children = janet_array(ArraySize(e->children)); - janet_table_put(table, - janet_ckeywordv("xml-name"), janet_cstringv(e->name) - ); - for (i = 0, len = ArraySize(e->children); i < len; i++) - { - ExtensionsFromXML(children, ArrayGet(e->children, i)); - } - janet_table_put(table, - janet_ckeywordv("xml-children"), janet_wrap_array(children) - ); - - attrs = janet_table(0); - while (HashMapIterate(e->attrs, &attr, (void **) &val)) - { - janet_table_put(attrs, - janet_cstringv(attr), janet_cstringv(val) - ); - } - janet_table_put(table, - janet_ckeywordv("xml-attrs"), janet_wrap_table(attrs) - ); - } - if (children && in) - { - janet_array_push(in, janet_wrap_table(table)); - } - return janet_wrap_table(table); -} -static Janet -JanetXMLAddAttr(int32_t argc, Janet *argv) -{ - Janet self_v, key_v, val_v, attr_v; - JanetTable *self, *attrs; - char *key, *val; - - janet_fixarity(argc, 3); - - if (!janet_checktype((self_v = argv[0]), JANET_TABLE)) - { - return janet_wrap_nil(); - } - if (!janet_checktype((key_v = argv[1]), JANET_STRING)) - { - return janet_wrap_nil(); - } - if (!janet_checktype((val_v = argv[2]), JANET_STRING)) - { - return janet_wrap_nil(); - } - key = (char *) janet_unwrap_string(key_v); - val = (char *) janet_unwrap_string(val_v); - - self = janet_unwrap_table(self_v); - attr_v = janet_table_get(self, janet_ckeywordv("xml-attrs")); - if (!janet_checktype(attr_v, JANET_TABLE)) - { - return janet_wrap_nil(); - } - attrs = janet_unwrap_table(attr_v); - - janet_table_put(attrs, janet_cstringv(key), janet_cstringv(val)); - - return janet_wrap_nil(); -} -static Janet -XMLToString(int32_t argc, Janet *argv) -{ - XMLElement *xml; - Stream *fake; - Janet ret; - char *buf = NULL; - janet_fixarity(argc, 1); - - if (!janet_checktype(argv[0], JANET_TABLE)) - { - return janet_wrap_nil(); - } - xml = ExtensionsToXML(janet_gettable(argv, 0)); - if (!xml) - { - return janet_wrap_nil(); - } - - fake = StrStreamWriter(&buf); - XMLEncode(fake, xml); - StreamFlush(fake); - StreamClose(fake); - XMLFreeElement(xml); - - ret = janet_cstringv(buf); - Free(buf); - return ret; -} - -Janet -JanetCreateXMLData(int32_t argc, Janet *argv) -{ - Janet parent_v, text_v; - JanetTable *parent; - JanetTable *ret; - char *text; - - janet_arity(argc, 1, 2); - if (argc == 1) - { - text_v = argv[0]; - parent_v = janet_wrap_nil(); - } - else - { - parent_v = argv[0]; - text_v = argv[1]; - } - - if (janet_type(text_v) != JANET_STRING) - { - return janet_wrap_nil(); - } - - text = (char *) janet_unwrap_string(text_v); - ret = janet_table(0); - InitialiseBasicXML(ret, "data"); - janet_table_put(ret, - janet_ckeywordv("xml-data"), janet_cstringv(text) - ); - - if (janet_type(parent_v) == JANET_TABLE) - { - JanetArray *children; - Janet children_v; - - parent = janet_unwrap_table(parent_v); - children_v = janet_table_get(parent, janet_ckeywordv("xml-children")); - if (janet_type(children_v) != JANET_ARRAY) - { - goto end; - } - children = janet_unwrap_array(children_v); - - janet_array_push(children, janet_wrap_table(ret)); - } - -end: - return janet_wrap_table(ret); -} -Janet -JanetCreateXMLTag(int32_t argc, Janet *argv) -{ - Janet parent_v, text_v; - JanetTable *parent; - JanetTable *ret; - char *text; - - janet_arity(argc, 1, 2); - if (argc == 1) - { - text_v = argv[0]; - parent_v = janet_wrap_nil(); - } - else - { - parent_v = argv[0]; - text_v = argv[1]; - } - - if (janet_type(text_v) != JANET_STRING) - { - return janet_wrap_nil(); - } - - text = (char *) janet_unwrap_string(text_v); - ret = janet_table(0); - - InitialiseBasicXML(ret, "tag"); - janet_table_put(ret, - janet_ckeywordv("xml-name"), janet_cstringv(text) - ); - - if (janet_type(parent_v) == JANET_TABLE) - { - JanetArray *children; - Janet children_v; - - parent = janet_unwrap_table(parent_v); - children_v = janet_table_get(parent, janet_ckeywordv("xml-children")); - if (janet_type(children_v) != JANET_ARRAY) - { - goto end; - } - children = janet_unwrap_array(children_v); - - janet_array_push(children, janet_wrap_table(ret)); - } - -end: - return janet_wrap_table(ret); -} -void -InitialiseBasicXML(JanetTable *table, char *type) -{ - janet_table_put(table, - janet_ckeywordv("stringify"), janet_wrap_cfunction(XMLToString) - ); - janet_table_put(table, - janet_ckeywordv("set-attr"), janet_wrap_cfunction(JanetXMLAddAttr) - ); - janet_table_put(table, - janet_ckeywordv("xml-type"), janet_cstringv(type) - ); - if (StrEquals(type, "tag")) - { - janet_table_put(table, - janet_ckeywordv("xml-attrs"), janet_wrap_table(janet_table(0)) - ); - janet_table_put(table, - janet_ckeywordv("xml-children"), janet_wrap_array(janet_array(0)) - ); - } -} -#endif diff --git a/src/HttParsee.c b/src/HttParsee.c index c47728b..572487c 100644 --- a/src/HttParsee.c +++ b/src/HttParsee.c @@ -22,9 +22,6 @@ ParseeRequest(HttpServerContext *ctx, void *argp) /* Basic headers */ HttpResponseStatus(ctx, HTTP_OK); HttpResponseHeader(ctx, "Server", NAME "/v" VERSION); -#ifdef PLATFORM_WINDOWS - HttpResponseHeader(ctx, "X-Powered-By", "some weirdows"); -#endif HttpResponseHeader(ctx, "Connection", "close"); arg.data = data; @@ -76,7 +73,7 @@ ParseeCreateRequest(const ParseeConfig *conf, HttpRequestMethod meth, char *path ctx = HttpRequest( meth, - HTTP_FLAG_TLS, + conf->homeserver_tls ? HTTP_FLAG_TLS : HTTP_FLAG_NONE, conf->homeserver_port, conf->homeserver_host, path ); diff --git a/src/Main.c b/src/Main.c index baeb984..10c809f 100644 --- a/src/Main.c +++ b/src/Main.c @@ -13,16 +13,11 @@ #include #include -#include +#include #include #include #include -#ifdef JANET - #include -#endif -#include - static HttpServer *server = NULL; static pthread_t xmpp_thr; static XMPPComponent *jabber = NULL; @@ -68,23 +63,39 @@ ParseeCheckMatrix(void *datp) { static volatile uint64_t streak = 0; ParseeData *data = datp; - if (!ASPing(data->config)) + if (data->config->accept_pings && !ASPing(data->config)) { Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host); - if (++streak >= 5) + if (++streak == 10) { - Log(LOG_ERR, "This has been at least the fifth time in a row."); - Log(LOG_ERR, "Please check if your homeserver is active."); - Log(LOG_ERR, "%s shall now exit...", NAME); + DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats"); + HashMap *json = DbJson(ref); + HashMap *mucs = GrabObject(json, 1, "mucs"); + char *muc; + void *ignored; - pthread_mutex_lock(&data->halt_lock); - data->halted = true; - pthread_mutex_unlock(&data->halt_lock); - XMPPFinishCompStream(data->jabber); - pthread_join(xmpp_thr, NULL); - Log(LOG_INFO, "Stopping server..."); - HttpServerStop(data->server); + /* Notify any potential MUCs about this */ + while (HashMapIterate(mucs, &muc, &ignored)) + { + char *id = StrRandom(32); + char *sender = StrConcat(3, "parsee@", data->jabber->host, "/parsee"); + StanzaBuilder *b = CreateStanzaBuilder(sender, muc, id); + SetStanzaType(b, "groupchat"); + SetStanzaBody(b, + "This bridge hasn't been able to reach the Matrix host, and " + "as such, some messages may not have been sent over." + ); + + WriteoutStanza(b, data->jabber, 0); + DestroyStanzaBuilder(b); + + Free(sender); + Free(id); + } + (void) ignored; + + DbUnlock(data->db, ref); } return; } @@ -104,8 +115,6 @@ Main(Array *args, HashMap *env) int http = 8; int verbose = 0; - Extensions *ext_ctx = NULL; - start = UtilTsMillis(); memset(&conf, 0, sizeof(conf)); @@ -115,11 +124,8 @@ Main(Array *args, HashMap *env) ); ParseePrintASCII(); Log(LOG_INFO, "======================="); - Log(LOG_INFO, "(C)opyright 2024 LDA and other contributors"); + Log(LOG_INFO, "(C)opyright 2024-2025 LDA and other contributors"); Log(LOG_INFO, "(This program is free software, see LICENSE.)"); -#ifdef JANET - Log(LOG_INFO, "**Built with Janet!**"); -#endif #ifdef PLATFORM_IPHONE Log(LOG_WARNING, "Wait. Are you running this on an iPhone?"); @@ -217,9 +223,11 @@ Main(Array *args, HashMap *env) Log(LOG_NOTICE, "Connecting to XMPP..."); jabber = XMPPInitialiseCompStream( + parsee_conf->component_addr, parsee_conf->component_host, parsee_conf->component_port ); + Log(LOG_NOTICE, "Connecting to XMPP... %p", jabber); if (!XMPPAuthenticateCompStream( jabber, parsee_conf->shared_comp_secret @@ -279,22 +287,18 @@ Main(Array *args, HashMap *env) Log(LOG_DEBUG, "Verbosity level: %d", verbose); ((ParseeData *) conf.handlerArgs)->verbosity = verbose; - ext_ctx = ExtensionCreateContext(conf.handlerArgs); - ((ParseeData *) conf.handlerArgs)->exts = ext_ctx; - ExtensionLoadDir(ext_ctx, parsee_conf->extensions); - Log(LOG_NOTICE, "Setting up local Matrix user..."); if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart)) { char *parsee = ParseeMXID(conf.handlerArgs); - /* TODO: An hardcoded avatar like this sucks. */ ASSetAvatar(parsee_conf, parsee, "mxc://tedomum.net/" "7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136" ); ASSetName(parsee_conf, parsee, "Parsee bridge"); + Free(parsee); } @@ -346,8 +350,6 @@ end: ParseeDestroyHeadTable(); ParseeDestroyJIDTable(); - ExtensionDestroyContext(ext_ctx); - (void) env; return 0; } diff --git a/src/MatrixEventHandler.c b/src/MatrixEventHandler.c index 3d6a69e..6311fac 100644 --- a/src/MatrixEventHandler.c +++ b/src/MatrixEventHandler.c @@ -21,13 +21,11 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char char *sender = GrabString(event, 1, "sender"); Unistr *uninick = UnistrCreate(name); - Unistr *filtered = UnistrFilter(uninick, UnistrIsBMP); + Unistr *filtered = UnistrFilter(uninick, UnistrIsASCII); /* I'm not even going to try messing with the BMP anymore. */ char *nick = UnistrC(filtered); char *rev = StrConcat(3, muc, "/", nick); int nonce = 0; - Log(LOG_DEBUG, "MUCJOINER: filtered '%s' to '%s'", name, nick); - UnistrFree(uninick); UnistrFree(filtered); @@ -102,7 +100,7 @@ ParseeMemberHandler(ParseeData *data, HashMap *event) char *jid = ParseeEncodeMXID(state_key); char *sha = NULL, *mime = NULL; char *avatar = ASGetAvatar(data->config, NULL, state_key); - char *url = ParseeToUnauth(data, avatar); + char *url = ParseeToUnauth(data, avatar, NULL); chat_id = ParseeGetFromRoomID(data, room_id); ASGetMIMESHA(data->config, avatar, &mime, &sha); @@ -257,14 +255,9 @@ ParseeBotHandler(ParseeData *data, HashMap *event) return; } - if (*body != '!') + if (!body || *body != '!') { /* All commands are to be marked with a ! */ - Free(ASSend( - data->config, id, profile, - "m.room.message", - MatrixCreateNotice("Please enter a valid command"), 0 - )); Free(profile); return; } @@ -296,10 +289,10 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) char *room_id = GrabString(event, 1, "room_id"); char *matrix_sender = GrabString(event, 1, "sender"); char *chat_id = NULL, *muc_id = NULL; - char *user; + char *user = NULL; - DbRef *room_data; - HashMap *data_json; + DbRef *room_data = NULL; + HashMap *data_json = NULL; bool direct = false; if (!data || !event || !from || !to) @@ -331,8 +324,8 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) } else { - char *matrix_name, *matrix_avatar; - char *mime, *sha; + char *matrix_name = NULL, *matrix_avatar = NULL; + char *mime = NULL, *sha = NULL; muc_id = ParseeGetMUCID(data, chat_id); if (!chat_id) @@ -365,28 +358,47 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to) static void ParseeMessageHandler(ParseeData *data, HashMap *event) { + if (!data || !event) + { + return; + } XMPPComponent *jabber = data->jabber; - StanzaBuilder *builder; + StanzaBuilder *builder = NULL; DbRef *ref = NULL; - HashMap *json; + HashMap *json = NULL; + char *msgtype = GrabString(event, 2, "content", "msgtype"); + char *m_sender = GrabString(event, 1, "sender"); + char *unedited_id = NULL; char *body = GrabString(event, 2, "content", "body"); char *id = GrabString(event, 1, "room_id"); char *ev_id = GrabString(event, 1, "event_id"); - char *m_sender = GrabString(event, 1, "sender"); - char *chat_id, *muc_id; + char *chat_id = NULL, *muc_id = NULL; char *reply_id = MatrixGetReply(event); - char *xepd = ParseeXMPPify(event); + char *xepd = ParseeXMPPify(data, event); char *type, *user, *xmppified_user = NULL, *to = NULL; char *unauth = NULL; char *origin_id = NULL, *stanza = NULL; char *sender = NULL; - char *unedited_id = MatrixGetEdit(event); char *url = GrabString(event, 2, "content", "url"); char *encoded_from = NULL; bool direct = false; + unedited_id = MatrixGetEdit(event); + if (unedited_id) + { + char *new_content = GrabString(event, 3, "content", "m.new_content", "body"); + if (new_content) body = new_content; + } + + if (data->config->ignore_bots && StrEquals(msgtype, "m.notice")) + { + Free(reply_id); + Free(xepd); + Free(unedited_id); + return; + } if (ParseeIsPuppet(data->config, m_sender) || ParseeManageBan(data, m_sender, id)) { @@ -417,7 +429,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) type = direct ? "chat" : "groupchat"; user = GrabString(json, 1, "xmpp_user"); - unauth = ParseeToUnauth(data, url); + unauth = ParseeToUnauth(data, url, GrabString(event, 2, "content", "filename")); encoded_from = ParseeEncodeMXID(m_sender); xmppified_user = StrConcat(3, @@ -454,6 +466,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event) Free(name); Free(avatar); } + if (reply_id) { /* TODO: Monocles chat DM users HATE this trick! @@ -510,8 +523,8 @@ end: Free(stanza); Free(sender); Free(unauth); - Free(unedited_id); Free(encoded_from); + Free(unedited_id); DbUnlock(data->db, ref); ref = NULL; diff --git a/src/MatrixID.c b/src/MatrixID.c index 4488fd4..bb7c584 100644 --- a/src/MatrixID.c +++ b/src/MatrixID.c @@ -1,6 +1,9 @@ #include #include +#include +#include +#include #include @@ -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; +} diff --git a/src/Parsee/Config.c b/src/Parsee/Config.c index 46b37fa..b95e5e1 100644 --- a/src/Parsee/Config.c +++ b/src/Parsee/Config.c @@ -36,6 +36,12 @@ ParseeConfigLoad(char *conf) return; } json = JsonDecode(stream); + if (!json) + { + Log(LOG_ERR, "Could not parse config JSON"); + StreamClose(stream); + return; + } config = Malloc(sizeof(*config)); #define CopyToStr(to, str) config->to = StrDuplicate( \ @@ -44,6 +50,9 @@ ParseeConfigLoad(char *conf) #define CopyToInt(to, str) config->to = (int) ( \ JsonValueAsInteger(HashMapGet(json, str)) \ ) +#define CopyToBool(to, str) config->to = (int) ( \ + JsonValueAsBoolean(HashMapGet(json, str)) \ + ) config->http_threads = 8; config->xmpp_threads = 8; @@ -58,19 +67,26 @@ ParseeConfigLoad(char *conf) CopyToStr(server_base, "hs_base"); CopyToStr(homeserver_host, "hs_host"); CopyToInt(homeserver_port, "hs_port"); + CopyToBool(homeserver_tls, "hs_tls"); + if (!HashMapGet(json, "hs_tls")) + { + config->homeserver_tls = true; + } + CopyToBool(accept_pings, "accept_pings"); CopyToInt(component_port, "component_port"); + CopyToStr(component_addr, "component_addr"); CopyToStr(component_host, "component_host"); CopyToStr(shared_comp_secret, "shared_secret"); CopyToInt(max_stanza_size, "max_stanza_size"); - - CopyToStr(extensions, "extensions"); if (!config->max_stanza_size) { /* Standard XMPP "minimum" maximum */ config->max_stanza_size = 10000; } + CopyToBool(ignore_bots, "ignore_bots"); + CopyToStr(media_base, "media_base"); CopyToStr(db_path, "db"); @@ -131,6 +147,7 @@ ParseeConfigFree(void) return; } Free(config->component_host); + Free(config->component_addr); Free(config->shared_comp_secret); Free(config->db_path); Free(config->homeserver_host); @@ -142,7 +159,6 @@ ParseeConfigFree(void) Free(config->namespace_base); Free(config->media_base); Free(config->listen_as); - Free(config->extensions); Free(config); } const ParseeConfig * diff --git a/src/Parsee/Data.c b/src/Parsee/Data.c index afdf188..46d1433 100644 --- a/src/Parsee/Data.c +++ b/src/Parsee/Data.c @@ -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); @@ -130,7 +132,6 @@ ParseeCleanup(void *datp) char *chat; size_t i; uint64_t ts = UtilTsMillis(); - size_t entries = 0; chats = DbList(data->db, 1, "chats"); @@ -174,7 +175,6 @@ ParseeCleanup(void *datp) if (cleaned > threshold) \ { \ DbDelete(data->db, 4, "chats", chat, #field"s", field); \ - entries++; \ } \ Free(field); \ } \ @@ -231,7 +231,6 @@ ParseeCleanup(void *datp) if (cleaned > threshold) \ { \ JsonValueFree(HashMapDelete(field##s, field)); \ - entries++; \ } \ Free(field); \ } \ @@ -726,13 +725,3 @@ ParseeIsMediaEnabled(ParseeData *data, char *chat_id) return ret; } -bool -ParseeIsMUC(ParseeData *data, char *jid) -{ - if (!data) - { - return false; - } - - return XMPPQueryMUC(data->jabber, jid, NULL); -} diff --git a/src/Parsee/User.c b/src/Parsee/User.c index e77f20a..6e673bb 100644 --- a/src/Parsee/User.c +++ b/src/Parsee/User.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -538,7 +539,6 @@ ParseeGetMUCID(ParseeData *data, char *chat_id) return ret; } - void ParseeSendPresence(ParseeData *data) { @@ -562,7 +562,7 @@ ParseeSendPresence(ParseeData *data) uint64_t ts = GrabInteger(DbJson(chat), 1, "ts"); int diff = ts ? (int) ((UtilTsMillis() - ts) / 1000) : -1; /* Make a fake user join the MUC */ - Log(LOG_NOTICE, "Sending presence to %s last=%ds", rev, diff); + Log(LOG_NOTICE, "Sending presence to %s", rev); XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false); DbUnlock(data->db, chat); @@ -688,12 +688,13 @@ end: #include char * -ParseeToUnauth(ParseeData *data, char *mxc) +ParseeToUnauth(ParseeData *data, char *mxc, char *filename) { Uri *url = NULL; char *ret; char *key, *hmac; -#define PAT "%s/_matrix/client/v1/media/download/%s%s?hmac=%s" +#define PAT "%s/media/%s%s?hmac=%s" +#define PATF "%s/media/%s%s/%s?hmac=%s" size_t l; if (!data || !mxc) { @@ -714,19 +715,45 @@ ParseeToUnauth(ParseeData *data, char *mxc) hmac = ParseeHMACS(data->id, key); Free(key); - l = snprintf(NULL, 0, - PAT, - data->config->media_base, - url->host, url->path, - hmac - ); + if (!filename) + { + l = snprintf(NULL, 0, + PAT, + data->config->media_base, + url->host, url->path, + hmac + ); + } + else + { + char *encoded = HttpUrlEncode(filename); + l = snprintf(NULL, 0, + PATF, + data->config->media_base, + url->host, url->path, encoded, + hmac + ); + Free(encoded); + } ret = Malloc(l + 3); - snprintf(ret, l + 1, - PAT, - data->config->media_base, - url->host, url->path, - hmac - ); + if (!filename) + { + snprintf(ret, l + 1, + PAT, + data->config->media_base, + url->host, url->path, + hmac + ); + } + else + { + snprintf(ret, l + 1, + PATF, + data->config->media_base, + url->host, url->path, filename, + hmac + ); + } UriFree(url); Free(hmac); return ret; diff --git a/src/Parsee/Utils/Formatting.c b/src/Parsee/Utils/Formatting.c index ea97b2c..ec95aef 100644 --- a/src/Parsee/Utils/Formatting.c +++ b/src/Parsee/Utils/Formatting.c @@ -5,11 +5,14 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -17,7 +20,7 @@ typedef struct XMPPFlags { bool quote; } XMPPFlags; static char * -XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) +XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPFlags flags) { char *xepd = NULL, *tmp = NULL; @@ -55,7 +58,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) } \ } \ while (0) - switch (elem->type) + switch (elem ? elem->type : -1) { case XML_ELEMENT_DATA: Concat(elem->data); @@ -67,7 +70,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); + subxep = XMPPifyElement(conf, event, child, flags); Concat(subxep); Free(subxep); @@ -80,7 +83,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); + subxep = XMPPifyElement(conf, event, child, flags); Concat(subxep); Free(subxep); @@ -93,7 +96,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); + subxep = XMPPifyElement(conf, event, child, flags); Concat(subxep); Free(subxep); @@ -126,7 +129,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); + subxep = XMPPifyElement(conf, event, child, flags); Concat(subxep); Free(subxep); @@ -141,36 +144,70 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); + subxep = XMPPifyElement(conf, event, child, flags); Concat(subxep); Free(subxep); } - Concat("\n"); + if (i != 0) + { + Concat("\n"); + } } 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. */ - for (i = 0; i < ArraySize(elem->children); i++) + Uri *pref = UriParse(href); + if (pref && 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) + { + char *real_id = StrConcat(4, "@", id->localpart, ":", id->server); + /* TODO: Detect if it already is a Parsee user */ + if (ParseeIsPuppet(conf, real_id)) + { + char *name = ASGetName(conf, NULL, real_id); + Concat((name ? name : real_id)); + Free(name); + } + else + { + Concat(real_id); + } + Free(real_id); + } + else + { + Concat(href); + } + + Free(id); } - Concat("< "); - Concat(href); - Concat(" >"); + else + { + for (i = 0; i < ArraySize(elem->children); i++) + { + child = ArrayGet(elem->children, i); + subxep = XMPPifyElement(conf, event, child, flags); + + Concat(subxep); + Free(subxep); + } + Concat(" < "); + Concat(href); + Concat(" >"); + } + UriFree(pref); } else { for (i = 0; i < ArraySize(elem->children); i++) { child = ArrayGet(elem->children, i); - subxep = XMPPifyElement(event, child, flags); + subxep = XMPPifyElement(conf, event, child, flags); Concat(subxep); Free(subxep); @@ -182,8 +219,45 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags) } return xepd; } +static char * +GetRawBody(HashMap *event) +{ + void *id; + if ((id = MatrixGetEdit(event))) + { + char *new = GrabString(event, 3, "content", "m.new_content", "body"); + Free(id); + if (new) + { + return new; + } + } + return GrabString(event, 2, "content", "body"); +} +static char * +GetHTMLBody(HashMap *event) +{ + if (MatrixGetEdit(event)) + { + char *new = GrabString(event, 3, "content", "m.new_content", "formatted_body"); + if (new) + { + return new; + } + } + return GrabString(event, 2, "content", "formatted_body"); +} +static char * +GetBodyFormat(HashMap *event) +{ + if (MatrixGetEdit(event)) + { + return GrabString(event, 3, "content", "m.new_content", "format"); + } + return GrabString(event, 2, "content", "format"); +} char * -ParseeXMPPify(HashMap *event) +ParseeXMPPify(ParseeData *data, HashMap *event) { char *type, *format, *html; char *xepd = NULL; @@ -202,20 +276,28 @@ ParseeXMPPify(HashMap *event) return NULL; } - format = JsonValueAsString(JsonGet(event, 2, "content", "format")); - if (!StrEquals(format, "org.matrix.custom.html")) + if (!StrEquals(GetBodyFormat(event), "org.matrix.custom.html")) { /* Settle for the raw body instead. */ - char *body = JsonValueAsString(JsonGet(event, 2, "content", "body")); + char *body = GetRawBody(event); return StrDuplicate(body); } - html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body")); + html = GetHTMLBody(event); + html = StrConcat(3, "", html, ""); elem = XMLCDecode(StrStreamReader(html), true, true); + if (!elem) + { + /* Settle for the raw body instead. + * TODO: Have the parser be more leinent on errors in HTML mode. */ + char *body = GetRawBody(event); + Free(html); + return StrDuplicate(body); + } flags.quote = false; - xepd = XMPPifyElement(event, elem, flags); + xepd = XMPPifyElement(data ? data->config : NULL, event, elem, flags); XMLFreeElement(elem); Free(html); diff --git a/src/Parsee/Utils/Nofly.c b/src/Parsee/Utils/Nofly.c index 8a82c0f..439c7d0 100644 --- a/src/Parsee/Utils/Nofly.c +++ b/src/Parsee/Utils/Nofly.c @@ -6,6 +6,28 @@ #include #include +void +ParseeGlobalUnban(ParseeData *data, char *glob) +{ + DbRef *ref; + HashMap *j; + if (!data || !glob) + { + return; + } + + ref = DbLock(data->db, 1, "global_bans"); + if (!ref) + { + ref = DbCreate(data->db, 1, "global_bans"); + } + + j = DbJson(ref); + + JsonValueFree(HashMapDelete(j, glob)); + + DbUnlock(data->db, ref); +} void ParseeGlobalBan(ParseeData *data, char *glob, char *reason) { diff --git a/src/Routes/Media.c b/src/Routes/Media.c index 7d6bc68..498eba2 100644 --- a/src/Routes/Media.c +++ b/src/Routes/Media.c @@ -10,6 +10,37 @@ #include +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; @@ -17,7 +48,7 @@ RouteHead(RouteMedia, arr, argp) HashMap *reqh, *params; char *server = ArrayGet(arr, 0); char *identi = ArrayGet(arr, 1); - char *path, *key, *val; + char *key, *val; char *hmac, *chkmak = NULL; params = HttpRequestParams(args->ctx); @@ -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; } diff --git a/src/Routes/Ping.c b/src/Routes/Ping.c index a508336..1d4191e 100644 --- a/src/Routes/Ping.c +++ b/src/Routes/Ping.c @@ -10,8 +10,6 @@ RouteHead(RoutePing, arr, argp) ParseeHttpArg *args = argp; HashMap *request = NULL; HashMap *response = NULL; - Array *events; - size_t i; response = ASVerifyRequest(args); if (response) diff --git a/src/Routes/Transactions.c b/src/Routes/Transactions.c index 4ef0bee..580e43c 100644 --- a/src/Routes/Transactions.c +++ b/src/Routes/Transactions.c @@ -34,10 +34,6 @@ RouteHead(RouteTxns, arr, argp) for (i = 0; i < ArraySize(events); i++) { HashMap *event = JsonValueAsObject(ArrayGet(events, i)); - if (ExtensionPushEvent(args->data->exts, event)) - { - continue; - } ParseeEventHandler(args->data, event); } diff --git a/src/Signal.c b/src/Signal.c index e9d00e8..3f01dcb 100644 --- a/src/Signal.c +++ b/src/Signal.c @@ -26,11 +26,6 @@ SignalHandler(int signal) HttpServerStop(data->server); return; } - if (signal == SIGPIPE) - { - Log(LOG_DEBUG, "Caught a SIGPIPE..."); - return; - } } bool @@ -46,7 +41,7 @@ ParseeInitialiseSignals(ParseeData *d, pthread_t xmpp) sa.sa_flags = SA_RESTART; #define Register(act) (sigaction(act, &sa, NULL) >= 0) - if (!Register(SIGTERM) || !Register(SIGINT) || !Register(SIGPIPE)) + if (!Register(SIGTERM) || !Register(SIGINT)) { Log(LOG_ERR, "Couldn't register signals..."); return false; diff --git a/src/Unistr.c b/src/Unistr.c index 5eaae43..7eab2e0 100644 --- a/src/Unistr.c +++ b/src/Unistr.c @@ -192,6 +192,16 @@ UnistrGetch(Unistr *unistr, size_t i) return i < unistr->length ? unistr->codepoints[i] : 0; } bool +UnistrIsASCII(uint32_t u) +{ + if (u == 0) + { + return NULL; + } + + return u < 0x7F; +} +bool UnistrIsBMP(uint32_t u) { if (u == 0) @@ -267,3 +277,38 @@ UnistrGetOffset(Unistr *str, uint32_t sep) } return 0; } +size_t +UnistrGetUTFOffset(char *cstr, size_t unicode) +{ + Unistr *tmp; + size_t ret = 0; + if (!cstr) + { + return 0; + } + + tmp = UnistrCreate(cstr); + for (size_t i = 0; i < unicode && i < tmp->length; i++) + { + uint32_t codepoint = tmp->codepoints[i]; + if (codepoint >= 0x0000 && codepoint <= 0x007F) + { + ret += 1; + } + else if (codepoint >= 0x0080 && codepoint <= 0x07FF) + { + ret += 2; + } + else if (codepoint >= 0x0800 && codepoint <= 0xFFFF) + { + ret += 3; + } + else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) + { + ret += 4; + } + } +end: + Free(tmp); + return ret; +} diff --git a/src/XEP-0393.c b/src/XEP-0393.c index a0f6be9..784bacf 100644 --- a/src/XEP-0393.c +++ b/src/XEP-0393.c @@ -329,8 +329,8 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent) break; case XEP393_MONO: head = XMLCreateTag("code"); + XMLAddChild(xmlparent, XMLCreateText("`")); XMLAddChild(xmlparent, head); - XMLAddChild(head, XMLCreateText("`")); break; case XEP393_SRKE: head = XMLCreateTag("s"); @@ -372,7 +372,7 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent) XMLAddChild(head, XMLCreateText("_")); break; case XEP393_MONO: - XMLAddChild(head, XMLCreateText("`")); + XMLAddChild(xmlparent, XMLCreateText("`")); break; case XEP393_SRKE: XMLAddChild(head, XMLCreateText("~")); diff --git a/src/XML/Parser.c b/src/XML/Parser.c index 506e49d..350f0e4 100644 --- a/src/XML/Parser.c +++ b/src/XML/Parser.c @@ -31,6 +31,12 @@ XMLCDecode(Stream *stream, bool autofree, bool html) bool flag = false; switch (event->type) { + case XML_ERROR: + XMLFreeEvent(event); + XMLFreeElement(ret); + ArrayFree(stack); + XMLFreeLexer(lexer); + return NULL; case XML_LEXER_STARTELEM: /* Create a new element that will populated. */ top = XMLCreateTag(event->element); diff --git a/src/XML/SAX.c b/src/XML/SAX.c index 21430bb..defea6b 100644 --- a/src/XML/SAX.c +++ b/src/XML/SAX.c @@ -58,6 +58,7 @@ 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 * XMLCreateError(XMLexer *lexer); static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end); static XMLEvent * XMLCreateData(XMLexer *lexer); @@ -198,7 +199,9 @@ XMLCrank(XMLexer *lexer) else if (XMLookahead(lexer, "--", false)) { /* Throw error */ - return NULL; + XMLFreeEvent(event); + event = XMLCreateError(lexer); + break; } break; case XML_STATE_PI: @@ -215,6 +218,9 @@ XMLCrank(XMLexer *lexer) if (!attrname) { /* TODO: Throw error */ + XMLFreeEvent(event); + event = XMLCreateError(lexer); + break; } XMLPushElement(lexer, attrname); @@ -241,7 +247,10 @@ XMLCrank(XMLexer *lexer) } else if (XMLookahead(lexer, "'", true)) { - while (true); + //while (true); uh oh + XMLFreeEvent(event); + event = XMLCreateError(lexer); + break; } break; case XML_STATE_ATTRTAIL: @@ -250,6 +259,8 @@ XMLCrank(XMLexer *lexer) if (!XMLookahead(lexer, ">", true)) { /* TODO: Throw error. */ + XMLFreeEvent(event); + event = XMLCreateError(lexer); break; } lexer->state = XML_STATE_NONE; @@ -258,6 +269,8 @@ XMLCrank(XMLexer *lexer) break; default: /* TODO */ + XMLFreeEvent(event); + event = XMLCreateError(lexer); break; } /* TODO: Crank our XML parser. */ @@ -693,6 +706,26 @@ XMLCreateData(XMLexer *lexer) return event; } static XMLEvent * +XMLCreateError(XMLexer *lexer) +{ + XMLEvent *event = Malloc(sizeof(*event)); + size_t elements = ArraySize(lexer->data.elements); + + event->type = XML_ERROR; + 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 XMLEvent * XMLCreateRelax(XMLexer *lexer) { XMLEvent *event = Malloc(sizeof(*event)); diff --git a/src/XMPP/Component.c b/src/XMPP/Component.c index 42e2992..e3ff4e4 100644 --- a/src/XMPP/Component.c +++ b/src/XMPP/Component.c @@ -19,7 +19,7 @@ #define DEFAULT_PROSODY_PORT 5347 XMPPComponent * -XMPPInitialiseCompStream(char *host, int port) +XMPPInitialiseCompStream(char *addr, char *host, int port) { int sd = -1; struct addrinfo hints, *res, *res0; @@ -28,12 +28,17 @@ XMPPInitialiseCompStream(char *host, int port) Stream *stream; XMPPComponent *comp; + if (!addr) + { + addr = host; + } + snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - error = getaddrinfo(host, serv, &hints, &res0); + error = getaddrinfo(addr, serv, &hints, &res0); if (error) { const char *error_str = gai_strerror(error); @@ -56,6 +61,10 @@ XMPPInitialiseCompStream(char *host, int port) if (connect(sd, res->ai_addr, res->ai_addrlen) < 0) { + Log(LOG_ERR, + "%s: cannot connect to '%s': %s", __func__, + host, strerror(errno) + ); close(sd); sd = -1; continue; @@ -66,6 +75,10 @@ XMPPInitialiseCompStream(char *host, int port) if (sd < 0) { + Log(LOG_ERR, + "%s: cannot connect to '%s': no socket available", __func__, + host + ); return NULL; } freeaddrinfo(res0); @@ -73,6 +86,10 @@ XMPPInitialiseCompStream(char *host, int port) stream = StreamFd(sd); if (!stream) { + Log(LOG_ERR, + "%s: cannot connect to '%s': %s", __func__, + host, "couldn't create a Cytoplasm stream" + ); close(sd); return NULL; } @@ -165,6 +182,49 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared) { Log(LOG_ERR, "Excepted empty handshake reply, got nonsense."); Log(LOG_ERR, "Another service (possibly Parsee) may have taken over."); + while ((ev = XMLCrank(sax))) + { + char *key, *val; + switch (ev->type) + { + case XML_LEXER_STARTELEM: + Log(LOG_DEBUG, "<%s>", ev->element); + + LogConfigIndent(LogConfigGlobal()); + LogConfigIndent(LogConfigGlobal()); + /* TODO: Log out attributes a little better */ + while (HashMapIterate(ev->attrs, &key, (void **) &val)) + { + Log(LOG_DEBUG, "(%s=%s)", key, val); + } + LogConfigUnindent(LogConfigGlobal()); + LogConfigUnindent(LogConfigGlobal()); + + LogConfigIndent(LogConfigGlobal()); + break; + case XML_LEXER_ELEM: + Log(LOG_DEBUG, "<%s/>", ev->element); + LogConfigIndent(LogConfigGlobal()); + LogConfigIndent(LogConfigGlobal()); + /* TODO: Log out attributes a little better */ + while (HashMapIterate(ev->attrs, &key, (void **) &val)) + { + Log(LOG_DEBUG, "(%s=%s)", key, val); + } + LogConfigUnindent(LogConfigGlobal()); + LogConfigUnindent(LogConfigGlobal()); + break; + case XML_LEXER_ENDELEM: + LogConfigUnindent(LogConfigGlobal()); + Log(LOG_DEBUG, "", ev->element); + break; + case XML_LEXER_DATA: + Log(LOG_DEBUG, "%s", ev->data); + break; + } + XMLFreeEvent(ev); + } + LogConfigIndentSet(LogConfigGlobal(), 0); Log(LOG_ERR, ""); Log(LOG_ERR, "Simply jealous of that other service..."); Free(stream_id); diff --git a/src/XMPP/MUC.c b/src/XMPP/MUC.c index 7064698..9ecfcf2 100644 --- a/src/XMPP/MUC.c +++ b/src/XMPP/MUC.c @@ -8,6 +8,8 @@ #include #include +#include "XMPPThread/internal.h" + bool XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out) { @@ -42,6 +44,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out) Free(uuid); if (!iq_query || !StrEquals(iq_query->name, "iq")) { + Log(LOG_ERR, "Didn't receive an stanza"); XMLFreeElement(iq_query); return false; } @@ -53,6 +56,11 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out) "conference")) { XMLFreeElement(iq_query); + Log(LOG_DEBUG, "MUC INFO ERROR"); + Log(LOG_DEBUG, + "identityp=%p category=%s", identity, + identity ? HashMapGet(identity->attrs, "category") : NULL + ); return false; } @@ -162,6 +170,7 @@ bool XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret) { XMLElement *presence, *x, *reply, *history, *photo; + IQFeatures *features; char *from, *id, *stime = "3600"; if (!comp || !fr || !muc) { @@ -189,7 +198,15 @@ XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool XMLAddChild(x, history); XMLAddChild(presence, x); - XMPPAnnotatePresence(presence); + features = LookupJIDFeatures(from); +#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 + XMPPAnnotatePresence(presence, features); + FreeIQFeatures(features); if (hash) { @@ -225,6 +242,7 @@ void XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason) { XMLElement *presence; + IQFeatures *features; char *from, *id; if (!comp || !fr || !muc) { @@ -246,7 +264,16 @@ XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason) XMLAddChild(presence, status); } - XMPPAnnotatePresence(presence); + features = LookupJIDFeatures(from); +#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 + XMPPAnnotatePresence(presence, features); + FreeIQFeatures(features); + XMPPSendStanza(comp, presence, 10000); diff --git a/src/XMPP/MUCServ.c b/src/XMPP/MUCServ.c new file mode 100644 index 0000000..557c4a2 --- /dev/null +++ b/src/XMPP/MUCServ.c @@ -0,0 +1,154 @@ +#include + +#include +#include +#include + +#include +#include + +#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) */ + (void) ret; + 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); +} diff --git a/src/XMPP/Stanza.c b/src/XMPP/Stanza.c index 451c16f..a2daf5a 100644 --- a/src/XMPP/Stanza.c +++ b/src/XMPP/Stanza.c @@ -7,6 +7,8 @@ #include #include +#include + void XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact) @@ -159,27 +161,31 @@ XMPPGetReply(XMLElement *elem) return HashMapGet(rep->attrs, "id"); } -void -XMPPAnnotatePresence(XMLElement *presence) +ssize_t +XMPPGetReplyOffset(XMLElement *elem) { - XMLElement *c; - char *ver; - if (!presence) + if (!elem) { - return; + return -1; } - - ver = XMPPGenerateVer(); - c = XMLCreateTag("c"); - XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps"); - XMLAddAttr(c, "hash", "sha-1"); - XMLAddAttr(c, "node", REPOSITORY); - XMLAddAttr(c, "ver", ver); - - Free(ver); - XMLAddChild(presence, c); + for (size_t i = 0; i < ArraySize(elem->children); i++) + { + XMLElement *child = ArrayGet(elem->children, i); + char *xmlns = HashMapGet(child->attrs, "xmlns"); + char *xfor = HashMapGet(child->attrs, "for"); + if (StrEquals(child->name, "fallback") && + StrEquals(xmlns, "urn:xmpp:feature-fallback:0") && + StrEquals(xfor, "urn:xmpp:reply:0")) + { + XMLElement *body = XMLookForUnique(child, "body"); + if (body && HashMapGet(body->attrs, "end")) + { + return strtol(HashMapGet(body->attrs, "end"), NULL, 10); + } + } + } + return -1; } - char * XMPPGetModeration(XMLElement *stanza) { diff --git a/src/XMPPCommand/Commands.c b/src/XMPPCommand/Commands.c index 8d00598..7b418c6 100644 --- a/src/XMPPCommand/Commands.c +++ b/src/XMPPCommand/Commands.c @@ -4,10 +4,20 @@ #include #include -#include "XMPPCommand/Internal.h" +struct XMPPCommand { + XMPPCmdCallback callback; + char *node, *name; + + char *form_instruction; + char *form_title; + + /* TODO: On-the-fly generation of options */ + Array *options; + XMPPOptionWriter otf; +}; XMPPCommand * -XMPPBasicCmd(XMPPCommandFlags flags, char *node, char *name, XMPPCmdCallback callback_funct) +XMPPBasicCmd(char *node, char *name, XMPPCmdCallback callback_funct) { XMPPCommand *cmd; @@ -22,7 +32,6 @@ XMPPBasicCmd(XMPPCommandFlags flags, char *node, char *name, XMPPCmdCallback cal cmd->name = StrDuplicate(name); cmd->form_instruction = NULL; cmd->form_title = NULL; - cmd->flags = flags; /* No options -> no form required */ cmd->options = NULL; @@ -118,7 +127,7 @@ XMPPFormifyCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from) ArrayFree(cmd->options); cmd->options = NULL; - cmd->otf(m, cmd, from, cmd->node); + cmd->otf(m, cmd, from); } if (!cmd->options) { @@ -175,13 +184,13 @@ XMPPCommandRequiresForm(XMPPCommand *cmd) return cmd ? (cmd->otf || !!cmd->options) : false; } void -XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form, char *node) +XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form) { - if (!m || !cmd || !from || !to || !node) + if (!m || !cmd || !from || !to) { return; } - cmd->callback(m, from, form, to, node); + cmd->callback(m, from, form, to); } bool diff --git a/src/XMPPCommand/Internal.h b/src/XMPPCommand/Internal.h deleted file mode 100644 index 264e83e..0000000 --- a/src/XMPPCommand/Internal.h +++ /dev/null @@ -1,17 +0,0 @@ -#include - -#include - -struct XMPPCommand { - XMPPCmdCallback callback; - char *node, *name; - - char *form_instruction; - char *form_title; - - /* TODO: On-the-fly generation of options */ - Array *options; - XMPPOptionWriter otf; - - XMPPCommandFlags flags; -}; diff --git a/src/XMPPCommand/Manager.c b/src/XMPPCommand/Manager.c index b272fb7..00a8c89 100644 --- a/src/XMPPCommand/Manager.c +++ b/src/XMPPCommand/Manager.c @@ -8,8 +8,6 @@ #include -#include "XMPPCommand/Internal.h" - typedef struct XMPPSession { char *identifier; @@ -325,7 +323,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) XMLAddAttr(command_xml, "node", node); XMLAddAttr(command_xml, "status", "completed"); XMLAddAttr(command_xml, "sessionid", session_id); - XMPPExecuteCommand(m, cmd, from, command_xml, NULL, node); + XMPPExecuteCommand(m, cmd, from, command_xml, NULL); XMLAddChild(iq, command_xml); XMPPSendStanza(jabber, iq, data->config->max_stanza_size); @@ -365,7 +363,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data) XMLAddAttr(command_xml, "node", node); XMLAddAttr(command_xml, "status", "completed"); XMLAddAttr(command_xml, "sessionid", session_given); - XMPPExecuteCommand(m, cmd, from, command_xml, x_form, node); + XMPPExecuteCommand(m, cmd, from, command_xml, x_form); XMLAddChild(iq, command_xml); XMPPSendStanza(jabber, iq, data->config->max_stanza_size); @@ -387,15 +385,3 @@ XMPPGetManagerCookie(XMPPCommandManager *manager) { return manager ? manager->cookie : NULL; } -XMPPCommandFlags -XMPPGetCommandFlags(XMPPCommandManager *m, char *id) -{ - XMPPCommand *cmd; - if (!m || !id) - { - return XMPPCMD_ALL; - } - - cmd = HashMapGet(m->commands, id); - return cmd ? cmd->flags : XMPPCMD_ALL; -} diff --git a/src/XMPPCommands/AddAdmin.c b/src/XMPPCommands/AddAdmin.c index 70f4479..321d18d 100644 --- a/src/XMPPCommands/AddAdmin.c +++ b/src/XMPPCommands/AddAdmin.c @@ -12,7 +12,7 @@ #include void -AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -23,8 +23,6 @@ AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement GetFieldValue(glob, "glob", form); - (void) node; - if (!ParseeIsAdmin(data, trimmed)) { SetNote("error", "User is not authorised to execute command."); diff --git a/src/XMPPCommands/AddNofly.c b/src/XMPPCommands/AddNofly.c index 2b7f0ff..80f81e5 100644 --- a/src/XMPPCommands/AddNofly.c +++ b/src/XMPPCommands/AddNofly.c @@ -12,7 +12,7 @@ #include void -AddNoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +AddNoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -21,8 +21,6 @@ AddNoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement GetFieldValue(entity, "entity", form); GetFieldValue(reason, "reason", form); - (void) node; - if (!ParseeIsAdmin(data, trimmed)) { SetNote("error", "User is not authorised to execute command."); diff --git a/src/XMPPCommands/Admins.c b/src/XMPPCommands/Admins.c index 36c4f9b..5749165 100644 --- a/src/XMPPCommands/Admins.c +++ b/src/XMPPCommands/Admins.c @@ -12,7 +12,7 @@ #include void -AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); size_t i; @@ -20,8 +20,6 @@ AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement * XMLElement *title; XMLElement *reported, *item, *field, *value, *txt; - (void) node; - x = XMLCreateTag("x"); title = XMLCreateTag("title"); diff --git a/src/XMPPCommands/Cleanup.c b/src/XMPPCommands/Cleanup.c index a6fdb8b..22780ff 100644 --- a/src/XMPPCommands/Cleanup.c +++ b/src/XMPPCommands/Cleanup.c @@ -12,7 +12,7 @@ #include void -CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -31,5 +31,4 @@ CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o SetNote("info", "Parsee data was sucessfully cleant up."); (void) form; - (void) node; } diff --git a/src/XMPPCommands/DelAdmin.c b/src/XMPPCommands/DelAdmin.c index f4ada63..603b09f 100644 --- a/src/XMPPCommands/DelAdmin.c +++ b/src/XMPPCommands/DelAdmin.c @@ -31,7 +31,7 @@ DelAdmin(Array *admin_list, char *glob) return removed; } void -DelAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +DelAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -84,11 +84,9 @@ DelAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement } SetNote("info", "Sucessfully removed admins"); /* TODO */ - - (void) node; } void -FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from, char *node) +FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from) { ParseeData *data = XMPPGetManagerCookie(m); DbRef *admins; @@ -122,6 +120,4 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from, char * DbUnlock(data->db, admins); XMPPAddOption(cmd, admin_opt); - - (void) node; } diff --git a/src/XMPPCommands/MUCInformation.c b/src/XMPPCommands/MUCInformation.c index c96b0ed..53dc684 100644 --- a/src/XMPPCommands/MUCInformation.c +++ b/src/XMPPCommands/MUCInformation.c @@ -12,7 +12,7 @@ #include void -MUCInformationID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +MUCInformationID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *muc = ParseeTrimJID(from); @@ -29,10 +29,9 @@ MUCInformationID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement Free(room_id); Free(chat_id); (void) form; - (void) node; } void -MUCInformationCID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +MUCInformationCID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *muc = ParseeTrimJID(from); @@ -47,5 +46,4 @@ MUCInformationCID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElemen Free(msg); Free(chat_id); (void) form; - (void) node; } diff --git a/src/XMPPCommands/MUCKV.c b/src/XMPPCommands/MUCKV.c index 97bf7d2..80ac9c4 100644 --- a/src/XMPPCommands/MUCKV.c +++ b/src/XMPPCommands/MUCKV.c @@ -15,12 +15,12 @@ #include void -MUCSetKey(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +MUCSetKey(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); - char *affiliation, *role; - char *chat_id; - char *muc; + char *affiliation = NULL, *role = NULL; + char *chat_id = NULL; + char *muc = NULL; char *key = NULL, *val = NULL; @@ -51,10 +51,9 @@ end: Free(chat_id); Free(muc); (void) form; - (void) node; } void -MUCGetKeys(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +MUCGetKeys(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *affiliation = NULL, *role = NULL; @@ -115,5 +114,4 @@ end: Free(chat_id); Free(muc); (void) form; - (void) node; } diff --git a/src/XMPPCommands/MUCUnlink.c b/src/XMPPCommands/MUCUnlink.c index a553824..f5560d3 100644 --- a/src/XMPPCommands/MUCUnlink.c +++ b/src/XMPPCommands/MUCUnlink.c @@ -14,7 +14,7 @@ #include void -MUCUnlink(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +MUCUnlink(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *affiliation, *role; @@ -64,5 +64,4 @@ MUCUnlink(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, Free(room); Free(muc); (void) form; - (void) node; } diff --git a/src/XMPPCommands/Nofly.c b/src/XMPPCommands/Nofly.c index 05053b4..dcbef67 100644 --- a/src/XMPPCommands/Nofly.c +++ b/src/XMPPCommands/Nofly.c @@ -14,7 +14,7 @@ #define TITLE "Parsee global bans" void -NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -89,5 +89,4 @@ NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o } (void) form; - (void) node; } diff --git a/src/XMPPCommands/Status.c b/src/XMPPCommands/Status.c index e954eef..1b19129 100644 --- a/src/XMPPCommands/Status.c +++ b/src/XMPPCommands/Status.c @@ -12,7 +12,7 @@ #include void -StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { char *trimmed = ParseeTrimJID(from); size_t alloc = MemoryAllocated(); @@ -69,6 +69,5 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement * XMLAddChild(out, x); (void) form; - (void) node; (void) m; } diff --git a/src/XMPPCommands/Whitelist.c b/src/XMPPCommands/Whitelist.c index e9b4d01..8e097a8 100644 --- a/src/XMPPCommands/Whitelist.c +++ b/src/XMPPCommands/Whitelist.c @@ -12,7 +12,7 @@ #include void -ClearWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +ClearWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -35,10 +35,9 @@ ClearWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLE SetNote("info", "Parsee whitelist was removed."); (void) form; - (void) node; } void -AddWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +AddWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -80,10 +79,9 @@ AddWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLEle DbUnlock(data->db, ref); SetNote("info", "Server successfully whitelisted."); - (void) node; } void -WhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node) +WhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out) { ParseeData *data = XMPPGetManagerCookie(m); char *trimmed = ParseeTrimJID(from); @@ -141,5 +139,4 @@ WhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElemen } (void) form; - (void) node; } diff --git a/src/XMPPThread/Caps.c b/src/XMPPThread/Caps.c index 867adbd..d79a30c 100644 --- a/src/XMPPThread/Caps.c +++ b/src/XMPPThread/Caps.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -12,33 +13,86 @@ #include "XMPPThread/internal.h" +IQFeatures * +CreateIQFeatures(void) +{ + IQFeatures *ret = Malloc(sizeof(*ret)); + + ret->identity = ArrayCreate(); + ret->adverts = ArrayCreate(); + + return ret; +} +void +FreeIQFeatures(IQFeatures *features) +{ + size_t i; + if (!features) + { + return; + } + + for (i = 0; i < ArraySize(features->adverts); i++) + { + Free(ArrayGet(features->adverts, i)); + } + ArrayFree(features->adverts); + + for (i = 0; i < ArraySize(features->identity); i++) + { + XMPPIdentity *identity = ArrayGet(features->identity, i); + + Free(identity->category); + Free(identity->type); + Free(identity->lang); + Free(identity->name); + + Free(identity); + } + ArrayFree(features->identity); + + Free(features); +} + +void +AdvertiseIQFeature(IQFeatures *f, char *feature) +{ + if (!f || !feature) + { + return; + } + + ArrayAdd(f->adverts, StrDuplicate(feature)); +} +void +AddIQIdentity(IQFeatures *f, char *cat, char *lang, char *type, char *name) +{ + XMPPIdentity *identity; + if (!f) + { + return; + } + + identity = Malloc(sizeof(*identity)); + identity->category = StrDuplicate(cat); + identity->type = StrDuplicate(type); + identity->lang = StrDuplicate(lang); + identity->name = StrDuplicate(name); + ArrayAdd(f->identity, identity); +} /* Generates a SHA-256 hash of the ver field. */ char * -XMPPGenerateVer(void) +XMPPGenerateVer(IQFeatures *features) { 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++) + ArraySort(features->identity, IdentitySort); + for (i = 0; i < ArraySize(features->identity); i++) { - XMPPIdentity *identity = ArrayGet(identities, i); + XMPPIdentity *identity = ArrayGet(features->identity, i); char *id_chunk = StrConcat(7, identity->category, "/", identity->type, "/", @@ -50,10 +104,10 @@ XMPPGenerateVer(void) Free(id_chunk); } - ArraySort(features, ((int (*) (void *, void *)) ICollate)); - for (i = 0; i < ArraySize(features); i++) + ArraySort(features->adverts, ((int (*) (void *, void *)) ICollate)); + for (i = 0; i < ArraySize(features->adverts); i++) { - char *feature = ArrayGet(features, i); + char *feature = ArrayGet(features->adverts, i); char *tmp = S; S = StrConcat(3, S, feature, "<"); Free(tmp); @@ -64,16 +118,64 @@ XMPPGenerateVer(void) 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; } +void +XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features) +{ + XMLElement *c; + char *ver; + if (!presence || !features) + { + return; + } + + ver = XMPPGenerateVer(features); + c = XMLCreateTag("c"); + XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps"); + XMLAddAttr(c, "hash", "sha-1"); + XMLAddAttr(c, "node", REPOSITORY); + XMLAddAttr(c, "ver", ver); + + Free(ver); + 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; +} diff --git a/src/XMPPThread/PEPs/VCard.c b/src/XMPPThread/PEPs/VCard.c index 896d518..086e248 100644 --- a/src/XMPPThread/PEPs/VCard.c +++ b/src/XMPPThread/PEPs/VCard.c @@ -94,13 +94,15 @@ PEPVCardEvent(PEPManager *m, XMLElement *stanza, XMLElement *item) AddTextField(vcard, "fn", name); AddTextField(vcard, "nickname", mxid); + AddURIField(vcard, "url", REPOSITORY); AddURIField(vcard, "url", "https://kappach.at/parsee"); + AddURIField(vcard, "url", "https://matrix.org"); AddURIField(vcard, "url", m_to); AddTextField( vcard, "note", - "This is a bridged Matrix user, from " NAME "." + "This is a bridged Matrix user, from Parsee." ); Free(mxid); Free(name); diff --git a/src/XMPPThread/ReListener.c b/src/XMPPThread/ReListener.c index 3c86feb..44f22f8 100644 --- a/src/XMPPThread/ReListener.c +++ b/src/XMPPThread/ReListener.c @@ -56,6 +56,8 @@ XMPPDispatcher(void *argp) if (!stanza) { + /* TODO: We shouldn't be busywaiting. Even with a sleep call. + */ UtilSleepMillis(10); continue; } @@ -65,12 +67,8 @@ XMPPDispatcher(void *argp) Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name); } - if (ExtensionPushStanza(args->exts, stanza, STANZA_RAW)) + if (ManageMUCStanza(args->muc, stanza)) { - if (args->verbosity >= PARSEE_VERBOSE_COMICAL) - { - Log(LOG_DEBUG, "Stanza was vetoed by an extension."); - } XMLFreeElement(stanza); continue; } @@ -140,7 +138,6 @@ bool XMPPCommandFilter(XMPPCommandManager *m, char *id, XMLElement *stanza) { ParseeData *args = XMPPGetManagerCookie(m); - XMPPCommandFlags flags; char *trimmed_from; char *from; char *chat_id; @@ -155,24 +152,28 @@ XMPPCommandFilter(XMPPCommandManager *m, char *id, XMLElement *stanza) is_muc = !!(chat_id = ParseeGetFromMUCID(args, trimmed_from)); Free(trimmed_from); Free(chat_id); - flags = XMPPGetCommandFlags(m, id); - - if (flags == XMPPCMD_ALL) - { - return true; - } - else if (flags == XMPPCMD_MUC) - { - return is_muc; - } - else if (flags == XMPPCMD_ADMINS) - { - bool is_admin; - trimmed_from = ParseeTrimJID(from); - is_admin = ParseeIsAdmin(args, trimmed_from); - Free(trimmed_from); - return is_admin; +#define XMPP_COMMAND(f,l,n,t,s) \ + if (StrEquals(n, id)) \ + { \ + if (l == XMPPCMD_ALL) \ + { \ + return true; \ + } \ + else if (l == XMPPCMD_MUC) \ + { \ + return is_muc; \ + } \ + else if (l == XMPPCMD_ADMINS) \ + { \ + bool is_admin; \ + trimmed_from = ParseeTrimJID(from); \ + is_admin = ParseeIsAdmin(args, trimmed_from); \ + Free(trimmed_from); \ + return is_admin; \ + } \ } + XMPPCOMMANDS +#undef XMPP_COMMAND return false; } @@ -198,14 +199,13 @@ ParseeXMPPThread(void *argp) XMPPCommand *cmd; #define XMPP_COMMAND(f,l,n,t,s) \ cmd = XMPPBasicCmd( \ - l, n, t, f \ + n, t, f \ ); \ s \ XMPPRegisterCommand(info.m, cmd); XMPPCOMMANDS #undef XMPP_COMMAND } - ExtensionRequestCommands(args->exts); info.pep_manager = CreatePEPManager(args, &info); { PEPManagerAddEvent( @@ -244,66 +244,96 @@ ParseeXMPPThread(void *argp) } } - while (true) + while (!args->halted) { - char *id; - - stanza = XMLDecode(jabber->stream, false); - if (!stanza) + while (true) { - /* Try to check if an error is abound */ - if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + char *id; + + stanza = XMLDecode(jabber->stream, false); + if (!stanza) { - Log(LOG_DEBUG, "RECEIVED EOF."); + /* Try to check if an error is abound */ + if (args->verbosity >= PARSEE_VERBOSE_COMICAL) + { + Log(LOG_DEBUG, "RECEIVED EOF."); + } + break; } - break; - } - if (args->verbosity >= PARSEE_VERBOSE_STANZA) - { - Stream *output = StreamStderr(); - StreamPrintf(output, "-------STANZA BEGIN-------" "\n"); - XMLEncode(output, stanza); - StreamPrintf(output, "\n--------STANZA END--------" "\n"); - StreamFlush(output); - } - - id = HashMapGet(stanza->attrs, "id"); - if (id) - { - XMPPAwait *await; - /* Lock out the table to see if we're awaiting. */ - pthread_mutex_lock(&await_lock); - if ((await = HashMapGet(await_table, id))) + if (args->verbosity >= PARSEE_VERBOSE_STANZA) { - pthread_mutex_lock(&await->cond_lock); - await->stanza = stanza; - pthread_cond_signal(&await->condition); - pthread_mutex_unlock(&await->cond_lock); + Stream *output = StreamStderr(); + StreamPrintf(output, "-------STANZA BEGIN-------" "\n"); + XMLEncode(output, stanza); + StreamPrintf(output, "\n--------STANZA END--------" "\n"); + StreamFlush(output); + } - HashMapDelete(await_table, id); + id = HashMapGet(stanza->attrs, "id"); + if (id) + { + XMPPAwait *await; + /* Lock out the table to see if we're awaiting. */ + pthread_mutex_lock(&await_lock); + if ((await = HashMapGet(await_table, id))) + { + pthread_mutex_lock(&await->cond_lock); + await->stanza = stanza; + pthread_cond_signal(&await->condition); + pthread_mutex_unlock(&await->cond_lock); + HashMapDelete(await_table, id); + + pthread_mutex_unlock(&await_lock); + continue; + } pthread_mutex_unlock(&await_lock); - continue; } - pthread_mutex_unlock(&await_lock); - } - /* Push it into the stanza FIFO. A dispatcher thread should then - * be able to freely grab a value(locked by a mutex). We can't make - * dispatchers read stanzas on their own, since that's _not_ how - * streams work, but this should mitigate some issues, and allow a - * few threads to be busy, while the rest of Parsee works. */ - PushStanza(&info, stanza); + /* Push it into the stanza FIFO. A dispatcher thread should then + * be able to freely grab a value(locked by a mutex). We can't make + * dispatchers read stanzas on their own, since that's _not_ how + * streams work, but this should mitigate some issues, and allow a + * few threads to be busy, while the rest of Parsee works. */ + PushStanza(&info, stanza); + } + pthread_mutex_lock(&args->halt_lock); + if (!args->halted) + { + Log(LOG_WARNING, "XMPP server is closing stream..."); + for (size_t i = 0; i < 50; i++) + { + UtilSleepMillis(100); /* Wait a bit so that temporary failures don't fuck everything up */ + + Log(LOG_WARNING, "Restarting XMPP stream."); + /* This is the part where a new connection is being considered */ + XMPPFinishCompStream(jabber); + XMPPEndCompStream(jabber); + + args->jabber = XMPPInitialiseCompStream( + args->config->component_addr, + args->config->component_host, + args->config->component_port + ); + jabber = args->jabber; + if (!jabber || !XMPPAuthenticateCompStream(jabber, args->config->shared_comp_secret)) + { + /* Oops, there is something wrong! */ + Log(LOG_ERR, "Couldn't authenticate to XMPP server"); + XMPPEndCompStream(jabber); + args->jabber = NULL; + jabber = NULL; + if (i == 4) + { + pthread_mutex_unlock(&args->halt_lock); + break; + } + } + } + } + pthread_mutex_unlock(&args->halt_lock); } - pthread_mutex_lock(&args->halt_lock); - if (!args->halted) - { - Log(LOG_WARNING, "XMPP server is closing stream..."); - Log(LOG_WARNING, "Stopping %s...", NAME); - error = true; - } - pthread_mutex_unlock(&args->halt_lock); info.running = false; for (i = 0; i < info.available_dispatchers; i++) diff --git a/src/XMPPThread/Stanzas/IQ.c b/src/XMPPThread/Stanzas/IQ.c index 1ba5bd0..1b5087d 100644 --- a/src/XMPPThread/Stanzas/IQ.c +++ b/src/XMPPThread/Stanzas/IQ.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -115,33 +116,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,6 +156,7 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza) { char *from, *to, *id; XMLElement *iq_reply, *query; + IQFeatures *features; from = HashMapGet(stanza->attrs, "from"); to = HashMapGet(stanza->attrs, "to"); @@ -163,9 +169,10 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza) XMLAddAttr(iq_reply, "type", "result"); XMLAddAttr(iq_reply, "id", id); - query = IQGenerateQuery(); + features = LookupJIDFeatures(to); + query = IQGenerateQuery(features); { - char *ver = XMPPGenerateVer(); + char *ver = XMPPGenerateVer(features); char *node = StrConcat(3, REPOSITORY, "#", ver); XMLAddAttr(query, "node", node); @@ -173,6 +180,7 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza) Free(ver); } XMLAddChild(iq_reply, query); + FreeIQFeatures(features); XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size); XMLFreeElement(iq_reply); @@ -366,7 +374,7 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr) bool IQIsCommandList(ParseeData *args, XMLElement *stanza) { - char *parsee = NULL; + char *parsee = NULL, *to; XMLElement *query = XMLookForTKV( stanza, "query", "xmlns", "http://jabber.org/protocol/disco#items" @@ -381,7 +389,8 @@ IQIsCommandList(ParseeData *args, XMLElement *stanza) } parsee = ParseeJID(args); - ret = StrEquals(HashMapGet(stanza->attrs, "to"), parsee); + to = HashMapGet(stanza->attrs, "to"); + ret = StrEquals(to, parsee) || StrEquals(to, args->config->component_host); Free(parsee); return ret; @@ -421,16 +430,10 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr) char *parsee_muc_jid = StrConcat(3, trimmed, "/", "parsee"); XMLAddAttr(q, "xmlns", "http://jabber.org/protocol/disco#items"); XMLAddAttr(q, "node", "http://jabber.org/protocol/commands"); - XMPPShoveCommandList(thr->info->m, IsInMUC(args, from) ? parsee_muc_jid : to, q, stanza ); - ExtensionShoveCommands( - args->exts, - IsInMUC(args, from) ? parsee_muc_jid : to, - q, stanza - ); XMLAddChild(iq_reply, q); Free(parsee_muc_jid); } @@ -571,7 +574,7 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr) else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version")) { XMLElement *iq_reply, *query; - XMLElement *name, *version; + XMLElement *name, *version, *os; iq_reply = XMLCreateTag("iq"); XMLAddAttr(iq_reply, "to", from); @@ -582,14 +585,19 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr) query = XMLCreateTag("query"); XMLAddAttr(query, "xmlns", "jabber:iq:version"); { + struct utsname info; name = XMLCreateTag("name"); version = XMLCreateTag("version"); + os = XMLCreateTag("os"); + uname(&info); XMLAddChild(name, XMLCreateText(NAME)); XMLAddChild(version, XMLCreateText(VERSION "[" CODE "]")); + XMLAddChild(os, XMLCreateText(info.sysname)); } XMLAddChild(query, name); XMLAddChild(query, version); + XMLAddChild(query, os); XMLAddChild(iq_reply, query); XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size); @@ -597,10 +605,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); } } @@ -617,7 +629,6 @@ IQSet(ParseeData *args, XMLElement *stanza, XMPPThread *thr) { XMPPCommandManager *manager = thr->info->m; XMPPManageCommand(manager, stanza, args); - ExtensionManageCommands(args->exts, stanza); } #undef DISCO diff --git a/src/XMPPThread/Stanzas/Message.c b/src/XMPPThread/Stanzas/Message.c index 220cca4..13021df 100644 --- a/src/XMPPThread/Stanzas/Message.c +++ b/src/XMPPThread/Stanzas/Message.c @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -276,7 +277,7 @@ end_error: Log(LOG_DEBUG, "Error management: %fs", Elapsed(rectime)); } - if (moderated) + if (moderated && !(!chat && strncmp(HashMapGet(stanza->attrs, "to"), "parsee@", 7))) { /* TODO: Parsee MUST check if it is a valid MUC */ char *resource = ParseeGetResource(from); @@ -324,8 +325,9 @@ end_error: { Log(LOG_DEBUG, "Fetching bridge: %fs", Elapsed(rectime)); } + char *trim = ParseeTrimJID(from); if (!mroom_id && !room && !XMPPIsParseeStanza(stanza) && - to && *to == '@') + to && *to == '@' && !XMPPQueryMUC(jabber, trim, NULL)) { DbRef *room_ref; HashMap *room_json; @@ -344,6 +346,7 @@ end_error: ParseePushDMRoom(args, to, from, room); } } + Free(trim); /* TODO: THIS IS A HACK. THIS CODE IGNORES ALL MUC MESSAGES EVER * SENT TO NON-PARSEE PUPPETS, AS A "FIX" TO THE MULTITHREADED @@ -394,11 +397,17 @@ end_error: bool media_enabled = chat_id ? ParseeIsMediaEnabled(args, chat_id) : true ; - + bool is_parsee; + /* Note that chat_id still has meaningful info as a boolean + * despite being freed */ Free(chat_id); - pthread_mutex_unlock(&thr->info->chk_lock); + parsee = ParseeJID(args); + is_parsee = StrEquals(to, parsee); + Free(parsee); + parsee = NULL; + LazyRegister(args, encoded, !chat ? res : NULL); if (args->verbosity >= PARSEE_VERBOSE_TIMINGS) { @@ -411,15 +420,10 @@ end_error: /* Check if it is a media link */ oob = XMLookForTKV(stanza, "x", "xmlns", "jabber:x:oob"); - - /* TODO: This may be way too late. I think a way to listen to *all* - * stanzas may be worthwhile. */ - if (ExtensionPushStanza(args->exts, stanza, STANZA_MESSAGE)) + if (chat_id && StrEquals(type, "chat") && !is_parsee) { - //if (args->verbosity >= PARSEE_VERBOSE_COMICAL) - { - Log(LOG_DEBUG, "Stanza was vetoed by an extension."); - } + /* Very clearly a MUC DM. */ + event_id = NULL; } else if (oob && data && media_enabled) { @@ -517,6 +521,15 @@ end_error: * too. Go figure. */ size_t off = reply_to ? ParseeFindDatastart(data->data) : 0; + size_t stanzaoff = XMPPGetReplyOffset(stanza); + if (reply_to && stanzaoff != -1) + { + off = UnistrGetUTFOffset(data->data, stanzaoff); + } + if (data->data && off >= strlen(data->data)) + { + off = 0; + } HashMap *ev = MatrixCreateMessage(data->data + off); if (reply_to) { diff --git a/src/XMPPThread/Stanzas/Presence.c b/src/XMPPThread/Stanzas/Presence.c index 65c2082..947fc4b 100644 --- a/src/XMPPThread/Stanzas/Presence.c +++ b/src/XMPPThread/Stanzas/Presence.c @@ -298,7 +298,7 @@ end_item: Free(room); FreeStatuses(statuses); } - if (status) + if (status && !StrEquals(type, "unavailable")) { XMLElement *status_data = ArrayGet(status->children, 0); char *decode_from = ParseeLookupJID(oid); @@ -309,6 +309,8 @@ end_item: status_str = status_data->data; } + + /* TODO: "The server will automatically set a user's presence to * unavailable if their last active time was over a threshold value * (e.g. 5 minutes)." diff --git a/src/XMPPThread/internal.h b/src/XMPPThread/internal.h index 5931f3d..efdd300 100644 --- a/src/XMPPThread/internal.h +++ b/src/XMPPThread/internal.h @@ -32,13 +32,17 @@ IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \ IdentitySimple("component", "generic", "Parsee's component") -typedef struct PEPManager PEPManager; -typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item); - +typedef struct IQFeatures { + Array *identity; + Array *adverts; +} IQFeatures; typedef struct XMPPIdentity { char *category, *type, *lang, *name; } XMPPIdentity; +typedef struct PEPManager PEPManager; +typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item); + typedef struct XMPPThread XMPPThread; typedef struct XMPPThreadInfo { /* A FIFO of stanzas inbound, to be read by dispatcher @@ -65,9 +69,33 @@ struct XMPPThread { int ICollate(unsigned char *cata, unsigned char *catb); int IdentitySort(void *idap, void *idbp); +IQFeatures * CreateIQFeatures(void); +void AddIQIdentity(IQFeatures *, char *category, char *lang, char *type, char *name); +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] + * Modifies: NOTHING + * See-Also: https://xmpp.org/extensions/xep-0115.html */ +char * XMPPGenerateVer(IQFeatures *features); + +/* Annotates a presence with https://xmpp.org/extensions/xep-0115.html */ +void XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features); + 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); HashMap * ShoveStanza(HashMap *content, XMLElement *stanza); @@ -81,6 +109,9 @@ void PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr); bool ServerHasXEP421(ParseeData *data, char *from); +char * ParseeGetBridgedUserI(ParseeData *data, XMLElement *stanza, char *force); +#define ParseeGetBridgedUser(data, stanza) ParseeGetBridgedUserI(data, stanza, NULL) + PEPManager * CreatePEPManager(ParseeData *data, void *cookie); void * PEPManagerCookie(PEPManager *manager); void PEPManagerAddEvent(PEPManager *manager, char *node, PEPEvent event); diff --git a/src/include/Extension.h b/src/include/Extension.h deleted file mode 100644 index 54123d5..0000000 --- a/src/include/Extension.h +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef PARSEE_EXTENSION_H -#define PARSEE_EXTENSION_H - -#include - -#include - -#include - -typedef struct ParseeData ParseeData; - -typedef struct Extensions Extensions; -typedef struct Extension Extension; - -typedef enum StanzaType { - STANZA_RAW = 0, - STANZA_MESSAGE = 1, - STANZA_IQ = 2, - STANZA_PRESENCE = 3, - - STANZA_TYPE_COUNT -} StanzaType; - -/** Verifies if extensions are enabled with the Parsee binary. - * Functions here WILL fail if this function - * returns false! - * ------------------- - * Returns: true IFF extensions are enabled */ -extern bool ExtensionEnabled(void); - -/* Creates an extension context. */ -extern Extensions * ExtensionCreateContext(struct ParseeData *data); - -/** Destroys the entire extension context, and all of its extensions. - * ------------------------------------- - * Returns: NOTHING - * Thrashes: ctx */ -extern void ExtensionDestroyContext(Extensions *ctx); - -/** Loads a basic extension from a simple Janet file. - * ---------------------------- - * Returns: An extension handle[ctx] | NULL */ -extern Extension * ExtensionLoadBasic(Extensions *ctx, char *id, char *file); - -/** Reloads an extension from an ID. - * --------------------------- - * Returns: NOTHING */ -extern void ExtensionReload(Extensions *ctx, char *id); - -/** Loads extensions from a directory. The directory's Janet files are loaded - * (with the part before the .janet extension denoting the identifier). - * ----------------------------------- - * Returns: NOTHING */ -extern void ExtensionLoadDir(Extensions *ctx, char *dir); - -/** Broadcasts a stanza received to all extensions(by calling the - * on-stanza function). - * ----------------------------------------- - * Returns: NOTHING - * Modifies: the extensions */ -extern bool ExtensionPushStanza(Extensions *ctx, XMLElement *stanza, StanzaType t); - -/** Broadcasts a Matrix event to all extensions, akin to ExtensionPushStanza. - * ----------- - * Returns: NOTHING - * Modifies: the extensions */ -extern bool ExtensionPushEvent(Extensions *ctx, HashMap *obj); - -/** Calls "on-xmpp-cmd" on extensions' Janet side, with no arguments. - * ---------- - * Returns: NOTHING - * Modifies: the extensions */ -extern void ExtensionRequestCommands(Extensions *ctx); - -extern void -ExtensionShoveCommands(Extensions *ctx, char *jid, XMLElement *query, XMLElement *stanza); - -extern void -ExtensionManageCommands(Extensions *ctx, XMLElement *stanza); - -#endif diff --git a/src/include/MUCServ.h b/src/include/MUCServ.h new file mode 100644 index 0000000..f9a2e4b --- /dev/null +++ b/src/include/MUCServ.h @@ -0,0 +1,39 @@ +#ifndef PARSEE_MUCSERV_H +#define PARSEE_MUCSERV_H + +/*-* An easy interface for handling MUCs. + */ + +typedef struct MUCServer MUCServer; + +#include + +#include + +/** 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 + * '#foobar@bridge.blow.hole' -> 'foobar') 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 diff --git a/src/include/Matrix.h b/src/include/Matrix.h index a408389..fb0b5f7 100644 --- a/src/include/Matrix.h +++ b/src/include/Matrix.h @@ -2,6 +2,7 @@ #define PARSEE_MATRIX_H #include +#include #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); diff --git a/src/include/Parsee.h b/src/include/Parsee.h index 5993877..4b1f7aa 100644 --- a/src/include/Parsee.h +++ b/src/include/Parsee.h @@ -15,10 +15,11 @@ typedef struct ParseeData ParseeData; #include -#include #include #include +#include + #define PARSEE_VERBOSE_NONE 0 #define PARSEE_VERBOSE_LOG 1 #define PARSEE_VERBOSE_TIMINGS 2 @@ -40,9 +41,14 @@ typedef struct ParseeConfig { /* Homeserver port info */ char *homeserver_host; int homeserver_port; + int homeserver_tls; + + bool accept_pings; + bool ignore_bots; /* ------- JABBER -------- */ + char *component_addr; char *component_host; char *shared_comp_secret; int component_port; @@ -51,7 +57,6 @@ typedef struct ParseeConfig { /* ------- DB -------- */ char *db_path; size_t db_size; - char *extensions; /* - COMMAND-LINE FLAGS - */ int xmpp_threads, http_threads; @@ -64,6 +69,7 @@ typedef struct ParseeData { HttpServer *server; XMPPComponent *jabber; + MUCServer *muc; Db *db; char *id; @@ -74,10 +80,8 @@ typedef struct ParseeData { pthread_mutex_t oidl; /* If Parsee was intentionally halted */ - volatile bool halted; + bool halted; pthread_mutex_t halt_lock; - - Extensions *exts; } ParseeData; typedef struct Argument { @@ -97,8 +101,6 @@ typedef struct Argument { #define GrabObject(obj, ...) JsonValueAsObject(JsonGet(obj, __VA_ARGS__)) #define GrabArray(obj, ...) JsonValueAsArray(JsonGet(obj, __VA_ARGS__)) -#define IterateReentrant(h, k, v, i) HashMapIterateReentrant(h, &k, (void **) &v, &i) - /* Milliseconds to UNIT macros, to be used like 30 SECONDS and 1 MINUTES * in timestamps */ #define SECONDS * 1000 @@ -127,7 +129,6 @@ extern const char *parsee_ascii[PARSEE_ASCII_LINES]; * Modifies: the logger output */ extern void ParseePrintASCII(void); - /** * Checks if two versions of Parsee can be considered "compatible". * This is mainly used for things like database operations. TODO: @@ -283,12 +284,6 @@ extern char * ParseeGetRoomID(ParseeData *, char *chat_id); /* Finds the MUC JID from a chat ID */ extern char * ParseeGetMUCID(ParseeData *, char *chat_id); -/** Verifies if a JID maps to a chatroom through Service Discovery. - * ---------------- - * Returns: whenever the JID is a real MUC - * Modifies: the XMPP stream */ -extern bool ParseeIsMUC(ParseeData *data, char *jid); - /** Fetches a configuration value from a key in a chat(given a Chat ID), * as a string or NULL. Keys are to be stored like Java packages(reveres DNS). * Parsee has the right over any key with the 'p.' prefix. @@ -334,14 +329,6 @@ ParseeIsMediaEnabled(ParseeData *data, char *chat_id); extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *origin_id, char *event, char *sender); extern void ParseePushDMStanza(ParseeData *, char *room_id, char *stanza_id, char *origin_id, char *event, char *sender); -/** Automatically pushes the link between a stanza and a bridged Matrix event. - * It behaves like {ParseePushStanza} and {ParseePushDMStanza}, but the routing - * is done automatically. - * ---------------------------- - * Returns: NOTHING - * Modifies: the database */ -extern void ParseePushAllStanza(ParseeData *args, XMLElement *stanza, char *event); - /* Checks if a stanza is not duplicated in a chat ID */ extern bool ParseeVerifyStanza(ParseeData *, char *chat_id, char *stanza_id); @@ -372,7 +359,7 @@ extern int ParseeFindDatastartU(char *data); /* XMPP-ifies a message event to XEP-0393 if possible. */ -extern char * ParseeXMPPify(HashMap *event); +extern char * ParseeXMPPify(ParseeData *data, HashMap *event); /* Finds an event ID from an ID in the stanza's attributes */ extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id); @@ -382,7 +369,7 @@ extern char * ParseeDMEventFromID(ParseeData *d, char *r_id, char *ori_id); extern char * ParseeDMEventFromSID(ParseeData *d, char *r_id, char *ori_id); /* Gets a Parsee "shim" link to an MXC, usable as unauth for a limited time */ -extern char * ParseeToUnauth(ParseeData *data, char *mxc); +extern char * ParseeToUnauth(ParseeData *data, char *mxc, char *filename); extern void ParseeInitialiseOIDTable(void); extern void ParseePushOIDTable(char *muc, char *occupant); @@ -413,10 +400,17 @@ extern void ParseeDestroyNickTable(void); * to ban them from rooms where Parsee has the ability to do so ("noflying"). * --------------- * Returns: NOTHING - * See-Also: ParseeManageBan + * See-Also: ParseeManageBan, ParseeGlobalUnban * Modifies: the database */ extern void ParseeGlobalBan(ParseeData *, char *user, char *reason); +/** Revokes a user/room/MUC's nofly status + * --------------- + * Returns: NOTHING + * See-Also: ParseeManageBan, ParseeGlobalBan + * Modifies: the database */ +extern void ParseeGlobalUnban(ParseeData *, char *user); + /** Verifies if a user was banned globally. If so (and if {room} is set), * tries to ban the user from it. * --------------- @@ -509,8 +503,4 @@ extern void ParseeUnlinkRoom(ParseeData *data, char *chat_id); * Modifies: NOTHING */ extern bool ParseeIsMUCWhitelisted(ParseeData *data, char *muc); -/* TODO */ -extern char * ParseeGetBridgedUserI(ParseeData *data, XMLElement *stanza, char *force); -#define ParseeGetBridgedUser(data, stanza) ParseeGetBridgedUserI(data, stanza, NULL) - #endif diff --git a/src/include/Routes.h b/src/include/Routes.h index 8d233e6..664032b 100644 --- a/src/include/Routes.h +++ b/src/include/Routes.h @@ -27,6 +27,10 @@ typedef struct ParseeCmdArg { "ban-list", CmdNoFlyList, \ "Globally bans a user from using Parsee" \ ) \ + X_COMMAND( \ + "unban-list", CmdNoFlyListDel, \ + "Globally unbans a user from using Parsee" \ + ) \ X_COMMAND( \ "nuke-muc", CmdUnlinkMUC, \ "Removes a MUC. Users should then run " \ @@ -75,7 +79,8 @@ typedef struct ParseeCmdArg { X_ROUTE("/_matrix/app/v1/users/(.*)", RouteUserAck) \ X_ROUTE("/_matrix/app/v1/rooms/(.*)", RouteRoomAck) \ X_ROUTE("/_matrix/app/v1/ping", RoutePing) \ - X_ROUTE("/_matrix/client/v1/media/download/(.*)/(.*)", RouteMedia) + X_ROUTE("/media/(.*)/(.*)", RouteMedia) \ + X_ROUTE("/media/(.*)/(.*)/(.*)", RouteMedia) #define X_ROUTE(path, name) extern void * name(Array *, void *); ROUTES diff --git a/src/include/Unistring.h b/src/include/Unistring.h index 6fbc296..7eff7a1 100644 --- a/src/include/Unistring.h +++ b/src/include/Unistring.h @@ -64,6 +64,12 @@ extern void UnistrFree(Unistr *unistr); * Returns: whenever the character is within the BMP */ extern bool UnistrIsBMP(uint32_t u); +/** Returns true IFF the character is within the 7-bit ASCII range + * not 0x0000 + * ------------------------------------------------------------ + * Returns: whenever the character is within ASCII */ +extern bool UnistrIsASCII(uint32_t u); + typedef bool (*UnistrFilterFunc)(uint32_t u); /** "Filters" characters in a Unistring by codepoint, removing * those with callbacks which return false into a new unistring. @@ -76,4 +82,10 @@ extern Unistr * UnistrFilter(Unistr *str, UnistrFilterFunc filter); * -------- * Returns: an offset of the first line to not start by {c} | 0 */ extern size_t UnistrGetOffset(Unistr *str, uint32_t sep); + +/** Locates the new index within a regular UTF-8 sequence from an index + * within all codepoints. + * ----------------------------------- + * Returns: an offset */ +extern size_t UnistrGetUTFOffset(char *cstr, size_t unicode); #endif diff --git a/src/include/XML.h b/src/include/XML.h index 8fd108d..c7b6610 100644 --- a/src/include/XML.h +++ b/src/include/XML.h @@ -13,6 +13,8 @@ typedef struct XMLEvent { XML_LEXER_DATA, XML_LEXER_ELEM, /* Empty attr */ XML_LEXER_ENDELEM, + + XML_ERROR, XML_RELAX } type; diff --git a/src/include/XMPP.h b/src/include/XMPP.h index b2c5293..3b53f04 100644 --- a/src/include/XMPP.h +++ b/src/include/XMPP.h @@ -22,7 +22,7 @@ typedef struct XMPPComponent { /* Initialises a raw component stream to host, with an optional port. * If said port is 0, then it is set to the default Prosody port */ -extern XMPPComponent * XMPPInitialiseCompStream(char *host, int port); +extern XMPPComponent * XMPPInitialiseCompStream(char *addr, char *host, int port); /* Authenticates a component stream with a given shared secret, * with a stream ID from the server. This should be called right @@ -90,6 +90,9 @@ extern char * XMPPGetRetractedID(XMLElement *); /* Get the replied-to stanza ID, if existent. */ extern char * XMPPGetReply(XMLElement *elem); +/* Get the replied-to stanza offset, if existent. */ +extern ssize_t XMPPGetReplyOffset(XMLElement *elem); + /** Get the moderated message ID(as a stanza ID/plain ID) from a moderation * stanza, that lives *alongside* the stanza itself. * ---------------------------------------------------------------------- @@ -98,16 +101,6 @@ extern char * XMPPGetReply(XMLElement *elem); * See-Also: https://xmpp.org/extensions/xep-0425.html */ extern char * XMPPGetModeration(XMLElement *stanza); -/** Generate the B64-encoded SHA-256 hash for the 'ver' field in caps. - * -------- - * Returns: A base64 encoded ver hash[LA:HEAP] - * Modifies: NOTHING - * See-Also: https://xmpp.org/extensions/xep-0115.html */ -extern char * XMPPGenerateVer(void); - -/* Annotates a presence with https://xmpp.org/extensions/xep-0115.html */ -extern void XMPPAnnotatePresence(XMLElement *presence); - extern bool XMPPHasError(XMLElement *stanza, char *type); #include diff --git a/src/include/XMPPCommand.h b/src/include/XMPPCommand.h index 8fa0a03..33b121f 100644 --- a/src/include/XMPPCommand.h +++ b/src/include/XMPPCommand.h @@ -1,4 +1,3 @@ -typedef struct XMPPCommandManager XMPPCommandManager; #ifndef PARSEE_XMPPCOMMAND_H #define PARSEE_XMPPCOMMAND_H @@ -8,16 +7,11 @@ typedef struct XMPPCommandManager XMPPCommandManager; #include #include -typedef enum XMPPCommandFlags { - XMPPCMD_ALL = 0, - XMPPCMD_MUC , /* Only for MUCs */ - XMPPCMD_ADMINS /* Only for administrators */ -} XMPPCommandFlags; - +typedef struct XMPPCommandManager XMPPCommandManager; typedef struct XMPPCommand XMPPCommand; typedef struct XMPPOption XMPPOption; -typedef void (*XMPPCmdCallback)(XMPPCommandManager *, char *, XMLElement *, XMLElement *, char *); -typedef void (*XMPPOptionWriter)(XMPPCommandManager *, XMPPCommand *, char *, char *); +typedef void (*XMPPCmdCallback)(XMPPCommandManager *, char *, XMLElement *, XMLElement *); +typedef void (*XMPPOptionWriter)(XMPPCommandManager *, XMPPCommand *, char *); typedef bool (*XMPPCmdFilter)(XMPPCommandManager *, char *id, XMLElement *stanza); /** Creates a simple XMPP command manager, which routes commands @@ -45,7 +39,7 @@ XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter); * Modifies: NOTHING * See-Also: XMPPRegisterCommand */ extern XMPPCommand * -XMPPBasicCmd(XMPPCommandFlags flags, char *node, char *name, XMPPCmdCallback cb); + XMPPBasicCmd(char *node, char *name, XMPPCmdCallback cb); extern void XMPPCmdOptionsCreator(XMPPCommand *cmd, XMPPOptionWriter writer); extern void XMPPAddOption(XMPPCommand *cmd, XMPPOption *opt); @@ -55,7 +49,7 @@ extern char * XMPPGetCommandDesc(XMPPCommand *cmd); extern void XMPPSetFormInstruction(XMPPCommand *cmd, char *instruction); extern void XMPPSetFormTitle(XMPPCommand *cmd, char *title); extern bool XMPPCommandRequiresForm(XMPPCommand *cmd); -extern void XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form, char *node); +extern void XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form); /** Create a basic option. * ----------------------------------------------------- @@ -97,12 +91,6 @@ extern void XMPPRegisterCommand(XMPPCommandManager *m, XMPPCommand *cmd); * See-Also: XMPPCreateManager */ extern void XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s); -/** Returns the flags associated to a command. - * ---------------- - * Returns: some flags - * Modifies: NOTHING */ -extern XMPPCommandFlags XMPPGetCommandFlags(XMPPCommandManager *m, char *id); - /** Destroys all memory related to the command {manager}. * ----------------------------------------------------- * Returns: NOTHING @@ -123,11 +111,11 @@ extern bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeD #define XMPP_COMMAND(f,l,n,t,s) \ extern void \ - f(XMPPCommandManager *, char *, XMLElement *, XMLElement *, char *); \ + f(XMPPCommandManager *, char *, XMLElement *, XMLElement *); \ /* This symbol might not exist. We do, however, not care, as * it is not always done. */ \ extern void \ - Form##f(XMPPCommandManager *, XMPPCommand *, char *, char *); \ + Form##f(XMPPCommandManager *, XMPPCommand *, char *); \ #include "XMPPCommands.x.h" diff --git a/src/include/XMPPCommands.x.h b/src/include/XMPPCommands.x.h index 483aca9..dba6f48 100644 --- a/src/include/XMPPCommands.x.h +++ b/src/include/XMPPCommands.x.h @@ -1,4 +1,9 @@ /* C X-macro file */ +typedef enum XMPPCommandFlags { + XMPPCMD_ALL = 0, + XMPPCMD_MUC , /* Only for MUCs */ + XMPPCMD_ADMINS /* Only for administrators */ +} XMPPCommandFlags; #define XMPPCOMMANDS \ XMPP_COMMAND(StatusCallback, XMPPCMD_ALL, "stats", "Get Parsee statistics", {}) \ XMPP_COMMAND(CleanCallback, XMPPCMD_ADMINS, "clean", "Cleanup temporary Parsee data", {}) \ diff --git a/tools/adminify.c b/tools/adminify.c index e87d97e..1fdd6cf 100644 --- a/tools/adminify.c +++ b/tools/adminify.c @@ -89,31 +89,30 @@ Main(Array *args, HashMap *env) glob = ArrayGet(args, 2); parsee = GetDB(db_path); - if (parsee) + if (!parsee) { + Log(LOG_ERR, "%s: couldn't open config '%s' or couldnt edit DB", exec, db_path); + (void) env; + return EXIT_FAILURE; + } - if (glob) - { - Log(LOG_NOTICE, "Adding glob '%s' to database %s", glob, db_path); - AddAdmin(parsee, glob); - Log(LOG_INFO, "Successfully added glob %s.", glob); - Log(LOG_INFO, "*I'm jealous of all these admins!*"); - - DbClose(parsee); - return EXIT_SUCCESS; - } - - /* List admins */ - Log(LOG_INFO, "Admin list:"); - LogConfigIndent(LogConfigGlobal()); - ListAdmins(parsee); - LogConfigUnindent(LogConfigGlobal()); + if (glob) + { + Log(LOG_NOTICE, "Adding glob '%s' to database %s", glob, db_path); + AddAdmin(parsee, glob); + Log(LOG_INFO, "Successfully added glob %s.", glob); + Log(LOG_INFO, "*I'm jealous of all these admins!*"); DbClose(parsee); return EXIT_SUCCESS; } - Log(LOG_ERR, "%s: couldn't open config '%s' or couldnt edit DB", exec, db_path); - (void) env; - return EXIT_FAILURE; + /* List admins */ + Log(LOG_INFO, "Admin list:"); + LogConfigIndent(LogConfigGlobal()); + ListAdmins(parsee); + LogConfigUnindent(LogConfigGlobal()); + + DbClose(parsee); + return EXIT_SUCCESS; } diff --git a/tools/common.h b/tools/common.h index eee4793..9687ea3 100644 --- a/tools/common.h +++ b/tools/common.h @@ -108,7 +108,7 @@ GetDB(char *config) { ret = DbOpenLMDB(db_path, lmdb_size); } - if (!ret) + else { ret = DbOpen(db_path, 0); } diff --git a/tools/config.c b/tools/config.c index b949c14..d8e0aea 100644 --- a/tools/config.c +++ b/tools/config.c @@ -20,6 +20,7 @@ Main(Array *args, HashMap *env) Uri *api_base; char *homeserver = NULL, *jcp = NULL, *jabber = NULL; char *data = NULL, *media = NULL, *listen = NULL; + char *component_as = NULL; int flag, code = EXIT_FAILURE; int port = 5347; size_t lmdb_size = 0; @@ -28,7 +29,7 @@ Main(Array *args, HashMap *env) listen = "localhost"; ArgParseStateInit(&state); - while ((flag = ArgParse(&state, args, "H:J:s:d:p:m:l:S:M:")) != -1) + while ((flag = ArgParse(&state, args, "H:J:j:s:d:p:m:l:S:M:")) != -1) { switch (flag) { @@ -45,6 +46,9 @@ Main(Array *args, HashMap *env) case 'J': jabber = state.optArg; break; + case 'j': + component_as = state.optArg; + break; case 'd': data = state.optArg; break; @@ -123,6 +127,7 @@ Main(Array *args, HashMap *env) JsonSet(json, JsonValueString(media), 1, "media_base"); JsonSet(json, JsonValueString(listen), 1, "listen_as"); + JsonSet(json, JsonValueString(component_as), 1, "component_addr"); JsonEncode(json, file, JSON_PRETTY); StreamFlush(file); diff --git a/tools/plumb.c b/tools/plumb.c new file mode 100644 index 0000000..1bc9e14 --- /dev/null +++ b/tools/plumb.c @@ -0,0 +1,152 @@ +/* plumb.c - Small utility to manage plumbings from a shutoff instance. + * ============================================================ + * TODO: write other commands, and move some code to common.h + * + * Under CC0, as its a rather useful example of a Parsee tool. + * See LICENSE for more information about Parsee's licensing. */ + +#include "common.h" + +#include + +static void +DeletePlumbID(Db *parsee, char *chat_id) +{ + DbRef *chat_id_ref = DbLockIntent(parsee, DB_HINT_READONLY, 2, "chats", chat_id); + DbRef *chats = DbLock(parsee, 1, "chats"); + char *matrix_id = GrabString(DbJson(chat_id_ref), 1, "room_id"); + char *jabber_id = GrabString(DbJson(chat_id_ref), 1, "jabber_id"); + + HashMap *rooms = GrabObject(DbJson(chats), 1, "rooms"); + HashMap *mucs = GrabObject(DbJson(chats), 1, "mucs"); + + JsonValueFree(HashMapDelete(rooms, matrix_id)); + JsonValueFree(HashMapDelete(mucs, jabber_id)); + + DbUnlock(parsee, chat_id_ref); + DbUnlock(parsee, chats); + DbDelete(parsee, 2, "chats", chat_id); +} +static void +DeletePlumb(Db *parsee, char *potential_id) +{ + if (!parsee || !potential_id) + { + return; + } + + if (!strncmp(potential_id, "xmpp:", 5)) + { + DbRef *ref; + HashMap *mucs; + char *chat_id; + /* Try to parse it as an XMPP address */ + potential_id += 5; + + ref = DbLockIntent(parsee, DB_HINT_READONLY, 1, "chats"); + mucs = GrabObject(DbJson(ref), 1, "mucs"); + chat_id = StrDuplicate(GrabString(mucs, 1, potential_id)); + DbUnlock(parsee, ref); + + DeletePlumbID(parsee, chat_id); + Free(chat_id); + + return; + } + if (*potential_id == '!') + { + /* Try to parse it as a Matrix room ID */ + DbRef *ref; + HashMap *rooms; + char *chat_id; + + ref = DbLockIntent(parsee, DB_HINT_READONLY, 1, "chats"); + rooms = GrabObject(DbJson(ref), 1, "rooms"); + chat_id = StrDuplicate(GrabString(rooms, 1, potential_id)); + DbUnlock(parsee, ref); + + DeletePlumbID(parsee, chat_id); + Free(chat_id); + + return; + } + + /* Try to parse it as a chat ID */ + DeletePlumbID(parsee, potential_id); +} +static void +ListPlumbs(Db *parsee) +{ + DbRef *ref; + HashMap *mucs; + + char *muc; + JsonValue *value; + if (!parsee) + { + return; + } + + ref = DbLockIntent(parsee, DB_HINT_READONLY, 1, "chats"); + mucs = GrabObject(DbJson(ref), 1, "mucs"); + while (HashMapIterate(mucs, &muc, (void **) &value)) + { + char *chat_id = JsonValueAsString(value); + DbRef *chat_id_ref = DbLockIntent(parsee, DB_HINT_READONLY, 2, "chats", chat_id); + char *matrix_id = GrabString(DbJson(chat_id_ref), 1, "room_id"); + + /* TODO */ + Log(LOG_INFO, "- Plumb xmpp:%s <=> %s", muc, matrix_id); + Log(LOG_INFO, " - ID=%s", chat_id); + + DbUnlock(parsee, chat_id_ref); + } + + DbUnlock(parsee, ref); +} + +int +Main(Array *args, HashMap *env) +{ + char *db_path, *action, *exec; + int ret = EXIT_SUCCESS; + Db *parsee; + + exec = ArrayGet(args, 0); + + if (ArraySize(args) < 3) + { + Log(LOG_ERR, "Usage: %s [config] [action] ", exec); + return EXIT_FAILURE; + } + + db_path = ArrayGet(args, 1); + action = ArrayGet(args, 2); + + parsee = GetDB(db_path); + if (!parsee) + { + Log(LOG_ERR, "%s: couldn't open config '%s' or couldnt edit DB", exec, db_path); + return EXIT_FAILURE; + } + + if (StrEquals(action, "list") || StrEquals(action, "ls")) + { + ListPlumbs(parsee); + } + else if (StrEquals(action, "del")) + { + if (ArraySize(args) != 4) + { + Log(LOG_ERR, "%s: please show a !roomid:matrix.org, xmpp:mucid@jabber.org, or local hash", exec); + ret = EXIT_FAILURE; + goto end; + } + DeletePlumb(parsee, ArrayGet(args, 3)); + } + + +end: + DbClose(parsee); + return ret; +}