mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 21:05:10 +00:00
Compare commits
229 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 | ||
|
|
5ff92dda3d | ||
|
|
56433fc69c | ||
|
|
5d13410ac4 | ||
|
|
7f41a85a8a | ||
|
|
954c588310 | ||
|
|
dddf1680a0 | ||
|
|
960599d983 | ||
|
|
033dba6840 | ||
|
|
cf3bb5260b | ||
|
|
064040c18f | ||
|
|
55ac682d26 | ||
|
|
ca5f8fd5c5 | ||
|
|
947c2d48d2 | ||
|
|
dcd31201ab | ||
|
|
6aef0349b8 | ||
|
|
7f7ab41afa | ||
|
|
d4371ac30b | ||
|
|
9a16d96323 | ||
|
|
7c60ab28cb | ||
|
|
ce8bbb5540 | ||
|
|
4bc2d1606a | ||
|
|
fced112691 | ||
|
|
4b5a759ce7 | ||
|
|
b0cf0961a3 | ||
|
|
44d6df3be8 | ||
|
|
02a89270c0 | ||
|
|
ff5f085b7f | ||
|
|
d9b5141eb5 | ||
|
|
bb32eb48a6 | ||
|
|
2074d8e768 |
||
|
|
e0f76e7929 |
||
|
|
a66021bf46 |
||
|
|
ecbc211003 |
||
|
|
1be19d4b8d | ||
|
|
27223a5896 | ||
|
|
147e430a47 | ||
|
|
13a9d23fa0 | ||
|
|
a48c3ba126 | ||
|
|
94090a8c97 | ||
|
|
179fd405db | ||
|
|
b7c360d528 | ||
|
|
17474bda0f | ||
|
|
d585134ce1 | ||
|
|
9ea8f35c49 | ||
|
|
d989331716 | ||
|
|
09d38993bb | ||
|
|
a38e14e029 | ||
|
|
0b00a665bf | ||
|
|
af2d08a431 | ||
|
|
3ceae7b053 | ||
|
|
e7ba1fa48d | ||
|
|
749df0feb1 | ||
|
|
0f3253a385 | ||
|
|
2324f9afc0 | ||
|
|
63285ac24a | ||
|
|
dec0d1f3d9 | ||
|
|
c2ea3807ec | ||
|
|
dba3dcc85f | ||
|
|
b9954c06ce | ||
|
|
6167732e83 | ||
|
|
9b4d97b033 | ||
|
|
6e9ba8f0ee | ||
|
|
e54332e376 | ||
|
|
f31a9c37b6 | ||
|
|
c607a99004 | ||
|
|
7ee5c055f4 | ||
|
|
f666f39b7c | ||
|
|
420c05690f | ||
|
|
f96e0a95bd | ||
|
|
5ffd413e67 | ||
|
|
9d67cb6165 | ||
|
|
9ff70bc0ed | ||
|
|
05be7fe249 | ||
|
|
0cccef7194 | ||
|
|
f8b834c652 | ||
|
|
1ce2eea9d8 | ||
|
|
cb70d599bf | ||
|
|
d99b827d5c | ||
|
|
dd6dede2e8 | ||
|
|
bdb1f46c77 | ||
|
|
613822e40c | ||
|
|
2e4314be48 | ||
|
|
1f7a220a8f | ||
|
|
7c4333f137 | ||
|
|
901a88c638 | ||
|
|
4a2160503b | ||
|
|
9f1c2046fd | ||
|
|
4164eb5ed2 | ||
|
|
63772d50ab | ||
|
|
92db543d78 | ||
|
|
19cf159f21 | ||
|
|
fc75dc52aa | ||
|
|
0ba708e28f | ||
|
|
75bbe69633 | ||
|
|
b10f14d067 | ||
|
|
eb25ce3e2b | ||
|
|
92d33fa641 | ||
|
|
a110b44ad6 | ||
|
|
90ddce4757 | ||
|
|
e0e398bfb5 | ||
|
|
9352bf9bba | ||
|
|
355a9d9b99 | ||
|
|
0586e863e7 | ||
|
|
619f49f8c8 | ||
|
|
5c7189f518 | ||
|
|
b55457d5fe | ||
|
|
79e65f0822 | ||
|
|
79c94759da | ||
|
|
5c5540ffa6 | ||
|
|
9e4348c6e3 | ||
|
|
0e746465c6 | ||
|
|
aef19d5eaf | ||
|
|
0c402d8032 | ||
|
|
e9b36730b5 | ||
|
|
7ab99982f5 | ||
|
|
2955787aa2 | ||
|
|
4496f377fd | ||
|
|
4dcd989c8b | ||
|
|
eb9512cde5 | ||
|
|
89ab1b57f0 | ||
|
|
d9bea609dd | ||
|
|
e8fb9b9641 | ||
|
|
eefd5f0389 | ||
|
|
907ac13da9 | ||
|
|
1d188069db | ||
|
|
696def187c | ||
|
|
42f02010d4 | ||
|
|
a3dc90f93e | ||
|
|
3ae262c452 | ||
|
|
a7c06bdb5c | ||
|
|
e1b044b268 | ||
|
|
5149a37d2e | ||
|
|
9cba44f69b | ||
|
|
aa8faec486 | ||
|
|
7314be5aeb | ||
|
|
60f3dc12c1 | ||
|
|
4e22fb6f04 | ||
|
|
c8925dbe00 | ||
|
|
1f31ce2c35 | ||
|
|
f1d864c975 | ||
|
|
fdc0dfbcb6 | ||
|
|
971f8ba3aa | ||
|
|
2a6a3300e6 | ||
|
|
488ab92a90 | ||
|
|
7593225e18 | ||
|
|
aec3c87aee | ||
|
|
65c6e79fb2 | ||
|
|
9843a0057a | ||
|
|
9775bd8d4e | ||
|
|
5d268a71a5 | ||
|
|
53739dd42d | ||
|
|
0ec028d458 | ||
|
|
ad7f4f20e1 | ||
|
|
0ce72b52e9 | ||
|
|
6c5b85aea6 | ||
|
|
24c6e70cb7 | ||
|
|
8ce69d0829 | ||
|
|
34bd036ab7 | ||
|
|
80bab890f6 | ||
|
|
1f3d738bb4 | ||
|
|
9d9453f96a | ||
|
|
063314b081 | ||
|
|
3366fcb759 | ||
|
|
55674c369b | ||
|
|
39cc04fc2e | ||
|
|
692cb8aa6f | ||
|
|
3a60b773c9 | ||
|
|
27e0b96ecd | ||
|
|
2cc8e16ba2 | ||
|
|
69d913d92b | ||
|
|
54c5331835 | ||
|
|
8042ccc0cc | ||
|
|
fb511b4df0 | ||
|
|
1b62072a3a | ||
|
|
4c158ea186 | ||
|
|
6cd3df21db | ||
|
|
4b2ede6a66 | ||
|
|
1f747dd016 | ||
|
|
c975dba852 | ||
|
|
198bdb98e9 | ||
|
|
59cbd8b22d | ||
|
|
c0c13883d1 | ||
|
|
aa9b68e02d |
133 changed files with 8900 additions and 2915 deletions
85
.forgejo/workflows/build-release.yaml
Normal file
85
.forgejo/workflows/build-release.yaml
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
name: Builds static Parsee
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
compile:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
distro:
|
||||||
|
- debian
|
||||||
|
arch:
|
||||||
|
- amd64
|
||||||
|
- arm64
|
||||||
|
runs-on:
|
||||||
|
- ${{ matrix.distro }}
|
||||||
|
- ${{ matrix.arch }}
|
||||||
|
container:
|
||||||
|
image: ${{ matrix.distro }}
|
||||||
|
steps:
|
||||||
|
- name: Install LMDB+OpenSSL tools
|
||||||
|
run: |
|
||||||
|
echo $(uname -a) $(env | grep container)
|
||||||
|
apt update -y
|
||||||
|
apt install -y build-essential liblmdb-dev nodejs libssl-dev git wget python3 python3-pip bzip2
|
||||||
|
- name: Clone everything
|
||||||
|
run: |
|
||||||
|
mkdir -p repos
|
||||||
|
git clone --depth=1 https://git.musl-libc.org/git/musl repos/musl
|
||||||
|
wget https://github.com/Mbed-TLS/mbedtls/releases/download/mbedtls-3.6.1/mbedtls-3.6.1.tar.bz2
|
||||||
|
tar -xvf mbedtls-3.6.1.tar.bz2
|
||||||
|
mv mbedtls-3.6.1/ repos/mbed
|
||||||
|
git clone --depth=1 https://github.com/LMDB/lmdb repos/lmdb
|
||||||
|
git clone -b add-mbed --single-branch https://git.telodendria.io/lda/Cytoplasm repos/cyto
|
||||||
|
echo "PREFIX=$GITHUB_WORKSPACE/usr" >> $GITHUB_ENV
|
||||||
|
echo "PATH=$GITHUB_WORKSPACE/usr/bin:$PATH" >> $GITHUB_ENV
|
||||||
|
echo "INCLUDE_PATH=$GITHUB_WORKSPACE/usr/include" >> $GITHUB_ENV
|
||||||
|
echo "LIBRARY_PATH=$GITHUB_WORKSPACE/usr/lib" >> $GITHUB_ENV
|
||||||
|
- name: Build musl
|
||||||
|
run: |
|
||||||
|
cd repos/musl
|
||||||
|
./configure --prefix=${PREFIX}
|
||||||
|
make -j$(nproc)
|
||||||
|
make install -j$(nproc)
|
||||||
|
alias musl-gcc="musl-gcc -Wl,-Bstatic -L ${PREFIX}/lib"
|
||||||
|
- name: Build MbedTLS
|
||||||
|
run: |
|
||||||
|
alias musl-gcc="musl-gcc -Wl,-Bstatic -L ${PREFIX}/lib"
|
||||||
|
cd repos/mbed
|
||||||
|
python3 -m pip install -r scripts/basic.requirements.txt --break-system-packages
|
||||||
|
make CC=musl-gcc -j$(nproc)
|
||||||
|
make install DESTDIR=${PREFIX} -j$(nproc)
|
||||||
|
- name: Build LMDB
|
||||||
|
run: |
|
||||||
|
alias musl-gcc="musl-gcc -Wl,-Bstatic -L ${PREFIX}/lib"
|
||||||
|
cd repos/lmdb/libraries/liblmdb
|
||||||
|
make CC=musl-gcc
|
||||||
|
make install prefix=${PREFIX}
|
||||||
|
- name: Build Cytoplasm
|
||||||
|
run: |
|
||||||
|
alias musl-gcc="musl-gcc -Wl,-Bstatic -L ${PREFIX}/lib"
|
||||||
|
cd repos/cyto
|
||||||
|
git fetch
|
||||||
|
rm configure
|
||||||
|
wget https://kappach.at/configure
|
||||||
|
chmod +x configure
|
||||||
|
./configure --cc=musl-gcc --prefix=${PREFIX} --with-lmdb --with-mbed
|
||||||
|
make -j$(nproc)
|
||||||
|
make install
|
||||||
|
- name: Clone/Build Parsee
|
||||||
|
run: |
|
||||||
|
git clone https://git.kappach.at/${{ github.repository}} parsee
|
||||||
|
cd parsee
|
||||||
|
alias musl-gcc="musl-gcc -Wl,-Bstatic -L ${PREFIX}/lib"
|
||||||
|
musl-gcc -static configure.c -o configure && ./configure -s -l
|
||||||
|
make CC=musl-gcc CYTO_INC=${PREFIX}/include CYTO_LIB=${PREFIX}/lib LDFLAGS=-s
|
||||||
|
make PREFIX=bins install
|
||||||
|
- name: Create a final archive
|
||||||
|
run: |
|
||||||
|
cd parsee
|
||||||
|
echo "NAM=parsee-$(uname)-$(uname -m)" >> $GITHUB_ENV
|
||||||
|
echo "DIR=$PWD/bins" >> $GITHUB_ENV
|
||||||
|
- name: Upload it all(as a ZIP)
|
||||||
|
uses: https://code.forgejo.org/forgejo/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ env.NAM }}
|
||||||
|
path: ${{ env.DIR }}
|
||||||
|
compression-level: 9
|
||||||
41
.forgejo/workflows/check-gcc.yaml
Normal file
41
.forgejo/workflows/check-gcc.yaml
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
name: Checks Parsee's correctness on GCC+Debian
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
compile:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
distro:
|
||||||
|
- debian
|
||||||
|
arch:
|
||||||
|
- amd64
|
||||||
|
runs-on:
|
||||||
|
- ${{ matrix.distro }}
|
||||||
|
- ${{ matrix.arch }}
|
||||||
|
container:
|
||||||
|
image: ${{ matrix.distro }}
|
||||||
|
steps:
|
||||||
|
- name: Install LMDB+OpenSSL tools
|
||||||
|
run: |
|
||||||
|
echo $(uname -a) $(env | grep container)
|
||||||
|
apt update -y
|
||||||
|
apt install -y build-essential liblmdb-dev nodejs libssl-dev git
|
||||||
|
- name: Clone/Configure Cytoplasm
|
||||||
|
run: |
|
||||||
|
git clone https://git.kappach.at/KappaChat/Cytoplasm --depth=1
|
||||||
|
cd Cytoplasm
|
||||||
|
./configure --with-lmdb --prefix=/usr
|
||||||
|
- name: Build Cytoplasm
|
||||||
|
run: 'cd Cytoplasm && make -j$(nproc)'
|
||||||
|
- name: Install Cytoplasm
|
||||||
|
run: 'cd Cytoplasm && make install'
|
||||||
|
- name: Clone Parsee
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Build Parsee
|
||||||
|
run: |
|
||||||
|
cc configure.c -o configure && ./configure
|
||||||
|
echo 'CFLAGS=-Werror -Wall -Wextra -pedantic -fanalyzer' >> build.conf
|
||||||
|
cat build.conf
|
||||||
|
make && make ayadoc
|
||||||
|
- name: Install Parsee
|
||||||
|
run: 'make install'
|
||||||
|
|
||||||
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -4,11 +4,29 @@ parsee*
|
||||||
parsee
|
parsee
|
||||||
*.swp
|
*.swp
|
||||||
.*
|
.*
|
||||||
data
|
data*
|
||||||
data/*
|
data*/*
|
||||||
|
Makefile
|
||||||
|
configure
|
||||||
|
gmon.out
|
||||||
|
|
||||||
tools/out
|
tools/out
|
||||||
tools/out/*
|
tools/out/*
|
||||||
|
|
||||||
ayaya/*
|
ayaya/*
|
||||||
ayaya
|
ayaya
|
||||||
|
|
||||||
|
#ctags
|
||||||
|
tags
|
||||||
|
|
||||||
|
# Whitelists
|
||||||
|
!etc/*
|
||||||
|
!etc/**
|
||||||
|
!.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
|
||||||
84
CHANGELOG.md
Normal file
84
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
# Parsee changelogs
|
||||||
|
|
||||||
|
This document holds changes logged between versions, ever
|
||||||
|
since `tomboyish-bridges-adventure`'s release.
|
||||||
|
Dates are to be written as DD/MM/YYYY. Please update the
|
||||||
|
changelog as you go, no one wants to keep track of every
|
||||||
|
commit done between releases.
|
||||||
|
|
||||||
|
## Release
|
||||||
|
*There is currently no full releases of Parsee*
|
||||||
|
|
||||||
|
## Beta
|
||||||
|
*There is currently no beta releases of Parsee*
|
||||||
|
|
||||||
|
## Alpha
|
||||||
|
|
||||||
|
### v0.3.0[lunar-rainbow] <XX/XX/2025>
|
||||||
|
This is the first release of 2025!
|
||||||
|
TBD
|
||||||
|
#### New things
|
||||||
|
- Allow admins to remove users from the nofly list.
|
||||||
|
- Parsee can now access the homeserver/component locally (rather than over the network)
|
||||||
|
- The endpoint for media has been changed to '/media/[SERV]/[ID]?hmac=...'
|
||||||
|
- Add parsee-plumb tool to manage plumbing from outside Parsee if it is not running.
|
||||||
|
- Add packaging for Guix (see #18)
|
||||||
|
- Parsee is now dependent on authenticated media to function.
|
||||||
|
(Please, Matrix, do _not_ touch anything, I do _not_ want to mess with this anymore)
|
||||||
|
#### Bugfixes
|
||||||
|
- Fix potential infinite loops when processing some messages.
|
||||||
|
- Parsee now handles pinging puppets from Matrix more sanely.
|
||||||
|
|
||||||
|
### v0.2.0[star-of-hope] <8/11/2024>
|
||||||
|
Fixes some media metadata things, replaces the build system,
|
||||||
|
tries out avatar support some more, MUC contexts, and speeds
|
||||||
|
up Parsee just a bit. MbedTLS support is still highly unstable.
|
||||||
|
#### New things
|
||||||
|
- Start dealing with some basic PEP and vCard-based avatar
|
||||||
|
support from both sides.
|
||||||
|
- Banning Parsee from a XMPP MUC effectively unlinks it from
|
||||||
|
the MUC.
|
||||||
|
- Start adding basic documentation to Parsee, through the
|
||||||
|
wiki page.
|
||||||
|
- Add MUC whitelists for plumbing, alongside a `whitelist` tool
|
||||||
|
- Add parameter for setting the max stanza size allowed, with
|
||||||
|
the default being the XMPP minimum of 10000 bytes.
|
||||||
|
- Allows experimental MbedTLS through a specific Cytoplasm
|
||||||
|
patch (though still unstable AND slow).
|
||||||
|
- Does basic work towards NetBSD support(especially with DEC Alpha)
|
||||||
|
- Start contextualising XMPP commands(all/Parsee admins/MUCs).
|
||||||
|
|
||||||
|
#### Bugfixes
|
||||||
|
- Adds more information to media events so that clients can
|
||||||
|
behave.
|
||||||
|
- Fixes issues where SIGPIPE can actually just kill Parsee.
|
||||||
|
- "Lone" XMPP messages no longer render weirdly on Element
|
||||||
|
Android's weird rendering.
|
||||||
|
- Start fixing bug where Parsee takes several seconds to send
|
||||||
|
a message coming from XMPP with MbedTLS(it is still slow and
|
||||||
|
unstable)
|
||||||
|
- Fix issue where the XMPP server would kill Parsee if avatars
|
||||||
|
are too large.
|
||||||
|
- Refactor some of the code to abstract sending stanzas down
|
||||||
|
the wire to a specific function, thus allowing us to check
|
||||||
|
for certain conditions more easily(for example, verifying the
|
||||||
|
size on the fly, or having extensions being able to postprocess
|
||||||
|
the stanza).
|
||||||
|
- Parsee now stops when a stream error is received, instead of
|
||||||
|
being in a limbo state.
|
||||||
|
- The format for links has been changed to be slighlty *less*
|
||||||
|
annoying.
|
||||||
|
|
||||||
|
#### Deprecated features
|
||||||
|
- The old `build.c` and `Makefile`s used for building are removed,
|
||||||
|
and replaced by the `configure.c` C file(/script via TCC).
|
||||||
|
|
||||||
|
### v0.1.0[tomboyish-bridges-adventure] <9/9/2024>
|
||||||
|
Nothing much to say, but this is the first alpha release
|
||||||
|
of Parsee. May occasionally deadlock.
|
||||||
|
#### New things
|
||||||
|
*NONE*
|
||||||
|
#### Bugfixes
|
||||||
|
*NONE*
|
||||||
|
#### Deprecated features
|
||||||
|
*NONE*
|
||||||
14
CONTRIBUTORS
Normal file
14
CONTRIBUTORS
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Lines starting with a '#' are ignored.
|
||||||
|
# This is a list of people who have contributed to Parsee.
|
||||||
|
# You *may* add some information about yourself after having made a change
|
||||||
|
# to the repository(or wiki!), with those fields:
|
||||||
|
# (N) - A nickname
|
||||||
|
# (L) - An approximate place of residence
|
||||||
|
# (X) - An XMPP URI
|
||||||
|
# (M) - A Matrix MXID
|
||||||
|
# (D) - A short description about yourself
|
||||||
|
(N): LDA
|
||||||
|
(L): France
|
||||||
|
(X): xmpp:lda@monocles.eu
|
||||||
|
(M): @fourier:ari.lt
|
||||||
|
(M): @fourier:ari.lt
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
Some dates for Parsee-related events. They mostly serve as LDA's TODOs with
|
Some dates for Parsee-related events. They mostly serve as LDA's TODOs with
|
||||||
a strict deadline:
|
a strict deadline:
|
||||||
- ~September 2024[star-of-hope]:
|
- Get star-of-hope out by November 8
|
||||||
Get Parsee into the _Phantasmagoria of Bug View_ stage (essentially
|
|
||||||
v0.0.1 for a public testing) once I can afford `yama`, and start
|
|
||||||
bridging the Matrix room alongside a shiny XMPP MUC, bridged by
|
|
||||||
yours truly.
|
|
||||||
|
|
|
||||||
6
LICENSE
6
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
For the files src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile, see COPYING.CC0
|
For the files src/include/Unistring.h, src/Unistr.h rc/Parsee/HMAC.c, src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile,
|
||||||
to be present.
|
see COPYING.CC0.
|
||||||
For any other file in src/, see COPYING.AGPL as the primary license.
|
For any other file in src/, see COPYING.AGPL as the primary license(AGPL-3.0-or-later)
|
||||||
|
|
||||||
As Parsee depends on Cytoplasm, its license is left here in COPYING.CYTO
|
As Parsee depends on Cytoplasm, its license is left here in COPYING.CYTO
|
||||||
|
|
||||||
|
|
|
||||||
89
Makefile
89
Makefile
|
|
@ -1,89 +0,0 @@
|
||||||
# Makefile for building Parsee
|
|
||||||
# ================================
|
|
||||||
# TODO: Consider making something akin to a configure script that checks
|
|
||||||
# for dependencies, or maybe even use *autoconf* (the devil!)
|
|
||||||
|
|
||||||
|
|
||||||
# =========================== Parsee Flags =============================
|
|
||||||
|
|
||||||
# phantasmagoria - test runs without an actual code
|
|
||||||
CODE=phantasmagoria
|
|
||||||
NAME=Parsee
|
|
||||||
VERSION=0.0.0
|
|
||||||
|
|
||||||
REPOSITORY=$(shell git remote get-url origin)
|
|
||||||
|
|
||||||
# =========================== Compilation Flags ============================
|
|
||||||
CYTO_INC ?=/usr/local/include/ # Where Cytoplasm's include path is
|
|
||||||
# located.
|
|
||||||
CYTO_LIB ?=/usr/local/lib # Where's Cytoplasm's library is
|
|
||||||
# located.
|
|
||||||
PREFIX ?=/usr/local
|
|
||||||
|
|
||||||
SOURCE=src
|
|
||||||
OBJECT=build
|
|
||||||
AYAS=ayaya
|
|
||||||
ETC=etc
|
|
||||||
INCLUDES=src/include
|
|
||||||
CC=cc
|
|
||||||
CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -g -ggdb -Wall -Werror
|
|
||||||
LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -g -ggdb
|
|
||||||
AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)"
|
|
||||||
BINARY=parsee
|
|
||||||
# ============================ Compilation =================================
|
|
||||||
SRC_FILES:=$(shell find $(SOURCE) -name '*.c')
|
|
||||||
OBJ_FILES:=${subst $(SOURCE)/,$(OBJECT)/,$(patsubst %.c, %.o, $(SRC_FILES))}
|
|
||||||
|
|
||||||
CPP_FILES:=$(shell find $(INCLUDES) -name '*.h')
|
|
||||||
AYA_FILES:=${subst $(INCLUDES)/,$(AYAS)/,$(patsubst %.h, %.html, $(CPP_FILES))}
|
|
||||||
|
|
||||||
all: binary utils
|
|
||||||
|
|
||||||
binary: $(OBJ_FILES)
|
|
||||||
$(CC) $(LDFLAGS) $(OBJ_FILES) -o $(BINARY)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(OBJECT) $(BINARY) $(AYAS)
|
|
||||||
|
|
||||||
$(OBJECT)/%.o: $(SOURCE)/%.c
|
|
||||||
@mkdir -p $(shell dirname "$@")
|
|
||||||
$(CC) -c $(CFLAGS) $< -o $@
|
|
||||||
|
|
||||||
utils:
|
|
||||||
(cd tools && make)
|
|
||||||
|
|
||||||
ayadoc: utils $(AYA_FILES)
|
|
||||||
|
|
||||||
$(AYAS)/%.html: $(INCLUDES)/%.h
|
|
||||||
@mkdir -p $(shell dirname "$@")
|
|
||||||
tools/out/aya $(AFLAGS) -i $< -o $@
|
|
||||||
|
|
||||||
|
|
||||||
# Installs everything.
|
|
||||||
install: binary utils ayadoc install_setup install_parsee install_tools install_aya install_man
|
|
||||||
@echo Installed $(NAME) to $(PREFIX)!
|
|
||||||
|
|
||||||
install_setup:
|
|
||||||
install -dm755 "$(PREFIX)/bin"
|
|
||||||
install -dm755 "$(PREFIX)/share/doc"
|
|
||||||
install -dm755 "$(PREFIX)/man"
|
|
||||||
|
|
||||||
install_parsee:
|
|
||||||
install -Dm755 "$(BINARY)" "$(PREFIX)/bin/$(BINARY)"
|
|
||||||
|
|
||||||
TOOLS:=$(shell find 'tools/out' -name '*')
|
|
||||||
ITOOL:=${subst tools/out/,$(PREFIX)/bin/,$(patsubst tools/out/%, tools/out/$(BINARY)-%, $(TOOLS))}
|
|
||||||
install_tools: $(ITOOL)
|
|
||||||
$(PREFIX)/bin/$(BINARY)-%: tools/out/%
|
|
||||||
install -Dm755 "$<" "$@"
|
|
||||||
|
|
||||||
IHTML:=${subst $(AYAS)/,$(PREFIX)/share/doc/$(BINARY)/,$(AYA_FILES)}
|
|
||||||
install_aya: $(IHTML)
|
|
||||||
$(PREFIX)/share/doc/$(BINARY)/%: $(AYAS)/%
|
|
||||||
install -Dm644 "$<" "$@"
|
|
||||||
|
|
||||||
MPAGE:=$(shell find 'etc/man' -name '*.*')
|
|
||||||
PPAGE:=${subst etc/man/,$(PREFIX)/man/,$(MPAGE)}
|
|
||||||
install_man: $(PPAGE)
|
|
||||||
$(PREFIX)/man/%: etc/man/%
|
|
||||||
install -Dm644 "$<" "$@"
|
|
||||||
85
README.MD
85
README.MD
|
|
@ -1,39 +1,54 @@
|
||||||
# Parsee - the jealous XMPP<=>Matrix bridge
|
# Parsee - the jealous XMPP<=>Matrix bridge
|
||||||
Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is NOT a drop-in replacment.
|
Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is
|
||||||
|
NOT a drop-in replacment.
|
||||||
|
Currently, it is *alpha* stage, which means that I wouldn't recommend using this in production,
|
||||||
|
as I can change anything, at any time, and it may behave strangely at times.
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
### Naming
|
### Naming
|
||||||
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi), a
|
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi),
|
||||||
"*bridge* princess".
|
a "*bridge* princess". The other name you actually can sometimes see explains itself, so I won't
|
||||||
|
be talking about it.
|
||||||
|
|
||||||
### Reasoning (personal to LDA)
|
### Reasoning (personal to LDA)
|
||||||
I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister project to KappaChat,
|
I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister
|
||||||
this means that I can integrate Parsee with KappaChat however I wish it to be, which allows me to mess around with a
|
project to KappaChat, this means that I can integrate Parsee with KappaChat however I wish it
|
||||||
codebase I'm already familiar with.
|
to be, which allows me to mess around with a codebase I'm already familiar with.
|
||||||
A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*.
|
A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*,
|
||||||
Well, I'm *trying* to do that, at least. Please scream at me if that fails(or just doesn't run
|
and maybe as a testing ground for Cytoplasm features I sometimes add.
|
||||||
on a overclocked Raspberry Pi 4B, which, by the way, was literally where Parsee+XMPP ran for
|
|
||||||
a good chunk of Parsee's start.)
|
(Well, I'm *trying* to do that, at least.
|
||||||
|
Please scream at me if that fails(or just doesn't run on a overclocked Raspberry Pi 4B))
|
||||||
|
|
||||||
### "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.
|
||||||
|
|
||||||
## BUILDING
|
## BUILDING
|
||||||
```sh
|
```sh
|
||||||
$ make # This generates a 'parsee' executable.
|
$ cc configure.c -o configure # that or use tcc -run to consolidate these two steps.
|
||||||
$ cd tools # If you want to build more tools
|
$ ./configure # use -s if you want static Parsee+MbedTLS, use -s -l if LMDB is needed
|
||||||
$ make && cd ..
|
$ make
|
||||||
$ make ayadoc # If you want to build HTML documentation
|
$ make [PREFIX=...] install # run as root if on a protected dir like /usr
|
||||||
$ make [PREFIX=(install path)] install # To install Parsee.
|
|
||||||
```
|
```
|
||||||
If there are any Cytoplasm-related build failures, you may want to check the Makefile to
|
If there are any Cytoplasm-related build failures, you may want to check the Makefile to
|
||||||
change a few variables (you can set `CYTO_INC` and `CYTO_LIB`)
|
change a few variables (you can set `CYTO_INC` and `CYTO_LIB` for Cytoplasm's include and
|
||||||
|
library paths specifically.)
|
||||||
|
If you build with MbedTLS, please mind setting the `CYTO_TLS_CA` env to Parsee.
|
||||||
|
|
||||||
### DEPENDENCIES
|
### DEPENDENCIES
|
||||||
Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendria.io/Telodendria/Cytoplasm). Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but you can get away without those).
|
Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendria.io/Telodendria/Cytoplasm).
|
||||||
|
Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but
|
||||||
|
you can get away without those if you're adventurous).
|
||||||
|
|
||||||
|
### BUILDING WITH GUIX
|
||||||
|
If you have [Guix](https://guix.gnu.org/) installed, you can build Parsee using `guix package -f guix.scm`, or test it
|
||||||
|
using `guix shell -f guix.scm`. You can also generate a Docker/OCI image.
|
||||||
|
```sh
|
||||||
|
guix pack -f docker -S /bin=bin -L.guix/modules parsee
|
||||||
|
```
|
||||||
|
|
||||||
## RUNNING
|
## 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
|
||||||
|
|
@ -46,7 +61,8 @@ parsee-config \
|
||||||
-H 'blow.hole' \ # Matrix homeserver name
|
-H 'blow.hole' \ # Matrix homeserver name
|
||||||
-J 'parsee.blow.hole' \ # XMPP component host, must be reachable
|
-J 'parsee.blow.hole' \ # XMPP component host, must be reachable
|
||||||
-s 'A very secure XMPP component secret' \
|
-s 'A very secure XMPP component secret' \
|
||||||
-p 5347
|
-p 5347 \
|
||||||
|
-M 65535 # Maximum stanza size. Stanzas larger than this from Parsee will be dropped to avoid stream closures. Leave this empty if you're unsure.
|
||||||
```
|
```
|
||||||
|
|
||||||
If everything goes well, it should generate a `parsee.json` file.
|
If everything goes well, it should generate a `parsee.json` file.
|
||||||
|
|
@ -59,31 +75,44 @@ returns with a landing page, then this side works. You can read it for some more
|
||||||
|
|
||||||
## DOCS
|
## DOCS
|
||||||
Currently, the main sources of documentation are the Ayadocs(for headers) and the manpages
|
Currently, the main sources of documentation are the Ayadocs(for headers) and the manpages
|
||||||
(see `etc/man`)
|
(see `etc/man`).
|
||||||
|
|
||||||
## TODOS
|
## TODOS before 1.0 rolls around
|
||||||
- Add [libomemo](https://github.com/gkdr/libomemo) as an optional dependency.
|
- Make Parsee actually go *vroooooooooommmmmmm*.
|
||||||
|
- Make sure Parsee can easily run on just about any reasonable POSIX
|
||||||
|
system.
|
||||||
|
- Avoid making 'back-puppets' from Matrix as much as possible
|
||||||
|
- Extension support. I'd need to design a good system, and maybe do it
|
||||||
|
with either shared libraries(`dlopen`/`dlclose` on POSIX) or use a
|
||||||
|
language like Janet or Lua.
|
||||||
|
- Add [libomemo](https://github.com/gkdr/libomemo) or something as an optional dependency.
|
||||||
- It depends on more stuff anyways, and I don't want to weigh down the
|
- It depends on more stuff anyways, and I don't want to weigh down the
|
||||||
dependency list of Parsee for that.
|
dependency list of Parsee for that.
|
||||||
- Matrix's libolm is deprecated. They replaced it with a Rust version that
|
- Matrix's libolm is deprecated. They replaced it with a Rust version that
|
||||||
pulls in *way too many* dependencies, and that lacks a C binding. We may
|
pulls in *way too many* dependencies, and that lacks a C binding. We may
|
||||||
put in the work of either forking off libolm or making a binding to KappaChat.
|
~~put in the work of either forking off libolm or~~ be making a binding with
|
||||||
|
KappaChat(when I get around to remaking UI :p).
|
||||||
|
- Josh did infact tell me that maybe C bindings may happen. I'd be
|
||||||
|
willing to help out, but IDK. In any case, this will at best be an
|
||||||
|
extension packagers may integrate properly.
|
||||||
- Get rid of the '?'-syntax and use another invalid Matrix char/valid XMPP char
|
- Get rid of the '?'-syntax and use another invalid Matrix char/valid XMPP char
|
||||||
('$'?) for escaped?
|
('$'?) for escaped?
|
||||||
- PROPER FUCKING AVATARS
|
|
||||||
XEP-0084 IS THE WORST PIECE OF SHIT KNOWN TO MAN. If any Jabberbros want to
|
|
||||||
look at terrible code/XML and suggest things to have *proper* avatar support,
|
|
||||||
I'm all in.
|
|
||||||
- Consider making room/MUC admins/owners be able to plumb instead of it being
|
- Consider making room/MUC admins/owners be able to plumb instead of it being
|
||||||
restricted to Parsee admins, with permission from MUC owners, too
|
restricted to Parsee admins, with permission from MUC owners, too
|
||||||
- Limiting to admins may be a way to "control" consent for both, but this is
|
- Limiting to admins may be a way to "control" consent for both, but this is
|
||||||
only if Parsee admins are good-willed, which we must assume such statment to
|
only if Parsee admins are good-willed, which we must assume such statment to
|
||||||
be false by default.
|
be false by default.
|
||||||
|
- Currently, MUC owners may kick Parsee out, with the effect of unlinking the
|
||||||
|
MUC.
|
||||||
- Look at XEPS-TBD.TXT for XEPs to be done
|
- Look at XEPS-TBD.TXT for XEPs to be done
|
||||||
- Add a MUC server to Parsee, such that it may be able to hook onto it and therefore
|
- Add a MUC server to Parsee, such that it may be able to hook onto it and therefore
|
||||||
support XMPP->Matrix bridging.
|
support XMPP->Matrix bridging.
|
||||||
- Manage MUC DMs in a reasonable manner. Thanks `@freeoffers4u:matrix.org` for being
|
- Manage MUC DMs in a reasonable manner. Thanks `@freeoffers4u:matrix.org` for being
|
||||||
a fucking annoyance and DMing an old Parsee semi-anon user for no clear reason.
|
a fucking annoyance and DMing an old Parsee semi-anon user for no clear reason.
|
||||||
|
- Make Parsee cope with stream closures(i.e: XMPP server turning off) better. As of
|
||||||
|
now, it just kills itself when that happens, instead of trying to negociate a new
|
||||||
|
connection, which would be a better method that would actually fit Parsee's own
|
||||||
|
principles.
|
||||||
|
|
||||||
## DONATING/CONTRIBUTING
|
## DONATING/CONTRIBUTING
|
||||||
If you know things about XMPP or Matrix, yet aren't familiar with C99, or just
|
If you know things about XMPP or Matrix, yet aren't familiar with C99, or just
|
||||||
|
|
@ -100,7 +129,7 @@ You may also donate to [the LiberaPay](https://en.liberapay.com/Parsee), alongsi
|
||||||
currently maintaining Cytoplasm.
|
currently maintaining Cytoplasm.
|
||||||
|
|
||||||
## IM chats
|
## IM chats
|
||||||
Please avoid asking for help/issues here. If you *really* want, you may just open
|
Please avoid asking for help/issues there. If you *really* want, you may just open
|
||||||
an issue and link it over it. Basic respect for others/not being an asshat is required.
|
an issue and link it over it. Basic respect for others/not being an asshat is required.
|
||||||
(Also, these are temporary room aliases.)
|
(Also, these are temporary room aliases.)
|
||||||
- [#parsee:tedomum.net](https://matrix.to/#/%23parsee:tedomum.net)
|
- [#parsee:tedomum.net](https://matrix.to/#/%23parsee:tedomum.net)
|
||||||
|
|
|
||||||
42
XEPS-TBD.TXT
42
XEPS-TBD.TXT
|
|
@ -1,6 +1,19 @@
|
||||||
XEPs current supported are in src/XMPPThread, at the IQ disco advertising.
|
XEPs current supported are in src/XMPPThread, at the IQ disco advertising.
|
||||||
|
|
||||||
Somewhat implemented XEPs:
|
Somewhat implemented XEPs:
|
||||||
|
v https://xmpp.org/extensions/xep-0050.html
|
||||||
|
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
||||||
|
also a nice to have.
|
||||||
|
There are commands, but not a lot of them as of now, and localisation
|
||||||
|
is missing.
|
||||||
|
v https://xmpp.org/extensions/xep-0421.html
|
||||||
|
Using the occupant ID in semi-anonymous MUCs is a desirable property.
|
||||||
|
I dont know of a lot of places that don't use the occupant ID anymore
|
||||||
|
within Parsee.
|
||||||
|
v "also it [Bifrost] doesn't respect voice either"
|
||||||
|
As far as I am aware, works.
|
||||||
|
v https://xmpp.org/extensions/xep-0425.html
|
||||||
|
As mentionned in #2, moderation _needs_ to be done.
|
||||||
~ https://xmpp.org/extensions/xep-0085.html
|
~ https://xmpp.org/extensions/xep-0085.html
|
||||||
Only XMPP->Matrix at the moment. Still need to figure out how to
|
Only XMPP->Matrix at the moment. Still need to figure out how to
|
||||||
get typing indicators as an AS.
|
get typing indicators as an AS.
|
||||||
|
|
@ -8,21 +21,14 @@ Somewhat implemented XEPs:
|
||||||
This allows reactions, which Matrix also has support to. The two
|
This allows reactions, which Matrix also has support to. The two
|
||||||
systems don't seem *too* restrictive on one-another (unlike some
|
systems don't seem *too* restrictive on one-another (unlike some
|
||||||
IM platforms I won't mention), so this doesn't sound too bad to do
|
IM platforms I won't mention), so this doesn't sound too bad to do
|
||||||
HALF-IMPLEMENTED: Removing reacts won't work.
|
TODO: Add support from Matrix.
|
||||||
~ https://xmpp.org/extensions/xep-0184.html
|
~ https://xmpp.org/extensions/xep-0184.html
|
||||||
Only Matrix->XMPP as of now. Requesting data from Matrix ASes without
|
Only Matrix->XMPP as of now. Requesting data from Matrix ASes without
|
||||||
/sync seems like a non-option as of now, which _sucks_.
|
/sync seems like a non-option as of now, which _sucks_.
|
||||||
~ https://xmpp.org/extensions/xep-0050.html
|
~ https://xmpp.org/extensions/xep-0084.html
|
||||||
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
Avatar support would be extremely useful, if just a QoL improvment.
|
||||||
also a nice to have.
|
Matrix and XMPP both have support for these.
|
||||||
There are commands, but not a lot of them as of now, and localisation
|
XEP-0084 is a pain in the ass to implement and seems generally just
|
||||||
is missing.
|
|
||||||
~ https://xmpp.org/extensions/xep-0421.html
|
|
||||||
Using the occupant ID in semi-anonymous MUCs is a desirable property.
|
|
||||||
I dont know of a lot of places that don't use the occupant ID anymore
|
|
||||||
within Parsee.
|
|
||||||
~ https://xmpp.org/extensions/xep-0425.html
|
|
||||||
As mentionned in #2, moderation _needs_ to be done.
|
|
||||||
|
|
||||||
For future XEPs:
|
For future XEPs:
|
||||||
- https://xmpp.org/extensions/xep-0449.html
|
- https://xmpp.org/extensions/xep-0449.html
|
||||||
|
|
@ -32,22 +38,16 @@ For future XEPs:
|
||||||
which is used along PEP, it seems, and meanwhile Matrix has ''support''
|
which is used along PEP, it seems, and meanwhile Matrix has ''support''
|
||||||
for packs too, tracking them is between "annoyance" and "yeah, no.".
|
for packs too, tracking them is between "annoyance" and "yeah, no.".
|
||||||
|
|
||||||
ON STANDBY BECAUSE THESE HAVE BEEN TERRIBLE TO DEAL WITH AND WHO KEEPS WRITING
|
On Standby:
|
||||||
THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT:
|
|
||||||
x https://xmpp.org/extensions/xep-0084.html
|
|
||||||
Avatar support would be extremely useful, if just a QoL improvment.
|
|
||||||
Matrix and XMPP both have support for these.
|
|
||||||
XEP-0084 is a pain in the ass to implement and seems generally just
|
|
||||||
unreliable, however.
|
unreliable, however.
|
||||||
x https://xmpp.org/extensions/xep-0080.html
|
x https://xmpp.org/extensions/xep-0080.html
|
||||||
Can't think of a good analogy to these...
|
Can't think of a good analogy to these...
|
||||||
|
|
||||||
|
|
||||||
Not XEPs, but ideas that _needs_ to be added:
|
Not XEPs, but ideas that _needs_ to be added:
|
||||||
v "also it [Bifrost] doesn't respect voice either" -> Send a form on moderated
|
|
||||||
MUCs (which is standard, so its not too bad!). Currently WIP, and barely tested.
|
|
||||||
~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d
|
~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d
|
||||||
- Standalone/Static Parsee, ideally as small as it can be(if not as APE).
|
Happens on Matrix. I'll need to handle that on XMPP as well.
|
||||||
|
~ Standalone/Static Parsee, ideally as small as it can be(if not as APE).
|
||||||
- Kappa-like extension system(maybe bridging more than just Matrix-XMPP.)
|
- Kappa-like extension system(maybe bridging more than just Matrix-XMPP.)
|
||||||
- https://www.youtube.com/watch?v=InL414iDZmY
|
- https://www.youtube.com/watch?v=InL414iDZmY
|
||||||
|
|
||||||
|
|
|
||||||
10
build.conf
Normal file
10
build.conf
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
CODE=lunar-rainbow
|
||||||
|
NAME=Parsee
|
||||||
|
VERSION=0.3.0
|
||||||
|
BINARY=parsee
|
||||||
|
SOURCE=src
|
||||||
|
INCLUDES=src/include
|
||||||
|
OBJECT=build
|
||||||
|
CC=cc
|
||||||
|
CFLAGS=-O3
|
||||||
|
PREFIX=/usr
|
||||||
727
configure.c
Normal file
727
configure.c
Normal file
|
|
@ -0,0 +1,727 @@
|
||||||
|
/* configure.c - Simple, POSIX, non-Cytoplasm utility to build out
|
||||||
|
* a Parsee Makefile.
|
||||||
|
* To run it, just build it with:
|
||||||
|
* cc configure.c -o configure && configure
|
||||||
|
* --------------------------------
|
||||||
|
* LICENSE: CC0
|
||||||
|
* Written-By: LDA [lda@freetards.xyz] [@fourier:ari.lt] */
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
static char *
|
||||||
|
string_rep_ext(char *in, char *ext1, char *ext2)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
size_t inLen;
|
||||||
|
if (!in || !ext1 || !ext2)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
inLen = strlen(in);
|
||||||
|
end = inLen + in;
|
||||||
|
|
||||||
|
while (end >= in)
|
||||||
|
{
|
||||||
|
if (!strcmp(end, ext1))
|
||||||
|
{
|
||||||
|
size_t cpyLen = end - in;
|
||||||
|
size_t extLen = strlen(ext2);
|
||||||
|
char *ret = malloc(cpyLen + extLen + 1);
|
||||||
|
|
||||||
|
memcpy(ret, in, cpyLen);
|
||||||
|
memcpy(ret + cpyLen, ext2, extLen);
|
||||||
|
ret[cpyLen + extLen] = '\0';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
static char *
|
||||||
|
string_dup(char *in)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
size_t len;
|
||||||
|
if (!in)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
len = strlen(in);
|
||||||
|
out = malloc(len + 1);
|
||||||
|
memcpy(out, in, len);
|
||||||
|
out[len] = '\0';
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
static char *
|
||||||
|
string_cat(char *in1, char *in2)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
size_t len1, len2;
|
||||||
|
if (!in1)
|
||||||
|
{
|
||||||
|
return string_dup(in2);
|
||||||
|
}
|
||||||
|
if (!in2)
|
||||||
|
{
|
||||||
|
return string_dup(in1);
|
||||||
|
}
|
||||||
|
|
||||||
|
len1 = strlen(in1);
|
||||||
|
len2 = strlen(in2);
|
||||||
|
out = malloc(len1 + len2 + 1);
|
||||||
|
memcpy(out, in1, len1);
|
||||||
|
memcpy(out + len1, in2, len2);
|
||||||
|
out[len1 + len2] = '\0';
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct str_array {
|
||||||
|
char **values;
|
||||||
|
size_t quantity;
|
||||||
|
} str_array_t;
|
||||||
|
static str_array_t *
|
||||||
|
str_array_create(void)
|
||||||
|
{
|
||||||
|
str_array_t *ret = malloc(sizeof(*ret));
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret->values = NULL;
|
||||||
|
ret->quantity = 0;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
str_array_free(str_array_t *arr)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!arr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < arr->quantity; i++)
|
||||||
|
{
|
||||||
|
if (arr->values[i]) free(arr->values[i]);
|
||||||
|
}
|
||||||
|
if (arr->values) free(arr->values);
|
||||||
|
free(arr);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
str_array_set(str_array_t *arr, size_t i, char *str)
|
||||||
|
{
|
||||||
|
if (!arr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= arr->quantity)
|
||||||
|
{
|
||||||
|
size_t size = (i+1) * sizeof(*arr->values);
|
||||||
|
size_t j;
|
||||||
|
arr->values = realloc(arr->values, size);
|
||||||
|
for (j = arr->quantity; j <= i; j++)
|
||||||
|
{
|
||||||
|
arr->values[j] = NULL;
|
||||||
|
}
|
||||||
|
arr->quantity = i + 1 ;
|
||||||
|
}
|
||||||
|
if (arr->values[i]) free(arr->values[i]);
|
||||||
|
arr->values[i] = string_dup(str);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
str_array_add(str_array_t *arr, char *str)
|
||||||
|
{
|
||||||
|
if (!arr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
str_array_set(arr, arr->quantity, str);
|
||||||
|
}
|
||||||
|
static char *
|
||||||
|
str_array_get(str_array_t *arr, size_t i)
|
||||||
|
{
|
||||||
|
if (!arr)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return i < arr->quantity ? arr->values[i] : NULL;
|
||||||
|
}
|
||||||
|
static size_t
|
||||||
|
str_array_len(str_array_t *arr)
|
||||||
|
{
|
||||||
|
return arr ? arr->quantity : 0;
|
||||||
|
}
|
||||||
|
static char *
|
||||||
|
cmd_stdout(char *cmd)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
char *line = NULL;
|
||||||
|
size_t size;
|
||||||
|
int result;
|
||||||
|
if (!cmd)
|
||||||
|
{
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
if (!(f = popen(cmd, "r")))
|
||||||
|
{
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
getline(&line, &size, f);
|
||||||
|
result = pclose(f);
|
||||||
|
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
perror("pclose(3)");
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
else if (result)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "command exited with status %d: %s\n", result, cmd);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
free(line);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
strchrn(char *s, char c)
|
||||||
|
{
|
||||||
|
s = strchr(s, c);
|
||||||
|
return s ? s+1 : NULL;
|
||||||
|
}
|
||||||
|
static str_array_t *
|
||||||
|
split(char *dir)
|
||||||
|
{
|
||||||
|
str_array_t *ret;
|
||||||
|
char *start;
|
||||||
|
if (!dir || strlen(dir) >= PATH_MAX - 1)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = str_array_create();
|
||||||
|
for (start = dir; start; start = strchrn(start, '/'))
|
||||||
|
{
|
||||||
|
char subtmp[PATH_MAX];
|
||||||
|
char *next = strchr(start, '/');
|
||||||
|
if (!next)
|
||||||
|
{
|
||||||
|
next = start + strlen(start);
|
||||||
|
}
|
||||||
|
memcpy(subtmp, start, next - start);
|
||||||
|
subtmp[next - start] = '\0';
|
||||||
|
|
||||||
|
str_array_add(ret, subtmp);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static str_array_t *
|
||||||
|
collect_sources(char *dir, bool head, char *ext)
|
||||||
|
{
|
||||||
|
DIR *handle;
|
||||||
|
str_array_t *ret;
|
||||||
|
struct dirent *ent;
|
||||||
|
if (!dir)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = str_array_create();
|
||||||
|
handle = opendir(dir);
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
printf("error: cannot open directory '%s'\n", dir);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
while ((ent = readdir(handle)))
|
||||||
|
{
|
||||||
|
char *name = ent->d_name;
|
||||||
|
|
||||||
|
if (!strcmp(name, ".") || !strcmp(name, "..")) continue;
|
||||||
|
|
||||||
|
if (strlen(name) > strlen(ext) &&
|
||||||
|
!strcmp(name + strlen(name) - strlen(ext), ext))
|
||||||
|
{
|
||||||
|
char *d1 = string_cat(dir, "/");
|
||||||
|
char *na = string_cat(d1, name);
|
||||||
|
str_array_add(ret, na);
|
||||||
|
free(d1);
|
||||||
|
free(na);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
char *d1 = string_cat(dir, "/");
|
||||||
|
char *d2 = string_cat(d1, name);
|
||||||
|
size_t i;
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if (stat(d2, &sb))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "stat(2) %s: %s\n", d2, strerror(errno));
|
||||||
|
free(d2);
|
||||||
|
free(d1);
|
||||||
|
}
|
||||||
|
else if (S_ISDIR(sb.st_mode))
|
||||||
|
{
|
||||||
|
str_array_t *sub = collect_sources(d2, false, ext);
|
||||||
|
for (i = 0; i < str_array_len(sub); i++)
|
||||||
|
{
|
||||||
|
char *file = str_array_get(sub, i);
|
||||||
|
str_array_add(ret, file);
|
||||||
|
}
|
||||||
|
str_array_free(sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(d2);
|
||||||
|
free(d1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(handle);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Builds the entirety of Parsee. */
|
||||||
|
static void
|
||||||
|
write_objects(FILE *makefile, str_array_t *sources)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!makefile || !sources)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < str_array_len(sources); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(sources, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".c", ".o");
|
||||||
|
char *obj = string_cat("build", ofl + 3);
|
||||||
|
|
||||||
|
fprintf(makefile, " %s", obj);
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
write_images(FILE *makefile, str_array_t *sources)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!makefile || !sources)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < str_array_len(sources); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(sources, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".png", ".o");
|
||||||
|
char *obj = string_cat("build", ofl + 3 + 1 + 5);
|
||||||
|
|
||||||
|
fprintf(makefile, " %s", obj);
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
write_executable(FILE *makefile, str_array_t *sources)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!makefile || !sources)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < str_array_len(sources); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(sources, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".c", "");
|
||||||
|
char *obj = string_cat("tools/out", ofl + 5);
|
||||||
|
|
||||||
|
fprintf(makefile, " %s", obj);
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
write_ayas(FILE *makefile, str_array_t *sources)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!makefile || !sources)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i = 0; i < str_array_len(sources); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(sources, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".h", ".html");
|
||||||
|
char *obj = string_cat("$(AYAYAS)", ofl + 3 + 1 + 7);
|
||||||
|
|
||||||
|
fprintf(makefile, " %s", obj);
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
analyse_dependencies(FILE *makefile, char *src)
|
||||||
|
{
|
||||||
|
FILE *source = fopen(src, "r");
|
||||||
|
char *lineptr = NULL;
|
||||||
|
char *dn = strdup(src), *dirn;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
dirn = dirname(dn);
|
||||||
|
|
||||||
|
while (getline(&lineptr, &len, source) != -1)
|
||||||
|
{
|
||||||
|
char *corrptr = lineptr, *nl, *chevron, *quote, *cutoff;
|
||||||
|
char *basedir, *srcdir, *incdir, *tmp;
|
||||||
|
struct stat statinfo;
|
||||||
|
|
||||||
|
/* try to parse the line */
|
||||||
|
if ((nl = strchr(corrptr, '\n')))
|
||||||
|
{
|
||||||
|
*nl = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*corrptr && isblank(*corrptr))
|
||||||
|
{
|
||||||
|
corrptr++;
|
||||||
|
}
|
||||||
|
if (strncmp(corrptr, "#include \"", 10) &&
|
||||||
|
strncmp(corrptr, "#include <", 10))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
corrptr += 10;
|
||||||
|
chevron = strchr(corrptr, '>');
|
||||||
|
chevron = chevron ? chevron : corrptr + strlen(corrptr);
|
||||||
|
quote = strchr(corrptr, '"');
|
||||||
|
quote = quote ? quote : corrptr + strlen(corrptr);
|
||||||
|
cutoff = chevron < quote ? chevron : quote;
|
||||||
|
|
||||||
|
*cutoff = '\0';
|
||||||
|
|
||||||
|
/* if we found something, try to resolve it */
|
||||||
|
tmp = string_cat(dirn, "/");
|
||||||
|
basedir = string_cat(tmp, corrptr);
|
||||||
|
free(tmp);
|
||||||
|
if (!stat(basedir, &statinfo))
|
||||||
|
{
|
||||||
|
fprintf(makefile, " %s", basedir);
|
||||||
|
free(basedir);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free(basedir);
|
||||||
|
|
||||||
|
srcdir = string_cat("src/", corrptr);
|
||||||
|
if (!stat(srcdir, &statinfo))
|
||||||
|
{
|
||||||
|
fprintf(makefile, " %s", srcdir);
|
||||||
|
free(srcdir);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free(srcdir);
|
||||||
|
|
||||||
|
incdir = string_cat("src/include/", corrptr);
|
||||||
|
if (!stat(incdir, &statinfo))
|
||||||
|
{
|
||||||
|
fprintf(makefile, " %s", incdir);
|
||||||
|
free(incdir);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
free(incdir);
|
||||||
|
}
|
||||||
|
free(lineptr);
|
||||||
|
free(dn);
|
||||||
|
fclose(source);
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
main_build(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
FILE *makefile;
|
||||||
|
char *repo = cmd_stdout("git remote get-url origin");
|
||||||
|
size_t i;
|
||||||
|
bool with_static = false, with_lmdb = false;
|
||||||
|
int opt;
|
||||||
|
str_array_t *sources, *images, *utils, *aya;
|
||||||
|
|
||||||
|
if (repo)
|
||||||
|
{
|
||||||
|
char *lf = strchr(repo, '\n');
|
||||||
|
if (lf)
|
||||||
|
{
|
||||||
|
*lf = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
repo = strdup("N/A");
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "sl")) != -1)
|
||||||
|
{
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
with_static = true;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
with_lmdb = with_static;
|
||||||
|
with_static = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makefile = fopen("Makefile", "w");
|
||||||
|
if (!makefile)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Couldn't create Makefile.\n");
|
||||||
|
fprintf(stderr, "This isn't good, actually.\n");
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
fprintf(makefile, "# Autogenerated POSIX Makefile from Parsee\n");
|
||||||
|
fprintf(makefile, "# Ideally do not touch, unless you have a very ");
|
||||||
|
fprintf(makefile, "good reason to do it. \n\n");
|
||||||
|
fprintf(makefile, ".POSIX: \n");
|
||||||
|
fprintf(makefile, "include build.conf\n\n");
|
||||||
|
fprintf(makefile, "AYAYAS=ayaya\n");
|
||||||
|
fprintf(makefile, "CYTO_LIB=/usr/local/lib\n");
|
||||||
|
fprintf(makefile, "CYTO_INC=/usr/local/include\n");
|
||||||
|
fprintf(makefile, "ETC=etc\n\n");
|
||||||
|
|
||||||
|
fprintf(makefile, "all: utils binary ayadoc\n\n");
|
||||||
|
|
||||||
|
/* Create all objects */
|
||||||
|
sources = collect_sources("src", true, ".c");
|
||||||
|
images = collect_sources("etc/media", true, ".png");
|
||||||
|
fprintf(makefile, "binary:");
|
||||||
|
write_objects(makefile, sources);
|
||||||
|
write_images(makefile, images);
|
||||||
|
fprintf(makefile, "\n\t");
|
||||||
|
{
|
||||||
|
fprintf(makefile, "$(CC) -o $(BINARY)");
|
||||||
|
if (with_static)
|
||||||
|
{
|
||||||
|
fprintf(makefile, " -static");
|
||||||
|
}
|
||||||
|
fprintf(makefile, " -L $(CYTO_LIB)");
|
||||||
|
write_objects(makefile, sources);
|
||||||
|
write_images(makefile, images);
|
||||||
|
if (with_static)
|
||||||
|
{
|
||||||
|
fprintf(makefile, " -lm -lpthread -lmbedtls -lmbedx509 -lmbedcrypto -lCytoplasm -lmbedtls -lmbedx509 -lmbedcrypto");
|
||||||
|
if (with_lmdb) fprintf(makefile, " -llmdb");
|
||||||
|
}
|
||||||
|
fprintf(makefile, " -lCytoplasm $(LDFLAGS)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write rules for every source */
|
||||||
|
for (i = 0; i < str_array_len(sources); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(sources, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".c", ".o");
|
||||||
|
char *obj = string_cat("build", ofl + 3);
|
||||||
|
|
||||||
|
fprintf(makefile, "%s: %s", obj, src);
|
||||||
|
analyse_dependencies(makefile, src);
|
||||||
|
fprintf(makefile, "\n");
|
||||||
|
{
|
||||||
|
str_array_t *s = split(obj);
|
||||||
|
ssize_t j;
|
||||||
|
|
||||||
|
fprintf(makefile, "\t@mkdir -p ");
|
||||||
|
for (j = 0; j < str_array_len(s) - 1; j++)
|
||||||
|
{
|
||||||
|
fprintf(makefile, "%s/", str_array_get(s, j));
|
||||||
|
}
|
||||||
|
fprintf(makefile, "\n");
|
||||||
|
str_array_free(s);
|
||||||
|
|
||||||
|
fprintf(makefile, "\t$(CC) -c -Isrc -Isrc/include -I$(CYTO_INC) ");
|
||||||
|
fprintf(makefile, "-DVERSION=\"\\\"$(VERSION)\\\"\" ");
|
||||||
|
fprintf(makefile, "-DNAME=\"\\\"$(NAME)\\\"\" ");
|
||||||
|
fprintf(makefile, "-DCODE=\"\\\"$(CODE)\\\"\" ");
|
||||||
|
fprintf(makefile, "-DREPOSITORY=\"\\\"%s\\\"\" ", repo);
|
||||||
|
fprintf(makefile, "$(CFLAGS) %s -o %s\n", src, obj);
|
||||||
|
}
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
for (i = 0; i < str_array_len(images); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(images, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".png", ".o");
|
||||||
|
char *obj = string_cat("build", ofl + 3 + 1 + 5);
|
||||||
|
char *cfl = string_cat("build", src + 3 + 1 + 5);
|
||||||
|
|
||||||
|
fprintf(makefile, "%s: %s\n", obj, src);
|
||||||
|
|
||||||
|
{
|
||||||
|
str_array_t *s = split(obj);
|
||||||
|
char *sym;
|
||||||
|
ssize_t j;
|
||||||
|
|
||||||
|
fprintf(makefile, "\t@mkdir -p ");
|
||||||
|
for (j = 0; j < str_array_len(s) - 1; j++)
|
||||||
|
{
|
||||||
|
fprintf(makefile, "%s/", str_array_get(s, j));
|
||||||
|
}
|
||||||
|
fprintf(makefile, "\n");
|
||||||
|
sym = j != -1 ? str_array_get(s, j): NULL;
|
||||||
|
sym = string_rep_ext(sym, ".o", "");
|
||||||
|
str_array_free(s);
|
||||||
|
|
||||||
|
fprintf(makefile, "\ttools/out/b64 %s 'media_%s' '%s.c'\n", src, sym, obj);
|
||||||
|
fprintf(makefile, "\t$(CC) -c %s.c -o %s\n", obj, obj);
|
||||||
|
free(sym);
|
||||||
|
}
|
||||||
|
free(obj);
|
||||||
|
free(ofl);
|
||||||
|
free(cfl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build utilities */
|
||||||
|
utils = collect_sources("tools", true, ".c");
|
||||||
|
fprintf(makefile, "utils:");
|
||||||
|
write_executable(makefile, utils);
|
||||||
|
fprintf(makefile, "\n");
|
||||||
|
for (i = 0; i < str_array_len(utils); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(utils, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".c", "");
|
||||||
|
char *obj = string_cat("tools/out", ofl + 5);
|
||||||
|
|
||||||
|
fprintf(makefile, "%s: %s\n", obj, src);
|
||||||
|
{
|
||||||
|
fprintf(makefile, "\t@mkdir -p tools/out\n");
|
||||||
|
fprintf(makefile, "\t$(CC) -o %s", obj);
|
||||||
|
if (with_static)
|
||||||
|
{
|
||||||
|
fprintf(makefile, " -static");
|
||||||
|
}
|
||||||
|
fprintf(makefile, " %s", src);
|
||||||
|
fprintf(makefile, " -I $(CYTO_INC)");
|
||||||
|
fprintf(makefile, " -L $(CYTO_LIB)");
|
||||||
|
if (with_static)
|
||||||
|
{
|
||||||
|
fprintf(makefile, " -lm -lpthread -lmbedtls -lmbedx509 -lmbedcrypto");
|
||||||
|
fprintf(makefile, " -lCytoplasm -lmbedtls -lmbedx509 -lmbedcrypto");
|
||||||
|
if (with_lmdb) fprintf(makefile, " -llmdb");
|
||||||
|
}
|
||||||
|
fprintf(makefile, " -lCytoplasm $(CFLAGS)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build Aya */
|
||||||
|
aya = collect_sources("src/include", true, ".h");
|
||||||
|
fprintf(makefile, "ayadoc:");
|
||||||
|
write_ayas(makefile, aya);
|
||||||
|
fprintf(makefile, "\n");
|
||||||
|
for (i = 0; i < str_array_len(aya); i++)
|
||||||
|
{
|
||||||
|
char *src = str_array_get(aya, i);
|
||||||
|
char *ofl = string_rep_ext(src, ".h", ".html");
|
||||||
|
char *obj = string_cat("$(AYAYAS)", ofl + 3 + 1 + 7);
|
||||||
|
|
||||||
|
fprintf(makefile, "%s: %s\n", obj, src);
|
||||||
|
{
|
||||||
|
str_array_t *s = split(obj);
|
||||||
|
ssize_t j;
|
||||||
|
|
||||||
|
fprintf(makefile, "\t@mkdir -p ");
|
||||||
|
for (j = 0; j < str_array_len(s) - 1; j++)
|
||||||
|
{
|
||||||
|
fprintf(makefile, "%s/", str_array_get(s, j));
|
||||||
|
}
|
||||||
|
fprintf(makefile, "\n");
|
||||||
|
str_array_free(s);
|
||||||
|
|
||||||
|
fprintf(makefile,
|
||||||
|
"\ttools/out/aya "
|
||||||
|
"-C etc/ayadoc/style.css "
|
||||||
|
"-p $(NAME) "
|
||||||
|
"-i %s -o %s\n", src, obj
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(makefile, "install-parsee: binary\n");
|
||||||
|
{
|
||||||
|
fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/bin\n");
|
||||||
|
fprintf(makefile, "\tcp $(BINARY) $(PREFIX)/bin\n");
|
||||||
|
fprintf(makefile, "\tchmod 755 $(PREFIX)/bin/$(BINARY)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(makefile, "install-tools: utils\n");
|
||||||
|
fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/bin\n");
|
||||||
|
for (i = 0; i < str_array_len(utils); i++)
|
||||||
|
{
|
||||||
|
char *tool = str_array_get(utils, i);
|
||||||
|
char *ofl = string_rep_ext(tool, ".c", "");
|
||||||
|
char *obj = string_cat("tools/out", ofl + 5);
|
||||||
|
char *bna = basename(obj);
|
||||||
|
|
||||||
|
fprintf(makefile, "\tcp %s $(PREFIX)/bin/parsee-%s\n", obj, bna);
|
||||||
|
fprintf(makefile, "\tchmod 755 $(PREFIX)/bin/parsee-%s\n", bna);
|
||||||
|
|
||||||
|
free(ofl);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
fprintf(makefile, "install-aya: ayadoc\n");
|
||||||
|
fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/aya/$(BINARY)\n");
|
||||||
|
fprintf(makefile, "\tcp -R $(AYAYAS)/* $(PREFIX)/aya/$(BINARY)\n");
|
||||||
|
fprintf(makefile, "install-man:\n");
|
||||||
|
fprintf(makefile, "\tmkdir -m 755 -p $(PREFIX)/share/man\n");
|
||||||
|
fprintf(makefile, "\tcp -R etc/man/* $(PREFIX)/share/man\n");
|
||||||
|
|
||||||
|
fprintf(makefile,
|
||||||
|
"install: "
|
||||||
|
"install-parsee "
|
||||||
|
"install-tools "
|
||||||
|
"install-aya "
|
||||||
|
"install-man\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
fprintf(makefile, "clean:\n\trm -rf ayaya build $(BINARY) tools/out\n");
|
||||||
|
|
||||||
|
str_array_free(sources);
|
||||||
|
str_array_free(images);
|
||||||
|
str_array_free(utils);
|
||||||
|
str_array_free(aya);
|
||||||
|
fflush(makefile);
|
||||||
|
fclose(makefile);
|
||||||
|
free(repo);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return main_build(argc, argv);
|
||||||
|
}
|
||||||
40
etc/man/man1/parsee-adminify.1
Normal file
40
etc/man/man1/parsee-adminify.1
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
." The last field is the codename, by the way.
|
||||||
|
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||||
|
.TH parsee-adminify 1 "Parsee Utility" "star-of-hope"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
parsee-adminify - bootstrap an admin to a new Parsee server
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
parsee-adminify
|
||||||
|
.B [DB path]
|
||||||
|
.B [user]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I parsee-adminify
|
||||||
|
is a tool to add/list the Parsee administrator list. It currently only
|
||||||
|
allows you to see/add admins to it, using simplified globrules.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.B [config]
|
||||||
|
The configuration file used by the Parsee daemon.
|
||||||
|
.TP
|
||||||
|
.B [user]
|
||||||
|
If set, adds the glob
|
||||||
|
.I [user]
|
||||||
|
as a Parsee administrator. Otherwise, lists all administrators in
|
||||||
|
the Parsee instance.
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
.PP
|
||||||
|
None as I know of. If anyone notices any issues with this, please
|
||||||
|
let me know.
|
||||||
|
|
||||||
|
.SH LICENSE
|
||||||
|
Unlike Parsee,
|
||||||
|
.B parsee-adminify
|
||||||
|
is under the CC0/PD.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.B parsee-config(1), parsee-aya(1), parsee(1)
|
||||||
40
etc/man/man1/parsee-aya.1
Normal file
40
etc/man/man1/parsee-aya.1
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
." The last field is the codename, by the way.
|
||||||
|
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||||
|
.TH parsee-aya 1 "Parsee Utility" "star-of-hope"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
parsee-aya - generate some nice Ayaya! documentation
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
parsee-aya
|
||||||
|
.B [-i HEADER]
|
||||||
|
.B [-o HTML]
|
||||||
|
.B [-C STYLESHEET]
|
||||||
|
.B [-p NAME]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I parsee-aya
|
||||||
|
is a tool to generate Ayadocs(HTML documentation) from a formatted
|
||||||
|
header file. See a Parsee header file for an example of the inner usage.
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BR -i HEADER
|
||||||
|
The input header file to process out.
|
||||||
|
.TP
|
||||||
|
.BR -o HTML
|
||||||
|
The HTML file to write out the Ayadoc.
|
||||||
|
.TP
|
||||||
|
.BR -C STYLESHEET
|
||||||
|
A stylesheet file to use for Ayadocs.
|
||||||
|
.TP
|
||||||
|
.BR -p NAME
|
||||||
|
The project's name. If unavailable, defaults to Ayaya!
|
||||||
|
|
||||||
|
.SH LICENSE
|
||||||
|
Unlike Parsee,
|
||||||
|
.B parsee-aya
|
||||||
|
is under the CC0/PD.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.B parsee-config(1), parsee-adminify(1), parsee(1)
|
||||||
103
etc/man/man1/parsee-config.1
Normal file
103
etc/man/man1/parsee-config.1
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
." The last field is the codename, by the way.
|
||||||
|
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||||
|
.TH parsee-config 1 "Parsee Utility" "lunar-rainbow"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
parsee-config - generate a basic configuration file
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
parsee-config
|
||||||
|
.B [-H HOMESERVER_NAME]
|
||||||
|
.B [-s SHARED_SECRET]
|
||||||
|
.B [-m MEDIA_URL]
|
||||||
|
.B [-J JABBER_HOST]
|
||||||
|
.B [-j JABBER_ADDR]
|
||||||
|
.B [-p JABBER_PORT]
|
||||||
|
.B [-d DATABASE]
|
||||||
|
.B [-M MAX_STANZA]
|
||||||
|
.B [-S DATABASE size]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
.I parsee-config
|
||||||
|
creates a basic configuration file to initialise a Parsee instance with the
|
||||||
|
given input flags, and makes a basic database. A basic example of
|
||||||
|
.I parsee-config
|
||||||
|
would be this(for a blow.hole server, with a xmpp.blow.hole JCP on Prosody,
|
||||||
|
and a 128MB LMDB backend)
|
||||||
|
.sp
|
||||||
|
.if n \{\
|
||||||
|
.RS 4
|
||||||
|
.\}
|
||||||
|
.nf
|
||||||
|
$ parsee-config \\
|
||||||
|
-d '/var/lib/parsee' \\
|
||||||
|
-m 'https://pmedia.blow.hole' \\
|
||||||
|
-H 'blow.hole' \\
|
||||||
|
-s 'The Dark Shared Secret' \\
|
||||||
|
-J 'xmpp.blow.hole' \\
|
||||||
|
-j 'localhost' \\
|
||||||
|
-S 128
|
||||||
|
.fi
|
||||||
|
.if n \{\
|
||||||
|
.RE
|
||||||
|
.\}
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BR -H HOMESERVER_NAME
|
||||||
|
.I HOMESERVER_NAME
|
||||||
|
points to the homeserver name, without delegation (which is autosensed).
|
||||||
|
For example, if you except Parsee users to be on
|
||||||
|
.IR @something:blow.hole
|
||||||
|
, then it should be set to
|
||||||
|
.IR blow.hole .
|
||||||
|
.TP
|
||||||
|
.BR -s SHARED_SECRET
|
||||||
|
.I SHARED_SECRET
|
||||||
|
is a shared secret known by Parsee and the XMPP component to authenticate.
|
||||||
|
.TP
|
||||||
|
.BR -M MAX_STANZA
|
||||||
|
.I MAX_STANZA
|
||||||
|
is the maximum stanza size accepted by the XMPP host. If it is less than 10000 bytes, then it shall be set to that limit(the standardised value).
|
||||||
|
.TP
|
||||||
|
.BR -m MEDIA_URL
|
||||||
|
.I MEDIA_URL
|
||||||
|
is an optional field used by Parsee as an address that points to Matrix
|
||||||
|
media. It must be publicly accessible (behind a reverse proxy to HTTP:7642)
|
||||||
|
.TP
|
||||||
|
.BR -J JABBER_HOST
|
||||||
|
.I JABBER_HOST
|
||||||
|
is used as the component host for Parsee.
|
||||||
|
.TP
|
||||||
|
.BR -j JABBER_ADDR
|
||||||
|
.I JABBER_ADDR
|
||||||
|
can optionally be used to change the hostname Parsee will try to contact
|
||||||
|
for XMPP. Users should ideally use localhost (or a hostname pointing to
|
||||||
|
the server itself), as XMPP component streams are not encrypted.
|
||||||
|
.TP
|
||||||
|
.BR -p JABBER_PORT
|
||||||
|
.I JABBER_PORT
|
||||||
|
is used as the component post for Parsee. Parsee uses it alongside
|
||||||
|
.I JABBER_HOST
|
||||||
|
to connect to
|
||||||
|
.I JABBER_HOST:JABBER_PORT
|
||||||
|
over TCP.
|
||||||
|
.TP
|
||||||
|
.BR -d DATABASE
|
||||||
|
The directory inwhich Parsee stores its database(LMDB/flat-file). Whenever the
|
||||||
|
database is flat or LMDB is dictated by the presence of the -S flag, and
|
||||||
|
whenever Cytoplasm has LMDB enabled.
|
||||||
|
.TP
|
||||||
|
.BR -S SIZE
|
||||||
|
If set, enables LMDB in Parsee, and sets its max DB size to
|
||||||
|
.BR SIZE MiB .
|
||||||
|
|
||||||
|
|
||||||
|
.SH LICENSE
|
||||||
|
Unlike Parsee,
|
||||||
|
.B parsee-config
|
||||||
|
is under the CC0/PD.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.B parsee-aya(1), parsee-adminify(1), parsee(1)
|
||||||
118
etc/man/man1/parsee.1
Normal file
118
etc/man/man1/parsee.1
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
." The last field is the codename, by the way.
|
||||||
|
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||||
|
.TH parsee 1 "Parsee Utility" "star-of-hope"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
parsee - the jealous XMPP-Matrix bridge
|
||||||
|
|
||||||
|
.SH SYNOPSIS
|
||||||
|
parsee
|
||||||
|
.B [-J num]
|
||||||
|
.B [-H num]
|
||||||
|
.B [-C file]
|
||||||
|
.B [-g]
|
||||||
|
.B [-v]
|
||||||
|
.B [-h]
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Parsee is a
|
||||||
|
.B bridging program
|
||||||
|
, that is, it connects two chat protocols together (here, XMPP/Jabber,
|
||||||
|
and Matrix).
|
||||||
|
.PP
|
||||||
|
As such, a user on XMPP can communicate with Matrix users, and
|
||||||
|
vice-versa with it.
|
||||||
|
.PP
|
||||||
|
Currently, Parsee is under
|
||||||
|
.BI alpha .
|
||||||
|
This means that it is still subject to bugs, flaws, and may change in a
|
||||||
|
backwards-incompatible manner at any time.
|
||||||
|
|
||||||
|
.SH FIRST RUN
|
||||||
|
To start with a new install of Parsee(assuming you have a homeserver
|
||||||
|
with AS support and a XMPP server with JCP support, and that you know
|
||||||
|
the shared secret/domain for that component. Guides for that are
|
||||||
|
outside the scope of this manpage.), start by running
|
||||||
|
.B parsee-config(1)
|
||||||
|
with the flags provided in it, according to your configuration.
|
||||||
|
.PP
|
||||||
|
This should generate a
|
||||||
|
.I parsee.json
|
||||||
|
file, which is the configuration files for Parsee, and a directory where
|
||||||
|
the Parsee database may reside. You can then run
|
||||||
|
.I parsee -g
|
||||||
|
in that directory, which should generate a
|
||||||
|
.I parsee.yaml
|
||||||
|
file, this time with the generated AS configuration file, which you can
|
||||||
|
apply to your homeserver(how is implementation-dependent, but it is
|
||||||
|
generally a matter of modifying a configuration file to add the path
|
||||||
|
to such file).
|
||||||
|
|
||||||
|
.SH OPTIONS
|
||||||
|
.TP
|
||||||
|
.BR -J num
|
||||||
|
Set the number of threads used for XMPP stanza processing to
|
||||||
|
.I num\[char46]
|
||||||
|
.TP
|
||||||
|
.BR -H num
|
||||||
|
Set the number of threads used for HTTP request processing to
|
||||||
|
.I num\[char46]
|
||||||
|
.TP
|
||||||
|
.BR -C file
|
||||||
|
Sets the configuration JSON path to
|
||||||
|
.I file\[char46]
|
||||||
|
.PP
|
||||||
|
Defaults to
|
||||||
|
.I parsee.json
|
||||||
|
if none can be found.
|
||||||
|
.TP
|
||||||
|
.BR -g
|
||||||
|
Generates a
|
||||||
|
.I parsee.yaml
|
||||||
|
file to be used as an Application-Service entry within Matrix, and
|
||||||
|
exits.
|
||||||
|
.TP
|
||||||
|
.BR -v
|
||||||
|
Verbose logging of Parsee's behaviour. Recommended when testing Parsee
|
||||||
|
for bugs/hints.
|
||||||
|
.TP
|
||||||
|
.BR -h
|
||||||
|
Prints the command list with descriptions.
|
||||||
|
|
||||||
|
.SH BUGS
|
||||||
|
.PP
|
||||||
|
Sometimes Parsee will not respond to ^C requests properly, which
|
||||||
|
causes a system administrator to have to invoke SIGKILL.
|
||||||
|
.PP
|
||||||
|
Parsee seems to grow slowly in memory usage when messages are bridged
|
||||||
|
which, in the long run, could cause a memory error. All of the memory is
|
||||||
|
tracked, however, which means that this isn't exactly a leak.
|
||||||
|
.PP
|
||||||
|
Some important features still aren't implemented yet(e.g being able to join
|
||||||
|
a MUC from XMPP)
|
||||||
|
|
||||||
|
.SH CHATROOMS
|
||||||
|
You may talk about Parsee on these rooms(on Matrix and XMPP):
|
||||||
|
.RE
|
||||||
|
.IP xmpp:parsee@conference.monocles.eu?join
|
||||||
|
for XMPP, which is bridged along Matrix
|
||||||
|
.IP #parsee:tedomum.net
|
||||||
|
for Matrix, which is bridged along Parsee
|
||||||
|
.RS
|
||||||
|
|
||||||
|
.SH AUTHORS
|
||||||
|
." Contributors, feel free to put your names here in a PR, as long as
|
||||||
|
." it is acceptable
|
||||||
|
. PP
|
||||||
|
.BR LDA:
|
||||||
|
main maintainer of Parsee, accessible over XMPP at lda@freetards.xyz
|
||||||
|
and over Matrix as @fourier:ari.lt.
|
||||||
|
|
||||||
|
.SH LICENSE
|
||||||
|
Parsee is available under the AGPL3, but has some code under CC0/PD, and
|
||||||
|
some from Cytoplasm itself. Please see
|
||||||
|
.I https://git.kappach.at/lda/Parsee
|
||||||
|
for more information.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.B parsee-config(1), parsee-adminify(1), parsee-aya(1)
|
||||||
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)
|
||||||
|
|
||||||
50
etc/man/man7/parsee-cmd-syntax.7
Normal file
50
etc/man/man7/parsee-cmd-syntax.7
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
." The last field is the codename, by the way.
|
||||||
|
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||||
|
.TH parsee-cmd-syntax 7 "Parsee Utility" "star-of-hope"
|
||||||
|
|
||||||
|
.SH NAME
|
||||||
|
parsee-cmd-syntax - Basic syntax information with Parsee Matrix commands
|
||||||
|
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Parsee uses a specific syntax for commands, which is generally different
|
||||||
|
from regular bots, but closer to
|
||||||
|
.B dd(1) 's
|
||||||
|
syntax.
|
||||||
|
.PP
|
||||||
|
A command is formatted as so.
|
||||||
|
.sp
|
||||||
|
.if n \{\
|
||||||
|
.RS 4
|
||||||
|
.\}
|
||||||
|
.nf
|
||||||
|
.B ![NAME] arg1=val1 arg2='val2' arg3="val\(rs\(dq3"
|
||||||
|
.fi
|
||||||
|
.if n \{\
|
||||||
|
.RE
|
||||||
|
.\}
|
||||||
|
.sp
|
||||||
|
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B arg1=val1
|
||||||
|
syntax is to be used for simple values, with values containing no spaces.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
The
|
||||||
|
.B NAME
|
||||||
|
attribute defines the command to be called, and the
|
||||||
|
.B arg1='val1' ,
|
||||||
|
and
|
||||||
|
.B arg2="val2"
|
||||||
|
syntax is to be used for simple values, with values containing spaces. If
|
||||||
|
the value needs to contain quotes, they may be escaped with
|
||||||
|
.B \(rs'
|
||||||
|
and
|
||||||
|
.B \(rs"
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
.SH LICENSE
|
||||||
|
This document is under public domain, or CC0 if not allowed by local law.
|
||||||
|
|
||||||
|
.SH SEE ALSO
|
||||||
|
.B parsee(1)
|
||||||
5
etc/media/README
Normal file
5
etc/media/README
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
This directory is for any PNG media that needs to be integrated into Parsee.
|
||||||
|
Each file here is Base64-encoded then defined as a const char[] symbol with
|
||||||
|
the name being a function of the PNG filename: N(FILE.png)=media_FILE
|
||||||
|
|
||||||
|
NOTE: Medias should be about ~1024B MAX. Avoid to stride for anything larger.
|
||||||
BIN
etc/media/parsee_logo.png
Normal file
BIN
etc/media/parsee_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 871 B |
BIN
etc/media/unknown.png
Normal file
BIN
etc/media/unknown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 542 B |
1
guix.scm
Symbolic link
1
guix.scm
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
.guix/modules/parsee.scm
|
||||||
971
src/AS.c
971
src/AS.c
|
|
@ -55,976 +55,5 @@ ASAuthenticateRequest(const ParseeConfig *data, HttpClientContext *ctx)
|
||||||
HttpRequestHeader(ctx, "Authorization", bearer);
|
HttpRequestHeader(ctx, "Authorization", bearer);
|
||||||
Free(bearer);
|
Free(bearer);
|
||||||
}
|
}
|
||||||
bool
|
|
||||||
ASRegisterUser(const ParseeConfig *conf, char *user)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
HttpStatus status;
|
|
||||||
if (!conf || !user)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create user. We don't actually care about the value as we can
|
|
||||||
* masquerade, as long as it exists. */
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, "/_matrix/client/v3/register"
|
|
||||||
);
|
|
||||||
json = HashMapCreate();
|
|
||||||
|
|
||||||
HashMapSet(json,"type",JsonValueString("m.login.application_service"));
|
|
||||||
|
|
||||||
user = ParseeGetLocal(user);
|
|
||||||
HashMapSet(json,"username",JsonValueString(user));
|
|
||||||
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
status = ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
Free(user);
|
|
||||||
|
|
||||||
return status == HTTP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ASPing(const ParseeConfig *conf)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path;
|
|
||||||
if (!conf)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = StrConcat(3,
|
|
||||||
"/_matrix/client/v1/appservice/",
|
|
||||||
"Parsee%20XMPP",
|
|
||||||
"/ping"
|
|
||||||
);
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASInvite(const ParseeConfig *conf, char *id, char *invited)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path, *bridge;
|
|
||||||
if (!conf || !id || !invited)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bridge = StrConcat(4,
|
|
||||||
"@", conf->sender_localpart,
|
|
||||||
":", conf->server_base
|
|
||||||
);
|
|
||||||
path = StrConcat(5,
|
|
||||||
"/_matrix/client/v3/rooms/", id, "/invite",
|
|
||||||
"?user_id=", bridge
|
|
||||||
);
|
|
||||||
Free(bridge);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
HashMapSet(json, "user_id", JsonValueString(invited));
|
|
||||||
HashMapSet(json, "reason", JsonValueString("Pass over."));
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASBan(const ParseeConfig *conf, char *id, char *banned)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path, *bridge;
|
|
||||||
if (!conf || !id || !banned)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bridge = StrConcat(4,
|
|
||||||
"@", conf->sender_localpart,
|
|
||||||
":", conf->server_base
|
|
||||||
);
|
|
||||||
path = StrConcat(5,
|
|
||||||
"/_matrix/client/v3/rooms/", id, "/ban",
|
|
||||||
"?user_id=", bridge
|
|
||||||
);
|
|
||||||
Free(bridge);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
|
||||||
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASKick(const ParseeConfig *conf, char *id, char *banned)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path, *bridge;
|
|
||||||
if (!conf || !id || !banned)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bridge = StrConcat(4,
|
|
||||||
"@", conf->sender_localpart,
|
|
||||||
":", conf->server_base
|
|
||||||
);
|
|
||||||
path = StrConcat(5,
|
|
||||||
"/_matrix/client/v3/rooms/", id, "/kick",
|
|
||||||
"?user_id=", bridge
|
|
||||||
);
|
|
||||||
Free(bridge);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
|
||||||
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path, *ret;
|
|
||||||
if (!conf || !id)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!masquerade)
|
|
||||||
{
|
|
||||||
char *raw = StrConcat(4,
|
|
||||||
"@", conf->sender_localpart,
|
|
||||||
":", conf->server_base
|
|
||||||
);
|
|
||||||
masquerade = HttpUrlEncode(raw);
|
|
||||||
Free(raw);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
masquerade = HttpUrlEncode(masquerade);
|
|
||||||
}
|
|
||||||
id = HttpUrlEncode(id);
|
|
||||||
path = StrConcat(5,
|
|
||||||
"/_matrix/client/v3/join/", id, "?",
|
|
||||||
"user_id=", masquerade
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
json = JsonDecode(HttpClientStream(ctx));
|
|
||||||
ret = StrDuplicate(GrabString(json, 1, "room_id"));
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(masquerade);
|
|
||||||
Free(id);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASLeave(const ParseeConfig *conf, char *id, char *masquerade)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path;
|
|
||||||
if (!conf || !id)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!masquerade)
|
|
||||||
{
|
|
||||||
char *raw = StrConcat(4,
|
|
||||||
"@", conf->sender_localpart,
|
|
||||||
":", conf->server_base
|
|
||||||
);
|
|
||||||
masquerade = HttpUrlEncode(raw);
|
|
||||||
Free(raw);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
masquerade = HttpUrlEncode(masquerade);
|
|
||||||
}
|
|
||||||
id = HttpUrlEncode(id);
|
|
||||||
path = StrConcat(5,
|
|
||||||
"/_matrix/client/v3/rooms/", id, "/leave?",
|
|
||||||
"user_id=", masquerade
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(masquerade);
|
|
||||||
Free(id);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
char *path;
|
|
||||||
if (!conf || !id || !type || !mask || !state)
|
|
||||||
{
|
|
||||||
JsonFree(state);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = StrConcat(9,
|
|
||||||
"/_matrix/client/v3/rooms/", id, "/state/",
|
|
||||||
type, "/", key, "?", "user_id=", mask
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, state);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(state);
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
char *path;
|
|
||||||
char *txn, *ret;
|
|
||||||
HashMap *reply;
|
|
||||||
if (!conf || !id || !type || !user || !c)
|
|
||||||
{
|
|
||||||
JsonFree(c);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
txn = StrRandom(16);
|
|
||||||
path = StrConcat(9,
|
|
||||||
"/_matrix/client/v3/rooms/",
|
|
||||||
id, "/send/", type, "/", txn, "?",
|
|
||||||
"user_id=", user
|
|
||||||
);
|
|
||||||
Free(txn);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, c);
|
|
||||||
|
|
||||||
reply = JsonDecode(HttpClientStream(ctx));
|
|
||||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
|
||||||
JsonFree(reply);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(c);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ASCreateRoom(const ParseeConfig *conf, char *by, char *alias)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path, *id;
|
|
||||||
if (!conf || !by)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = StrConcat(3,
|
|
||||||
"/_matrix/client/v3/createRoom",
|
|
||||||
"?user_id=", by
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
if (alias)
|
|
||||||
{
|
|
||||||
char *trimmed = StrDuplicate(alias);
|
|
||||||
if (*alias == '#')
|
|
||||||
{
|
|
||||||
char *tmp, cb[2] = { 0 };
|
|
||||||
alias++;
|
|
||||||
Free(trimmed);
|
|
||||||
trimmed = NULL;
|
|
||||||
|
|
||||||
while (*alias && *alias != ':')
|
|
||||||
{
|
|
||||||
cb[0] = *alias;
|
|
||||||
tmp = trimmed;
|
|
||||||
trimmed = StrConcat(2, trimmed, cb);
|
|
||||||
Free(tmp);
|
|
||||||
alias ++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
HashMapSet(json, "room_alias_name", JsonValueString(trimmed));
|
|
||||||
Free(trimmed);
|
|
||||||
}
|
|
||||||
HashMapSet(json, "visibility", JsonValueString("public"));
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
JsonFree(json);
|
|
||||||
json = JsonDecode(HttpClientStream(ctx));
|
|
||||||
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ASCreateDM(const ParseeConfig *conf, char *by, char *with)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json = NULL;
|
|
||||||
char *path, *id;
|
|
||||||
if (!conf || !by || !with)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = StrConcat(3,
|
|
||||||
"/_matrix/client/v3/createRoom",
|
|
||||||
"?user_id=", by
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(
|
|
||||||
conf,
|
|
||||||
HTTP_POST, path
|
|
||||||
);
|
|
||||||
Free(path);
|
|
||||||
json = HashMapCreate();
|
|
||||||
{
|
|
||||||
Array *invitees = ArrayCreate();
|
|
||||||
|
|
||||||
ArrayAdd(invitees, JsonValueString(with));
|
|
||||||
HashMapSet(json, "invite", JsonValueArray(invitees));
|
|
||||||
HashMapSet(json, "is_direct", JsonValueBoolean(true));
|
|
||||||
}
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
JsonFree(json);
|
|
||||||
json = JsonDecode(HttpClientStream(ctx));
|
|
||||||
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASSetAvatar(const ParseeConfig *conf, char *user, char *mxc)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json;
|
|
||||||
char *path;
|
|
||||||
if (!conf || !user || !mxc)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
path = StrConcat(6,
|
|
||||||
"/_matrix/client/v3/profile/",
|
|
||||||
user, "/avatar_url", "?",
|
|
||||||
"user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
json = HashMapCreate();
|
|
||||||
HashMapSet(json, "avatar_url", JsonValueString(mxc));
|
|
||||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
Free(user);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASSetName(const ParseeConfig *conf, char *user, char *name)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json;
|
|
||||||
char *path;
|
|
||||||
if (!conf || !user || !name)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
path = StrConcat(6,
|
|
||||||
"/_matrix/client/v3/profile/",
|
|
||||||
user, "/displayname", "?",
|
|
||||||
"user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
json = HashMapCreate();
|
|
||||||
HashMapSet(json, "displayname", JsonValueString(name));
|
|
||||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(conf, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(json);
|
|
||||||
Free(user);
|
|
||||||
}
|
|
||||||
HashMap *
|
|
||||||
ASFind(const ParseeConfig *c, char *room, char *event)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json;
|
|
||||||
char *path, *user;
|
|
||||||
if (!c || !room || !event)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
|
||||||
path = StrConcat(7,
|
|
||||||
"/_matrix/client/v3/rooms/",
|
|
||||||
room, "/event/", event, "?",
|
|
||||||
"user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
HttpRequestSendHeaders(ctx);
|
|
||||||
HttpRequestSend(ctx);
|
|
||||||
|
|
||||||
json = JsonDecode(HttpClientStream(ctx));
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(user);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ASGetName(const ParseeConfig *c, char *room, char *user)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx;
|
|
||||||
HashMap *reply;
|
|
||||||
char *path, *ret;
|
|
||||||
char *u2 = user;
|
|
||||||
if (!c || !user)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!room)
|
|
||||||
{
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
path = StrConcat(3,
|
|
||||||
"/_matrix/client/v3/profile/", user, "/displayname"
|
|
||||||
);
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
|
||||||
Free(user);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
HttpRequestSendHeaders(ctx);
|
|
||||||
HttpRequestSend(ctx);
|
|
||||||
|
|
||||||
reply = JsonDecode(HttpClientStream(ctx));
|
|
||||||
|
|
||||||
ret = StrDuplicate(
|
|
||||||
JsonValueAsString(HashMapGet(reply, "displayname"))
|
|
||||||
);
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(reply);
|
|
||||||
Free(path);
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
{
|
|
||||||
ret = StrDuplicate(u2);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
room = HttpUrlEncode(room);
|
|
||||||
path = StrConcat(4,
|
|
||||||
"/_matrix/client/v3/rooms/", room,
|
|
||||||
"/state/m.room.member/", user
|
|
||||||
);
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
|
||||||
Free(user);
|
|
||||||
Free(room);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
HttpRequestSendHeaders(ctx);
|
|
||||||
HttpRequestSend(ctx);
|
|
||||||
|
|
||||||
reply = JsonDecode(HttpClientStream(ctx));
|
|
||||||
|
|
||||||
ret = StrDuplicate(
|
|
||||||
JsonValueAsString(HashMapGet(reply, "displayname"))
|
|
||||||
);
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(reply);
|
|
||||||
Free(path);
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
{
|
|
||||||
ret = StrDuplicate(u2);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
HashMap *
|
|
||||||
ASGetPL(const ParseeConfig *c, char *room)
|
|
||||||
{
|
|
||||||
char *path;
|
|
||||||
HttpClientContext *ctx;
|
|
||||||
HashMap *reply;
|
|
||||||
if (!c || !room)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
room = HttpUrlEncode(room);
|
|
||||||
path = StrConcat(4,
|
|
||||||
"/_matrix/client/v3/rooms/", room,
|
|
||||||
"/state/m.room.power_levels/", ""
|
|
||||||
);
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
|
||||||
Free(room);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
HttpRequestSendHeaders(ctx);
|
|
||||||
HttpRequestSend(ctx);
|
|
||||||
|
|
||||||
reply = JsonDecode(HttpClientStream(ctx));
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(path);
|
|
||||||
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASSetPL(const ParseeConfig *conf, char *id, HashMap *m)
|
|
||||||
{
|
|
||||||
char *user;
|
|
||||||
if (!conf || !id || !m)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
user = StrConcat(4,
|
|
||||||
"@",conf->sender_localpart,
|
|
||||||
":",conf->server_base
|
|
||||||
);
|
|
||||||
ASSetState(conf, id, "m.room.power_levels", "", user, m);
|
|
||||||
Free(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
|
||||||
ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime)
|
|
||||||
{
|
|
||||||
char *size_str, *path, *ret, *user;
|
|
||||||
int i;
|
|
||||||
HttpClientContext *ctx;
|
|
||||||
HashMap *reply;
|
|
||||||
if (!c || !from)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_str = StrInt(size);
|
|
||||||
user = StrConcat(4, "@",c->sender_localpart,":",c->server_base);
|
|
||||||
path = StrConcat(2,
|
|
||||||
"/_matrix/media/v3/upload?user_id=", user
|
|
||||||
);
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
if (size)
|
|
||||||
{
|
|
||||||
HttpRequestHeader(ctx, "Content-Length", size_str);
|
|
||||||
}
|
|
||||||
if (mime)
|
|
||||||
{
|
|
||||||
HttpRequestHeader(ctx, "Content-Type", mime);
|
|
||||||
}
|
|
||||||
HttpRequestSendHeaders(ctx);
|
|
||||||
|
|
||||||
for (i = 0; i < size; i++)
|
|
||||||
{
|
|
||||||
int ch = StreamGetc(from);
|
|
||||||
if (ch == EOF)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
StreamPutc(HttpClientStream(ctx), ch);
|
|
||||||
}
|
|
||||||
HttpRequestSend(ctx);
|
|
||||||
reply = JsonDecode(HttpClientStream(ctx));
|
|
||||||
ret = StrDuplicate(
|
|
||||||
JsonValueAsString(HashMapGet(reply, "content_uri"))
|
|
||||||
);
|
|
||||||
if (!ret)
|
|
||||||
{
|
|
||||||
JsonEncode(reply, StreamStdout(), JSON_PRETTY);
|
|
||||||
StreamFlush(StreamStdout());
|
|
||||||
}
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
JsonFree(reply);
|
|
||||||
Free(size_str);
|
|
||||||
Free(path);
|
|
||||||
Free(user);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ASReupload(const ParseeConfig *c, char *from, char **mime)
|
|
||||||
{
|
|
||||||
Uri *uri;
|
|
||||||
HttpClientContext *ctx;
|
|
||||||
unsigned short port;
|
|
||||||
int size = 0, flags = HTTP_FLAG_NONE;
|
|
||||||
char *ret, *content_len;
|
|
||||||
|
|
||||||
if (!c || !from)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uri = UriParse(from);
|
|
||||||
if (!uri)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (uri->port)
|
|
||||||
{
|
|
||||||
port = uri->port;
|
|
||||||
}
|
|
||||||
else if (StrEquals(uri->proto, "https"))
|
|
||||||
{
|
|
||||||
port = 443;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
port = 80;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StrEquals(uri->proto, "https"))
|
|
||||||
{
|
|
||||||
flags |= HTTP_FLAG_TLS;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = HttpRequest(
|
|
||||||
HTTP_GET, flags, port, uri->host, uri->path
|
|
||||||
);
|
|
||||||
HttpRequestSendHeaders(ctx);
|
|
||||||
HttpRequestSend(ctx);
|
|
||||||
|
|
||||||
if (mime)
|
|
||||||
{
|
|
||||||
*mime = HashMapGet(HttpResponseHeaders(ctx), "content-type");
|
|
||||||
*mime = StrDuplicate(*mime);
|
|
||||||
}
|
|
||||||
|
|
||||||
content_len = HashMapGet(HttpResponseHeaders(ctx), "content-length");
|
|
||||||
if (content_len)
|
|
||||||
{
|
|
||||||
size = strtol(content_len, NULL, 10);
|
|
||||||
}
|
|
||||||
ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
UriFree(uri);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASType(const ParseeConfig *c, char *user, char *room, bool status)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json;
|
|
||||||
char *path;
|
|
||||||
if (!c || !user || !room)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
path = StrConcat(6,
|
|
||||||
"/_matrix/client/v3/rooms/",
|
|
||||||
room, "/typing/", user,
|
|
||||||
"?user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
json = HashMapCreate();
|
|
||||||
HashMapSet(json, "typing", JsonValueBoolean(status));
|
|
||||||
/* If someone types for 10 minutes straight, they got something weird man. */
|
|
||||||
HashMapSet(json, "timeout", JsonValueBoolean(10 MINUTES));
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ASPresence(const ParseeConfig *c, char *user, char *room, char *ev)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json;
|
|
||||||
char *path;
|
|
||||||
if (!c || !user || !room || !ev)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
room = HttpUrlEncode(room);
|
|
||||||
ev = HttpUrlEncode(ev);
|
|
||||||
path = StrConcat(6,
|
|
||||||
"/_matrix/client/v3/rooms/",
|
|
||||||
room, "/receipt/m.read/", ev,
|
|
||||||
"?user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
json = HashMapCreate();
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, json);
|
|
||||||
JsonFree(json);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(user);
|
|
||||||
Free(room);
|
|
||||||
Free(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap *
|
|
||||||
ASGetUserConfig(const ParseeConfig *c, char *user, char *key)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *json;
|
|
||||||
char *path;
|
|
||||||
if (!c || !key)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user)
|
|
||||||
{
|
|
||||||
char *raw = StrConcat(4,
|
|
||||||
"@", c->sender_localpart,
|
|
||||||
":", c->server_base
|
|
||||||
);
|
|
||||||
user = HttpUrlEncode(raw);
|
|
||||||
Free(raw);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
}
|
|
||||||
path = StrConcat(7,
|
|
||||||
"/_matrix/client/v3/user/",
|
|
||||||
user, "/account_data/", key, "?",
|
|
||||||
"user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
HttpRequestSendHeaders(ctx);
|
|
||||||
HttpRequestSend(ctx);
|
|
||||||
|
|
||||||
json = JsonDecode(HttpClientStream(ctx));
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(user);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASSetUserConfig(const ParseeConfig *c, char *user, char *key, HashMap *map)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
char *path;
|
|
||||||
if (!c || !key || !map)
|
|
||||||
{
|
|
||||||
JsonFree(map);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user)
|
|
||||||
{
|
|
||||||
char *raw = StrConcat(4,
|
|
||||||
"@", c->sender_localpart,
|
|
||||||
":", c->server_base
|
|
||||||
);
|
|
||||||
user = HttpUrlEncode(raw);
|
|
||||||
Free(raw);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
path = StrConcat(7,
|
|
||||||
"/_matrix/client/v3/user/",
|
|
||||||
user, "/account_data/", key, "?",
|
|
||||||
"user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, map);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(user);
|
|
||||||
JsonFree(map);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASRedact(const ParseeConfig *c, char *room, char *user, char *e_id)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *request;
|
|
||||||
char *path, *txn;
|
|
||||||
if (!c || !room || !e_id)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user)
|
|
||||||
{
|
|
||||||
char *raw = StrConcat(4,
|
|
||||||
"@", c->sender_localpart,
|
|
||||||
":", c->server_base
|
|
||||||
);
|
|
||||||
user = HttpUrlEncode(raw);
|
|
||||||
Free(raw);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
}
|
|
||||||
room = HttpUrlEncode(room);
|
|
||||||
e_id = HttpUrlEncode(e_id);
|
|
||||||
txn = StrRandom(16);
|
|
||||||
|
|
||||||
path = StrConcat(9,
|
|
||||||
"/_matrix/client/v3/rooms/",
|
|
||||||
room, "/redact/", e_id, "/", txn,
|
|
||||||
"?", "user_id=", user
|
|
||||||
);
|
|
||||||
|
|
||||||
request = HashMapCreate();
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
|
||||||
Free(path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, request);
|
|
||||||
JsonFree(request);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(user);
|
|
||||||
Free(room);
|
|
||||||
Free(e_id);
|
|
||||||
Free(txn);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg)
|
|
||||||
{
|
|
||||||
HttpClientContext *ctx = NULL;
|
|
||||||
HashMap *request;
|
|
||||||
char *path;
|
|
||||||
char *status_str = NULL;
|
|
||||||
if (!c || !user)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case USER_STATUS_ONLINE: status_str = "online"; break;
|
|
||||||
case USER_STATUS_OFFLINE: status_str = "offline"; break;
|
|
||||||
case USER_STATUS_UNAVAILABLE: status_str = "unavailable"; break;
|
|
||||||
default: return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user = HttpUrlEncode(user);
|
|
||||||
path = StrConcat(5,
|
|
||||||
"/_matrix/client/v3/presence/",user,"/status",
|
|
||||||
"?user_id=", user
|
|
||||||
);
|
|
||||||
Free(user);
|
|
||||||
|
|
||||||
request = HashMapCreate();
|
|
||||||
HashMapSet(request, "presence", JsonValueString(status_str));
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
HashMapSet(request, "status_msg", JsonValueString(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
|
||||||
ASAuthenticateRequest(c, ctx);
|
|
||||||
ParseeSetRequestJSON(ctx, request);
|
|
||||||
JsonFree(request);
|
|
||||||
|
|
||||||
HttpClientContextFree(ctx);
|
|
||||||
Free(path);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
93
src/AS/Events.c
Normal file
93
src/AS/Events.c
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
HashMap *
|
||||||
|
ASFind(const ParseeConfig *c, char *room, char *event)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path, *user;
|
||||||
|
if (!c || !room || !event)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
||||||
|
path = StrConcat(7,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/event/", event, "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ASRedact(const ParseeConfig *c, char *room, char *user, char *e_id)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *request;
|
||||||
|
char *path, *txn;
|
||||||
|
if (!c || !room || !e_id)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", c->sender_localpart,
|
||||||
|
":", c->server_base
|
||||||
|
);
|
||||||
|
user = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
}
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
e_id = HttpUrlEncode(e_id);
|
||||||
|
txn = StrRandom(16);
|
||||||
|
|
||||||
|
path = StrConcat(9,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/redact/", e_id, "/", txn,
|
||||||
|
"?", "user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
request = HashMapCreate();
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, request);
|
||||||
|
JsonFree(request);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
Free(room);
|
||||||
|
Free(e_id);
|
||||||
|
Free(txn);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
120
src/AS/Indicators.c
Normal file
120
src/AS/Indicators.c
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ASType(const ParseeConfig *c, char *user, char *room, bool status)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!c || !user || !room)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/typing/", user,
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "typing", JsonValueBoolean(status));
|
||||||
|
/* If someone types for 5 minutes straight, they got something
|
||||||
|
* weird man. */
|
||||||
|
HashMapSet(json, "timeout", JsonValueInteger(5 MINUTES));
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ASPresence(const ParseeConfig *c, char *user, char *room, char *ev)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!c || !user || !room || !ev)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
ev = HttpUrlEncode(ev);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/receipt/m.read/", ev,
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
Free(room);
|
||||||
|
Free(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *request;
|
||||||
|
char *path;
|
||||||
|
char *status_str = NULL;
|
||||||
|
if (!c || !user)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case USER_STATUS_ONLINE: status_str = "online"; break;
|
||||||
|
case USER_STATUS_OFFLINE: status_str = "offline"; break;
|
||||||
|
case USER_STATUS_UNAVAILABLE: status_str = "unavailable"; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/presence/",user,"/status",
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
request = HashMapCreate();
|
||||||
|
HashMapSet(request, "presence", JsonValueString(status_str));
|
||||||
|
if (msg)
|
||||||
|
{
|
||||||
|
HashMapSet(request, "status_msg", JsonValueString(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, request);
|
||||||
|
JsonFree(request);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(path);
|
||||||
|
}
|
||||||
220
src/AS/Media.c
Normal file
220
src/AS/Media.c
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
char *
|
||||||
|
ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime)
|
||||||
|
{
|
||||||
|
char *size_str, *path, *ret, *user;
|
||||||
|
unsigned int i;
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
HashMap *reply;
|
||||||
|
if (!c || !from)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_str = StrInt(size);
|
||||||
|
user = StrConcat(4, "@",c->sender_localpart,":",c->server_base);
|
||||||
|
path = StrConcat(2,
|
||||||
|
"/_matrix/media/v3/upload?user_id=", user
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
if (size)
|
||||||
|
{
|
||||||
|
HttpRequestHeader(ctx, "Content-Length", size_str);
|
||||||
|
}
|
||||||
|
if (mime)
|
||||||
|
{
|
||||||
|
HttpRequestHeader(ctx, "Content-Type", mime);
|
||||||
|
}
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
int ch = StreamGetc(from);
|
||||||
|
if (ch == EOF)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
StreamPutc(HttpClientStream(ctx), ch);
|
||||||
|
}
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "content_uri"))
|
||||||
|
);
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
JsonEncode(reply, StreamStdout(), JSON_PRETTY);
|
||||||
|
StreamFlush(StreamStdout());
|
||||||
|
}
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(size_str);
|
||||||
|
Free(path);
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASReupload(const ParseeConfig *c, char *from, char **mime)
|
||||||
|
{
|
||||||
|
Uri *uri;
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
unsigned short port;
|
||||||
|
int size = 0, flags = HTTP_FLAG_NONE;
|
||||||
|
char *ret, *content_len;
|
||||||
|
|
||||||
|
if (!c || !from)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri = UriParse(from);
|
||||||
|
if (!uri)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (uri->port)
|
||||||
|
{
|
||||||
|
port = uri->port;
|
||||||
|
}
|
||||||
|
else if (StrEquals(uri->proto, "https"))
|
||||||
|
{
|
||||||
|
port = 443;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
port = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StrEquals(uri->proto, "https"))
|
||||||
|
{
|
||||||
|
flags |= HTTP_FLAG_TLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = HttpRequest(
|
||||||
|
HTTP_GET, flags, port, uri->host, uri->path
|
||||||
|
);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
if (mime)
|
||||||
|
{
|
||||||
|
*mime = HashMapGet(HttpResponseHeaders(ctx), "content-type");
|
||||||
|
*mime = StrDuplicate(*mime);
|
||||||
|
}
|
||||||
|
|
||||||
|
content_len = HashMapGet(HttpResponseHeaders(ctx), "content-length");
|
||||||
|
if (content_len)
|
||||||
|
{
|
||||||
|
size = strtol(content_len, NULL, 10);
|
||||||
|
}
|
||||||
|
ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
UriFree(uri);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha)
|
||||||
|
{
|
||||||
|
HttpClientContext *cctx;
|
||||||
|
Stream *stream;
|
||||||
|
Stream *fake;
|
||||||
|
Uri *uri;
|
||||||
|
char *path, *buf = NULL;
|
||||||
|
unsigned char *sha1;
|
||||||
|
size_t len;
|
||||||
|
if (!c || !mxc || !mime || !sha)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*mime = NULL;
|
||||||
|
*sha = NULL;
|
||||||
|
|
||||||
|
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
|
||||||
|
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
ASAuthenticateRequest(c, cctx);
|
||||||
|
HttpRequestSendHeaders(cctx);
|
||||||
|
HttpRequestSend(cctx);
|
||||||
|
|
||||||
|
*mime = StrDuplicate(
|
||||||
|
HashMapGet(HttpResponseHeaders(cctx), "content-type")
|
||||||
|
);
|
||||||
|
stream = HttpClientStream(cctx);
|
||||||
|
fake = StreamFile(open_memstream(&buf, &len));
|
||||||
|
StreamCopy(stream, fake);
|
||||||
|
StreamClose(fake);
|
||||||
|
|
||||||
|
sha1 = Sha1Raw((unsigned char *) buf, len);
|
||||||
|
free(buf);
|
||||||
|
*sha = ShaToHex(sha1, HASH_SHA1);
|
||||||
|
Free(sha1);
|
||||||
|
|
||||||
|
HttpClientContextFree(cctx);
|
||||||
|
UriFree(uri);
|
||||||
|
Free(path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len)
|
||||||
|
{
|
||||||
|
HttpClientContext *cctx;
|
||||||
|
Stream *stream;
|
||||||
|
Stream *fake;
|
||||||
|
Uri *uri;
|
||||||
|
char *path, *buf = NULL;
|
||||||
|
if (!c || !mxc || !mime || !out || !len)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*mime = NULL;
|
||||||
|
*out = NULL;
|
||||||
|
*len = 0;
|
||||||
|
|
||||||
|
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
|
||||||
|
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
ASAuthenticateRequest(c, cctx);
|
||||||
|
HttpRequestSendHeaders(cctx);
|
||||||
|
HttpRequestSend(cctx);
|
||||||
|
|
||||||
|
*mime = StrDuplicate(
|
||||||
|
HashMapGet(HttpResponseHeaders(cctx), "content-type")
|
||||||
|
);
|
||||||
|
stream = HttpClientStream(cctx);
|
||||||
|
fake = StreamFile(open_memstream(&buf, len));
|
||||||
|
StreamCopy(stream, fake);
|
||||||
|
StreamClose(fake);
|
||||||
|
|
||||||
|
*out = Malloc(*len);
|
||||||
|
memcpy(*out, buf, *len);
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
HttpClientContextFree(cctx);
|
||||||
|
UriFree(uri);
|
||||||
|
Free(path);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
42
src/AS/Ping.c
Normal file
42
src/AS/Ping.c
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
bool
|
||||||
|
ASPing(const ParseeConfig *conf)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path;
|
||||||
|
bool ret;
|
||||||
|
if (!conf)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v1/appservice/",
|
||||||
|
"Parsee%20XMPP",
|
||||||
|
"/ping"
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ret = ParseeSetRequestJSON(ctx, json) == HTTP_OK;
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
206
src/AS/Profile.c
Normal file
206
src/AS/Profile.c
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ASSetAvatar(const ParseeConfig *conf, char *user, char *mxc)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !user || !mxc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/profile/",
|
||||||
|
user, "/avatar_url", "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "avatar_url", JsonValueString(mxc));
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetName(const ParseeConfig *conf, char *user, char *name)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !user || !name)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/profile/",
|
||||||
|
user, "/displayname", "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "displayname", JsonValueString(name));
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ASGetName(const ParseeConfig *c, char *room, char *user)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
HashMap *reply;
|
||||||
|
char *path, *ret;
|
||||||
|
char *u2 = user;
|
||||||
|
if (!c || !user)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!room)
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v3/profile/", user, "/displayname"
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(user);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||||
|
);
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
ret = StrDuplicate(u2);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
path = StrConcat(4,
|
||||||
|
"/_matrix/client/v3/rooms/", room,
|
||||||
|
"/state/m.room.member/", user
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(user);
|
||||||
|
Free(room);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||||
|
);
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
ret = StrDuplicate(u2);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASGetAvatar(const ParseeConfig *c, char *room, char *user)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
HashMap *reply;
|
||||||
|
char *path = NULL, *ret = NULL;
|
||||||
|
char *u2 = user;
|
||||||
|
if (!c || !user)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room)
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
path = StrConcat(4,
|
||||||
|
"/_matrix/client/v3/rooms/", room,
|
||||||
|
"/state/m.room.member/", user
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(user);
|
||||||
|
Free(room);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "avatar_url"))
|
||||||
|
);
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
user = u2;
|
||||||
|
|
||||||
|
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from room, got %s", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v3/profile/", user, "/avatar_url"
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(user);
|
||||||
|
user = u2;
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "avatar_url"))
|
||||||
|
);
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from profile, got %s", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
46
src/AS/Register.c
Normal file
46
src/AS/Register.c
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
bool
|
||||||
|
ASRegisterUser(const ParseeConfig *conf, char *user)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
HttpStatus status;
|
||||||
|
if (!conf || !user)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create user. We don't actually care about the value as we can
|
||||||
|
* masquerade, as long as it exists. */
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, "/_matrix/client/v3/register"
|
||||||
|
);
|
||||||
|
json = HashMapCreate();
|
||||||
|
|
||||||
|
HashMapSet(json,"type",JsonValueString("m.login.application_service"));
|
||||||
|
|
||||||
|
user = ParseeGetLocal(user);
|
||||||
|
HashMapSet(json,"username",JsonValueString(user));
|
||||||
|
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
status = ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
return status == HTTP_OK;
|
||||||
|
}
|
||||||
82
src/AS/Relations.c
Normal file
82
src/AS/Relations.c
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
Array *
|
||||||
|
ASGetRelations(const ParseeConfig *c, size_t n, char *room, char *event, char *type)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
Array *ret, *chunk;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path;
|
||||||
|
char *user;
|
||||||
|
size_t i;
|
||||||
|
if (!c || !n || !room || !event)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
||||||
|
if (type)
|
||||||
|
{
|
||||||
|
path = StrConcat(8,
|
||||||
|
"/_matrix/client/v1/rooms/", room,
|
||||||
|
"/relations/", event, "/", type,
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v1/rooms/", room,
|
||||||
|
"/relations/", event,
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
ret = ArrayCreate();
|
||||||
|
chunk = GrabArray(json, 1, "chunk");
|
||||||
|
for (i = 0; i < ArraySize(chunk); i++)
|
||||||
|
{
|
||||||
|
HashMap *obj = JsonValueAsObject(ArrayGet(chunk, i));
|
||||||
|
ArrayAdd(ret, JsonDuplicate(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ASFreeRelations(Array *relations)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!relations)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ArraySize(relations); i++)
|
||||||
|
{
|
||||||
|
JsonFree(ArrayGet(relations, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayFree(relations);
|
||||||
|
}
|
||||||
366
src/AS/Room.c
Normal file
366
src/AS/Room.c
Normal file
|
|
@ -0,0 +1,366 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ASInvite(const ParseeConfig *conf, char *id, char *invited)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *bridge;
|
||||||
|
if (!conf || !id || !invited)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/invite",
|
||||||
|
"?user_id=", bridge
|
||||||
|
);
|
||||||
|
Free(bridge);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "user_id", JsonValueString(invited));
|
||||||
|
HashMapSet(json, "reason", JsonValueString("Pass over."));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASBan(const ParseeConfig *conf, char *id, char *banned)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *bridge;
|
||||||
|
if (!conf || !id || !banned)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/ban",
|
||||||
|
"?user_id=", bridge
|
||||||
|
);
|
||||||
|
Free(bridge);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||||
|
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASKick(const ParseeConfig *conf, char *id, char *banned)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *bridge;
|
||||||
|
if (!conf || !id || !banned)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/kick",
|
||||||
|
"?user_id=", bridge
|
||||||
|
);
|
||||||
|
Free(bridge);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||||
|
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *ret, *serv;
|
||||||
|
int status;
|
||||||
|
if (!conf || !id)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!masquerade)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
masquerade = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
masquerade = HttpUrlEncode(masquerade);
|
||||||
|
}
|
||||||
|
serv = strchr(id, ':');
|
||||||
|
if (serv)
|
||||||
|
{
|
||||||
|
serv = serv + 1;
|
||||||
|
}
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/join/", id, "?",
|
||||||
|
"user_id=", masquerade
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
status = ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
ret = StrDuplicate(GrabString(json, 1, "room_id"));
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(masquerade);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
|
(void) serv; // TODO
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASLeave(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !id)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!masquerade)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
masquerade = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
masquerade = HttpUrlEncode(masquerade);
|
||||||
|
}
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/leave?",
|
||||||
|
"user_id=", masquerade
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(masquerade);
|
||||||
|
Free(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ASCreateRoom(const ParseeConfig *conf, char *by, char *alias)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *id;
|
||||||
|
if (!conf || !by)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v3/createRoom",
|
||||||
|
"?user_id=", by
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
if (alias)
|
||||||
|
{
|
||||||
|
char *trimmed = StrDuplicate(alias);
|
||||||
|
if (*alias == '#')
|
||||||
|
{
|
||||||
|
char *tmp, cb[2] = { 0 };
|
||||||
|
alias++;
|
||||||
|
Free(trimmed);
|
||||||
|
trimmed = NULL;
|
||||||
|
|
||||||
|
while (*alias && *alias != ':')
|
||||||
|
{
|
||||||
|
cb[0] = *alias;
|
||||||
|
tmp = trimmed;
|
||||||
|
trimmed = StrConcat(2, trimmed, cb);
|
||||||
|
Free(tmp);
|
||||||
|
alias ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HashMapSet(json, "room_alias_name", JsonValueString(trimmed));
|
||||||
|
Free(trimmed);
|
||||||
|
}
|
||||||
|
HashMapSet(json, "visibility", JsonValueString("public"));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
JsonFree(json);
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASCreateDM(const ParseeConfig *conf, char *by, char *with)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *id;
|
||||||
|
if (!conf || !by || !with)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v3/createRoom",
|
||||||
|
"?user_id=", by
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
{
|
||||||
|
Array *invitees = ArrayCreate();
|
||||||
|
|
||||||
|
ArrayAdd(invitees, JsonValueString(with));
|
||||||
|
HashMapSet(json, "invite", JsonValueArray(invitees));
|
||||||
|
HashMapSet(json, "is_direct", JsonValueBoolean(true));
|
||||||
|
}
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
JsonFree(json);
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap *
|
||||||
|
ASGetPL(const ParseeConfig *c, char *room)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
HashMap *reply;
|
||||||
|
if (!c || !room)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
path = StrConcat(4,
|
||||||
|
"/_matrix/client/v3/rooms/", room,
|
||||||
|
"/state/m.room.power_levels/", ""
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(room);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetPL(const ParseeConfig *conf, char *id, HashMap *m)
|
||||||
|
{
|
||||||
|
char *user;
|
||||||
|
if (!conf || !id || !m)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
user = StrConcat(4,
|
||||||
|
"@",conf->sender_localpart,
|
||||||
|
":",conf->server_base
|
||||||
|
);
|
||||||
|
ASSetState(conf, id, "m.room.power_levels", "", user, m);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
79
src/AS/Send.c
Normal file
79
src/AS/Send.c
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Util.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
static char *
|
||||||
|
TSToStr(uint64_t ts)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *str;
|
||||||
|
|
||||||
|
len = snprintf(NULL, 0, "%"PRIu64, ts);
|
||||||
|
str = Malloc(len+1);
|
||||||
|
snprintf(str, len+1, "%"PRIu64, ts);
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c, uint64_t ts)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
char *path;
|
||||||
|
char *txn, *ret;
|
||||||
|
char *ts_str;
|
||||||
|
HttpStatus status;
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "%", ret);
|
||||||
|
}
|
||||||
|
HashMap *reply;
|
||||||
|
if (!conf || !id || !type || !user || !c)
|
||||||
|
{
|
||||||
|
JsonFree(c);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ts)
|
||||||
|
{
|
||||||
|
ts = UtilTsMillis();
|
||||||
|
}
|
||||||
|
ts_str = TSToStr(ts);
|
||||||
|
|
||||||
|
txn = StrRandom(16);
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(11,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
id, "/send/", type, "/", txn, "?",
|
||||||
|
"user_id=", user, "&ts=", ts_str
|
||||||
|
);
|
||||||
|
Free(id);
|
||||||
|
Free(txn);
|
||||||
|
Free(ts_str);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
status = ParseeSetRequestJSON(ctx, c);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
||||||
|
JsonFree(reply);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(c);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
37
src/AS/State.c
Normal file
37
src/AS/State.c
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !id || !type || !mask || !state)
|
||||||
|
{
|
||||||
|
JsonFree(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(9,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/state/",
|
||||||
|
type, "/", key, "?", "user_id=", mask
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, state);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -25,7 +25,7 @@ CommandParse(char *cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
end_data = strchr(cmd, ' ');
|
end_data = strchr(cmd, ' ');
|
||||||
if (!end_data)
|
if (!end_data || (cmd > end_data))
|
||||||
{
|
{
|
||||||
ret = Malloc(sizeof(*ret));
|
ret = Malloc(sizeof(*ret));
|
||||||
ret->command = StrDuplicate(cmd);
|
ret->command = StrDuplicate(cmd);
|
||||||
|
|
@ -49,11 +49,11 @@ CommandParse(char *cmd)
|
||||||
char c = *cur;
|
char c = *cur;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
bool type;
|
bool type;
|
||||||
char char_type;
|
char char_type = '\0';
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case STATE_WHITE:
|
case STATE_WHITE:
|
||||||
if (!isblank(c))
|
if (!isblank((int) c))
|
||||||
{
|
{
|
||||||
state = STATE_NAME;
|
state = STATE_NAME;
|
||||||
namestart = cur;
|
namestart = cur;
|
||||||
|
|
@ -84,7 +84,7 @@ CommandParse(char *cmd)
|
||||||
{
|
{
|
||||||
char c = *cur;
|
char c = *cur;
|
||||||
char cb[2] = { c, '\0' };
|
char cb[2] = { c, '\0' };
|
||||||
if ((type && c == char_type) || (!type && isblank(c)))
|
if ((type && c == char_type) || (!type && isblank((int) c)))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,35 +15,47 @@ CommandCreateRouter(void)
|
||||||
void
|
void
|
||||||
CommandAddCommand(CommandRouter *rter, char *c, CommandRoute rte)
|
CommandAddCommand(CommandRouter *rter, char *c, CommandRoute rte)
|
||||||
{
|
{
|
||||||
|
CommandRoute *indirect;
|
||||||
if (!rter || !c || !rte)
|
if (!rter || !c || !rte)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
HashMapSet(rter->routes, c, rte);
|
|
||||||
|
/* Little dirty trick to force C99 into submission, and since
|
||||||
|
* some architectures may separate data/code. Still don't like it... */
|
||||||
|
indirect = Malloc(sizeof(rte));
|
||||||
|
*indirect = rte;
|
||||||
|
HashMapSet(rter->routes, c, (void *) indirect);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
RouteCommand(CommandRouter *rter, Command *cmd, void *d)
|
RouteCommand(CommandRouter *rter, Command *cmd, void *d)
|
||||||
{
|
{
|
||||||
CommandRoute route;
|
CommandRoute *route;
|
||||||
if (!rter || !cmd)
|
if (!rter || !cmd)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
route = HashMapGet(rter->routes, cmd->command);
|
route = HashMapGet(rter->routes, cmd->command);
|
||||||
if (route)
|
if (route && *route)
|
||||||
{
|
{
|
||||||
route(cmd, d);
|
(*route)(cmd, d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
CommandFreeRouter(CommandRouter *rter)
|
CommandFreeRouter(CommandRouter *rter)
|
||||||
{
|
{
|
||||||
|
char *key;
|
||||||
|
CommandRoute *val;
|
||||||
if (!rter)
|
if (!rter)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (HashMapIterate(rter->routes, &key, (void **) &val))
|
||||||
|
{
|
||||||
|
Free(val);
|
||||||
|
}
|
||||||
HashMapFree(rter->routes);
|
HashMapFree(rter->routes);
|
||||||
Free(rter);
|
Free(rter);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,28 @@ CommandHead(CmdBanUser, cmd, argp)
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ASBan(data->config, room, user);
|
|
||||||
|
|
||||||
ReplySprintf("Banning %s from '%s'...",
|
ASBan(data->config, room, user);
|
||||||
user, room
|
ReplySprintf("Banning %s from '%s'...", user, room);
|
||||||
);
|
|
||||||
|
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();
|
BotDestroy();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,6 @@ CommandHead(CmdHelp, cmd, argp)
|
||||||
);
|
);
|
||||||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
|
|
||||||
|
(void) cmd;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,4 +32,6 @@ CommandHead(CmdListBans, cmd, argp)
|
||||||
|
|
||||||
DbUnlock(data->db, listed);
|
DbUnlock(data->db, listed);
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
|
|
||||||
|
(void) cmd;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ CommandHead(CmdPlumb, cmd, argp)
|
||||||
BotRequired(room);
|
BotRequired(room);
|
||||||
|
|
||||||
/* Check MUC viability */
|
/* Check MUC viability */
|
||||||
if (ParseeManageBan(args->data, muc, NULL))
|
if (ParseeManageBan(args->data, muc, NULL) ||
|
||||||
|
!ParseeIsMUCWhitelisted(args->data, muc))
|
||||||
{
|
{
|
||||||
ReplySprintf("MUC '%s' is not allowed on this bridge.", muc);
|
ReplySprintf("MUC '%s' is not allowed on this bridge.", muc);
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -62,7 +63,7 @@ CommandHead(CmdPlumb, cmd, argp)
|
||||||
if (chat_id)
|
if (chat_id)
|
||||||
{
|
{
|
||||||
char *rev = StrConcat(2, muc, "/parsee");
|
char *rev = StrConcat(2, muc, "/parsee");
|
||||||
XMPPJoinMUC(args->data->jabber, "parsee", rev);
|
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
|
||||||
|
|
||||||
Free(rev);
|
Free(rev);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ CommandHead(CmdStats, cmd, argp)
|
||||||
BotInitialise();
|
BotInitialise();
|
||||||
|
|
||||||
|
|
||||||
/* TODO: Separate these into different "categories" */
|
|
||||||
ReplySprintf("Information for %s v%s (Cytoplasm %s)",
|
ReplySprintf("Information for %s v%s (Cytoplasm %s)",
|
||||||
NAME, VERSION, CytoplasmGetVersionStr()
|
NAME, VERSION, CytoplasmGetVersionStr()
|
||||||
);
|
);
|
||||||
|
|
@ -41,4 +40,6 @@ CommandHead(CmdStats, cmd, argp)
|
||||||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||||
|
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
|
|
||||||
|
(void) cmd;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,43 +9,60 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static bool
|
||||||
|
Grab(ParseeData *data, Command *cmd, char **muc, char **chat_id, char **room)
|
||||||
|
{
|
||||||
|
if (HashMapGet(cmd->arguments, "muc"))
|
||||||
|
{
|
||||||
|
*muc = HashMapGet(cmd->arguments, "muc");
|
||||||
|
|
||||||
|
*chat_id = ParseeGetFromMUCID(data, *muc);
|
||||||
|
*room = ParseeGetRoomID(data, *chat_id);
|
||||||
|
if (!chat_id || !room)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (HashMapGet(cmd->arguments, "room"))
|
||||||
|
{
|
||||||
|
*room = HashMapGet(cmd->arguments, "room");
|
||||||
|
|
||||||
|
*chat_id = ParseeGetFromRoomID(data, *room);
|
||||||
|
*muc = ParseeGetMUCID(data, *chat_id);
|
||||||
|
if (!chat_id || !muc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CommandHead(CmdUnlinkMUC, cmd, argp)
|
CommandHead(CmdUnlinkMUC, cmd, argp)
|
||||||
{
|
{
|
||||||
ParseeCmdArg *args = argp;
|
ParseeCmdArg *args = argp;
|
||||||
ParseeData *data = args->data;
|
ParseeData *data = args->data;
|
||||||
HashMap *json, *event = args->event, *mucs;
|
HashMap *event = args->event;
|
||||||
DbRef *ref;
|
|
||||||
char *muc = NULL, *chat_id = NULL, *room = NULL;
|
char *muc = NULL, *chat_id = NULL, *room = NULL;
|
||||||
|
|
||||||
BotInitialise();
|
BotInitialise();
|
||||||
|
|
||||||
muc = HashMapGet(cmd->arguments, "muc");
|
if (!Grab(data, cmd, &muc, &chat_id, &room))
|
||||||
if (!muc)
|
|
||||||
{
|
{
|
||||||
ReplyBasic("`muc` field REQUIRED.");
|
ReplyBasic("`muc`|`room` REQUIRED");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref = DbLock(data->db, 1, "chats");
|
chat_id = ParseeGetFromMUCID(data, muc);
|
||||||
json = DbJson(ref);
|
room = ParseeGetRoomID(data, chat_id);
|
||||||
chat_id = StrDuplicate(GrabString(json, 2, "mucs", muc));
|
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
{
|
{
|
||||||
ReplySprintf("No internal mapping to '%s'.", muc);
|
ReplySprintf("No internal mapping to '%s'.", muc);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
mucs = GrabObject(json, 1, "mucs");
|
|
||||||
JsonValueFree(HashMapDelete(mucs, muc));
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
|
|
||||||
room = ParseeGetRoomID(data, chat_id);
|
ParseeUnlinkRoom(data, chat_id);
|
||||||
ref = DbLock(data->db, 1, "chats");
|
|
||||||
json = DbJson(ref);
|
|
||||||
mucs = GrabObject(json, 1, "rooms");
|
|
||||||
JsonValueFree(HashMapDelete(mucs, room));
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
|
|
||||||
DbDelete(data->db, 2, "chats", chat_id);
|
|
||||||
|
|
||||||
/* TODO: Do it automatically, if *not plumbed* */
|
/* TODO: Do it automatically, if *not plumbed* */
|
||||||
ReplySprintf("The MUC %s is now *unlinked*.", muc);
|
ReplySprintf("The MUC %s is now *unlinked*.", muc);
|
||||||
|
|
|
||||||
27
src/Events.c
27
src/Events.c
|
|
@ -37,12 +37,10 @@ MatrixCreateMessage(char *body)
|
||||||
HashMapSet(map, "msgtype", JsonValueString("m.text"));
|
HashMapSet(map, "msgtype", JsonValueString("m.text"));
|
||||||
HashMapSet(map, "body", JsonValueString(body));
|
HashMapSet(map, "body", JsonValueString(body));
|
||||||
{
|
{
|
||||||
/* TODO */
|
|
||||||
XEP393Element *e = XEP393(body);
|
XEP393Element *e = XEP393(body);
|
||||||
text = XEP393ToXMLString(e);
|
text = XEP393ToXMLString(e);
|
||||||
XEP393FreeElement(e);
|
XEP393FreeElement(e);
|
||||||
|
|
||||||
|
|
||||||
HashMapSet(map, "formatted_body", JsonValueString(text));
|
HashMapSet(map, "formatted_body", JsonValueString(text));
|
||||||
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
||||||
Free(text);
|
Free(text);
|
||||||
|
|
@ -81,7 +79,7 @@ MatrixCreateNickChange(char *nick)
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
HashMap *
|
HashMap *
|
||||||
MatrixCreateMedia(char *mxc, char *body, char *mime)
|
MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
||||||
{
|
{
|
||||||
HashMap *map;
|
HashMap *map;
|
||||||
char *mime_type = NULL, *matrix_type = NULL;
|
char *mime_type = NULL, *matrix_type = NULL;
|
||||||
|
|
@ -93,9 +91,10 @@ MatrixCreateMedia(char *mxc, char *body, char *mime)
|
||||||
matrix_type = "m.file";
|
matrix_type = "m.file";
|
||||||
if (mime)
|
if (mime)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i, len;
|
||||||
mime_type = StrDuplicate(mime);
|
mime_type = StrDuplicate(mime);
|
||||||
for (i = 0; i < strlen(mime); i++)
|
len = strlen(mime);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (mime_type[i] == '/')
|
if (mime_type[i] == '/')
|
||||||
{
|
{
|
||||||
|
|
@ -121,6 +120,12 @@ MatrixCreateMedia(char *mxc, char *body, char *mime)
|
||||||
}
|
}
|
||||||
|
|
||||||
map = HashMapCreate();
|
map = HashMapCreate();
|
||||||
|
JsonSet(map, JsonValueString(mime), 2, "info", "mimetype");
|
||||||
|
if (info && info->width && info->height)
|
||||||
|
{
|
||||||
|
JsonSet(map, JsonValueInteger(info->width), 2, "info", "w");
|
||||||
|
JsonSet(map, JsonValueInteger(info->height), 2, "info", "h");
|
||||||
|
}
|
||||||
HashMapSet(map, "msgtype", JsonValueString(matrix_type));
|
HashMapSet(map, "msgtype", JsonValueString(matrix_type));
|
||||||
HashMapSet(map, "mimetype", JsonValueString(mime));
|
HashMapSet(map, "mimetype", JsonValueString(mime));
|
||||||
HashMapSet(map, "body", JsonValueString(body));
|
HashMapSet(map, "body", JsonValueString(body));
|
||||||
|
|
@ -182,6 +187,18 @@ MatrixCreateReplace(char *event, char *body)
|
||||||
HashMapSet(map, "m.new_content", JsonValueObject(new));
|
HashMapSet(map, "m.new_content", JsonValueObject(new));
|
||||||
HashMapSet(map, "m.relates_to", JsonValueObject(rel));
|
HashMapSet(map, "m.relates_to", JsonValueObject(rel));
|
||||||
|
|
||||||
|
{
|
||||||
|
XEP393Element *e = XEP393(body);
|
||||||
|
char *text = XEP393ToXMLString(e);
|
||||||
|
XEP393FreeElement(e);
|
||||||
|
|
||||||
|
HashMapSet(map, "formatted_body", JsonValueString(text));
|
||||||
|
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
||||||
|
HashMapSet(new, "formatted_body", JsonValueString(text));
|
||||||
|
HashMapSet(new, "format", JsonValueString("org.matrix.custom.html"));
|
||||||
|
Free(text);
|
||||||
|
}
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
HashMap *
|
HashMap *
|
||||||
|
|
|
||||||
68
src/FileInfo.c
Normal file
68
src/FileInfo.c
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <FileInfo.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
GetField(XMLElement *elem)
|
||||||
|
{
|
||||||
|
XMLElement *child;
|
||||||
|
if (!elem || ArraySize(elem->children) != 1)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = ArrayGet(elem->children, 0);
|
||||||
|
|
||||||
|
return strtol(child->data, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileInfo *
|
||||||
|
FileInfoFromXMPP(XMLElement *stanza)
|
||||||
|
{
|
||||||
|
FileInfo *info;
|
||||||
|
XMLElement *reference, *sims, *file;
|
||||||
|
|
||||||
|
if (!stanza)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference = XMLookForTKV(stanza,
|
||||||
|
"reference", "xmlns", "urn:xmpp:reference:0"
|
||||||
|
);
|
||||||
|
sims = XMLookForTKV(reference,
|
||||||
|
"media-sharing", "xmlns", "urn:xmpp:sims:1"
|
||||||
|
);
|
||||||
|
file = XMLookForTKV(sims,
|
||||||
|
"file", "xmlns", "urn:xmpp:jingle:apps:file-transfer:5"
|
||||||
|
);
|
||||||
|
/* TODO: We'll definitely need MIME types to do things like
|
||||||
|
* WebXDC */
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
info = Malloc(sizeof(*info));
|
||||||
|
|
||||||
|
|
||||||
|
info->width = GetField(XMLookForUnique(file, "width"));
|
||||||
|
info->height = GetField(XMLookForUnique(file, "height"));
|
||||||
|
info->size = GetField(XMLookForUnique(file, "size"));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FileInfoFree(FileInfo *info)
|
||||||
|
{
|
||||||
|
if (!info)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(info);
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,6 @@ GlobMatches(char *rule, char *string)
|
||||||
switch (c1)
|
switch (c1)
|
||||||
{
|
{
|
||||||
case '*':
|
case '*':
|
||||||
/* TODO */
|
|
||||||
while ((c2 = *string) && (c2 != next))
|
while ((c2 = *string) && (c2 != next))
|
||||||
{
|
{
|
||||||
string++;
|
string++;
|
||||||
|
|
|
||||||
|
|
@ -29,19 +29,18 @@ ParseeRequest(HttpServerContext *ctx, void *argp)
|
||||||
|
|
||||||
arg.stream = stream;
|
arg.stream = stream;
|
||||||
|
|
||||||
Log(LOG_NOTICE, "%s %s",
|
Log(LOG_DEBUG, "%s %s",
|
||||||
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
||||||
path
|
path
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!HttpRouterRoute(data->router, path, &arg, (void **) &response))
|
if (!HttpRouterRoute(data->router, path, &arg, (void **) &response))
|
||||||
{
|
{
|
||||||
Log(LOG_NOTICE, "Couldn't route %s", path);
|
Log(LOG_DEBUG, "Couldn't route %s", path);
|
||||||
HttpResponseStatus(ctx, HTTP_NOT_FOUND);
|
HttpResponseStatus(ctx, HTTP_NOT_FOUND);
|
||||||
JsonFree(response);
|
JsonFree(response);
|
||||||
|
|
||||||
response = MatrixCreateError("M_NOT_FOUND", "Route not found.");
|
response = MatrixCreateError("M_NOT_FOUND", "Route not found.");
|
||||||
/* TODO: Set a thing */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Whatever, we routed a thing. */
|
/* Whatever, we routed a thing. */
|
||||||
|
|
@ -56,8 +55,11 @@ ParseeRequest(HttpServerContext *ctx, void *argp)
|
||||||
HttpSendHeaders(ctx);
|
HttpSendHeaders(ctx);
|
||||||
JsonEncode(response, stream, JSON_DEFAULT);
|
JsonEncode(response, stream, JSON_DEFAULT);
|
||||||
JsonFree(response);
|
JsonFree(response);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
Log(LOG_DEBUG, "%s %s (%d)",
|
||||||
|
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
||||||
|
path, HttpResponseStatusGet(ctx)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClientContext *
|
HttpClientContext *
|
||||||
|
|
@ -71,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
|
||||||
);
|
);
|
||||||
|
|
|
||||||
228
src/Main.c
228
src/Main.c
|
|
@ -1,5 +1,6 @@
|
||||||
#include <Cytoplasm/HttpServer.h>
|
#include <Cytoplasm/HttpServer.h>
|
||||||
#include <Cytoplasm/Cytoplasm.h>
|
#include <Cytoplasm/Cytoplasm.h>
|
||||||
|
#include <Cytoplasm/Platform.h>
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Cron.h>
|
#include <Cytoplasm/Cron.h>
|
||||||
|
|
@ -12,6 +13,7 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <StanzaBuilder.h>
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
@ -27,6 +29,79 @@ ParseeUptime(void)
|
||||||
return UtilTsMillis() - start;
|
return UtilTsMillis() - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const Argument arguments[] =
|
||||||
|
{
|
||||||
|
#define Arg(c, req, vdesc, desc) \
|
||||||
|
{ \
|
||||||
|
.end = false, \
|
||||||
|
.argument = c, .value_req = req, \
|
||||||
|
.value_descr = vdesc, \
|
||||||
|
.description = desc \
|
||||||
|
},
|
||||||
|
#define EndOfArgs { .end = true }
|
||||||
|
|
||||||
|
Arg('H', true, "number(=8)", "Sets the number of HTTP threads")
|
||||||
|
Arg('J', true, "number(=8)", "Sets the number of XMPP threads")
|
||||||
|
Arg('C', true, "file(='parsee.json')", "Sets the JSON config to use")
|
||||||
|
|
||||||
|
Arg('g', false, NULL,
|
||||||
|
"Generates a parsee.yaml AS file before exiting")
|
||||||
|
Arg('v', false, NULL,
|
||||||
|
"Forces Parsee to print in a more verbose fashion "
|
||||||
|
"(-vvv prints stanzas to stderr)")
|
||||||
|
Arg('h', false, NULL,
|
||||||
|
"Generates an help screen(this one!)")
|
||||||
|
|
||||||
|
EndOfArgs
|
||||||
|
|
||||||
|
#undef EndOfArgs
|
||||||
|
#undef Argument
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeCheckMatrix(void *datp)
|
||||||
|
{
|
||||||
|
static volatile uint64_t streak = 0;
|
||||||
|
ParseeData *data = datp;
|
||||||
|
if (data->config->accept_pings && !ASPing(data->config))
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
|
||||||
|
if (++streak == 10)
|
||||||
|
{
|
||||||
|
DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats");
|
||||||
|
HashMap *json = DbJson(ref);
|
||||||
|
HashMap *mucs = GrabObject(json, 1, "mucs");
|
||||||
|
char *muc;
|
||||||
|
void *ignored;
|
||||||
|
|
||||||
|
|
||||||
|
/* Notify any potential MUCs about this */
|
||||||
|
while (HashMapIterate(mucs, &muc, &ignored))
|
||||||
|
{
|
||||||
|
char *id = StrRandom(32);
|
||||||
|
char *sender = StrConcat(3, "parsee@", data->jabber->host, "/parsee");
|
||||||
|
StanzaBuilder *b = CreateStanzaBuilder(sender, muc, id);
|
||||||
|
SetStanzaType(b, "groupchat");
|
||||||
|
SetStanzaBody(b,
|
||||||
|
"This bridge hasn't been able to reach the Matrix host, and "
|
||||||
|
"as such, some messages may not have been sent over."
|
||||||
|
);
|
||||||
|
|
||||||
|
WriteoutStanza(b, data->jabber, 0);
|
||||||
|
DestroyStanzaBuilder(b);
|
||||||
|
|
||||||
|
Free(sender);
|
||||||
|
Free(id);
|
||||||
|
}
|
||||||
|
(void) ignored;
|
||||||
|
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
streak = 0;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
Main(Array *args, HashMap *env)
|
Main(Array *args, HashMap *env)
|
||||||
{
|
{
|
||||||
|
|
@ -35,6 +110,11 @@ Main(Array *args, HashMap *env)
|
||||||
Stream *yaml;
|
Stream *yaml;
|
||||||
Cron *cron = NULL;
|
Cron *cron = NULL;
|
||||||
|
|
||||||
|
char *configuration = "parsee.json";
|
||||||
|
int xmpp = 8;
|
||||||
|
int http = 8;
|
||||||
|
int verbose = 0;
|
||||||
|
|
||||||
start = UtilTsMillis();
|
start = UtilTsMillis();
|
||||||
|
|
||||||
memset(&conf, 0, sizeof(conf));
|
memset(&conf, 0, sizeof(conf));
|
||||||
|
|
@ -42,24 +122,32 @@ Main(Array *args, HashMap *env)
|
||||||
"%s - v%s[%s] (Cytoplasm %s)",
|
"%s - v%s[%s] (Cytoplasm %s)",
|
||||||
NAME, VERSION, CODE, CytoplasmGetVersionStr()
|
NAME, VERSION, CODE, CytoplasmGetVersionStr()
|
||||||
);
|
);
|
||||||
|
ParseePrintASCII();
|
||||||
Log(LOG_INFO, "=======================");
|
Log(LOG_INFO, "=======================");
|
||||||
LogConfigIndent(LogConfigGlobal());
|
Log(LOG_INFO, "(C)opyright 2024-2025 LDA and other contributors");
|
||||||
|
Log(LOG_INFO, "(This program is free software, see LICENSE.)");
|
||||||
|
|
||||||
ParseeConfigLoad("parsee.json");
|
#ifdef PLATFORM_IPHONE
|
||||||
ParseeConfigInit();
|
Log(LOG_WARNING, "Wait. Are you running this on an iPhone?");
|
||||||
parsee_conf = ParseeConfigGet();
|
Log(LOG_WARNING, "You *ought* to have spoofed this, haven't you?");
|
||||||
|
Log(LOG_WARNING, "Simply jealous of you for doing this.");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LogConfigIndent(LogConfigGlobal());
|
||||||
|
|
||||||
{
|
{
|
||||||
ArgParseState state;
|
ArgParseState state;
|
||||||
|
char *opts = ParseeGenerateGetopt(arguments);
|
||||||
int flag;
|
int flag;
|
||||||
int xmpp = 8;
|
|
||||||
int http = 8;
|
|
||||||
|
|
||||||
ArgParseStateInit(&state);
|
ArgParseStateInit(&state);
|
||||||
while ((flag = ArgParse(&state, args, "gH:J:")) != -1)
|
while ((flag = ArgParse(&state, args, opts)) != -1)
|
||||||
{
|
{
|
||||||
switch (flag)
|
switch (flag)
|
||||||
{
|
{
|
||||||
|
case 'h':
|
||||||
|
ParseeGenerateHelp(arguments);
|
||||||
|
Free(opts);
|
||||||
|
goto end;
|
||||||
case 'H':
|
case 'H':
|
||||||
http = strtol(state.optArg, NULL, 10);
|
http = strtol(state.optArg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
|
|
@ -70,46 +158,155 @@ Main(Array *args, HashMap *env)
|
||||||
/* Write out the config file to a YAML document */
|
/* Write out the config file to a YAML document */
|
||||||
Log(LOG_INFO, "Generating YAML...");
|
Log(LOG_INFO, "Generating YAML...");
|
||||||
yaml = StreamOpen("parsee.yaml", "w");
|
yaml = StreamOpen("parsee.yaml", "w");
|
||||||
|
ParseeConfigLoad(configuration);
|
||||||
|
ParseeConfigInit();
|
||||||
ParseeExportConfigYAML(yaml);
|
ParseeExportConfigYAML(yaml);
|
||||||
StreamClose(yaml);
|
StreamClose(yaml);
|
||||||
|
Free(opts);
|
||||||
|
goto end;
|
||||||
|
case 'v':
|
||||||
|
switch (++verbose)
|
||||||
|
{
|
||||||
|
case PARSEE_VERBOSE_LOG:
|
||||||
|
LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG);
|
||||||
|
break;
|
||||||
|
case PARSEE_VERBOSE_TIMINGS:
|
||||||
|
Log(LOG_DEBUG, "Logging bench information.");
|
||||||
|
break;
|
||||||
|
case PARSEE_VERBOSE_STANZA:
|
||||||
|
Log(LOG_DEBUG, "Enabling stanza printing.");
|
||||||
|
break;
|
||||||
|
case PARSEE_VERBOSE_COMICAL:
|
||||||
|
Log(LOG_DEBUG, "What?");
|
||||||
|
Log(LOG_DEBUG, "No, but like, what do you except?");
|
||||||
|
Log(LOG_DEBUG, "Like do you want to log _every_ instruction?");
|
||||||
|
Log(LOG_DEBUG, "Like just every single thing %s does?", NAME);
|
||||||
|
Log(LOG_DEBUG, " ( why??? )");
|
||||||
|
Log(LOG_DEBUG, ".....................................");
|
||||||
|
Log(LOG_DEBUG, "Argh.");
|
||||||
|
Log(LOG_DEBUG, "Alright. I'll do my best.");
|
||||||
|
Log(LOG_DEBUG, "Get what you paid for.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
if (!UtilLastModified(state.optArg))
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "Invalid config: %s", state.optArg);
|
||||||
|
Log(LOG_ERR, "Ignoring.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
configuration = state.optArg;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
Log(LOG_ERR, "INVALID ARGUMENT GIVEN");
|
||||||
|
Free(opts);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParseeSetThreads(xmpp, http);
|
Free(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG, "Loading configuration...");
|
||||||
|
}
|
||||||
|
ParseeConfigLoad(configuration);
|
||||||
|
ParseeConfigInit();
|
||||||
|
parsee_conf = ParseeConfigGet();
|
||||||
|
if (!parsee_conf)
|
||||||
|
{
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
ParseeSetThreads(xmpp, http);
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Could not connect to XMPP...");
|
Log(LOG_ERR, "Could not connect to XMPP...");
|
||||||
|
|
||||||
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG, "Destroying component...");
|
||||||
|
}
|
||||||
XMPPEndCompStream(jabber);
|
XMPPEndCompStream(jabber);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Creating volatile tables...");
|
Log(LOG_NOTICE, "Creating volatile tables...");
|
||||||
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG, "Initialising JID table");
|
||||||
|
}
|
||||||
ParseeInitialiseJIDTable();
|
ParseeInitialiseJIDTable();
|
||||||
|
|
||||||
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG, "Initialising OID table");
|
||||||
|
}
|
||||||
ParseeInitialiseOIDTable();
|
ParseeInitialiseOIDTable();
|
||||||
|
|
||||||
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG, "Initialising head table");
|
||||||
|
}
|
||||||
ParseeInitialiseHeadTable();
|
ParseeInitialiseHeadTable();
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Setting up local Matrix user...");
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||||
ASRegisterUser(parsee_conf, parsee_conf->sender_localpart);
|
{
|
||||||
|
Log(LOG_DEBUG, "Initialising nick table");
|
||||||
|
}
|
||||||
|
ParseeInitialiseNickTable();
|
||||||
|
|
||||||
|
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||||
|
{
|
||||||
|
Log(LOG_DEBUG, "Initialising affiliation table");
|
||||||
|
}
|
||||||
|
ParseeInitialiseAffiliationTable();
|
||||||
|
|
||||||
conf.port = parsee_conf->port;
|
conf.port = parsee_conf->port;
|
||||||
conf.threads = parsee_conf->http_threads;
|
conf.threads = parsee_conf->http_threads;
|
||||||
conf.maxConnections = conf.threads << 2;
|
conf.maxConnections = conf.threads << 2;
|
||||||
conf.handlerArgs = ParseeInitData(jabber);
|
conf.handlerArgs = ParseeInitData(jabber);
|
||||||
conf.handler = ParseeRequest;
|
conf.handler = ParseeRequest;
|
||||||
|
if (!conf.handlerArgs)
|
||||||
|
{
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(LOG_DEBUG, "Verbosity level: %d", verbose);
|
||||||
|
((ParseeData *) conf.handlerArgs)->verbosity = verbose;
|
||||||
|
|
||||||
|
Log(LOG_NOTICE, "Setting up local Matrix user...");
|
||||||
|
if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart))
|
||||||
|
{
|
||||||
|
char *parsee = ParseeMXID(conf.handlerArgs);
|
||||||
|
|
||||||
|
ASSetAvatar(parsee_conf,
|
||||||
|
parsee,
|
||||||
|
"mxc://tedomum.net/"
|
||||||
|
"7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136"
|
||||||
|
);
|
||||||
|
ASSetName(parsee_conf, parsee, "Parsee bridge");
|
||||||
|
|
||||||
|
Free(parsee);
|
||||||
|
}
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Starting up local cronjobs...");
|
Log(LOG_NOTICE, "Starting up local cronjobs...");
|
||||||
cron = CronCreate( 10 SECONDS );
|
cron = CronCreate(10 SECONDS);
|
||||||
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
||||||
|
CronEvery(cron, 10 SECONDS, ParseeCheckMatrix, conf.handlerArgs);
|
||||||
|
ParseeCleanup(conf.handlerArgs);
|
||||||
|
|
||||||
CronStart(cron);
|
CronStart(cron);
|
||||||
|
|
||||||
|
|
@ -121,8 +318,9 @@ Main(Array *args, HashMap *env)
|
||||||
}
|
}
|
||||||
|
|
||||||
server = HttpServerCreate(&conf);
|
server = HttpServerCreate(&conf);
|
||||||
|
((ParseeData *) conf.handlerArgs)->server = server;
|
||||||
|
|
||||||
if (!ParseeInitialiseSignals(server, xmpp_thr, jabber))
|
if (!ParseeInitialiseSignals(conf.handlerArgs, xmpp_thr))
|
||||||
{
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
@ -146,8 +344,12 @@ end:
|
||||||
CronStop(cron);
|
CronStop(cron);
|
||||||
CronFree(cron);
|
CronFree(cron);
|
||||||
ParseeFreeData(conf.handlerArgs);
|
ParseeFreeData(conf.handlerArgs);
|
||||||
|
ParseeDestroyAffiliationTable();
|
||||||
|
ParseeDestroyNickTable();
|
||||||
ParseeDestroyOIDTable();
|
ParseeDestroyOIDTable();
|
||||||
ParseeDestroyHeadTable();
|
ParseeDestroyHeadTable();
|
||||||
ParseeDestroyJIDTable();
|
ParseeDestroyJIDTable();
|
||||||
|
|
||||||
|
(void) env;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,56 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <StanzaBuilder.h>
|
#include <StanzaBuilder.h>
|
||||||
|
#include <Unistring.h>
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to);
|
||||||
|
|
||||||
|
static char *
|
||||||
|
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char *hash)
|
||||||
|
{
|
||||||
|
char *sender = GrabString(event, 1, "sender");
|
||||||
|
|
||||||
|
Unistr *uninick = UnistrCreate(name);
|
||||||
|
Unistr *filtered = UnistrFilter(uninick, UnistrIsASCII); /* I'm not even going to try messing with the BMP anymore. */
|
||||||
|
char *nick = UnistrC(filtered);
|
||||||
|
char *rev = StrConcat(3, muc, "/", nick);
|
||||||
|
int nonce = 0;
|
||||||
|
|
||||||
|
UnistrFree(uninick);
|
||||||
|
UnistrFree(filtered);
|
||||||
|
|
||||||
|
/* TODO: vCards! */
|
||||||
|
while (!XMPPJoinMUC(data->jabber, jid, rev, hash, -1, true) && nonce < 32)
|
||||||
|
{
|
||||||
|
char *nonce_str = StrInt(nonce);
|
||||||
|
char *input = StrConcat(3, sender, name, nonce_str);
|
||||||
|
char *hex = ParseeHMACS(data->id, input);
|
||||||
|
|
||||||
|
if (strlen(hex) >= 8)
|
||||||
|
{
|
||||||
|
hex[8] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(nick);
|
||||||
|
Free(rev);
|
||||||
|
|
||||||
|
nick = StrConcat(4, name, "[", hex, "]");
|
||||||
|
rev = StrConcat(3, muc, "/", nick);
|
||||||
|
nonce++;
|
||||||
|
|
||||||
|
Free(nonce_str);
|
||||||
|
Free(input);
|
||||||
|
Free(hex);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParseePushNickTable(muc, sender, nick);
|
||||||
|
Free(nick);
|
||||||
|
return (rev);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ParseeMemberHandler(ParseeData *data, HashMap *event)
|
ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||||
{
|
{
|
||||||
|
|
@ -51,20 +98,95 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||||
else if (StrEquals(membership, "join") && !ParseeIsPuppet(conf, state_key))
|
else if (StrEquals(membership, "join") && !ParseeIsPuppet(conf, state_key))
|
||||||
{
|
{
|
||||||
char *jid = ParseeEncodeMXID(state_key);
|
char *jid = ParseeEncodeMXID(state_key);
|
||||||
|
char *sha = NULL, *mime = NULL;
|
||||||
|
char *avatar = ASGetAvatar(data->config, NULL, state_key);
|
||||||
|
char *url = ParseeToUnauth(data, avatar, NULL);
|
||||||
chat_id = ParseeGetFromRoomID(data, room_id);
|
chat_id = ParseeGetFromRoomID(data, room_id);
|
||||||
|
|
||||||
|
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||||
|
Free(avatar);
|
||||||
|
avatar = NULL;
|
||||||
if (chat_id)
|
if (chat_id)
|
||||||
{
|
{
|
||||||
char *muc = ParseeGetMUCID(data, chat_id);
|
char *muc = ParseeGetMUCID(data, chat_id);
|
||||||
char *rev = StrConcat(2, muc, "/parsee");
|
char *name = ASGetName(data->config, room_id, state_key);
|
||||||
|
char *jabber = JoinMUC(data, event, jid, muc, name, sha);
|
||||||
|
avatar = ASGetAvatar(data->config, NULL, state_key);
|
||||||
|
Log(LOG_DEBUG, "MATRIX: Joining as '%s' (avatar=%s)", jabber, avatar);
|
||||||
|
|
||||||
XMPPJoinMUC(data->jabber, jid, rev);
|
Free(jabber);
|
||||||
Free(rev);
|
Free(avatar);
|
||||||
|
Free(name);
|
||||||
Free(muc);
|
Free(muc);
|
||||||
|
avatar = NULL;
|
||||||
|
|
||||||
/* TODO: XEP-0084 magic to advertise a new avatar if possible. */
|
/* TODO: XEP-0084 magic to advertise a new avatar if possible. */
|
||||||
}
|
}
|
||||||
Free(jid);
|
else
|
||||||
|
{
|
||||||
|
char *full_jid = StrConcat(3,
|
||||||
|
jid, "@", data->config->component_host
|
||||||
|
);
|
||||||
|
XMLElement *elem, *pevent, *items, *item, *meta, *info;
|
||||||
|
|
||||||
|
Log(LOG_DEBUG, "MATRIX: Got local user '%s'(mxid=%s)", jid, state_key);
|
||||||
|
|
||||||
|
elem = XMLCreateTag("message");
|
||||||
|
{
|
||||||
|
#define PUBSUB "http://jabber.org/protocol/pubsub"
|
||||||
|
#define AVATAR "urn:xmpp:avatar:metadata"
|
||||||
|
pevent = XMLCreateTag("event");
|
||||||
|
XMLAddAttr(pevent, "xmlns", PUBSUB "#event");
|
||||||
|
{
|
||||||
|
items = XMLCreateTag("items");
|
||||||
|
item = XMLCreateTag("item");
|
||||||
|
XMLAddAttr(items, "node", AVATAR);
|
||||||
|
XMLAddAttr(item, "id", sha);
|
||||||
|
{
|
||||||
|
meta = XMLCreateTag("metadata");
|
||||||
|
info = XMLCreateTag("info");
|
||||||
|
XMLAddAttr(meta, "xmlns", AVATAR);
|
||||||
|
|
||||||
|
XMLAddAttr(info, "id", sha);
|
||||||
|
XMLAddAttr(info, "url", url);
|
||||||
|
XMLAddAttr(info, "type", mime);
|
||||||
|
|
||||||
|
XMLAddChild(meta, info);
|
||||||
|
XMLAddChild(item, meta);
|
||||||
|
}
|
||||||
|
XMLAddChild(items, item);
|
||||||
|
XMLAddChild(pevent, items);
|
||||||
|
}
|
||||||
|
XMLAddChild(elem, pevent);
|
||||||
|
#undef PUBSUB
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Broadcast PEP avatar change */
|
||||||
|
ParseeBroadcastStanza(data, full_jid, elem);
|
||||||
|
XMLFreeElement(elem);
|
||||||
|
|
||||||
|
elem = XMLCreateTag("presence");
|
||||||
|
{
|
||||||
|
XMLElement *x = XMLCreateTag("x");
|
||||||
|
XMLElement *photo;
|
||||||
|
|
||||||
|
XMLAddAttr(x, "xmlns", "vcard-temp:x:update");
|
||||||
|
photo = XMLCreateTag("photo");
|
||||||
|
XMLAddChild(photo, XMLCreateText(sha));
|
||||||
|
XMLAddChild(x, photo);
|
||||||
|
XMLAddChild(elem, x);
|
||||||
|
}
|
||||||
|
ParseeBroadcastStanza(data, full_jid, elem);
|
||||||
|
XMLFreeElement(elem);
|
||||||
|
|
||||||
|
Free(full_jid);
|
||||||
|
}
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
|
Free(avatar);
|
||||||
|
Free(mime);
|
||||||
|
Free(sha);
|
||||||
|
Free(jid);
|
||||||
|
Free(url);
|
||||||
}
|
}
|
||||||
else if ((StrEquals(membership, "leave") ||
|
else if ((StrEquals(membership, "leave") ||
|
||||||
StrEquals(membership, "ban"))
|
StrEquals(membership, "ban"))
|
||||||
|
|
@ -82,14 +204,29 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||||
muc_id = ParseeGetMUCID(data, chat_id);
|
muc_id = ParseeGetMUCID(data, chat_id);
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
{
|
{
|
||||||
|
/* If it can't be found, try to see if it's as a DM */
|
||||||
|
char *info_from = NULL, *info_to = NULL;
|
||||||
|
const char *type = GetXMPPInformation(data, event, &info_from, &info_to);
|
||||||
|
|
||||||
|
if (StrEquals(type, "chat"))
|
||||||
|
{
|
||||||
|
char *jid_to = ParseeTrimJID(info_to);
|
||||||
|
Log(LOG_DEBUG, "('%s'->'%s') is gone.", state_key, info_to);
|
||||||
|
/* TODO: Send a last DM, signifying that all is gone. */
|
||||||
|
ParseeDeleteDM(data, state_key, jid_to);
|
||||||
|
Free(jid_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(info_from);
|
||||||
|
Free(info_to);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Check the name's validity */
|
name = StrDuplicate(ParseeLookupNick(muc_id, sender));
|
||||||
name = ASGetName(data->config, room_id, state_key);
|
rev = StrConcat(3, muc_id, "/", name);
|
||||||
rev = StrConcat(4, muc_id, "/", name, "[p]");
|
|
||||||
|
|
||||||
XMPPLeaveMUC(jabber, jid, rev, reason);
|
XMPPLeaveMUC(jabber, jid, rev, reason);
|
||||||
|
ParseePushNickTable(muc_id, sender, NULL);
|
||||||
end:
|
end:
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
Free(muc_id);
|
Free(muc_id);
|
||||||
|
|
@ -118,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")
|
|
||||||
));
|
|
||||||
Free(profile);
|
Free(profile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +267,7 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
||||||
Free(ASSend(
|
Free(ASSend(
|
||||||
data->config, id, profile,
|
data->config, id, profile,
|
||||||
"m.room.message",
|
"m.room.message",
|
||||||
MatrixCreateNotice("You are not authorised to do this.")
|
MatrixCreateNotice("You are not authorised to do this."), 0
|
||||||
));
|
));
|
||||||
Free(profile);
|
Free(profile);
|
||||||
return;
|
return;
|
||||||
|
|
@ -157,12 +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;
|
||||||
|
|
||||||
XMPPComponent *jabber = data ? data->jabber : NULL;
|
|
||||||
|
|
||||||
bool direct = false;
|
bool direct = false;
|
||||||
if (!data || !event || !from || !to)
|
if (!data || !event || !from || !to)
|
||||||
|
|
@ -194,7 +324,8 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *matrix_name, *muc_join_as;
|
char *matrix_name = NULL, *matrix_avatar = NULL;
|
||||||
|
char *mime = NULL, *sha = NULL;
|
||||||
|
|
||||||
muc_id = ParseeGetMUCID(data, chat_id);
|
muc_id = ParseeGetMUCID(data, chat_id);
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
|
|
@ -208,18 +339,16 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
}
|
}
|
||||||
|
|
||||||
matrix_name = ASGetName(data->config, room_id, matrix_sender);
|
matrix_name = ASGetName(data->config, room_id, matrix_sender);
|
||||||
muc_join_as = StrConcat(4, muc_id, "/", matrix_name, "[p]");
|
matrix_avatar = ASGetAvatar(data->config, NULL, matrix_sender);
|
||||||
|
|
||||||
/* TODO: Manage name conflicts. That would have been an easy
|
|
||||||
* task(try the original one, and use a counter if it fails),
|
|
||||||
* but that'd involve modifying the rest of the code, which
|
|
||||||
* I'm not doing at 01:39 ... */
|
|
||||||
XMPPJoinMUC(jabber, *from, muc_join_as);
|
|
||||||
|
|
||||||
|
ASGetMIMESHA(data->config, matrix_avatar, &mime, &sha);
|
||||||
|
Free(JoinMUC(data, event, *from, muc_id, matrix_name, sha));
|
||||||
*to = muc_id;
|
*to = muc_id;
|
||||||
|
|
||||||
|
Free(matrix_avatar);
|
||||||
Free(matrix_name);
|
Free(matrix_name);
|
||||||
Free(muc_join_as);
|
Free(mime);
|
||||||
|
Free(sha);
|
||||||
}
|
}
|
||||||
|
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
|
|
@ -229,27 +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;
|
||||||
|
|
||||||
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))
|
||||||
{
|
{
|
||||||
|
|
@ -261,9 +410,6 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
|
|
||||||
chat_id = ParseeGetFromRoomID(data, id);
|
chat_id = ParseeGetFromRoomID(data, id);
|
||||||
|
|
||||||
/* TODO: This ref should be marked as read-only,
|
|
||||||
* as LMDB doesn't seem to like having two concurrent RW
|
|
||||||
* transactions running. */
|
|
||||||
ref = DbLockIntent(data->db, DB_HINT_READONLY, 3, "rooms", id, "data");
|
ref = DbLockIntent(data->db, DB_HINT_READONLY, 3, "rooms", id, "data");
|
||||||
json = DbJson(ref);
|
json = DbJson(ref);
|
||||||
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
|
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
|
||||||
|
|
@ -283,38 +429,44 @@ 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);
|
||||||
|
xmppified_user = StrConcat(3,
|
||||||
|
encoded_from, "@", jabber->host
|
||||||
|
);
|
||||||
if (direct)
|
if (direct)
|
||||||
{
|
{
|
||||||
xmppified_user = ParseeEncodeMXID(m_sender);
|
|
||||||
to = StrDuplicate(user);
|
to = StrDuplicate(user);
|
||||||
|
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *name, *rev;
|
char *name, *mime = NULL, *sha = NULL;
|
||||||
|
char *avatar;
|
||||||
/* Try to find the chat ID */
|
/* Try to find the chat ID */
|
||||||
muc_id = ParseeGetMUCID(data, chat_id);
|
muc_id = ParseeGetMUCID(data, chat_id);
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
{
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
xmppified_user = ParseeEncodeMXID(m_sender);
|
|
||||||
|
|
||||||
/* TODO: Check the name's validity.
|
/* TODO: Avoid using the AS endpoints */
|
||||||
* Is there a good way to check for that that isn't
|
|
||||||
* just "await on join and try again?" */
|
|
||||||
name = ASGetName(data->config, id, m_sender);
|
name = ASGetName(data->config, id, m_sender);
|
||||||
rev = StrConcat(4, muc_id, "/", name, "[p]");
|
avatar = ASGetAvatar(data->config, NULL, m_sender);
|
||||||
|
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||||
|
|
||||||
XMPPJoinMUC(jabber, xmppified_user, rev);
|
Free(JoinMUC(data, event, encoded_from, muc_id, name, sha));
|
||||||
|
|
||||||
to = muc_id;
|
to = muc_id;
|
||||||
|
|
||||||
|
Free(sha);
|
||||||
|
Free(mime);
|
||||||
Free(name);
|
Free(name);
|
||||||
Free(rev);
|
Free(avatar);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply_id)
|
if (reply_id)
|
||||||
{
|
{
|
||||||
/* TODO: Monocles chat DM users HATE this trick!
|
/* TODO: Monocles chat DM users HATE this trick!
|
||||||
|
|
@ -342,13 +494,13 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
char *xmpp_ident = StrRandom(32);
|
char *xmpp_ident = StrRandom(32);
|
||||||
builder = CreateStanzaBuilder(xmppified_user, to, xmpp_ident);
|
builder = CreateStanzaBuilder(xmppified_user, to, xmpp_ident);
|
||||||
SetStanzaType(builder, type);
|
SetStanzaType(builder, type);
|
||||||
SetStanzaBody(builder, xepd ? xepd : body);
|
SetStanzaBody(builder, unauth ? unauth : (xepd ? xepd : body));
|
||||||
SetStanzaReply(builder, stanza, sender);
|
SetStanzaReply(builder, stanza, sender);
|
||||||
SetStanzaLink(builder, unauth);
|
SetStanzaLink(builder, unauth);
|
||||||
SetStanzaEdit(builder, origin_id);
|
SetStanzaEdit(builder, origin_id);
|
||||||
SetStanzaXParsee(builder, event);
|
SetStanzaXParsee(builder, event);
|
||||||
|
|
||||||
WriteoutStanza(builder, jabber);
|
WriteoutStanza(builder, jabber, data->config->max_stanza_size);
|
||||||
DestroyStanzaBuilder(builder);
|
DestroyStanzaBuilder(builder);
|
||||||
|
|
||||||
if (direct)
|
if (direct)
|
||||||
|
|
@ -371,6 +523,7 @@ end:
|
||||||
Free(stanza);
|
Free(stanza);
|
||||||
Free(sender);
|
Free(sender);
|
||||||
Free(unauth);
|
Free(unauth);
|
||||||
|
Free(encoded_from);
|
||||||
Free(unedited_id);
|
Free(unedited_id);
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
|
|
@ -402,8 +555,7 @@ ParseeEventHandler(ParseeData *data, HashMap *event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (StrEquals(event_type, "m.room.message") ||
|
else if (StrEquals(event_type, "m.room.message") ||
|
||||||
StrEquals(event_type, "m.sticker")) /* TODO: Actual sticker
|
StrEquals(event_type, "m.sticker"))
|
||||||
* support here... */
|
|
||||||
{
|
{
|
||||||
ParseeMessageHandler(data, event);
|
ParseeMessageHandler(data, event);
|
||||||
Free(parsee);
|
Free(parsee);
|
||||||
|
|
|
||||||
68
src/MatrixID.c
Normal file
68
src/MatrixID.c
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Http.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
UserID *
|
||||||
|
MatrixParseID(char *user)
|
||||||
|
{
|
||||||
|
UserID *ret = NULL;
|
||||||
|
char *localstart, *serverstart;
|
||||||
|
if (!user || *user != '@')
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
localstart = user + 1;
|
||||||
|
serverstart = strchr(user, ':');
|
||||||
|
if (!*localstart || !serverstart || localstart == serverstart)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!*++serverstart)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = Malloc(sizeof(*ret));
|
||||||
|
memset(ret, '\0', sizeof(*ret));
|
||||||
|
memcpy(ret->localpart, localstart, serverstart - localstart - 1);
|
||||||
|
memcpy(ret->server, serverstart, strlen(serverstart));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -11,210 +11,15 @@
|
||||||
|
|
||||||
static ParseeConfig *config = NULL;
|
static ParseeConfig *config = NULL;
|
||||||
|
|
||||||
static char *
|
|
||||||
GetLine(void)
|
|
||||||
{
|
|
||||||
Stream *input = StreamStdin();
|
|
||||||
char *out = NULL;
|
|
||||||
size_t length;
|
|
||||||
UtilGetLine(&out, &length, input);
|
|
||||||
|
|
||||||
if (out)
|
|
||||||
{
|
|
||||||
char *line = strchr(out, '\n');
|
|
||||||
if (line)
|
|
||||||
{
|
|
||||||
*line = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
static char *
|
|
||||||
PromptString(const char *expression, const char *def, ...)
|
|
||||||
{
|
|
||||||
Stream *output = StreamStdout();
|
|
||||||
char *out = NULL;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
while (!out)
|
|
||||||
{
|
|
||||||
va_start(ap, def);
|
|
||||||
|
|
||||||
StreamVprintf(output, expression, ap);
|
|
||||||
if (def)
|
|
||||||
{
|
|
||||||
StreamPrintf(output, " [%s]", def);
|
|
||||||
}
|
|
||||||
StreamPrintf(output, ": ");
|
|
||||||
StreamFlush(output);
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
out = GetLine();
|
|
||||||
if (!*out)
|
|
||||||
{
|
|
||||||
Free(out);
|
|
||||||
out = NULL;
|
|
||||||
if (def)
|
|
||||||
{
|
|
||||||
return StrDuplicate(def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log(LOG_INFO, "R=%s", out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
static int
|
|
||||||
PromptInteger(const char *expression, int def, ...)
|
|
||||||
{
|
|
||||||
Stream *output = StreamStdout();
|
|
||||||
char *out;
|
|
||||||
long l;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, def);
|
|
||||||
|
|
||||||
StreamVprintf(output, expression, ap);
|
|
||||||
if (def >= 0)
|
|
||||||
{
|
|
||||||
StreamPrintf(output, " [%d]", def);
|
|
||||||
}
|
|
||||||
StreamPrintf(output, ": ");
|
|
||||||
StreamFlush(output);
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
char *inval;
|
|
||||||
out = GetLine();
|
|
||||||
l = strtol(out, &inval, 10);
|
|
||||||
Free(out);
|
|
||||||
|
|
||||||
/* Not a use-after-free, as we reference only the addresses. */
|
|
||||||
if (l != 0 || inval != out)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def >= 0)
|
|
||||||
{
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: Memleaks, galore! */
|
|
||||||
void
|
void
|
||||||
ParseeConfigInit(void)
|
ParseeConfigInit(void)
|
||||||
{
|
{
|
||||||
Stream *stream;
|
|
||||||
HashMap *json;
|
|
||||||
if (config)
|
if (config)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Log(LOG_ERR, "No config file found.");
|
||||||
/* TODO: Give the user an achievement at the end, just because they're
|
Log(LOG_ERR, "Please use parsee-config to initialise %s.", NAME);
|
||||||
* cool. */
|
|
||||||
Log(LOG_NOTICE, "It seems like it is the first time you have configured ");
|
|
||||||
Log(LOG_NOTICE, "Parsee.");
|
|
||||||
Log(LOG_NOTICE, "As such, I need to ask you a couple of questions before ");
|
|
||||||
Log(LOG_NOTICE, "being able to use it.");
|
|
||||||
Log(LOG_NOTICE, "(don't worry; it won't take too long.)");
|
|
||||||
Log(LOG_NOTICE, "");
|
|
||||||
Log(LOG_NOTICE, "");
|
|
||||||
|
|
||||||
config = Malloc(sizeof(*config));
|
|
||||||
config->as_token = StrRandom(32);
|
|
||||||
config->hs_token = StrRandom(32);
|
|
||||||
config->http_threads = 8;
|
|
||||||
config->xmpp_threads = 8;
|
|
||||||
config->db_size = 64 MB;
|
|
||||||
|
|
||||||
/* TODO: This is NOT user friendly, and I know it! */
|
|
||||||
config->sender_localpart = PromptString(
|
|
||||||
"Name of the bridge bot, used for commands and bridged rooms",
|
|
||||||
"_parsee_bridge"
|
|
||||||
);
|
|
||||||
config->namespace_base = PromptString(
|
|
||||||
"Base namespace for Parsee (so foo@bar.com => @[NS]_foo=40bar.com)",
|
|
||||||
"_jabber"
|
|
||||||
);
|
|
||||||
|
|
||||||
config->listen_as = StrDuplicate("localhost");
|
|
||||||
config->port = PromptInteger(
|
|
||||||
"Matrix port for the AS service to use",
|
|
||||||
7642 /* proposed by Saint */
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
config->component_host = PromptString(
|
|
||||||
"XMPP component to be used for the configuration",
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
config->component_port = PromptInteger(
|
|
||||||
"XMPP port for to use for '%s'",
|
|
||||||
5347, config->component_host
|
|
||||||
);
|
|
||||||
config->shared_comp_secret = PromptString(
|
|
||||||
"%s's shared secret",
|
|
||||||
NULL, config->component_host
|
|
||||||
);
|
|
||||||
|
|
||||||
config->homeserver_host = PromptString(
|
|
||||||
"Delegated homeserver to be used for the configuration",
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
config->homeserver_port = PromptInteger(
|
|
||||||
"HTTP port for to use for '%s'",
|
|
||||||
443, config->homeserver_host
|
|
||||||
);
|
|
||||||
|
|
||||||
config->db_path = PromptString(
|
|
||||||
"Base directory for Parsee data",
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
config->media_base = PromptString(
|
|
||||||
"Base media URL for bridged media",
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
/* TODO: Make that configurable. */
|
|
||||||
config->server_base = StrDuplicate(config->homeserver_host);
|
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Done! Please look over to the parsee.yaml file, ");
|
|
||||||
Log(LOG_NOTICE, "and follow the instructions listed in it. Then, ");
|
|
||||||
Log(LOG_NOTICE, "restart Parsee. ");
|
|
||||||
Log(LOG_NOTICE, "------------------------------------------------");
|
|
||||||
|
|
||||||
stream = StreamOpen("parsee.json", "w");
|
|
||||||
json = HashMapCreate();
|
|
||||||
HashMapSet(json, "as_token", JsonValueString(config->as_token));
|
|
||||||
HashMapSet(json, "hs_token", JsonValueString(config->hs_token));
|
|
||||||
|
|
||||||
HashMapSet(json, "sender", JsonValueString(config->sender_localpart));
|
|
||||||
HashMapSet(json, "namespace", JsonValueString(config->namespace_base));
|
|
||||||
HashMapSet(json, "listen_as", JsonValueString(config->listen_as));
|
|
||||||
HashMapSet(json, "port", JsonValueInteger(config->port));
|
|
||||||
|
|
||||||
HashMapSet(json, "hs_base", JsonValueString(config->server_base));
|
|
||||||
HashMapSet(json, "hs_host", JsonValueString(config->homeserver_host));
|
|
||||||
HashMapSet(json, "hs_port", JsonValueInteger(config->homeserver_port));
|
|
||||||
|
|
||||||
HashMapSet(json, "component_host", JsonValueString(config->component_host));
|
|
||||||
HashMapSet(json, "component_port", JsonValueInteger(config->component_port));
|
|
||||||
HashMapSet(json, "shared_comp_secret", JsonValueString(config->shared_comp_secret));
|
|
||||||
HashMapSet(json, "db", JsonValueString(config->db_path));
|
|
||||||
|
|
||||||
JsonEncode(json, stream, JSON_PRETTY);
|
|
||||||
JsonFree(json);
|
|
||||||
StreamClose(stream);
|
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
ParseeConfigLoad(char *conf)
|
ParseeConfigLoad(char *conf)
|
||||||
|
|
@ -225,12 +30,18 @@ ParseeConfigLoad(char *conf)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stream = StreamOpen("parsee.json", "r");
|
stream = StreamOpen(conf ? conf : "parsee.json", "r");
|
||||||
if (!stream)
|
if (!stream)
|
||||||
{
|
{
|
||||||
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( \
|
||||||
|
|
@ -239,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;
|
||||||
|
|
@ -253,10 +67,25 @@ 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");
|
||||||
|
if (!config->max_stanza_size)
|
||||||
|
{
|
||||||
|
/* Standard XMPP "minimum" maximum */
|
||||||
|
config->max_stanza_size = 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyToBool(ignore_bots, "ignore_bots");
|
||||||
|
|
||||||
CopyToStr(media_base, "media_base");
|
CopyToStr(media_base, "media_base");
|
||||||
|
|
||||||
|
|
@ -272,6 +101,7 @@ ParseeSetThreads(int xmpp, int http)
|
||||||
{
|
{
|
||||||
if (!config)
|
if (!config)
|
||||||
{
|
{
|
||||||
|
Achievement("THREAD COUNT REQUEST WITHOUT CONFIG", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config->http_threads = http;
|
config->http_threads = http;
|
||||||
|
|
@ -283,6 +113,7 @@ ParseeExportConfigYAML(Stream *stream)
|
||||||
{
|
{
|
||||||
if (!stream || !config)
|
if (!stream || !config)
|
||||||
{
|
{
|
||||||
|
Achievement("YAML EXPORT REQUEST WITHOUT CONFIG", true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StreamPrintf(stream, "# Autogenerated YAML AS entry for %s\n", NAME);
|
StreamPrintf(stream, "# Autogenerated YAML AS entry for %s\n", NAME);
|
||||||
|
|
@ -296,6 +127,7 @@ ParseeExportConfigYAML(Stream *stream)
|
||||||
StreamPrintf(stream, "hs_token: \"%s\"\n", config->hs_token);
|
StreamPrintf(stream, "hs_token: \"%s\"\n", config->hs_token);
|
||||||
StreamPrintf(stream, "sender_localpart: \"%s\"\n", config->sender_localpart);
|
StreamPrintf(stream, "sender_localpart: \"%s\"\n", config->sender_localpart);
|
||||||
StreamPrintf(stream, "protocols: [\"xmpp\", \"jabber\"]\n");
|
StreamPrintf(stream, "protocols: [\"xmpp\", \"jabber\"]\n");
|
||||||
|
StreamPrintf(stream, "receive_ephemeral: true\n"); /* TODO: Actually use that field */
|
||||||
StreamPrintf(stream, "\n");
|
StreamPrintf(stream, "\n");
|
||||||
StreamPrintf(stream, "namespaces: \n");
|
StreamPrintf(stream, "namespaces: \n");
|
||||||
StreamPrintf(stream, " users:\n");
|
StreamPrintf(stream, " users:\n");
|
||||||
|
|
@ -304,6 +136,7 @@ ParseeExportConfigYAML(Stream *stream)
|
||||||
StreamPrintf(stream, " aliases:\n");
|
StreamPrintf(stream, " aliases:\n");
|
||||||
StreamPrintf(stream, " - exclusive: true\n");
|
StreamPrintf(stream, " - exclusive: true\n");
|
||||||
StreamPrintf(stream, " regex: \"#%s_.*\"\n", config->namespace_base);
|
StreamPrintf(stream, " regex: \"#%s_.*\"\n", config->namespace_base);
|
||||||
|
StreamFlush(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -314,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);
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,18 @@
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <Routes.h>
|
#include <Routes.h>
|
||||||
#include <Glob.h>
|
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
ParseeData *
|
ParseeData *
|
||||||
ParseeInitData(XMPPComponent *comp)
|
ParseeInitData(XMPPComponent *comp)
|
||||||
{
|
{
|
||||||
|
char *version;
|
||||||
ParseeData *data;
|
ParseeData *data;
|
||||||
|
DbRef *ref;
|
||||||
if (!ParseeConfigGet())
|
if (!ParseeConfigGet())
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -26,8 +27,15 @@ 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();
|
||||||
|
pthread_mutex_init(&data->oidl, NULL);
|
||||||
|
|
||||||
|
data->halted = false;
|
||||||
|
pthread_mutex_init(&data->halt_lock, NULL);
|
||||||
|
|
||||||
if (data->config->db_size)
|
if (data->config->db_size)
|
||||||
{
|
{
|
||||||
data->db = DbOpenLMDB(data->config->db_path, data->config->db_size);
|
data->db = DbOpenLMDB(data->config->db_path, data->config->db_size);
|
||||||
|
|
@ -35,10 +43,47 @@ ParseeInitData(XMPPComponent *comp)
|
||||||
if (!data->db)
|
if (!data->db)
|
||||||
{
|
{
|
||||||
Log(LOG_WARNING, "LMDB doesn't seem to be setup.");
|
Log(LOG_WARNING, "LMDB doesn't seem to be setup.");
|
||||||
|
if (data->config->db_size)
|
||||||
|
{
|
||||||
Log(LOG_WARNING, "Falling back to flat-file.");
|
Log(LOG_WARNING, "Falling back to flat-file.");
|
||||||
|
}
|
||||||
data->db = DbOpen(data->config->db_path, 0);
|
data->db = DbOpen(data->config->db_path, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(ref = DbLock(data->db, 1, "info")))
|
||||||
|
{
|
||||||
|
char *id = StrRandom(64);
|
||||||
|
ref = DbCreate(data->db, 1, "info");
|
||||||
|
HashMapSet(DbJson(ref), "identifier", JsonValueString(id));
|
||||||
|
HashMapSet(DbJson(ref), "version", JsonValueString(VERSION));
|
||||||
|
Free(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
version = GrabString(DbJson(ref), 1, "version");
|
||||||
|
if (version && !ParseeIsCompatible(VERSION, version))
|
||||||
|
{
|
||||||
|
Log(LOG_WARNING, "Version mismatch(curr=%s db=%s).", VERSION, version);
|
||||||
|
Log(LOG_WARNING, "Yeah. You may want to _not_ do that.");
|
||||||
|
Log(LOG_WARNING, "(Parsee still needs an upgradepath mechanism.)");
|
||||||
|
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
DbClose(data->db);
|
||||||
|
|
||||||
|
HashMapFree(data->oid_servers);
|
||||||
|
pthread_mutex_destroy(&data->oidl);
|
||||||
|
|
||||||
|
XMPPEndCompStream(data->jabber);
|
||||||
|
|
||||||
|
HttpRouterFree(data->router);
|
||||||
|
CommandFreeRouter(data->handler);
|
||||||
|
|
||||||
|
Free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->id = StrDuplicate(GrabString(DbJson(ref), 1, "identifier"));
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
|
||||||
#define X_ROUTE(path, func) do {\
|
#define X_ROUTE(path, func) do {\
|
||||||
if (!HttpRouterAdd(data->router, path, func))\
|
if (!HttpRouterAdd(data->router, path, func))\
|
||||||
{\
|
{\
|
||||||
|
|
@ -56,12 +101,23 @@ ParseeInitData(XMPPComponent *comp)
|
||||||
void
|
void
|
||||||
ParseeFreeData(ParseeData *data)
|
ParseeFreeData(ParseeData *data)
|
||||||
{
|
{
|
||||||
|
char *entity;
|
||||||
|
XMLElement *disco;
|
||||||
if (!data)
|
if (!data)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (HashMapIterate(data->oid_servers, &entity, (void **) &disco))
|
||||||
|
{
|
||||||
|
XMLFreeElement(disco);
|
||||||
|
}
|
||||||
|
HashMapFree(data->oid_servers);
|
||||||
|
pthread_mutex_destroy(&data->oidl);
|
||||||
|
pthread_mutex_destroy(&data->halt_lock);
|
||||||
|
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);
|
||||||
|
|
@ -77,8 +133,6 @@ ParseeCleanup(void *datp)
|
||||||
size_t i;
|
size_t i;
|
||||||
uint64_t ts = UtilTsMillis();
|
uint64_t ts = UtilTsMillis();
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Cleaning up...");
|
|
||||||
|
|
||||||
chats = DbList(data->db, 1, "chats");
|
chats = DbList(data->db, 1, "chats");
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(chats); i++)
|
for (i = 0; i < ArraySize(chats); i++)
|
||||||
|
|
@ -128,9 +182,12 @@ ParseeCleanup(void *datp)
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
CleanupField(stanza, 30 MINUTES, 50);
|
/* TODO: Custom retention period for any 1.0 */
|
||||||
CleanupField(event, 30 MINUTES, 50);
|
CleanupField(stanza, 30 MINUTES, 500);
|
||||||
CleanupField(id, 30 MINUTES, 50);
|
CleanupField(event, 30 MINUTES, 500);
|
||||||
|
CleanupField(id, 30 MINUTES, 500);
|
||||||
|
|
||||||
|
/* TODO: Also cleanup user cache information */
|
||||||
#undef CleanupField
|
#undef CleanupField
|
||||||
}
|
}
|
||||||
DbListFree(chats);
|
DbListFree(chats);
|
||||||
|
|
@ -181,252 +238,15 @@ ParseeCleanup(void *datp)
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
CleanupField(stanza, 3 HOURS, 50);
|
CleanupField(stanza, 3 HOURS, 500);
|
||||||
CleanupField(event, 3 HOURS, 50);
|
CleanupField(event, 3 HOURS, 500);
|
||||||
CleanupField(id, 3 HOURS, 50);
|
CleanupField(id, 3 HOURS, 500);
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
DbListFree(chats);
|
DbListFree(chats);
|
||||||
}
|
}
|
||||||
int
|
|
||||||
ParseeFindDatastart(char *data)
|
|
||||||
{
|
|
||||||
char *startline;
|
|
||||||
bool found = false;
|
|
||||||
if (!data)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
startline = data;
|
|
||||||
while (startline)
|
|
||||||
{
|
|
||||||
char *endline = strchr(startline, '\n');
|
|
||||||
|
|
||||||
if (*startline != '>')
|
|
||||||
{
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
startline = endline ? endline + 1 : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) (startline - data);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include <StringStream.h>
|
|
||||||
typedef struct XMPPFlags {
|
|
||||||
bool quote;
|
|
||||||
} XMPPFlags;
|
|
||||||
static char *
|
|
||||||
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|
||||||
{
|
|
||||||
char *xepd = NULL, *tmp = NULL;
|
|
||||||
|
|
||||||
size_t i;
|
|
||||||
XMLElement *child;
|
|
||||||
char *reply_id = JsonValueAsString(
|
|
||||||
JsonGet(event, 4,
|
|
||||||
"content", "m.relates_to", "m.in_reply_to", "event_id"
|
|
||||||
));
|
|
||||||
char *room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
|
||||||
HashMap *referenced;
|
|
||||||
char *subxep;
|
|
||||||
#define Concat(strp) do \
|
|
||||||
{ \
|
|
||||||
size_t cidx; \
|
|
||||||
size_t len = strp ? strlen(strp) : 0; \
|
|
||||||
for (cidx = 0; cidx < len; cidx++) \
|
|
||||||
{ \
|
|
||||||
char cch[2] = { strp[cidx], 0 }; \
|
|
||||||
char nch = *cch ? strp[cidx+1] : '\0'; \
|
|
||||||
bool c = *cch == '\n' && nch != '>'; \
|
|
||||||
if (c && flags.quote) \
|
|
||||||
{ \
|
|
||||||
tmp = xepd; \
|
|
||||||
xepd = StrConcat(2, xepd, "\n>"); \
|
|
||||||
Free(tmp); \
|
|
||||||
continue; \
|
|
||||||
} \
|
|
||||||
tmp = xepd; \
|
|
||||||
xepd = StrConcat(2, xepd, cch); \
|
|
||||||
Free(tmp); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
while (0)
|
|
||||||
switch (elem->type)
|
|
||||||
{
|
|
||||||
case XML_ELEMENT_DATA:
|
|
||||||
Concat(elem->data);
|
|
||||||
break;
|
|
||||||
case XML_ELEMENT_TAG:
|
|
||||||
if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong"))
|
|
||||||
{
|
|
||||||
Concat("*");
|
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(elem->children, i);
|
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
|
||||||
|
|
||||||
Concat(subxep);
|
|
||||||
Free(subxep);
|
|
||||||
}
|
|
||||||
Concat("*");
|
|
||||||
}
|
|
||||||
else if (StrEquals(elem->name, "em"))
|
|
||||||
{
|
|
||||||
Concat("_");
|
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(elem->children, i);
|
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
|
||||||
|
|
||||||
Concat(subxep);
|
|
||||||
Free(subxep);
|
|
||||||
}
|
|
||||||
Concat("_");
|
|
||||||
}
|
|
||||||
else if (StrEquals(elem->name, "code"))
|
|
||||||
{
|
|
||||||
Concat("`");
|
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(elem->children, i);
|
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
|
||||||
|
|
||||||
Concat(subxep);
|
|
||||||
Free(subxep);
|
|
||||||
}
|
|
||||||
Concat("`");
|
|
||||||
}
|
|
||||||
else if (StrEquals(elem->name, "mx-reply"))
|
|
||||||
{
|
|
||||||
char *str;
|
|
||||||
referenced = ASFind(ParseeConfigGet(), room_id, reply_id);
|
|
||||||
str = JsonValueAsString(
|
|
||||||
JsonGet(referenced, 2, "content", "body")
|
|
||||||
);
|
|
||||||
if (!str)
|
|
||||||
{
|
|
||||||
JsonFree(referenced);
|
|
||||||
return xepd;
|
|
||||||
}
|
|
||||||
Concat(">");
|
|
||||||
flags.quote = true;
|
|
||||||
Concat(str);
|
|
||||||
flags.quote = false;
|
|
||||||
Concat("\n");
|
|
||||||
JsonFree(referenced);
|
|
||||||
}
|
|
||||||
else if (StrEquals(elem->name, "blockquote"))
|
|
||||||
{
|
|
||||||
Concat(">");
|
|
||||||
flags.quote = true;
|
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(elem->children, i);
|
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
|
||||||
|
|
||||||
Concat(subxep);
|
|
||||||
Free(subxep);
|
|
||||||
}
|
|
||||||
flags.quote = false;
|
|
||||||
Concat("\n");
|
|
||||||
}
|
|
||||||
else if (StrEquals(elem->name, "br"))
|
|
||||||
{
|
|
||||||
Concat("\n");
|
|
||||||
/* HTML fucking SUCKS */
|
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(elem->children, i);
|
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
|
||||||
|
|
||||||
Concat(subxep);
|
|
||||||
Free(subxep);
|
|
||||||
}
|
|
||||||
Concat("\n");
|
|
||||||
}
|
|
||||||
else if (StrEquals(elem->name, "a"))
|
|
||||||
{
|
|
||||||
char *href = HashMapGet(elem->attrs, "href");
|
|
||||||
Concat("(");
|
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(elem->children, i);
|
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
|
||||||
|
|
||||||
Concat(subxep);
|
|
||||||
Free(subxep);
|
|
||||||
}
|
|
||||||
Concat(" points to ");
|
|
||||||
Concat(href);
|
|
||||||
Concat(" )");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(elem->children, i);
|
|
||||||
subxep = XMPPifyElement(event, child, flags);
|
|
||||||
|
|
||||||
Concat(subxep);
|
|
||||||
Free(subxep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return xepd;
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ParseeXMPPify(HashMap *event)
|
|
||||||
{
|
|
||||||
char *type, *format, *html;
|
|
||||||
char *xepd = NULL;
|
|
||||||
XMLElement *elem;
|
|
||||||
|
|
||||||
XMPPFlags flags;
|
|
||||||
if (!event)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if it is a message event. */
|
|
||||||
type = JsonValueAsString(HashMapGet(event, "type"));
|
|
||||||
if (!StrEquals(type, "m.room.message"))
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
|
|
||||||
if (!StrEquals(format, "org.matrix.custom.html"))
|
|
||||||
{
|
|
||||||
/* Settle for the raw body instead. */
|
|
||||||
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
|
|
||||||
return StrDuplicate(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
|
|
||||||
html = StrConcat(3, "<html>", html, "</html>");
|
|
||||||
elem = XMLCDecode(StrStreamReader(html), true, true);
|
|
||||||
|
|
||||||
flags.quote = false;
|
|
||||||
xepd = XMPPifyElement(event, elem, flags);
|
|
||||||
|
|
||||||
XMLFreeElement(elem);
|
|
||||||
Free(html);
|
|
||||||
|
|
||||||
return xepd;
|
|
||||||
}
|
|
||||||
void
|
void
|
||||||
ParseePushDMStanza(ParseeData *data, char *room_id, char *stanza_id, char *id, char *ev, char *sender)
|
ParseePushDMStanza(ParseeData *data, char *room_id, char *stanza_id, char *id, char *ev, char *sender)
|
||||||
{
|
{
|
||||||
|
|
@ -519,6 +339,15 @@ ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, cha
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO */
|
/* TODO */
|
||||||
|
{
|
||||||
|
ref = DbLock(data->db, 2, "chats", chat_id);
|
||||||
|
j = DbJson(ref);
|
||||||
|
if (j)
|
||||||
|
{
|
||||||
|
JsonValueFree(HashMapSet(j, "ts", JsonValueInteger(age)));
|
||||||
|
}
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
ref = DbCreate(data->db, 4, "chats", chat_id, "stanzas", stanza_id);
|
ref = DbCreate(data->db, 4, "chats", chat_id, "stanzas", stanza_id);
|
||||||
j = DbJson(ref);
|
j = DbJson(ref);
|
||||||
|
|
@ -721,140 +550,178 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
ParseeUnlinkRoom(ParseeData *data, char *chat_id)
|
||||||
{
|
{
|
||||||
|
char *muc, *room;
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
HashMap *j, *obj;
|
if (!data || !chat_id)
|
||||||
if (!data || !glob)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref = DbLock(data->db, 1, "global_bans");
|
muc = ParseeGetMUCID(data, chat_id);
|
||||||
if (!ref)
|
room = ParseeGetRoomID(data, chat_id);
|
||||||
|
if (!muc || !room)
|
||||||
{
|
{
|
||||||
ref = DbCreate(data->db, 1, "global_bans");
|
Free(muc);
|
||||||
|
Free(room);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
j = DbJson(ref);
|
ref = DbLock(data->db, 1, "chats");
|
||||||
|
JsonValueFree(HashMapDelete(
|
||||||
obj = HashMapCreate();
|
GrabObject(DbJson(ref), 1, "rooms"),
|
||||||
if (reason)
|
room
|
||||||
{
|
));
|
||||||
HashMapSet(obj, "reason", JsonValueString(reason));
|
JsonValueFree(HashMapDelete(
|
||||||
}
|
GrabObject(DbJson(ref), 1, "mucs"),
|
||||||
HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis()));
|
muc
|
||||||
JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj)));
|
));
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
|
DbDelete(data->db, 2, "chats", chat_id);
|
||||||
|
|
||||||
|
Free(muc);
|
||||||
|
Free(room);
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
ParseeManageBan(ParseeData *data, char *user, char *room)
|
ParseeIsMUCWhitelisted(ParseeData *data, char *muc)
|
||||||
{
|
{
|
||||||
|
char *server, *serv_start, *postserv;
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
HashMap *j;
|
bool ret;
|
||||||
char *key;
|
if (!data || !muc)
|
||||||
JsonValue *val;
|
|
||||||
bool banned = false , matches = false;
|
|
||||||
if (!data || !user)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ref = DbLock(data->db, 1, "global_bans");
|
if (!DbExists(data->db, 1, "whitelist"))
|
||||||
j = DbJson(ref);
|
|
||||||
while (HashMapIterate(j, &key, (void **) &val))
|
|
||||||
{
|
{
|
||||||
HashMap *obj = JsonValueAsObject(val);
|
return true;
|
||||||
if (matches)
|
}
|
||||||
|
|
||||||
|
serv_start = strchr(muc, '@');
|
||||||
|
serv_start = serv_start ? serv_start : muc;
|
||||||
|
server = StrDuplicate(serv_start + 1);
|
||||||
|
postserv = server ? strchr(server, '/') : NULL;
|
||||||
|
if (postserv) /* GCC doesn't know strchr is pure. */
|
||||||
{
|
{
|
||||||
continue;
|
*postserv = '\0';
|
||||||
}
|
}
|
||||||
if (GlobMatches(key, user))
|
|
||||||
{
|
|
||||||
banned = true;
|
|
||||||
matches = true;
|
|
||||||
if (room)
|
|
||||||
{
|
|
||||||
/* TODO: Use the object to set the reason */
|
|
||||||
ASBan(data->config, room, user);
|
|
||||||
(void) obj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
|
|
||||||
return banned;
|
ref = DbLockIntent(data->db,
|
||||||
}
|
DB_HINT_READONLY,
|
||||||
char *
|
1, "whitelist"
|
||||||
ParseeStringifyDate(uint64_t millis)
|
|
||||||
{
|
|
||||||
uint64_t rest = millis;
|
|
||||||
uint64_t hours, minutes, seconds;
|
|
||||||
char *hs, *ms, *ss, *out;
|
|
||||||
|
|
||||||
hours = rest / (1 HOURS);
|
|
||||||
rest = rest % (1 HOURS);
|
|
||||||
|
|
||||||
minutes = rest / (1 MINUTES);
|
|
||||||
rest = rest % (1 MINUTES);
|
|
||||||
|
|
||||||
seconds = rest / (1 SECONDS);
|
|
||||||
|
|
||||||
hs = StrInt(hours);
|
|
||||||
ms = StrInt(minutes);
|
|
||||||
ss = StrInt(seconds);
|
|
||||||
|
|
||||||
out = StrConcat(8,
|
|
||||||
hours ? hs : "",
|
|
||||||
hours ? " hours" : "",
|
|
||||||
(hours && minutes) ? ", " : "",
|
|
||||||
|
|
||||||
minutes ? ms : "",
|
|
||||||
minutes ? " minutes" : "",
|
|
||||||
(minutes && seconds) ? ", " : "",
|
|
||||||
|
|
||||||
seconds ? ss : "",
|
|
||||||
seconds ? " seconds" : ""
|
|
||||||
);
|
);
|
||||||
Free(hs);
|
ret = HashMapGet(DbJson(ref), server);
|
||||||
Free(ms);
|
DbUnlock(data->db, ref);
|
||||||
Free(ss);
|
Free(server);
|
||||||
|
|
||||||
return out;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
extern HashMap *
|
||||||
ParseeAchievement(const char *func, const char *msg, bool die)
|
ParseeGetChatSettings(ParseeData *data, char *chat)
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
HashMap *ret, *json;
|
||||||
Log(LOG_ERR, "%s: %s.", func, msg);
|
DbRef *ref;
|
||||||
Log(LOG_ERR, "THIS IS, LET'S SAY, NOT GOOD, AND A SIGN OF ");
|
|
||||||
Log(LOG_ERR, "A PROGRAMMER ERROR. PLEASE KILLALL -9 PARSEE.");
|
|
||||||
Log(LOG_ERR, "");
|
|
||||||
Log(LOG_ERR, "YOU, HOWEVER, GET TO WIN AN AWARD FOR THIS.");
|
|
||||||
Log(LOG_ERR, "I AM SIMPLY JEALOUS OF YOU GETTING SUCH A ");
|
|
||||||
Log(LOG_ERR, "GOOD ERROR MESSAGE.");
|
|
||||||
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
|
||||||
|
|
||||||
if (die)
|
char *key;
|
||||||
{
|
JsonValue *value;
|
||||||
abort();
|
if (!data || !chat)
|
||||||
}
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ParseeGenerateMTO(char *common_id)
|
|
||||||
{
|
|
||||||
char *matrix_to;
|
|
||||||
if (!common_id)
|
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
common_id = HttpUrlEncode(common_id);
|
ref = DbLockIntent(data->db, DB_HINT_READONLY,
|
||||||
matrix_to = StrConcat(2, "https://matrix.to/#/", common_id);
|
3, "chats", chat, "settings"
|
||||||
Free(common_id);
|
);
|
||||||
|
json = DbJson(ref);
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
return HashMapCreate();
|
||||||
|
}
|
||||||
|
|
||||||
return matrix_to;
|
ret = HashMapCreate();
|
||||||
|
while (HashMapIterate(json, &key, (void **) &value))
|
||||||
|
{
|
||||||
|
char *str = JsonValueAsString(value);
|
||||||
|
HashMapSet(ret, key, StrDuplicate(str));
|
||||||
|
}
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeFreeChatSettings(HashMap *settings)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
void *val;
|
||||||
|
if (!settings)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (HashMapIterate(settings, &key, &val))
|
||||||
|
{
|
||||||
|
Free(val);
|
||||||
|
}
|
||||||
|
HashMapFree(settings);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeGetChatSetting(ParseeData *data, char *chat, char *key)
|
||||||
|
{
|
||||||
|
HashMap *map;
|
||||||
|
char *ret;
|
||||||
|
if (!data || !chat || !key)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
map = ParseeGetChatSettings(data, chat);
|
||||||
|
ret = StrDuplicate(HashMapGet(map, key));
|
||||||
|
ParseeFreeChatSettings(map);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeSetChatSetting(ParseeData *data, char *chat, char *key, char *val)
|
||||||
|
{
|
||||||
|
DbRef *ref;
|
||||||
|
HashMap *json;
|
||||||
|
if (!data || !chat || !key || !val)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbLockIntent(data->db, DB_HINT_WRITE,
|
||||||
|
3, "chats", chat, "settings"
|
||||||
|
);
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
ref = DbCreate(data->db, 3, "chats", chat, "settings");
|
||||||
|
}
|
||||||
|
json = DbJson(ref);
|
||||||
|
|
||||||
|
JsonValueFree(HashMapSet(json, key, JsonValueString(val)));
|
||||||
|
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
ParseeIsMediaEnabled(ParseeData *data, char *chat_id)
|
||||||
|
{
|
||||||
|
char *value;
|
||||||
|
bool ret;
|
||||||
|
if (!data || !chat_id)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = !StrEquals(
|
||||||
|
(value = ParseeGetChatSetting(data, chat_id, "p.media.enabled")),
|
||||||
|
"false"
|
||||||
|
);
|
||||||
|
Free(value);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
74
src/Parsee/HMAC.c
Normal file
74
src/Parsee/HMAC.c
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* CC0 implementation of a HMAC system based on Cytoplasm.
|
||||||
|
* Ignore the scary "Parsee.h", its practically unused. */
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
ComputeKPad(char *key, uint8_t pad, uint8_t *kopad)
|
||||||
|
{
|
||||||
|
size_t klen;
|
||||||
|
uint8_t *kp;
|
||||||
|
uint8_t kpi[64] = { 0 };
|
||||||
|
size_t i;
|
||||||
|
if ((klen = strlen(key)) <= 64)
|
||||||
|
{
|
||||||
|
kp = Malloc(klen * sizeof(uint8_t));
|
||||||
|
memcpy(kp, key, klen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
kp = (uint8_t *) Sha256(key);
|
||||||
|
klen = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(kpi, 0x00, 64);
|
||||||
|
memcpy(kpi, kp, klen);
|
||||||
|
Free(kp);
|
||||||
|
|
||||||
|
/* Now that we have K', lets compute it XORd with opad */
|
||||||
|
for (i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
uint8_t byte = kpi[i];
|
||||||
|
kopad[i] = byte ^ pad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ParseeHMAC(char *key, uint8_t *msg, size_t msglen)
|
||||||
|
{
|
||||||
|
uint8_t opad[64], ipad[64 + msglen];
|
||||||
|
uint8_t outer[64 + 32];
|
||||||
|
|
||||||
|
uint8_t *innersha;
|
||||||
|
uint8_t *sha;
|
||||||
|
char *str;
|
||||||
|
if (!key || !msg || !msglen)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialise K' XOR opad and K' XOR ipad */
|
||||||
|
ComputeKPad(key, 0x5C, opad);
|
||||||
|
ComputeKPad(key, 0x36, ipad);
|
||||||
|
|
||||||
|
/* Compute H((K' XOR ipad) || msg) */
|
||||||
|
memcpy(ipad + 64, msg, msglen);
|
||||||
|
innersha = Sha256Raw(ipad, 64 + msglen);
|
||||||
|
|
||||||
|
/* Compute (K' XOR opad) || H((K' XOR ipad) || msg) */
|
||||||
|
memcpy(outer, opad, 64);
|
||||||
|
memcpy(outer + 64, innersha, 32);
|
||||||
|
|
||||||
|
/* Compute H((K' XOR opad) || H((K' XOR ipad) || msg)) */
|
||||||
|
sha = Sha256Raw(outer, 64 + 32);
|
||||||
|
str = ShaToHex(sha, HASH_SHA256);
|
||||||
|
|
||||||
|
Free(innersha);
|
||||||
|
Free(sha);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
@ -1,194 +0,0 @@
|
||||||
#include <Parsee.h>
|
|
||||||
|
|
||||||
#include <Cytoplasm/HashMap.h>
|
|
||||||
#include <Cytoplasm/Memory.h>
|
|
||||||
#include <Cytoplasm/Str.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
static pthread_mutex_t lock;
|
|
||||||
static HashMap *jid_table = NULL;
|
|
||||||
|
|
||||||
void
|
|
||||||
ParseeInitialiseJIDTable(void)
|
|
||||||
{
|
|
||||||
if (jid_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_init(&lock, NULL);
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
jid_table = HashMapCreate();
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ParseePushJIDTable(char *muc, char *bare)
|
|
||||||
{
|
|
||||||
if (!muc || !bare || !jid_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
bare = ParseeTrimJID(bare);
|
|
||||||
Free(HashMapSet(jid_table, muc, bare));
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ParseeLookupJID(char *muc)
|
|
||||||
{
|
|
||||||
char *bare;
|
|
||||||
if (!muc || !jid_table)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
bare = StrDuplicate(HashMapGet(jid_table, muc));
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
|
|
||||||
if (!bare)
|
|
||||||
{
|
|
||||||
bare = StrDuplicate(muc);
|
|
||||||
}
|
|
||||||
return bare;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ParseeDestroyJIDTable(void)
|
|
||||||
{
|
|
||||||
char *key;
|
|
||||||
void *val;
|
|
||||||
if (!jid_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&lock);
|
|
||||||
while (HashMapIterate(jid_table, &key, &val))
|
|
||||||
{
|
|
||||||
Free(val);
|
|
||||||
}
|
|
||||||
HashMapFree(jid_table);
|
|
||||||
jid_table = NULL;
|
|
||||||
pthread_mutex_unlock(&lock);
|
|
||||||
pthread_mutex_destroy(&lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static pthread_mutex_t head_lock;
|
|
||||||
static HashMap *head_table = NULL;
|
|
||||||
void
|
|
||||||
ParseeInitialiseHeadTable(void)
|
|
||||||
{
|
|
||||||
if (head_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_init(&head_lock, NULL);
|
|
||||||
pthread_mutex_lock(&head_lock);
|
|
||||||
head_table = HashMapCreate();
|
|
||||||
pthread_mutex_unlock(&head_lock);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ParseePushHeadTable(char *room, char *event)
|
|
||||||
{
|
|
||||||
if (!room || !event || !head_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&head_lock);
|
|
||||||
event = StrDuplicate(event);
|
|
||||||
Free(HashMapSet(head_table, room, event));
|
|
||||||
pthread_mutex_unlock(&head_lock);
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ParseeLookupHead(char *room)
|
|
||||||
{
|
|
||||||
char *event;
|
|
||||||
if (!room || !head_table)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&head_lock);
|
|
||||||
event = StrDuplicate(HashMapGet(head_table, room));
|
|
||||||
pthread_mutex_unlock(&head_lock);
|
|
||||||
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ParseeDestroyHeadTable(void)
|
|
||||||
{
|
|
||||||
char *key;
|
|
||||||
void *val;
|
|
||||||
if (!head_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&head_lock);
|
|
||||||
while (HashMapIterate(head_table, &key, &val))
|
|
||||||
{
|
|
||||||
Free(val);
|
|
||||||
}
|
|
||||||
HashMapFree(head_table);
|
|
||||||
head_table = NULL;
|
|
||||||
pthread_mutex_unlock(&head_lock);
|
|
||||||
pthread_mutex_destroy(&head_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static pthread_mutex_t oid_lock;
|
|
||||||
static HashMap *oid_table = NULL;
|
|
||||||
|
|
||||||
void
|
|
||||||
ParseeInitialiseOIDTable(void)
|
|
||||||
{
|
|
||||||
if (oid_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_init(&oid_lock, NULL);
|
|
||||||
pthread_mutex_lock(&oid_lock);
|
|
||||||
oid_table = HashMapCreate();
|
|
||||||
pthread_mutex_unlock(&oid_lock);
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ParseePushOIDTable(char *muc, char *bare)
|
|
||||||
{
|
|
||||||
if (!muc || !bare || !oid_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&oid_lock);
|
|
||||||
bare = StrDuplicate(bare);
|
|
||||||
Free(HashMapSet(oid_table, muc, bare));
|
|
||||||
pthread_mutex_unlock(&oid_lock);
|
|
||||||
}
|
|
||||||
char *
|
|
||||||
ParseeLookupOID(char *muc)
|
|
||||||
{
|
|
||||||
char *bare;
|
|
||||||
if (!muc || !oid_table)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&oid_lock);
|
|
||||||
bare = StrDuplicate(HashMapGet(oid_table, muc));
|
|
||||||
pthread_mutex_unlock(&oid_lock);
|
|
||||||
|
|
||||||
return bare;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
ParseeDestroyOIDTable(void)
|
|
||||||
{
|
|
||||||
char *key;
|
|
||||||
void *val;
|
|
||||||
if (!oid_table)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pthread_mutex_lock(&oid_lock);
|
|
||||||
while (HashMapIterate(oid_table, &key, &val))
|
|
||||||
{
|
|
||||||
Free(val);
|
|
||||||
}
|
|
||||||
HashMapFree(oid_table);
|
|
||||||
oid_table = NULL;
|
|
||||||
pthread_mutex_unlock(&oid_lock);
|
|
||||||
pthread_mutex_destroy(&oid_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
25
src/Parsee/Logo.c
Normal file
25
src/Parsee/Logo.c
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
const char *parsee_ascii[PARSEE_ASCII_LINES] =
|
||||||
|
{
|
||||||
|
" =+======",
|
||||||
|
" || | _ _/__----",
|
||||||
|
" / || \\ ==+= _/_____\\_",
|
||||||
|
" | || | -|- L___J ",
|
||||||
|
"_/ || \\_ ||| .______\\",
|
||||||
|
" || | | | |.____.|",
|
||||||
|
" || / | \\ |L____||",
|
||||||
|
" _// | | J"
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseePrintASCII(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < PARSEE_ASCII_LINES; i++)
|
||||||
|
{
|
||||||
|
Log(LOG_INFO, "%s", parsee_ascii[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
57
src/Parsee/SHA.c
Normal file
57
src/Parsee/SHA.c
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ToHex(unsigned char *input, size_t length)
|
||||||
|
{
|
||||||
|
char *hex = Malloc(length * 2 + 1);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
const char *table =
|
||||||
|
"0123456789abcdef";
|
||||||
|
unsigned char byte = input[i];
|
||||||
|
hex[2 * i] = table[(byte >> 4) & 0xF];
|
||||||
|
hex[2*i+1] = table[(byte >> 0) & 0xF];
|
||||||
|
}
|
||||||
|
|
||||||
|
hex[length * 2] = '\0';
|
||||||
|
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ParseeSHA256(char *string)
|
||||||
|
{
|
||||||
|
unsigned char *sha;
|
||||||
|
char *returnString;
|
||||||
|
if (!string)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha = Sha256(string);
|
||||||
|
returnString = ToHex(sha, 32);
|
||||||
|
Free(sha);
|
||||||
|
|
||||||
|
return returnString;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeSHA1(char *string)
|
||||||
|
{
|
||||||
|
unsigned char *sha;
|
||||||
|
char *returnString;
|
||||||
|
if (!string)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sha = Sha1(string);
|
||||||
|
returnString = ToHex(sha, 20);
|
||||||
|
Free(sha);
|
||||||
|
|
||||||
|
return returnString;
|
||||||
|
}
|
||||||
106
src/Parsee/Tables/Affiliation.c
Normal file
106
src/Parsee/Tables/Affiliation.c
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t affi_lock;
|
||||||
|
static HashMap *affi_table = NULL;
|
||||||
|
|
||||||
|
typedef struct XMPPStatus {
|
||||||
|
char *role;
|
||||||
|
char *affiliation;
|
||||||
|
} XMPPStatus;
|
||||||
|
static XMPPStatus *
|
||||||
|
CreateStatus(char *role, char *affiliation)
|
||||||
|
{
|
||||||
|
XMPPStatus *ret;
|
||||||
|
if (!role || !affiliation)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = Malloc(sizeof(*ret));
|
||||||
|
ret->role = StrDuplicate(role);
|
||||||
|
ret->affiliation = StrDuplicate(affiliation);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
FreeStatus(XMPPStatus *status)
|
||||||
|
{
|
||||||
|
if (!status)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(status->affiliation);
|
||||||
|
Free(status->role);
|
||||||
|
Free(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeInitialiseAffiliationTable(void)
|
||||||
|
{
|
||||||
|
if (affi_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(&affi_lock, NULL);
|
||||||
|
pthread_mutex_lock(&affi_lock);
|
||||||
|
affi_table = HashMapCreate();
|
||||||
|
pthread_mutex_unlock(&affi_lock);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseePushAffiliationTable(char *user, char *affi, char *role)
|
||||||
|
{
|
||||||
|
XMPPStatus *status;
|
||||||
|
if (!user || !affi || !role)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&affi_lock);
|
||||||
|
|
||||||
|
status = CreateStatus(role, affi);
|
||||||
|
FreeStatus(HashMapSet(affi_table, user, status));
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&affi_lock);
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
ParseeLookupAffiliation(char *user, char **affiliation, char **role)
|
||||||
|
{
|
||||||
|
XMPPStatus *status;
|
||||||
|
if (!user || !affiliation || !role)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&affi_lock);
|
||||||
|
|
||||||
|
status = HashMapGet(affi_table, user);
|
||||||
|
*affiliation = StrDuplicate(status ? status->affiliation : NULL);
|
||||||
|
*role = StrDuplicate(status ? status->role : NULL);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&affi_lock);
|
||||||
|
return !!status;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeDestroyAffiliationTable(void)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
void *val;
|
||||||
|
if (!affi_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&affi_lock);
|
||||||
|
while (HashMapIterate(affi_table, &key, &val))
|
||||||
|
{
|
||||||
|
FreeStatus(val);
|
||||||
|
}
|
||||||
|
HashMapFree(affi_table);
|
||||||
|
affi_table = NULL;
|
||||||
|
pthread_mutex_unlock(&affi_lock);
|
||||||
|
pthread_mutex_destroy(&affi_lock);
|
||||||
|
}
|
||||||
|
|
||||||
67
src/Parsee/Tables/HeadTable.c
Normal file
67
src/Parsee/Tables/HeadTable.c
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t head_lock;
|
||||||
|
static HashMap *head_table = NULL;
|
||||||
|
void
|
||||||
|
ParseeInitialiseHeadTable(void)
|
||||||
|
{
|
||||||
|
if (head_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(&head_lock, NULL);
|
||||||
|
pthread_mutex_lock(&head_lock);
|
||||||
|
head_table = HashMapCreate();
|
||||||
|
pthread_mutex_unlock(&head_lock);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseePushHeadTable(char *room, char *event)
|
||||||
|
{
|
||||||
|
if (!room || !event || !head_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&head_lock);
|
||||||
|
event = StrDuplicate(event);
|
||||||
|
Free(HashMapSet(head_table, room, event));
|
||||||
|
pthread_mutex_unlock(&head_lock);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeLookupHead(char *room)
|
||||||
|
{
|
||||||
|
char *event;
|
||||||
|
if (!room || !head_table)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&head_lock);
|
||||||
|
event = StrDuplicate(HashMapGet(head_table, room));
|
||||||
|
pthread_mutex_unlock(&head_lock);
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeDestroyHeadTable(void)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
void *val;
|
||||||
|
if (!head_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&head_lock);
|
||||||
|
while (HashMapIterate(head_table, &key, &val))
|
||||||
|
{
|
||||||
|
Free(val);
|
||||||
|
}
|
||||||
|
HashMapFree(head_table);
|
||||||
|
head_table = NULL;
|
||||||
|
pthread_mutex_unlock(&head_lock);
|
||||||
|
pthread_mutex_destroy(&head_lock);
|
||||||
|
}
|
||||||
|
|
||||||
72
src/Parsee/Tables/JIDTable.c
Normal file
72
src/Parsee/Tables/JIDTable.c
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t lock;
|
||||||
|
static HashMap *jid_table = NULL;
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeInitialiseJIDTable(void)
|
||||||
|
{
|
||||||
|
if (jid_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(&lock, NULL);
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
jid_table = HashMapCreate();
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseePushJIDTable(char *muc, char *bare)
|
||||||
|
{
|
||||||
|
if (!muc || !bare || !jid_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
bare = ParseeTrimJID(bare);
|
||||||
|
Free(HashMapSet(jid_table, muc, bare));
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeLookupJID(char *muc)
|
||||||
|
{
|
||||||
|
char *bare;
|
||||||
|
if (!muc || !jid_table)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
bare = StrDuplicate(HashMapGet(jid_table, muc));
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
|
||||||
|
if (!bare)
|
||||||
|
{
|
||||||
|
bare = StrDuplicate(muc);
|
||||||
|
}
|
||||||
|
return bare;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeDestroyJIDTable(void)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
void *val;
|
||||||
|
if (!jid_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
while (HashMapIterate(jid_table, &key, &val))
|
||||||
|
{
|
||||||
|
Free(val);
|
||||||
|
}
|
||||||
|
HashMapFree(jid_table);
|
||||||
|
jid_table = NULL;
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
pthread_mutex_destroy(&lock);
|
||||||
|
}
|
||||||
|
|
||||||
100
src/Parsee/Tables/NickTable.c
Normal file
100
src/Parsee/Tables/NickTable.c
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t nick_lock;
|
||||||
|
static HashMap *nick_table = NULL;
|
||||||
|
|
||||||
|
static char *
|
||||||
|
GenerateKey(char *muc, char *mxid)
|
||||||
|
{
|
||||||
|
char *concatStr;
|
||||||
|
char *hexDigest;
|
||||||
|
if (!muc || !mxid)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
concatStr = StrConcat(3, muc, ":", mxid);
|
||||||
|
hexDigest = ParseeSHA256(concatStr);
|
||||||
|
|
||||||
|
Free (concatStr);
|
||||||
|
return hexDigest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeInitialiseNickTable(void)
|
||||||
|
{
|
||||||
|
if (nick_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(&nick_lock, NULL);
|
||||||
|
pthread_mutex_lock(&nick_lock);
|
||||||
|
nick_table = HashMapCreate();
|
||||||
|
pthread_mutex_unlock(&nick_lock);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseePushNickTable(char *muc, char *mxid, char *nick)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
if (!muc || !mxid || !nick_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&nick_lock);
|
||||||
|
|
||||||
|
key = GenerateKey(muc, mxid);
|
||||||
|
nick = StrDuplicate(nick);
|
||||||
|
if (nick)
|
||||||
|
{
|
||||||
|
Free(HashMapSet(nick_table, key, nick));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Free(HashMapDelete(nick_table, key));
|
||||||
|
}
|
||||||
|
Free(key);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&nick_lock);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeLookupNick(char *muc, char *mxid)
|
||||||
|
{
|
||||||
|
char *ret, *key;
|
||||||
|
if (!muc || !nick_table)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&nick_lock);
|
||||||
|
|
||||||
|
key = GenerateKey(muc, mxid);
|
||||||
|
ret = HashMapGet(nick_table, key);
|
||||||
|
Free(key);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&nick_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeDestroyNickTable(void)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
void *val;
|
||||||
|
if (!nick_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&nick_lock);
|
||||||
|
while (HashMapIterate(nick_table, &key, &val))
|
||||||
|
{
|
||||||
|
Free(val);
|
||||||
|
}
|
||||||
|
HashMapFree(nick_table);
|
||||||
|
nick_table = NULL;
|
||||||
|
pthread_mutex_unlock(&nick_lock);
|
||||||
|
pthread_mutex_destroy(&nick_lock);
|
||||||
|
}
|
||||||
|
|
||||||
68
src/Parsee/Tables/OIDTable.c
Normal file
68
src/Parsee/Tables/OIDTable.c
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
static pthread_mutex_t oid_lock;
|
||||||
|
static HashMap *oid_table = NULL;
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeInitialiseOIDTable(void)
|
||||||
|
{
|
||||||
|
if (oid_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_init(&oid_lock, NULL);
|
||||||
|
pthread_mutex_lock(&oid_lock);
|
||||||
|
oid_table = HashMapCreate();
|
||||||
|
pthread_mutex_unlock(&oid_lock);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseePushOIDTable(char *muc, char *bare)
|
||||||
|
{
|
||||||
|
if (!muc || !bare || !oid_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&oid_lock);
|
||||||
|
bare = StrDuplicate(bare);
|
||||||
|
Free(HashMapSet(oid_table, muc, bare));
|
||||||
|
pthread_mutex_unlock(&oid_lock);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeLookupOID(char *muc)
|
||||||
|
{
|
||||||
|
char *bare;
|
||||||
|
if (!muc || !oid_table)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&oid_lock);
|
||||||
|
bare = StrDuplicate(HashMapGet(oid_table, muc));
|
||||||
|
pthread_mutex_unlock(&oid_lock);
|
||||||
|
|
||||||
|
return bare;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeDestroyOIDTable(void)
|
||||||
|
{
|
||||||
|
char *key;
|
||||||
|
void *val;
|
||||||
|
if (!oid_table)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_mutex_lock(&oid_lock);
|
||||||
|
while (HashMapIterate(oid_table, &key, &val))
|
||||||
|
{
|
||||||
|
Free(val);
|
||||||
|
}
|
||||||
|
HashMapFree(oid_table);
|
||||||
|
oid_table = NULL;
|
||||||
|
pthread_mutex_unlock(&oid_lock);
|
||||||
|
pthread_mutex_destroy(&oid_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,42 +1,46 @@
|
||||||
#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>
|
||||||
#include <Cytoplasm/Sha.h>
|
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <Matrix.h>
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseeIsPuppet(const ParseeConfig *conf, char *user)
|
ParseeIsPuppet(const ParseeConfig *conf, char *user)
|
||||||
{
|
{
|
||||||
char *localpart;
|
UserID *id;
|
||||||
bool flag = true;
|
bool flag = true;
|
||||||
size_t len;
|
size_t len;
|
||||||
if (!user || !conf || *user != '@')
|
if (!user ||
|
||||||
|
!conf ||
|
||||||
|
*user != '@' ||
|
||||||
|
!(id = MatrixParseID(user)))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
localpart = user + 1;
|
|
||||||
len = strlen(conf->namespace_base);
|
len = strlen(conf->namespace_base);
|
||||||
if (strncmp(localpart, conf->namespace_base, len))
|
if (strncmp(id->localpart, conf->namespace_base, len))
|
||||||
{
|
{
|
||||||
flag = false;
|
flag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = strlen(conf->sender_localpart);
|
if (StrEquals(id->localpart, conf->sender_localpart))
|
||||||
if (!strncmp(localpart, conf->sender_localpart, len))
|
|
||||||
{
|
{
|
||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Check serverpart. 2 Parsee instances may be running in the same
|
flag = flag && StrEquals(id->server, conf->server_base);
|
||||||
* room. */
|
|
||||||
|
Free(id);
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
char *
|
char *
|
||||||
|
|
@ -93,7 +97,7 @@ ParseeDecodeLocalJID(const ParseeConfig *c, char *mxid)
|
||||||
data_start = jid_flags;
|
data_start = jid_flags;
|
||||||
while (*data_start && *data_start != '_')
|
while (*data_start && *data_start != '_')
|
||||||
{
|
{
|
||||||
/* TODO: Make this a macro */
|
/* TODO: Get rid of this */
|
||||||
if (*data_start == 'l')
|
if (*data_start == 'l')
|
||||||
{
|
{
|
||||||
plain_jid = true;
|
plain_jid = true;
|
||||||
|
|
@ -123,7 +127,7 @@ ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias)
|
||||||
data_start = jid_flags;
|
data_start = jid_flags;
|
||||||
while (*data_start && *data_start != '_')
|
while (*data_start && *data_start != '_')
|
||||||
{
|
{
|
||||||
/* TODO: Make this a macro */
|
/* TODO: Get rid of this */
|
||||||
if (*data_start == 'm')
|
if (*data_start == 'm')
|
||||||
{
|
{
|
||||||
plain_jid = true;
|
plain_jid = true;
|
||||||
|
|
@ -144,14 +148,15 @@ char *
|
||||||
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
||||||
{
|
{
|
||||||
char *ret, *tmp;
|
char *ret, *tmp;
|
||||||
size_t i;
|
size_t i, len;
|
||||||
if (!c || !jid)
|
if (!c || !jid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = StrConcat(2, c->namespace_base, "_l_");
|
ret = StrConcat(2, c->namespace_base, "_l_");
|
||||||
for (i = 0; i < strlen(jid); i++)
|
len = strlen(jid);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
char cpy = jid[i];
|
char cpy = jid[i];
|
||||||
char cs[4] = { 0 };
|
char cs[4] = { 0 };
|
||||||
|
|
@ -161,7 +166,7 @@ ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
||||||
/* RID: Break everything and die. */
|
/* RID: Break everything and die. */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (islower(*cs) || isalnum(*cs) || *cs == '_' ||
|
if (islower((int) *cs) || isalnum((int) *cs) || *cs == '_' ||
|
||||||
*cs == '=' || *cs == '-' || *cs == '/' ||
|
*cs == '=' || *cs == '-' || *cs == '/' ||
|
||||||
*cs == '+' || *cs == '.')
|
*cs == '+' || *cs == '.')
|
||||||
{
|
{
|
||||||
|
|
@ -190,7 +195,7 @@ char *
|
||||||
ParseeGetLocal(char *mxid)
|
ParseeGetLocal(char *mxid)
|
||||||
{
|
{
|
||||||
char *cpy;
|
char *cpy;
|
||||||
size_t i;
|
size_t i, len;
|
||||||
if (!mxid)
|
if (!mxid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -200,12 +205,14 @@ ParseeGetLocal(char *mxid)
|
||||||
return StrDuplicate(mxid);
|
return StrDuplicate(mxid);
|
||||||
}
|
}
|
||||||
|
|
||||||
mxid++;
|
len = strlen(mxid);
|
||||||
cpy = Malloc(strlen(mxid) + 1);
|
|
||||||
memset(cpy, '\0', strlen(mxid) + 1);
|
|
||||||
memcpy(cpy, mxid, strlen(mxid));
|
|
||||||
|
|
||||||
for (i = 0; i < strlen(mxid); i++)
|
mxid++;
|
||||||
|
cpy = Malloc(len + 1);
|
||||||
|
memset(cpy, '\0', len + 1);
|
||||||
|
memcpy(cpy, mxid, len);
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (cpy[i] == ':')
|
if (cpy[i] == ':')
|
||||||
{
|
{
|
||||||
|
|
@ -221,19 +228,19 @@ char *
|
||||||
ParseeEncodeMXID(char *mxid)
|
ParseeEncodeMXID(char *mxid)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
size_t i, j;
|
size_t i, j, len;
|
||||||
if (!mxid)
|
if (!mxid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Worst case scenario of 3-bytes the char */
|
/* Worst case scenario of 3-bytes the char */
|
||||||
ret = Malloc(strlen(mxid) * 3 + 1);
|
len = strlen(mxid);
|
||||||
for (i = 0, j = 0; i < strlen(mxid); i++)
|
ret = Malloc(len * 3 + 1);
|
||||||
|
for (i = 0, j = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
char src = mxid[i];
|
char src = mxid[i];
|
||||||
|
|
||||||
/* TODO: More robust system */
|
|
||||||
if (src <= 0x20 ||
|
if (src <= 0x20 ||
|
||||||
(src == '"' || src == '&' ||
|
(src == '"' || src == '&' ||
|
||||||
src == '\'' || src == '/' ||
|
src == '\'' || src == '/' ||
|
||||||
|
|
@ -295,19 +302,17 @@ char *
|
||||||
ParseeGetDMID(char *mxid, char *jid)
|
ParseeGetDMID(char *mxid, char *jid)
|
||||||
{
|
{
|
||||||
char *concat, *sha;
|
char *concat, *sha;
|
||||||
unsigned char *raw;
|
|
||||||
if (!mxid || !jid)
|
if (!mxid || !jid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We really don't care about safety here. */
|
||||||
jid = ParseeTrimJID(jid);
|
jid = ParseeTrimJID(jid);
|
||||||
concat = StrConcat(2, mxid, jid);
|
concat = StrConcat(2, mxid, jid);
|
||||||
raw = Sha1(concat);
|
sha = ParseeSHA1(concat);
|
||||||
sha = ShaToHex(raw);
|
|
||||||
|
|
||||||
Free(concat);
|
Free(concat);
|
||||||
Free(raw);
|
|
||||||
Free(jid);
|
Free(jid);
|
||||||
|
|
||||||
return sha;
|
return sha;
|
||||||
|
|
@ -353,19 +358,34 @@ ParseePushDMRoom(ParseeData *d, char *mxid, char *jid, char *r)
|
||||||
Free(dmid);
|
Free(dmid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
void
|
||||||
|
ParseeDeleteDM(ParseeData *d, char *mxid, char *jid)
|
||||||
|
{
|
||||||
|
char *dmid;
|
||||||
|
if (!d || !mxid || !jid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dmid = ParseeGetDMID(mxid, jid);
|
||||||
|
DbDelete(d->db, 2, "users", dmid);
|
||||||
|
Free(dmid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
ParseeTrimJID(char *jid)
|
ParseeTrimJID(char *jid)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
size_t i;
|
size_t i, len;
|
||||||
if (!jid)
|
if (!jid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = StrDuplicate(jid);
|
ret = StrDuplicate(jid);
|
||||||
for (i = 0; i < strlen(ret); i++)
|
len = strlen(ret);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (ret[i] == '/')
|
if (ret[i] == '/')
|
||||||
{
|
{
|
||||||
|
|
@ -519,7 +539,6 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ParseeSendPresence(ParseeData *data)
|
ParseeSendPresence(ParseeData *data)
|
||||||
{
|
{
|
||||||
|
|
@ -538,10 +557,16 @@ ParseeSendPresence(ParseeData *data)
|
||||||
while (HashMapIterate(mucs, &muc, (void **) &val))
|
while (HashMapIterate(mucs, &muc, (void **) &val))
|
||||||
{
|
{
|
||||||
char *rev = StrConcat(2, muc, "/parsee");
|
char *rev = StrConcat(2, muc, "/parsee");
|
||||||
|
char *chat_id = ParseeGetFromMUCID(data, muc);
|
||||||
|
DbRef *chat = DbLockIntent(data->db, DB_HINT_READONLY, 2, "chats", chat_id);
|
||||||
|
uint64_t ts = GrabInteger(DbJson(chat), 1, "ts");
|
||||||
|
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", rev);
|
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
||||||
XMPPJoinMUC(data->jabber, "parsee", rev);
|
XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false);
|
||||||
|
|
||||||
|
DbUnlock(data->db, chat);
|
||||||
|
Free(chat_id);
|
||||||
Free(rev);
|
Free(rev);
|
||||||
}
|
}
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
|
|
@ -663,11 +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;
|
||||||
#define PAT "%s/_matrix/client/v1/media/download/%s%s"
|
char *key, *hmac;
|
||||||
|
#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)
|
||||||
{
|
{
|
||||||
|
|
@ -684,19 +711,51 @@ ParseeToUnauth(ParseeData *data, char *mxc)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: HTTPS */
|
key = StrConcat(2, url->host, url->path);
|
||||||
|
hmac = ParseeHMACS(data->id, 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
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
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
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(ret, l + 1,
|
||||||
|
PATF,
|
||||||
|
data->config->media_base,
|
||||||
|
url->host, url->path, filename,
|
||||||
|
hmac
|
||||||
|
);
|
||||||
|
}
|
||||||
UriFree(url);
|
UriFree(url);
|
||||||
|
Free(hmac);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
26
src/Parsee/Utils/Achievement.c
Normal file
26
src/Parsee/Utils/Achievement.c
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeAchievement(const char *func, const char *msg, bool die)
|
||||||
|
{
|
||||||
|
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
||||||
|
Log(LOG_ERR, "%s: %s.", func, msg);
|
||||||
|
Log(LOG_ERR, "THIS IS, LET'S SAY, NOT GOOD, AND A SIGN OF ");
|
||||||
|
Log(LOG_ERR, "A PROGRAMMER ERROR. PLEASE KILLALL -9 PARSEE.");
|
||||||
|
Log(LOG_ERR, "");
|
||||||
|
Log(LOG_ERR, "YOU, HOWEVER, GET TO WIN AN AWARD FOR THIS.");
|
||||||
|
Log(LOG_ERR, "I AM SIMPLY JEALOUS OF YOU GETTING SUCH A ");
|
||||||
|
Log(LOG_ERR, "GOOD ERROR MESSAGE.");
|
||||||
|
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
||||||
|
|
||||||
|
if (die)
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
53
src/Parsee/Utils/Arguments.c
Normal file
53
src/Parsee/Utils/Arguments.c
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeGenerateHelp(const Argument *list)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!list->end)
|
||||||
|
{
|
||||||
|
char *str = list->value_req ?
|
||||||
|
StrConcat(3, " [", list->value_descr, "]") :
|
||||||
|
StrDuplicate("");
|
||||||
|
Log(LOG_INFO, "-%c%s", list->argument, str);
|
||||||
|
LogConfigIndent(LogConfigGlobal());
|
||||||
|
Log(LOG_INFO, "%s", list->description);
|
||||||
|
LogConfigUnindent(LogConfigGlobal());
|
||||||
|
list++;
|
||||||
|
|
||||||
|
Free(str);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeGenerateGetopt(const Argument *list)
|
||||||
|
{
|
||||||
|
char *ret = NULL, *tmp = NULL;
|
||||||
|
if (!list)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!list->end)
|
||||||
|
{
|
||||||
|
char buffer[3] = {
|
||||||
|
list->argument, list->value_req ? ':' : '\0',
|
||||||
|
'\0'
|
||||||
|
};
|
||||||
|
|
||||||
|
tmp = ret;
|
||||||
|
ret = StrConcat(2, ret, buffer);
|
||||||
|
Free(tmp);
|
||||||
|
|
||||||
|
list++;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
323
src/Parsee/Utils/Formatting.c
Normal file
323
src/Parsee/Utils/Formatting.c
Normal file
|
|
@ -0,0 +1,323 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Http.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Uri.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <StringStream.h>
|
||||||
|
#include <Matrix.h>
|
||||||
|
#include <XML.h>
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
typedef struct XMPPFlags {
|
||||||
|
bool quote;
|
||||||
|
} XMPPFlags;
|
||||||
|
static char *
|
||||||
|
XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
|
{
|
||||||
|
char *xepd = NULL, *tmp = NULL;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
XMLElement *child;
|
||||||
|
char *reply_id = GrabString(
|
||||||
|
event,
|
||||||
|
4, "content",
|
||||||
|
"m.relates_to", "m.in_reply_to", "event_id"
|
||||||
|
);
|
||||||
|
char *room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
||||||
|
HashMap *referenced;
|
||||||
|
char *subxep;
|
||||||
|
#define Concat(strp) do \
|
||||||
|
{ \
|
||||||
|
size_t cidx; \
|
||||||
|
size_t len = strp ? strlen(strp) : 0; \
|
||||||
|
for (cidx = 0; cidx < len; cidx++) \
|
||||||
|
{ \
|
||||||
|
char cch[2]; \
|
||||||
|
cch[0] = strp[cidx]; \
|
||||||
|
cch[1] = '\0'; \
|
||||||
|
char nch = *cch ? strp[cidx+1] : '\0'; \
|
||||||
|
bool c = *cch == '\n' && nch != '>'; \
|
||||||
|
if (c && flags.quote) \
|
||||||
|
{ \
|
||||||
|
tmp = xepd; \
|
||||||
|
xepd = StrConcat(2, xepd, "\n>"); \
|
||||||
|
Free(tmp); \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
tmp = xepd; \
|
||||||
|
xepd = StrConcat(2, xepd, cch); \
|
||||||
|
Free(tmp); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
switch (elem ? elem->type : -1)
|
||||||
|
{
|
||||||
|
case XML_ELEMENT_DATA:
|
||||||
|
Concat(elem->data);
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_TAG:
|
||||||
|
if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong"))
|
||||||
|
{
|
||||||
|
Concat("*");
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat("*");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "em"))
|
||||||
|
{
|
||||||
|
Concat("_");
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat("_");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "code"))
|
||||||
|
{
|
||||||
|
Concat("`");
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat("`");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "mx-reply"))
|
||||||
|
{
|
||||||
|
char *str;
|
||||||
|
referenced = ASFind(ParseeConfigGet(), room_id, reply_id);
|
||||||
|
str = JsonValueAsString(
|
||||||
|
JsonGet(referenced, 2, "content", "body")
|
||||||
|
);
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
JsonFree(referenced);
|
||||||
|
return xepd;
|
||||||
|
}
|
||||||
|
Concat(">");
|
||||||
|
flags.quote = true;
|
||||||
|
Concat(str);
|
||||||
|
flags.quote = false;
|
||||||
|
Concat("\n");
|
||||||
|
JsonFree(referenced);
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "blockquote"))
|
||||||
|
{
|
||||||
|
Concat(">");
|
||||||
|
flags.quote = true;
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
flags.quote = false;
|
||||||
|
Concat("\n");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "br"))
|
||||||
|
{
|
||||||
|
Concat("\n");
|
||||||
|
/* HTML fucking SUCKS */
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
if (i != 0)
|
||||||
|
{
|
||||||
|
Concat("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "a"))
|
||||||
|
{
|
||||||
|
char *href = HashMapGet(elem->attrs, "href");
|
||||||
|
Uri *pref = UriParse(href);
|
||||||
|
if (pref && StrEquals(pref->host, "matrix.to"))
|
||||||
|
{
|
||||||
|
/* TODO: Check if the element here is a Matrix.TO
|
||||||
|
* pointing to a Parsee user. */
|
||||||
|
UserID *id = MatrixParseIDFromMTO(pref);
|
||||||
|
if (id)
|
||||||
|
{
|
||||||
|
char *real_id = StrConcat(4, "@", id->localpart, ":", id->server);
|
||||||
|
/* TODO: Detect if it already is a Parsee user */
|
||||||
|
if (ParseeIsPuppet(conf, real_id))
|
||||||
|
{
|
||||||
|
char *name = ASGetName(conf, NULL, real_id);
|
||||||
|
Concat((name ? name : real_id));
|
||||||
|
Free(name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Concat(real_id);
|
||||||
|
}
|
||||||
|
Free(real_id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Concat(href);
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(conf, event, child, flags);
|
||||||
|
|
||||||
|
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);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return xepd;
|
||||||
|
}
|
||||||
|
static char *
|
||||||
|
GetRawBody(HashMap *event)
|
||||||
|
{
|
||||||
|
void *id;
|
||||||
|
if ((id = MatrixGetEdit(event)))
|
||||||
|
{
|
||||||
|
char *new = GrabString(event, 3, "content", "m.new_content", "body");
|
||||||
|
Free(id);
|
||||||
|
if (new)
|
||||||
|
{
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GrabString(event, 2, "content", "body");
|
||||||
|
}
|
||||||
|
static char *
|
||||||
|
GetHTMLBody(HashMap *event)
|
||||||
|
{
|
||||||
|
if (MatrixGetEdit(event))
|
||||||
|
{
|
||||||
|
char *new = GrabString(event, 3, "content", "m.new_content", "formatted_body");
|
||||||
|
if (new)
|
||||||
|
{
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return GrabString(event, 2, "content", "formatted_body");
|
||||||
|
}
|
||||||
|
static char *
|
||||||
|
GetBodyFormat(HashMap *event)
|
||||||
|
{
|
||||||
|
if (MatrixGetEdit(event))
|
||||||
|
{
|
||||||
|
return GrabString(event, 3, "content", "m.new_content", "format");
|
||||||
|
}
|
||||||
|
return GrabString(event, 2, "content", "format");
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeXMPPify(ParseeData *data, HashMap *event)
|
||||||
|
{
|
||||||
|
char *type, *format, *html;
|
||||||
|
char *xepd = NULL;
|
||||||
|
XMLElement *elem;
|
||||||
|
|
||||||
|
XMPPFlags flags;
|
||||||
|
if (!event)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if it is a message event. */
|
||||||
|
type = JsonValueAsString(HashMapGet(event, "type"));
|
||||||
|
if (!StrEquals(type, "m.room.message"))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StrEquals(GetBodyFormat(event), "org.matrix.custom.html"))
|
||||||
|
{
|
||||||
|
/* Settle for the raw body instead. */
|
||||||
|
char *body = GetRawBody(event);
|
||||||
|
return StrDuplicate(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
html = GetHTMLBody(event);
|
||||||
|
|
||||||
|
html = StrConcat(3, "<html>", html, "</html>");
|
||||||
|
elem = XMLCDecode(StrStreamReader(html), true, true);
|
||||||
|
if (!elem)
|
||||||
|
{
|
||||||
|
/* Settle for the raw body instead.
|
||||||
|
* TODO: Have the parser be more leinent on errors in HTML mode. */
|
||||||
|
char *body = GetRawBody(event);
|
||||||
|
Free(html);
|
||||||
|
return StrDuplicate(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
flags.quote = false;
|
||||||
|
xepd = XMPPifyElement(data ? data->config : NULL, event, elem, flags);
|
||||||
|
|
||||||
|
XMLFreeElement(elem);
|
||||||
|
Free(html);
|
||||||
|
|
||||||
|
return xepd;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ParseeGenerateMTO(char *common_id)
|
||||||
|
{
|
||||||
|
char *matrix_to;
|
||||||
|
if (!common_id)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Is HttpUrlEncode okay? */
|
||||||
|
common_id = HttpUrlEncode(common_id);
|
||||||
|
matrix_to = StrConcat(2, "https://matrix.to/#/", common_id);
|
||||||
|
Free(common_id);
|
||||||
|
|
||||||
|
return matrix_to;
|
||||||
|
}
|
||||||
97
src/Parsee/Utils/Nofly.c
Normal file
97
src/Parsee/Utils/Nofly.c
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Util.h>
|
||||||
|
|
||||||
|
#include <Glob.h>
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ParseeGlobalUnban(ParseeData *data, char *glob)
|
||||||
|
{
|
||||||
|
DbRef *ref;
|
||||||
|
HashMap *j;
|
||||||
|
if (!data || !glob)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbLock(data->db, 1, "global_bans");
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
ref = DbCreate(data->db, 1, "global_bans");
|
||||||
|
}
|
||||||
|
|
||||||
|
j = DbJson(ref);
|
||||||
|
|
||||||
|
JsonValueFree(HashMapDelete(j, glob));
|
||||||
|
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
||||||
|
{
|
||||||
|
DbRef *ref;
|
||||||
|
HashMap *j, *obj;
|
||||||
|
if (!data || !glob)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbLock(data->db, 1, "global_bans");
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
ref = DbCreate(data->db, 1, "global_bans");
|
||||||
|
}
|
||||||
|
|
||||||
|
j = DbJson(ref);
|
||||||
|
|
||||||
|
obj = HashMapCreate();
|
||||||
|
if (reason)
|
||||||
|
{
|
||||||
|
HashMapSet(obj, "reason", JsonValueString(reason));
|
||||||
|
}
|
||||||
|
HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis()));
|
||||||
|
JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj)));
|
||||||
|
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
ParseeManageBan(ParseeData *data, char *user, char *room)
|
||||||
|
{
|
||||||
|
DbRef *ref;
|
||||||
|
HashMap *j;
|
||||||
|
char *key;
|
||||||
|
JsonValue *val;
|
||||||
|
bool banned = false , matches = false;
|
||||||
|
if (!data || !user)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "global_bans");
|
||||||
|
j = DbJson(ref);
|
||||||
|
while (HashMapIterate(j, &key, (void **) &val))
|
||||||
|
{
|
||||||
|
HashMap *obj = JsonValueAsObject(val);
|
||||||
|
if (matches)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (GlobMatches(key, user))
|
||||||
|
{
|
||||||
|
banned = true;
|
||||||
|
matches = true;
|
||||||
|
if (room)
|
||||||
|
{
|
||||||
|
/* TODO: Use the object to set the reason */
|
||||||
|
ASBan(data->config, room, user);
|
||||||
|
(void) obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
|
||||||
|
return banned;
|
||||||
|
}
|
||||||
|
|
||||||
95
src/Parsee/Utils/String.c
Normal file
95
src/Parsee/Utils/String.c
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
#include <Unistring.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
ParseeFindDatastart(char *data)
|
||||||
|
{
|
||||||
|
char *startline;
|
||||||
|
bool found = false;
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
startline = data;
|
||||||
|
while (startline)
|
||||||
|
{
|
||||||
|
char *endline = strchr(startline, '\n');
|
||||||
|
|
||||||
|
if (*startline != '>')
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
startline = endline ? endline + 1 : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) (startline - data);
|
||||||
|
}
|
||||||
|
int
|
||||||
|
ParseeFindDatastartU(char *data)
|
||||||
|
{
|
||||||
|
Unistr *str;
|
||||||
|
size_t ret;
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = UnistrCreate(data);
|
||||||
|
ret = UnistrGetOffset(str, (uint32_t) '>');
|
||||||
|
UnistrFree(str);
|
||||||
|
|
||||||
|
return (int) ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ParseeStringifyDate(uint64_t millis)
|
||||||
|
{
|
||||||
|
uint64_t rest = millis;
|
||||||
|
uint64_t hours, minutes, seconds;
|
||||||
|
char *hs, *ms, *ss, *out;
|
||||||
|
|
||||||
|
hours = rest / (1 HOURS);
|
||||||
|
rest = rest % (1 HOURS);
|
||||||
|
|
||||||
|
minutes = rest / (1 MINUTES);
|
||||||
|
rest = rest % (1 MINUTES);
|
||||||
|
|
||||||
|
seconds = rest / (1 SECONDS);
|
||||||
|
|
||||||
|
hs = StrInt(hours);
|
||||||
|
ms = StrInt(minutes);
|
||||||
|
ss = StrInt(seconds);
|
||||||
|
|
||||||
|
out = StrConcat(8,
|
||||||
|
hours ? hs : "",
|
||||||
|
hours ? " hours" : "",
|
||||||
|
(hours && minutes) ? ", " : "",
|
||||||
|
|
||||||
|
minutes ? ms : "",
|
||||||
|
minutes ? " minutes" : "",
|
||||||
|
(minutes && seconds) ? ", " : "",
|
||||||
|
|
||||||
|
seconds ? ss : "",
|
||||||
|
seconds ? " seconds" : ""
|
||||||
|
);
|
||||||
|
Free(hs);
|
||||||
|
Free(ms);
|
||||||
|
Free(ss);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
53
src/Parsee/Versions.c
Normal file
53
src/Parsee/Versions.c
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
|
bool
|
||||||
|
ParseeIsCompatible(char *ver1, char *ver2)
|
||||||
|
{
|
||||||
|
char *major1 = NULL;
|
||||||
|
char *major2 = NULL;
|
||||||
|
char *tmp;
|
||||||
|
if (!ver1 || !ver2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if one of them is a "flipped"(joke) version. If so,
|
||||||
|
* then the user should definitely NOT try funny things with
|
||||||
|
* their data. */
|
||||||
|
if (*ver1 == '-' || *ver2 == '-')
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GetMajor(v) do \
|
||||||
|
{ \
|
||||||
|
while (*ver##v != '.') \
|
||||||
|
{ \
|
||||||
|
char cb[2]; \
|
||||||
|
cb[0] = *ver##v; \
|
||||||
|
cb[1] = '\0'; \
|
||||||
|
tmp = major##v; \
|
||||||
|
major##v = StrConcat(2, major##v, cb); \
|
||||||
|
Free(tmp); \
|
||||||
|
ver##v++; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
|
||||||
|
GetMajor(1);
|
||||||
|
GetMajor(2);
|
||||||
|
|
||||||
|
if (!StrEquals(major1, major2))
|
||||||
|
{
|
||||||
|
Free(major1);
|
||||||
|
Free(major2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(major1);
|
||||||
|
Free(major2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -2,37 +2,80 @@
|
||||||
|
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <AS.h>
|
#include <AS.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;
|
||||||
HttpClientContext *cctx;
|
HttpClientContext *cctx;
|
||||||
HashMap *reqh;
|
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;
|
||||||
|
|
||||||
/* TODO: Make it check the DB for its validicity. "Purging" would be useful.
|
params = HttpRequestParams(args->ctx);
|
||||||
*/
|
hmac = HashMapGet(params, "hmac");
|
||||||
if (!server || !identi)
|
|
||||||
|
/* TODO: Make it check the DB for its validicity. "Purging" would be
|
||||||
|
* useful, alongside checking if someone isn't just a little idiotic. */
|
||||||
{
|
{
|
||||||
HttpResponseStatus(args->ctx, HTTP_BAD_REQUEST);
|
char *concat = StrConcat(3, server, "/", identi);
|
||||||
return MatrixCreateError("M_NOT_YET_UPLOADED", "No server/identifier");
|
chkmak = ParseeHMACS(args->data->id, concat);
|
||||||
|
Free(concat);
|
||||||
}
|
}
|
||||||
|
if (!server || !identi || !hmac || !StrEquals(hmac, chkmak))
|
||||||
|
{
|
||||||
|
char *err =
|
||||||
|
hmac && StrEquals(hmac, chkmak) ?
|
||||||
|
"No server/identifier/HMAC code" :
|
||||||
|
"Hah! You _dirty_ little liar! Try a little harder!";
|
||||||
|
Free(chkmak);
|
||||||
|
HttpResponseStatus(args->ctx, HTTP_BAD_REQUEST);
|
||||||
|
return MatrixCreateError("M_NOT_YET_UPLOADED", err);
|
||||||
|
}
|
||||||
|
Free(chkmak);
|
||||||
|
|
||||||
server = HttpUrlEncode(server);
|
/* Proxy the media through an authenticated endpoint if the HMAC
|
||||||
identi = HttpUrlEncode(identi);
|
* is valid. */
|
||||||
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
cctx = TryDownload(args->data, 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))
|
||||||
{
|
{
|
||||||
|
|
@ -45,8 +88,6 @@ RouteHead(RouteMedia, arr, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClientContextFree(cctx);
|
HttpClientContextFree(cctx);
|
||||||
Free(server);
|
|
||||||
Free(identi);
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,12 @@ RouteHead(RoutePing, arr, argp)
|
||||||
);
|
);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
Log(LOG_INFO, "Pong!");
|
|
||||||
|
|
||||||
RequestJSON();
|
RequestJSON();
|
||||||
|
|
||||||
/* TODO: Load ping info */
|
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
end:
|
end:
|
||||||
|
(void) arr;
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,26 @@ GetRandomQuote(void)
|
||||||
"We truly live in a " CODE "...",
|
"We truly live in a " CODE "...",
|
||||||
"You truly lack the Desire Drive for this!",
|
"You truly lack the Desire Drive for this!",
|
||||||
"Eeh? Bifrost mode? Only bad servers use Bifrost mode!",
|
"Eeh? Bifrost mode? Only bad servers use Bifrost mode!",
|
||||||
|
"'Wow parsee can save the world' - said a wise guy",
|
||||||
"As small as a dwarf, and can run on your pie!",
|
"As small as a dwarf, and can run on your pie!",
|
||||||
"It's all wicked unsafe, memory slow 🚀🚀🚀🚀",
|
"It's all wicked unsafe, memory slow 🚀🚀🚀🚀",
|
||||||
|
|
||||||
"XMPP kinda sucks.",
|
"XMPP kinda sucks.",
|
||||||
"Matrix kinda sucks.",
|
"Matrix kinda sucks.",
|
||||||
|
|
||||||
"Throw jabs!"
|
"Throw jabs!",
|
||||||
|
"'Won't you hold my <presence/> safe?' - Kanako",
|
||||||
|
|
||||||
|
NAME ": the federated world's little little kobashi",
|
||||||
|
|
||||||
|
"Go take a look at your stanzas!",
|
||||||
|
"Go take a look at your objects!",
|
||||||
|
|
||||||
|
"DEC Alpha AXP-Certified!",
|
||||||
|
|
||||||
|
"this is the moment parsee started parsing or smth idk"
|
||||||
|
" - another wise person",
|
||||||
|
"Ah, merde, mon TGV est en retard de 53 minutes !"
|
||||||
};
|
};
|
||||||
const size_t count = sizeof(quotes)/sizeof(*quotes);
|
const size_t count = sizeof(quotes)/sizeof(*quotes);
|
||||||
|
|
||||||
|
|
@ -69,6 +82,7 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
{
|
{
|
||||||
P("<title>%s Lander</title>", NAME);
|
P("<title>%s Lander</title>", NAME);
|
||||||
P("<meta charset='UTF-8'/>");
|
P("<meta charset='UTF-8'/>");
|
||||||
|
P("<link rel='icon' href='data:image/png;base64,%s'/>", media_parsee_logo);
|
||||||
P("<style>");
|
P("<style>");
|
||||||
{
|
{
|
||||||
P("html {");
|
P("html {");
|
||||||
|
|
@ -76,6 +90,17 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("color: #eee;");
|
P("color: #eee;");
|
||||||
P("font-family: sans-serif;");
|
P("font-family: sans-serif;");
|
||||||
P("}");
|
P("}");
|
||||||
|
P("#ascii {");
|
||||||
|
P("text-align: center;");
|
||||||
|
P("color: #be1337;");
|
||||||
|
P("}");
|
||||||
|
P("#ascii pre {");
|
||||||
|
P("display: inline-block;");
|
||||||
|
P("text-align: left;");
|
||||||
|
P("}");
|
||||||
|
P("img {");
|
||||||
|
P("image-rendering: pixelated;");
|
||||||
|
P("}");
|
||||||
P("blockquote {");
|
P("blockquote {");
|
||||||
P("border-left: 2px solid #ccc;");
|
P("border-left: 2px solid #ccc;");
|
||||||
P("margin: 1.5em 10px;");
|
P("margin: 1.5em 10px;");
|
||||||
|
|
@ -98,15 +123,25 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
|
|
||||||
P("<body>");
|
P("<body>");
|
||||||
{
|
{
|
||||||
P("<center><h1>");
|
size_t i;
|
||||||
P("Your %s is running, all with that %s!", NAME, CODE);
|
P("<center>");
|
||||||
P("</h1></center>");
|
P("<h1>Your %s is running, all with that %s!</h1>", NAME, CODE);
|
||||||
|
P("</center>");
|
||||||
P("<hr/>");
|
P("<hr/>");
|
||||||
P("<blockquote><i>");
|
P("<blockquote><i>");
|
||||||
{
|
{
|
||||||
P("%s", GetRandomQuote());
|
P("%s", GetRandomQuote());
|
||||||
}
|
}
|
||||||
P("</i></blockquote>");
|
P("</i></blockquote>");
|
||||||
|
P("<pre id='ascii'><code>");
|
||||||
|
for (i = 0; i < PARSEE_ASCII_LINES; i++)
|
||||||
|
{
|
||||||
|
XMLElement *e = XMLCreateText((char *) parsee_ascii[i]);
|
||||||
|
XMLEncode(args->stream, e);
|
||||||
|
XMLFreeElement(e);
|
||||||
|
P("<br/>");
|
||||||
|
}
|
||||||
|
P("</code></pre>");
|
||||||
|
|
||||||
P("<p>");
|
P("<p>");
|
||||||
{
|
{
|
||||||
|
|
@ -144,7 +179,7 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("<p>");
|
P("<p>");
|
||||||
{
|
{
|
||||||
P("More information available at ");
|
P("More information available at ");
|
||||||
P("<a");
|
P("<a ");
|
||||||
P("href='https://kappach.at/parsee'");
|
P("href='https://kappach.at/parsee'");
|
||||||
P(">the actual page</a>.");
|
P(">the actual page</a>.");
|
||||||
}
|
}
|
||||||
|
|
@ -199,7 +234,7 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("Some clicky links relating to %s:", NAME);
|
P("Some clicky links relating to %s:", NAME);
|
||||||
P("<ul>");
|
P("<ul>");
|
||||||
{
|
{
|
||||||
const char *fedi = "https://ak.ari.lt/parsee";
|
const char *fedi = "https://tengu.kappach.at/parsee";
|
||||||
const char *icon = "https://kappach.at/parsee.gif";
|
const char *icon = "https://kappach.at/parsee.gif";
|
||||||
P("<li><a href='%s'>Repository</a></li>", REPOSITORY);
|
P("<li><a href='%s'>Repository</a></li>", REPOSITORY);
|
||||||
P("<li><a href='%s'>Fediverse</a></li>", fedi);
|
P("<li><a href='%s'>Fediverse</a></li>", fedi);
|
||||||
|
|
@ -219,5 +254,6 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("</html>");
|
P("</html>");
|
||||||
#undef P
|
#undef P
|
||||||
|
|
||||||
|
(void) arr;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ RouteHead(RouteTxns, arr, argp)
|
||||||
|
|
||||||
RequestJSON();
|
RequestJSON();
|
||||||
|
|
||||||
/* TODO: Do a thing with these. */
|
|
||||||
events = JsonValueAsArray(HashMapGet(request, "events"));
|
events = JsonValueAsArray(HashMapGet(request, "events"));
|
||||||
for (i = 0; i < ArraySize(events); i++)
|
for (i = 0; i < ArraySize(events); i++)
|
||||||
{
|
{
|
||||||
|
|
@ -43,6 +42,7 @@ RouteHead(RouteTxns, arr, argp)
|
||||||
|
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
end:
|
end:
|
||||||
|
(void) arr;
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ RouteHead(RouteRoomAck, arr, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
muc = ParseeDecodeLocalMUC(args->data->config, room);
|
muc = ParseeDecodeLocalMUC(args->data->config, room);
|
||||||
if (ParseeManageBan(args->data, muc, NULL))
|
if (ParseeManageBan(args->data, muc, NULL) ||
|
||||||
|
ParseeIsMUCWhitelisted(args->data, muc))
|
||||||
{
|
{
|
||||||
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
||||||
response = MatrixCreateError(
|
response = MatrixCreateError(
|
||||||
|
|
@ -130,7 +131,7 @@ RouteHead(RouteRoomAck, arr, argp)
|
||||||
{
|
{
|
||||||
char *rev = StrConcat(2, muc, "/parsee");
|
char *rev = StrConcat(2, muc, "/parsee");
|
||||||
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
||||||
XMPPJoinMUC(args->data->jabber, "parsee", rev);
|
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
|
||||||
|
|
||||||
Free(rev);
|
Free(rev);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
src/Signal.c
19
src/Signal.c
|
|
@ -6,32 +6,35 @@
|
||||||
|
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
|
|
||||||
static HttpServer *server = NULL;
|
static ParseeData *data;
|
||||||
static pthread_t xmpp_thr;
|
static pthread_t xmpp_thr;
|
||||||
static XMPPComponent *jabber = NULL;
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
SignalHandler(int signal)
|
SignalHandler(int signal)
|
||||||
{
|
{
|
||||||
if (server && (signal == SIGTERM || signal == SIGINT))
|
if (data->server && (signal == SIGTERM || signal == SIGINT))
|
||||||
{
|
{
|
||||||
Log(LOG_INFO, "Killing thread...");
|
Log(LOG_INFO, "Killing thread...");
|
||||||
XMPPFinishCompStream(jabber);
|
|
||||||
|
pthread_mutex_lock(&data->halt_lock);
|
||||||
|
data->halted = true;
|
||||||
|
pthread_mutex_unlock(&data->halt_lock);
|
||||||
|
|
||||||
|
XMPPFinishCompStream(data->jabber);
|
||||||
pthread_join(xmpp_thr, NULL);
|
pthread_join(xmpp_thr, NULL);
|
||||||
Log(LOG_INFO, "Stopping server...");
|
Log(LOG_INFO, "Stopping server...");
|
||||||
HttpServerStop(server);
|
HttpServerStop(data->server);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseeInitialiseSignals(HttpServer *s, pthread_t xmpp, XMPPComponent *j)
|
ParseeInitialiseSignals(ParseeData *d, pthread_t xmpp)
|
||||||
{
|
{
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
server = s;
|
data = d;
|
||||||
xmpp_thr = xmpp;
|
xmpp_thr = xmpp;
|
||||||
jabber = j;
|
|
||||||
|
|
||||||
sigfillset(&sa.sa_mask);
|
sigfillset(&sa.sa_mask);
|
||||||
sa.sa_handler = SignalHandler;
|
sa.sa_handler = SignalHandler;
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ CreateStanzaBuilder(char *from, char *to, char *id)
|
||||||
|
|
||||||
builder->replying_to_stanza = NULL;
|
builder->replying_to_stanza = NULL;
|
||||||
builder->replying_to_sender = NULL;
|
builder->replying_to_sender = NULL;
|
||||||
|
builder->editing = NULL;
|
||||||
builder->type = NULL;
|
builder->type = NULL;
|
||||||
builder->body = NULL;
|
builder->body = NULL;
|
||||||
builder->oob = NULL;
|
builder->oob = NULL;
|
||||||
|
|
@ -184,14 +185,14 @@ ExportStanza(StanzaBuilder *builder)
|
||||||
builder->replying_to_sender &&
|
builder->replying_to_sender &&
|
||||||
builder->body)
|
builder->body)
|
||||||
{
|
{
|
||||||
int off = ParseeFindDatastart(builder->body);
|
int off = ParseeFindDatastartU(builder->body);
|
||||||
char *ostr = StrInt(off);
|
char *ostr = StrInt(off);
|
||||||
XMLElement *reply = XMLCreateTag("reply");
|
XMLElement *reply = XMLCreateTag("reply");
|
||||||
XMLElement *fallback = XMLCreateTag("fallback");
|
XMLElement *fallback = XMLCreateTag("fallback");
|
||||||
XMLElement *fall_body = XMLCreateTag("body");
|
XMLElement *fall_body = XMLCreateTag("body");
|
||||||
|
|
||||||
XMLAddAttr(reply, "to", builder->replying_to_stanza);
|
XMLAddAttr(reply, "to", builder->replying_to_sender);
|
||||||
XMLAddAttr(reply, "id", builder->replying_to_sender);
|
XMLAddAttr(reply, "id", builder->replying_to_stanza);
|
||||||
XMLAddAttr(reply, "xmlns", "urn:xmpp:reply:0");
|
XMLAddAttr(reply, "xmlns", "urn:xmpp:reply:0");
|
||||||
|
|
||||||
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
||||||
|
|
@ -227,22 +228,21 @@ ExportStanza(StanzaBuilder *builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber)
|
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber, size_t max)
|
||||||
{
|
{
|
||||||
XMLElement *elem;
|
XMLElement *elem;
|
||||||
if (!builder || !jabber)
|
if (!builder || !jabber)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!max)
|
||||||
|
{
|
||||||
|
max = 10000; /* XMPP recommended limit */
|
||||||
|
}
|
||||||
|
|
||||||
elem = ExportStanza(builder);
|
elem = ExportStanza(builder);
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
XMPPSendStanza(jabber, elem, max);
|
||||||
XMLEncode(jabber->stream, elem);
|
|
||||||
StreamFlush(jabber->stream);
|
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
|
|
||||||
XMLFreeElement(elem);
|
XMLFreeElement(elem);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StanzaBuilder *
|
StanzaBuilder *
|
||||||
|
|
@ -258,7 +258,6 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
|
||||||
{
|
{
|
||||||
XMLElement *parsee_version, *ver_elem;
|
XMLElement *parsee_version, *ver_elem;
|
||||||
XMLElement *parsee_link, *link_elem;
|
XMLElement *parsee_link, *link_elem;
|
||||||
XMLElement *parsee_text, *text_elem;
|
|
||||||
XMLElement *parsee_event, *event_elem;
|
XMLElement *parsee_event, *event_elem;
|
||||||
XMLElement *parsee_json, *json_elem;
|
XMLElement *parsee_json, *json_elem;
|
||||||
char *event_id = GrabString(e, 1, "event_id");
|
char *event_id = GrabString(e, 1, "event_id");
|
||||||
|
|
@ -285,16 +284,6 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
|
||||||
XMLAddChild(parsee_link, link_elem);
|
XMLAddChild(parsee_link, link_elem);
|
||||||
XMLAddChild(parsee, parsee_link);
|
XMLAddChild(parsee, parsee_link);
|
||||||
|
|
||||||
parsee_text = XMLCreateTag("zayds-note");
|
|
||||||
text_elem = XMLCreateText("\"LDA HANG YOURSELF\" - Zayd");
|
|
||||||
XMLAddChild(parsee_text, text_elem);
|
|
||||||
XMLAddChild(parsee, parsee_text);
|
|
||||||
|
|
||||||
parsee_text = XMLCreateTag("mcnebs-note");
|
|
||||||
text_elem = XMLCreateText("LDA will never beat the allegations");
|
|
||||||
XMLAddChild(parsee_text, text_elem);
|
|
||||||
XMLAddChild(parsee, parsee_text);
|
|
||||||
|
|
||||||
if (event_id)
|
if (event_id)
|
||||||
{
|
{
|
||||||
parsee_event = XMLCreateTag("event-id");
|
parsee_event = XMLCreateTag("event-id");
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
char **
|
char **
|
||||||
StrSplitLines(char *text)
|
StrSplitLines(char *text)
|
||||||
|
|
@ -117,7 +116,7 @@ StrFullRect(char **split)
|
||||||
char
|
char
|
||||||
StrGet(StringRect *rect, int line, int col)
|
StrGet(StringRect *rect, int line, int col)
|
||||||
{
|
{
|
||||||
int actual_line, actual_col;
|
size_t actual_line, actual_col;
|
||||||
char *linep;
|
char *linep;
|
||||||
if (!rect || !rect->source_lines)
|
if (!rect || !rect->source_lines)
|
||||||
{
|
{
|
||||||
|
|
@ -150,7 +149,7 @@ StrGet(StringRect *rect, int line, int col)
|
||||||
size_t
|
size_t
|
||||||
StrViewChars(StringRect rect, int line)
|
StrViewChars(StringRect rect, int line)
|
||||||
{
|
{
|
||||||
int actual_line;
|
size_t actual_line;
|
||||||
char *linep;
|
char *linep;
|
||||||
if (!rect.source_lines)
|
if (!rect.source_lines)
|
||||||
{
|
{
|
||||||
|
|
@ -174,7 +173,7 @@ StrViewChars(StringRect rect, int line)
|
||||||
StringRect
|
StringRect
|
||||||
StrGetl(StringRect *rect, int line, bool extend)
|
StrGetl(StringRect *rect, int line, bool extend)
|
||||||
{
|
{
|
||||||
int actual_line;
|
size_t actual_line;
|
||||||
StringRect ret;
|
StringRect ret;
|
||||||
if (!rect->source_lines)
|
if (!rect->source_lines)
|
||||||
{
|
{
|
||||||
|
|
@ -204,7 +203,7 @@ StrGetl(StringRect *rect, int line, bool extend)
|
||||||
StringRect
|
StringRect
|
||||||
StrShift(StringRect rect, int n)
|
StrShift(StringRect rect, int n)
|
||||||
{
|
{
|
||||||
int new = rect.start_char + n;
|
size_t new = rect.start_char + n;
|
||||||
if (new > rect.end_char)
|
if (new > rect.end_char)
|
||||||
{
|
{
|
||||||
new = rect.end_char;
|
new = rect.end_char;
|
||||||
|
|
|
||||||
|
|
@ -7,125 +7,13 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
typedef struct ReaderCookie {
|
|
||||||
size_t length;
|
|
||||||
size_t offset;
|
|
||||||
char *buffer;
|
|
||||||
} ReaderCookie;
|
|
||||||
|
|
||||||
#define Remaining() (cook->length - cook->offset)
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
ReadStreamReader(void *coop, void *to, size_t n)
|
|
||||||
{
|
|
||||||
ReaderCookie *cook = coop;
|
|
||||||
size_t remaining;
|
|
||||||
if (!cook)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
remaining = Remaining();
|
|
||||||
if (n > remaining)
|
|
||||||
{
|
|
||||||
memcpy(to, cook->buffer + cook->offset, remaining);
|
|
||||||
cook->offset = cook->length;
|
|
||||||
return remaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(to, cook->buffer + cook->offset, n);
|
|
||||||
cook->offset += n;
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
static ssize_t
|
|
||||||
WriteStreamReader(void *coop, void *from, size_t n)
|
|
||||||
{
|
|
||||||
/* Writing to a stream reader is silly. */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
static off_t
|
|
||||||
SeekStreamReader(void *coop, off_t mag, int sgn)
|
|
||||||
{
|
|
||||||
ReaderCookie *cook = coop;
|
|
||||||
if (!cook)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (sgn)
|
|
||||||
{
|
|
||||||
case SEEK_SET:
|
|
||||||
if (mag > cook->length)
|
|
||||||
{
|
|
||||||
cook->offset = cook->length;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else if (mag < 0)
|
|
||||||
{
|
|
||||||
cook->offset = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
cook->offset = mag;
|
|
||||||
return 0;
|
|
||||||
case SEEK_CUR:
|
|
||||||
cook->offset += mag;
|
|
||||||
if (cook->offset > cook->length)
|
|
||||||
{
|
|
||||||
cook->offset = cook->length;
|
|
||||||
}
|
|
||||||
else if (cook->offset < 0)
|
|
||||||
{
|
|
||||||
cook->offset = 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
case SEEK_END:
|
|
||||||
cook->offset += cook->length + mag;
|
|
||||||
if (cook->offset > cook->length)
|
|
||||||
{
|
|
||||||
cook->offset = cook->length;
|
|
||||||
}
|
|
||||||
else if (cook->offset < 0)
|
|
||||||
{
|
|
||||||
cook->offset = 0;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
CloseStreamReader(void *coop)
|
|
||||||
{
|
|
||||||
/* Nothing to free as of now. */
|
|
||||||
if (coop)
|
|
||||||
{
|
|
||||||
Free(coop);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const static IoFunctions Functions = {
|
|
||||||
.read = ReadStreamReader,
|
|
||||||
.seek = SeekStreamReader,
|
|
||||||
.write = WriteStreamReader,
|
|
||||||
.close = CloseStreamReader,
|
|
||||||
};
|
|
||||||
|
|
||||||
Stream *
|
Stream *
|
||||||
StrStreamReaderN(char *buffer, int n)
|
StrStreamReaderN(char *buffer, int n)
|
||||||
{
|
{
|
||||||
Io *raw_io;
|
if (!buffer || n < 0)
|
||||||
ReaderCookie *cookie;
|
|
||||||
if (!buffer)
|
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie = Malloc(sizeof(*cookie));
|
return StreamFile(fmemopen(buffer, n ? (size_t) n : strlen(buffer), "rb"));
|
||||||
cookie->buffer = buffer;
|
|
||||||
cookie->length = n ? n : strlen(buffer);
|
|
||||||
cookie->offset = 0;
|
|
||||||
raw_io = IoCreate(cookie, Functions);
|
|
||||||
return StreamIo(raw_io);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ static ssize_t
|
||||||
ReadStreamWriter(void *coop, void *to, size_t n)
|
ReadStreamWriter(void *coop, void *to, size_t n)
|
||||||
{
|
{
|
||||||
/* Reading from a stream writer is silly. */
|
/* Reading from a stream writer is silly. */
|
||||||
|
(void) coop;
|
||||||
|
(void) to;
|
||||||
|
(void) n;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
|
@ -33,6 +36,9 @@ static off_t
|
||||||
SeekStreamWriter(void *coop, off_t mag, int sgn)
|
SeekStreamWriter(void *coop, off_t mag, int sgn)
|
||||||
{
|
{
|
||||||
/* TODO: Seeking would be useful, though not supported yet. */
|
/* TODO: Seeking would be useful, though not supported yet. */
|
||||||
|
(void) coop;
|
||||||
|
(void) mag;
|
||||||
|
(void) sgn;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,10 +46,11 @@ static int
|
||||||
CloseStreamWriter(void *coop)
|
CloseStreamWriter(void *coop)
|
||||||
{
|
{
|
||||||
/* Nothing to free as of now. */
|
/* Nothing to free as of now. */
|
||||||
|
(void) coop;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const static IoFunctions Functions = {
|
static const IoFunctions Functions = {
|
||||||
.read = ReadStreamWriter,
|
.read = ReadStreamWriter,
|
||||||
.seek = SeekStreamWriter,
|
.seek = SeekStreamWriter,
|
||||||
.write = WriteStreamWriter,
|
.write = WriteStreamWriter,
|
||||||
|
|
|
||||||
314
src/Unistr.c
Normal file
314
src/Unistr.c
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
#include <Unistring.h>
|
||||||
|
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
struct Unistr {
|
||||||
|
size_t length;
|
||||||
|
uint32_t *codepoints;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
UnistrAddch(Unistr *unistr, uint32_t u)
|
||||||
|
{
|
||||||
|
if (!unistr || !u)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unistr->length++;
|
||||||
|
unistr->codepoints = Realloc(
|
||||||
|
unistr->codepoints,
|
||||||
|
unistr->length * sizeof(*unistr->codepoints)
|
||||||
|
);
|
||||||
|
|
||||||
|
unistr->codepoints[unistr->length - 1] = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
UTFIsN(char *off, size_t available, size_t n, uint8_t pc)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint8_t *offu = (uint8_t *) off;
|
||||||
|
if (((available < n) || ((*offu >> (8-n-1)) != pc)) && (n >= 1))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < n - 1; i++)
|
||||||
|
{
|
||||||
|
if ((offu[i+1] >> 6) != 0x2)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unistr *
|
||||||
|
UnistrCreate(char *src)
|
||||||
|
{
|
||||||
|
size_t len, i;
|
||||||
|
Unistr *str;
|
||||||
|
if (!src)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = strlen(src);
|
||||||
|
str = Malloc(sizeof(*str));
|
||||||
|
str->length = 0;
|
||||||
|
str->codepoints = NULL;
|
||||||
|
|
||||||
|
/* We can't just set the length to {len}. */
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
char byte = src[i];
|
||||||
|
size_t available = len - i;
|
||||||
|
if ((byte & 0x80) == 0)
|
||||||
|
{
|
||||||
|
/* This is a regular codepoint */
|
||||||
|
UnistrAddch(str, byte & 0x7F);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (UTFIsN(&src[i], available, 2, 0x06))
|
||||||
|
{
|
||||||
|
char a = src[i+0] & 0x1F;
|
||||||
|
char b = src[i+1] & 0x3F;
|
||||||
|
uint32_t u = (a << (6 * 1)) | b;
|
||||||
|
|
||||||
|
/* Overlongs are errors. */
|
||||||
|
if (u < 0x0080 || u > 0x07FF)
|
||||||
|
{
|
||||||
|
UnistrFree(str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnistrAddch(str, u);
|
||||||
|
i += 2 - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (UTFIsN(&src[i], available, 3, 0x0E))
|
||||||
|
{
|
||||||
|
char a = src[i+0] & 0x0F;
|
||||||
|
char b = src[i+1] & 0x3F;
|
||||||
|
char c = src[i+2] & 0x3F;
|
||||||
|
uint32_t u =
|
||||||
|
(a << (6 * 2)) |
|
||||||
|
(b << (6 * 1)) |
|
||||||
|
(c << (6 * 0)) ;
|
||||||
|
|
||||||
|
/* Overlongs are errors. */
|
||||||
|
if (u < 0x0800 || u > 0xFFFF)
|
||||||
|
{
|
||||||
|
UnistrFree(str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnistrAddch(str, u);
|
||||||
|
i += 3 - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (UTFIsN(&src[i], available, 4, 0x1E))
|
||||||
|
{
|
||||||
|
char a = src[i+0] & 0x07;
|
||||||
|
char b = src[i+1] & 0x3F;
|
||||||
|
char c = src[i+2] & 0x3F;
|
||||||
|
char d = src[i+3] & 0x3F;
|
||||||
|
uint32_t u =
|
||||||
|
(a << (6 * 3)) |
|
||||||
|
(b << (6 * 2)) |
|
||||||
|
(c << (6 * 1)) |
|
||||||
|
(d << (6 * 0)) ;
|
||||||
|
|
||||||
|
/* Overlongs are errors. */
|
||||||
|
if (u < 0x10000 || u > 0x10FFFF)
|
||||||
|
{
|
||||||
|
UnistrFree(str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnistrAddch(str, u);
|
||||||
|
i += 4 - 1;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
UnistrFree(Unistr *unistr)
|
||||||
|
{
|
||||||
|
if (!unistr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(unistr->codepoints);
|
||||||
|
Free(unistr);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
UnistrC(Unistr *unistr)
|
||||||
|
{
|
||||||
|
char *ret, *tmp, *utf;
|
||||||
|
size_t i;
|
||||||
|
if (!unistr)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = NULL;
|
||||||
|
for (i = 0; i < unistr->length; i++)
|
||||||
|
{
|
||||||
|
uint32_t code = unistr->codepoints[i];
|
||||||
|
utf = StrUtf8Encode(code);
|
||||||
|
|
||||||
|
tmp = ret;
|
||||||
|
ret = StrConcat(2, ret, utf);
|
||||||
|
Free(tmp);
|
||||||
|
Free(utf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
size_t
|
||||||
|
UnistrSize(Unistr *unistr)
|
||||||
|
{
|
||||||
|
return unistr ? unistr->length : 0;
|
||||||
|
}
|
||||||
|
uint32_t
|
||||||
|
UnistrGetch(Unistr *unistr, size_t i)
|
||||||
|
{
|
||||||
|
if (!unistr)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i < unistr->length ? unistr->codepoints[i] : 0;
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
UnistrIsASCII(uint32_t u)
|
||||||
|
{
|
||||||
|
if (u == 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u < 0x7F;
|
||||||
|
}
|
||||||
|
bool
|
||||||
|
UnistrIsBMP(uint32_t u)
|
||||||
|
{
|
||||||
|
if (u == 0)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return u <= 0xFFFF;
|
||||||
|
}
|
||||||
|
Unistr *
|
||||||
|
UnistrFilter(Unistr *str, UnistrFilterFunc filter)
|
||||||
|
{
|
||||||
|
Unistr *unistr;
|
||||||
|
size_t i;
|
||||||
|
if (!str || !filter)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unistr = UnistrCreate("");
|
||||||
|
for (i = 0; i < UnistrSize(str); i++)
|
||||||
|
{
|
||||||
|
uint32_t code = UnistrGetch(str, i);
|
||||||
|
if (!filter(code))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UnistrAddch(unistr, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return unistr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unistr *
|
||||||
|
UnistrConcat(size_t n, ...)
|
||||||
|
{
|
||||||
|
va_list list;
|
||||||
|
size_t i;
|
||||||
|
Unistr *ret = UnistrCreate("");
|
||||||
|
|
||||||
|
va_start(list, n);
|
||||||
|
for (i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
Unistr *to_concat = va_arg(list, Unistr *);
|
||||||
|
size_t j;
|
||||||
|
for (j = 0; j < UnistrSize(to_concat); j++)
|
||||||
|
{
|
||||||
|
UnistrAddch(ret, UnistrGetch(to_concat, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(list);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
size_t
|
||||||
|
UnistrGetOffset(Unistr *str, uint32_t sep)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
uint32_t prev = 0x0A;
|
||||||
|
if (!str || !sep)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < str->length; i++)
|
||||||
|
{
|
||||||
|
uint32_t curr = str->codepoints[i];
|
||||||
|
if (prev == 0x0A && curr != sep)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
prev = curr;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
@ -80,7 +80,14 @@ DecodeQuote(StringRect rect, size_t *skip)
|
||||||
ret = rect;
|
ret = rect;
|
||||||
ret.end_line--;
|
ret.end_line--;
|
||||||
|
|
||||||
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace(ch))
|
/* TODO: You can easily craft strings that conceal data(
|
||||||
|
* > Please mind the whitespace
|
||||||
|
* > hidden we're concealing data to Matrix
|
||||||
|
* > star in Well, you can still stare at the body and XML
|
||||||
|
* > four but that's for Nerds
|
||||||
|
* > seasons See, Touhou reference!
|
||||||
|
* concealing!) */
|
||||||
|
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace((int) ch))
|
||||||
{
|
{
|
||||||
shift_by++;
|
shift_by++;
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +132,7 @@ DecodeSpan(StringRect rect, char del, size_t *skip)
|
||||||
{
|
{
|
||||||
return StrFullRect(NULL);
|
return StrFullRect(NULL);
|
||||||
}
|
}
|
||||||
if (!ret.source_lines && isspace(c))
|
if (!ret.source_lines && isspace((int) c))
|
||||||
{
|
{
|
||||||
return StrFullRect(NULL);
|
return StrFullRect(NULL);
|
||||||
}
|
}
|
||||||
|
|
@ -322,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");
|
||||||
|
|
@ -365,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("~"));
|
||||||
|
|
@ -392,21 +399,37 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
||||||
char *
|
char *
|
||||||
XEP393ToXMLString(XEP393Element *xepd)
|
XEP393ToXMLString(XEP393Element *xepd)
|
||||||
{
|
{
|
||||||
XMLElement *root;
|
XMLElement *root, *act_root;
|
||||||
|
XMLElement *child;
|
||||||
|
|
||||||
Stream *writer;
|
Stream *writer;
|
||||||
char *ret = NULL;
|
char *ret = NULL;
|
||||||
|
size_t i, children;
|
||||||
if (!xepd)
|
if (!xepd)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
root = XMLCreateTag("span");
|
root = XMLCreateTag("span");
|
||||||
|
act_root = root;
|
||||||
ShoveXML(xepd, root);
|
ShoveXML(xepd, root);
|
||||||
|
|
||||||
writer = StrStreamWriter(&ret);
|
writer = StrStreamWriter(&ret);
|
||||||
XMLEncode(writer, root);
|
children = ArraySize(root->children);
|
||||||
XMLFreeElement(root);
|
|
||||||
|
child = ArrayGet(root->children, 0);
|
||||||
|
if (children == 1 && StrEquals(child->name, "p"))
|
||||||
|
{
|
||||||
|
children = ArraySize(child->children);
|
||||||
|
root = child;
|
||||||
|
}
|
||||||
|
for (i = 0; i < children; i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(root->children, i);
|
||||||
|
|
||||||
|
XMLEncode(writer, child);
|
||||||
|
}
|
||||||
|
XMLFreeElement(act_root);
|
||||||
StreamFlush(writer);
|
StreamFlush(writer);
|
||||||
StreamClose(writer);
|
StreamClose(writer);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -114,9 +120,19 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
||||||
void
|
void
|
||||||
XMLEncodeString(Stream *stream, char *data)
|
XMLEncodeString(Stream *stream, char *data)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i, len;
|
||||||
|
|
||||||
for (i = 0; i < strlen(data); i++)
|
if (!stream || !data)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: I should write a "Parsee Best Practice" guideline and make sure
|
||||||
|
* people understand to NOT constantly recompute lengths parameter on
|
||||||
|
* these kinds of loops. ArraySize is fine(since its indirection), but
|
||||||
|
* operations like strlen take time! */
|
||||||
|
len = strlen(data);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
char c = data[i];
|
char c = data[i];
|
||||||
if (c == '<')
|
if (c == '<')
|
||||||
|
|
@ -145,6 +161,9 @@ XMLEncodeString(Stream *stream, char *data)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
StreamPrintf(stream, "%c", c);
|
StreamPrintf(stream, "%c", c);
|
||||||
|
/* TODO: Maybe consider Unistrings and encode arbitrary Unicode
|
||||||
|
* codepoints * with special XML. Oughta make it printable, you know?
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -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. */
|
||||||
|
|
@ -295,7 +308,7 @@ static bool
|
||||||
XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
||||||
{
|
{
|
||||||
int *stack;
|
int *stack;
|
||||||
size_t top, i;
|
size_t top, i, len;
|
||||||
ssize_t ntop;
|
ssize_t ntop;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (!lexer || !str)
|
if (!lexer || !str)
|
||||||
|
|
@ -304,9 +317,10 @@ XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
||||||
}
|
}
|
||||||
|
|
||||||
top = 0;
|
top = 0;
|
||||||
stack = Malloc(strlen(str) * sizeof(*stack));
|
len = strlen(str);
|
||||||
|
stack = Malloc(len * sizeof(*stack));
|
||||||
|
|
||||||
for (i = 0; i < strlen(str); i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
char c = str[i];
|
char c = str[i];
|
||||||
int getc = XMLGetc(lexer);
|
int getc = XMLGetc(lexer);
|
||||||
|
|
@ -336,8 +350,8 @@ seekback:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define IsNamestart(c) ((c == ':') || isalpha(c) || (c == '_'))
|
#define IsNamestart(c) ((c == ':') || isalpha((int) c) || (c == '_'))
|
||||||
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit(c))
|
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit((int) c))
|
||||||
static char *
|
static char *
|
||||||
XMLParseName(XMLexer *lexer)
|
XMLParseName(XMLexer *lexer)
|
||||||
{
|
{
|
||||||
|
|
@ -582,6 +596,8 @@ XMLCreateEnd(XMLexer *lexer, char *end)
|
||||||
event->col = 0;
|
event->col = 0;
|
||||||
event->offset = 0;
|
event->offset = 0;
|
||||||
|
|
||||||
|
(void) lexer;
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
static XMLEvent *
|
static XMLEvent *
|
||||||
|
|
@ -690,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));
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,15 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include <StringStream.h>
|
||||||
|
#include <Parsee.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
||||||
/* The default component port Prosody uses. */
|
/* The default component port Prosody uses. */
|
||||||
#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;
|
||||||
|
|
@ -26,12 +28,26 @@ 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)
|
||||||
|
{
|
||||||
|
const char *error_str = gai_strerror(error);
|
||||||
|
Log(LOG_ERR,
|
||||||
|
"%s: cannot connect to '%s': %s", __func__,
|
||||||
|
host, error_str
|
||||||
|
);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (res = res0; res; res = res->ai_next)
|
for (res = res0; res; res = res->ai_next)
|
||||||
{
|
{
|
||||||
|
|
@ -45,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;
|
||||||
|
|
@ -55,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);
|
||||||
|
|
@ -62,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;
|
||||||
}
|
}
|
||||||
|
|
@ -76,19 +104,15 @@ XMPPInitialiseCompStream(char *host, int port)
|
||||||
return comp;
|
return comp;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <Cytoplasm/Sha.h>
|
|
||||||
static char *
|
static char *
|
||||||
ComputeHandshake(char *shared, char *stream)
|
ComputeHandshake(char *shared, char *stream)
|
||||||
{
|
{
|
||||||
char *source;
|
char *source;
|
||||||
unsigned char *raw_sha;
|
|
||||||
char *sha;
|
char *sha;
|
||||||
|
|
||||||
source = StrConcat(2, stream, shared);
|
source = StrConcat(2, stream, shared);
|
||||||
raw_sha = Sha1(source);
|
sha = ParseeSHA1(source);
|
||||||
sha = ShaToHex(raw_sha);
|
|
||||||
|
|
||||||
Free(raw_sha);
|
|
||||||
Free(source);
|
Free(source);
|
||||||
|
|
||||||
return sha;
|
return sha;
|
||||||
|
|
@ -96,7 +120,6 @@ ComputeHandshake(char *shared, char *stream)
|
||||||
bool
|
bool
|
||||||
XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
{
|
{
|
||||||
/* TODO */
|
|
||||||
XMLexer *sax;
|
XMLexer *sax;
|
||||||
XMLEvent *ev;
|
XMLEvent *ev;
|
||||||
char *stream_id, *handshake;
|
char *stream_id, *handshake;
|
||||||
|
|
@ -129,7 +152,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ev->type != XML_LEXER_STARTELEM ||
|
if (!ev || ev->type != XML_LEXER_STARTELEM ||
|
||||||
!StrEquals(ev->element, "stream:stream"))
|
!StrEquals(ev->element, "stream:stream"))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Excepted stream:stream element.");
|
Log(LOG_ERR, "Excepted stream:stream element.");
|
||||||
|
|
@ -140,7 +163,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
stream_id = StrDuplicate(HashMapGet(ev->attrs, "id"));
|
stream_id = StrDuplicate(HashMapGet(ev->attrs, "id"));
|
||||||
handshake = ComputeHandshake(shared, stream_id);
|
handshake = ComputeHandshake(shared, stream_id);
|
||||||
|
|
||||||
Log(LOG_NOTICE, "- sID='%s'", stream_id);
|
Log(LOG_DEBUG, "- sID='%s'", stream_id);
|
||||||
StreamPrintf(stream, "<handshake>%s</handshake>", handshake);
|
StreamPrintf(stream, "<handshake>%s</handshake>", handshake);
|
||||||
StreamFlush(stream);
|
StreamFlush(stream);
|
||||||
XMLFreeEvent(ev);
|
XMLFreeEvent(ev);
|
||||||
|
|
@ -154,11 +177,54 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ev->type != XML_LEXER_ELEM ||
|
if (!ev || ev->type != XML_LEXER_ELEM ||
|
||||||
!StrEquals(ev->element, "handshake"))
|
!StrEquals(ev->element, "handshake"))
|
||||||
{
|
{
|
||||||
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);
|
||||||
|
|
@ -169,7 +235,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
/* We can uhh, send stanzas, and receive them! */
|
/* We can uhh, send stanzas, and receive them! */
|
||||||
Log(LOG_INFO, "Communications to '%s' established.", as);
|
Log(LOG_DEBUG, "Communications to '%s' established.", as);
|
||||||
|
|
||||||
XMLFreeEvent(ev);
|
XMLFreeEvent(ev);
|
||||||
Free(stream_id);
|
Free(stream_id);
|
||||||
|
|
@ -199,8 +265,43 @@ XMPPEndCompStream(XMPPComponent *comp)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pthread_mutex_destroy(&comp->write_lock);
|
pthread_mutex_lock(&comp->write_lock);
|
||||||
StreamClose(comp->stream);
|
StreamClose(comp->stream);
|
||||||
|
comp->stream = NULL;
|
||||||
|
pthread_mutex_unlock(&comp->write_lock);
|
||||||
|
pthread_mutex_destroy(&comp->write_lock);
|
||||||
Free(comp->host);
|
Free(comp->host);
|
||||||
Free(comp);
|
Free(comp);
|
||||||
}
|
}
|
||||||
|
void
|
||||||
|
XMPPSendStanza(XMPPComponent *comp, XMLElement *stanza, size_t max)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
char *c = NULL;
|
||||||
|
Stream *stringWriter;
|
||||||
|
if (!comp || !stanza)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stringWriter = StrStreamWriter(&c);
|
||||||
|
XMLEncode(stringWriter, stanza);
|
||||||
|
StreamFlush(stringWriter);
|
||||||
|
StreamClose(stringWriter);
|
||||||
|
if (c && max && (len = strlen(c)) > max)
|
||||||
|
{
|
||||||
|
Log(LOG_WARNING,
|
||||||
|
"Unexceptedly large stanza received (len=%d max=%d).",
|
||||||
|
(int) len, (int) max
|
||||||
|
);
|
||||||
|
Free(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pthread_mutex_lock(&comp->write_lock);
|
||||||
|
StreamPrintf(comp->stream, "%s", c);
|
||||||
|
StreamFlush(comp->stream);
|
||||||
|
pthread_mutex_unlock(&comp->write_lock);
|
||||||
|
Free(c);
|
||||||
|
}
|
||||||
|
|
|
||||||
136
src/XMPP/MUC.c
136
src/XMPP/MUC.c
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
@ -18,8 +20,6 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
|
||||||
|
|
||||||
iq_query = XMLCreateTag("iq");
|
iq_query = XMLCreateTag("iq");
|
||||||
query = XMLCreateTag("query");
|
query = XMLCreateTag("query");
|
||||||
|
|
||||||
|
|
@ -35,8 +35,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
|
|
||||||
{
|
{
|
||||||
XMLElement *identity;
|
XMLElement *identity;
|
||||||
XMLEncode(jabber->stream, iq_query);
|
XMPPSendStanza(jabber, iq_query, 10000);
|
||||||
StreamFlush(jabber->stream);
|
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
|
|
||||||
/* Except an IQ reply. 10 seconds of timeout is pretty
|
/* Except an IQ reply. 10 seconds of timeout is pretty
|
||||||
|
|
@ -45,8 +44,8 @@ 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);
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
query = XMLookForUnique(iq_query, "query");
|
query = XMLookForUnique(iq_query, "query");
|
||||||
|
|
@ -57,7 +56,11 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
"conference"))
|
"conference"))
|
||||||
{
|
{
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
Log(LOG_DEBUG, "MUC INFO ERROR");
|
||||||
|
Log(LOG_DEBUG,
|
||||||
|
"identityp=%p category=%s", identity,
|
||||||
|
identity ? HashMapGet(identity->attrs, "category") : NULL
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +76,6 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -116,7 +118,6 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
|
||||||
stanza = XMLCreateTag("message");
|
stanza = XMLCreateTag("message");
|
||||||
XMLAddAttr(stanza, "id", (identifier = StrRandom(32)));
|
XMLAddAttr(stanza, "id", (identifier = StrRandom(32)));
|
||||||
XMLAddAttr(stanza, "from", from);
|
XMLAddAttr(stanza, "from", from);
|
||||||
|
|
@ -160,10 +161,123 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
|
||||||
}
|
}
|
||||||
XMLAddChild(stanza, x);
|
XMLAddChild(stanza, x);
|
||||||
}
|
}
|
||||||
XMLEncode(jabber->stream, stanza);
|
XMPPSendStanza(jabber, stanza, 10000);
|
||||||
StreamFlush(jabber->stream);
|
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
|
|
||||||
XMLFreeElement(stanza);
|
XMLFreeElement(stanza);
|
||||||
Free(identifier);
|
Free(identifier);
|
||||||
}
|
}
|
||||||
|
bool
|
||||||
|
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret)
|
||||||
|
{
|
||||||
|
XMLElement *presence, *x, *reply, *history, *photo;
|
||||||
|
IQFeatures *features;
|
||||||
|
char *from, *id, *stime = "3600";
|
||||||
|
if (!comp || !fr || !muc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
presence = XMLCreateTag("presence");
|
||||||
|
x = XMLCreateTag("x");
|
||||||
|
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||||
|
XMLAddAttr(presence, "to", muc);
|
||||||
|
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||||
|
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
||||||
|
history = XMLCreateTag("history");
|
||||||
|
|
||||||
|
if (time > 0)
|
||||||
|
{
|
||||||
|
stime = StrInt(time);
|
||||||
|
}
|
||||||
|
XMLAddAttr(history, "seconds", stime);
|
||||||
|
if (time > 0)
|
||||||
|
{
|
||||||
|
Free(stime);
|
||||||
|
stime = NULL;
|
||||||
|
}
|
||||||
|
XMLAddChild(x, history);
|
||||||
|
|
||||||
|
XMLAddChild(presence, x);
|
||||||
|
features = LookupJIDFeatures(from);
|
||||||
|
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||||
|
IQ_IDENTITY
|
||||||
|
#undef IdentitySimple
|
||||||
|
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||||
|
IQ_ADVERT
|
||||||
|
#undef AdvertiseSimple
|
||||||
|
XMPPAnnotatePresence(presence, features);
|
||||||
|
FreeIQFeatures(features);
|
||||||
|
|
||||||
|
if (hash)
|
||||||
|
{
|
||||||
|
x = XMLCreateTag("x");
|
||||||
|
XMLAddAttr(x, "xmlns", "vcard-temp:x:update");
|
||||||
|
photo = XMLCreateTag("photo");
|
||||||
|
XMLAddChild(photo, XMLCreateText(hash));
|
||||||
|
XMLAddChild(x, photo);
|
||||||
|
XMLAddChild(presence, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
XMPPSendStanza(comp, presence, 10000);
|
||||||
|
XMLFreeElement(presence);
|
||||||
|
Free(from);
|
||||||
|
|
||||||
|
if (ret && (reply = ParseeAwaitStanza(id, 500)))
|
||||||
|
{
|
||||||
|
bool exit_code = true;
|
||||||
|
|
||||||
|
if (XMPPHasError(reply, "conflict"))
|
||||||
|
{
|
||||||
|
exit_code = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLFreeElement(reply);
|
||||||
|
Free(id);
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
Free(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||||
|
{
|
||||||
|
XMLElement *presence;
|
||||||
|
IQFeatures *features;
|
||||||
|
char *from, *id;
|
||||||
|
if (!comp || !fr || !muc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
presence = XMLCreateTag("presence");
|
||||||
|
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||||
|
XMLAddAttr(presence, "to", muc);
|
||||||
|
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||||
|
XMLAddAttr(presence, "type", "unavailable");
|
||||||
|
|
||||||
|
if (reason)
|
||||||
|
{
|
||||||
|
XMLElement *status = XMLCreateTag("status");
|
||||||
|
XMLElement *string = XMLCreateText(reason);
|
||||||
|
|
||||||
|
XMLAddChild(status, string);
|
||||||
|
XMLAddChild(presence, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
features = LookupJIDFeatures(from);
|
||||||
|
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||||
|
IQ_IDENTITY
|
||||||
|
#undef IdentitySimple
|
||||||
|
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||||
|
IQ_ADVERT
|
||||||
|
#undef AdvertiseSimple
|
||||||
|
XMPPAnnotatePresence(presence, features);
|
||||||
|
FreeIQFeatures(features);
|
||||||
|
|
||||||
|
|
||||||
|
XMPPSendStanza(comp, presence, 10000);
|
||||||
|
|
||||||
|
XMLFreeElement(presence);
|
||||||
|
Free(from);
|
||||||
|
Free(id);
|
||||||
|
}
|
||||||
|
|
|
||||||
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)
|
||||||
|
|
@ -66,103 +68,13 @@ XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&comp->write_lock);
|
XMPPSendStanza(comp, message, 10000);
|
||||||
XMLEncode(comp->stream, message);
|
|
||||||
StreamFlush(comp->stream);
|
|
||||||
XMLFreeElement(message);
|
XMLFreeElement(message);
|
||||||
|
|
||||||
pthread_mutex_unlock(&comp->write_lock);
|
|
||||||
|
|
||||||
Free(from);
|
Free(from);
|
||||||
Free(ident);
|
Free(ident);
|
||||||
}
|
}
|
||||||
void
|
|
||||||
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
|
||||||
{
|
|
||||||
XMLElement *presence;
|
|
||||||
char *from, *id;
|
|
||||||
if (!comp || !fr || !muc)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&comp->write_lock);
|
|
||||||
|
|
||||||
presence = XMLCreateTag("presence");
|
|
||||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
||||||
XMLAddAttr(presence, "to", muc);
|
|
||||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
|
||||||
XMLAddAttr(presence, "type", "unavailable");
|
|
||||||
|
|
||||||
if (reason)
|
|
||||||
{
|
|
||||||
XMLElement *status = XMLCreateTag("status");
|
|
||||||
XMLElement *string = XMLCreateText(reason);
|
|
||||||
|
|
||||||
XMLAddChild(status, string);
|
|
||||||
XMLAddChild(presence, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
XMPPAnnotatePresence(presence);
|
|
||||||
|
|
||||||
XMLEncode(comp->stream, presence);
|
|
||||||
StreamFlush(comp->stream);
|
|
||||||
|
|
||||||
XMLFreeElement(presence);
|
|
||||||
Free(from);
|
|
||||||
Free(id);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&comp->write_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
|
|
||||||
{
|
|
||||||
XMLElement *presence, *x, *reply;
|
|
||||||
char *from, *id;
|
|
||||||
if (!comp || !fr || !muc)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&comp->write_lock);
|
|
||||||
|
|
||||||
presence = XMLCreateTag("presence");
|
|
||||||
x = XMLCreateTag("x");
|
|
||||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
||||||
XMLAddAttr(presence, "to", muc);
|
|
||||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
|
||||||
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
|
||||||
|
|
||||||
XMLAddChild(presence, x);
|
|
||||||
XMPPAnnotatePresence(presence);
|
|
||||||
|
|
||||||
XMLEncode(comp->stream, presence);
|
|
||||||
StreamFlush(comp->stream);
|
|
||||||
|
|
||||||
XMLFreeElement(presence);
|
|
||||||
Free(from);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&comp->write_lock);
|
|
||||||
|
|
||||||
/*if ((reply = ParseeAwaitStanza(id, 500)))
|
|
||||||
{
|
|
||||||
bool exit_code = true;
|
|
||||||
|
|
||||||
if (XMPPHasError(reply, "conflict"))
|
|
||||||
{
|
|
||||||
Log(LOG_WARNING, "UNIMPLEMENTED NAMING CONFLICT.");
|
|
||||||
exit_code = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
XMLFreeElement(reply);
|
|
||||||
Free(id);
|
|
||||||
return exit_code;
|
|
||||||
}*/
|
|
||||||
(void) reply;
|
|
||||||
Free(id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool
|
bool
|
||||||
XMPPIsParseeStanza(XMLElement *stanza)
|
XMPPIsParseeStanza(XMLElement *stanza)
|
||||||
{
|
{
|
||||||
|
|
@ -171,6 +83,8 @@ XMPPIsParseeStanza(XMLElement *stanza)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Check if the user is a trustworthy Parsee puppet instead of some
|
||||||
|
* guy sending random stanzas */
|
||||||
return !!XMLookForUnique(stanza, "x-parsee");
|
return !!XMLookForUnique(stanza, "x-parsee");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,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)
|
||||||
{
|
{
|
||||||
|
|
@ -321,35 +239,51 @@ XMPPHasError(XMLElement *stanza, char *type)
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLElement *
|
XMLElement *
|
||||||
XMPPSendDisco(XMPPComponent *jabber, char *from, char *to)
|
XMPPSendDisco(ParseeData *data, char *from, char *to)
|
||||||
{
|
{
|
||||||
|
XMPPComponent *jabber = data ? data->jabber : NULL;
|
||||||
XMLElement *ret, *iq;
|
XMLElement *ret, *iq;
|
||||||
char *identifier;
|
char *identifier;
|
||||||
|
|
||||||
if (!jabber || !from || !to)
|
if (!data || !jabber || !from || !to)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&data->oidl);
|
||||||
|
if ((ret = HashMapGet(data->oid_servers, to)))
|
||||||
|
{
|
||||||
|
ret = XMLCopy(ret);
|
||||||
|
pthread_mutex_unlock(&data->oidl);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&data->oidl);
|
||||||
|
|
||||||
iq = XMLCreateTag("iq");
|
iq = XMLCreateTag("iq");
|
||||||
XMLAddAttr(iq, "type", "get");
|
XMLAddAttr(iq, "type", "get");
|
||||||
XMLAddAttr(iq, "from", from);
|
XMLAddAttr(iq, "from", from);
|
||||||
XMLAddAttr(iq, "to", to);
|
XMLAddAttr(iq, "to", to);
|
||||||
XMLAddAttr(iq, "id", (identifier = StrRandom(69)));
|
XMLAddAttr(iq, "id", (identifier = StrRandom(64)));
|
||||||
{
|
{
|
||||||
XMLElement *query = XMLCreateTag("query");
|
XMLElement *query = XMLCreateTag("query");
|
||||||
XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info");
|
XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info");
|
||||||
XMLAddChild(iq, query);
|
XMLAddChild(iq, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
XMPPSendStanza(jabber, iq, 10000);
|
||||||
XMLEncode(jabber->stream, iq);
|
|
||||||
StreamFlush(jabber->stream);
|
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
XMLFreeElement(iq);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
ret = ParseeAwaitStanza(identifier, 1.25 SECONDS);
|
ret = ParseeAwaitStanza(identifier, 1.25 SECONDS);
|
||||||
Free(identifier);
|
Free(identifier);
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
/* TODO: On my own instance, disco replies are _really_ expensive.
|
||||||
|
* About 120KB. This is sad. Really sad. */
|
||||||
|
pthread_mutex_lock(&data->oidl);
|
||||||
|
XMLFreeElement(HashMapSet(data->oid_servers, to, ret));
|
||||||
|
ret = XMLCopy(ret);
|
||||||
|
pthread_mutex_unlock(&data->oidl);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,17 @@ struct XMPPCommandManager {
|
||||||
HashMap *sessions;
|
HashMap *sessions;
|
||||||
|
|
||||||
void *cookie;
|
void *cookie;
|
||||||
|
|
||||||
|
XMPPCmdFilter filter;
|
||||||
};
|
};
|
||||||
|
static bool
|
||||||
|
XMPPDefaultFilter(XMPPCommandManager *manager, char *id, XMLElement *stanza)
|
||||||
|
{
|
||||||
|
(void) manager;
|
||||||
|
(void) stanza;
|
||||||
|
(void) id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
static void
|
static void
|
||||||
XMPPDestroySession(XMPPSession *session)
|
XMPPDestroySession(XMPPSession *session)
|
||||||
{
|
{
|
||||||
|
|
@ -118,6 +128,7 @@ XMPPCreateManager(void *cookie)
|
||||||
ret->commands = HashMapCreate();
|
ret->commands = HashMapCreate();
|
||||||
ret->sessions = HashMapCreate();
|
ret->sessions = HashMapCreate();
|
||||||
ret->cookie = cookie;
|
ret->cookie = cookie;
|
||||||
|
ret->filter = XMPPDefaultFilter;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -161,18 +172,20 @@ XMPPFreeManager(XMPPCommandManager *manager)
|
||||||
Free(manager);
|
Free(manager);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
|
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s)
|
||||||
{
|
{
|
||||||
char *node_name;
|
char *node_name;
|
||||||
XMPPCommand *val;
|
XMPPCommand *val;
|
||||||
XMLElement *item;
|
XMLElement *item;
|
||||||
if (!m || !p || !jid)
|
if (!m || !p || !jid || !s)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&m->lock);
|
pthread_mutex_lock(&m->lock);
|
||||||
while (HashMapIterate(m->commands, &node_name, (void **) &val))
|
while (HashMapIterate(m->commands, &node_name, (void **) &val))
|
||||||
|
{
|
||||||
|
if (m->filter(m, node_name, s))
|
||||||
{
|
{
|
||||||
item = XMLCreateTag("item");
|
item = XMLCreateTag("item");
|
||||||
XMLAddAttr(item, "jid", jid);
|
XMLAddAttr(item, "jid", jid);
|
||||||
|
|
@ -180,6 +193,7 @@ XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
|
||||||
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
|
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
|
||||||
XMLAddChild(p, item);
|
XMLAddChild(p, item);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
pthread_mutex_unlock(&m->lock);
|
pthread_mutex_unlock(&m->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,6 +220,16 @@ XMPPVerifySession(XMPPCommandManager *mgr, char *s_id, char *from, char *to)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter)
|
||||||
|
{
|
||||||
|
if (!manager || !filter)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
manager->filter = filter;
|
||||||
|
}
|
||||||
bool
|
bool
|
||||||
XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
{
|
{
|
||||||
|
|
@ -242,7 +266,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
/* This is an execution. */
|
/* This is an execution. */
|
||||||
cmd = HashMapGet(m->commands, node);
|
cmd = HashMapGet(m->commands, node);
|
||||||
|
|
||||||
if (!cmd)
|
if (!cmd || !m->filter(m, node, stanza))
|
||||||
{
|
{
|
||||||
/* TODO: Set an error note */
|
/* TODO: Set an error note */
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -274,10 +298,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
x = XMPPFormifyCommand(m, cmd, from);
|
x = XMPPFormifyCommand(m, cmd, from);
|
||||||
XMLAddChild(command_xml, x);
|
XMLAddChild(command_xml, x);
|
||||||
|
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||||
XMLEncode(jabber->stream, iq);
|
|
||||||
StreamFlush(jabber->stream);
|
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
XMLFreeElement(iq);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -305,10 +326,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
XMPPExecuteCommand(m, cmd, from, command_xml, NULL);
|
XMPPExecuteCommand(m, cmd, from, command_xml, NULL);
|
||||||
XMLAddChild(iq, command_xml);
|
XMLAddChild(iq, command_xml);
|
||||||
|
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||||
XMLEncode(jabber->stream, iq);
|
|
||||||
StreamFlush(jabber->stream);
|
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
XMLFreeElement(iq);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
InvalidateSession(m, session_id);
|
InvalidateSession(m, session_id);
|
||||||
|
|
@ -348,10 +366,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
XMPPExecuteCommand(m, cmd, from, command_xml, x_form);
|
XMPPExecuteCommand(m, cmd, from, command_xml, x_form);
|
||||||
XMLAddChild(iq, command_xml);
|
XMLAddChild(iq, command_xml);
|
||||||
|
|
||||||
pthread_mutex_lock(&jabber->write_lock);
|
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||||
XMLEncode(jabber->stream, iq);
|
|
||||||
StreamFlush(jabber->stream);
|
|
||||||
pthread_mutex_unlock(&jabber->write_lock);
|
|
||||||
XMLFreeElement(iq);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
InvalidateSession(m, session_given);
|
InvalidateSession(m, session_given);
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,6 @@ AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement
|
||||||
|
|
||||||
ref = DbLock(data->db, 1, "admins");
|
ref = DbLock(data->db, 1, "admins");
|
||||||
admins = GrabArray(DbJson(ref), 1, "admins");
|
admins = GrabArray(DbJson(ref), 1, "admins");
|
||||||
|
|
||||||
ArrayAdd(admins, JsonValueString(glob));
|
ArrayAdd(admins, JsonValueString(glob));
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,11 @@ void
|
||||||
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
{
|
{
|
||||||
ParseeData *data = XMPPGetManagerCookie(m);
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
char *trimmed = ParseeTrimJID(from);
|
|
||||||
size_t i;
|
size_t i;
|
||||||
XMLElement *x;
|
XMLElement *x;
|
||||||
XMLElement *title;
|
XMLElement *title;
|
||||||
XMLElement *reported, *item, *field, *value, *txt;
|
XMLElement *reported, *item, *field, *value, *txt;
|
||||||
|
|
||||||
if (!ParseeIsAdmin(data, trimmed))
|
|
||||||
{
|
|
||||||
SetNote("error", "User is not authorised to execute command.");
|
|
||||||
Free(trimmed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Free(trimmed);
|
|
||||||
x = XMLCreateTag("x");
|
x = XMLCreateTag("x");
|
||||||
title = XMLCreateTag("title");
|
title = XMLCreateTag("title");
|
||||||
|
|
||||||
|
|
@ -58,4 +50,7 @@ AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
XMLAddChild(out, x);
|
XMLAddChild(out, x);
|
||||||
|
|
||||||
|
(void) form;
|
||||||
|
(void) from;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,4 +29,6 @@ CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
|
||||||
ParseeCleanup(data);
|
ParseeCleanup(data);
|
||||||
/* TODO: Cleanup old sessions? */
|
/* TODO: Cleanup old sessions? */
|
||||||
SetNote("info", "Parsee data was sucessfully cleant up.");
|
SetNote("info", "Parsee data was sucessfully cleant up.");
|
||||||
|
|
||||||
|
(void) form;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
||||||
DbRef *admins;
|
DbRef *admins;
|
||||||
Array *admin_list;
|
Array *admin_list;
|
||||||
size_t i;
|
size_t i;
|
||||||
XMPPOption *admin_opt;
|
XMPPOption *admin_opt = NULL;
|
||||||
char *trimmed = ParseeTrimJID(from);
|
char *trimmed = ParseeTrimJID(from);
|
||||||
|
|
||||||
if (!ParseeIsAdmin(data, trimmed))
|
if (!ParseeIsAdmin(data, trimmed))
|
||||||
|
|
@ -104,14 +104,17 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
||||||
}
|
}
|
||||||
Free(trimmed);
|
Free(trimmed);
|
||||||
|
|
||||||
admin_opt = XMPPCreateList(true, false, "glob", "[NVM!]");
|
|
||||||
|
|
||||||
admins = DbLock(data->db, 1, "admins");
|
admins = DbLock(data->db, 1, "admins");
|
||||||
admin_list = GrabArray(DbJson(admins), 1, "admins");
|
admin_list = GrabArray(DbJson(admins), 1, "admins");
|
||||||
for (i = 0; i < ArraySize(admin_list); i++)
|
for (i = 0; i < ArraySize(admin_list); i++)
|
||||||
{
|
{
|
||||||
char *glob = JsonValueAsString(ArrayGet(admin_list, i));
|
char *glob = JsonValueAsString(ArrayGet(admin_list, i));
|
||||||
|
|
||||||
|
if (!admin_opt)
|
||||||
|
{
|
||||||
|
admin_opt = XMPPCreateList(true, false, "glob", glob);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
XMPPAddListOption(admin_opt, glob);
|
XMPPAddListOption(admin_opt, glob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
49
src/XMPPCommands/MUCInformation.c
Normal file
49
src/XMPPCommands/MUCInformation.c
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Util.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <XMPPFormTool.h>
|
||||||
|
#include <XMPPCommand.h>
|
||||||
|
#include <Parsee.h>
|
||||||
|
#include <XMPP.h>
|
||||||
|
#include <XML.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
MUCInformationID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *muc = ParseeTrimJID(from);
|
||||||
|
char *chat_id = ParseeGetFromMUCID(data, muc);
|
||||||
|
char *room_id = ParseeGetRoomID(data, chat_id);
|
||||||
|
char *msg = StrConcat(5,
|
||||||
|
"The MUC ", muc, " is bridged to ", room_id, "."
|
||||||
|
);
|
||||||
|
|
||||||
|
SetNote("info", msg);
|
||||||
|
|
||||||
|
Free(muc);
|
||||||
|
Free(msg);
|
||||||
|
Free(room_id);
|
||||||
|
Free(chat_id);
|
||||||
|
(void) form;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
MUCInformationCID(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *muc = ParseeTrimJID(from);
|
||||||
|
char *chat_id = ParseeGetFromMUCID(data, muc);
|
||||||
|
char *msg = StrConcat(5,
|
||||||
|
"The MUC ", muc, "'s internal ID is ", chat_id, "."
|
||||||
|
);
|
||||||
|
|
||||||
|
SetNote("info", msg);
|
||||||
|
|
||||||
|
Free(muc);
|
||||||
|
Free(msg);
|
||||||
|
Free(chat_id);
|
||||||
|
(void) form;
|
||||||
|
}
|
||||||
117
src/XMPPCommands/MUCKV.c
Normal file
117
src/XMPPCommands/MUCKV.c
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Util.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <XMPPFormTool.h>
|
||||||
|
#include <XMPPCommand.h>
|
||||||
|
#include <Matrix.h>
|
||||||
|
#include <Parsee.h>
|
||||||
|
#include <XMPP.h>
|
||||||
|
#include <XML.h>
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
MUCSetKey(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *affiliation = NULL, *role = NULL;
|
||||||
|
char *chat_id = NULL;
|
||||||
|
char *muc = NULL;
|
||||||
|
|
||||||
|
char *key = NULL, *val = NULL;
|
||||||
|
|
||||||
|
ParseeLookupAffiliation(from, &affiliation, &role);
|
||||||
|
Free(role);
|
||||||
|
if (!StrEquals(affiliation, "owner"))
|
||||||
|
{
|
||||||
|
SetNote("error", "Setting MUC properties requires the 'owner' affiliation.");
|
||||||
|
Free(affiliation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetFieldValue(key, "key", form);
|
||||||
|
GetFieldValue(val, "val", form);
|
||||||
|
if (!key || !val)
|
||||||
|
{
|
||||||
|
SetNote("error", "No keys or no value given.");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
muc = ParseeTrimJID(from);
|
||||||
|
chat_id = ParseeGetFromMUCID(data, muc);
|
||||||
|
ParseeSetChatSetting(data, chat_id, key, val);
|
||||||
|
|
||||||
|
SetNote("info", "Set key-value pair!");
|
||||||
|
end:
|
||||||
|
Free(affiliation);
|
||||||
|
Free(chat_id);
|
||||||
|
Free(muc);
|
||||||
|
(void) form;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
MUCGetKeys(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *affiliation = NULL, *role = NULL;
|
||||||
|
char *chat_id = NULL;
|
||||||
|
char *muc = NULL;
|
||||||
|
|
||||||
|
XMLElement *x;
|
||||||
|
XMLElement *title;
|
||||||
|
XMLElement *reported, *item, *field, *value, *txt;
|
||||||
|
|
||||||
|
HashMap *settings = NULL;
|
||||||
|
|
||||||
|
ParseeLookupAffiliation(from, &affiliation, &role);
|
||||||
|
Free(role);
|
||||||
|
if (!StrEquals(affiliation, "owner"))
|
||||||
|
{
|
||||||
|
SetNote("error", "Getting MUC roperties requires the 'owner' affiliation.");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
muc = ParseeTrimJID(from);
|
||||||
|
chat_id = ParseeGetFromMUCID(data, muc);
|
||||||
|
settings = ParseeGetChatSettings(data, chat_id);
|
||||||
|
|
||||||
|
x = XMLCreateTag("x");
|
||||||
|
title = XMLCreateTag("title");
|
||||||
|
|
||||||
|
SetTitle(x, "MUC/room settings");
|
||||||
|
|
||||||
|
XMLAddChild(x, title);
|
||||||
|
|
||||||
|
XMLAddAttr(x, "xmlns", "jabber:x:data");
|
||||||
|
XMLAddAttr(x, "type", "result");
|
||||||
|
{
|
||||||
|
char *key, *val;
|
||||||
|
reported = XMLCreateTag("reported");
|
||||||
|
XMLAddChild(x, reported);
|
||||||
|
|
||||||
|
/* Report */
|
||||||
|
Report("key", "Setting's key");
|
||||||
|
Report("val", "Setting's value");
|
||||||
|
|
||||||
|
/* Set */
|
||||||
|
while (HashMapIterate(settings, &key, (void **) &val))
|
||||||
|
{
|
||||||
|
BeginItem();
|
||||||
|
SetField("key", key);
|
||||||
|
SetField("val", val);
|
||||||
|
EndItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XMLAddChild(out, x);
|
||||||
|
|
||||||
|
end:
|
||||||
|
ParseeFreeChatSettings(settings);
|
||||||
|
Free(affiliation);
|
||||||
|
Free(chat_id);
|
||||||
|
Free(muc);
|
||||||
|
(void) form;
|
||||||
|
}
|
||||||
67
src/XMPPCommands/MUCUnlink.c
Normal file
67
src/XMPPCommands/MUCUnlink.c
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Util.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <XMPPFormTool.h>
|
||||||
|
#include <XMPPCommand.h>
|
||||||
|
#include <Matrix.h>
|
||||||
|
#include <Parsee.h>
|
||||||
|
#include <XMPP.h>
|
||||||
|
#include <XML.h>
|
||||||
|
#include <AS.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
MUCUnlink(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *affiliation, *role;
|
||||||
|
char *chat_id;
|
||||||
|
char *parsee;
|
||||||
|
char *room;
|
||||||
|
char *muc;
|
||||||
|
|
||||||
|
ParseeLookupAffiliation(from, &affiliation, &role);
|
||||||
|
Free(role);
|
||||||
|
if (!StrEquals(affiliation, "owner"))
|
||||||
|
{
|
||||||
|
SetNote("error", "Unlinking a MUC requires the 'owner' affiliation.");
|
||||||
|
Free(affiliation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
muc = ParseeTrimJID(from);
|
||||||
|
chat_id = ParseeGetFromMUCID(data, muc);
|
||||||
|
room = ParseeGetRoomID(data, chat_id);
|
||||||
|
if (!chat_id)
|
||||||
|
{
|
||||||
|
SetNote("error", "Couldn't fetch chat ID.");
|
||||||
|
Free(muc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ParseeUnlinkRoom(data, chat_id);
|
||||||
|
|
||||||
|
parsee = ParseeMXID(data);
|
||||||
|
Free(ASSend(
|
||||||
|
data->config, room, parsee,
|
||||||
|
"m.room.message",
|
||||||
|
MatrixCreateNotice("This room has been unlinked."),
|
||||||
|
0
|
||||||
|
));
|
||||||
|
ASLeave(data->config, room, parsee);
|
||||||
|
|
||||||
|
XMPPLeaveMUC(data->jabber, "parsee", muc, "Unlinked by MUC admin.");
|
||||||
|
|
||||||
|
/* Setting an error here won't work, as we're communicating through
|
||||||
|
* the MUC, which we *left*. I guess we can try to defer the leave. */
|
||||||
|
SetNote("info", "Unlinked MUC.");
|
||||||
|
|
||||||
|
Free(affiliation);
|
||||||
|
Free(chat_id);
|
||||||
|
Free(parsee);
|
||||||
|
Free(room);
|
||||||
|
Free(muc);
|
||||||
|
(void) form;
|
||||||
|
}
|
||||||
|
|
@ -87,4 +87,6 @@ NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
|
||||||
}
|
}
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(void) form;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@
|
||||||
void
|
void
|
||||||
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
{
|
{
|
||||||
ParseeData *data = XMPPGetManagerCookie(m);
|
|
||||||
char *trimmed = ParseeTrimJID(from);
|
char *trimmed = ParseeTrimJID(from);
|
||||||
size_t alloc = MemoryAllocated();
|
size_t alloc = MemoryAllocated();
|
||||||
size_t kb = alloc >> 10;
|
size_t kb = alloc >> 10;
|
||||||
|
|
@ -26,13 +25,6 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
||||||
XMLElement *x;
|
XMLElement *x;
|
||||||
XMLElement *title, *txt;
|
XMLElement *title, *txt;
|
||||||
|
|
||||||
if (!ParseeIsAdmin(data, trimmed))
|
|
||||||
{
|
|
||||||
SetNote("error", "User is not authorised to execute command.");
|
|
||||||
|
|
||||||
Free(trimmed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Free(trimmed);
|
Free(trimmed);
|
||||||
x = XMLCreateTag("x");
|
x = XMLCreateTag("x");
|
||||||
title = XMLCreateTag("title");
|
title = XMLCreateTag("title");
|
||||||
|
|
@ -75,4 +67,7 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
||||||
EndItem();
|
EndItem();
|
||||||
}
|
}
|
||||||
XMLAddChild(out, x);
|
XMLAddChild(out, x);
|
||||||
|
|
||||||
|
(void) form;
|
||||||
|
(void) m;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
142
src/XMPPCommands/Whitelist.c
Normal file
142
src/XMPPCommands/Whitelist.c
Normal file
|
|
@ -0,0 +1,142 @@
|
||||||
|
#include <Cytoplasm/HashMap.h>
|
||||||
|
#include <Cytoplasm/Memory.h>
|
||||||
|
#include <Cytoplasm/Json.h>
|
||||||
|
#include <Cytoplasm/Util.h>
|
||||||
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
|
#include <XMPPFormTool.h>
|
||||||
|
#include <XMPPCommand.h>
|
||||||
|
#include <Parsee.h>
|
||||||
|
#include <XMPP.h>
|
||||||
|
#include <XML.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *trimmed = ParseeTrimJID(from);
|
||||||
|
|
||||||
|
if (!ParseeIsAdmin(data, trimmed))
|
||||||
|
{
|
||||||
|
SetNote("error", "User is not authorised to execute command.");
|
||||||
|
|
||||||
|
Free(trimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Free(trimmed);
|
||||||
|
|
||||||
|
if (!DbDelete(data->db, 1, "whitelist"))
|
||||||
|
{
|
||||||
|
SetNote("error", "Parsee whitelist was non-existent or could not be removed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* TODO: Cleanup old sessions? */
|
||||||
|
SetNote("info", "Parsee whitelist was removed.");
|
||||||
|
|
||||||
|
(void) form;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
AddWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *trimmed = ParseeTrimJID(from);
|
||||||
|
char *entity = NULL;
|
||||||
|
DbRef *ref;
|
||||||
|
|
||||||
|
GetFieldValue(entity, "entity", form);
|
||||||
|
|
||||||
|
if (!ParseeIsAdmin(data, trimmed))
|
||||||
|
{
|
||||||
|
SetNote("error", "User is not authorised to execute command.");
|
||||||
|
|
||||||
|
Free(trimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!entity)
|
||||||
|
{
|
||||||
|
SetNote("error", "No entity found.");
|
||||||
|
Free(trimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Free(trimmed);
|
||||||
|
|
||||||
|
ref = DbLock(data->db, 1, "whitelist");
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
ref = DbCreate(data->db, 1, "whitelist");
|
||||||
|
}
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
SetNote("error", "Couldn't get a database entry. You're cooked.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JsonValueFree(HashMapSet(
|
||||||
|
DbJson(ref),
|
||||||
|
entity, JsonValueObject(HashMapCreate())
|
||||||
|
));
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
|
||||||
|
SetNote("info", "Server successfully whitelisted.");
|
||||||
|
}
|
||||||
|
void
|
||||||
|
WhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *trimmed = ParseeTrimJID(from);
|
||||||
|
XMLElement *x;
|
||||||
|
XMLElement *title;
|
||||||
|
XMLElement *reported, *item, *field, *value, *txt;
|
||||||
|
|
||||||
|
if (!ParseeIsAdmin(data, trimmed))
|
||||||
|
{
|
||||||
|
SetNote("error", "User is not authorised to execute command.");
|
||||||
|
|
||||||
|
Free(trimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = XMLCreateTag("x");
|
||||||
|
XMLAddAttr(x, "xmlns", "jabber:x:data");
|
||||||
|
title = XMLCreateTag("title");
|
||||||
|
XMLAddChild(x, title);
|
||||||
|
XMLAddChild(out, x);
|
||||||
|
|
||||||
|
Free(trimmed);
|
||||||
|
|
||||||
|
SetTitle(x, NAME " chat whitelist");
|
||||||
|
|
||||||
|
XMLAddAttr(x, "type", "result");
|
||||||
|
{
|
||||||
|
DbRef *ref = DbLock(data->db, 1, "whitelist");
|
||||||
|
HashMap *obj;
|
||||||
|
char *serv;
|
||||||
|
JsonValue *obj_val;
|
||||||
|
reported = XMLCreateTag("reported");
|
||||||
|
XMLAddChild(x, reported);
|
||||||
|
|
||||||
|
if (!ref)
|
||||||
|
{
|
||||||
|
ref = DbCreate(data->db, 1, "global_bans");
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = DbJson(ref);
|
||||||
|
|
||||||
|
/* Report */
|
||||||
|
Report("server", "Allowed servers");
|
||||||
|
|
||||||
|
/* Set */
|
||||||
|
while (HashMapIterate(obj, &serv, (void **) &obj_val))
|
||||||
|
{
|
||||||
|
BeginItem();
|
||||||
|
SetField("server", serv);
|
||||||
|
EndItem();
|
||||||
|
|
||||||
|
(void) obj_val;
|
||||||
|
}
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) form;
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
#include <Cytoplasm/Sha.h>
|
|
||||||
|
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
|
@ -119,13 +118,12 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Cache this information. */
|
|
||||||
bool
|
bool
|
||||||
ServerHasXEP421(ParseeData *data, char *from)
|
ServerHasXEP421(ParseeData *data, char *from)
|
||||||
{
|
{
|
||||||
char *server = NULL, *parsee;
|
char *server = NULL, *postserv, *parsee;
|
||||||
XMLElement *disco;
|
XMLElement *disco;
|
||||||
bool ret;
|
bool ret = false;
|
||||||
if (!data || !from)
|
if (!data || !from)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -140,15 +138,16 @@ ServerHasXEP421(ParseeData *data, char *from)
|
||||||
{
|
{
|
||||||
server++;
|
server++;
|
||||||
}
|
}
|
||||||
server = StrDuplicate(server);
|
|
||||||
|
|
||||||
if (strchr(server, '/'))
|
server = StrDuplicate(server);
|
||||||
|
postserv = server ? strchr(server, '/') : NULL;
|
||||||
|
if (postserv)
|
||||||
{
|
{
|
||||||
*(strchr(server, '/')) = '\0';
|
*postserv = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
parsee = ParseeJID(data);
|
parsee = ParseeJID(data);
|
||||||
disco = XMPPSendDisco(data->jabber, parsee, server);
|
disco = XMPPSendDisco(data, parsee, server);
|
||||||
|
|
||||||
ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0");
|
ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0");
|
||||||
|
|
||||||
|
|
@ -163,10 +162,9 @@ ServerHasXEP421(ParseeData *data, char *from)
|
||||||
* into a SHA-256 value
|
* into a SHA-256 value
|
||||||
* > "The recipient MUST consider the occupant identifier to be an opaque
|
* > "The recipient MUST consider the occupant identifier to be an opaque
|
||||||
* > string.". */
|
* > string.". */
|
||||||
static char *
|
char *
|
||||||
ScrambleOID(ParseeData *data, char *opaque_oid)
|
ScrambleOID(ParseeData *data, char *opaque_oid)
|
||||||
{
|
{
|
||||||
unsigned char *raw;
|
|
||||||
char *sha, *mxid;
|
char *sha, *mxid;
|
||||||
const ParseeConfig *c = data->config;
|
const ParseeConfig *c = data->config;
|
||||||
if (!opaque_oid)
|
if (!opaque_oid)
|
||||||
|
|
@ -175,9 +173,7 @@ ScrambleOID(ParseeData *data, char *opaque_oid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns this into a 128-byte long, Matrix-safe value. */
|
/* Turns this into a 128-byte long, Matrix-safe value. */
|
||||||
raw = Sha256(opaque_oid);
|
sha = ParseeSHA256(opaque_oid);
|
||||||
sha = ShaToHex(raw);
|
|
||||||
Free(raw);
|
|
||||||
|
|
||||||
/* TODO: Either mark this specially, or drop Parsee JID flags
|
/* TODO: Either mark this specially, or drop Parsee JID flags
|
||||||
* altogether before any 1.0.0 */
|
* altogether before any 1.0.0 */
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue