mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 18:25:10 +00:00
Compare commits
46 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e331d110c7 | ||
|
|
e617b5e8d4 | ||
|
|
e3749817d3 | ||
|
|
7b8ed08e88 | ||
|
|
45250096ad | ||
|
|
f19ff2274b | ||
|
|
7454c8c597 | ||
|
|
e21ebed134 | ||
|
|
4eaec4687e | ||
|
|
680b4261c2 | ||
|
|
44473878d0 | ||
|
|
b50b9bd615 | ||
|
|
a2c1de52dc | ||
|
|
6762d1c1ce | ||
|
|
40e3242465 | ||
|
|
b78f7b6ab3 | ||
|
|
fd1b3499b6 | ||
|
|
fb44ad4bf6 | ||
|
|
0a4aa45de5 | ||
|
|
cdbdc9345a | ||
|
|
c96f0486ff | ||
|
|
e2b014d000 | ||
|
|
d12255b226 | ||
|
|
43175e32e5 | ||
|
|
3bef6afa5d | ||
|
|
71f3836ee1 | ||
|
|
9a2d4188e2 | ||
|
|
7f396a0379 | ||
|
|
1c51d57355 | ||
|
|
176f390c4b | ||
|
|
f94db460ac | ||
|
|
1936be0078 | ||
|
|
c2536c2e84 | ||
|
|
4f694129fc | ||
|
|
110a1b695f | ||
|
|
1e7d71f9f6 | ||
|
|
b485298fbc | ||
|
|
f9de7f1750 | ||
|
|
389870c5d3 | ||
|
|
0facbaa5e5 | ||
|
|
5e1931a19f | ||
|
|
c433e31461 | ||
|
|
c365681fcb | ||
|
|
86deab29af | ||
|
|
5ddc5d3e5c | ||
|
|
2e566c73fc |
53 changed files with 1537 additions and 358 deletions
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -4,10 +4,11 @@ parsee*
|
||||||
parsee
|
parsee
|
||||||
*.swp
|
*.swp
|
||||||
.*
|
.*
|
||||||
data
|
data*
|
||||||
data/*
|
data*/*
|
||||||
Makefile
|
Makefile
|
||||||
configure
|
configure
|
||||||
|
gmon.out
|
||||||
|
|
||||||
tools/out
|
tools/out
|
||||||
tools/out/*
|
tools/out/*
|
||||||
|
|
@ -24,3 +25,8 @@ tags
|
||||||
!.forgejo
|
!.forgejo
|
||||||
!.forgejo/*
|
!.forgejo/*
|
||||||
!.forgejo/**
|
!.forgejo/**
|
||||||
|
|
||||||
|
!.guix
|
||||||
|
!.guix/*
|
||||||
|
!.guix/**
|
||||||
|
|
||||||
|
|
|
||||||
82
.guix/modules/parsee.scm
Normal file
82
.guix/modules/parsee.scm
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
(define-module (parsee)
|
||||||
|
#:use-module (guix packages)
|
||||||
|
#:use-module (guix git-download)
|
||||||
|
#:use-module (guix build-system gnu)
|
||||||
|
#:use-module (guix gexp)
|
||||||
|
#:use-module (guix utils)
|
||||||
|
#:use-module (gnu packages tls)
|
||||||
|
#:use-module (gnu packages databases)
|
||||||
|
#:use-module ((guix licenses) #:prefix license:))
|
||||||
|
|
||||||
|
(define vcs-file?
|
||||||
|
(or (git-predicate
|
||||||
|
(dirname (dirname (current-source-directory))))
|
||||||
|
(const #t)))
|
||||||
|
|
||||||
|
(define-public cytoplasm
|
||||||
|
(let ((commit "32f31fe6d61583630995d956ed7bd7566c4dc14f")
|
||||||
|
(revision "0"))
|
||||||
|
(package
|
||||||
|
(name "cytoplasm")
|
||||||
|
(version (git-version "0.4.1" revision commit))
|
||||||
|
(source
|
||||||
|
(origin
|
||||||
|
(method git-fetch)
|
||||||
|
(uri (git-reference
|
||||||
|
(url "https://git.telodendria.io/Telodendria/Cytoplasm")
|
||||||
|
(commit commit)))
|
||||||
|
(file-name (git-file-name name version))
|
||||||
|
(sha256
|
||||||
|
(base32 "09x5xfswryf3wjs1synh972yr2fmpjmffi7pjyjdzb4asqh4whrv"))))
|
||||||
|
(build-system gnu-build-system)
|
||||||
|
(arguments
|
||||||
|
(list
|
||||||
|
#:tests? #f
|
||||||
|
#:configure-flags #~'("--with-lmdb")
|
||||||
|
#:phases #~(modify-phases %standard-phases
|
||||||
|
(add-before 'configure 'add-ld-flags
|
||||||
|
(lambda _
|
||||||
|
(substitute* "./configure"
|
||||||
|
(("(LDFLAGS=\"\\$\\{LIBS\\} \\$\\{LDFLAGS\\})\"" all flags)
|
||||||
|
(string-append flags " -Wl,-rpath=" #$output "/lib\"")))
|
||||||
|
(mkdir-p (string-append #$output "/lib"))))
|
||||||
|
(replace 'configure
|
||||||
|
(lambda* (#:key configure-flags #:allow-other-keys)
|
||||||
|
(apply invoke `("./configure"
|
||||||
|
,(string-append "--prefix=" #$output)
|
||||||
|
,@configure-flags)))))))
|
||||||
|
(inputs (list openssl lmdb))
|
||||||
|
(home-page "https://git.telodendria.io/Telodendria/Cytoplasm")
|
||||||
|
(synopsis "General-purpose high-level networked C library")
|
||||||
|
(description "Cytoplasm is a general-purpose C library for creating
|
||||||
|
high-level (particularly networked and multi-threaded) C applications.")
|
||||||
|
(license license:expat))))
|
||||||
|
|
||||||
|
(define-public parsee
|
||||||
|
(package
|
||||||
|
(name "parsee")
|
||||||
|
(version "0.0.1-git")
|
||||||
|
(source
|
||||||
|
(local-file "../.." "parsee-checkout"
|
||||||
|
#:recursive? #t
|
||||||
|
#:select? vcs-file?))
|
||||||
|
(build-system gnu-build-system)
|
||||||
|
(arguments
|
||||||
|
(list
|
||||||
|
#:tests? #f
|
||||||
|
#:make-flags #~(list "CC=gcc"
|
||||||
|
(string-append "CYTO_INC=" #$cytoplasm "/include")
|
||||||
|
(string-append "CYTO_LIB=" #$cytoplasm "/lib")
|
||||||
|
(string-append "PREFIX=" #$output))
|
||||||
|
#:phases #~(modify-phases %standard-phases
|
||||||
|
(replace 'configure
|
||||||
|
(lambda* (#:key inputs #:allow-other-keys)
|
||||||
|
(let ((gcc (string-append (assoc-ref inputs "gcc") "/bin/gcc")))
|
||||||
|
(invoke gcc "configure.c" "-o" "configure")
|
||||||
|
(invoke "./configure")))))))
|
||||||
|
(home-page "https://git.kappach.at/lda/Parsee")
|
||||||
|
(synopsis "Jealous Matrix to XMPP bridge")
|
||||||
|
(description "Parsee is a Matrix-XMPP bridge written in C99 with Cytoplasm.")
|
||||||
|
(license license:agpl3+)))
|
||||||
|
|
||||||
|
parsee
|
||||||
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -13,6 +13,22 @@ commit done between releases.
|
||||||
*There is currently no beta releases of Parsee*
|
*There is currently no beta releases of Parsee*
|
||||||
|
|
||||||
## Alpha
|
## 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>
|
### v0.2.0[star-of-hope] <8/11/2024>
|
||||||
Fixes some media metadata things, replaces the build system,
|
Fixes some media metadata things, replaces the build system,
|
||||||
tries out avatar support some more, MUC contexts, and speeds
|
tries out avatar support some more, MUC contexts, and speeds
|
||||||
|
|
|
||||||
12
README.MD
12
README.MD
|
|
@ -18,12 +18,11 @@ A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solutio
|
||||||
and maybe as a testing ground for Cytoplasm features I sometimes add.
|
and maybe as a testing ground for Cytoplasm features I sometimes add.
|
||||||
|
|
||||||
(Well, I'm *trying* to do that, at least.
|
(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
|
Please scream at me if that fails(or just doesn't run on a overclocked Raspberry Pi 4B))
|
||||||
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 Matrix lol"
|
||||||
### "Why not just use XMPP lol"
|
### "Why not just use XMPP lol"
|
||||||
These two having the same answer should be enough information. Also can I *just* have fun?
|
These two having the same answer should be enough information.
|
||||||
One could also argue that both sides need to migrate(onboard) the other side, so
|
One could also argue that both sides need to migrate(onboard) the other side, so
|
||||||
a bridge may be a good way to start.
|
a bridge may be a good way to start.
|
||||||
|
|
||||||
|
|
@ -44,6 +43,13 @@ Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendri
|
||||||
Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but
|
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).
|
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
|
## RUNNING
|
||||||
First off, you may want to configure Parsee by running the `config` tool(generally named
|
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.
|
`parsee-config` in most cases), with the correct flags, like here.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
CODE=star-of-hope
|
CODE=lunar-rainbow
|
||||||
NAME=Parsee
|
NAME=Parsee
|
||||||
VERSION=0.2.0
|
VERSION=0.3.0
|
||||||
BINARY=parsee
|
BINARY=parsee
|
||||||
SOURCE=src
|
SOURCE=src
|
||||||
INCLUDES=src/include
|
INCLUDES=src/include
|
||||||
|
|
|
||||||
71
configure.c
71
configure.c
|
|
@ -90,22 +90,6 @@ string_cat(char *in1, char *in2)
|
||||||
|
|
||||||
return out;
|
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 {
|
typedef struct str_array {
|
||||||
char **values;
|
char **values;
|
||||||
|
|
@ -115,6 +99,10 @@ static str_array_t *
|
||||||
str_array_create(void)
|
str_array_create(void)
|
||||||
{
|
{
|
||||||
str_array_t *ret = malloc(sizeof(*ret));
|
str_array_t *ret = malloc(sizeof(*ret));
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ret->values = NULL;
|
ret->values = NULL;
|
||||||
ret->quantity = 0;
|
ret->quantity = 0;
|
||||||
|
|
@ -216,36 +204,6 @@ failure:
|
||||||
free(line);
|
free(line);
|
||||||
return NULL;
|
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 *
|
static char *
|
||||||
strchrn(char *s, char c)
|
strchrn(char *s, char c)
|
||||||
|
|
@ -253,17 +211,6 @@ strchrn(char *s, char c)
|
||||||
s = strchr(s, c);
|
s = strchr(s, c);
|
||||||
return s ? s+1 : NULL;
|
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 *
|
static str_array_t *
|
||||||
split(char *dir)
|
split(char *dir)
|
||||||
{
|
{
|
||||||
|
|
@ -512,8 +459,7 @@ main_build(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
FILE *makefile;
|
FILE *makefile;
|
||||||
char *repo = cmd_stdout("git remote get-url origin");
|
char *repo = cmd_stdout("git remote get-url origin");
|
||||||
size_t size, i;
|
size_t i;
|
||||||
ssize_t nread;
|
|
||||||
bool with_static = false, with_lmdb = false;
|
bool with_static = false, with_lmdb = false;
|
||||||
int opt;
|
int opt;
|
||||||
str_array_t *sources, *images, *utils, *aya;
|
str_array_t *sources, *images, *utils, *aya;
|
||||||
|
|
@ -546,6 +492,13 @@ main_build(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
makefile = fopen("Makefile", "w");
|
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, "# Autogenerated POSIX Makefile from Parsee\n");
|
||||||
fprintf(makefile, "# Ideally do not touch, unless you have a very ");
|
fprintf(makefile, "# Ideally do not touch, unless you have a very ");
|
||||||
fprintf(makefile, "good reason to do it. \n\n");
|
fprintf(makefile, "good reason to do it. \n\n");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
." The last field is the codename, by the way.
|
." The last field is the codename, by the way.
|
||||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||||
.TH parsee-config 1 "Parsee Utility" "star-of-hope"
|
.TH parsee-config 1 "Parsee Utility" "lunar-rainbow"
|
||||||
|
|
||||||
.SH NAME
|
.SH NAME
|
||||||
parsee-config - generate a basic configuration file
|
parsee-config - generate a basic configuration file
|
||||||
|
|
@ -11,6 +11,7 @@ parsee-config
|
||||||
.B [-s SHARED_SECRET]
|
.B [-s SHARED_SECRET]
|
||||||
.B [-m MEDIA_URL]
|
.B [-m MEDIA_URL]
|
||||||
.B [-J JABBER_HOST]
|
.B [-J JABBER_HOST]
|
||||||
|
.B [-j JABBER_ADDR]
|
||||||
.B [-p JABBER_PORT]
|
.B [-p JABBER_PORT]
|
||||||
.B [-d DATABASE]
|
.B [-d DATABASE]
|
||||||
.B [-M MAX_STANZA]
|
.B [-M MAX_STANZA]
|
||||||
|
|
@ -34,6 +35,7 @@ $ parsee-config \\
|
||||||
-H 'blow.hole' \\
|
-H 'blow.hole' \\
|
||||||
-s 'The Dark Shared Secret' \\
|
-s 'The Dark Shared Secret' \\
|
||||||
-J 'xmpp.blow.hole' \\
|
-J 'xmpp.blow.hole' \\
|
||||||
|
-j 'localhost' \\
|
||||||
-S 128
|
-S 128
|
||||||
.fi
|
.fi
|
||||||
.if n \{\
|
.if n \{\
|
||||||
|
|
@ -68,6 +70,12 @@ media. It must be publicly accessible (behind a reverse proxy to HTTP:7642)
|
||||||
.I JABBER_HOST
|
.I JABBER_HOST
|
||||||
is used as the component host for Parsee.
|
is used as the component host for Parsee.
|
||||||
.TP
|
.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
|
.BR -p JABBER_PORT
|
||||||
.I JABBER_PORT
|
.I JABBER_PORT
|
||||||
is used as the component post for Parsee. Parsee uses it alongside
|
is used as the component post for Parsee. Parsee uses it alongside
|
||||||
|
|
|
||||||
BIN
etc/man/man7/.parsee-bridge-guidebook.7.swp
Normal file
BIN
etc/man/man7/.parsee-bridge-guidebook.7.swp
Normal file
Binary file not shown.
BIN
etc/man/man7/.parsee-cmd-syntax.7.swp
Normal file
BIN
etc/man/man7/.parsee-cmd-syntax.7.swp
Normal file
Binary file not shown.
70
etc/man/man7/parsee-bridge-guidebook.7
Normal file
70
etc/man/man7/parsee-bridge-guidebook.7
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
." The last field is the codename, by the way.
|
||||||
|
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||||
|
.TH parsee-bridge-guidebook 7 "Parsee Utility" "star-of-hope"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
parsee-bridge-guidebook - A short guidebook on running a Parsee bridge
|
||||||
|
|
||||||
|
.SH INTRODUCTION
|
||||||
|
.P
|
||||||
|
This manpage is intended to be a guidebook for Parsee administrators. It
|
||||||
|
is meant to show how to create an instance with an XMPP-Matrix server
|
||||||
|
(though it cannot be specific, due to their ecosystem diversity), how to
|
||||||
|
plumb rooms, and moderate them through.
|
||||||
|
.P
|
||||||
|
It also assumes Parsee is properly built and installed, in which case you
|
||||||
|
are seeing this from
|
||||||
|
.I man
|
||||||
|
itself.
|
||||||
|
|
||||||
|
.SH CONVENTIONS
|
||||||
|
This page shall assume a few things that
|
||||||
|
.B must
|
||||||
|
be changed to fit your configuration. Please read those carefully, or it
|
||||||
|
will come and bite at you!
|
||||||
|
|
||||||
|
.P
|
||||||
|
First off, it assumes that you have a domain at
|
||||||
|
.I blow.hole
|
||||||
|
and that any other domains related to Parsee are it's subdomains, as
|
||||||
|
you'll see.
|
||||||
|
|
||||||
|
.P
|
||||||
|
We also assume you're planning on making the XMPP component available at
|
||||||
|
.I j.blow.hole ,
|
||||||
|
and that Parsee can reach it by using
|
||||||
|
.I localhost:1234 .
|
||||||
|
It is highly recommended that Parsee can reach the component locally, as
|
||||||
|
the stream cannot be encrypted!
|
||||||
|
|
||||||
|
.P
|
||||||
|
The Parsee HTTP server (which is used for media and the appservice) shall
|
||||||
|
be reached through
|
||||||
|
.I https://p.blow.hole/
|
||||||
|
via a reverse proxy. This manual shall only show you what port to make
|
||||||
|
available, as there are many reverse proxy options available.
|
||||||
|
|
||||||
|
.P
|
||||||
|
Finally, the Matrix server will be publicly known as
|
||||||
|
.I m.blow.hole
|
||||||
|
.
|
||||||
|
|
||||||
|
That is, if
|
||||||
|
.B bob
|
||||||
|
is in it, then they shall be known as
|
||||||
|
.I @bob:m.blow.hole .
|
||||||
|
|
||||||
|
.SH SETTING UP
|
||||||
|
Setting up Parsee mainly involves creating a valid configuration file
|
||||||
|
and the database. Most of this however is dealt with by
|
||||||
|
.I parsee-config(1)
|
||||||
|
TODO
|
||||||
|
|
||||||
|
.P
|
||||||
|
|
||||||
|
.SH LICENSE
|
||||||
|
This document is under public domain, or CC0 if not allowed by local law.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.B parsee(1), parsee-cmd-syntax(7)
|
||||||
|
|
||||||
1
guix.scm
Symbolic link
1
guix.scm
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
.guix/modules/parsee.scm
|
||||||
|
|
@ -150,7 +150,7 @@ ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = StrConcat(3, "/_matrix/media/v3/download/", uri->host, uri->path);
|
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
|
||||||
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
ASAuthenticateRequest(c, cctx);
|
ASAuthenticateRequest(c, cctx);
|
||||||
HttpRequestSendHeaders(cctx);
|
HttpRequestSendHeaders(cctx);
|
||||||
|
|
@ -195,7 +195,7 @@ ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = StrConcat(3, "/_matrix/media/v3/download/", uri->host, uri->path);
|
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
|
||||||
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
ASAuthenticateRequest(c, cctx);
|
ASAuthenticateRequest(c, cctx);
|
||||||
HttpRequestSendHeaders(cctx);
|
HttpRequestSendHeaders(cctx);
|
||||||
|
|
|
||||||
|
|
@ -25,11 +25,13 @@ ASInvite(const ParseeConfig *conf, char *id, char *invited)
|
||||||
"@", conf->sender_localpart,
|
"@", conf->sender_localpart,
|
||||||
":", conf->server_base
|
":", conf->server_base
|
||||||
);
|
);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
path = StrConcat(5,
|
path = StrConcat(5,
|
||||||
"/_matrix/client/v3/rooms/", id, "/invite",
|
"/_matrix/client/v3/rooms/", id, "/invite",
|
||||||
"?user_id=", bridge
|
"?user_id=", bridge
|
||||||
);
|
);
|
||||||
Free(bridge);
|
Free(bridge);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
ctx = ParseeCreateRequest(
|
||||||
conf,
|
conf,
|
||||||
|
|
@ -60,11 +62,13 @@ ASBan(const ParseeConfig *conf, char *id, char *banned)
|
||||||
"@", conf->sender_localpart,
|
"@", conf->sender_localpart,
|
||||||
":", conf->server_base
|
":", conf->server_base
|
||||||
);
|
);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
path = StrConcat(5,
|
path = StrConcat(5,
|
||||||
"/_matrix/client/v3/rooms/", id, "/ban",
|
"/_matrix/client/v3/rooms/", id, "/ban",
|
||||||
"?user_id=", bridge
|
"?user_id=", bridge
|
||||||
);
|
);
|
||||||
Free(bridge);
|
Free(bridge);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
ctx = ParseeCreateRequest(
|
||||||
conf,
|
conf,
|
||||||
|
|
@ -95,11 +99,13 @@ ASKick(const ParseeConfig *conf, char *id, char *banned)
|
||||||
"@", conf->sender_localpart,
|
"@", conf->sender_localpart,
|
||||||
":", conf->server_base
|
":", conf->server_base
|
||||||
);
|
);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
path = StrConcat(5,
|
path = StrConcat(5,
|
||||||
"/_matrix/client/v3/rooms/", id, "/kick",
|
"/_matrix/client/v3/rooms/", id, "/kick",
|
||||||
"?user_id=", bridge
|
"?user_id=", bridge
|
||||||
);
|
);
|
||||||
Free(bridge);
|
Free(bridge);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
ctx = ParseeCreateRequest(
|
||||||
conf,
|
conf,
|
||||||
|
|
@ -120,7 +126,8 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
{
|
{
|
||||||
HttpClientContext *ctx = NULL;
|
HttpClientContext *ctx = NULL;
|
||||||
HashMap *json = NULL;
|
HashMap *json = NULL;
|
||||||
char *path, *ret;
|
char *path, *ret, *serv;
|
||||||
|
int status;
|
||||||
if (!conf || !id)
|
if (!conf || !id)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -139,6 +146,11 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
{
|
{
|
||||||
masquerade = HttpUrlEncode(masquerade);
|
masquerade = HttpUrlEncode(masquerade);
|
||||||
}
|
}
|
||||||
|
serv = strchr(id, ':');
|
||||||
|
if (serv)
|
||||||
|
{
|
||||||
|
serv = serv + 1;
|
||||||
|
}
|
||||||
id = HttpUrlEncode(id);
|
id = HttpUrlEncode(id);
|
||||||
path = StrConcat(5,
|
path = StrConcat(5,
|
||||||
"/_matrix/client/v3/join/", id, "?",
|
"/_matrix/client/v3/join/", id, "?",
|
||||||
|
|
@ -152,7 +164,7 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
Free(path);
|
Free(path);
|
||||||
json = HashMapCreate();
|
json = HashMapCreate();
|
||||||
ASAuthenticateRequest(conf, ctx);
|
ASAuthenticateRequest(conf, ctx);
|
||||||
ParseeSetRequestJSON(ctx, json);
|
status = ParseeSetRequestJSON(ctx, json);
|
||||||
JsonFree(json);
|
JsonFree(json);
|
||||||
|
|
||||||
json = JsonDecode(HttpClientStream(ctx));
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
@ -163,6 +175,8 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
Free(masquerade);
|
Free(masquerade);
|
||||||
Free(id);
|
Free(id);
|
||||||
|
|
||||||
|
(void) serv; // TODO
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c, u
|
||||||
char *path;
|
char *path;
|
||||||
char *txn, *ret;
|
char *txn, *ret;
|
||||||
char *ts_str;
|
char *ts_str;
|
||||||
|
HttpStatus status;
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "%", ret);
|
||||||
|
}
|
||||||
HashMap *reply;
|
HashMap *reply;
|
||||||
if (!conf || !id || !type || !user || !c)
|
if (!conf || !id || !type || !user || !c)
|
||||||
{
|
{
|
||||||
|
|
@ -47,18 +52,20 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c, u
|
||||||
ts_str = TSToStr(ts);
|
ts_str = TSToStr(ts);
|
||||||
|
|
||||||
txn = StrRandom(16);
|
txn = StrRandom(16);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
path = StrConcat(11,
|
path = StrConcat(11,
|
||||||
"/_matrix/client/v3/rooms/",
|
"/_matrix/client/v3/rooms/",
|
||||||
id, "/send/", type, "/", txn, "?",
|
id, "/send/", type, "/", txn, "?",
|
||||||
"user_id=", user, "&ts=", ts_str
|
"user_id=", user, "&ts=", ts_str
|
||||||
);
|
);
|
||||||
|
Free(id);
|
||||||
Free(txn);
|
Free(txn);
|
||||||
Free(ts_str);
|
Free(ts_str);
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
Free(path);
|
Free(path);
|
||||||
ASAuthenticateRequest(conf, ctx);
|
ASAuthenticateRequest(conf, ctx);
|
||||||
ParseeSetRequestJSON(ctx, c);
|
status = ParseeSetRequestJSON(ctx, c);
|
||||||
|
|
||||||
reply = JsonDecode(HttpClientStream(ctx));
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,25 @@ CommandHead(CmdBanUser, cmd, argp)
|
||||||
|
|
||||||
BotDestroy();
|
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)
|
CommandHead(CmdNoFlyList, cmd, argp)
|
||||||
{
|
{
|
||||||
ParseeCmdArg *args = argp;
|
ParseeCmdArg *args = argp;
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ ParseeCreateRequest(const ParseeConfig *conf, HttpRequestMethod meth, char *path
|
||||||
|
|
||||||
ctx = HttpRequest(
|
ctx = HttpRequest(
|
||||||
meth,
|
meth,
|
||||||
HTTP_FLAG_TLS,
|
conf->homeserver_tls ? HTTP_FLAG_TLS : HTTP_FLAG_NONE,
|
||||||
conf->homeserver_port, conf->homeserver_host,
|
conf->homeserver_port, conf->homeserver_host,
|
||||||
path
|
path
|
||||||
);
|
);
|
||||||
|
|
|
||||||
48
src/Main.c
48
src/Main.c
|
|
@ -13,7 +13,7 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <Unistring.h>
|
#include <StanzaBuilder.h>
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
@ -63,23 +63,39 @@ ParseeCheckMatrix(void *datp)
|
||||||
{
|
{
|
||||||
static volatile uint64_t streak = 0;
|
static volatile uint64_t streak = 0;
|
||||||
ParseeData *data = datp;
|
ParseeData *data = datp;
|
||||||
if (!ASPing(data->config))
|
if (data->config->accept_pings && !ASPing(data->config))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
|
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
|
||||||
if (++streak >= 5)
|
if (++streak == 10)
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "This has been at least the fifth time in a row.");
|
DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats");
|
||||||
Log(LOG_ERR, "Please check if your homeserver is active.");
|
HashMap *json = DbJson(ref);
|
||||||
Log(LOG_ERR, "%s shall now exit...", NAME);
|
HashMap *mucs = GrabObject(json, 1, "mucs");
|
||||||
|
char *muc;
|
||||||
|
void *ignored;
|
||||||
|
|
||||||
pthread_mutex_lock(&data->halt_lock);
|
|
||||||
data->halted = true;
|
|
||||||
pthread_mutex_unlock(&data->halt_lock);
|
|
||||||
|
|
||||||
XMPPFinishCompStream(data->jabber);
|
/* Notify any potential MUCs about this */
|
||||||
pthread_join(xmpp_thr, NULL);
|
while (HashMapIterate(mucs, &muc, &ignored))
|
||||||
Log(LOG_INFO, "Stopping server...");
|
{
|
||||||
HttpServerStop(data->server);
|
char *id = StrRandom(32);
|
||||||
|
char *sender = StrConcat(3, "parsee@", data->jabber->host, "/parsee");
|
||||||
|
StanzaBuilder *b = CreateStanzaBuilder(sender, muc, id);
|
||||||
|
SetStanzaType(b, "groupchat");
|
||||||
|
SetStanzaBody(b,
|
||||||
|
"This bridge hasn't been able to reach the Matrix host, and "
|
||||||
|
"as such, some messages may not have been sent over."
|
||||||
|
);
|
||||||
|
|
||||||
|
WriteoutStanza(b, data->jabber, 0);
|
||||||
|
DestroyStanzaBuilder(b);
|
||||||
|
|
||||||
|
Free(sender);
|
||||||
|
Free(id);
|
||||||
|
}
|
||||||
|
(void) ignored;
|
||||||
|
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +124,7 @@ Main(Array *args, HashMap *env)
|
||||||
);
|
);
|
||||||
ParseePrintASCII();
|
ParseePrintASCII();
|
||||||
Log(LOG_INFO, "=======================");
|
Log(LOG_INFO, "=======================");
|
||||||
Log(LOG_INFO, "(C)opyright 2024 LDA and other contributors");
|
Log(LOG_INFO, "(C)opyright 2024-2025 LDA and other contributors");
|
||||||
Log(LOG_INFO, "(This program is free software, see LICENSE.)");
|
Log(LOG_INFO, "(This program is free software, see LICENSE.)");
|
||||||
|
|
||||||
#ifdef PLATFORM_IPHONE
|
#ifdef PLATFORM_IPHONE
|
||||||
|
|
@ -207,9 +223,11 @@ Main(Array *args, HashMap *env)
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Connecting to XMPP...");
|
Log(LOG_NOTICE, "Connecting to XMPP...");
|
||||||
jabber = XMPPInitialiseCompStream(
|
jabber = XMPPInitialiseCompStream(
|
||||||
|
parsee_conf->component_addr,
|
||||||
parsee_conf->component_host,
|
parsee_conf->component_host,
|
||||||
parsee_conf->component_port
|
parsee_conf->component_port
|
||||||
);
|
);
|
||||||
|
Log(LOG_NOTICE, "Connecting to XMPP... %p", jabber);
|
||||||
if (!XMPPAuthenticateCompStream(
|
if (!XMPPAuthenticateCompStream(
|
||||||
jabber,
|
jabber,
|
||||||
parsee_conf->shared_comp_secret
|
parsee_conf->shared_comp_secret
|
||||||
|
|
@ -274,13 +292,13 @@ Main(Array *args, HashMap *env)
|
||||||
{
|
{
|
||||||
char *parsee = ParseeMXID(conf.handlerArgs);
|
char *parsee = ParseeMXID(conf.handlerArgs);
|
||||||
|
|
||||||
/* TODO: An hardcoded avatar like this sucks. */
|
|
||||||
ASSetAvatar(parsee_conf,
|
ASSetAvatar(parsee_conf,
|
||||||
parsee,
|
parsee,
|
||||||
"mxc://tedomum.net/"
|
"mxc://tedomum.net/"
|
||||||
"7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136"
|
"7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136"
|
||||||
);
|
);
|
||||||
ASSetName(parsee_conf, parsee, "Parsee bridge");
|
ASSetName(parsee_conf, parsee, "Parsee bridge");
|
||||||
|
|
||||||
Free(parsee);
|
Free(parsee);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,13 +21,11 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char
|
||||||
char *sender = GrabString(event, 1, "sender");
|
char *sender = GrabString(event, 1, "sender");
|
||||||
|
|
||||||
Unistr *uninick = UnistrCreate(name);
|
Unistr *uninick = UnistrCreate(name);
|
||||||
Unistr *filtered = UnistrFilter(uninick, UnistrIsBMP);
|
Unistr *filtered = UnistrFilter(uninick, UnistrIsASCII); /* I'm not even going to try messing with the BMP anymore. */
|
||||||
char *nick = UnistrC(filtered);
|
char *nick = UnistrC(filtered);
|
||||||
char *rev = StrConcat(3, muc, "/", nick);
|
char *rev = StrConcat(3, muc, "/", nick);
|
||||||
int nonce = 0;
|
int nonce = 0;
|
||||||
|
|
||||||
Log(LOG_DEBUG, "MUCJOINER: filtered '%s' to '%s'", name, nick);
|
|
||||||
|
|
||||||
UnistrFree(uninick);
|
UnistrFree(uninick);
|
||||||
UnistrFree(filtered);
|
UnistrFree(filtered);
|
||||||
|
|
||||||
|
|
@ -102,7 +100,7 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||||
char *jid = ParseeEncodeMXID(state_key);
|
char *jid = ParseeEncodeMXID(state_key);
|
||||||
char *sha = NULL, *mime = NULL;
|
char *sha = NULL, *mime = NULL;
|
||||||
char *avatar = ASGetAvatar(data->config, NULL, state_key);
|
char *avatar = ASGetAvatar(data->config, NULL, state_key);
|
||||||
char *url = ParseeToUnauth(data, avatar);
|
char *url = ParseeToUnauth(data, avatar, NULL);
|
||||||
chat_id = ParseeGetFromRoomID(data, room_id);
|
chat_id = ParseeGetFromRoomID(data, room_id);
|
||||||
|
|
||||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||||
|
|
@ -257,14 +255,9 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*body != '!')
|
if (!body || *body != '!')
|
||||||
{
|
{
|
||||||
/* All commands are to be marked with a ! */
|
/* 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);
|
Free(profile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -296,10 +289,10 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
char *room_id = GrabString(event, 1, "room_id");
|
char *room_id = GrabString(event, 1, "room_id");
|
||||||
char *matrix_sender = GrabString(event, 1, "sender");
|
char *matrix_sender = GrabString(event, 1, "sender");
|
||||||
char *chat_id = NULL, *muc_id = NULL;
|
char *chat_id = NULL, *muc_id = NULL;
|
||||||
char *user;
|
char *user = NULL;
|
||||||
|
|
||||||
DbRef *room_data;
|
DbRef *room_data = NULL;
|
||||||
HashMap *data_json;
|
HashMap *data_json = NULL;
|
||||||
|
|
||||||
bool direct = false;
|
bool direct = false;
|
||||||
if (!data || !event || !from || !to)
|
if (!data || !event || !from || !to)
|
||||||
|
|
@ -331,8 +324,8 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *matrix_name, *matrix_avatar;
|
char *matrix_name = NULL, *matrix_avatar = NULL;
|
||||||
char *mime, *sha;
|
char *mime = NULL, *sha = NULL;
|
||||||
|
|
||||||
muc_id = ParseeGetMUCID(data, chat_id);
|
muc_id = ParseeGetMUCID(data, chat_id);
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
|
|
@ -365,28 +358,47 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
static void
|
static void
|
||||||
ParseeMessageHandler(ParseeData *data, HashMap *event)
|
ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
{
|
{
|
||||||
|
if (!data || !event)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
XMPPComponent *jabber = data->jabber;
|
XMPPComponent *jabber = data->jabber;
|
||||||
StanzaBuilder *builder;
|
StanzaBuilder *builder = NULL;
|
||||||
DbRef *ref = NULL;
|
DbRef *ref = NULL;
|
||||||
HashMap *json;
|
HashMap *json = NULL;
|
||||||
|
|
||||||
|
char *msgtype = GrabString(event, 2, "content", "msgtype");
|
||||||
|
char *m_sender = GrabString(event, 1, "sender");
|
||||||
|
char *unedited_id = NULL;
|
||||||
char *body = GrabString(event, 2, "content", "body");
|
char *body = GrabString(event, 2, "content", "body");
|
||||||
char *id = GrabString(event, 1, "room_id");
|
char *id = GrabString(event, 1, "room_id");
|
||||||
char *ev_id = GrabString(event, 1, "event_id");
|
char *ev_id = GrabString(event, 1, "event_id");
|
||||||
char *m_sender = GrabString(event, 1, "sender");
|
char *chat_id = NULL, *muc_id = NULL;
|
||||||
char *chat_id, *muc_id;
|
|
||||||
char *reply_id = MatrixGetReply(event);
|
char *reply_id = MatrixGetReply(event);
|
||||||
char *xepd = ParseeXMPPify(event);
|
char *xepd = ParseeXMPPify(data, event);
|
||||||
char *type, *user, *xmppified_user = NULL, *to = NULL;
|
char *type, *user, *xmppified_user = NULL, *to = NULL;
|
||||||
char *unauth = NULL;
|
char *unauth = NULL;
|
||||||
char *origin_id = NULL, *stanza = NULL;
|
char *origin_id = NULL, *stanza = NULL;
|
||||||
char *sender = NULL;
|
char *sender = NULL;
|
||||||
char *unedited_id = MatrixGetEdit(event);
|
|
||||||
char *url = GrabString(event, 2, "content", "url");
|
char *url = GrabString(event, 2, "content", "url");
|
||||||
char *encoded_from = NULL;
|
char *encoded_from = NULL;
|
||||||
|
|
||||||
bool direct = false;
|
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) ||
|
if (ParseeIsPuppet(data->config, m_sender) ||
|
||||||
ParseeManageBan(data, m_sender, id))
|
ParseeManageBan(data, m_sender, id))
|
||||||
{
|
{
|
||||||
|
|
@ -417,7 +429,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
|
|
||||||
type = direct ? "chat" : "groupchat";
|
type = direct ? "chat" : "groupchat";
|
||||||
user = GrabString(json, 1, "xmpp_user");
|
user = GrabString(json, 1, "xmpp_user");
|
||||||
unauth = ParseeToUnauth(data, url);
|
unauth = ParseeToUnauth(data, url, GrabString(event, 2, "content", "filename"));
|
||||||
|
|
||||||
encoded_from = ParseeEncodeMXID(m_sender);
|
encoded_from = ParseeEncodeMXID(m_sender);
|
||||||
xmppified_user = StrConcat(3,
|
xmppified_user = StrConcat(3,
|
||||||
|
|
@ -454,6 +466,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
Free(name);
|
Free(name);
|
||||||
Free(avatar);
|
Free(avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply_id)
|
if (reply_id)
|
||||||
{
|
{
|
||||||
/* TODO: Monocles chat DM users HATE this trick!
|
/* TODO: Monocles chat DM users HATE this trick!
|
||||||
|
|
@ -510,8 +523,8 @@ end:
|
||||||
Free(stanza);
|
Free(stanza);
|
||||||
Free(sender);
|
Free(sender);
|
||||||
Free(unauth);
|
Free(unauth);
|
||||||
Free(unedited_id);
|
|
||||||
Free(encoded_from);
|
Free(encoded_from);
|
||||||
|
Free(unedited_id);
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
ref = NULL;
|
ref = NULL;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
|
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Http.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
|
@ -32,3 +35,34 @@ MatrixParseID(char *user)
|
||||||
|
|
||||||
return ret;
|
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,6 +36,12 @@ ParseeConfigLoad(char *conf)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
json = JsonDecode(stream);
|
json = JsonDecode(stream);
|
||||||
|
if (!json)
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "Could not parse config JSON");
|
||||||
|
StreamClose(stream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
config = Malloc(sizeof(*config));
|
config = Malloc(sizeof(*config));
|
||||||
#define CopyToStr(to, str) config->to = StrDuplicate( \
|
#define CopyToStr(to, str) config->to = StrDuplicate( \
|
||||||
|
|
@ -44,6 +50,9 @@ ParseeConfigLoad(char *conf)
|
||||||
#define CopyToInt(to, str) config->to = (int) ( \
|
#define CopyToInt(to, str) config->to = (int) ( \
|
||||||
JsonValueAsInteger(HashMapGet(json, str)) \
|
JsonValueAsInteger(HashMapGet(json, str)) \
|
||||||
)
|
)
|
||||||
|
#define CopyToBool(to, str) config->to = (int) ( \
|
||||||
|
JsonValueAsBoolean(HashMapGet(json, str)) \
|
||||||
|
)
|
||||||
|
|
||||||
config->http_threads = 8;
|
config->http_threads = 8;
|
||||||
config->xmpp_threads = 8;
|
config->xmpp_threads = 8;
|
||||||
|
|
@ -58,8 +67,15 @@ ParseeConfigLoad(char *conf)
|
||||||
CopyToStr(server_base, "hs_base");
|
CopyToStr(server_base, "hs_base");
|
||||||
CopyToStr(homeserver_host, "hs_host");
|
CopyToStr(homeserver_host, "hs_host");
|
||||||
CopyToInt(homeserver_port, "hs_port");
|
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");
|
CopyToInt(component_port, "component_port");
|
||||||
|
CopyToStr(component_addr, "component_addr");
|
||||||
CopyToStr(component_host, "component_host");
|
CopyToStr(component_host, "component_host");
|
||||||
CopyToStr(shared_comp_secret, "shared_secret");
|
CopyToStr(shared_comp_secret, "shared_secret");
|
||||||
CopyToInt(max_stanza_size, "max_stanza_size");
|
CopyToInt(max_stanza_size, "max_stanza_size");
|
||||||
|
|
@ -69,6 +85,8 @@ ParseeConfigLoad(char *conf)
|
||||||
config->max_stanza_size = 10000;
|
config->max_stanza_size = 10000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CopyToBool(ignore_bots, "ignore_bots");
|
||||||
|
|
||||||
CopyToStr(media_base, "media_base");
|
CopyToStr(media_base, "media_base");
|
||||||
|
|
||||||
CopyToStr(db_path, "db");
|
CopyToStr(db_path, "db");
|
||||||
|
|
@ -129,6 +147,7 @@ ParseeConfigFree(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Free(config->component_host);
|
Free(config->component_host);
|
||||||
|
Free(config->component_addr);
|
||||||
Free(config->shared_comp_secret);
|
Free(config->shared_comp_secret);
|
||||||
Free(config->db_path);
|
Free(config->db_path);
|
||||||
Free(config->homeserver_host);
|
Free(config->homeserver_host);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ ParseeInitData(XMPPComponent *comp)
|
||||||
data->config = ParseeConfigGet();
|
data->config = ParseeConfigGet();
|
||||||
data->router = HttpRouterCreate();
|
data->router = HttpRouterCreate();
|
||||||
data->jabber = comp;
|
data->jabber = comp;
|
||||||
|
data->muc = CreateMUCServer(data);
|
||||||
data->handler = CommandCreateRouter();
|
data->handler = CommandCreateRouter();
|
||||||
|
|
||||||
data->oid_servers = HashMapCreate();
|
data->oid_servers = HashMapCreate();
|
||||||
|
|
@ -116,6 +117,7 @@ ParseeFreeData(ParseeData *data)
|
||||||
pthread_mutex_destroy(&data->halt_lock);
|
pthread_mutex_destroy(&data->halt_lock);
|
||||||
Free(data->id);
|
Free(data->id);
|
||||||
XMPPEndCompStream(data->jabber);
|
XMPPEndCompStream(data->jabber);
|
||||||
|
FreeMUCServer(data->muc);
|
||||||
DbClose(data->db);
|
DbClose(data->db);
|
||||||
HttpRouterFree(data->router);
|
HttpRouterFree(data->router);
|
||||||
CommandFreeRouter(data->handler);
|
CommandFreeRouter(data->handler);
|
||||||
|
|
@ -130,7 +132,6 @@ ParseeCleanup(void *datp)
|
||||||
char *chat;
|
char *chat;
|
||||||
size_t i;
|
size_t i;
|
||||||
uint64_t ts = UtilTsMillis();
|
uint64_t ts = UtilTsMillis();
|
||||||
size_t entries = 0;
|
|
||||||
|
|
||||||
chats = DbList(data->db, 1, "chats");
|
chats = DbList(data->db, 1, "chats");
|
||||||
|
|
||||||
|
|
@ -174,7 +175,6 @@ ParseeCleanup(void *datp)
|
||||||
if (cleaned > threshold) \
|
if (cleaned > threshold) \
|
||||||
{ \
|
{ \
|
||||||
DbDelete(data->db, 4, "chats", chat, #field"s", field); \
|
DbDelete(data->db, 4, "chats", chat, #field"s", field); \
|
||||||
entries++; \
|
|
||||||
} \
|
} \
|
||||||
Free(field); \
|
Free(field); \
|
||||||
} \
|
} \
|
||||||
|
|
@ -231,7 +231,6 @@ ParseeCleanup(void *datp)
|
||||||
if (cleaned > threshold) \
|
if (cleaned > threshold) \
|
||||||
{ \
|
{ \
|
||||||
JsonValueFree(HashMapDelete(field##s, field)); \
|
JsonValueFree(HashMapDelete(field##s, field)); \
|
||||||
entries++; \
|
|
||||||
} \
|
} \
|
||||||
Free(field); \
|
Free(field); \
|
||||||
} \
|
} \
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
|
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Http.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
@ -538,7 +539,6 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ParseeSendPresence(ParseeData *data)
|
ParseeSendPresence(ParseeData *data)
|
||||||
{
|
{
|
||||||
|
|
@ -562,7 +562,7 @@ ParseeSendPresence(ParseeData *data)
|
||||||
uint64_t ts = GrabInteger(DbJson(chat), 1, "ts");
|
uint64_t ts = GrabInteger(DbJson(chat), 1, "ts");
|
||||||
int diff = ts ? (int) ((UtilTsMillis() - ts) / 1000) : -1;
|
int diff = ts ? (int) ((UtilTsMillis() - ts) / 1000) : -1;
|
||||||
/* Make a fake user join the MUC */
|
/* Make a fake user join the MUC */
|
||||||
Log(LOG_NOTICE, "Sending presence to %s last=%ds", rev, diff);
|
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
||||||
XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false);
|
XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false);
|
||||||
|
|
||||||
DbUnlock(data->db, chat);
|
DbUnlock(data->db, chat);
|
||||||
|
|
@ -688,12 +688,13 @@ end:
|
||||||
|
|
||||||
#include <Cytoplasm/Uri.h>
|
#include <Cytoplasm/Uri.h>
|
||||||
char *
|
char *
|
||||||
ParseeToUnauth(ParseeData *data, char *mxc)
|
ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
||||||
{
|
{
|
||||||
Uri *url = NULL;
|
Uri *url = NULL;
|
||||||
char *ret;
|
char *ret;
|
||||||
char *key, *hmac;
|
char *key, *hmac;
|
||||||
#define PAT "%s/_matrix/client/v1/media/download/%s%s?hmac=%s"
|
#define PAT "%s/media/%s%s?hmac=%s"
|
||||||
|
#define PATF "%s/media/%s%s/%s?hmac=%s"
|
||||||
size_t l;
|
size_t l;
|
||||||
if (!data || !mxc)
|
if (!data || !mxc)
|
||||||
{
|
{
|
||||||
|
|
@ -714,19 +715,45 @@ ParseeToUnauth(ParseeData *data, char *mxc)
|
||||||
hmac = ParseeHMACS(data->id, key);
|
hmac = ParseeHMACS(data->id, key);
|
||||||
Free(key);
|
Free(key);
|
||||||
|
|
||||||
|
if (!filename)
|
||||||
|
{
|
||||||
l = snprintf(NULL, 0,
|
l = snprintf(NULL, 0,
|
||||||
PAT,
|
PAT,
|
||||||
data->config->media_base,
|
data->config->media_base,
|
||||||
url->host, url->path,
|
url->host, url->path,
|
||||||
hmac
|
hmac
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *encoded = HttpUrlEncode(filename);
|
||||||
|
l = snprintf(NULL, 0,
|
||||||
|
PATF,
|
||||||
|
data->config->media_base,
|
||||||
|
url->host, url->path, encoded,
|
||||||
|
hmac
|
||||||
|
);
|
||||||
|
Free(encoded);
|
||||||
|
}
|
||||||
ret = Malloc(l + 3);
|
ret = Malloc(l + 3);
|
||||||
|
if (!filename)
|
||||||
|
{
|
||||||
snprintf(ret, l + 1,
|
snprintf(ret, l + 1,
|
||||||
PAT,
|
PAT,
|
||||||
data->config->media_base,
|
data->config->media_base,
|
||||||
url->host, url->path,
|
url->host, url->path,
|
||||||
hmac
|
hmac
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(ret, l + 1,
|
||||||
|
PATF,
|
||||||
|
data->config->media_base,
|
||||||
|
url->host, url->path, filename,
|
||||||
|
hmac
|
||||||
|
);
|
||||||
|
}
|
||||||
UriFree(url);
|
UriFree(url);
|
||||||
Free(hmac);
|
Free(hmac);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@
|
||||||
#include <Cytoplasm/Http.h>
|
#include <Cytoplasm/Http.h>
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <StringStream.h>
|
#include <StringStream.h>
|
||||||
|
#include <Matrix.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
|
|
@ -17,7 +20,7 @@ typedef struct XMPPFlags {
|
||||||
bool quote;
|
bool quote;
|
||||||
} XMPPFlags;
|
} XMPPFlags;
|
||||||
static char *
|
static char *
|
||||||
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
{
|
{
|
||||||
char *xepd = NULL, *tmp = NULL;
|
char *xepd = NULL, *tmp = NULL;
|
||||||
|
|
||||||
|
|
@ -55,7 +58,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
switch (elem->type)
|
switch (elem ? elem->type : -1)
|
||||||
{
|
{
|
||||||
case XML_ELEMENT_DATA:
|
case XML_ELEMENT_DATA:
|
||||||
Concat(elem->data);
|
Concat(elem->data);
|
||||||
|
|
@ -67,7 +70,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
{
|
{
|
||||||
child = ArrayGet(elem->children, i);
|
child = ArrayGet(elem->children, i);
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
Concat(subxep);
|
Concat(subxep);
|
||||||
Free(subxep);
|
Free(subxep);
|
||||||
|
|
@ -80,7 +83,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
{
|
{
|
||||||
child = ArrayGet(elem->children, i);
|
child = ArrayGet(elem->children, i);
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
Concat(subxep);
|
Concat(subxep);
|
||||||
Free(subxep);
|
Free(subxep);
|
||||||
|
|
@ -93,7 +96,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
{
|
{
|
||||||
child = ArrayGet(elem->children, i);
|
child = ArrayGet(elem->children, i);
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
Concat(subxep);
|
Concat(subxep);
|
||||||
Free(subxep);
|
Free(subxep);
|
||||||
|
|
@ -126,7 +129,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
{
|
{
|
||||||
child = ArrayGet(elem->children, i);
|
child = ArrayGet(elem->children, i);
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
Concat(subxep);
|
Concat(subxep);
|
||||||
Free(subxep);
|
Free(subxep);
|
||||||
|
|
@ -141,37 +144,70 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
{
|
{
|
||||||
child = ArrayGet(elem->children, i);
|
child = ArrayGet(elem->children, i);
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
Concat(subxep);
|
Concat(subxep);
|
||||||
Free(subxep);
|
Free(subxep);
|
||||||
}
|
}
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
Concat("\n");
|
Concat("\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (StrEquals(elem->name, "a"))
|
else if (StrEquals(elem->name, "a"))
|
||||||
{
|
{
|
||||||
char *href = HashMapGet(elem->attrs, "href");
|
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
|
/* TODO: Check if the element here is a Matrix.TO
|
||||||
* pointing to a Parsee user. */
|
* pointing to a Parsee user. */
|
||||||
Concat("(");
|
UserID *id = MatrixParseIDFromMTO(pref);
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
if (id)
|
||||||
{
|
{
|
||||||
child = ArrayGet(elem->children, i);
|
char *real_id = StrConcat(4, "@", id->localpart, ":", id->server);
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
/* TODO: Detect if it already is a Parsee user */
|
||||||
|
if (ParseeIsPuppet(conf, real_id))
|
||||||
Concat(subxep);
|
{
|
||||||
Free(subxep);
|
char *name = ASGetName(conf, NULL, real_id);
|
||||||
|
Concat((name ? name : real_id));
|
||||||
|
Free(name);
|
||||||
}
|
}
|
||||||
Concat(" points to ");
|
else
|
||||||
|
{
|
||||||
|
Concat(real_id);
|
||||||
|
}
|
||||||
|
Free(real_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Concat(href);
|
Concat(href);
|
||||||
Concat(" )");
|
}
|
||||||
|
|
||||||
|
Free(id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
{
|
{
|
||||||
child = ArrayGet(elem->children, i);
|
child = ArrayGet(elem->children, i);
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat(" < ");
|
||||||
|
Concat(href);
|
||||||
|
Concat(" >");
|
||||||
|
}
|
||||||
|
UriFree(pref);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
Concat(subxep);
|
Concat(subxep);
|
||||||
Free(subxep);
|
Free(subxep);
|
||||||
|
|
@ -183,8 +219,45 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
}
|
}
|
||||||
return xepd;
|
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 *
|
char *
|
||||||
ParseeXMPPify(HashMap *event)
|
ParseeXMPPify(ParseeData *data, HashMap *event)
|
||||||
{
|
{
|
||||||
char *type, *format, *html;
|
char *type, *format, *html;
|
||||||
char *xepd = NULL;
|
char *xepd = NULL;
|
||||||
|
|
@ -203,20 +276,28 @@ ParseeXMPPify(HashMap *event)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
|
if (!StrEquals(GetBodyFormat(event), "org.matrix.custom.html"))
|
||||||
if (!StrEquals(format, "org.matrix.custom.html"))
|
|
||||||
{
|
{
|
||||||
/* Settle for the raw body instead. */
|
/* Settle for the raw body instead. */
|
||||||
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
|
char *body = GetRawBody(event);
|
||||||
return StrDuplicate(body);
|
return StrDuplicate(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
|
html = GetHTMLBody(event);
|
||||||
|
|
||||||
html = StrConcat(3, "<html>", html, "</html>");
|
html = StrConcat(3, "<html>", html, "</html>");
|
||||||
elem = XMLCDecode(StrStreamReader(html), true, true);
|
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;
|
flags.quote = false;
|
||||||
xepd = XMPPifyElement(event, elem, flags);
|
xepd = XMPPifyElement(data ? data->config : NULL, event, elem, flags);
|
||||||
|
|
||||||
XMLFreeElement(elem);
|
XMLFreeElement(elem);
|
||||||
Free(html);
|
Free(html);
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,28 @@
|
||||||
#include <Glob.h>
|
#include <Glob.h>
|
||||||
#include <AS.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
|
void
|
||||||
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,37 @@
|
||||||
|
|
||||||
#include <string.h>
|
#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)
|
RouteHead(RouteMedia, arr, argp)
|
||||||
{
|
{
|
||||||
ParseeHttpArg *args = argp;
|
ParseeHttpArg *args = argp;
|
||||||
|
|
@ -17,7 +48,7 @@ RouteHead(RouteMedia, arr, argp)
|
||||||
HashMap *reqh, *params;
|
HashMap *reqh, *params;
|
||||||
char *server = ArrayGet(arr, 0);
|
char *server = ArrayGet(arr, 0);
|
||||||
char *identi = ArrayGet(arr, 1);
|
char *identi = ArrayGet(arr, 1);
|
||||||
char *path, *key, *val;
|
char *key, *val;
|
||||||
char *hmac, *chkmak = NULL;
|
char *hmac, *chkmak = NULL;
|
||||||
|
|
||||||
params = HttpRequestParams(args->ctx);
|
params = HttpRequestParams(args->ctx);
|
||||||
|
|
@ -44,15 +75,7 @@ RouteHead(RouteMedia, arr, argp)
|
||||||
|
|
||||||
/* Proxy the media through an authenticated endpoint if the HMAC
|
/* Proxy the media through an authenticated endpoint if the HMAC
|
||||||
* is valid. */
|
* is valid. */
|
||||||
server = HttpUrlEncode(server);
|
cctx = TryDownload(args->data, server, identi);
|
||||||
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);
|
reqh = HttpResponseHeaders(cctx);
|
||||||
while (HashMapIterate(reqh, &key, (void **) &val))
|
while (HashMapIterate(reqh, &key, (void **) &val))
|
||||||
{
|
{
|
||||||
|
|
@ -65,8 +88,6 @@ RouteHead(RouteMedia, arr, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClientContextFree(cctx);
|
HttpClientContextFree(cctx);
|
||||||
Free(server);
|
|
||||||
Free(identi);
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@ RouteHead(RoutePing, arr, argp)
|
||||||
ParseeHttpArg *args = argp;
|
ParseeHttpArg *args = argp;
|
||||||
HashMap *request = NULL;
|
HashMap *request = NULL;
|
||||||
HashMap *response = NULL;
|
HashMap *response = NULL;
|
||||||
Array *events;
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
response = ASVerifyRequest(args);
|
response = ASVerifyRequest(args);
|
||||||
if (response)
|
if (response)
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,6 @@ SignalHandler(int signal)
|
||||||
HttpServerStop(data->server);
|
HttpServerStop(data->server);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (signal == SIGPIPE)
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "Caught a SIGPIPE...");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -46,7 +41,7 @@ ParseeInitialiseSignals(ParseeData *d, pthread_t xmpp)
|
||||||
sa.sa_flags = SA_RESTART;
|
sa.sa_flags = SA_RESTART;
|
||||||
|
|
||||||
#define Register(act) (sigaction(act, &sa, NULL) >= 0)
|
#define Register(act) (sigaction(act, &sa, NULL) >= 0)
|
||||||
if (!Register(SIGTERM) || !Register(SIGINT) || !Register(SIGPIPE))
|
if (!Register(SIGTERM) || !Register(SIGINT))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Couldn't register signals...");
|
Log(LOG_ERR, "Couldn't register signals...");
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
45
src/Unistr.c
45
src/Unistr.c
|
|
@ -192,6 +192,16 @@ UnistrGetch(Unistr *unistr, size_t i)
|
||||||
return i < unistr->length ? unistr->codepoints[i] : 0;
|
return i < unistr->length ? unistr->codepoints[i] : 0;
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
|
UnistrIsASCII(uint32_t u)
|
||||||
|
{
|
||||||
|
if (u == 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u < 0x7F;
|
||||||
|
}
|
||||||
|
bool
|
||||||
UnistrIsBMP(uint32_t u)
|
UnistrIsBMP(uint32_t u)
|
||||||
{
|
{
|
||||||
if (u == 0)
|
if (u == 0)
|
||||||
|
|
@ -267,3 +277,38 @@ UnistrGetOffset(Unistr *str, uint32_t sep)
|
||||||
}
|
}
|
||||||
return 0;
|
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;
|
break;
|
||||||
case XEP393_MONO:
|
case XEP393_MONO:
|
||||||
head = XMLCreateTag("code");
|
head = XMLCreateTag("code");
|
||||||
|
XMLAddChild(xmlparent, XMLCreateText("`"));
|
||||||
XMLAddChild(xmlparent, head);
|
XMLAddChild(xmlparent, head);
|
||||||
XMLAddChild(head, XMLCreateText("`"));
|
|
||||||
break;
|
break;
|
||||||
case XEP393_SRKE:
|
case XEP393_SRKE:
|
||||||
head = XMLCreateTag("s");
|
head = XMLCreateTag("s");
|
||||||
|
|
@ -372,7 +372,7 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
||||||
XMLAddChild(head, XMLCreateText("_"));
|
XMLAddChild(head, XMLCreateText("_"));
|
||||||
break;
|
break;
|
||||||
case XEP393_MONO:
|
case XEP393_MONO:
|
||||||
XMLAddChild(head, XMLCreateText("`"));
|
XMLAddChild(xmlparent, XMLCreateText("`"));
|
||||||
break;
|
break;
|
||||||
case XEP393_SRKE:
|
case XEP393_SRKE:
|
||||||
XMLAddChild(head, XMLCreateText("~"));
|
XMLAddChild(head, XMLCreateText("~"));
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,12 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
switch (event->type)
|
switch (event->type)
|
||||||
{
|
{
|
||||||
|
case XML_ERROR:
|
||||||
|
XMLFreeEvent(event);
|
||||||
|
XMLFreeElement(ret);
|
||||||
|
ArrayFree(stack);
|
||||||
|
XMLFreeLexer(lexer);
|
||||||
|
return NULL;
|
||||||
case XML_LEXER_STARTELEM:
|
case XML_LEXER_STARTELEM:
|
||||||
/* Create a new element that will populated. */
|
/* Create a new element that will populated. */
|
||||||
top = XMLCreateTag(event->element);
|
top = XMLCreateTag(event->element);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ static char * XMLPopElement(XMLexer *lexer);
|
||||||
static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs);
|
static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs);
|
||||||
static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs);
|
static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs);
|
||||||
static XMLEvent * XMLCreateRelax(XMLexer *lexer);
|
static XMLEvent * XMLCreateRelax(XMLexer *lexer);
|
||||||
|
static XMLEvent * XMLCreateError(XMLexer *lexer);
|
||||||
static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end);
|
static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end);
|
||||||
static XMLEvent * XMLCreateData(XMLexer *lexer);
|
static XMLEvent * XMLCreateData(XMLexer *lexer);
|
||||||
|
|
||||||
|
|
@ -198,7 +199,9 @@ XMLCrank(XMLexer *lexer)
|
||||||
else if (XMLookahead(lexer, "--", false))
|
else if (XMLookahead(lexer, "--", false))
|
||||||
{
|
{
|
||||||
/* Throw error */
|
/* Throw error */
|
||||||
return NULL;
|
XMLFreeEvent(event);
|
||||||
|
event = XMLCreateError(lexer);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XML_STATE_PI:
|
case XML_STATE_PI:
|
||||||
|
|
@ -215,6 +218,9 @@ XMLCrank(XMLexer *lexer)
|
||||||
if (!attrname)
|
if (!attrname)
|
||||||
{
|
{
|
||||||
/* TODO: Throw error */
|
/* TODO: Throw error */
|
||||||
|
XMLFreeEvent(event);
|
||||||
|
event = XMLCreateError(lexer);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
XMLPushElement(lexer, attrname);
|
XMLPushElement(lexer, attrname);
|
||||||
|
|
||||||
|
|
@ -241,7 +247,10 @@ XMLCrank(XMLexer *lexer)
|
||||||
}
|
}
|
||||||
else if (XMLookahead(lexer, "'", true))
|
else if (XMLookahead(lexer, "'", true))
|
||||||
{
|
{
|
||||||
while (true);
|
//while (true); uh oh
|
||||||
|
XMLFreeEvent(event);
|
||||||
|
event = XMLCreateError(lexer);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XML_STATE_ATTRTAIL:
|
case XML_STATE_ATTRTAIL:
|
||||||
|
|
@ -250,6 +259,8 @@ XMLCrank(XMLexer *lexer)
|
||||||
if (!XMLookahead(lexer, ">", true))
|
if (!XMLookahead(lexer, ">", true))
|
||||||
{
|
{
|
||||||
/* TODO: Throw error. */
|
/* TODO: Throw error. */
|
||||||
|
XMLFreeEvent(event);
|
||||||
|
event = XMLCreateError(lexer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
lexer->state = XML_STATE_NONE;
|
lexer->state = XML_STATE_NONE;
|
||||||
|
|
@ -258,6 +269,8 @@ XMLCrank(XMLexer *lexer)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
XMLFreeEvent(event);
|
||||||
|
event = XMLCreateError(lexer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* TODO: Crank our XML parser. */
|
/* TODO: Crank our XML parser. */
|
||||||
|
|
@ -693,6 +706,26 @@ XMLCreateData(XMLexer *lexer)
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
static XMLEvent *
|
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)
|
XMLCreateRelax(XMLexer *lexer)
|
||||||
{
|
{
|
||||||
XMLEvent *event = Malloc(sizeof(*event));
|
XMLEvent *event = Malloc(sizeof(*event));
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
#define DEFAULT_PROSODY_PORT 5347
|
#define DEFAULT_PROSODY_PORT 5347
|
||||||
|
|
||||||
XMPPComponent *
|
XMPPComponent *
|
||||||
XMPPInitialiseCompStream(char *host, int port)
|
XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||||
{
|
{
|
||||||
int sd = -1;
|
int sd = -1;
|
||||||
struct addrinfo hints, *res, *res0;
|
struct addrinfo hints, *res, *res0;
|
||||||
|
|
@ -28,12 +28,17 @@ XMPPInitialiseCompStream(char *host, int port)
|
||||||
Stream *stream;
|
Stream *stream;
|
||||||
XMPPComponent *comp;
|
XMPPComponent *comp;
|
||||||
|
|
||||||
|
if (!addr)
|
||||||
|
{
|
||||||
|
addr = host;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT);
|
snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT);
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
error = getaddrinfo(host, serv, &hints, &res0);
|
error = getaddrinfo(addr, serv, &hints, &res0);
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
const char *error_str = gai_strerror(error);
|
const char *error_str = gai_strerror(error);
|
||||||
|
|
@ -56,6 +61,10 @@ XMPPInitialiseCompStream(char *host, int port)
|
||||||
|
|
||||||
if (connect(sd, res->ai_addr, res->ai_addrlen) < 0)
|
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);
|
close(sd);
|
||||||
sd = -1;
|
sd = -1;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -66,6 +75,10 @@ XMPPInitialiseCompStream(char *host, int port)
|
||||||
|
|
||||||
if (sd < 0)
|
if (sd < 0)
|
||||||
{
|
{
|
||||||
|
Log(LOG_ERR,
|
||||||
|
"%s: cannot connect to '%s': no socket available", __func__,
|
||||||
|
host
|
||||||
|
);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
freeaddrinfo(res0);
|
freeaddrinfo(res0);
|
||||||
|
|
@ -73,6 +86,10 @@ XMPPInitialiseCompStream(char *host, int port)
|
||||||
stream = StreamFd(sd);
|
stream = StreamFd(sd);
|
||||||
if (!stream)
|
if (!stream)
|
||||||
{
|
{
|
||||||
|
Log(LOG_ERR,
|
||||||
|
"%s: cannot connect to '%s': %s", __func__,
|
||||||
|
host, "couldn't create a Cytoplasm stream"
|
||||||
|
);
|
||||||
close(sd);
|
close(sd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -165,6 +182,49 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Excepted empty handshake reply, got nonsense.");
|
Log(LOG_ERR, "Excepted empty handshake reply, got nonsense.");
|
||||||
Log(LOG_ERR, "Another service (possibly Parsee) may have taken over.");
|
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, "");
|
||||||
Log(LOG_ERR, "Simply jealous of that other service...");
|
Log(LOG_ERR, "Simply jealous of that other service...");
|
||||||
Free(stream_id);
|
Free(stream_id);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
||||||
|
#include "XMPPThread/internal.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
{
|
{
|
||||||
|
|
@ -42,6 +44,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
Free(uuid);
|
Free(uuid);
|
||||||
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
||||||
{
|
{
|
||||||
|
Log(LOG_ERR, "Didn't receive an <iq> stanza");
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +56,11 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
"conference"))
|
"conference"))
|
||||||
{
|
{
|
||||||
XMLFreeElement(iq_query);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,6 +170,7 @@ bool
|
||||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret)
|
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret)
|
||||||
{
|
{
|
||||||
XMLElement *presence, *x, *reply, *history, *photo;
|
XMLElement *presence, *x, *reply, *history, *photo;
|
||||||
|
IQFeatures *features;
|
||||||
char *from, *id, *stime = "3600";
|
char *from, *id, *stime = "3600";
|
||||||
if (!comp || !fr || !muc)
|
if (!comp || !fr || !muc)
|
||||||
{
|
{
|
||||||
|
|
@ -189,7 +198,15 @@ XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool
|
||||||
XMLAddChild(x, history);
|
XMLAddChild(x, history);
|
||||||
|
|
||||||
XMLAddChild(presence, x);
|
XMLAddChild(presence, x);
|
||||||
XMPPAnnotatePresence(presence);
|
features = LookupJIDFeatures(from);
|
||||||
|
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||||
|
IQ_IDENTITY
|
||||||
|
#undef IdentitySimple
|
||||||
|
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||||
|
IQ_ADVERT
|
||||||
|
#undef AdvertiseSimple
|
||||||
|
XMPPAnnotatePresence(presence, features);
|
||||||
|
FreeIQFeatures(features);
|
||||||
|
|
||||||
if (hash)
|
if (hash)
|
||||||
{
|
{
|
||||||
|
|
@ -225,6 +242,7 @@ void
|
||||||
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||||
{
|
{
|
||||||
XMLElement *presence;
|
XMLElement *presence;
|
||||||
|
IQFeatures *features;
|
||||||
char *from, *id;
|
char *from, *id;
|
||||||
if (!comp || !fr || !muc)
|
if (!comp || !fr || !muc)
|
||||||
{
|
{
|
||||||
|
|
@ -246,7 +264,16 @@ XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||||
XMLAddChild(presence, status);
|
XMLAddChild(presence, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
XMPPAnnotatePresence(presence);
|
features = LookupJIDFeatures(from);
|
||||||
|
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||||
|
IQ_IDENTITY
|
||||||
|
#undef IdentitySimple
|
||||||
|
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||||
|
IQ_ADVERT
|
||||||
|
#undef AdvertiseSimple
|
||||||
|
XMPPAnnotatePresence(presence, features);
|
||||||
|
FreeIQFeatures(features);
|
||||||
|
|
||||||
|
|
||||||
XMPPSendStanza(comp, presence, 10000);
|
XMPPSendStanza(comp, presence, 10000);
|
||||||
|
|
||||||
|
|
|
||||||
154
src/XMPP/MUCServ.c
Normal file
154
src/XMPP/MUCServ.c
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
#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,6 +7,8 @@
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
||||||
|
|
@ -159,27 +161,31 @@ XMPPGetReply(XMLElement *elem)
|
||||||
|
|
||||||
return HashMapGet(rep->attrs, "id");
|
return HashMapGet(rep->attrs, "id");
|
||||||
}
|
}
|
||||||
void
|
ssize_t
|
||||||
XMPPAnnotatePresence(XMLElement *presence)
|
XMPPGetReplyOffset(XMLElement *elem)
|
||||||
{
|
{
|
||||||
XMLElement *c;
|
if (!elem)
|
||||||
char *ver;
|
|
||||||
if (!presence)
|
|
||||||
{
|
{
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
for (size_t i = 0; i < ArraySize(elem->children); i++)
|
||||||
ver = XMPPGenerateVer();
|
{
|
||||||
c = XMLCreateTag("c");
|
XMLElement *child = ArrayGet(elem->children, i);
|
||||||
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
char *xmlns = HashMapGet(child->attrs, "xmlns");
|
||||||
XMLAddAttr(c, "hash", "sha-1");
|
char *xfor = HashMapGet(child->attrs, "for");
|
||||||
XMLAddAttr(c, "node", REPOSITORY);
|
if (StrEquals(child->name, "fallback") &&
|
||||||
XMLAddAttr(c, "ver", ver);
|
StrEquals(xmlns, "urn:xmpp:feature-fallback:0") &&
|
||||||
|
StrEquals(xfor, "urn:xmpp:reply:0"))
|
||||||
Free(ver);
|
{
|
||||||
XMLAddChild(presence, c);
|
XMLElement *body = XMLookForUnique(child, "body");
|
||||||
|
if (body && HashMapGet(body->attrs, "end"))
|
||||||
|
{
|
||||||
|
return strtol(HashMapGet(body->attrs, "end"), NULL, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
XMPPGetModeration(XMLElement *stanza)
|
XMPPGetModeration(XMLElement *stanza)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ void
|
||||||
MUCSetKey(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
MUCSetKey(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
{
|
{
|
||||||
ParseeData *data = XMPPGetManagerCookie(m);
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
char *affiliation, *role;
|
char *affiliation = NULL, *role = NULL;
|
||||||
char *chat_id;
|
char *chat_id = NULL;
|
||||||
char *muc;
|
char *muc = NULL;
|
||||||
|
|
||||||
char *key = NULL, *val = NULL;
|
char *key = NULL, *val = NULL;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Sha.h>
|
#include <Cytoplasm/Sha.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
#include <StringStream.h>
|
#include <StringStream.h>
|
||||||
#include <XMPPCommand.h>
|
#include <XMPPCommand.h>
|
||||||
|
|
@ -12,33 +13,86 @@
|
||||||
|
|
||||||
#include "XMPPThread/internal.h"
|
#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. */
|
/* Generates a SHA-256 hash of the ver field. */
|
||||||
char *
|
char *
|
||||||
XMPPGenerateVer(void)
|
XMPPGenerateVer(IQFeatures *features)
|
||||||
{
|
{
|
||||||
char *S = NULL;
|
char *S = NULL;
|
||||||
unsigned char *Sha = NULL;
|
unsigned char *Sha = NULL;
|
||||||
Array *identities = ArrayCreate();
|
|
||||||
Array *features = ArrayCreate();
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
/* Initialise identity table, to be sorted */
|
/* Initialise identity table, to be sorted */
|
||||||
#define IdentitySimple(cat, Type, Name) { \
|
ArraySort(features->identity, IdentitySort);
|
||||||
XMPPIdentity *id = Malloc(sizeof(*id)); \
|
for (i = 0; i < ArraySize(features->identity); i++)
|
||||||
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(identities, i);
|
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||||
char *id_chunk = StrConcat(7,
|
char *id_chunk = StrConcat(7,
|
||||||
identity->category, "/",
|
identity->category, "/",
|
||||||
identity->type, "/",
|
identity->type, "/",
|
||||||
|
|
@ -50,10 +104,10 @@ XMPPGenerateVer(void)
|
||||||
Free(id_chunk);
|
Free(id_chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArraySort(features, ((int (*) (void *, void *)) ICollate));
|
ArraySort(features->adverts, ((int (*) (void *, void *)) ICollate));
|
||||||
for (i = 0; i < ArraySize(features); i++)
|
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||||
{
|
{
|
||||||
char *feature = ArrayGet(features, i);
|
char *feature = ArrayGet(features->adverts, i);
|
||||||
char *tmp = S;
|
char *tmp = S;
|
||||||
S = StrConcat(3, S, feature, "<");
|
S = StrConcat(3, S, feature, "<");
|
||||||
Free(tmp);
|
Free(tmp);
|
||||||
|
|
@ -64,16 +118,64 @@ XMPPGenerateVer(void)
|
||||||
S = Base64Encode((const char *) Sha, 20);
|
S = Base64Encode((const char *) Sha, 20);
|
||||||
Free(Sha);
|
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;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,8 @@ XMPPDispatcher(void *argp)
|
||||||
|
|
||||||
if (!stanza)
|
if (!stanza)
|
||||||
{
|
{
|
||||||
|
/* TODO: We shouldn't be busywaiting. Even with a sleep call.
|
||||||
|
*/
|
||||||
UtilSleepMillis(10);
|
UtilSleepMillis(10);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +67,12 @@ XMPPDispatcher(void *argp)
|
||||||
Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name);
|
Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ManageMUCStanza(args->muc, stanza))
|
||||||
|
{
|
||||||
|
XMLFreeElement(stanza);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (StrEquals(stanza->name, "presence"))
|
if (StrEquals(stanza->name, "presence"))
|
||||||
{
|
{
|
||||||
PresenceStanza(args, stanza, thread);
|
PresenceStanza(args, stanza, thread);
|
||||||
|
|
@ -236,6 +244,8 @@ ParseeXMPPThread(void *argp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (!args->halted)
|
||||||
|
{
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
char *id;
|
char *id;
|
||||||
|
|
@ -292,10 +302,38 @@ ParseeXMPPThread(void *argp)
|
||||||
if (!args->halted)
|
if (!args->halted)
|
||||||
{
|
{
|
||||||
Log(LOG_WARNING, "XMPP server is closing stream...");
|
Log(LOG_WARNING, "XMPP server is closing stream...");
|
||||||
Log(LOG_WARNING, "Stopping %s...", NAME);
|
for (size_t i = 0; i < 50; i++)
|
||||||
error = true;
|
{
|
||||||
|
UtilSleepMillis(100); /* Wait a bit so that temporary failures don't fuck everything up */
|
||||||
|
|
||||||
|
Log(LOG_WARNING, "Restarting XMPP stream.");
|
||||||
|
/* This is the part where a new connection is being considered */
|
||||||
|
XMPPFinishCompStream(jabber);
|
||||||
|
XMPPEndCompStream(jabber);
|
||||||
|
|
||||||
|
args->jabber = XMPPInitialiseCompStream(
|
||||||
|
args->config->component_addr,
|
||||||
|
args->config->component_host,
|
||||||
|
args->config->component_port
|
||||||
|
);
|
||||||
|
jabber = args->jabber;
|
||||||
|
if (!jabber || !XMPPAuthenticateCompStream(jabber, args->config->shared_comp_secret))
|
||||||
|
{
|
||||||
|
/* Oops, there is something wrong! */
|
||||||
|
Log(LOG_ERR, "Couldn't authenticate to XMPP server");
|
||||||
|
XMPPEndCompStream(jabber);
|
||||||
|
args->jabber = NULL;
|
||||||
|
jabber = NULL;
|
||||||
|
if (i == 4)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&args->halt_lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&args->halt_lock);
|
pthread_mutex_unlock(&args->halt_lock);
|
||||||
|
}
|
||||||
|
|
||||||
info.running = false;
|
info.running = false;
|
||||||
for (i = 0; i < info.available_dispatchers; i++)
|
for (i = 0; i < info.available_dispatchers; i++)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <sys/utsname.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
@ -115,33 +116,37 @@ end:
|
||||||
|
|
||||||
#define DISCO "http://jabber.org/protocol/disco#info"
|
#define DISCO "http://jabber.org/protocol/disco#info"
|
||||||
static XMLElement *
|
static XMLElement *
|
||||||
IQGenerateQuery(void)
|
IQGenerateQuery(IQFeatures *features)
|
||||||
{
|
{
|
||||||
XMLElement *query = XMLCreateTag("query");
|
XMLElement *query;
|
||||||
|
if (!features)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
query = XMLCreateTag("query");
|
||||||
XMLAddAttr(query, "xmlns", DISCO);
|
XMLAddAttr(query, "xmlns", DISCO);
|
||||||
{
|
{
|
||||||
XMLElement *feature;
|
XMLElement *feature;
|
||||||
#define IdentitySimple(c,t,n) do \
|
size_t i;
|
||||||
{ \
|
for (i = 0; i < ArraySize(features->identity); i++)
|
||||||
feature = XMLCreateTag("identity"); \
|
{
|
||||||
XMLAddAttr(feature, "category", c); \
|
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||||
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);
|
|
||||||
|
|
||||||
IQ_ADVERT
|
feature = XMLCreateTag("identity");
|
||||||
#undef AdvertiseSimple
|
XMLAddAttr(feature, "category", identity->category);
|
||||||
|
XMLAddAttr(feature, "type", identity->type);
|
||||||
|
XMLAddAttr(feature, "name", identity->name);
|
||||||
|
|
||||||
|
XMLAddChild(query, feature);
|
||||||
|
}
|
||||||
|
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||||
|
{
|
||||||
|
char *var = ArrayGet(features->adverts, i);
|
||||||
|
|
||||||
|
feature = XMLCreateTag("feature");
|
||||||
|
XMLAddAttr(feature, "var", var);
|
||||||
|
XMLAddChild(query, feature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
|
|
@ -151,6 +156,7 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
||||||
{
|
{
|
||||||
char *from, *to, *id;
|
char *from, *to, *id;
|
||||||
XMLElement *iq_reply, *query;
|
XMLElement *iq_reply, *query;
|
||||||
|
IQFeatures *features;
|
||||||
|
|
||||||
from = HashMapGet(stanza->attrs, "from");
|
from = HashMapGet(stanza->attrs, "from");
|
||||||
to = HashMapGet(stanza->attrs, "to");
|
to = HashMapGet(stanza->attrs, "to");
|
||||||
|
|
@ -163,9 +169,10 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
||||||
XMLAddAttr(iq_reply, "type", "result");
|
XMLAddAttr(iq_reply, "type", "result");
|
||||||
XMLAddAttr(iq_reply, "id", id);
|
XMLAddAttr(iq_reply, "id", id);
|
||||||
|
|
||||||
query = IQGenerateQuery();
|
features = LookupJIDFeatures(to);
|
||||||
|
query = IQGenerateQuery(features);
|
||||||
{
|
{
|
||||||
char *ver = XMPPGenerateVer();
|
char *ver = XMPPGenerateVer(features);
|
||||||
char *node = StrConcat(3, REPOSITORY, "#", ver);
|
char *node = StrConcat(3, REPOSITORY, "#", ver);
|
||||||
XMLAddAttr(query, "node", node);
|
XMLAddAttr(query, "node", node);
|
||||||
|
|
||||||
|
|
@ -173,6 +180,7 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
||||||
Free(ver);
|
Free(ver);
|
||||||
}
|
}
|
||||||
XMLAddChild(iq_reply, query);
|
XMLAddChild(iq_reply, query);
|
||||||
|
FreeIQFeatures(features);
|
||||||
|
|
||||||
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
|
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
|
||||||
XMLFreeElement(iq_reply);
|
XMLFreeElement(iq_reply);
|
||||||
|
|
@ -366,7 +374,7 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
bool
|
bool
|
||||||
IQIsCommandList(ParseeData *args, XMLElement *stanza)
|
IQIsCommandList(ParseeData *args, XMLElement *stanza)
|
||||||
{
|
{
|
||||||
char *parsee = NULL;
|
char *parsee = NULL, *to;
|
||||||
XMLElement *query = XMLookForTKV(
|
XMLElement *query = XMLookForTKV(
|
||||||
stanza, "query", "xmlns",
|
stanza, "query", "xmlns",
|
||||||
"http://jabber.org/protocol/disco#items"
|
"http://jabber.org/protocol/disco#items"
|
||||||
|
|
@ -381,7 +389,8 @@ IQIsCommandList(ParseeData *args, XMLElement *stanza)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsee = ParseeJID(args);
|
parsee = ParseeJID(args);
|
||||||
ret = StrEquals(HashMapGet(stanza->attrs, "to"), parsee);
|
to = HashMapGet(stanza->attrs, "to");
|
||||||
|
ret = StrEquals(to, parsee) || StrEquals(to, args->config->component_host);
|
||||||
Free(parsee);
|
Free(parsee);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
@ -565,7 +574,7 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version"))
|
else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version"))
|
||||||
{
|
{
|
||||||
XMLElement *iq_reply, *query;
|
XMLElement *iq_reply, *query;
|
||||||
XMLElement *name, *version;
|
XMLElement *name, *version, *os;
|
||||||
|
|
||||||
iq_reply = XMLCreateTag("iq");
|
iq_reply = XMLCreateTag("iq");
|
||||||
XMLAddAttr(iq_reply, "to", from);
|
XMLAddAttr(iq_reply, "to", from);
|
||||||
|
|
@ -576,14 +585,19 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
query = XMLCreateTag("query");
|
query = XMLCreateTag("query");
|
||||||
XMLAddAttr(query, "xmlns", "jabber:iq:version");
|
XMLAddAttr(query, "xmlns", "jabber:iq:version");
|
||||||
{
|
{
|
||||||
|
struct utsname info;
|
||||||
name = XMLCreateTag("name");
|
name = XMLCreateTag("name");
|
||||||
version = XMLCreateTag("version");
|
version = XMLCreateTag("version");
|
||||||
|
os = XMLCreateTag("os");
|
||||||
|
uname(&info);
|
||||||
|
|
||||||
XMLAddChild(name, XMLCreateText(NAME));
|
XMLAddChild(name, XMLCreateText(NAME));
|
||||||
XMLAddChild(version, XMLCreateText(VERSION "[" CODE "]"));
|
XMLAddChild(version, XMLCreateText(VERSION "[" CODE "]"));
|
||||||
|
XMLAddChild(os, XMLCreateText(info.sysname));
|
||||||
}
|
}
|
||||||
XMLAddChild(query, name);
|
XMLAddChild(query, name);
|
||||||
XMLAddChild(query, version);
|
XMLAddChild(query, version);
|
||||||
|
XMLAddChild(query, os);
|
||||||
XMLAddChild(iq_reply, query);
|
XMLAddChild(iq_reply, query);
|
||||||
|
|
||||||
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
|
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
|
||||||
|
|
@ -591,10 +605,14 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
char *buf = NULL;
|
||||||
|
Stream *s = StrStreamWriter(&buf);
|
||||||
Log(LOG_WARNING, "Unknown I/Q received:");
|
Log(LOG_WARNING, "Unknown I/Q received:");
|
||||||
XMLEncode(StreamStdout(), stanza);
|
XMLEncode(s, stanza);
|
||||||
StreamPrintf(StreamStdout(),"\n");
|
StreamFlush(s);
|
||||||
StreamFlush(StreamStdout());
|
StreamClose(s);
|
||||||
|
Log(LOG_WARNING, "%s", buf);
|
||||||
|
Free(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <Unistring.h>
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
|
|
@ -276,7 +277,7 @@ end_error:
|
||||||
Log(LOG_DEBUG, "Error management: %fs", Elapsed(rectime));
|
Log(LOG_DEBUG, "Error management: %fs", Elapsed(rectime));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moderated)
|
if (moderated && !(!chat && strncmp(HashMapGet(stanza->attrs, "to"), "parsee@", 7)))
|
||||||
{
|
{
|
||||||
/* TODO: Parsee MUST check if it is a valid MUC */
|
/* TODO: Parsee MUST check if it is a valid MUC */
|
||||||
char *resource = ParseeGetResource(from);
|
char *resource = ParseeGetResource(from);
|
||||||
|
|
@ -324,8 +325,9 @@ end_error:
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG, "Fetching bridge: %fs", Elapsed(rectime));
|
Log(LOG_DEBUG, "Fetching bridge: %fs", Elapsed(rectime));
|
||||||
}
|
}
|
||||||
|
char *trim = ParseeTrimJID(from);
|
||||||
if (!mroom_id && !room && !XMPPIsParseeStanza(stanza) &&
|
if (!mroom_id && !room && !XMPPIsParseeStanza(stanza) &&
|
||||||
to && *to == '@')
|
to && *to == '@' && !XMPPQueryMUC(jabber, trim, NULL))
|
||||||
{
|
{
|
||||||
DbRef *room_ref;
|
DbRef *room_ref;
|
||||||
HashMap *room_json;
|
HashMap *room_json;
|
||||||
|
|
@ -344,6 +346,7 @@ end_error:
|
||||||
ParseePushDMRoom(args, to, from, room);
|
ParseePushDMRoom(args, to, from, room);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Free(trim);
|
||||||
|
|
||||||
/* TODO: THIS IS A HACK. THIS CODE IGNORES ALL MUC MESSAGES EVER
|
/* TODO: THIS IS A HACK. THIS CODE IGNORES ALL MUC MESSAGES EVER
|
||||||
* SENT TO NON-PARSEE PUPPETS, AS A "FIX" TO THE MULTITHREADED
|
* SENT TO NON-PARSEE PUPPETS, AS A "FIX" TO THE MULTITHREADED
|
||||||
|
|
@ -394,11 +397,17 @@ end_error:
|
||||||
bool media_enabled = chat_id ?
|
bool media_enabled = chat_id ?
|
||||||
ParseeIsMediaEnabled(args, chat_id) :
|
ParseeIsMediaEnabled(args, chat_id) :
|
||||||
true ;
|
true ;
|
||||||
|
bool is_parsee;
|
||||||
|
/* Note that chat_id still has meaningful info as a boolean
|
||||||
|
* despite being freed */
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
|
|
||||||
pthread_mutex_unlock(&thr->info->chk_lock);
|
pthread_mutex_unlock(&thr->info->chk_lock);
|
||||||
|
|
||||||
|
parsee = ParseeJID(args);
|
||||||
|
is_parsee = StrEquals(to, parsee);
|
||||||
|
Free(parsee);
|
||||||
|
parsee = NULL;
|
||||||
|
|
||||||
LazyRegister(args, encoded, !chat ? res : NULL);
|
LazyRegister(args, encoded, !chat ? res : NULL);
|
||||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||||
{
|
{
|
||||||
|
|
@ -411,7 +420,12 @@ end_error:
|
||||||
|
|
||||||
/* Check if it is a media link */
|
/* Check if it is a media link */
|
||||||
oob = XMLookForTKV(stanza, "x", "xmlns", "jabber:x:oob");
|
oob = XMLookForTKV(stanza, "x", "xmlns", "jabber:x:oob");
|
||||||
if (oob && data && media_enabled)
|
if (chat_id && StrEquals(type, "chat") && !is_parsee)
|
||||||
|
{
|
||||||
|
/* Very clearly a MUC DM. */
|
||||||
|
event_id = NULL;
|
||||||
|
}
|
||||||
|
else if (oob && data && media_enabled)
|
||||||
{
|
{
|
||||||
char *mxc, *mime = NULL;
|
char *mxc, *mime = NULL;
|
||||||
HashMap *content = NULL;
|
HashMap *content = NULL;
|
||||||
|
|
@ -507,6 +521,15 @@ end_error:
|
||||||
* too. Go figure. */
|
* too. Go figure. */
|
||||||
size_t off =
|
size_t off =
|
||||||
reply_to ? ParseeFindDatastart(data->data) : 0;
|
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);
|
HashMap *ev = MatrixCreateMessage(data->data + off);
|
||||||
if (reply_to)
|
if (reply_to)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ end_item:
|
||||||
Free(room);
|
Free(room);
|
||||||
FreeStatuses(statuses);
|
FreeStatuses(statuses);
|
||||||
}
|
}
|
||||||
if (status)
|
if (status && !StrEquals(type, "unavailable"))
|
||||||
{
|
{
|
||||||
XMLElement *status_data = ArrayGet(status->children, 0);
|
XMLElement *status_data = ArrayGet(status->children, 0);
|
||||||
char *decode_from = ParseeLookupJID(oid);
|
char *decode_from = ParseeLookupJID(oid);
|
||||||
|
|
@ -309,6 +309,8 @@ end_item:
|
||||||
status_str = status_data->data;
|
status_str = status_data->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* TODO: "The server will automatically set a user's presence to
|
/* TODO: "The server will automatically set a user's presence to
|
||||||
* unavailable if their last active time was over a threshold value
|
* unavailable if their last active time was over a threshold value
|
||||||
* (e.g. 5 minutes)."
|
* (e.g. 5 minutes)."
|
||||||
|
|
|
||||||
|
|
@ -32,13 +32,17 @@
|
||||||
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
|
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
|
||||||
IdentitySimple("component", "generic", "Parsee's component")
|
IdentitySimple("component", "generic", "Parsee's component")
|
||||||
|
|
||||||
typedef struct PEPManager PEPManager;
|
typedef struct IQFeatures {
|
||||||
typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item);
|
Array *identity;
|
||||||
|
Array *adverts;
|
||||||
|
} IQFeatures;
|
||||||
typedef struct XMPPIdentity {
|
typedef struct XMPPIdentity {
|
||||||
char *category, *type, *lang, *name;
|
char *category, *type, *lang, *name;
|
||||||
} XMPPIdentity;
|
} XMPPIdentity;
|
||||||
|
|
||||||
|
typedef struct PEPManager PEPManager;
|
||||||
|
typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item);
|
||||||
|
|
||||||
typedef struct XMPPThread XMPPThread;
|
typedef struct XMPPThread XMPPThread;
|
||||||
typedef struct XMPPThreadInfo {
|
typedef struct XMPPThreadInfo {
|
||||||
/* A FIFO of stanzas inbound, to be read by dispatcher
|
/* A FIFO of stanzas inbound, to be read by dispatcher
|
||||||
|
|
@ -65,6 +69,29 @@ struct XMPPThread {
|
||||||
int ICollate(unsigned char *cata, unsigned char *catb);
|
int ICollate(unsigned char *cata, unsigned char *catb);
|
||||||
int IdentitySort(void *idap, void *idbp);
|
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 * ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza);
|
||||||
char * ParseeGetEventFromID(ParseeData *data, XMLElement *stanza, char *id);
|
char * ParseeGetEventFromID(ParseeData *data, XMLElement *stanza, char *id);
|
||||||
char * ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza);
|
char * ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza);
|
||||||
|
|
|
||||||
39
src/include/MUCServ.h
Normal file
39
src/include/MUCServ.h
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef PARSEE_MUCSERV_H
|
||||||
|
#define PARSEE_MUCSERV_H
|
||||||
|
|
||||||
|
/*-* An easy interface for handling MUCs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct MUCServer MUCServer;
|
||||||
|
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/** Creates a MUC server handle given a ParseeData handle.
|
||||||
|
* This handle shall be used for all MUC-related operations.
|
||||||
|
* ----------------------------------------------------------
|
||||||
|
* Returns: a MUC server handle[HEAP] | NULL
|
||||||
|
* Thrasher: FreeMUCServer
|
||||||
|
* See-Also: https://xmpp.org/extensions/xep-0045.html */
|
||||||
|
extern MUCServer * CreateMUCServer(ParseeData *data);
|
||||||
|
|
||||||
|
/** Manages a stanza from a MUC, and returns true if the stanza
|
||||||
|
* was actually processed by the MUC server.
|
||||||
|
* -------------------------------------------------------------
|
||||||
|
* See-Also: CreateMUCServer */
|
||||||
|
extern bool ManageMUCStanza(MUCServer *serv, XMLElement *stanza);
|
||||||
|
|
||||||
|
/** Checks if a MUC(from its localpart, so
|
||||||
|
* <code>'#foobar@bridge.blow.hole' -> 'foobar'</code>) exists.
|
||||||
|
* --------------
|
||||||
|
* Returns: whenever the MUC exists */
|
||||||
|
extern bool MUCServerExists(MUCServer *serv, char *muc);
|
||||||
|
|
||||||
|
/** Destroys all memory and references from a MUC server handle.
|
||||||
|
* --------------
|
||||||
|
* Thrashes: CreateMUCServer
|
||||||
|
* See-Also: https://xmpp.org/extensions/xep-0045.html */
|
||||||
|
extern void FreeMUCServer(MUCServer *serv);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define PARSEE_MATRIX_H
|
#define PARSEE_MATRIX_H
|
||||||
|
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
#include "FileInfo.h"
|
#include "FileInfo.h"
|
||||||
|
|
||||||
|
|
@ -20,6 +21,12 @@ typedef struct UserID {
|
||||||
* Thrasher: Free */
|
* Thrasher: Free */
|
||||||
extern UserID * MatrixParseID(char *user);
|
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. */
|
/* Creates an error message JSON, with the specified code and message. */
|
||||||
extern HashMap * MatrixCreateError(char *err, char *msg);
|
extern HashMap * MatrixCreateError(char *err, char *msg);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ typedef struct ParseeData ParseeData;
|
||||||
#include <Command.h>
|
#include <Command.h>
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
|
|
||||||
|
#include <MUCServ.h>
|
||||||
|
|
||||||
#define PARSEE_VERBOSE_NONE 0
|
#define PARSEE_VERBOSE_NONE 0
|
||||||
#define PARSEE_VERBOSE_LOG 1
|
#define PARSEE_VERBOSE_LOG 1
|
||||||
#define PARSEE_VERBOSE_TIMINGS 2
|
#define PARSEE_VERBOSE_TIMINGS 2
|
||||||
|
|
@ -39,9 +41,14 @@ typedef struct ParseeConfig {
|
||||||
/* Homeserver port info */
|
/* Homeserver port info */
|
||||||
char *homeserver_host;
|
char *homeserver_host;
|
||||||
int homeserver_port;
|
int homeserver_port;
|
||||||
|
int homeserver_tls;
|
||||||
|
|
||||||
|
bool accept_pings;
|
||||||
|
bool ignore_bots;
|
||||||
|
|
||||||
|
|
||||||
/* ------- JABBER -------- */
|
/* ------- JABBER -------- */
|
||||||
|
char *component_addr;
|
||||||
char *component_host;
|
char *component_host;
|
||||||
char *shared_comp_secret;
|
char *shared_comp_secret;
|
||||||
int component_port;
|
int component_port;
|
||||||
|
|
@ -62,6 +69,7 @@ typedef struct ParseeData {
|
||||||
|
|
||||||
HttpServer *server;
|
HttpServer *server;
|
||||||
XMPPComponent *jabber;
|
XMPPComponent *jabber;
|
||||||
|
MUCServer *muc;
|
||||||
|
|
||||||
Db *db;
|
Db *db;
|
||||||
char *id;
|
char *id;
|
||||||
|
|
@ -351,7 +359,7 @@ extern int ParseeFindDatastartU(char *data);
|
||||||
|
|
||||||
|
|
||||||
/* XMPP-ifies a message event to XEP-0393 if possible. */
|
/* XMPP-ifies a message event to XEP-0393 if possible. */
|
||||||
extern char * ParseeXMPPify(HashMap *event);
|
extern char * ParseeXMPPify(ParseeData *data, HashMap *event);
|
||||||
|
|
||||||
/* Finds an event ID from an ID in the stanza's attributes */
|
/* Finds an event ID from an ID in the stanza's attributes */
|
||||||
extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id);
|
extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id);
|
||||||
|
|
@ -361,7 +369,7 @@ extern char * ParseeDMEventFromID(ParseeData *d, char *r_id, char *ori_id);
|
||||||
extern char * ParseeDMEventFromSID(ParseeData *d, char *r_id, char *ori_id);
|
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 */
|
/* Gets a Parsee "shim" link to an MXC, usable as unauth for a limited time */
|
||||||
extern char * ParseeToUnauth(ParseeData *data, char *mxc);
|
extern char * ParseeToUnauth(ParseeData *data, char *mxc, char *filename);
|
||||||
|
|
||||||
extern void ParseeInitialiseOIDTable(void);
|
extern void ParseeInitialiseOIDTable(void);
|
||||||
extern void ParseePushOIDTable(char *muc, char *occupant);
|
extern void ParseePushOIDTable(char *muc, char *occupant);
|
||||||
|
|
@ -392,10 +400,17 @@ extern void ParseeDestroyNickTable(void);
|
||||||
* to ban them from rooms where Parsee has the ability to do so ("noflying").
|
* to ban them from rooms where Parsee has the ability to do so ("noflying").
|
||||||
* ---------------
|
* ---------------
|
||||||
* Returns: NOTHING
|
* Returns: NOTHING
|
||||||
* See-Also: ParseeManageBan
|
* See-Also: ParseeManageBan, ParseeGlobalUnban
|
||||||
* Modifies: the database */
|
* Modifies: the database */
|
||||||
extern void ParseeGlobalBan(ParseeData *, char *user, char *reason);
|
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),
|
/** Verifies if a user was banned globally. If so (and if {room} is set),
|
||||||
* tries to ban the user from it.
|
* tries to ban the user from it.
|
||||||
* ---------------
|
* ---------------
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,10 @@ typedef struct ParseeCmdArg {
|
||||||
"ban-list", CmdNoFlyList, \
|
"ban-list", CmdNoFlyList, \
|
||||||
"Globally bans a user from using Parsee" \
|
"Globally bans a user from using Parsee" \
|
||||||
) \
|
) \
|
||||||
|
X_COMMAND( \
|
||||||
|
"unban-list", CmdNoFlyListDel, \
|
||||||
|
"Globally unbans a user from using Parsee" \
|
||||||
|
) \
|
||||||
X_COMMAND( \
|
X_COMMAND( \
|
||||||
"nuke-muc", CmdUnlinkMUC, \
|
"nuke-muc", CmdUnlinkMUC, \
|
||||||
"Removes a MUC. Users should then run " \
|
"Removes a MUC. Users should then run " \
|
||||||
|
|
@ -75,7 +79,8 @@ typedef struct ParseeCmdArg {
|
||||||
X_ROUTE("/_matrix/app/v1/users/(.*)", RouteUserAck) \
|
X_ROUTE("/_matrix/app/v1/users/(.*)", RouteUserAck) \
|
||||||
X_ROUTE("/_matrix/app/v1/rooms/(.*)", RouteRoomAck) \
|
X_ROUTE("/_matrix/app/v1/rooms/(.*)", RouteRoomAck) \
|
||||||
X_ROUTE("/_matrix/app/v1/ping", RoutePing) \
|
X_ROUTE("/_matrix/app/v1/ping", RoutePing) \
|
||||||
X_ROUTE("/_matrix/client/v1/media/download/(.*)/(.*)", RouteMedia)
|
X_ROUTE("/media/(.*)/(.*)", RouteMedia) \
|
||||||
|
X_ROUTE("/media/(.*)/(.*)/(.*)", RouteMedia)
|
||||||
|
|
||||||
#define X_ROUTE(path, name) extern void * name(Array *, void *);
|
#define X_ROUTE(path, name) extern void * name(Array *, void *);
|
||||||
ROUTES
|
ROUTES
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,12 @@ extern void UnistrFree(Unistr *unistr);
|
||||||
* Returns: whenever the character is within the BMP */
|
* Returns: whenever the character is within the BMP */
|
||||||
extern bool UnistrIsBMP(uint32_t u);
|
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);
|
typedef bool (*UnistrFilterFunc)(uint32_t u);
|
||||||
/** "Filters" characters in a Unistring by codepoint, removing
|
/** "Filters" characters in a Unistring by codepoint, removing
|
||||||
* those with callbacks which return false into a new unistring.
|
* those with callbacks which return false into a new unistring.
|
||||||
|
|
@ -76,4 +82,10 @@ extern Unistr * UnistrFilter(Unistr *str, UnistrFilterFunc filter);
|
||||||
* --------
|
* --------
|
||||||
* Returns: an offset of the first line to not start by {c} | 0 */
|
* Returns: an offset of the first line to not start by {c} | 0 */
|
||||||
extern size_t UnistrGetOffset(Unistr *str, uint32_t sep);
|
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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ typedef struct XMLEvent {
|
||||||
XML_LEXER_ELEM, /* Empty attr */
|
XML_LEXER_ELEM, /* Empty attr */
|
||||||
XML_LEXER_ENDELEM,
|
XML_LEXER_ENDELEM,
|
||||||
|
|
||||||
|
XML_ERROR,
|
||||||
|
|
||||||
XML_RELAX
|
XML_RELAX
|
||||||
} type;
|
} type;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ typedef struct XMPPComponent {
|
||||||
|
|
||||||
/* Initialises a raw component stream to host, with an optional port.
|
/* 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 */
|
* If said port is 0, then it is set to the default Prosody port */
|
||||||
extern XMPPComponent * XMPPInitialiseCompStream(char *host, int port);
|
extern XMPPComponent * XMPPInitialiseCompStream(char *addr, char *host, int port);
|
||||||
|
|
||||||
/* Authenticates a component stream with a given shared secret,
|
/* Authenticates a component stream with a given shared secret,
|
||||||
* with a stream ID from the server. This should be called right
|
* with a stream ID from the server. This should be called right
|
||||||
|
|
@ -90,6 +90,9 @@ extern char * XMPPGetRetractedID(XMLElement *);
|
||||||
/* Get the replied-to stanza ID, if existent. */
|
/* Get the replied-to stanza ID, if existent. */
|
||||||
extern char * XMPPGetReply(XMLElement *elem);
|
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
|
/** Get the moderated message ID(as a stanza ID/plain ID) from a moderation
|
||||||
* stanza, that lives *alongside* the stanza itself.
|
* stanza, that lives *alongside* the stanza itself.
|
||||||
* ----------------------------------------------------------------------
|
* ----------------------------------------------------------------------
|
||||||
|
|
@ -98,16 +101,6 @@ extern char * XMPPGetReply(XMLElement *elem);
|
||||||
* See-Also: https://xmpp.org/extensions/xep-0425.html */
|
* See-Also: https://xmpp.org/extensions/xep-0425.html */
|
||||||
extern char * XMPPGetModeration(XMLElement *stanza);
|
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);
|
extern bool XMPPHasError(XMLElement *stanza, char *type);
|
||||||
|
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
|
|
|
||||||
|
|
@ -89,8 +89,12 @@ Main(Array *args, HashMap *env)
|
||||||
glob = ArrayGet(args, 2);
|
glob = ArrayGet(args, 2);
|
||||||
|
|
||||||
parsee = GetDB(db_path);
|
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)
|
if (glob)
|
||||||
{
|
{
|
||||||
|
|
@ -112,8 +116,3 @@ Main(Array *args, HashMap *env)
|
||||||
DbClose(parsee);
|
DbClose(parsee);
|
||||||
return EXIT_SUCCESS;
|
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);
|
ret = DbOpenLMDB(db_path, lmdb_size);
|
||||||
}
|
}
|
||||||
if (!ret)
|
else
|
||||||
{
|
{
|
||||||
ret = DbOpen(db_path, 0);
|
ret = DbOpen(db_path, 0);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ Main(Array *args, HashMap *env)
|
||||||
Uri *api_base;
|
Uri *api_base;
|
||||||
char *homeserver = NULL, *jcp = NULL, *jabber = NULL;
|
char *homeserver = NULL, *jcp = NULL, *jabber = NULL;
|
||||||
char *data = NULL, *media = NULL, *listen = NULL;
|
char *data = NULL, *media = NULL, *listen = NULL;
|
||||||
|
char *component_as = NULL;
|
||||||
int flag, code = EXIT_FAILURE;
|
int flag, code = EXIT_FAILURE;
|
||||||
int port = 5347;
|
int port = 5347;
|
||||||
size_t lmdb_size = 0;
|
size_t lmdb_size = 0;
|
||||||
|
|
@ -28,7 +29,7 @@ Main(Array *args, HashMap *env)
|
||||||
listen = "localhost";
|
listen = "localhost";
|
||||||
|
|
||||||
ArgParseStateInit(&state);
|
ArgParseStateInit(&state);
|
||||||
while ((flag = ArgParse(&state, args, "H:J:s:d:p:m:l:S:M:")) != -1)
|
while ((flag = ArgParse(&state, args, "H:J:j:s:d:p:m:l:S:M:")) != -1)
|
||||||
{
|
{
|
||||||
switch (flag)
|
switch (flag)
|
||||||
{
|
{
|
||||||
|
|
@ -45,6 +46,9 @@ Main(Array *args, HashMap *env)
|
||||||
case 'J':
|
case 'J':
|
||||||
jabber = state.optArg;
|
jabber = state.optArg;
|
||||||
break;
|
break;
|
||||||
|
case 'j':
|
||||||
|
component_as = state.optArg;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
data = state.optArg;
|
data = state.optArg;
|
||||||
break;
|
break;
|
||||||
|
|
@ -123,6 +127,7 @@ Main(Array *args, HashMap *env)
|
||||||
|
|
||||||
JsonSet(json, JsonValueString(media), 1, "media_base");
|
JsonSet(json, JsonValueString(media), 1, "media_base");
|
||||||
JsonSet(json, JsonValueString(listen), 1, "listen_as");
|
JsonSet(json, JsonValueString(listen), 1, "listen_as");
|
||||||
|
JsonSet(json, JsonValueString(component_as), 1, "component_addr");
|
||||||
|
|
||||||
JsonEncode(json, file, JSON_PRETTY);
|
JsonEncode(json, file, JSON_PRETTY);
|
||||||
StreamFlush(file);
|
StreamFlush(file);
|
||||||
|
|
|
||||||
152
tools/plumb.c
Normal file
152
tools/plumb.c
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
/* plumb.c - Small utility to manage plumbings from a shutoff instance.
|
||||||
|
* ============================================================
|
||||||
|
* TODO: write other commands, and move some code to common.h
|
||||||
|
*
|
||||||
|
* Under CC0, as its a rather useful example of a Parsee tool.
|
||||||
|
* See LICENSE for more information about Parsee's licensing. */
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <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