mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 15:15:10 +00:00
Compare commits
5 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de40106932 | ||
|
|
81edbec1d0 | ||
|
|
e3979e0e6e | ||
|
|
6d7d85662b | ||
|
|
ca87972b3a |
89 changed files with 3889 additions and 1612 deletions
16
.gitignore
vendored
16
.gitignore
vendored
|
|
@ -4,11 +4,10 @@ parsee*
|
|||
parsee
|
||||
*.swp
|
||||
.*
|
||||
data*
|
||||
data*/*
|
||||
data
|
||||
data/*
|
||||
Makefile
|
||||
configure
|
||||
gmon.out
|
||||
|
||||
tools/out
|
||||
tools/out/*
|
||||
|
|
@ -26,7 +25,10 @@ tags
|
|||
!.forgejo/*
|
||||
!.forgejo/**
|
||||
|
||||
!.guix
|
||||
!.guix/*
|
||||
!.guix/**
|
||||
|
||||
# Janet
|
||||
janet/*
|
||||
janet/**
|
||||
janet
|
||||
extensions
|
||||
extensions/*
|
||||
extensions/**
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
(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
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -13,22 +13,6 @@ commit done between releases.
|
|||
*There is currently no beta releases of Parsee*
|
||||
|
||||
## Alpha
|
||||
|
||||
### v0.3.0[lunar-rainbow] <XX/XX/2025>
|
||||
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
|
||||
|
|
|
|||
12
README.MD
12
README.MD
|
|
@ -18,11 +18,12 @@ 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))
|
||||
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.))
|
||||
|
||||
### "Why not just use Matrix lol"
|
||||
### "Why not just use XMPP lol"
|
||||
These two having the same answer should be enough information.
|
||||
These two having the same answer should be enough information. Also can I *just* have fun?
|
||||
One could also argue that both sides need to migrate(onboard) the other side, so
|
||||
a bridge may be a good way to start.
|
||||
|
||||
|
|
@ -43,13 +44,6 @@ 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.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
CODE=lunar-rainbow
|
||||
CODE=star-of-hope
|
||||
NAME=Parsee
|
||||
VERSION=0.3.0
|
||||
VERSION=0.2.0
|
||||
BINARY=parsee
|
||||
SOURCE=src
|
||||
INCLUDES=src/include
|
||||
|
|
|
|||
103
configure.c
103
configure.c
|
|
@ -90,6 +90,22 @@ 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;
|
||||
|
|
@ -99,10 +115,6 @@ 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;
|
||||
|
|
@ -204,6 +216,36 @@ 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)
|
||||
|
|
@ -211,6 +253,17 @@ 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)
|
||||
{
|
||||
|
|
@ -315,7 +368,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", ofl + 3);
|
||||
char *obj = string_cat("build", strchr(ofl, '/'));
|
||||
|
||||
fprintf(makefile, " %s", obj);
|
||||
free(ofl);
|
||||
|
|
@ -459,10 +512,12 @@ main_build(int argc, char *argv[])
|
|||
{
|
||||
FILE *makefile;
|
||||
char *repo = cmd_stdout("git remote get-url origin");
|
||||
size_t i;
|
||||
size_t size, i;
|
||||
ssize_t nread;
|
||||
bool with_static = false, with_lmdb = false;
|
||||
int opt;
|
||||
str_array_t *sources, *images, *utils, *aya;
|
||||
str_array_t *sources, *janet = NULL, *images, *utils, *aya;
|
||||
char *janet_dir = NULL;
|
||||
|
||||
if (repo)
|
||||
{
|
||||
|
|
@ -477,7 +532,7 @@ main_build(int argc, char *argv[])
|
|||
repo = strdup("N/A");
|
||||
}
|
||||
|
||||
while ((opt = getopt(argc, argv, "sl")) != -1)
|
||||
while ((opt = getopt(argc, argv, "slJ:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
|
|
@ -488,17 +543,13 @@ 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");
|
||||
|
|
@ -513,13 +564,22 @@ 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) -o $(BINARY)");
|
||||
fprintf(makefile, "$(CC) -lm -o $(BINARY)");
|
||||
if (with_static)
|
||||
{
|
||||
fprintf(makefile, " -static");
|
||||
|
|
@ -532,7 +592,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 $(LDFLAGS)\n");
|
||||
fprintf(makefile, " -lCytoplasm -lm $(LDFLAGS)\n");
|
||||
}
|
||||
|
||||
/* Write rules for every source */
|
||||
|
|
@ -540,7 +600,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", ofl + 3);
|
||||
char *obj = string_cat("build", strchr(ofl, '/'));
|
||||
|
||||
fprintf(makefile, "%s: %s", obj, src);
|
||||
analyse_dependencies(makefile, src);
|
||||
|
|
@ -558,6 +618,10 @@ 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)\\\"\" ");
|
||||
|
|
@ -628,7 +692,7 @@ main_build(int argc, char *argv[])
|
|||
fprintf(makefile, " -lCytoplasm -lmbedtls -lmbedx509 -lmbedcrypto");
|
||||
if (with_lmdb) fprintf(makefile, " -llmdb");
|
||||
}
|
||||
fprintf(makefile, " -lCytoplasm $(CFLAGS)\n");
|
||||
fprintf(makefile, " -lCytoplasm -lm $(CFLAGS)\n");
|
||||
}
|
||||
|
||||
free(ofl);
|
||||
|
|
@ -716,6 +780,7 @@ main_build(int argc, char *argv[])
|
|||
str_array_free(aya);
|
||||
fflush(makefile);
|
||||
fclose(makefile);
|
||||
free(janet_dir);
|
||||
free(repo);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" "lunar-rainbow"
|
||||
.TH parsee-config 1 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee-config - generate a basic configuration file
|
||||
|
|
@ -11,7 +11,6 @@ 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]
|
||||
|
|
@ -35,7 +34,6 @@ $ parsee-config \\
|
|||
-H 'blow.hole' \\
|
||||
-s 'The Dark Shared Secret' \\
|
||||
-J 'xmpp.blow.hole' \\
|
||||
-j 'localhost' \\
|
||||
-S 128
|
||||
.fi
|
||||
.if n \{\
|
||||
|
|
@ -70,12 +68,6 @@ 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
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -1,70 +0,0 @@
|
|||
." 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)
|
||||
|
||||
1
guix.scm
1
guix.scm
|
|
@ -1 +0,0 @@
|
|||
.guix/modules/parsee.scm
|
||||
|
|
@ -150,7 +150,7 @@ ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha)
|
|||
return false;
|
||||
}
|
||||
|
||||
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
|
||||
path = StrConcat(3, "/_matrix/media/v3/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/client/v1/media/download/", uri->host, uri->path);
|
||||
path = StrConcat(3, "/_matrix/media/v3/download/", uri->host, uri->path);
|
||||
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
ASAuthenticateRequest(c, cctx);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
|
|
|
|||
|
|
@ -25,13 +25,11 @@ 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,
|
||||
|
|
@ -62,13 +60,11 @@ 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,
|
||||
|
|
@ -99,13 +95,11 @@ 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,
|
||||
|
|
@ -126,8 +120,7 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *ret, *serv;
|
||||
int status;
|
||||
char *path, *ret;
|
||||
if (!conf || !id)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -146,11 +139,6 @@ 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, "?",
|
||||
|
|
@ -164,7 +152,7 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
status = ParseeSetRequestJSON(ctx, json);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
|
|
@ -175,8 +163,6 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
Free(masquerade);
|
||||
Free(id);
|
||||
|
||||
(void) serv; // TODO
|
||||
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
|
|
|
|||
|
|
@ -33,11 +33,6 @@ 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)
|
||||
{
|
||||
|
|
@ -52,20 +47,18 @@ 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);
|
||||
status = ParseeSetRequestJSON(ctx, c);
|
||||
ParseeSetRequestJSON(ctx, c);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
||||
|
|
|
|||
|
|
@ -28,25 +28,6 @@ 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;
|
||||
|
|
|
|||
|
|
@ -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 = strtol(pl_str, NULL, 10);
|
||||
long pl = pl_str ? strtol(pl_str, NULL, 10) : 0;
|
||||
HashMap *map;
|
||||
|
||||
|
||||
|
|
|
|||
74
src/Extensions.c
Normal file
74
src/Extensions.c
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
#ifndef JANET
|
||||
#include <Extension.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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
|
||||
366
src/Extensions/DB.c
Normal file
366
src/Extensions/DB.c
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
#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
|
||||
222
src/Extensions/Data.c
Normal file
222
src/Extensions/Data.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
#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
|
||||
207
src/Extensions/Functions.c
Normal file
207
src/Extensions/Functions.c
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#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
|
||||
34
src/Extensions/Functions.h
Normal file
34
src/Extensions/Functions.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#ifdef JANET
|
||||
|
||||
#include <janet.h>
|
||||
|
||||
#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
|
||||
258
src/Extensions/Http.c
Normal file
258
src/Extensions/Http.c
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
#ifdef JANET
|
||||
|
||||
#include "Extensions/Internal.h"
|
||||
#include "Extensions/Functions.h"
|
||||
|
||||
#include <Cytoplasm/Uri.h>
|
||||
#include <Cytoplasm/HttpClient.h>
|
||||
|
||||
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
|
||||
98
src/Extensions/Internal.h
Normal file
98
src/Extensions/Internal.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef PARSEE_IEXTENSIONS_H
|
||||
#define PARSEE_IEXTENSIONS_H
|
||||
#ifdef JANET
|
||||
|
||||
#include <Extension.h>
|
||||
|
||||
#include <Cytoplasm/Cytoplasm.h>
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Stream.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <XMPPCommand.h>
|
||||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
#include <janet.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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
|
||||
170
src/Extensions/JSON.c
Normal file
170
src/Extensions/JSON.c
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
#ifdef JANET
|
||||
#include <janet.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <XML.h>
|
||||
|
||||
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
|
||||
30
src/Extensions/Jabber.c
Normal file
30
src/Extensions/Jabber.c
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#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
|
||||
304
src/Extensions/Janet.c
Normal file
304
src/Extensions/Janet.c
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
#ifdef JANET
|
||||
#include <Extension.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <janet.h>
|
||||
|
||||
#include <Cytoplasm/Cytoplasm.h>
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Stream.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#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
|
||||
86
src/Extensions/KV.c
Normal file
86
src/Extensions/KV.c
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
#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
|
||||
|
||||
149
src/Extensions/Matrix.c
Normal file
149
src/Extensions/Matrix.c
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
#ifdef JANET
|
||||
|
||||
#include "Extensions/Internal.h"
|
||||
|
||||
#include <AS.h>
|
||||
|
||||
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
|
||||
89
src/Extensions/PushEvents.c
Normal file
89
src/Extensions/PushEvents.c
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#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
|
||||
98
src/Extensions/PushStanza.c
Normal file
98
src/Extensions/PushStanza.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#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
|
||||
580
src/Extensions/RequestCmds.c
Normal file
580
src/Extensions/RequestCmds.c
Normal file
|
|
@ -0,0 +1,580 @@
|
|||
#ifdef JANET
|
||||
|
||||
#include "Extensions/Internal.h"
|
||||
|
||||
#include <XMPPCommand.h>
|
||||
#include <XMPPFormTool.h>
|
||||
|
||||
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
|
||||
98
src/Extensions/StanzaInfo.c
Normal file
98
src/Extensions/StanzaInfo.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#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
|
||||
357
src/Extensions/XML.c
Normal file
357
src/Extensions/XML.c
Normal file
|
|
@ -0,0 +1,357 @@
|
|||
#ifdef JANET
|
||||
#include <janet.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <XML.h>
|
||||
|
||||
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
|
||||
|
|
@ -22,6 +22,9 @@ 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;
|
||||
|
|
@ -73,7 +76,7 @@ ParseeCreateRequest(const ParseeConfig *conf, HttpRequestMethod meth, char *path
|
|||
|
||||
ctx = HttpRequest(
|
||||
meth,
|
||||
conf->homeserver_tls ? HTTP_FLAG_TLS : HTTP_FLAG_NONE,
|
||||
HTTP_FLAG_TLS,
|
||||
conf->homeserver_port, conf->homeserver_host,
|
||||
path
|
||||
);
|
||||
|
|
|
|||
64
src/Main.c
64
src/Main.c
|
|
@ -13,11 +13,16 @@
|
|||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <StanzaBuilder.h>
|
||||
#include <Unistring.h>
|
||||
#include <Parsee.h>
|
||||
#include <XMPP.h>
|
||||
#include <AS.h>
|
||||
|
||||
#ifdef JANET
|
||||
#include <janet.h>
|
||||
#endif
|
||||
#include <Extension.h>
|
||||
|
||||
static HttpServer *server = NULL;
|
||||
static pthread_t xmpp_thr;
|
||||
static XMPPComponent *jabber = NULL;
|
||||
|
|
@ -63,39 +68,23 @@ ParseeCheckMatrix(void *datp)
|
|||
{
|
||||
static volatile uint64_t streak = 0;
|
||||
ParseeData *data = datp;
|
||||
if (data->config->accept_pings && !ASPing(data->config))
|
||||
if (!ASPing(data->config))
|
||||
{
|
||||
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
|
||||
if (++streak == 10)
|
||||
if (++streak >= 5)
|
||||
{
|
||||
DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats");
|
||||
HashMap *json = DbJson(ref);
|
||||
HashMap *mucs = GrabObject(json, 1, "mucs");
|
||||
char *muc;
|
||||
void *ignored;
|
||||
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);
|
||||
|
||||
pthread_mutex_lock(&data->halt_lock);
|
||||
data->halted = true;
|
||||
pthread_mutex_unlock(&data->halt_lock);
|
||||
|
||||
/* 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);
|
||||
XMPPFinishCompStream(data->jabber);
|
||||
pthread_join(xmpp_thr, NULL);
|
||||
Log(LOG_INFO, "Stopping server...");
|
||||
HttpServerStop(data->server);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -115,6 +104,8 @@ Main(Array *args, HashMap *env)
|
|||
int http = 8;
|
||||
int verbose = 0;
|
||||
|
||||
Extensions *ext_ctx = NULL;
|
||||
|
||||
start = UtilTsMillis();
|
||||
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
|
|
@ -124,8 +115,11 @@ Main(Array *args, HashMap *env)
|
|||
);
|
||||
ParseePrintASCII();
|
||||
Log(LOG_INFO, "=======================");
|
||||
Log(LOG_INFO, "(C)opyright 2024-2025 LDA and other contributors");
|
||||
Log(LOG_INFO, "(C)opyright 2024 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?");
|
||||
|
|
@ -223,11 +217,9 @@ 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
|
||||
|
|
@ -287,18 +279,22 @@ 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);
|
||||
}
|
||||
|
||||
|
|
@ -350,6 +346,8 @@ end:
|
|||
ParseeDestroyHeadTable();
|
||||
ParseeDestroyJIDTable();
|
||||
|
||||
ExtensionDestroyContext(ext_ctx);
|
||||
|
||||
(void) env;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ 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, UnistrIsASCII); /* I'm not even going to try messing with the BMP anymore. */
|
||||
Unistr *filtered = UnistrFilter(uninick, UnistrIsBMP);
|
||||
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);
|
||||
|
||||
|
|
@ -100,7 +102,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, NULL);
|
||||
char *url = ParseeToUnauth(data, avatar);
|
||||
chat_id = ParseeGetFromRoomID(data, room_id);
|
||||
|
||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||
|
|
@ -255,9 +257,14 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!body || *body != '!')
|
||||
if (*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;
|
||||
}
|
||||
|
|
@ -289,10 +296,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 = NULL;
|
||||
char *user;
|
||||
|
||||
DbRef *room_data = NULL;
|
||||
HashMap *data_json = NULL;
|
||||
DbRef *room_data;
|
||||
HashMap *data_json;
|
||||
|
||||
bool direct = false;
|
||||
if (!data || !event || !from || !to)
|
||||
|
|
@ -324,8 +331,8 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
}
|
||||
else
|
||||
{
|
||||
char *matrix_name = NULL, *matrix_avatar = NULL;
|
||||
char *mime = NULL, *sha = NULL;
|
||||
char *matrix_name, *matrix_avatar;
|
||||
char *mime, *sha;
|
||||
|
||||
muc_id = ParseeGetMUCID(data, chat_id);
|
||||
if (!chat_id)
|
||||
|
|
@ -358,47 +365,28 @@ 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 = NULL;
|
||||
StanzaBuilder *builder;
|
||||
DbRef *ref = NULL;
|
||||
HashMap *json = NULL;
|
||||
HashMap *json;
|
||||
|
||||
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 *chat_id = NULL, *muc_id = NULL;
|
||||
char *m_sender = GrabString(event, 1, "sender");
|
||||
char *chat_id, *muc_id;
|
||||
char *reply_id = MatrixGetReply(event);
|
||||
char *xepd = ParseeXMPPify(data, event);
|
||||
char *xepd = ParseeXMPPify(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))
|
||||
{
|
||||
|
|
@ -429,7 +417,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
|
||||
type = direct ? "chat" : "groupchat";
|
||||
user = GrabString(json, 1, "xmpp_user");
|
||||
unauth = ParseeToUnauth(data, url, GrabString(event, 2, "content", "filename"));
|
||||
unauth = ParseeToUnauth(data, url);
|
||||
|
||||
encoded_from = ParseeEncodeMXID(m_sender);
|
||||
xmppified_user = StrConcat(3,
|
||||
|
|
@ -466,7 +454,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
Free(name);
|
||||
Free(avatar);
|
||||
}
|
||||
|
||||
if (reply_id)
|
||||
{
|
||||
/* TODO: Monocles chat DM users HATE this trick!
|
||||
|
|
@ -523,8 +510,8 @@ end:
|
|||
Free(stanza);
|
||||
Free(sender);
|
||||
Free(unauth);
|
||||
Free(encoded_from);
|
||||
Free(unedited_id);
|
||||
Free(encoded_from);
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
ref = NULL;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
#include <Matrix.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -35,34 +32,3 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,12 +36,6 @@ 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( \
|
||||
|
|
@ -50,9 +44,6 @@ 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;
|
||||
|
|
@ -67,26 +58,19 @@ 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");
|
||||
|
|
@ -147,7 +131,6 @@ ParseeConfigFree(void)
|
|||
return;
|
||||
}
|
||||
Free(config->component_host);
|
||||
Free(config->component_addr);
|
||||
Free(config->shared_comp_secret);
|
||||
Free(config->db_path);
|
||||
Free(config->homeserver_host);
|
||||
|
|
@ -159,6 +142,7 @@ ParseeConfigFree(void)
|
|||
Free(config->namespace_base);
|
||||
Free(config->media_base);
|
||||
Free(config->listen_as);
|
||||
Free(config->extensions);
|
||||
Free(config);
|
||||
}
|
||||
const ParseeConfig *
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ ParseeInitData(XMPPComponent *comp)
|
|||
data->config = ParseeConfigGet();
|
||||
data->router = HttpRouterCreate();
|
||||
data->jabber = comp;
|
||||
data->muc = CreateMUCServer(data);
|
||||
data->handler = CommandCreateRouter();
|
||||
|
||||
data->oid_servers = HashMapCreate();
|
||||
|
|
@ -117,7 +116,6 @@ 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);
|
||||
|
|
@ -132,6 +130,7 @@ ParseeCleanup(void *datp)
|
|||
char *chat;
|
||||
size_t i;
|
||||
uint64_t ts = UtilTsMillis();
|
||||
size_t entries = 0;
|
||||
|
||||
chats = DbList(data->db, 1, "chats");
|
||||
|
||||
|
|
@ -175,6 +174,7 @@ ParseeCleanup(void *datp)
|
|||
if (cleaned > threshold) \
|
||||
{ \
|
||||
DbDelete(data->db, 4, "chats", chat, #field"s", field); \
|
||||
entries++; \
|
||||
} \
|
||||
Free(field); \
|
||||
} \
|
||||
|
|
@ -231,6 +231,7 @@ ParseeCleanup(void *datp)
|
|||
if (cleaned > threshold) \
|
||||
{ \
|
||||
JsonValueFree(HashMapDelete(field##s, field)); \
|
||||
entries++; \
|
||||
} \
|
||||
Free(field); \
|
||||
} \
|
||||
|
|
@ -725,3 +726,13 @@ ParseeIsMediaEnabled(ParseeData *data, char *chat_id)
|
|||
|
||||
return ret;
|
||||
}
|
||||
bool
|
||||
ParseeIsMUC(ParseeData *data, char *jid)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return XMPPQueryMUC(data->jabber, jid, NULL);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
|
@ -539,6 +538,7 @@ 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", rev);
|
||||
Log(LOG_NOTICE, "Sending presence to %s last=%ds", rev, diff);
|
||||
XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false);
|
||||
|
||||
DbUnlock(data->db, chat);
|
||||
|
|
@ -688,13 +688,12 @@ end:
|
|||
|
||||
#include <Cytoplasm/Uri.h>
|
||||
char *
|
||||
ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
||||
ParseeToUnauth(ParseeData *data, char *mxc)
|
||||
{
|
||||
Uri *url = NULL;
|
||||
char *ret;
|
||||
char *key, *hmac;
|
||||
#define PAT "%s/media/%s%s?hmac=%s"
|
||||
#define PATF "%s/media/%s%s/%s?hmac=%s"
|
||||
#define PAT "%s/_matrix/client/v1/media/download/%s%s?hmac=%s"
|
||||
size_t l;
|
||||
if (!data || !mxc)
|
||||
{
|
||||
|
|
@ -715,45 +714,19 @@ ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
|||
hmac = ParseeHMACS(data->id, key);
|
||||
Free(key);
|
||||
|
||||
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);
|
||||
}
|
||||
l = snprintf(NULL, 0,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
ret = Malloc(l + 3);
|
||||
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
|
||||
);
|
||||
}
|
||||
snprintf(ret, l + 1,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
UriFree(url);
|
||||
Free(hmac);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -5,14 +5,11 @@
|
|||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <Matrix.h>
|
||||
#include <XML.h>
|
||||
#include <AS.h>
|
||||
|
||||
|
|
@ -20,7 +17,7 @@ typedef struct XMPPFlags {
|
|||
bool quote;
|
||||
} XMPPFlags;
|
||||
static char *
|
||||
XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||
{
|
||||
char *xepd = NULL, *tmp = NULL;
|
||||
|
||||
|
|
@ -58,7 +55,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
|
|||
} \
|
||||
} \
|
||||
while (0)
|
||||
switch (elem ? elem->type : -1)
|
||||
switch (elem->type)
|
||||
{
|
||||
case XML_ELEMENT_DATA:
|
||||
Concat(elem->data);
|
||||
|
|
@ -70,7 +67,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -83,7 +80,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -96,7 +93,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -129,7 +126,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -144,70 +141,36 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
if (i != 0)
|
||||
{
|
||||
Concat("\n");
|
||||
}
|
||||
Concat("\n");
|
||||
}
|
||||
else if (StrEquals(elem->name, "a"))
|
||||
{
|
||||
char *href = HashMapGet(elem->attrs, "href");
|
||||
Uri *pref = UriParse(href);
|
||||
if (pref && StrEquals(pref->host, "matrix.to"))
|
||||
/* TODO: Check if the element here is a Matrix.TO
|
||||
* pointing to a Parsee user. */
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat(" < ");
|
||||
Concat(href);
|
||||
Concat(" >");
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
UriFree(pref);
|
||||
Concat("< ");
|
||||
Concat(href);
|
||||
Concat(" >");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -219,45 +182,8 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
|
|||
}
|
||||
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(ParseeData *data, HashMap *event)
|
||||
ParseeXMPPify(HashMap *event)
|
||||
{
|
||||
char *type, *format, *html;
|
||||
char *xepd = NULL;
|
||||
|
|
@ -276,28 +202,20 @@ ParseeXMPPify(ParseeData *data, HashMap *event)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!StrEquals(GetBodyFormat(event), "org.matrix.custom.html"))
|
||||
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
|
||||
if (!StrEquals(format, "org.matrix.custom.html"))
|
||||
{
|
||||
/* Settle for the raw body instead. */
|
||||
char *body = GetRawBody(event);
|
||||
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
|
||||
return StrDuplicate(body);
|
||||
}
|
||||
|
||||
html = GetHTMLBody(event);
|
||||
|
||||
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
|
||||
html = StrConcat(3, "<html>", html, "</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(data ? data->config : NULL, event, elem, flags);
|
||||
xepd = XMPPifyElement(event, elem, flags);
|
||||
|
||||
XMLFreeElement(elem);
|
||||
Free(html);
|
||||
|
|
|
|||
|
|
@ -6,28 +6,6 @@
|
|||
#include <Glob.h>
|
||||
#include <AS.h>
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,37 +10,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
static HttpClientContext *
|
||||
TryDownload(ParseeData *data, char *server, char *identi)
|
||||
{
|
||||
HttpClientContext *cctx;
|
||||
char *path;
|
||||
server = HttpUrlEncode(server);
|
||||
identi = HttpUrlEncode(identi);
|
||||
|
||||
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(data->config, cctx);
|
||||
Free(path);
|
||||
|
||||
HttpRequestSendHeaders(cctx);
|
||||
if (HttpRequestSend(cctx) != HTTP_OK)
|
||||
{
|
||||
Log(LOG_WARNING, "Failing back.");
|
||||
HttpClientContextFree(cctx);
|
||||
path = StrConcat(4, "/_matrix/client/v1/media/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(data->config, cctx);
|
||||
Free(path);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
}
|
||||
|
||||
Free(server);
|
||||
Free(identi);
|
||||
return cctx;
|
||||
}
|
||||
|
||||
RouteHead(RouteMedia, arr, argp)
|
||||
{
|
||||
ParseeHttpArg *args = argp;
|
||||
|
|
@ -48,7 +17,7 @@ RouteHead(RouteMedia, arr, argp)
|
|||
HashMap *reqh, *params;
|
||||
char *server = ArrayGet(arr, 0);
|
||||
char *identi = ArrayGet(arr, 1);
|
||||
char *key, *val;
|
||||
char *path, *key, *val;
|
||||
char *hmac, *chkmak = NULL;
|
||||
|
||||
params = HttpRequestParams(args->ctx);
|
||||
|
|
@ -75,7 +44,15 @@ RouteHead(RouteMedia, arr, argp)
|
|||
|
||||
/* Proxy the media through an authenticated endpoint if the HMAC
|
||||
* is valid. */
|
||||
cctx = TryDownload(args->data, server, identi);
|
||||
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);
|
||||
reqh = HttpResponseHeaders(cctx);
|
||||
while (HashMapIterate(reqh, &key, (void **) &val))
|
||||
{
|
||||
|
|
@ -88,6 +65,8 @@ RouteHead(RouteMedia, arr, argp)
|
|||
}
|
||||
|
||||
HttpClientContextFree(cctx);
|
||||
Free(server);
|
||||
Free(identi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ RouteHead(RoutePing, arr, argp)
|
|||
ParseeHttpArg *args = argp;
|
||||
HashMap *request = NULL;
|
||||
HashMap *response = NULL;
|
||||
Array *events;
|
||||
size_t i;
|
||||
|
||||
response = ASVerifyRequest(args);
|
||||
if (response)
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ SignalHandler(int signal)
|
|||
HttpServerStop(data->server);
|
||||
return;
|
||||
}
|
||||
if (signal == SIGPIPE)
|
||||
{
|
||||
Log(LOG_DEBUG, "Caught a SIGPIPE...");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -41,7 +46,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))
|
||||
if (!Register(SIGTERM) || !Register(SIGINT) || !Register(SIGPIPE))
|
||||
{
|
||||
Log(LOG_ERR, "Couldn't register signals...");
|
||||
return false;
|
||||
|
|
|
|||
45
src/Unistr.c
45
src/Unistr.c
|
|
@ -192,16 +192,6 @@ 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)
|
||||
|
|
@ -277,38 +267,3 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(xmlparent, XMLCreateText("`"));
|
||||
XMLAddChild(head, XMLCreateText("`"));
|
||||
break;
|
||||
case XEP393_SRKE:
|
||||
XMLAddChild(head, XMLCreateText("~"));
|
||||
|
|
|
|||
|
|
@ -31,12 +31,6 @@ 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);
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ 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);
|
||||
|
||||
|
|
@ -199,9 +198,7 @@ XMLCrank(XMLexer *lexer)
|
|||
else if (XMLookahead(lexer, "--", false))
|
||||
{
|
||||
/* Throw error */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case XML_STATE_PI:
|
||||
|
|
@ -218,9 +215,6 @@ XMLCrank(XMLexer *lexer)
|
|||
if (!attrname)
|
||||
{
|
||||
/* TODO: Throw error */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
XMLPushElement(lexer, attrname);
|
||||
|
||||
|
|
@ -247,10 +241,7 @@ XMLCrank(XMLexer *lexer)
|
|||
}
|
||||
else if (XMLookahead(lexer, "'", true))
|
||||
{
|
||||
//while (true); uh oh
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
while (true);
|
||||
}
|
||||
break;
|
||||
case XML_STATE_ATTRTAIL:
|
||||
|
|
@ -259,8 +250,6 @@ XMLCrank(XMLexer *lexer)
|
|||
if (!XMLookahead(lexer, ">", true))
|
||||
{
|
||||
/* TODO: Throw error. */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
lexer->state = XML_STATE_NONE;
|
||||
|
|
@ -269,8 +258,6 @@ XMLCrank(XMLexer *lexer)
|
|||
break;
|
||||
default:
|
||||
/* TODO */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
/* TODO: Crank our XML parser. */
|
||||
|
|
@ -706,26 +693,6 @@ 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));
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
#define DEFAULT_PROSODY_PORT 5347
|
||||
|
||||
XMPPComponent *
|
||||
XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||
XMPPInitialiseCompStream(char *host, int port)
|
||||
{
|
||||
int sd = -1;
|
||||
struct addrinfo hints, *res, *res0;
|
||||
|
|
@ -28,17 +28,12 @@ XMPPInitialiseCompStream(char *addr, 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(addr, serv, &hints, &res0);
|
||||
error = getaddrinfo(host, serv, &hints, &res0);
|
||||
if (error)
|
||||
{
|
||||
const char *error_str = gai_strerror(error);
|
||||
|
|
@ -61,10 +56,6 @@ XMPPInitialiseCompStream(char *addr, 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;
|
||||
|
|
@ -75,10 +66,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
|||
|
||||
if (sd < 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': no socket available", __func__,
|
||||
host
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
freeaddrinfo(res0);
|
||||
|
|
@ -86,10 +73,6 @@ XMPPInitialiseCompStream(char *addr, 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;
|
||||
}
|
||||
|
|
@ -182,49 +165,6 @@ 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, "</%s>", 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);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
#include "XMPPThread/internal.h"
|
||||
|
||||
bool
|
||||
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||
{
|
||||
|
|
@ -44,7 +42,6 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
Free(uuid);
|
||||
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
||||
{
|
||||
Log(LOG_ERR, "Didn't receive an <iq> stanza");
|
||||
XMLFreeElement(iq_query);
|
||||
return false;
|
||||
}
|
||||
|
|
@ -56,11 +53,6 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -170,7 +162,6 @@ 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)
|
||||
{
|
||||
|
|
@ -198,15 +189,7 @@ XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool
|
|||
XMLAddChild(x, history);
|
||||
|
||||
XMLAddChild(presence, x);
|
||||
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);
|
||||
XMPPAnnotatePresence(presence);
|
||||
|
||||
if (hash)
|
||||
{
|
||||
|
|
@ -242,7 +225,6 @@ void
|
|||
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||
{
|
||||
XMLElement *presence;
|
||||
IQFeatures *features;
|
||||
char *from, *id;
|
||||
if (!comp || !fr || !muc)
|
||||
{
|
||||
|
|
@ -264,16 +246,7 @@ XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
|||
XMLAddChild(presence, status);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
XMPPAnnotatePresence(presence);
|
||||
|
||||
XMPPSendStanza(comp, presence, 10000);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,154 +0,0 @@
|
|||
#include <MUCServ.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MUCNS "http://jabber.org/protocol/muc"
|
||||
|
||||
struct MUCServer {
|
||||
pthread_mutex_t mut;
|
||||
|
||||
ParseeData *data;
|
||||
};
|
||||
|
||||
static char *
|
||||
GetMUCName(char *jid)
|
||||
{
|
||||
char *name, *at;
|
||||
size_t len;
|
||||
if (!jid || *jid != '#')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jid++;
|
||||
|
||||
at = strchr(jid, '@');
|
||||
if (!at)
|
||||
{
|
||||
return StrDuplicate(jid);
|
||||
}
|
||||
len = at - jid;
|
||||
name = Malloc(len + 1);
|
||||
memset(name, '\0', len + 1);
|
||||
memcpy(name, jid, len);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
MUCServer *
|
||||
CreateMUCServer(ParseeData *data)
|
||||
{
|
||||
MUCServer *server;
|
||||
if (!data)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
server = Malloc(sizeof(*server));
|
||||
pthread_mutex_init(&server->mut, NULL);
|
||||
server->data = data;
|
||||
return server;
|
||||
}
|
||||
|
||||
static bool
|
||||
MUCManagePresence(MUCServer *serv, XMLElement *stanza, char *from, char *to)
|
||||
{
|
||||
char *name;
|
||||
XMLElement *x = XMLookForTKV(stanza, "x", "xmlns", MUCNS);
|
||||
char *type = HashMapGet(stanza->attrs, "type");
|
||||
if (x && !type)
|
||||
{
|
||||
/* The user is trying to join the MUC */
|
||||
name = GetMUCName(to);
|
||||
Log(LOG_WARNING, "%s is trying to join MUC '%s'", from, name);
|
||||
|
||||
/* TODO: Check if the user should be joining. If so, make them.
|
||||
* Implementing MUCs is gonna be fun. */
|
||||
|
||||
/* TODO: Presence broadcast hell. */
|
||||
Free(name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
MUCManageMessage(MUCServer *serv, XMLElement *stanza, char *from, char *to)
|
||||
{
|
||||
Log(LOG_WARNING, "MUCSERV: got a message %s->%s", from, to);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ManageMUCStanza(MUCServer *serv, XMLElement *stanza)
|
||||
{
|
||||
char *stype, *from, *to;
|
||||
bool ret;
|
||||
if (!serv || !stanza)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
to = HashMapGet(stanza->attrs, "to");
|
||||
stype = stanza->name;
|
||||
|
||||
if (to && *to != '#')
|
||||
{
|
||||
/* We aren't interacting with a MUC at all. Don't do anything. */
|
||||
return false;
|
||||
}
|
||||
if (StrEquals(stype, "iq"))
|
||||
{
|
||||
/* TODO: Worry about IQ later */
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = false;
|
||||
pthread_mutex_lock(&serv->mut);
|
||||
if (StrEquals(stype, "presence"))
|
||||
{
|
||||
ret = MUCManagePresence(serv, stanza, from, to);
|
||||
}
|
||||
else if (StrEquals(stype, "message"))
|
||||
{
|
||||
ret = MUCManageMessage(serv, stanza, from, to);
|
||||
}
|
||||
/* TODO: Do stuff while locked */
|
||||
pthread_mutex_unlock(&serv->mut);
|
||||
|
||||
/* TODO: Verify the destination, and make sure we aren't doing
|
||||
* anything stupid(especially with discovery) */
|
||||
(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);
|
||||
}
|
||||
|
|
@ -7,8 +7,6 @@
|
|||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
void
|
||||
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
||||
|
|
@ -161,31 +159,27 @@ XMPPGetReply(XMLElement *elem)
|
|||
|
||||
return HashMapGet(rep->attrs, "id");
|
||||
}
|
||||
ssize_t
|
||||
XMPPGetReplyOffset(XMLElement *elem)
|
||||
void
|
||||
XMPPAnnotatePresence(XMLElement *presence)
|
||||
{
|
||||
if (!elem)
|
||||
XMLElement *c;
|
||||
char *ver;
|
||||
if (!presence)
|
||||
{
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
char *
|
||||
XMPPGetModeration(XMLElement *stanza)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,20 +4,10 @@
|
|||
#include <Cytoplasm/Array.h>
|
||||
#include <Cytoplasm/Str.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;
|
||||
};
|
||||
#include "XMPPCommand/Internal.h"
|
||||
|
||||
XMPPCommand *
|
||||
XMPPBasicCmd(char *node, char *name, XMPPCmdCallback callback_funct)
|
||||
XMPPBasicCmd(XMPPCommandFlags flags, char *node, char *name, XMPPCmdCallback callback_funct)
|
||||
{
|
||||
XMPPCommand *cmd;
|
||||
|
||||
|
|
@ -32,6 +22,7 @@ XMPPBasicCmd(char *node, char *name, XMPPCmdCallback callback_funct)
|
|||
cmd->name = StrDuplicate(name);
|
||||
cmd->form_instruction = NULL;
|
||||
cmd->form_title = NULL;
|
||||
cmd->flags = flags;
|
||||
|
||||
/* No options -> no form required */
|
||||
cmd->options = NULL;
|
||||
|
|
@ -127,7 +118,7 @@ XMPPFormifyCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
|||
ArrayFree(cmd->options);
|
||||
cmd->options = NULL;
|
||||
|
||||
cmd->otf(m, cmd, from);
|
||||
cmd->otf(m, cmd, from, cmd->node);
|
||||
}
|
||||
if (!cmd->options)
|
||||
{
|
||||
|
|
@ -184,13 +175,13 @@ XMPPCommandRequiresForm(XMPPCommand *cmd)
|
|||
return cmd ? (cmd->otf || !!cmd->options) : false;
|
||||
}
|
||||
void
|
||||
XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form)
|
||||
XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form, char *node)
|
||||
{
|
||||
if (!m || !cmd || !from || !to)
|
||||
if (!m || !cmd || !from || !to || !node)
|
||||
{
|
||||
return;
|
||||
}
|
||||
cmd->callback(m, from, form, to);
|
||||
cmd->callback(m, from, form, to, node);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
17
src/XMPPCommand/Internal.h
Normal file
17
src/XMPPCommand/Internal.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#include <XMPPCommand.h>
|
||||
|
||||
#include <Cytoplasm/Array.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;
|
||||
|
||||
XMPPCommandFlags flags;
|
||||
};
|
||||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <pthread.h>
|
||||
|
||||
#include "XMPPCommand/Internal.h"
|
||||
|
||||
typedef struct XMPPSession {
|
||||
char *identifier;
|
||||
|
||||
|
|
@ -323,7 +325,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);
|
||||
XMPPExecuteCommand(m, cmd, from, command_xml, NULL, node);
|
||||
XMLAddChild(iq, command_xml);
|
||||
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
|
|
@ -363,7 +365,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);
|
||||
XMPPExecuteCommand(m, cmd, from, command_xml, x_form, node);
|
||||
XMLAddChild(iq, command_xml);
|
||||
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
|
|
@ -385,3 +387,15 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <XML.h>
|
||||
|
||||
void
|
||||
AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -23,6 +23,8 @@ 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.");
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <XML.h>
|
||||
|
||||
void
|
||||
AddNoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
AddNoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -21,6 +21,8 @@ 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.");
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <XML.h>
|
||||
|
||||
void
|
||||
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
size_t i;
|
||||
|
|
@ -20,6 +20,8 @@ AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
|||
XMLElement *title;
|
||||
XMLElement *reported, *item, *field, *value, *txt;
|
||||
|
||||
(void) node;
|
||||
|
||||
x = XMLCreateTag("x");
|
||||
title = XMLCreateTag("title");
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <XML.h>
|
||||
|
||||
void
|
||||
CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -31,4 +31,5 @@ CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
|
|||
SetNote("info", "Parsee data was sucessfully cleant up.");
|
||||
|
||||
(void) form;
|
||||
(void) node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ DelAdmin(Array *admin_list, char *glob)
|
|||
return removed;
|
||||
}
|
||||
void
|
||||
DelAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
DelAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -84,9 +84,11 @@ DelAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement
|
|||
}
|
||||
SetNote("info", "Sucessfully removed admins");
|
||||
/* TODO */
|
||||
|
||||
(void) node;
|
||||
}
|
||||
void
|
||||
FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
||||
FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
DbRef *admins;
|
||||
|
|
@ -120,4 +122,6 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
|||
|
||||
DbUnlock(data->db, admins);
|
||||
XMPPAddOption(cmd, admin_opt);
|
||||
|
||||
(void) node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <XML.h>
|
||||
|
||||
void
|
||||
MUCInformationID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
MUCInformationID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *muc = ParseeTrimJID(from);
|
||||
|
|
@ -29,9 +29,10 @@ 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)
|
||||
MUCInformationCID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *muc = ParseeTrimJID(from);
|
||||
|
|
@ -46,4 +47,5 @@ MUCInformationCID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElemen
|
|||
Free(msg);
|
||||
Free(chat_id);
|
||||
(void) form;
|
||||
(void) node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,12 @@
|
|||
#include <AS.h>
|
||||
|
||||
void
|
||||
MUCSetKey(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
MUCSetKey(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *affiliation = NULL, *role = NULL;
|
||||
char *chat_id = NULL;
|
||||
char *muc = NULL;
|
||||
char *affiliation, *role;
|
||||
char *chat_id;
|
||||
char *muc;
|
||||
|
||||
char *key = NULL, *val = NULL;
|
||||
|
||||
|
|
@ -51,9 +51,10 @@ end:
|
|||
Free(chat_id);
|
||||
Free(muc);
|
||||
(void) form;
|
||||
(void) node;
|
||||
}
|
||||
void
|
||||
MUCGetKeys(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
MUCGetKeys(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *affiliation = NULL, *role = NULL;
|
||||
|
|
@ -114,4 +115,5 @@ end:
|
|||
Free(chat_id);
|
||||
Free(muc);
|
||||
(void) form;
|
||||
(void) node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#include <AS.h>
|
||||
|
||||
void
|
||||
MUCUnlink(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
MUCUnlink(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *affiliation, *role;
|
||||
|
|
@ -64,4 +64,5 @@ MUCUnlink(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
|||
Free(room);
|
||||
Free(muc);
|
||||
(void) form;
|
||||
(void) node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
#define TITLE "Parsee global bans"
|
||||
|
||||
void
|
||||
NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -89,4 +89,5 @@ NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
|
|||
}
|
||||
|
||||
(void) form;
|
||||
(void) node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <XML.h>
|
||||
|
||||
void
|
||||
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
size_t alloc = MemoryAllocated();
|
||||
|
|
@ -69,5 +69,6 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
|||
XMLAddChild(out, x);
|
||||
|
||||
(void) form;
|
||||
(void) node;
|
||||
(void) m;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
#include <XML.h>
|
||||
|
||||
void
|
||||
ClearWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
ClearWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -35,9 +35,10 @@ 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)
|
||||
AddWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -79,9 +80,10 @@ 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)
|
||||
WhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out, char *node)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
|
@ -139,4 +141,5 @@ WhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElemen
|
|||
}
|
||||
|
||||
(void) form;
|
||||
(void) node;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <XMPPCommand.h>
|
||||
|
|
@ -13,86 +12,33 @@
|
|||
|
||||
#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(IQFeatures *features)
|
||||
XMPPGenerateVer(void)
|
||||
{
|
||||
char *S = NULL;
|
||||
unsigned char *Sha = NULL;
|
||||
Array *identities = ArrayCreate();
|
||||
Array *features = ArrayCreate();
|
||||
size_t i;
|
||||
|
||||
/* Initialise identity table, to be sorted */
|
||||
ArraySort(features->identity, IdentitySort);
|
||||
for (i = 0; i < ArraySize(features->identity); i++)
|
||||
#define IdentitySimple(cat, Type, Name) { \
|
||||
XMPPIdentity *id = Malloc(sizeof(*id)); \
|
||||
id->category = cat; \
|
||||
id->lang = NULL; \
|
||||
id->type = Type; \
|
||||
id->name = Name; \
|
||||
ArrayAdd(identities, id); }
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) ArrayAdd(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
ArraySort(identities, IdentitySort);
|
||||
for (i = 0; i < ArraySize(identities); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||
char *id_chunk = StrConcat(7,
|
||||
identity->category, "/",
|
||||
identity->type, "/",
|
||||
|
|
@ -104,10 +50,10 @@ XMPPGenerateVer(IQFeatures *features)
|
|||
Free(id_chunk);
|
||||
}
|
||||
|
||||
ArraySort(features->adverts, ((int (*) (void *, void *)) ICollate));
|
||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||
ArraySort(features, ((int (*) (void *, void *)) ICollate));
|
||||
for (i = 0; i < ArraySize(features); i++)
|
||||
{
|
||||
char *feature = ArrayGet(features->adverts, i);
|
||||
char *feature = ArrayGet(features, i);
|
||||
char *tmp = S;
|
||||
S = StrConcat(3, S, feature, "<");
|
||||
Free(tmp);
|
||||
|
|
@ -118,64 +64,16 @@ XMPPGenerateVer(IQFeatures *features)
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,15 +94,13 @@ 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 Parsee."
|
||||
"This is a bridged Matrix user, from " NAME "."
|
||||
);
|
||||
Free(mxid);
|
||||
Free(name);
|
||||
|
|
|
|||
|
|
@ -56,8 +56,6 @@ XMPPDispatcher(void *argp)
|
|||
|
||||
if (!stanza)
|
||||
{
|
||||
/* TODO: We shouldn't be busywaiting. Even with a sleep call.
|
||||
*/
|
||||
UtilSleepMillis(10);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -67,8 +65,12 @@ XMPPDispatcher(void *argp)
|
|||
Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name);
|
||||
}
|
||||
|
||||
if (ManageMUCStanza(args->muc, stanza))
|
||||
if (ExtensionPushStanza(args->exts, stanza, STANZA_RAW))
|
||||
{
|
||||
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Stanza was vetoed by an extension.");
|
||||
}
|
||||
XMLFreeElement(stanza);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -138,6 +140,7 @@ bool
|
|||
XMPPCommandFilter(XMPPCommandManager *m, char *id, XMLElement *stanza)
|
||||
{
|
||||
ParseeData *args = XMPPGetManagerCookie(m);
|
||||
XMPPCommandFlags flags;
|
||||
char *trimmed_from;
|
||||
char *from;
|
||||
char *chat_id;
|
||||
|
|
@ -152,28 +155,24 @@ XMPPCommandFilter(XMPPCommandManager *m, char *id, XMLElement *stanza)
|
|||
is_muc = !!(chat_id = ParseeGetFromMUCID(args, trimmed_from));
|
||||
Free(trimmed_from);
|
||||
Free(chat_id);
|
||||
#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; \
|
||||
} \
|
||||
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;
|
||||
}
|
||||
XMPPCOMMANDS
|
||||
#undef XMPP_COMMAND
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -199,13 +198,14 @@ ParseeXMPPThread(void *argp)
|
|||
XMPPCommand *cmd;
|
||||
#define XMPP_COMMAND(f,l,n,t,s) \
|
||||
cmd = XMPPBasicCmd( \
|
||||
n, t, f \
|
||||
l, n, t, f \
|
||||
); \
|
||||
s \
|
||||
XMPPRegisterCommand(info.m, cmd);
|
||||
XMPPCOMMANDS
|
||||
#undef XMPP_COMMAND
|
||||
}
|
||||
ExtensionRequestCommands(args->exts);
|
||||
info.pep_manager = CreatePEPManager(args, &info);
|
||||
{
|
||||
PEPManagerAddEvent(
|
||||
|
|
@ -244,96 +244,66 @@ ParseeXMPPThread(void *argp)
|
|||
}
|
||||
}
|
||||
|
||||
while (!args->halted)
|
||||
while (true)
|
||||
{
|
||||
while (true)
|
||||
char *id;
|
||||
|
||||
stanza = XMLDecode(jabber->stream, false);
|
||||
if (!stanza)
|
||||
{
|
||||
char *id;
|
||||
|
||||
stanza = XMLDecode(jabber->stream, false);
|
||||
if (!stanza)
|
||||
/* Try to check if an error is abound */
|
||||
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
/* Try to check if an error is abound */
|
||||
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "RECEIVED EOF.");
|
||||
}
|
||||
break;
|
||||
Log(LOG_DEBUG, "RECEIVED EOF.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (args->verbosity >= PARSEE_VERBOSE_STANZA)
|
||||
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)))
|
||||
{
|
||||
Stream *output = StreamStderr();
|
||||
StreamPrintf(output, "-------STANZA BEGIN-------" "\n");
|
||||
XMLEncode(output, stanza);
|
||||
StreamPrintf(output, "\n--------STANZA END--------" "\n");
|
||||
StreamFlush(output);
|
||||
}
|
||||
pthread_mutex_lock(&await->cond_lock);
|
||||
await->stanza = stanza;
|
||||
pthread_cond_signal(&await->condition);
|
||||
pthread_mutex_unlock(&await->cond_lock);
|
||||
|
||||
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);
|
||||
|
||||
HashMapDelete(await_table, id);
|
||||
|
||||
pthread_mutex_unlock(&await_lock);
|
||||
continue;
|
||||
}
|
||||
pthread_mutex_unlock(&await_lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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_unlock(&await_lock);
|
||||
}
|
||||
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);
|
||||
/* 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...");
|
||||
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++)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
#include <Matrix.h>
|
||||
#include <AS.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
|
@ -116,37 +115,33 @@ end:
|
|||
|
||||
#define DISCO "http://jabber.org/protocol/disco#info"
|
||||
static XMLElement *
|
||||
IQGenerateQuery(IQFeatures *features)
|
||||
IQGenerateQuery(void)
|
||||
{
|
||||
XMLElement *query;
|
||||
if (!features)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
query = XMLCreateTag("query");
|
||||
XMLElement *query = XMLCreateTag("query");
|
||||
XMLAddAttr(query, "xmlns", DISCO);
|
||||
{
|
||||
XMLElement *feature;
|
||||
size_t i;
|
||||
for (i = 0; i < ArraySize(features->identity); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||
#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);
|
||||
|
||||
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);
|
||||
}
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
}
|
||||
|
||||
return query;
|
||||
|
|
@ -156,7 +151,6 @@ 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");
|
||||
|
|
@ -169,10 +163,9 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
|||
XMLAddAttr(iq_reply, "type", "result");
|
||||
XMLAddAttr(iq_reply, "id", id);
|
||||
|
||||
features = LookupJIDFeatures(to);
|
||||
query = IQGenerateQuery(features);
|
||||
query = IQGenerateQuery();
|
||||
{
|
||||
char *ver = XMPPGenerateVer(features);
|
||||
char *ver = XMPPGenerateVer();
|
||||
char *node = StrConcat(3, REPOSITORY, "#", ver);
|
||||
XMLAddAttr(query, "node", node);
|
||||
|
||||
|
|
@ -180,7 +173,6 @@ 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);
|
||||
|
|
@ -374,7 +366,7 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
bool
|
||||
IQIsCommandList(ParseeData *args, XMLElement *stanza)
|
||||
{
|
||||
char *parsee = NULL, *to;
|
||||
char *parsee = NULL;
|
||||
XMLElement *query = XMLookForTKV(
|
||||
stanza, "query", "xmlns",
|
||||
"http://jabber.org/protocol/disco#items"
|
||||
|
|
@ -389,8 +381,7 @@ IQIsCommandList(ParseeData *args, XMLElement *stanza)
|
|||
}
|
||||
|
||||
parsee = ParseeJID(args);
|
||||
to = HashMapGet(stanza->attrs, "to");
|
||||
ret = StrEquals(to, parsee) || StrEquals(to, args->config->component_host);
|
||||
ret = StrEquals(HashMapGet(stanza->attrs, "to"), parsee);
|
||||
Free(parsee);
|
||||
|
||||
return ret;
|
||||
|
|
@ -430,10 +421,16 @@ 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);
|
||||
}
|
||||
|
|
@ -574,7 +571,7 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version"))
|
||||
{
|
||||
XMLElement *iq_reply, *query;
|
||||
XMLElement *name, *version, *os;
|
||||
XMLElement *name, *version;
|
||||
|
||||
iq_reply = XMLCreateTag("iq");
|
||||
XMLAddAttr(iq_reply, "to", from);
|
||||
|
|
@ -585,19 +582,14 @@ 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);
|
||||
|
|
@ -605,14 +597,10 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
}
|
||||
else
|
||||
{
|
||||
char *buf = NULL;
|
||||
Stream *s = StrStreamWriter(&buf);
|
||||
Log(LOG_WARNING, "Unknown I/Q received:");
|
||||
XMLEncode(s, stanza);
|
||||
StreamFlush(s);
|
||||
StreamClose(s);
|
||||
Log(LOG_WARNING, "%s", buf);
|
||||
Free(buf);
|
||||
XMLEncode(StreamStdout(), stanza);
|
||||
StreamPrintf(StreamStdout(),"\n");
|
||||
StreamFlush(StreamStdout());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -629,6 +617,7 @@ IQSet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
{
|
||||
XMPPCommandManager *manager = thr->info->m;
|
||||
XMPPManageCommand(manager, stanza, args);
|
||||
ExtensionManageCommands(args->exts, stanza);
|
||||
}
|
||||
#undef DISCO
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Unistring.h>
|
||||
#include <Matrix.h>
|
||||
#include <AS.h>
|
||||
|
||||
|
|
@ -277,7 +276,7 @@ end_error:
|
|||
Log(LOG_DEBUG, "Error management: %fs", Elapsed(rectime));
|
||||
}
|
||||
|
||||
if (moderated && !(!chat && strncmp(HashMapGet(stanza->attrs, "to"), "parsee@", 7)))
|
||||
if (moderated)
|
||||
{
|
||||
/* TODO: Parsee MUST check if it is a valid MUC */
|
||||
char *resource = ParseeGetResource(from);
|
||||
|
|
@ -325,9 +324,8 @@ end_error:
|
|||
{
|
||||
Log(LOG_DEBUG, "Fetching bridge: %fs", Elapsed(rectime));
|
||||
}
|
||||
char *trim = ParseeTrimJID(from);
|
||||
if (!mroom_id && !room && !XMPPIsParseeStanza(stanza) &&
|
||||
to && *to == '@' && !XMPPQueryMUC(jabber, trim, NULL))
|
||||
to && *to == '@')
|
||||
{
|
||||
DbRef *room_ref;
|
||||
HashMap *room_json;
|
||||
|
|
@ -346,7 +344,6 @@ 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
|
||||
|
|
@ -397,16 +394,10 @@ 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;
|
||||
Free(chat_id);
|
||||
|
||||
pthread_mutex_unlock(&thr->info->chk_lock);
|
||||
|
||||
LazyRegister(args, encoded, !chat ? res : NULL);
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
|
|
@ -420,10 +411,15 @@ end_error:
|
|||
|
||||
/* Check if it is a media link */
|
||||
oob = XMLookForTKV(stanza, "x", "xmlns", "jabber:x:oob");
|
||||
if (chat_id && StrEquals(type, "chat") && !is_parsee)
|
||||
|
||||
/* 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))
|
||||
{
|
||||
/* Very clearly a MUC DM. */
|
||||
event_id = NULL;
|
||||
//if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Stanza was vetoed by an extension.");
|
||||
}
|
||||
}
|
||||
else if (oob && data && media_enabled)
|
||||
{
|
||||
|
|
@ -521,15 +517,6 @@ 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ end_item:
|
|||
Free(room);
|
||||
FreeStatuses(statuses);
|
||||
}
|
||||
if (status && !StrEquals(type, "unavailable"))
|
||||
if (status)
|
||||
{
|
||||
XMLElement *status_data = ArrayGet(status->children, 0);
|
||||
char *decode_from = ParseeLookupJID(oid);
|
||||
|
|
@ -309,8 +309,6 @@ 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)."
|
||||
|
|
|
|||
|
|
@ -32,17 +32,13 @@
|
|||
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
|
||||
IdentitySimple("component", "generic", "Parsee's component")
|
||||
|
||||
typedef struct IQFeatures {
|
||||
Array *identity;
|
||||
Array *adverts;
|
||||
} IQFeatures;
|
||||
typedef struct PEPManager PEPManager;
|
||||
typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item);
|
||||
|
||||
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
|
||||
|
|
@ -69,33 +65,9 @@ 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);
|
||||
|
|
@ -109,9 +81,6 @@ 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);
|
||||
|
|
|
|||
81
src/include/Extension.h
Normal file
81
src/include/Extension.h
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#ifndef PARSEE_EXTENSION_H
|
||||
#define PARSEE_EXTENSION_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <Cytoplasm/Json.h>
|
||||
|
||||
#include <XML.h>
|
||||
|
||||
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 <strong>WILL</strong> 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
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#ifndef PARSEE_MUCSERV_H
|
||||
#define PARSEE_MUCSERV_H
|
||||
|
||||
/*-* An easy interface for handling MUCs.
|
||||
*/
|
||||
|
||||
typedef struct MUCServer MUCServer;
|
||||
|
||||
#include <Parsee.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/** Creates a MUC server handle given a ParseeData handle.
|
||||
* This handle shall be used for all MUC-related operations.
|
||||
* ----------------------------------------------------------
|
||||
* Returns: a MUC server handle[HEAP] | NULL
|
||||
* Thrasher: FreeMUCServer
|
||||
* See-Also: https://xmpp.org/extensions/xep-0045.html */
|
||||
extern MUCServer * CreateMUCServer(ParseeData *data);
|
||||
|
||||
/** Manages a stanza from a MUC, and returns true if the stanza
|
||||
* was actually processed by the MUC server.
|
||||
* -------------------------------------------------------------
|
||||
* See-Also: CreateMUCServer */
|
||||
extern bool ManageMUCStanza(MUCServer *serv, XMLElement *stanza);
|
||||
|
||||
/** Checks if a MUC(from its localpart, so
|
||||
* <code>'#foobar@bridge.blow.hole' -> 'foobar'</code>) exists.
|
||||
* --------------
|
||||
* Returns: whenever the MUC exists */
|
||||
extern bool MUCServerExists(MUCServer *serv, char *muc);
|
||||
|
||||
/** Destroys all memory and references from a MUC server handle.
|
||||
* --------------
|
||||
* Thrashes: CreateMUCServer
|
||||
* See-Also: https://xmpp.org/extensions/xep-0045.html */
|
||||
extern void FreeMUCServer(MUCServer *serv);
|
||||
|
||||
#endif
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
#define PARSEE_MATRIX_H
|
||||
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include "FileInfo.h"
|
||||
|
||||
|
|
@ -21,12 +20,6 @@ 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);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,10 @@ typedef struct ParseeData ParseeData;
|
|||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <Extension.h>
|
||||
#include <Command.h>
|
||||
#include <XMPP.h>
|
||||
|
||||
#include <MUCServ.h>
|
||||
|
||||
#define PARSEE_VERBOSE_NONE 0
|
||||
#define PARSEE_VERBOSE_LOG 1
|
||||
#define PARSEE_VERBOSE_TIMINGS 2
|
||||
|
|
@ -41,14 +40,9 @@ 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;
|
||||
|
|
@ -57,6 +51,7 @@ typedef struct ParseeConfig {
|
|||
/* ------- DB -------- */
|
||||
char *db_path;
|
||||
size_t db_size;
|
||||
char *extensions;
|
||||
|
||||
/* - COMMAND-LINE FLAGS - */
|
||||
int xmpp_threads, http_threads;
|
||||
|
|
@ -69,7 +64,6 @@ typedef struct ParseeData {
|
|||
|
||||
HttpServer *server;
|
||||
XMPPComponent *jabber;
|
||||
MUCServer *muc;
|
||||
|
||||
Db *db;
|
||||
char *id;
|
||||
|
|
@ -80,8 +74,10 @@ typedef struct ParseeData {
|
|||
pthread_mutex_t oidl;
|
||||
|
||||
/* If Parsee was intentionally halted */
|
||||
bool halted;
|
||||
volatile bool halted;
|
||||
pthread_mutex_t halt_lock;
|
||||
|
||||
Extensions *exts;
|
||||
} ParseeData;
|
||||
|
||||
typedef struct Argument {
|
||||
|
|
@ -101,6 +97,8 @@ 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
|
||||
|
|
@ -129,6 +127,7 @@ 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:
|
||||
|
|
@ -284,6 +283,12 @@ 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 <code>'p.'</code> prefix.
|
||||
|
|
@ -329,6 +334,14 @@ 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);
|
||||
|
||||
|
|
@ -359,7 +372,7 @@ extern int ParseeFindDatastartU(char *data);
|
|||
|
||||
|
||||
/* XMPP-ifies a message event to XEP-0393 if possible. */
|
||||
extern char * ParseeXMPPify(ParseeData *data, HashMap *event);
|
||||
extern char * ParseeXMPPify(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);
|
||||
|
|
@ -369,7 +382,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, char *filename);
|
||||
extern char * ParseeToUnauth(ParseeData *data, char *mxc);
|
||||
|
||||
extern void ParseeInitialiseOIDTable(void);
|
||||
extern void ParseePushOIDTable(char *muc, char *occupant);
|
||||
|
|
@ -400,17 +413,10 @@ extern void ParseeDestroyNickTable(void);
|
|||
* to ban them from rooms where Parsee has the ability to do so ("noflying").
|
||||
* ---------------
|
||||
* Returns: NOTHING
|
||||
* See-Also: ParseeManageBan, ParseeGlobalUnban
|
||||
* See-Also: ParseeManageBan
|
||||
* 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.
|
||||
* ---------------
|
||||
|
|
@ -503,4 +509,8 @@ 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
|
||||
|
|
|
|||
|
|
@ -27,10 +27,6 @@ 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 " \
|
||||
|
|
@ -79,8 +75,7 @@ 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("/media/(.*)/(.*)", RouteMedia) \
|
||||
X_ROUTE("/media/(.*)/(.*)/(.*)", RouteMedia)
|
||||
X_ROUTE("/_matrix/client/v1/media/download/(.*)/(.*)", RouteMedia)
|
||||
|
||||
#define X_ROUTE(path, name) extern void * name(Array *, void *);
|
||||
ROUTES
|
||||
|
|
|
|||
|
|
@ -64,12 +64,6 @@ 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.
|
||||
|
|
@ -82,10 +76,4 @@ 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
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@ typedef struct XMLEvent {
|
|||
XML_LEXER_DATA,
|
||||
XML_LEXER_ELEM, /* Empty attr */
|
||||
XML_LEXER_ENDELEM,
|
||||
|
||||
XML_ERROR,
|
||||
|
||||
XML_RELAX
|
||||
} type;
|
||||
|
|
|
|||
|
|
@ -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 *addr, char *host, int port);
|
||||
extern XMPPComponent * XMPPInitialiseCompStream(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,9 +90,6 @@ 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.
|
||||
* ----------------------------------------------------------------------
|
||||
|
|
@ -101,6 +98,16 @@ extern ssize_t XMPPGetReplyOffset(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 <Parsee.h>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
typedef struct XMPPCommandManager XMPPCommandManager;
|
||||
#ifndef PARSEE_XMPPCOMMAND_H
|
||||
#define PARSEE_XMPPCOMMAND_H
|
||||
|
||||
|
|
@ -7,11 +8,16 @@
|
|||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
typedef struct XMPPCommandManager XMPPCommandManager;
|
||||
typedef enum XMPPCommandFlags {
|
||||
XMPPCMD_ALL = 0,
|
||||
XMPPCMD_MUC , /* Only for MUCs */
|
||||
XMPPCMD_ADMINS /* Only for administrators */
|
||||
} XMPPCommandFlags;
|
||||
|
||||
typedef struct XMPPCommand XMPPCommand;
|
||||
typedef struct XMPPOption XMPPOption;
|
||||
typedef void (*XMPPCmdCallback)(XMPPCommandManager *, char *, XMLElement *, XMLElement *);
|
||||
typedef void (*XMPPOptionWriter)(XMPPCommandManager *, XMPPCommand *, char *);
|
||||
typedef void (*XMPPCmdCallback)(XMPPCommandManager *, char *, XMLElement *, XMLElement *, char *);
|
||||
typedef void (*XMPPOptionWriter)(XMPPCommandManager *, XMPPCommand *, char *, char *);
|
||||
typedef bool (*XMPPCmdFilter)(XMPPCommandManager *, char *id, XMLElement *stanza);
|
||||
|
||||
/** Creates a simple XMPP command manager, which routes commands
|
||||
|
|
@ -39,7 +45,7 @@ XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter);
|
|||
* Modifies: NOTHING
|
||||
* See-Also: XMPPRegisterCommand */
|
||||
extern XMPPCommand *
|
||||
XMPPBasicCmd(char *node, char *name, XMPPCmdCallback cb);
|
||||
XMPPBasicCmd(XMPPCommandFlags flags, char *node, char *name, XMPPCmdCallback cb);
|
||||
extern void
|
||||
XMPPCmdOptionsCreator(XMPPCommand *cmd, XMPPOptionWriter writer);
|
||||
extern void XMPPAddOption(XMPPCommand *cmd, XMPPOption *opt);
|
||||
|
|
@ -49,7 +55,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);
|
||||
extern void XMPPExecuteCommand(XMPPCommandManager *m, XMPPCommand *cmd, char *from, XMLElement *to, XMLElement *form, char *node);
|
||||
|
||||
/** Create a basic option.
|
||||
* -----------------------------------------------------
|
||||
|
|
@ -91,6 +97,12 @@ 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
|
||||
|
|
@ -111,11 +123,11 @@ extern bool XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeD
|
|||
|
||||
#define XMPP_COMMAND(f,l,n,t,s) \
|
||||
extern void \
|
||||
f(XMPPCommandManager *, char *, XMLElement *, XMLElement *); \
|
||||
f(XMPPCommandManager *, char *, XMLElement *, XMLElement *, char *); \
|
||||
/* This symbol might not exist. We do, however, not care, as
|
||||
* it is not always done. */ \
|
||||
extern void \
|
||||
Form##f(XMPPCommandManager *, XMPPCommand *, char *); \
|
||||
Form##f(XMPPCommandManager *, XMPPCommand *, char *, char *); \
|
||||
|
||||
#include "XMPPCommands.x.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,4 @@
|
|||
/* 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", {}) \
|
||||
|
|
|
|||
|
|
@ -89,30 +89,31 @@ 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!*");
|
||||
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());
|
||||
|
||||
DbClose(parsee);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* List admins */
|
||||
Log(LOG_INFO, "Admin list:");
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
ListAdmins(parsee);
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ GetDB(char *config)
|
|||
{
|
||||
ret = DbOpenLMDB(db_path, lmdb_size);
|
||||
}
|
||||
else
|
||||
if (!ret)
|
||||
{
|
||||
ret = DbOpen(db_path, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ 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;
|
||||
|
|
@ -29,7 +28,7 @@ Main(Array *args, HashMap *env)
|
|||
listen = "localhost";
|
||||
|
||||
ArgParseStateInit(&state);
|
||||
while ((flag = ArgParse(&state, args, "H:J:j:s:d:p:m:l:S:M:")) != -1)
|
||||
while ((flag = ArgParse(&state, args, "H:J:s:d:p:m:l:S:M:")) != -1)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
|
|
@ -46,9 +45,6 @@ Main(Array *args, HashMap *env)
|
|||
case 'J':
|
||||
jabber = state.optArg;
|
||||
break;
|
||||
case 'j':
|
||||
component_as = state.optArg;
|
||||
break;
|
||||
case 'd':
|
||||
data = state.optArg;
|
||||
break;
|
||||
|
|
@ -127,7 +123,6 @@ 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);
|
||||
|
|
|
|||
152
tools/plumb.c
152
tools/plumb.c
|
|
@ -1,152 +0,0 @@
|
|||
/* 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 <string.h>
|
||||
|
||||
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] <args>", 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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue