mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 13:45:10 +00:00
Compare commits
193 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 |
114 changed files with 5491 additions and 1658 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'
|
||||
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
|
|
@ -4,8 +4,11 @@ parsee*
|
|||
parsee
|
||||
*.swp
|
||||
.*
|
||||
data
|
||||
data/*
|
||||
data*
|
||||
data*/*
|
||||
Makefile
|
||||
configure
|
||||
gmon.out
|
||||
|
||||
tools/out
|
||||
tools/out/*
|
||||
|
|
@ -19,3 +22,11 @@ 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
|
||||
66
CHANGELOG.md
66
CHANGELOG.md
|
|
@ -6,7 +6,73 @@ 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.
|
||||
|
|
|
|||
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
|
||||
a strict deadline:
|
||||
- ~September 2024[tomboyish-bridges-adventure]:
|
||||
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.
|
||||
- Get star-of-hope out by November 8
|
||||
|
|
|
|||
4
LICENSE
4
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
For the files src/Parsee/HMAC.c, src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile,
|
||||
For the files src/include/Unistring.h, src/Unistr.h rc/Parsee/HMAC.c, src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile,
|
||||
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
|
||||
|
||||
|
|
|
|||
90
Makefile
90
Makefile
|
|
@ -1,90 +0,0 @@
|
|||
# (GNU)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 =============================
|
||||
|
||||
include build.conf
|
||||
|
||||
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
|
||||
|
||||
AYAS=ayaya
|
||||
ETC=etc
|
||||
FCFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" $(CFLAGS)
|
||||
FLDFLAGS=-L $(CYTO_LIB) -lCytoplasm $(LDFLAGS)
|
||||
AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)"
|
||||
# ============================ Compilation =================================
|
||||
SRC_FILES:=$(shell find $(SOURCE) -name '*.c') $(shell find $(ETC)/media -name '*.png')
|
||||
OBJ_FILES:=${subst $(ETC)/media/,$(OBJECT)/,${subst $(SOURCE)/,$(OBJECT)/,$(patsubst %.png, %.o, $(patsubst %.c, %.o, $(SRC_FILES)))}}
|
||||
|
||||
CPP_FILES:=$(shell find $(INCLUDES) -name '*.h')
|
||||
AYA_FILES:=${subst $(INCLUDES)/,$(AYAS)/,$(patsubst %.h, %.html, $(CPP_FILES))}
|
||||
|
||||
all: utils binary
|
||||
|
||||
binary: $(OBJ_FILES)
|
||||
$(CC) $(FLDFLAGS) $(OBJ_FILES) -o $(BINARY)
|
||||
tags: $(SRC_FILES)
|
||||
@ctags --recurse $(SOURCE)/
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJECT) $(BINARY) $(AYAS)
|
||||
|
||||
$(OBJECT)/%.o: $(ETC)/media/%.png
|
||||
@mkdir -p $(shell dirname "$@")
|
||||
@echo "const char media_$(shell basename $< .png)[] =" > $@.c
|
||||
@base64 $< | \
|
||||
sed -e 's/^\(.*\)$$/ "\1"/' | \
|
||||
sed -e '$$ s/^\(.*\)$$/\1;/' >> $@.c
|
||||
$(CC) -c $(FCFLAGS) $@.c -o $@
|
||||
$(OBJECT)/%.o: $(SOURCE)/%.c
|
||||
@mkdir -p $(shell dirname "$@")
|
||||
$(CC) -c $(FCFLAGS) $< -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 "$<" "$@"
|
||||
58
README.MD
58
README.MD
|
|
@ -1,45 +1,55 @@
|
|||
# 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.
|
||||
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?
|
||||
### Naming
|
||||
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi),
|
||||
a "*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)
|
||||
I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister
|
||||
project to KappaChat, this means that I can integrate Parsee with KappaChat however I wish it
|
||||
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*,
|
||||
and maybe as a testing ground for Cytoplasm features I sometimes add.
|
||||
|
||||
Well, I'm *trying* to do that, at least.
|
||||
Please scream at me if that fails(or just doesn't run on a overclocked Raspberry
|
||||
Pi 4B, which, by the way, is literally where Parsee+XMPP is running for now.)
|
||||
(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 XMPP lol"
|
||||
These two having the same answer should be enough information. Also can I *just* have fun?
|
||||
These two having the same answer should be enough information.
|
||||
One could also argue that both sides need to migrate(onboard) the other side, so
|
||||
a bridge may be a good way to start.
|
||||
|
||||
## BUILDING
|
||||
```sh
|
||||
$ make # This generates a 'parsee' executable.
|
||||
$ cd tools # If you want to build more tools
|
||||
$ make && cd ..
|
||||
$ make ayadoc # If you want to build HTML documentation
|
||||
$ make [PREFIX=(install path)] install # To install Parsee.
|
||||
$ cc configure.c -o configure # that or use tcc -run to consolidate these two steps.
|
||||
$ ./configure # use -s if you want static Parsee+MbedTLS, use -s -l if LMDB is needed
|
||||
$ make
|
||||
$ make [PREFIX=...] install # run as root if on a protected dir like /usr
|
||||
```
|
||||
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` for Cytoplasm's include and
|
||||
library paths specifically.)
|
||||
If you build with MbedTLS, please mind setting the `CYTO_TLS_CA` env to Parsee.
|
||||
|
||||
### 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 if you're adventurous).
|
||||
|
||||
### BUILDING WITH GUIX
|
||||
If you have [Guix](https://guix.gnu.org/) installed, you can build Parsee using `guix package -f guix.scm`, or test it
|
||||
using `guix shell -f guix.scm`. You can also generate a Docker/OCI image.
|
||||
```sh
|
||||
guix pack -f docker -S /bin=bin -L.guix/modules parsee
|
||||
```
|
||||
|
||||
## RUNNING
|
||||
First off, you may want to configure Parsee by running the `config` tool(generally named
|
||||
`parsee-config` in most cases), with the correct flags, like here.
|
||||
|
|
@ -51,7 +61,8 @@ parsee-config \
|
|||
-H 'blow.hole' \ # Matrix homeserver name
|
||||
-J 'parsee.blow.hole' \ # XMPP component host, must be reachable
|
||||
-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.
|
||||
|
|
@ -64,33 +75,44 @@ returns with a landing page, then this side works. You can read it for some more
|
|||
|
||||
## DOCS
|
||||
Currently, the main sources of documentation are the Ayadocs(for headers) and the manpages
|
||||
(see `etc/man`)
|
||||
(see `etc/man`).
|
||||
|
||||
## TODOS before 1.0 rolls around
|
||||
- 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
|
||||
dependency list of Parsee for 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
|
||||
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
|
||||
('$'?) for escaped?
|
||||
- PROPER FUCKING AVATARS
|
||||
XMPP->Matrix is decent, Matrix->XMPP is effectiveny not done
|
||||
- Consider making room/MUC admins/owners be able to plumb instead of it being
|
||||
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
|
||||
only if Parsee admins are good-willed, which we must assume such statment to
|
||||
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
|
||||
- Add a MUC server to Parsee, such that it may be able to hook onto it and therefore
|
||||
support XMPP->Matrix bridging.
|
||||
- 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.
|
||||
- Deadlocks. It's always deadlocks.
|
||||
- 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
|
||||
If you know things about XMPP or Matrix, yet aren't familiar with C99, or just
|
||||
|
|
@ -107,7 +129,7 @@ You may also donate to [the LiberaPay](https://en.liberapay.com/Parsee), alongsi
|
|||
currently maintaining Cytoplasm.
|
||||
|
||||
## 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.
|
||||
(Also, these are temporary room aliases.)
|
||||
- [#parsee:tedomum.net](https://matrix.to/#/%23parsee:tedomum.net)
|
||||
|
|
|
|||
15
XEPS-TBD.TXT
15
XEPS-TBD.TXT
|
|
@ -21,10 +21,14 @@ Somewhat implemented XEPs:
|
|||
This allows reactions, which Matrix also has support to. The two
|
||||
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
|
||||
HALF-IMPLEMENTED: Removing reacts won't work.
|
||||
TODO: Add support from Matrix.
|
||||
~ https://xmpp.org/extensions/xep-0184.html
|
||||
Only Matrix->XMPP as of now. Requesting data from Matrix ASes without
|
||||
/sync seems like a non-option as of now, which _sucks_.
|
||||
~ 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
|
||||
|
||||
For future XEPs:
|
||||
- https://xmpp.org/extensions/xep-0449.html
|
||||
|
|
@ -34,12 +38,7 @@ For future XEPs:
|
|||
which is used along PEP, it seems, and meanwhile Matrix has ''support''
|
||||
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
|
||||
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
|
||||
On Standby:
|
||||
unreliable, however.
|
||||
x https://xmpp.org/extensions/xep-0080.html
|
||||
Can't think of a good analogy to these...
|
||||
|
|
@ -48,7 +47,7 @@ THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT:
|
|||
Not XEPs, but ideas that _needs_ to be added:
|
||||
~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d
|
||||
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).
|
||||
~ 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.)
|
||||
- https://www.youtube.com/watch?v=InL414iDZmY
|
||||
|
||||
|
|
|
|||
863
build.c
863
build.c
|
|
@ -1,863 +0,0 @@
|
|||
/* build.c - Simple, POSIX, non-Cytoplasm utility to build out
|
||||
* the entirety of Parsee from scratch, without any Makefiles.
|
||||
*
|
||||
* The main reason why this tool exists is merely because the
|
||||
* current Make-based building is not POSIX compliant, and I
|
||||
* am simply not porting it to be. The Makefile shall stay
|
||||
* supported however, but if possible, use build.c.
|
||||
* To run it, just build it with:
|
||||
* cc build.c -o /tmp/build && /tmp/build
|
||||
*
|
||||
* TODO: Parallel jobs, CFLAGS and LDFLAGS.
|
||||
* Note that this bit is formatted differently.
|
||||
* --------------------------------
|
||||
* 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 <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define DEFAULT_BUILD_PATH "build.conf"
|
||||
#define DEFAULT_COMPILER "cc"
|
||||
#define Compiler(info) (info.basic.cc ? info.basic.cc : DEFAULT_COMPILER)
|
||||
|
||||
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;
|
||||
}
|
||||
static char *
|
||||
trim_nl(char *in)
|
||||
{
|
||||
char *tc;
|
||||
if (!in)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((tc = strrchr(in, '\n')))
|
||||
{
|
||||
*tc = '\0';
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
typedef struct str_array {
|
||||
char **values;
|
||||
size_t quantity;
|
||||
} str_array_t;
|
||||
static str_array_t *
|
||||
str_array_create(void)
|
||||
{
|
||||
str_array_t *ret = malloc(sizeof(*ret));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
typedef struct buildinfo {
|
||||
struct basic {
|
||||
char *codename;
|
||||
char *version;
|
||||
|
||||
char *name;
|
||||
|
||||
char *binary;
|
||||
|
||||
char *src;
|
||||
char *inc;
|
||||
char *obj;
|
||||
|
||||
char *cflags, *ldflags;
|
||||
|
||||
char *cc;
|
||||
} basic;
|
||||
|
||||
char *repo;
|
||||
} buildinfo_t;
|
||||
static void
|
||||
destroy_buildinfo(buildinfo_t *info)
|
||||
{
|
||||
if (!info)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#define FreeIfExistent(v) do \
|
||||
{ \
|
||||
if (v) \
|
||||
{ \
|
||||
free(v); \
|
||||
v = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
FreeIfExistent(info->basic.codename);
|
||||
FreeIfExistent(info->basic.version);
|
||||
FreeIfExistent(info->basic.ldflags);
|
||||
FreeIfExistent(info->basic.binary);
|
||||
FreeIfExistent(info->basic.cflags);
|
||||
FreeIfExistent(info->basic.name);
|
||||
FreeIfExistent(info->basic.src);
|
||||
FreeIfExistent(info->basic.inc);
|
||||
FreeIfExistent(info->basic.obj);
|
||||
FreeIfExistent(info->basic.cc);
|
||||
FreeIfExistent(info->repo);
|
||||
}
|
||||
|
||||
static char *
|
||||
cmd_stdout(char *cmd)
|
||||
{
|
||||
FILE *f;
|
||||
char *line = NULL;
|
||||
size_t size;
|
||||
if (!cmd)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!(f = popen(cmd, "r")))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
getline(&line, &size, f);
|
||||
pclose(f);
|
||||
return line;
|
||||
}
|
||||
static int
|
||||
exec_code(char *program, char *argv[])
|
||||
{
|
||||
pid_t forkRet;
|
||||
if (!program || !argv)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
forkRet = fork();
|
||||
if (forkRet == 0)
|
||||
{
|
||||
/* Child */
|
||||
execvp(program, argv);
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Parent */
|
||||
int status;
|
||||
if (waitpid(forkRet, &status, 0) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/* We're not meant to ever be there, but TCC is stupid. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static char *
|
||||
strchrn(char *s, char c)
|
||||
{
|
||||
s = strchr(s, c);
|
||||
return s ? s+1 : NULL;
|
||||
}
|
||||
static char *
|
||||
strchrl(char *s, char c)
|
||||
{
|
||||
char *s1 = NULL;
|
||||
while ((s = strchr(s, c)))
|
||||
{
|
||||
s1 = s;
|
||||
s++;
|
||||
}
|
||||
return s1;
|
||||
}
|
||||
static void
|
||||
mkdir_rec(char *dir)
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
char *start;
|
||||
if (!dir || strlen(dir) >= PATH_MAX - 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memset(tmp, '\0', sizeof(tmp));
|
||||
for (start = dir; start && *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';
|
||||
|
||||
{
|
||||
memcpy(tmp, dir, start - dir);
|
||||
tmp[strlen(tmp) - 1] = '\0';
|
||||
mkdir(tmp, 0770);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 time_t
|
||||
mod_date(char *file)
|
||||
{
|
||||
struct stat s;
|
||||
if (stat(file, &s))
|
||||
{
|
||||
return (time_t) 0;
|
||||
}
|
||||
return s.st_mtime;
|
||||
}
|
||||
static bool
|
||||
build_file(char *cSource, buildinfo_t info, bool isTool)
|
||||
{
|
||||
str_array_t *args, *cflags;
|
||||
char *oFileName, *objPath, *oFile;
|
||||
int ret, i = 0;
|
||||
int srclen;
|
||||
|
||||
char *code, *name, *vers, *repo;
|
||||
if (!cSource)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isTool)
|
||||
{
|
||||
srclen = strncmp(cSource, "src", 3) ?
|
||||
strlen(info.basic.obj) + 1 :
|
||||
strlen(info.basic.src) + 1 ;
|
||||
objPath = string_cat(info.basic.obj, "/");
|
||||
oFile = string_rep_ext(cSource + srclen, ".c", ".o");
|
||||
oFileName = string_cat(objPath, oFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
srclen = 6;
|
||||
objPath = string_dup("tools/out/");
|
||||
oFile = string_rep_ext(cSource + srclen, ".c", "");
|
||||
oFileName = string_cat(objPath, oFile);
|
||||
}
|
||||
mkdir_rec(oFileName);
|
||||
|
||||
if (!isTool && (mod_date(cSource) < mod_date(oFileName)))
|
||||
{
|
||||
free(objPath);
|
||||
free(oFileName);
|
||||
free(oFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
args = str_array_create();
|
||||
if (!isTool)
|
||||
{
|
||||
printf("\tCC %s...\n", cSource);
|
||||
}
|
||||
|
||||
str_array_add(args, Compiler(info));
|
||||
if (isTool)
|
||||
{
|
||||
str_array_add(args, "-lCytoplasm");
|
||||
str_array_add(args, "-I.");
|
||||
}
|
||||
else
|
||||
{
|
||||
str_array_add(args, "-c");
|
||||
}
|
||||
str_array_add(args, cSource);
|
||||
|
||||
cflags = split(info.basic.cflags);
|
||||
for (i = 0; i < str_array_len(cflags); i++)
|
||||
{
|
||||
str_array_add(args, str_array_get(cflags, i));
|
||||
}
|
||||
str_array_free(cflags);
|
||||
|
||||
str_array_add(args, "-o");
|
||||
str_array_add(args, oFileName);
|
||||
|
||||
str_array_add(args, "-I");
|
||||
str_array_add(args, info.basic.inc);
|
||||
str_array_add(args, "-I");
|
||||
str_array_add(args, info.basic.src);
|
||||
|
||||
{
|
||||
char *pre = string_cat("\"", info.basic.version);
|
||||
char *pos = string_cat(pre, "\"");
|
||||
vers = string_cat("-DVERSION=", pos);
|
||||
str_array_add(args, vers);
|
||||
|
||||
free(pos);
|
||||
free(pre);
|
||||
}
|
||||
{
|
||||
char *pre = string_cat("\"", info.basic.name);
|
||||
char *pos = string_cat(pre, "\"");
|
||||
name = string_cat("-DNAME=", pos);
|
||||
str_array_add(args, name);
|
||||
|
||||
free(pos);
|
||||
free(pre);
|
||||
}
|
||||
{
|
||||
char *pre = string_cat("\"", info.basic.codename);
|
||||
char *pos = string_cat(pre, "\"");
|
||||
code = string_cat("-DCODE=", pos);
|
||||
str_array_add(args, code);
|
||||
|
||||
free(pos);
|
||||
free(pre);
|
||||
}
|
||||
{
|
||||
char *pre = string_cat("\"", info.repo);
|
||||
char *pos = string_cat(pre, "\"");
|
||||
repo = string_cat("-DREPOSITORY=", pos);
|
||||
str_array_add(args, repo);
|
||||
|
||||
free(pos);
|
||||
free(pre);
|
||||
}
|
||||
|
||||
str_array_add(args, NULL);
|
||||
|
||||
ret = exec_code(Compiler(info), args->values);
|
||||
|
||||
str_array_free(args);
|
||||
free(objPath);
|
||||
free(oFileName);
|
||||
free(oFile);
|
||||
free(vers);
|
||||
free(name);
|
||||
free(code);
|
||||
free(repo);
|
||||
return ret == EXIT_SUCCESS;
|
||||
}
|
||||
static bool
|
||||
finalise_file(str_array_t *arr, buildinfo_t info)
|
||||
{
|
||||
str_array_t *flags, *ldflags;
|
||||
size_t i;
|
||||
bool ret = true;
|
||||
if (!arr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
flags = str_array_create();
|
||||
str_array_add(flags, Compiler(info));
|
||||
|
||||
ldflags = split(info.basic.cflags);
|
||||
for (i = 0; i < str_array_len(ldflags); i++)
|
||||
{
|
||||
str_array_add(flags, str_array_get(ldflags, i));
|
||||
}
|
||||
str_array_free(ldflags);
|
||||
|
||||
str_array_add(flags, "-lCytoplasm");
|
||||
|
||||
for (i = 0; i < str_array_len(arr); i++)
|
||||
{
|
||||
char *file = str_array_get(arr, i);
|
||||
if (file)
|
||||
{
|
||||
size_t srclen = strncmp(file, "src", 3) ?
|
||||
strlen(info.basic.obj) + 1 :
|
||||
strlen(info.basic.src) + 1 ;
|
||||
char *objPath = string_cat(info.basic.obj, "/");
|
||||
char *oFile = string_rep_ext(file + srclen, ".c", ".o");
|
||||
char *oFileName = string_cat(objPath, oFile);
|
||||
|
||||
str_array_add(flags, oFileName);
|
||||
|
||||
free(oFileName);
|
||||
free(oFile);
|
||||
free(objPath);
|
||||
}
|
||||
}
|
||||
|
||||
str_array_add(flags, "-o");
|
||||
str_array_add(flags, info.basic.binary);
|
||||
str_array_add(flags, NULL);
|
||||
ret = exec_code(Compiler(info), flags->values);
|
||||
|
||||
str_array_free(flags);
|
||||
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 (*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;
|
||||
}
|
||||
if (!strchr(name, '.') &&
|
||||
strcmp(name, "out") &&
|
||||
strcmp(name, "Makefile"))
|
||||
{
|
||||
str_array_t *sub;
|
||||
char *d1 = string_cat(dir, "/");
|
||||
char *d2 = string_cat(d1, name);
|
||||
size_t i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static char *
|
||||
process_png(char *png, buildinfo_t info)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *symbol;
|
||||
char *cFile, *oFile;
|
||||
char *pcFile, *poFile;
|
||||
char *pcFile1, *poFile1;
|
||||
char *filename, *symbol1;
|
||||
char *arguments[8] = { 0 };
|
||||
if (!png)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!(filename = strchrl(png, '/')))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
filename++;
|
||||
|
||||
pcFile1= string_cat(info.basic.obj, "/");
|
||||
pcFile = string_cat(pcFile1, filename);
|
||||
cFile = string_rep_ext(pcFile, ".png", ".c");
|
||||
free(pcFile);
|
||||
free(pcFile1);
|
||||
|
||||
poFile1= string_cat(info.basic.obj, "/");
|
||||
poFile = string_cat(poFile1, filename);
|
||||
oFile = string_rep_ext(poFile, ".png", ".o");
|
||||
free(poFile);
|
||||
free(poFile1);
|
||||
|
||||
symbol1 = string_rep_ext(filename, ".png", "");
|
||||
symbol = string_cat("media_", symbol1);
|
||||
free(symbol1);
|
||||
|
||||
mkdir_rec(oFile);
|
||||
mkdir_rec(cFile);
|
||||
|
||||
/* Build the image into Base64 */
|
||||
arguments[i++] = "tools/out/b64";
|
||||
arguments[i++] = png;
|
||||
arguments[i++] = symbol;
|
||||
arguments[i++] = cFile;
|
||||
arguments[i++] = NULL;
|
||||
|
||||
if (exec_code(arguments[0], arguments))
|
||||
{
|
||||
free(symbol);
|
||||
free(cFile);
|
||||
free(oFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Compile it out */
|
||||
i = 0;
|
||||
arguments[i++] = Compiler(info);
|
||||
arguments[i++] = "-c";
|
||||
arguments[i++] = cFile;
|
||||
arguments[i++] = "-o";
|
||||
arguments[i++] = oFile;
|
||||
arguments[i++] = NULL;
|
||||
|
||||
if (exec_code(arguments[0], arguments))
|
||||
{
|
||||
free(symbol);
|
||||
free(cFile);
|
||||
free(oFile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(symbol);
|
||||
free(oFile);
|
||||
return cFile;
|
||||
}
|
||||
/* Builds the entirety of Parsee. */
|
||||
static int
|
||||
main_build(int argc, char *argv[])
|
||||
{
|
||||
buildinfo_t info = { 0 };
|
||||
FILE *buildinfo = NULL;
|
||||
char *line = NULL;
|
||||
size_t size, i;
|
||||
ssize_t nread;
|
||||
str_array_t *sources, *images;
|
||||
|
||||
/* Step 1: Get all basic information from build.conf */
|
||||
if (!(buildinfo = fopen(DEFAULT_BUILD_PATH, "r")))
|
||||
{
|
||||
printf("error: cannot open '%s'\n", DEFAULT_BUILD_PATH);
|
||||
goto fail;
|
||||
}
|
||||
while ((nread = getline(&line, &size, buildinfo)) != -1)
|
||||
{
|
||||
char *eq = strchr(line, '=');
|
||||
char *end = strchr(line, '\n');
|
||||
|
||||
char *key, *val;
|
||||
if (!eq)
|
||||
{
|
||||
free(line);
|
||||
printf(
|
||||
"error: line in '%s' does not contain '='\n",
|
||||
DEFAULT_BUILD_PATH
|
||||
);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Set delimiters */
|
||||
*eq = '\0';
|
||||
if (end) *end = '\0';
|
||||
|
||||
key = line;
|
||||
val = eq + 1;
|
||||
|
||||
/* Now, we have KV mappings. */
|
||||
#define If(k, v, d) do \
|
||||
{ \
|
||||
if (!strcmp(key, k) && !info.v) \
|
||||
{ \
|
||||
info.v = string_dup(val); \
|
||||
printf("%s: %s\n", d, val); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
If("CODE", basic.codename, "Codename");
|
||||
If("NAME", basic.name, "Name");
|
||||
If("VERSION", basic.version, "Version");
|
||||
If("BINARY", basic.binary, "Binary name");
|
||||
|
||||
If("CFLAGS", basic.cflags, "C compiler arguments");
|
||||
If("LDFLAGS", basic.ldflags, "Linker arguments");
|
||||
|
||||
If("INCLUDES", basic.inc, "Include path");
|
||||
If("SOURCE", basic.src, "Source path");
|
||||
If("OBJECT", basic.obj, "Object path");
|
||||
If("CC", basic.cc, "Compiler");
|
||||
}
|
||||
if (line)
|
||||
{
|
||||
free(line);
|
||||
line = NULL;
|
||||
}
|
||||
|
||||
fclose(buildinfo);
|
||||
buildinfo = NULL;
|
||||
|
||||
/* Step 2: Get all information from commands. */
|
||||
if (!(info.repo = cmd_stdout("git remote get-url origin")))
|
||||
{
|
||||
printf("error: cannot find origins url\n");
|
||||
goto fail;
|
||||
}
|
||||
info.repo = trim_nl(info.repo);
|
||||
|
||||
if (argc >= 2 && !strcmp(argv[1], "clean"))
|
||||
{
|
||||
char *args[8];
|
||||
size_t i;
|
||||
unlink(info.basic.binary);
|
||||
|
||||
args[i++] = "rm";
|
||||
args[i++] = "-r";
|
||||
args[i++] = info.basic.obj;
|
||||
args[i++] = NULL;
|
||||
exec_code(args[0], args);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
||||
/* Step 3: Build all utilities. */
|
||||
sources = collect_sources("tools", true, ".c");
|
||||
for (i = 0; i < str_array_len(sources); i++)
|
||||
{
|
||||
char *file = str_array_get(sources, i);
|
||||
printf("\tTOOL %s...\n", file);
|
||||
if (!build_file(file, info, true))
|
||||
{
|
||||
str_array_free(sources);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
str_array_free(sources);
|
||||
|
||||
/* Step 4: Build all media files. */
|
||||
sources = collect_sources(info.basic.src, true, ".c");
|
||||
images = collect_sources("etc/media", true, ".png");
|
||||
for (i = 0; i < str_array_len(images); i++)
|
||||
{
|
||||
char *file = str_array_get(images, i);
|
||||
char *out;
|
||||
|
||||
out = process_png(file, info);
|
||||
if (!out)
|
||||
{
|
||||
str_array_free(images);
|
||||
str_array_free(sources);
|
||||
goto fail;
|
||||
}
|
||||
printf("\tPNG %s\n", file);
|
||||
str_array_add(sources, out);
|
||||
free(out);
|
||||
}
|
||||
str_array_free(images);
|
||||
|
||||
/* Step 5: Build all of Parsee itself */
|
||||
for (i = 0; i < str_array_len(sources); i++)
|
||||
{
|
||||
char *file = str_array_get(sources, i);
|
||||
if (!build_file(file, info, false))
|
||||
{
|
||||
str_array_free(sources);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
printf("\tLINK\n");
|
||||
if (!finalise_file(sources, info))
|
||||
{
|
||||
str_array_free(sources);
|
||||
goto fail;
|
||||
}
|
||||
str_array_free(sources);
|
||||
/* TODO: Step 6: Build every Ayadoc */
|
||||
end:
|
||||
destroy_buildinfo(&info);
|
||||
return EXIT_SUCCESS;
|
||||
fail:
|
||||
if (buildinfo)
|
||||
{
|
||||
fclose(buildinfo);
|
||||
buildinfo = NULL;
|
||||
}
|
||||
destroy_buildinfo(&info);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
/* TODO: Multiple flags(build/install/ayadoc/...) */
|
||||
if ((argc - 1) < 1)
|
||||
{
|
||||
/* No arguments, let's just build. */
|
||||
return main_build(argc, argv);
|
||||
}
|
||||
|
||||
if (!strcmp(argv[1], "build") ||
|
||||
!strcmp(argv[1], "clean"))
|
||||
{
|
||||
return main_build(argc, argv);
|
||||
}
|
||||
|
||||
printf("%s: unknown verb: %s\n", argv[0], argv[1]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
CODE=tomboyish-bridges-adventure
|
||||
CODE=lunar-rainbow
|
||||
NAME=Parsee
|
||||
VERSION=0.1.0
|
||||
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);
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee-adminify 1 "Parsee Utility" "tomboyish-bridges-adventure"
|
||||
.TH parsee-adminify 1 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee-adminify - bootstrap an admin to a new Parsee server
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee-aya 1 "Parsee Utility" "tomboyish-bridges-adventure"
|
||||
.TH parsee-aya 1 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee-aya - generate some nice Ayaya! documentation
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee-config 1 "Parsee Utility" "tomboyish-bridges-adventure"
|
||||
.TH parsee-config 1 "Parsee Utility" "lunar-rainbow"
|
||||
|
||||
.SH NAME
|
||||
parsee-config - generate a basic configuration file
|
||||
|
|
@ -11,8 +11,10 @@ parsee-config
|
|||
.B [-s SHARED_SECRET]
|
||||
.B [-m MEDIA_URL]
|
||||
.B [-J JABBER_HOST]
|
||||
.B [-j JABBER_ADDR]
|
||||
.B [-p JABBER_PORT]
|
||||
.B [-d DATABASE]
|
||||
.B [-M MAX_STANZA]
|
||||
.B [-S DATABASE size]
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
|
@ -33,6 +35,7 @@ $ parsee-config \\
|
|||
-H 'blow.hole' \\
|
||||
-s 'The Dark Shared Secret' \\
|
||||
-J 'xmpp.blow.hole' \\
|
||||
-j 'localhost' \\
|
||||
-S 128
|
||||
.fi
|
||||
.if n \{\
|
||||
|
|
@ -54,6 +57,10 @@ For example, if you except Parsee users to be on
|
|||
.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
|
||||
|
|
@ -63,6 +70,12 @@ media. It must be publicly accessible (behind a reverse proxy to HTTP:7642)
|
|||
.I JABBER_HOST
|
||||
is used as the component host for Parsee.
|
||||
.TP
|
||||
.BR -j JABBER_ADDR
|
||||
.I JABBER_ADDR
|
||||
can optionally be used to change the hostname Parsee will try to contact
|
||||
for XMPP. Users should ideally use localhost (or a hostname pointing to
|
||||
the server itself), as XMPP component streams are not encrypted.
|
||||
.TP
|
||||
.BR -p JABBER_PORT
|
||||
.I JABBER_PORT
|
||||
is used as the component post for Parsee. Parsee uses it alongside
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee 1 "Parsee Utility" "tomboyish-bridges-adventure"
|
||||
.TH parsee 1 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee - the jealous XMPP-Matrix bridge
|
||||
|
|
|
|||
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)
|
||||
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
|
||||
|
|
@ -30,8 +30,9 @@ ASType(const ParseeConfig *c, char *user, char *room, bool status)
|
|||
|
||||
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));
|
||||
/* 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);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -14,7 +15,7 @@ char *
|
|||
ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime)
|
||||
{
|
||||
char *size_str, *path, *ret, *user;
|
||||
int i;
|
||||
unsigned int i;
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
if (!c || !from)
|
||||
|
|
@ -127,3 +128,93 @@ ASReupload(const ParseeConfig *c, char *from, char **mime)
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,16 @@
|
|||
|
||||
#include <Matrix.h>
|
||||
|
||||
void
|
||||
bool
|
||||
ASPing(const ParseeConfig *conf)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path;
|
||||
bool ret;
|
||||
if (!conf)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
|
|
@ -33,7 +34,9 @@ ASPing(const ParseeConfig *conf)
|
|||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
ret = ParseeSetRequestJSON(ctx, json) == HTTP_OK;
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,3 +136,71 @@ ASGetName(const ParseeConfig *c, char *room, char *user)
|
|||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,19 +25,20 @@ ASGetRelations(const ParseeConfig *c, size_t n, char *room, char *event, char *t
|
|||
}
|
||||
|
||||
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
||||
if (event)
|
||||
if (type)
|
||||
{
|
||||
path = StrConcat(6,
|
||||
path = StrConcat(8,
|
||||
"/_matrix/client/v1/rooms/", room,
|
||||
"/relations/", event,
|
||||
"/relations/", event, "/", type,
|
||||
"?user_id=", user
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = StrConcat(4,
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v1/rooms/", room,
|
||||
"/relations?user_id=", user
|
||||
"/relations/", event,
|
||||
"?user_id=", user
|
||||
);
|
||||
}
|
||||
Free(user);
|
||||
|
|
|
|||
|
|
@ -25,11 +25,13 @@ ASInvite(const ParseeConfig *conf, char *id, char *invited)
|
|||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/invite",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
Free(id);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
|
|
@ -60,11 +62,13 @@ ASBan(const ParseeConfig *conf, char *id, char *banned)
|
|||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/ban",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
Free(id);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
|
|
@ -73,7 +77,7 @@ ASBan(const ParseeConfig *conf, char *id, char *banned)
|
|||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
||||
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
|
|
@ -95,11 +99,13 @@ ASKick(const ParseeConfig *conf, char *id, char *banned)
|
|||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/kick",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
Free(id);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
|
|
@ -108,7 +114,7 @@ ASKick(const ParseeConfig *conf, char *id, char *banned)
|
|||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
||||
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
|
|
@ -120,7 +126,8 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *ret;
|
||||
char *path, *ret, *serv;
|
||||
int status;
|
||||
if (!conf || !id)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -139,6 +146,11 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
{
|
||||
masquerade = HttpUrlEncode(masquerade);
|
||||
}
|
||||
serv = strchr(id, ':');
|
||||
if (serv)
|
||||
{
|
||||
serv = serv + 1;
|
||||
}
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/join/", id, "?",
|
||||
|
|
@ -152,7 +164,7 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
status = ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
|
|
@ -163,6 +175,8 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
|||
Free(masquerade);
|
||||
Free(id);
|
||||
|
||||
(void) serv; // TODO
|
||||
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
|
|
|
|||
|
|
@ -1,21 +1,43 @@
|
|||
#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)
|
||||
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)
|
||||
{
|
||||
|
|
@ -23,18 +45,27 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!ts)
|
||||
{
|
||||
ts = UtilTsMillis();
|
||||
}
|
||||
ts_str = TSToStr(ts);
|
||||
|
||||
txn = StrRandom(16);
|
||||
path = StrConcat(9,
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(11,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
id, "/send/", type, "/", txn, "?",
|
||||
"user_id=", user
|
||||
"user_id=", user, "&ts=", ts_str
|
||||
);
|
||||
Free(id);
|
||||
Free(txn);
|
||||
Free(ts_str);
|
||||
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, c);
|
||||
status = ParseeSetRequestJSON(ctx, c);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ CommandParse(char *cmd)
|
|||
}
|
||||
|
||||
end_data = strchr(cmd, ' ');
|
||||
if (!end_data)
|
||||
if (!end_data || (cmd > end_data))
|
||||
{
|
||||
ret = Malloc(sizeof(*ret));
|
||||
ret->command = StrDuplicate(cmd);
|
||||
|
|
@ -53,7 +53,7 @@ CommandParse(char *cmd)
|
|||
switch (state)
|
||||
{
|
||||
case STATE_WHITE:
|
||||
if (!isblank(c))
|
||||
if (!isblank((int) c))
|
||||
{
|
||||
state = STATE_NAME;
|
||||
namestart = cur;
|
||||
|
|
@ -84,7 +84,7 @@ CommandParse(char *cmd)
|
|||
{
|
||||
char c = *cur;
|
||||
char cb[2] = { c, '\0' };
|
||||
if ((type && c == char_type) || (!type && isblank(c)))
|
||||
if ((type && c == char_type) || (!type && isblank((int) c)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,35 +15,47 @@ CommandCreateRouter(void)
|
|||
void
|
||||
CommandAddCommand(CommandRouter *rter, char *c, CommandRoute rte)
|
||||
{
|
||||
CommandRoute *indirect;
|
||||
if (!rter || !c || !rte)
|
||||
{
|
||||
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
|
||||
RouteCommand(CommandRouter *rter, Command *cmd, void *d)
|
||||
{
|
||||
CommandRoute route;
|
||||
CommandRoute *route;
|
||||
if (!rter || !cmd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
route = HashMapGet(rter->routes, cmd->command);
|
||||
if (route)
|
||||
if (route && *route)
|
||||
{
|
||||
route(cmd, d);
|
||||
(*route)(cmd, d);
|
||||
}
|
||||
}
|
||||
void
|
||||
CommandFreeRouter(CommandRouter *rter)
|
||||
{
|
||||
char *key;
|
||||
CommandRoute *val;
|
||||
if (!rter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (HashMapIterate(rter->routes, &key, (void **) &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(rter->routes);
|
||||
Free(rter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,28 @@ CommandHead(CmdBanUser, cmd, argp)
|
|||
BotDestroy();
|
||||
return;
|
||||
}
|
||||
ASBan(data->config, room, user);
|
||||
|
||||
ReplySprintf("Banning %s from '%s'...",
|
||||
user, room
|
||||
);
|
||||
ASBan(data->config, room, user);
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,4 +25,6 @@ CommandHead(CmdHelp, cmd, argp)
|
|||
);
|
||||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||
BotDestroy();
|
||||
|
||||
(void) cmd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,4 +32,6 @@ CommandHead(CmdListBans, cmd, argp)
|
|||
|
||||
DbUnlock(data->db, listed);
|
||||
BotDestroy();
|
||||
|
||||
(void) cmd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ CommandHead(CmdPlumb, cmd, argp)
|
|||
BotRequired(room);
|
||||
|
||||
/* 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);
|
||||
goto end;
|
||||
|
|
@ -62,7 +63,7 @@ CommandHead(CmdPlumb, cmd, argp)
|
|||
if (chat_id)
|
||||
{
|
||||
char *rev = StrConcat(2, muc, "/parsee");
|
||||
XMPPJoinMUC(args->data->jabber, "parsee", rev, false);
|
||||
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
|
||||
|
||||
Free(rev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,4 +40,6 @@ CommandHead(CmdStats, cmd, argp)
|
|||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||
|
||||
BotDestroy();
|
||||
|
||||
(void) cmd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,43 +9,60 @@
|
|||
|
||||
#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)
|
||||
{
|
||||
ParseeCmdArg *args = argp;
|
||||
ParseeData *data = args->data;
|
||||
HashMap *json, *event = args->event, *mucs;
|
||||
DbRef *ref;
|
||||
HashMap *event = args->event;
|
||||
char *muc = NULL, *chat_id = NULL, *room = NULL;
|
||||
|
||||
BotInitialise();
|
||||
|
||||
muc = HashMapGet(cmd->arguments, "muc");
|
||||
if (!muc)
|
||||
if (!Grab(data, cmd, &muc, &chat_id, &room))
|
||||
{
|
||||
ReplyBasic("`muc` field REQUIRED.");
|
||||
ReplyBasic("`muc`|`room` REQUIRED");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ref = DbLock(data->db, 1, "chats");
|
||||
json = DbJson(ref);
|
||||
chat_id = StrDuplicate(GrabString(json, 2, "mucs", muc));
|
||||
chat_id = ParseeGetFromMUCID(data, muc);
|
||||
room = ParseeGetRoomID(data, chat_id);
|
||||
if (!chat_id)
|
||||
{
|
||||
ReplySprintf("No internal mapping to '%s'.", muc);
|
||||
goto end;
|
||||
}
|
||||
mucs = GrabObject(json, 1, "mucs");
|
||||
JsonValueFree(HashMapDelete(mucs, muc));
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
room = ParseeGetRoomID(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);
|
||||
ParseeUnlinkRoom(data, chat_id);
|
||||
|
||||
/* TODO: Do it automatically, if *not plumbed* */
|
||||
ReplySprintf("The MUC %s is now *unlinked*.", muc);
|
||||
|
|
|
|||
13
src/Events.c
13
src/Events.c
|
|
@ -79,7 +79,7 @@ MatrixCreateNickChange(char *nick)
|
|||
return map;
|
||||
}
|
||||
HashMap *
|
||||
MatrixCreateMedia(char *mxc, char *body, char *mime)
|
||||
MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
||||
{
|
||||
HashMap *map;
|
||||
char *mime_type = NULL, *matrix_type = NULL;
|
||||
|
|
@ -91,9 +91,10 @@ MatrixCreateMedia(char *mxc, char *body, char *mime)
|
|||
matrix_type = "m.file";
|
||||
if (mime)
|
||||
{
|
||||
size_t i;
|
||||
size_t i, len;
|
||||
mime_type = StrDuplicate(mime);
|
||||
for (i = 0; i < strlen(mime); i++)
|
||||
len = strlen(mime);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (mime_type[i] == '/')
|
||||
{
|
||||
|
|
@ -119,6 +120,12 @@ MatrixCreateMedia(char *mxc, char *body, char *mime)
|
|||
}
|
||||
|
||||
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, "mimetype", JsonValueString(mime));
|
||||
HashMapSet(map, "body", JsonValueString(body));
|
||||
|
|
|
|||
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);
|
||||
}
|
||||
|
|
@ -73,7 +73,7 @@ ParseeCreateRequest(const ParseeConfig *conf, HttpRequestMethod meth, char *path
|
|||
|
||||
ctx = HttpRequest(
|
||||
meth,
|
||||
HTTP_FLAG_TLS,
|
||||
conf->homeserver_tls ? HTTP_FLAG_TLS : HTTP_FLAG_NONE,
|
||||
conf->homeserver_port, conf->homeserver_host,
|
||||
path
|
||||
);
|
||||
|
|
|
|||
81
src/Main.c
81
src/Main.c
|
|
@ -1,5 +1,6 @@
|
|||
#include <Cytoplasm/HttpServer.h>
|
||||
#include <Cytoplasm/Cytoplasm.h>
|
||||
#include <Cytoplasm/Platform.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Cron.h>
|
||||
|
|
@ -12,6 +13,7 @@
|
|||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <StanzaBuilder.h>
|
||||
#include <Parsee.h>
|
||||
#include <XMPP.h>
|
||||
#include <AS.h>
|
||||
|
|
@ -46,7 +48,7 @@ static const Argument arguments[] =
|
|||
"Generates a parsee.yaml AS file before exiting")
|
||||
Arg('v', false, NULL,
|
||||
"Forces Parsee to print in a more verbose fashion "
|
||||
"(-vv prints stanzas to stderr)")
|
||||
"(-vvv prints stanzas to stderr)")
|
||||
Arg('h', false, NULL,
|
||||
"Generates an help screen(this one!)")
|
||||
|
||||
|
|
@ -56,6 +58,50 @@ static const Argument arguments[] =
|
|||
#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
|
||||
Main(Array *args, HashMap *env)
|
||||
{
|
||||
|
|
@ -78,6 +124,15 @@ Main(Array *args, HashMap *env)
|
|||
);
|
||||
ParseePrintASCII();
|
||||
Log(LOG_INFO, "=======================");
|
||||
Log(LOG_INFO, "(C)opyright 2024-2025 LDA and other contributors");
|
||||
Log(LOG_INFO, "(This program is free software, see LICENSE.)");
|
||||
|
||||
#ifdef PLATFORM_IPHONE
|
||||
Log(LOG_WARNING, "Wait. Are you running this on an iPhone?");
|
||||
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());
|
||||
|
||||
{
|
||||
|
|
@ -103,6 +158,8 @@ Main(Array *args, HashMap *env)
|
|||
/* Write out the config file to a YAML document */
|
||||
Log(LOG_INFO, "Generating YAML...");
|
||||
yaml = StreamOpen("parsee.yaml", "w");
|
||||
ParseeConfigLoad(configuration);
|
||||
ParseeConfigInit();
|
||||
ParseeExportConfigYAML(yaml);
|
||||
StreamClose(yaml);
|
||||
Free(opts);
|
||||
|
|
@ -113,6 +170,9 @@ Main(Array *args, HashMap *env)
|
|||
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;
|
||||
|
|
@ -145,7 +205,6 @@ Main(Array *args, HashMap *env)
|
|||
}
|
||||
}
|
||||
Free(opts);
|
||||
ParseeSetThreads(xmpp, http);
|
||||
}
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
|
|
@ -159,13 +218,16 @@ Main(Array *args, HashMap *env)
|
|||
{
|
||||
goto end;
|
||||
}
|
||||
ParseeSetThreads(xmpp, http);
|
||||
|
||||
|
||||
Log(LOG_NOTICE, "Connecting to XMPP...");
|
||||
jabber = XMPPInitialiseCompStream(
|
||||
parsee_conf->component_addr,
|
||||
parsee_conf->component_host,
|
||||
parsee_conf->component_port
|
||||
);
|
||||
Log(LOG_NOTICE, "Connecting to XMPP... %p", jabber);
|
||||
if (!XMPPAuthenticateCompStream(
|
||||
jabber,
|
||||
parsee_conf->shared_comp_secret
|
||||
|
|
@ -206,6 +268,12 @@ Main(Array *args, HashMap *env)
|
|||
}
|
||||
ParseeInitialiseNickTable();
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Initialising affiliation table");
|
||||
}
|
||||
ParseeInitialiseAffiliationTable();
|
||||
|
||||
conf.port = parsee_conf->port;
|
||||
conf.threads = parsee_conf->http_threads;
|
||||
conf.maxConnections = conf.threads << 2;
|
||||
|
|
@ -223,18 +291,21 @@ Main(Array *args, HashMap *env)
|
|||
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...");
|
||||
cron = CronCreate(10 SECONDS);
|
||||
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
||||
CronEvery(cron, 10 SECONDS, ParseeCheckMatrix, conf.handlerArgs);
|
||||
ParseeCleanup(conf.handlerArgs);
|
||||
|
||||
CronStart(cron);
|
||||
|
|
@ -247,8 +318,9 @@ Main(Array *args, HashMap *env)
|
|||
}
|
||||
|
||||
server = HttpServerCreate(&conf);
|
||||
((ParseeData *) conf.handlerArgs)->server = server;
|
||||
|
||||
if (!ParseeInitialiseSignals(server, xmpp_thr, jabber))
|
||||
if (!ParseeInitialiseSignals(conf.handlerArgs, xmpp_thr))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
|
@ -272,9 +344,12 @@ end:
|
|||
CronStop(cron);
|
||||
CronFree(cron);
|
||||
ParseeFreeData(conf.handlerArgs);
|
||||
ParseeDestroyAffiliationTable();
|
||||
ParseeDestroyNickTable();
|
||||
ParseeDestroyOIDTable();
|
||||
ParseeDestroyHeadTable();
|
||||
ParseeDestroyJIDTable();
|
||||
|
||||
(void) env;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,24 +8,29 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <StanzaBuilder.h>
|
||||
#include <Unistring.h>
|
||||
#include <Matrix.h>
|
||||
#include <AS.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
static const char *
|
||||
GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to);
|
||||
|
||||
static void
|
||||
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name)
|
||||
static char *
|
||||
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char *hash)
|
||||
{
|
||||
char *sender = GrabString(event, 1, "sender");
|
||||
|
||||
char *nick = StrDuplicate(name);
|
||||
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;
|
||||
|
||||
while (!XMPPJoinMUC(data->jabber, jid, rev, true) && nonce < 32)
|
||||
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);
|
||||
|
|
@ -50,7 +55,7 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name)
|
|||
|
||||
ParseePushNickTable(muc, sender, nick);
|
||||
Free(nick);
|
||||
Free(rev);
|
||||
return (rev);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -93,20 +98,95 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
|||
else if (StrEquals(membership, "join") && !ParseeIsPuppet(conf, 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);
|
||||
|
||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||
Free(avatar);
|
||||
avatar = NULL;
|
||||
if (chat_id)
|
||||
{
|
||||
char *muc = ParseeGetMUCID(data, chat_id);
|
||||
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);
|
||||
|
||||
JoinMUC(data, event, jid, muc, name);
|
||||
Free(jabber);
|
||||
Free(avatar);
|
||||
Free(name);
|
||||
Free(muc);
|
||||
avatar = NULL;
|
||||
|
||||
/* 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(avatar);
|
||||
Free(mime);
|
||||
Free(sha);
|
||||
Free(jid);
|
||||
Free(url);
|
||||
}
|
||||
else if ((StrEquals(membership, "leave") ||
|
||||
StrEquals(membership, "ban"))
|
||||
|
|
@ -175,14 +255,9 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (*body != '!')
|
||||
if (!body || *body != '!')
|
||||
{
|
||||
/* All commands are to be marked with a ! */
|
||||
Free(ASSend(
|
||||
data->config, id, profile,
|
||||
"m.room.message",
|
||||
MatrixCreateNotice("Please enter a valid command")
|
||||
));
|
||||
Free(profile);
|
||||
return;
|
||||
}
|
||||
|
|
@ -192,7 +267,7 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
|||
Free(ASSend(
|
||||
data->config, id, profile,
|
||||
"m.room.message",
|
||||
MatrixCreateNotice("You are not authorised to do this.")
|
||||
MatrixCreateNotice("You are not authorised to do this."), 0
|
||||
));
|
||||
Free(profile);
|
||||
return;
|
||||
|
|
@ -214,10 +289,10 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
char *room_id = GrabString(event, 1, "room_id");
|
||||
char *matrix_sender = GrabString(event, 1, "sender");
|
||||
char *chat_id = NULL, *muc_id = NULL;
|
||||
char *user;
|
||||
char *user = NULL;
|
||||
|
||||
DbRef *room_data;
|
||||
HashMap *data_json;
|
||||
DbRef *room_data = NULL;
|
||||
HashMap *data_json = NULL;
|
||||
|
||||
bool direct = false;
|
||||
if (!data || !event || !from || !to)
|
||||
|
|
@ -249,7 +324,8 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
}
|
||||
else
|
||||
{
|
||||
char *matrix_name;
|
||||
char *matrix_name = NULL, *matrix_avatar = NULL;
|
||||
char *mime = NULL, *sha = NULL;
|
||||
|
||||
muc_id = ParseeGetMUCID(data, chat_id);
|
||||
if (!chat_id)
|
||||
|
|
@ -263,10 +339,16 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
}
|
||||
|
||||
matrix_name = ASGetName(data->config, room_id, matrix_sender);
|
||||
JoinMUC(data, event, *from, muc_id, matrix_name);
|
||||
matrix_avatar = ASGetAvatar(data->config, NULL, matrix_sender);
|
||||
|
||||
ASGetMIMESHA(data->config, matrix_avatar, &mime, &sha);
|
||||
Free(JoinMUC(data, event, *from, muc_id, matrix_name, sha));
|
||||
*to = muc_id;
|
||||
|
||||
Free(matrix_avatar);
|
||||
Free(matrix_name);
|
||||
Free(mime);
|
||||
Free(sha);
|
||||
}
|
||||
|
||||
Free(chat_id);
|
||||
|
|
@ -276,28 +358,47 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
static void
|
||||
ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||
{
|
||||
if (!data || !event)
|
||||
{
|
||||
return;
|
||||
}
|
||||
XMPPComponent *jabber = data->jabber;
|
||||
StanzaBuilder *builder;
|
||||
StanzaBuilder *builder = NULL;
|
||||
DbRef *ref = NULL;
|
||||
HashMap *json;
|
||||
HashMap *json = NULL;
|
||||
|
||||
char *msgtype = GrabString(event, 2, "content", "msgtype");
|
||||
char *m_sender = GrabString(event, 1, "sender");
|
||||
char *unedited_id = NULL;
|
||||
char *body = GrabString(event, 2, "content", "body");
|
||||
char *id = GrabString(event, 1, "room_id");
|
||||
char *ev_id = GrabString(event, 1, "event_id");
|
||||
char *m_sender = GrabString(event, 1, "sender");
|
||||
char *chat_id, *muc_id;
|
||||
char *chat_id = NULL, *muc_id = NULL;
|
||||
char *reply_id = MatrixGetReply(event);
|
||||
char *xepd = ParseeXMPPify(event);
|
||||
char *xepd = ParseeXMPPify(data, event);
|
||||
char *type, *user, *xmppified_user = NULL, *to = NULL;
|
||||
char *unauth = NULL;
|
||||
char *origin_id = NULL, *stanza = NULL;
|
||||
char *sender = NULL;
|
||||
char *unedited_id = MatrixGetEdit(event);
|
||||
char *url = GrabString(event, 2, "content", "url");
|
||||
char *encoded_from = NULL;
|
||||
|
||||
bool direct = false;
|
||||
unedited_id = MatrixGetEdit(event);
|
||||
|
||||
if (unedited_id)
|
||||
{
|
||||
char *new_content = GrabString(event, 3, "content", "m.new_content", "body");
|
||||
if (new_content) body = new_content;
|
||||
}
|
||||
|
||||
if (data->config->ignore_bots && StrEquals(msgtype, "m.notice"))
|
||||
{
|
||||
Free(reply_id);
|
||||
Free(xepd);
|
||||
Free(unedited_id);
|
||||
return;
|
||||
}
|
||||
if (ParseeIsPuppet(data->config, m_sender) ||
|
||||
ParseeManageBan(data, m_sender, id))
|
||||
{
|
||||
|
|
@ -328,7 +429,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
|
||||
type = direct ? "chat" : "groupchat";
|
||||
user = GrabString(json, 1, "xmpp_user");
|
||||
unauth = ParseeToUnauth(data, url);
|
||||
unauth = ParseeToUnauth(data, url, GrabString(event, 2, "content", "filename"));
|
||||
|
||||
encoded_from = ParseeEncodeMXID(m_sender);
|
||||
xmppified_user = StrConcat(3,
|
||||
|
|
@ -342,7 +443,8 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
}
|
||||
else
|
||||
{
|
||||
char *name;
|
||||
char *name, *mime = NULL, *sha = NULL;
|
||||
char *avatar;
|
||||
/* Try to find the chat ID */
|
||||
muc_id = ParseeGetMUCID(data, chat_id);
|
||||
if (!chat_id)
|
||||
|
|
@ -350,17 +452,21 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* TODO: Check the name's validity.
|
||||
* Is there a good way to check for that that isn't
|
||||
* just "await on join and try again?" */
|
||||
/* TODO: Avoid using the AS endpoints */
|
||||
name = ASGetName(data->config, id, m_sender);
|
||||
avatar = ASGetAvatar(data->config, NULL, m_sender);
|
||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||
|
||||
JoinMUC(data, event, encoded_from, muc_id, name);
|
||||
Free(JoinMUC(data, event, encoded_from, muc_id, name, sha));
|
||||
|
||||
to = muc_id;
|
||||
|
||||
Free(sha);
|
||||
Free(mime);
|
||||
Free(name);
|
||||
Free(avatar);
|
||||
}
|
||||
|
||||
if (reply_id)
|
||||
{
|
||||
/* TODO: Monocles chat DM users HATE this trick!
|
||||
|
|
@ -394,7 +500,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
SetStanzaEdit(builder, origin_id);
|
||||
SetStanzaXParsee(builder, event);
|
||||
|
||||
WriteoutStanza(builder, jabber);
|
||||
WriteoutStanza(builder, jabber, data->config->max_stanza_size);
|
||||
DestroyStanzaBuilder(builder);
|
||||
|
||||
if (direct)
|
||||
|
|
@ -417,8 +523,8 @@ end:
|
|||
Free(stanza);
|
||||
Free(sender);
|
||||
Free(unauth);
|
||||
Free(unedited_id);
|
||||
Free(encoded_from);
|
||||
Free(unedited_id);
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
ref = NULL;
|
||||
|
|
@ -449,8 +555,7 @@ ParseeEventHandler(ParseeData *data, HashMap *event)
|
|||
return;
|
||||
}
|
||||
else if (StrEquals(event_type, "m.room.message") ||
|
||||
StrEquals(event_type, "m.sticker")) /* TODO: Actual sticker
|
||||
* support here... */
|
||||
StrEquals(event_type, "m.sticker"))
|
||||
{
|
||||
ParseeMessageHandler(data, event);
|
||||
Free(parsee);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
#include <Matrix.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -32,3 +35,34 @@ MatrixParseID(char *user)
|
|||
|
||||
return ret;
|
||||
}
|
||||
UserID *
|
||||
MatrixParseIDFromMTO(Uri *uri)
|
||||
{
|
||||
UserID *id = NULL;
|
||||
char *path, *params, *decoded;
|
||||
if (!uri)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!StrEquals(uri->proto, "https") || !StrEquals(uri->host, "matrix.to"))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (strncmp(uri->path, "/#/", 3))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
path = StrDuplicate(uri->path + 3);
|
||||
params = path ? strchr(path, '?') : NULL;
|
||||
if (params)
|
||||
{
|
||||
*params = '\0';
|
||||
}
|
||||
decoded = HttpUrlDecode(path);
|
||||
id = MatrixParseID(decoded);
|
||||
Free(decoded);
|
||||
Free(path);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,18 @@ ParseeConfigLoad(char *conf)
|
|||
{
|
||||
return;
|
||||
}
|
||||
stream = StreamOpen("parsee.json", "r");
|
||||
stream = StreamOpen(conf ? conf : "parsee.json", "r");
|
||||
if (!stream)
|
||||
{
|
||||
return;
|
||||
}
|
||||
json = JsonDecode(stream);
|
||||
if (!json)
|
||||
{
|
||||
Log(LOG_ERR, "Could not parse config JSON");
|
||||
StreamClose(stream);
|
||||
return;
|
||||
}
|
||||
|
||||
config = Malloc(sizeof(*config));
|
||||
#define CopyToStr(to, str) config->to = StrDuplicate( \
|
||||
|
|
@ -44,6 +50,9 @@ ParseeConfigLoad(char *conf)
|
|||
#define CopyToInt(to, str) config->to = (int) ( \
|
||||
JsonValueAsInteger(HashMapGet(json, str)) \
|
||||
)
|
||||
#define CopyToBool(to, str) config->to = (int) ( \
|
||||
JsonValueAsBoolean(HashMapGet(json, str)) \
|
||||
)
|
||||
|
||||
config->http_threads = 8;
|
||||
config->xmpp_threads = 8;
|
||||
|
|
@ -58,10 +67,25 @@ ParseeConfigLoad(char *conf)
|
|||
CopyToStr(server_base, "hs_base");
|
||||
CopyToStr(homeserver_host, "hs_host");
|
||||
CopyToInt(homeserver_port, "hs_port");
|
||||
CopyToBool(homeserver_tls, "hs_tls");
|
||||
if (!HashMapGet(json, "hs_tls"))
|
||||
{
|
||||
config->homeserver_tls = true;
|
||||
}
|
||||
CopyToBool(accept_pings, "accept_pings");
|
||||
|
||||
CopyToInt(component_port, "component_port");
|
||||
CopyToStr(component_addr, "component_addr");
|
||||
CopyToStr(component_host, "component_host");
|
||||
CopyToStr(shared_comp_secret, "shared_secret");
|
||||
CopyToInt(max_stanza_size, "max_stanza_size");
|
||||
if (!config->max_stanza_size)
|
||||
{
|
||||
/* Standard XMPP "minimum" maximum */
|
||||
config->max_stanza_size = 10000;
|
||||
}
|
||||
|
||||
CopyToBool(ignore_bots, "ignore_bots");
|
||||
|
||||
CopyToStr(media_base, "media_base");
|
||||
|
||||
|
|
@ -77,6 +101,7 @@ ParseeSetThreads(int xmpp, int http)
|
|||
{
|
||||
if (!config)
|
||||
{
|
||||
Achievement("THREAD COUNT REQUEST WITHOUT CONFIG", true);
|
||||
return;
|
||||
}
|
||||
config->http_threads = http;
|
||||
|
|
@ -88,6 +113,7 @@ ParseeExportConfigYAML(Stream *stream)
|
|||
{
|
||||
if (!stream || !config)
|
||||
{
|
||||
Achievement("YAML EXPORT REQUEST WITHOUT CONFIG", true);
|
||||
return;
|
||||
}
|
||||
StreamPrintf(stream, "# Autogenerated YAML AS entry for %s\n", NAME);
|
||||
|
|
@ -101,6 +127,7 @@ ParseeExportConfigYAML(Stream *stream)
|
|||
StreamPrintf(stream, "hs_token: \"%s\"\n", config->hs_token);
|
||||
StreamPrintf(stream, "sender_localpart: \"%s\"\n", config->sender_localpart);
|
||||
StreamPrintf(stream, "protocols: [\"xmpp\", \"jabber\"]\n");
|
||||
StreamPrintf(stream, "receive_ephemeral: true\n"); /* TODO: Actually use that field */
|
||||
StreamPrintf(stream, "\n");
|
||||
StreamPrintf(stream, "namespaces: \n");
|
||||
StreamPrintf(stream, " users:\n");
|
||||
|
|
@ -109,6 +136,7 @@ ParseeExportConfigYAML(Stream *stream)
|
|||
StreamPrintf(stream, " aliases:\n");
|
||||
StreamPrintf(stream, " - exclusive: true\n");
|
||||
StreamPrintf(stream, " regex: \"#%s_.*\"\n", config->namespace_base);
|
||||
StreamFlush(stream);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -119,6 +147,7 @@ ParseeConfigFree(void)
|
|||
return;
|
||||
}
|
||||
Free(config->component_host);
|
||||
Free(config->component_addr);
|
||||
Free(config->shared_comp_secret);
|
||||
Free(config->db_path);
|
||||
Free(config->homeserver_host);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <Routes.h>
|
||||
#include <AS.h>
|
||||
|
|
@ -26,11 +27,15 @@ ParseeInitData(XMPPComponent *comp)
|
|||
data->config = ParseeConfigGet();
|
||||
data->router = HttpRouterCreate();
|
||||
data->jabber = comp;
|
||||
data->muc = CreateMUCServer(data);
|
||||
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)
|
||||
{
|
||||
data->db = DbOpenLMDB(data->config->db_path, data->config->db_size);
|
||||
|
|
@ -50,14 +55,16 @@ ParseeInitData(XMPPComponent *comp)
|
|||
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 (!ParseeIsCompatible(VERSION, 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);
|
||||
|
|
@ -107,8 +114,10 @@ ParseeFreeData(ParseeData *data)
|
|||
}
|
||||
HashMapFree(data->oid_servers);
|
||||
pthread_mutex_destroy(&data->oidl);
|
||||
pthread_mutex_destroy(&data->halt_lock);
|
||||
Free(data->id);
|
||||
XMPPEndCompStream(data->jabber);
|
||||
FreeMUCServer(data->muc);
|
||||
DbClose(data->db);
|
||||
HttpRouterFree(data->router);
|
||||
CommandFreeRouter(data->handler);
|
||||
|
|
@ -123,9 +132,6 @@ ParseeCleanup(void *datp)
|
|||
char *chat;
|
||||
size_t i;
|
||||
uint64_t ts = UtilTsMillis();
|
||||
size_t entries = 0;
|
||||
|
||||
Log(LOG_DEBUG, "Cleaning up...");
|
||||
|
||||
chats = DbList(data->db, 1, "chats");
|
||||
|
||||
|
|
@ -169,7 +175,6 @@ ParseeCleanup(void *datp)
|
|||
if (cleaned > threshold) \
|
||||
{ \
|
||||
DbDelete(data->db, 4, "chats", chat, #field"s", field); \
|
||||
entries++; \
|
||||
} \
|
||||
Free(field); \
|
||||
} \
|
||||
|
|
@ -177,9 +182,12 @@ ParseeCleanup(void *datp)
|
|||
} \
|
||||
while (0)
|
||||
|
||||
CleanupField(stanza, 30 MINUTES, 50);
|
||||
CleanupField(event, 30 MINUTES, 50);
|
||||
CleanupField(id, 30 MINUTES, 50);
|
||||
/* TODO: Custom retention period for any 1.0 */
|
||||
CleanupField(stanza, 30 MINUTES, 500);
|
||||
CleanupField(event, 30 MINUTES, 500);
|
||||
CleanupField(id, 30 MINUTES, 500);
|
||||
|
||||
/* TODO: Also cleanup user cache information */
|
||||
#undef CleanupField
|
||||
}
|
||||
DbListFree(chats);
|
||||
|
|
@ -223,7 +231,6 @@ ParseeCleanup(void *datp)
|
|||
if (cleaned > threshold) \
|
||||
{ \
|
||||
JsonValueFree(HashMapDelete(field##s, field)); \
|
||||
entries++; \
|
||||
} \
|
||||
Free(field); \
|
||||
} \
|
||||
|
|
@ -231,14 +238,13 @@ ParseeCleanup(void *datp)
|
|||
} \
|
||||
while (0)
|
||||
|
||||
CleanupField(stanza, 3 HOURS, 50);
|
||||
CleanupField(event, 3 HOURS, 50);
|
||||
CleanupField(id, 3 HOURS, 50);
|
||||
CleanupField(stanza, 3 HOURS, 500);
|
||||
CleanupField(event, 3 HOURS, 500);
|
||||
CleanupField(id, 3 HOURS, 500);
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
DbListFree(chats);
|
||||
Log(LOG_DEBUG, "Cleant up %d entries...", entries);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -333,6 +339,15 @@ ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, cha
|
|||
}
|
||||
|
||||
/* 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);
|
||||
j = DbJson(ref);
|
||||
|
|
@ -535,3 +550,178 @@ end:
|
|||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ParseeUnlinkRoom(ParseeData *data, char *chat_id)
|
||||
{
|
||||
char *muc, *room;
|
||||
DbRef *ref;
|
||||
if (!data || !chat_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
muc = ParseeGetMUCID(data, chat_id);
|
||||
room = ParseeGetRoomID(data, chat_id);
|
||||
if (!muc || !room)
|
||||
{
|
||||
Free(muc);
|
||||
Free(room);
|
||||
return;
|
||||
}
|
||||
|
||||
ref = DbLock(data->db, 1, "chats");
|
||||
JsonValueFree(HashMapDelete(
|
||||
GrabObject(DbJson(ref), 1, "rooms"),
|
||||
room
|
||||
));
|
||||
JsonValueFree(HashMapDelete(
|
||||
GrabObject(DbJson(ref), 1, "mucs"),
|
||||
muc
|
||||
));
|
||||
DbUnlock(data->db, ref);
|
||||
DbDelete(data->db, 2, "chats", chat_id);
|
||||
|
||||
Free(muc);
|
||||
Free(room);
|
||||
}
|
||||
bool
|
||||
ParseeIsMUCWhitelisted(ParseeData *data, char *muc)
|
||||
{
|
||||
char *server, *serv_start, *postserv;
|
||||
DbRef *ref;
|
||||
bool ret;
|
||||
if (!data || !muc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DbExists(data->db, 1, "whitelist"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
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. */
|
||||
{
|
||||
*postserv = '\0';
|
||||
}
|
||||
|
||||
ref = DbLockIntent(data->db,
|
||||
DB_HINT_READONLY,
|
||||
1, "whitelist"
|
||||
);
|
||||
ret = HashMapGet(DbJson(ref), server);
|
||||
DbUnlock(data->db, ref);
|
||||
Free(server);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
extern HashMap *
|
||||
ParseeGetChatSettings(ParseeData *data, char *chat)
|
||||
{
|
||||
HashMap *ret, *json;
|
||||
DbRef *ref;
|
||||
|
||||
char *key;
|
||||
JsonValue *value;
|
||||
if (!data || !chat)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ref = DbLockIntent(data->db, DB_HINT_READONLY,
|
||||
3, "chats", chat, "settings"
|
||||
);
|
||||
json = DbJson(ref);
|
||||
if (!ref)
|
||||
{
|
||||
return HashMapCreate();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,10 @@
|
|||
|
||||
const char *parsee_ascii[PARSEE_ASCII_LINES] =
|
||||
{
|
||||
"----------------------------",
|
||||
" =+======",
|
||||
" || | _ _/__----",
|
||||
" / || \\ ==+= _/_____\\_",
|
||||
" | || | -|- L___J",
|
||||
" | || | -|- L___J ",
|
||||
"_/ || \\_ ||| .______\\",
|
||||
" || | | | |.____.|",
|
||||
" || / | \\ |L____||",
|
||||
|
|
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
|
@ -147,14 +148,15 @@ char *
|
|||
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
||||
{
|
||||
char *ret, *tmp;
|
||||
size_t i;
|
||||
size_t i, len;
|
||||
if (!c || !jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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 cs[4] = { 0 };
|
||||
|
|
@ -164,7 +166,7 @@ ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
|||
/* RID: Break everything and die. */
|
||||
break;
|
||||
}
|
||||
if (islower(*cs) || isalnum(*cs) || *cs == '_' ||
|
||||
if (islower((int) *cs) || isalnum((int) *cs) || *cs == '_' ||
|
||||
*cs == '=' || *cs == '-' || *cs == '/' ||
|
||||
*cs == '+' || *cs == '.')
|
||||
{
|
||||
|
|
@ -193,7 +195,7 @@ char *
|
|||
ParseeGetLocal(char *mxid)
|
||||
{
|
||||
char *cpy;
|
||||
size_t i;
|
||||
size_t i, len;
|
||||
if (!mxid)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -203,12 +205,14 @@ ParseeGetLocal(char *mxid)
|
|||
return StrDuplicate(mxid);
|
||||
}
|
||||
|
||||
mxid++;
|
||||
cpy = Malloc(strlen(mxid) + 1);
|
||||
memset(cpy, '\0', strlen(mxid) + 1);
|
||||
memcpy(cpy, mxid, strlen(mxid));
|
||||
len = 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] == ':')
|
||||
{
|
||||
|
|
@ -224,15 +228,16 @@ char *
|
|||
ParseeEncodeMXID(char *mxid)
|
||||
{
|
||||
char *ret;
|
||||
size_t i, j;
|
||||
size_t i, j, len;
|
||||
if (!mxid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Worst case scenario of 3-bytes the char */
|
||||
ret = Malloc(strlen(mxid) * 3 + 1);
|
||||
for (i = 0, j = 0; i < strlen(mxid); i++)
|
||||
len = strlen(mxid);
|
||||
ret = Malloc(len * 3 + 1);
|
||||
for (i = 0, j = 0; i < len; i++)
|
||||
{
|
||||
char src = mxid[i];
|
||||
|
||||
|
|
@ -356,8 +361,6 @@ ParseePushDMRoom(ParseeData *d, char *mxid, char *jid, char *r)
|
|||
void
|
||||
ParseeDeleteDM(ParseeData *d, char *mxid, char *jid)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *dmid;
|
||||
if (!d || !mxid || !jid)
|
||||
{
|
||||
|
|
@ -374,14 +377,15 @@ char *
|
|||
ParseeTrimJID(char *jid)
|
||||
{
|
||||
char *ret;
|
||||
size_t i;
|
||||
size_t i, len;
|
||||
if (!jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = StrDuplicate(jid);
|
||||
for (i = 0; i < strlen(ret); i++)
|
||||
len = strlen(ret);
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (ret[i] == '/')
|
||||
{
|
||||
|
|
@ -535,7 +539,6 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ParseeSendPresence(ParseeData *data)
|
||||
{
|
||||
|
|
@ -554,10 +557,16 @@ ParseeSendPresence(ParseeData *data)
|
|||
while (HashMapIterate(mucs, &muc, (void **) &val))
|
||||
{
|
||||
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 */
|
||||
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
||||
XMPPJoinMUC(data->jabber, "parsee", rev, false);
|
||||
XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false);
|
||||
|
||||
DbUnlock(data->db, chat);
|
||||
Free(chat_id);
|
||||
Free(rev);
|
||||
}
|
||||
DbUnlock(data->db, ref);
|
||||
|
|
@ -679,12 +688,13 @@ end:
|
|||
|
||||
#include <Cytoplasm/Uri.h>
|
||||
char *
|
||||
ParseeToUnauth(ParseeData *data, char *mxc)
|
||||
ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
||||
{
|
||||
Uri *url = NULL;
|
||||
char *ret;
|
||||
char *key, *hmac;
|
||||
#define PAT "%s/_matrix/client/v1/media/download/%s%s?hmac=%s"
|
||||
#define PAT "%s/media/%s%s?hmac=%s"
|
||||
#define PATF "%s/media/%s%s/%s?hmac=%s"
|
||||
size_t l;
|
||||
if (!data || !mxc)
|
||||
{
|
||||
|
|
@ -705,19 +715,45 @@ ParseeToUnauth(ParseeData *data, char *mxc)
|
|||
hmac = ParseeHMACS(data->id, key);
|
||||
Free(key);
|
||||
|
||||
l = snprintf(NULL, 0,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
if (!filename)
|
||||
{
|
||||
l = snprintf(NULL, 0,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *encoded = HttpUrlEncode(filename);
|
||||
l = snprintf(NULL, 0,
|
||||
PATF,
|
||||
data->config->media_base,
|
||||
url->host, url->path, encoded,
|
||||
hmac
|
||||
);
|
||||
Free(encoded);
|
||||
}
|
||||
ret = Malloc(l + 3);
|
||||
snprintf(ret, l + 1,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
if (!filename)
|
||||
{
|
||||
snprintf(ret, l + 1,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(ret, l + 1,
|
||||
PATF,
|
||||
data->config->media_base,
|
||||
url->host, url->path, filename,
|
||||
hmac
|
||||
);
|
||||
}
|
||||
UriFree(url);
|
||||
Free(hmac);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -5,11 +5,14 @@
|
|||
#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>
|
||||
|
||||
|
|
@ -17,7 +20,7 @@ typedef struct XMPPFlags {
|
|||
bool quote;
|
||||
} XMPPFlags;
|
||||
static char *
|
||||
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||
XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||
{
|
||||
char *xepd = NULL, *tmp = NULL;
|
||||
|
||||
|
|
@ -55,7 +58,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|||
} \
|
||||
} \
|
||||
while (0)
|
||||
switch (elem->type)
|
||||
switch (elem ? elem->type : -1)
|
||||
{
|
||||
case XML_ELEMENT_DATA:
|
||||
Concat(elem->data);
|
||||
|
|
@ -67,7 +70,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -80,7 +83,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -93,7 +96,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -126,7 +129,7 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -141,35 +144,70 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("\n");
|
||||
if (i != 0)
|
||||
{
|
||||
Concat("\n");
|
||||
}
|
||||
}
|
||||
else if (StrEquals(elem->name, "a"))
|
||||
{
|
||||
char *href = HashMapGet(elem->attrs, "href");
|
||||
Concat("(");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
Uri *pref = UriParse(href);
|
||||
if (pref && StrEquals(pref->host, "matrix.to"))
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
/* TODO: Check if the element here is a Matrix.TO
|
||||
* pointing to a Parsee user. */
|
||||
UserID *id = MatrixParseIDFromMTO(pref);
|
||||
if (id)
|
||||
{
|
||||
char *real_id = StrConcat(4, "@", id->localpart, ":", id->server);
|
||||
/* TODO: Detect if it already is a Parsee user */
|
||||
if (ParseeIsPuppet(conf, real_id))
|
||||
{
|
||||
char *name = ASGetName(conf, NULL, real_id);
|
||||
Concat((name ? name : real_id));
|
||||
Free(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Concat(real_id);
|
||||
}
|
||||
Free(real_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Concat(href);
|
||||
}
|
||||
|
||||
Free(id);
|
||||
}
|
||||
Concat(" points to ");
|
||||
Concat(href);
|
||||
Concat(" )");
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat(" < ");
|
||||
Concat(href);
|
||||
Concat(" >");
|
||||
}
|
||||
UriFree(pref);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
|
|
@ -181,8 +219,45 @@ XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
|||
}
|
||||
return xepd;
|
||||
}
|
||||
static char *
|
||||
GetRawBody(HashMap *event)
|
||||
{
|
||||
void *id;
|
||||
if ((id = MatrixGetEdit(event)))
|
||||
{
|
||||
char *new = GrabString(event, 3, "content", "m.new_content", "body");
|
||||
Free(id);
|
||||
if (new)
|
||||
{
|
||||
return new;
|
||||
}
|
||||
}
|
||||
return GrabString(event, 2, "content", "body");
|
||||
}
|
||||
static char *
|
||||
GetHTMLBody(HashMap *event)
|
||||
{
|
||||
if (MatrixGetEdit(event))
|
||||
{
|
||||
char *new = GrabString(event, 3, "content", "m.new_content", "formatted_body");
|
||||
if (new)
|
||||
{
|
||||
return new;
|
||||
}
|
||||
}
|
||||
return GrabString(event, 2, "content", "formatted_body");
|
||||
}
|
||||
static char *
|
||||
GetBodyFormat(HashMap *event)
|
||||
{
|
||||
if (MatrixGetEdit(event))
|
||||
{
|
||||
return GrabString(event, 3, "content", "m.new_content", "format");
|
||||
}
|
||||
return GrabString(event, 2, "content", "format");
|
||||
}
|
||||
char *
|
||||
ParseeXMPPify(HashMap *event)
|
||||
ParseeXMPPify(ParseeData *data, HashMap *event)
|
||||
{
|
||||
char *type, *format, *html;
|
||||
char *xepd = NULL;
|
||||
|
|
@ -201,20 +276,28 @@ ParseeXMPPify(HashMap *event)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
|
||||
if (!StrEquals(format, "org.matrix.custom.html"))
|
||||
if (!StrEquals(GetBodyFormat(event), "org.matrix.custom.html"))
|
||||
{
|
||||
/* Settle for the raw body instead. */
|
||||
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
|
||||
char *body = GetRawBody(event);
|
||||
return StrDuplicate(body);
|
||||
}
|
||||
|
||||
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
|
||||
html = GetHTMLBody(event);
|
||||
|
||||
html = StrConcat(3, "<html>", 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(event, elem, flags);
|
||||
xepd = XMPPifyElement(data ? data->config : NULL, event, elem, flags);
|
||||
|
||||
XMLFreeElement(elem);
|
||||
Free(html);
|
||||
|
|
@ -231,6 +314,7 @@ ParseeGenerateMTO(char *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);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,28 @@
|
|||
#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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <Unistring.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -37,6 +39,22 @@ ParseeFindDatastart(char *data)
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -10,6 +10,37 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
static HttpClientContext *
|
||||
TryDownload(ParseeData *data, char *server, char *identi)
|
||||
{
|
||||
HttpClientContext *cctx;
|
||||
char *path;
|
||||
server = HttpUrlEncode(server);
|
||||
identi = HttpUrlEncode(identi);
|
||||
|
||||
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(data->config, cctx);
|
||||
Free(path);
|
||||
|
||||
HttpRequestSendHeaders(cctx);
|
||||
if (HttpRequestSend(cctx) != HTTP_OK)
|
||||
{
|
||||
Log(LOG_WARNING, "Failing back.");
|
||||
HttpClientContextFree(cctx);
|
||||
path = StrConcat(4, "/_matrix/client/v1/media/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(data->config, cctx);
|
||||
Free(path);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
}
|
||||
|
||||
Free(server);
|
||||
Free(identi);
|
||||
return cctx;
|
||||
}
|
||||
|
||||
RouteHead(RouteMedia, arr, argp)
|
||||
{
|
||||
ParseeHttpArg *args = argp;
|
||||
|
|
@ -17,7 +48,7 @@ RouteHead(RouteMedia, arr, argp)
|
|||
HashMap *reqh, *params;
|
||||
char *server = ArrayGet(arr, 0);
|
||||
char *identi = ArrayGet(arr, 1);
|
||||
char *path, *key, *val;
|
||||
char *key, *val;
|
||||
char *hmac, *chkmak = NULL;
|
||||
|
||||
params = HttpRequestParams(args->ctx);
|
||||
|
|
@ -44,15 +75,7 @@ RouteHead(RouteMedia, arr, argp)
|
|||
|
||||
/* Proxy the media through an authenticated endpoint if the HMAC
|
||||
* is valid. */
|
||||
server = HttpUrlEncode(server);
|
||||
identi = HttpUrlEncode(identi);
|
||||
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(args->data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(args->data->config, cctx);
|
||||
Free(path);
|
||||
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
cctx = TryDownload(args->data, server, identi);
|
||||
reqh = HttpResponseHeaders(cctx);
|
||||
while (HashMapIterate(reqh, &key, (void **) &val))
|
||||
{
|
||||
|
|
@ -65,8 +88,6 @@ RouteHead(RouteMedia, arr, argp)
|
|||
}
|
||||
|
||||
HttpClientContextFree(cctx);
|
||||
Free(server);
|
||||
Free(identi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
36
src/Routes/Ping.c
Normal file
36
src/Routes/Ping.c
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#include <Routes.h>
|
||||
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
#include <AS.h>
|
||||
|
||||
RouteHead(RoutePing, arr, argp)
|
||||
{
|
||||
ParseeHttpArg *args = argp;
|
||||
HashMap *request = NULL;
|
||||
HashMap *response = NULL;
|
||||
|
||||
response = ASVerifyRequest(args);
|
||||
if (response)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
if (HttpRequestMethodGet(args->ctx) != HTTP_POST)
|
||||
{
|
||||
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
||||
response = MatrixCreateError(
|
||||
"M_UNRECOGNIZED",
|
||||
"Path /ping only accepts POST as a valid method."
|
||||
);
|
||||
goto end;
|
||||
}
|
||||
|
||||
RequestJSON();
|
||||
|
||||
response = HashMapCreate();
|
||||
end:
|
||||
(void) arr;
|
||||
JsonFree(request);
|
||||
return response;
|
||||
}
|
||||
|
|
@ -54,7 +54,13 @@ GetRandomQuote(void)
|
|||
NAME ": the federated world's little little kobashi",
|
||||
|
||||
"Go take a look at your stanzas!",
|
||||
"Go take a look at your objects!"
|
||||
"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);
|
||||
|
||||
|
|
@ -84,11 +90,13 @@ RouteHead(RouteRoot, arr, argp)
|
|||
P("color: #eee;");
|
||||
P("font-family: sans-serif;");
|
||||
P("}");
|
||||
P("#cols {");
|
||||
P("column-count: 3;");
|
||||
P("min-width: 100%;");
|
||||
P("max-width: 100%;");
|
||||
P("width: 100%;");
|
||||
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;");
|
||||
|
|
@ -115,6 +123,7 @@ RouteHead(RouteRoot, arr, argp)
|
|||
|
||||
P("<body>");
|
||||
{
|
||||
size_t i;
|
||||
P("<center>");
|
||||
P("<h1>Your %s is running, all with that %s!</h1>", NAME, CODE);
|
||||
P("</center>");
|
||||
|
|
@ -124,6 +133,15 @@ RouteHead(RouteRoot, arr, argp)
|
|||
P("%s", GetRandomQuote());
|
||||
}
|
||||
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>");
|
||||
{
|
||||
|
|
@ -161,7 +179,7 @@ RouteHead(RouteRoot, arr, argp)
|
|||
P("<p>");
|
||||
{
|
||||
P("More information available at ");
|
||||
P("<a");
|
||||
P("<a ");
|
||||
P("href='https://kappach.at/parsee'");
|
||||
P(">the actual page</a>.");
|
||||
}
|
||||
|
|
@ -236,5 +254,6 @@ RouteHead(RouteRoot, arr, argp)
|
|||
P("</html>");
|
||||
#undef P
|
||||
|
||||
(void) arr;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ RouteHead(RouteTxns, arr, argp)
|
|||
|
||||
response = HashMapCreate();
|
||||
end:
|
||||
(void) arr;
|
||||
JsonFree(request);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ RouteHead(RouteRoomAck, arr, argp)
|
|||
}
|
||||
|
||||
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);
|
||||
response = MatrixCreateError(
|
||||
|
|
@ -130,7 +131,7 @@ RouteHead(RouteRoomAck, arr, argp)
|
|||
{
|
||||
char *rev = StrConcat(2, muc, "/parsee");
|
||||
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
||||
XMPPJoinMUC(args->data->jabber, "parsee", rev, false);
|
||||
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
|
||||
|
||||
Free(rev);
|
||||
}
|
||||
|
|
|
|||
19
src/Signal.c
19
src/Signal.c
|
|
@ -6,32 +6,35 @@
|
|||
|
||||
#include <XMPP.h>
|
||||
|
||||
static HttpServer *server = NULL;
|
||||
static ParseeData *data;
|
||||
static pthread_t xmpp_thr;
|
||||
static XMPPComponent *jabber = NULL;
|
||||
|
||||
static void
|
||||
SignalHandler(int signal)
|
||||
{
|
||||
if (server && (signal == SIGTERM || signal == SIGINT))
|
||||
if (data->server && (signal == SIGTERM || signal == SIGINT))
|
||||
{
|
||||
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);
|
||||
Log(LOG_INFO, "Stopping server...");
|
||||
HttpServerStop(server);
|
||||
HttpServerStop(data->server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ParseeInitialiseSignals(HttpServer *s, pthread_t xmpp, XMPPComponent *j)
|
||||
ParseeInitialiseSignals(ParseeData *d, pthread_t xmpp)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
server = s;
|
||||
data = d;
|
||||
xmpp_thr = xmpp;
|
||||
jabber = j;
|
||||
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_handler = SignalHandler;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ CreateStanzaBuilder(char *from, char *to, char *id)
|
|||
|
||||
builder->replying_to_stanza = NULL;
|
||||
builder->replying_to_sender = NULL;
|
||||
builder->editing = NULL;
|
||||
builder->type = NULL;
|
||||
builder->body = NULL;
|
||||
builder->oob = NULL;
|
||||
|
|
@ -184,7 +185,7 @@ ExportStanza(StanzaBuilder *builder)
|
|||
builder->replying_to_sender &&
|
||||
builder->body)
|
||||
{
|
||||
int off = ParseeFindDatastart(builder->body);
|
||||
int off = ParseeFindDatastartU(builder->body);
|
||||
char *ostr = StrInt(off);
|
||||
XMLElement *reply = XMLCreateTag("reply");
|
||||
XMLElement *fallback = XMLCreateTag("fallback");
|
||||
|
|
@ -227,22 +228,21 @@ ExportStanza(StanzaBuilder *builder)
|
|||
}
|
||||
|
||||
void
|
||||
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber)
|
||||
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber, size_t max)
|
||||
{
|
||||
XMLElement *elem;
|
||||
if (!builder || !jabber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!max)
|
||||
{
|
||||
max = 10000; /* XMPP recommended limit */
|
||||
}
|
||||
|
||||
elem = ExportStanza(builder);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, elem);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
|
||||
XMPPSendStanza(jabber, elem, max);
|
||||
XMLFreeElement(elem);
|
||||
return;
|
||||
}
|
||||
|
||||
StanzaBuilder *
|
||||
|
|
@ -258,7 +258,6 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
|
|||
{
|
||||
XMLElement *parsee_version, *ver_elem;
|
||||
XMLElement *parsee_link, *link_elem;
|
||||
XMLElement *parsee_text, *text_elem;
|
||||
XMLElement *parsee_event, *event_elem;
|
||||
XMLElement *parsee_json, *json_elem;
|
||||
char *event_id = GrabString(e, 1, "event_id");
|
||||
|
|
@ -285,16 +284,6 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
|
|||
XMLAddChild(parsee_link, link_elem);
|
||||
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)
|
||||
{
|
||||
parsee_event = XMLCreateTag("event-id");
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
char **
|
||||
StrSplitLines(char *text)
|
||||
|
|
@ -117,7 +116,7 @@ StrFullRect(char **split)
|
|||
char
|
||||
StrGet(StringRect *rect, int line, int col)
|
||||
{
|
||||
int actual_line, actual_col;
|
||||
size_t actual_line, actual_col;
|
||||
char *linep;
|
||||
if (!rect || !rect->source_lines)
|
||||
{
|
||||
|
|
@ -150,7 +149,7 @@ StrGet(StringRect *rect, int line, int col)
|
|||
size_t
|
||||
StrViewChars(StringRect rect, int line)
|
||||
{
|
||||
int actual_line;
|
||||
size_t actual_line;
|
||||
char *linep;
|
||||
if (!rect.source_lines)
|
||||
{
|
||||
|
|
@ -174,7 +173,7 @@ StrViewChars(StringRect rect, int line)
|
|||
StringRect
|
||||
StrGetl(StringRect *rect, int line, bool extend)
|
||||
{
|
||||
int actual_line;
|
||||
size_t actual_line;
|
||||
StringRect ret;
|
||||
if (!rect->source_lines)
|
||||
{
|
||||
|
|
@ -204,7 +203,7 @@ StrGetl(StringRect *rect, int line, bool extend)
|
|||
StringRect
|
||||
StrShift(StringRect rect, int n)
|
||||
{
|
||||
int new = rect.start_char + n;
|
||||
size_t new = rect.start_char + n;
|
||||
if (new > rect.end_char)
|
||||
{
|
||||
new = rect.end_char;
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@
|
|||
Stream *
|
||||
StrStreamReaderN(char *buffer, int n)
|
||||
{
|
||||
if (!buffer)
|
||||
if (!buffer || n < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return StreamFile(fmemopen(buffer, n ? n : strlen(buffer), "rb"));
|
||||
return StreamFile(fmemopen(buffer, n ? (size_t) n : strlen(buffer), "rb"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ static ssize_t
|
|||
ReadStreamWriter(void *coop, void *to, size_t n)
|
||||
{
|
||||
/* Reading from a stream writer is silly. */
|
||||
(void) coop;
|
||||
(void) to;
|
||||
(void) n;
|
||||
return 0;
|
||||
}
|
||||
static ssize_t
|
||||
|
|
@ -33,6 +36,9 @@ static off_t
|
|||
SeekStreamWriter(void *coop, off_t mag, int sgn)
|
||||
{
|
||||
/* TODO: Seeking would be useful, though not supported yet. */
|
||||
(void) coop;
|
||||
(void) mag;
|
||||
(void) sgn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -40,10 +46,11 @@ static int
|
|||
CloseStreamWriter(void *coop)
|
||||
{
|
||||
/* Nothing to free as of now. */
|
||||
(void) coop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const static IoFunctions Functions = {
|
||||
static const IoFunctions Functions = {
|
||||
.read = ReadStreamWriter,
|
||||
.seek = SeekStreamWriter,
|
||||
.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;
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ DecodeQuote(StringRect rect, size_t *skip)
|
|||
* > four but that's for Nerds
|
||||
* > seasons See, Touhou reference!
|
||||
* concealing!) */
|
||||
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace(ch))
|
||||
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace((int) ch))
|
||||
{
|
||||
shift_by++;
|
||||
}
|
||||
|
|
@ -132,7 +132,7 @@ DecodeSpan(StringRect rect, char del, size_t *skip)
|
|||
{
|
||||
return StrFullRect(NULL);
|
||||
}
|
||||
if (!ret.source_lines && isspace(c))
|
||||
if (!ret.source_lines && isspace((int) c))
|
||||
{
|
||||
return StrFullRect(NULL);
|
||||
}
|
||||
|
|
@ -329,8 +329,8 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
|||
break;
|
||||
case XEP393_MONO:
|
||||
head = XMLCreateTag("code");
|
||||
XMLAddChild(xmlparent, XMLCreateText("`"));
|
||||
XMLAddChild(xmlparent, head);
|
||||
XMLAddChild(head, XMLCreateText("`"));
|
||||
break;
|
||||
case XEP393_SRKE:
|
||||
head = XMLCreateTag("s");
|
||||
|
|
@ -372,7 +372,7 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
|||
XMLAddChild(head, XMLCreateText("_"));
|
||||
break;
|
||||
case XEP393_MONO:
|
||||
XMLAddChild(head, XMLCreateText("`"));
|
||||
XMLAddChild(xmlparent, XMLCreateText("`"));
|
||||
break;
|
||||
case XEP393_SRKE:
|
||||
XMLAddChild(head, XMLCreateText("~"));
|
||||
|
|
@ -399,21 +399,37 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
|||
char *
|
||||
XEP393ToXMLString(XEP393Element *xepd)
|
||||
{
|
||||
XMLElement *root;
|
||||
XMLElement *root, *act_root;
|
||||
XMLElement *child;
|
||||
|
||||
Stream *writer;
|
||||
char *ret = NULL;
|
||||
size_t i, children;
|
||||
if (!xepd)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
root = XMLCreateTag("span");
|
||||
act_root = root;
|
||||
ShoveXML(xepd, root);
|
||||
|
||||
writer = StrStreamWriter(&ret);
|
||||
XMLEncode(writer, root);
|
||||
XMLFreeElement(root);
|
||||
children = ArraySize(root->children);
|
||||
|
||||
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);
|
||||
StreamClose(writer);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
|||
bool flag = false;
|
||||
switch (event->type)
|
||||
{
|
||||
case XML_ERROR:
|
||||
XMLFreeEvent(event);
|
||||
XMLFreeElement(ret);
|
||||
ArrayFree(stack);
|
||||
XMLFreeLexer(lexer);
|
||||
return NULL;
|
||||
case XML_LEXER_STARTELEM:
|
||||
/* Create a new element that will populated. */
|
||||
top = XMLCreateTag(event->element);
|
||||
|
|
@ -114,9 +120,19 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
|||
void
|
||||
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];
|
||||
if (c == '<')
|
||||
|
|
@ -145,6 +161,9 @@ XMLEncodeString(Stream *stream, char *data)
|
|||
continue;
|
||||
}
|
||||
StreamPrintf(stream, "%c", c);
|
||||
/* TODO: Maybe consider Unistrings and encode arbitrary Unicode
|
||||
* codepoints * with special XML. Oughta make it printable, you know?
|
||||
*/
|
||||
}
|
||||
}
|
||||
void
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ static char * XMLPopElement(XMLexer *lexer);
|
|||
static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs);
|
||||
static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs);
|
||||
static XMLEvent * XMLCreateRelax(XMLexer *lexer);
|
||||
static XMLEvent * XMLCreateError(XMLexer *lexer);
|
||||
static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end);
|
||||
static XMLEvent * XMLCreateData(XMLexer *lexer);
|
||||
|
||||
|
|
@ -198,7 +199,9 @@ XMLCrank(XMLexer *lexer)
|
|||
else if (XMLookahead(lexer, "--", false))
|
||||
{
|
||||
/* Throw error */
|
||||
return NULL;
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XML_STATE_PI:
|
||||
|
|
@ -215,6 +218,9 @@ XMLCrank(XMLexer *lexer)
|
|||
if (!attrname)
|
||||
{
|
||||
/* TODO: Throw error */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
XMLPushElement(lexer, attrname);
|
||||
|
||||
|
|
@ -241,7 +247,10 @@ XMLCrank(XMLexer *lexer)
|
|||
}
|
||||
else if (XMLookahead(lexer, "'", true))
|
||||
{
|
||||
while (true);
|
||||
//while (true); uh oh
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XML_STATE_ATTRTAIL:
|
||||
|
|
@ -250,6 +259,8 @@ XMLCrank(XMLexer *lexer)
|
|||
if (!XMLookahead(lexer, ">", true))
|
||||
{
|
||||
/* TODO: Throw error. */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
lexer->state = XML_STATE_NONE;
|
||||
|
|
@ -258,6 +269,8 @@ XMLCrank(XMLexer *lexer)
|
|||
break;
|
||||
default:
|
||||
/* TODO */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
/* TODO: Crank our XML parser. */
|
||||
|
|
@ -295,7 +308,7 @@ static bool
|
|||
XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
||||
{
|
||||
int *stack;
|
||||
size_t top, i;
|
||||
size_t top, i, len;
|
||||
ssize_t ntop;
|
||||
bool ret = false;
|
||||
if (!lexer || !str)
|
||||
|
|
@ -304,9 +317,10 @@ XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
|||
}
|
||||
|
||||
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];
|
||||
int getc = XMLGetc(lexer);
|
||||
|
|
@ -336,8 +350,8 @@ seekback:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define IsNamestart(c) ((c == ':') || isalpha(c) || (c == '_'))
|
||||
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit(c))
|
||||
#define IsNamestart(c) ((c == ':') || isalpha((int) c) || (c == '_'))
|
||||
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit((int) c))
|
||||
static char *
|
||||
XMLParseName(XMLexer *lexer)
|
||||
{
|
||||
|
|
@ -582,6 +596,8 @@ XMLCreateEnd(XMLexer *lexer, char *end)
|
|||
event->col = 0;
|
||||
event->offset = 0;
|
||||
|
||||
(void) lexer;
|
||||
|
||||
return event;
|
||||
}
|
||||
static XMLEvent *
|
||||
|
|
@ -690,6 +706,26 @@ XMLCreateData(XMLexer *lexer)
|
|||
return event;
|
||||
}
|
||||
static XMLEvent *
|
||||
XMLCreateError(XMLexer *lexer)
|
||||
{
|
||||
XMLEvent *event = Malloc(sizeof(*event));
|
||||
size_t elements = ArraySize(lexer->data.elements);
|
||||
|
||||
event->type = XML_ERROR;
|
||||
event->element = elements ?
|
||||
StrDuplicate(ArrayGet(lexer->data.elements, elements - 1)) :
|
||||
NULL;
|
||||
event->attrs = NULL;
|
||||
event->data = NULL;
|
||||
|
||||
/* TODO */
|
||||
event->line = 0;
|
||||
event->col = 0;
|
||||
event->offset = 0;
|
||||
|
||||
return event;
|
||||
}
|
||||
static XMLEvent *
|
||||
XMLCreateRelax(XMLexer *lexer)
|
||||
{
|
||||
XMLEvent *event = Malloc(sizeof(*event));
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
|
|
@ -18,7 +19,7 @@
|
|||
#define DEFAULT_PROSODY_PORT 5347
|
||||
|
||||
XMPPComponent *
|
||||
XMPPInitialiseCompStream(char *host, int port)
|
||||
XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||
{
|
||||
int sd = -1;
|
||||
struct addrinfo hints, *res, *res0;
|
||||
|
|
@ -27,12 +28,17 @@ XMPPInitialiseCompStream(char *host, int port)
|
|||
Stream *stream;
|
||||
XMPPComponent *comp;
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = host;
|
||||
}
|
||||
|
||||
snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
error = getaddrinfo(host, serv, &hints, &res0);
|
||||
error = getaddrinfo(addr, serv, &hints, &res0);
|
||||
if (error)
|
||||
{
|
||||
const char *error_str = gai_strerror(error);
|
||||
|
|
@ -55,6 +61,10 @@ XMPPInitialiseCompStream(char *host, int port)
|
|||
|
||||
if (connect(sd, res->ai_addr, res->ai_addrlen) < 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': %s", __func__,
|
||||
host, strerror(errno)
|
||||
);
|
||||
close(sd);
|
||||
sd = -1;
|
||||
continue;
|
||||
|
|
@ -65,6 +75,10 @@ XMPPInitialiseCompStream(char *host, int port)
|
|||
|
||||
if (sd < 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': no socket available", __func__,
|
||||
host
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
freeaddrinfo(res0);
|
||||
|
|
@ -72,6 +86,10 @@ XMPPInitialiseCompStream(char *host, int port)
|
|||
stream = StreamFd(sd);
|
||||
if (!stream)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': %s", __func__,
|
||||
host, "couldn't create a Cytoplasm stream"
|
||||
);
|
||||
close(sd);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -134,7 +152,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (ev->type != XML_LEXER_STARTELEM ||
|
||||
if (!ev || ev->type != XML_LEXER_STARTELEM ||
|
||||
!StrEquals(ev->element, "stream:stream"))
|
||||
{
|
||||
Log(LOG_ERR, "Excepted stream:stream element.");
|
||||
|
|
@ -159,12 +177,54 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (ev->type != XML_LEXER_ELEM ||
|
||||
if (!ev || ev->type != XML_LEXER_ELEM ||
|
||||
!StrEquals(ev->element, "handshake"))
|
||||
{
|
||||
Log(LOG_DEBUG, "type=%d elem='%s'", ev->type, ev->element);
|
||||
Log(LOG_ERR, "Excepted empty handshake reply, got nonsense.");
|
||||
Log(LOG_ERR, "Another service (possibly Parsee) may have taken over.");
|
||||
while ((ev = XMLCrank(sax)))
|
||||
{
|
||||
char *key, *val;
|
||||
switch (ev->type)
|
||||
{
|
||||
case XML_LEXER_STARTELEM:
|
||||
Log(LOG_DEBUG, "<%s>", ev->element);
|
||||
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
/* TODO: Log out attributes a little better */
|
||||
while (HashMapIterate(ev->attrs, &key, (void **) &val))
|
||||
{
|
||||
Log(LOG_DEBUG, "(%s=%s)", key, val);
|
||||
}
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
break;
|
||||
case XML_LEXER_ELEM:
|
||||
Log(LOG_DEBUG, "<%s/>", ev->element);
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
/* TODO: Log out attributes a little better */
|
||||
while (HashMapIterate(ev->attrs, &key, (void **) &val))
|
||||
{
|
||||
Log(LOG_DEBUG, "(%s=%s)", key, val);
|
||||
}
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
break;
|
||||
case XML_LEXER_ENDELEM:
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
Log(LOG_DEBUG, "</%s>", ev->element);
|
||||
break;
|
||||
case XML_LEXER_DATA:
|
||||
Log(LOG_DEBUG, "%s", ev->data);
|
||||
break;
|
||||
}
|
||||
XMLFreeEvent(ev);
|
||||
}
|
||||
LogConfigIndentSet(LogConfigGlobal(), 0);
|
||||
Log(LOG_ERR, "");
|
||||
Log(LOG_ERR, "Simply jealous of that other service...");
|
||||
Free(stream_id);
|
||||
|
|
@ -213,3 +273,35 @@ XMPPEndCompStream(XMPPComponent *comp)
|
|||
Free(comp->host);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
#include "XMPPThread/internal.h"
|
||||
|
||||
bool
|
||||
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||
{
|
||||
|
|
@ -18,8 +20,6 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
|
||||
iq_query = XMLCreateTag("iq");
|
||||
query = XMLCreateTag("query");
|
||||
|
||||
|
|
@ -35,8 +35,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
|
||||
{
|
||||
XMLElement *identity;
|
||||
XMLEncode(jabber->stream, iq_query);
|
||||
StreamFlush(jabber->stream);
|
||||
XMPPSendStanza(jabber, iq_query, 10000);
|
||||
XMLFreeElement(iq_query);
|
||||
|
||||
/* Except an IQ reply. 10 seconds of timeout is pretty
|
||||
|
|
@ -45,8 +44,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
Free(uuid);
|
||||
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
||||
{
|
||||
Log(LOG_ERR, "Didn't receive an <iq> stanza");
|
||||
XMLFreeElement(iq_query);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
return false;
|
||||
}
|
||||
query = XMLookForUnique(iq_query, "query");
|
||||
|
|
@ -57,7 +56,11 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
"conference"))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +76,6 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
XMLFreeElement(iq_query);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +118,6 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
|
|||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
stanza = XMLCreateTag("message");
|
||||
XMLAddAttr(stanza, "id", (identifier = StrRandom(32)));
|
||||
XMLAddAttr(stanza, "from", from);
|
||||
|
|
@ -160,44 +161,68 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
|
|||
}
|
||||
XMLAddChild(stanza, x);
|
||||
}
|
||||
XMLEncode(jabber->stream, stanza);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, stanza, 10000);
|
||||
|
||||
XMLFreeElement(stanza);
|
||||
Free(identifier);
|
||||
}
|
||||
bool
|
||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, bool care)
|
||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret)
|
||||
{
|
||||
XMLElement *presence, *x, *reply;
|
||||
char *from, *id;
|
||||
XMLElement *presence, *x, *reply, *history, *photo;
|
||||
IQFeatures *features;
|
||||
char *from, *id, *stime = "3600";
|
||||
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");
|
||||
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);
|
||||
XMPPAnnotatePresence(presence);
|
||||
features = LookupJIDFeatures(from);
|
||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
XMPPAnnotatePresence(presence, features);
|
||||
FreeIQFeatures(features);
|
||||
|
||||
XMLEncode(comp->stream, presence);
|
||||
StreamFlush(comp->stream);
|
||||
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);
|
||||
|
||||
pthread_mutex_unlock(&comp->write_lock);
|
||||
|
||||
if (care && (reply = ParseeAwaitStanza(id, 500)))
|
||||
if (ret && (reply = ParseeAwaitStanza(id, 500)))
|
||||
{
|
||||
bool exit_code = true;
|
||||
|
||||
|
|
@ -217,14 +242,13 @@ void
|
|||
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||
{
|
||||
XMLElement *presence;
|
||||
IQFeatures *features;
|
||||
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);
|
||||
|
|
@ -240,14 +264,20 @@ XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
|||
XMLAddChild(presence, status);
|
||||
}
|
||||
|
||||
XMPPAnnotatePresence(presence);
|
||||
features = LookupJIDFeatures(from);
|
||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
XMPPAnnotatePresence(presence, features);
|
||||
FreeIQFeatures(features);
|
||||
|
||||
XMLEncode(comp->stream, presence);
|
||||
StreamFlush(comp->stream);
|
||||
|
||||
XMPPSendStanza(comp, presence, 10000);
|
||||
|
||||
XMLFreeElement(presence);
|
||||
Free(from);
|
||||
Free(id);
|
||||
|
||||
pthread_mutex_unlock(&comp->write_lock);
|
||||
}
|
||||
|
|
|
|||
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 <XML.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
void
|
||||
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
||||
|
|
@ -66,13 +68,9 @@ XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
|||
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&comp->write_lock);
|
||||
XMLEncode(comp->stream, message);
|
||||
StreamFlush(comp->stream);
|
||||
XMPPSendStanza(comp, message, 10000);
|
||||
XMLFreeElement(message);
|
||||
|
||||
pthread_mutex_unlock(&comp->write_lock);
|
||||
|
||||
Free(from);
|
||||
Free(ident);
|
||||
}
|
||||
|
|
@ -85,6 +83,8 @@ XMPPIsParseeStanza(XMLElement *stanza)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* TODO: Check if the user is a trustworthy Parsee puppet instead of some
|
||||
* guy sending random stanzas */
|
||||
return !!XMLookForUnique(stanza, "x-parsee");
|
||||
}
|
||||
|
||||
|
|
@ -161,27 +161,31 @@ XMPPGetReply(XMLElement *elem)
|
|||
|
||||
return HashMapGet(rep->attrs, "id");
|
||||
}
|
||||
void
|
||||
XMPPAnnotatePresence(XMLElement *presence)
|
||||
ssize_t
|
||||
XMPPGetReplyOffset(XMLElement *elem)
|
||||
{
|
||||
XMLElement *c;
|
||||
char *ver;
|
||||
if (!presence)
|
||||
if (!elem)
|
||||
{
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ver = XMPPGenerateVer();
|
||||
c = XMLCreateTag("c");
|
||||
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
||||
XMLAddAttr(c, "hash", "sha-1");
|
||||
XMLAddAttr(c, "node", REPOSITORY);
|
||||
XMLAddAttr(c, "ver", ver);
|
||||
|
||||
Free(ver);
|
||||
XMLAddChild(presence, c);
|
||||
for (size_t i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
XMLElement *child = ArrayGet(elem->children, i);
|
||||
char *xmlns = HashMapGet(child->attrs, "xmlns");
|
||||
char *xfor = HashMapGet(child->attrs, "for");
|
||||
if (StrEquals(child->name, "fallback") &&
|
||||
StrEquals(xmlns, "urn:xmpp:feature-fallback:0") &&
|
||||
StrEquals(xfor, "urn:xmpp:reply:0"))
|
||||
{
|
||||
XMLElement *body = XMLookForUnique(child, "body");
|
||||
if (body && HashMapGet(body->attrs, "end"))
|
||||
{
|
||||
return strtol(HashMapGet(body->attrs, "end"), NULL, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *
|
||||
XMPPGetModeration(XMLElement *stanza)
|
||||
{
|
||||
|
|
@ -266,10 +270,7 @@ XMPPSendDisco(ParseeData *data, char *from, char *to)
|
|||
XMLAddChild(iq, query);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, iq, 10000);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
ret = ParseeAwaitStanza(identifier, 1.25 SECONDS);
|
||||
|
|
|
|||
|
|
@ -34,7 +34,17 @@ struct XMPPCommandManager {
|
|||
HashMap *sessions;
|
||||
|
||||
void *cookie;
|
||||
|
||||
XMPPCmdFilter filter;
|
||||
};
|
||||
static bool
|
||||
XMPPDefaultFilter(XMPPCommandManager *manager, char *id, XMLElement *stanza)
|
||||
{
|
||||
(void) manager;
|
||||
(void) stanza;
|
||||
(void) id;
|
||||
return true;
|
||||
}
|
||||
static void
|
||||
XMPPDestroySession(XMPPSession *session)
|
||||
{
|
||||
|
|
@ -118,6 +128,7 @@ XMPPCreateManager(void *cookie)
|
|||
ret->commands = HashMapCreate();
|
||||
ret->sessions = HashMapCreate();
|
||||
ret->cookie = cookie;
|
||||
ret->filter = XMPPDefaultFilter;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -161,12 +172,12 @@ XMPPFreeManager(XMPPCommandManager *manager)
|
|||
Free(manager);
|
||||
}
|
||||
void
|
||||
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
|
||||
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s)
|
||||
{
|
||||
char *node_name;
|
||||
XMPPCommand *val;
|
||||
XMLElement *item;
|
||||
if (!m || !p || !jid)
|
||||
if (!m || !p || !jid || !s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -174,11 +185,14 @@ XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
|
|||
pthread_mutex_lock(&m->lock);
|
||||
while (HashMapIterate(m->commands, &node_name, (void **) &val))
|
||||
{
|
||||
item = XMLCreateTag("item");
|
||||
XMLAddAttr(item, "jid", jid);
|
||||
XMLAddAttr(item, "node", node_name);
|
||||
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
|
||||
XMLAddChild(p, item);
|
||||
if (m->filter(m, node_name, s))
|
||||
{
|
||||
item = XMLCreateTag("item");
|
||||
XMLAddAttr(item, "jid", jid);
|
||||
XMLAddAttr(item, "node", node_name);
|
||||
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
|
||||
XMLAddChild(p, item);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&m->lock);
|
||||
}
|
||||
|
|
@ -206,6 +220,16 @@ XMPPVerifySession(XMPPCommandManager *mgr, char *s_id, char *from, char *to)
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter)
|
||||
{
|
||||
if (!manager || !filter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
manager->filter = filter;
|
||||
}
|
||||
bool
|
||||
XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||
{
|
||||
|
|
@ -242,7 +266,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
/* This is an execution. */
|
||||
cmd = HashMapGet(m->commands, node);
|
||||
|
||||
if (!cmd)
|
||||
if (!cmd || !m->filter(m, node, stanza))
|
||||
{
|
||||
/* TODO: Set an error note */
|
||||
goto end;
|
||||
|
|
@ -274,10 +298,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
x = XMPPFormifyCommand(m, cmd, from);
|
||||
XMLAddChild(command_xml, x);
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
goto end;
|
||||
|
|
@ -305,10 +326,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
XMPPExecuteCommand(m, cmd, from, command_xml, NULL);
|
||||
XMLAddChild(iq, command_xml);
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
InvalidateSession(m, session_id);
|
||||
|
|
@ -348,10 +366,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
XMPPExecuteCommand(m, cmd, from, command_xml, x_form);
|
||||
XMLAddChild(iq, command_xml);
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
InvalidateSession(m, session_given);
|
||||
|
|
|
|||
|
|
@ -15,19 +15,11 @@ void
|
|||
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
size_t i;
|
||||
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;
|
||||
}
|
||||
Free(trimmed);
|
||||
x = XMLCreateTag("x");
|
||||
title = XMLCreateTag("title");
|
||||
|
||||
|
|
@ -58,4 +50,7 @@ AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
|||
DbUnlock(data->db, ref);
|
||||
}
|
||||
XMLAddChild(out, x);
|
||||
|
||||
(void) form;
|
||||
(void) from;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,4 +29,6 @@ CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
|
|||
ParseeCleanup(data);
|
||||
/* TODO: Cleanup old sessions? */
|
||||
SetNote("info", "Parsee data was sucessfully cleant up.");
|
||||
|
||||
(void) form;
|
||||
}
|
||||
|
|
|
|||
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);
|
||||
}
|
||||
|
||||
(void) form;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
void
|
||||
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
size_t alloc = MemoryAllocated();
|
||||
size_t kb = alloc >> 10;
|
||||
|
|
@ -26,13 +25,6 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
|||
XMLElement *x;
|
||||
XMLElement *title, *txt;
|
||||
|
||||
if (!ParseeIsAdmin(data, trimmed))
|
||||
{
|
||||
SetNote("error", "User is not authorised to execute command.");
|
||||
|
||||
Free(trimmed);
|
||||
return;
|
||||
}
|
||||
Free(trimmed);
|
||||
x = XMLCreateTag("x");
|
||||
title = XMLCreateTag("title");
|
||||
|
|
@ -75,4 +67,7 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
|||
EndItem();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
@ -121,7 +121,7 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
|
|||
bool
|
||||
ServerHasXEP421(ParseeData *data, char *from)
|
||||
{
|
||||
char *server = NULL, *parsee;
|
||||
char *server = NULL, *postserv, *parsee;
|
||||
XMLElement *disco;
|
||||
bool ret = false;
|
||||
if (!data || !from)
|
||||
|
|
@ -140,9 +140,10 @@ ServerHasXEP421(ParseeData *data, char *from)
|
|||
}
|
||||
|
||||
server = StrDuplicate(server);
|
||||
if (strchr(server, '/'))
|
||||
postserv = server ? strchr(server, '/') : NULL;
|
||||
if (postserv)
|
||||
{
|
||||
*(strchr(server, '/')) = '\0';
|
||||
*postserv = '\0';
|
||||
}
|
||||
|
||||
parsee = ParseeJID(data);
|
||||
|
|
@ -226,7 +227,6 @@ ParseeGetBridgedUserI(ParseeData *data, XMLElement *stanza, char *force)
|
|||
{
|
||||
ParseePushOIDTable(xmpp_from, occ_id);
|
||||
}
|
||||
Log(LOG_DEBUG, "Trying Occ ID for %s{%s}", xmpp_from, occ_id);
|
||||
}
|
||||
|
||||
if (!occ_id)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <XMPPCommand.h>
|
||||
|
|
@ -12,33 +13,86 @@
|
|||
|
||||
#include "XMPPThread/internal.h"
|
||||
|
||||
IQFeatures *
|
||||
CreateIQFeatures(void)
|
||||
{
|
||||
IQFeatures *ret = Malloc(sizeof(*ret));
|
||||
|
||||
ret->identity = ArrayCreate();
|
||||
ret->adverts = ArrayCreate();
|
||||
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
FreeIQFeatures(IQFeatures *features)
|
||||
{
|
||||
size_t i;
|
||||
if (!features)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||
{
|
||||
Free(ArrayGet(features->adverts, i));
|
||||
}
|
||||
ArrayFree(features->adverts);
|
||||
|
||||
for (i = 0; i < ArraySize(features->identity); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||
|
||||
Free(identity->category);
|
||||
Free(identity->type);
|
||||
Free(identity->lang);
|
||||
Free(identity->name);
|
||||
|
||||
Free(identity);
|
||||
}
|
||||
ArrayFree(features->identity);
|
||||
|
||||
Free(features);
|
||||
}
|
||||
|
||||
void
|
||||
AdvertiseIQFeature(IQFeatures *f, char *feature)
|
||||
{
|
||||
if (!f || !feature)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayAdd(f->adverts, StrDuplicate(feature));
|
||||
}
|
||||
void
|
||||
AddIQIdentity(IQFeatures *f, char *cat, char *lang, char *type, char *name)
|
||||
{
|
||||
XMPPIdentity *identity;
|
||||
if (!f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
identity = Malloc(sizeof(*identity));
|
||||
identity->category = StrDuplicate(cat);
|
||||
identity->type = StrDuplicate(type);
|
||||
identity->lang = StrDuplicate(lang);
|
||||
identity->name = StrDuplicate(name);
|
||||
ArrayAdd(f->identity, identity);
|
||||
}
|
||||
/* Generates a SHA-256 hash of the ver field. */
|
||||
char *
|
||||
XMPPGenerateVer(void)
|
||||
XMPPGenerateVer(IQFeatures *features)
|
||||
{
|
||||
char *S = NULL;
|
||||
unsigned char *Sha = NULL;
|
||||
Array *identities = ArrayCreate();
|
||||
Array *features = ArrayCreate();
|
||||
size_t i;
|
||||
|
||||
/* Initialise identity table, to be sorted */
|
||||
#define IdentitySimple(cat, Type, Name) { \
|
||||
XMPPIdentity *id = Malloc(sizeof(*id)); \
|
||||
id->category = cat; \
|
||||
id->lang = NULL; \
|
||||
id->type = Type; \
|
||||
id->name = Name; \
|
||||
ArrayAdd(identities, id); }
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) ArrayAdd(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
ArraySort(identities, IdentitySort);
|
||||
for (i = 0; i < ArraySize(identities); i++)
|
||||
ArraySort(features->identity, IdentitySort);
|
||||
for (i = 0; i < ArraySize(features->identity); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||
char *id_chunk = StrConcat(7,
|
||||
identity->category, "/",
|
||||
identity->type, "/",
|
||||
|
|
@ -50,10 +104,10 @@ XMPPGenerateVer(void)
|
|||
Free(id_chunk);
|
||||
}
|
||||
|
||||
ArraySort(features, ((int (*) (void *, void *)) ICollate));
|
||||
for (i = 0; i < ArraySize(features); i++)
|
||||
ArraySort(features->adverts, ((int (*) (void *, void *)) ICollate));
|
||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||
{
|
||||
char *feature = ArrayGet(features, i);
|
||||
char *feature = ArrayGet(features->adverts, i);
|
||||
char *tmp = S;
|
||||
S = StrConcat(3, S, feature, "<");
|
||||
Free(tmp);
|
||||
|
|
@ -64,16 +118,64 @@ XMPPGenerateVer(void)
|
|||
S = Base64Encode((const char *) Sha, 20);
|
||||
Free(Sha);
|
||||
|
||||
ArrayFree(features);
|
||||
for (i = 0; i < ArraySize(identities); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||
/* We don't have to do anything here. */
|
||||
Free(identity);
|
||||
}
|
||||
ArrayFree(identities);
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features)
|
||||
{
|
||||
XMLElement *c;
|
||||
char *ver;
|
||||
if (!presence || !features)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ver = XMPPGenerateVer(features);
|
||||
c = XMLCreateTag("c");
|
||||
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
||||
XMLAddAttr(c, "hash", "sha-1");
|
||||
XMLAddAttr(c, "node", REPOSITORY);
|
||||
XMLAddAttr(c, "ver", ver);
|
||||
|
||||
Free(ver);
|
||||
XMLAddChild(presence, c);
|
||||
}
|
||||
|
||||
|
||||
IQFeatures *
|
||||
LookupJIDFeatures(char *jid)
|
||||
{
|
||||
IQFeatures *features;
|
||||
if (!jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
features = CreateIQFeatures();
|
||||
|
||||
if (*jid == '#')
|
||||
{
|
||||
/* This is a MUC. As such, we need to advertise MUCs */
|
||||
#define ID(...) AddIQIdentity(features, __VA_ARGS__)
|
||||
#define AD(var) AdvertiseIQFeature(features, var)
|
||||
ID("gateway", NULL, "matrix", "Parsee MUC gateway");
|
||||
ID("conference", NULL, "text", "Parsee MUC gateway");
|
||||
ID("component", NULL, "generic", "Parsee component");
|
||||
|
||||
AD("http://jabber.org/protocol/muc");
|
||||
#undef AD
|
||||
#undef ID
|
||||
}
|
||||
else
|
||||
{
|
||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
#include <XMPP.h>
|
||||
#include <XML.h>
|
||||
|
||||
#define PUBSUB "http://jabber.org/protocol/pubsub"
|
||||
|
||||
XMLElement *
|
||||
CreatePubsubRequest(char *from, char *to, char *node)
|
||||
{
|
||||
|
|
@ -22,7 +24,7 @@ CreatePubsubRequest(char *from, char *to, char *node)
|
|||
XMLAddAttr(iq_req, "type", "set");
|
||||
|
||||
pubsub = XMLCreateTag("pubsub");
|
||||
XMLAddAttr(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
|
||||
XMLAddAttr(pubsub, "xmlns", PUBSUB);
|
||||
XMLAddChild(iq_req, pubsub);
|
||||
|
||||
sub = XMLCreateTag("subscribe");
|
||||
|
|
@ -34,12 +36,40 @@ CreatePubsubRequest(char *from, char *to, char *node)
|
|||
return iq_req;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsPubsubRequest(XMLElement *stanza)
|
||||
{
|
||||
char *type = HashMapGet(stanza ? stanza->attrs : NULL, "type");
|
||||
XMLElement *pubsub;
|
||||
if (!stanza)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!StrEquals(stanza->name, "iq") ||
|
||||
!StrEquals(type, "set"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pubsub = XMLookForTKV(stanza, "pubsub", "xmlns", PUBSUB);
|
||||
if (!pubsub)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return XMLookForUnique(pubsub, "subscribe");
|
||||
}
|
||||
|
||||
|
||||
struct PEPManager {
|
||||
pthread_mutex_t lock;
|
||||
|
||||
ParseeData *data;
|
||||
HashMap *node_table;
|
||||
|
||||
HashMap *followers;
|
||||
|
||||
void *cookie;
|
||||
};
|
||||
|
||||
|
|
@ -56,6 +86,7 @@ CreatePEPManager(ParseeData *data, void *cookie)
|
|||
ret->cookie = cookie;
|
||||
ret->data = data;
|
||||
ret->node_table = HashMapCreate();
|
||||
ret->followers = HashMapCreate();
|
||||
pthread_mutex_init(&ret->lock, NULL);
|
||||
|
||||
return ret;
|
||||
|
|
@ -68,29 +99,32 @@ PEPManagerCookie(PEPManager *manager)
|
|||
void
|
||||
PEPManagerAddEvent(PEPManager *manager, char *node, PEPEvent event)
|
||||
{
|
||||
PEPEvent *indirect;
|
||||
if (!manager || !node || !event)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
indirect = Malloc(sizeof(event));
|
||||
*indirect = event;
|
||||
pthread_mutex_lock(&manager->lock);
|
||||
HashMapSet(manager->node_table, node, event);
|
||||
HashMapSet(manager->node_table, node, indirect);
|
||||
pthread_mutex_unlock(&manager->lock);
|
||||
}
|
||||
|
||||
static bool
|
||||
PEPManagerHandleEvent(PEPManager *manager, XMLElement *stanza)
|
||||
{
|
||||
PEPEvent call = NULL;
|
||||
XMLElement *event, *ps, *ev;
|
||||
PEPEvent *call = NULL;
|
||||
XMLElement *event = NULL, *ps = NULL, *ev = NULL;
|
||||
size_t i;
|
||||
if (!manager || !stanza)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#define PEP_NS "http://jabber.org/protocol/pubsub"
|
||||
if (!(ps = XMLookForTKV(stanza, "pubsub", "xmlns", PEP_NS)) &&
|
||||
!(ev = XMLookForTKV(stanza, "event", "xmlns", PEP_NS "#event")))
|
||||
|
||||
if (!(ps = XMLookForTKV(stanza, "pubsub", "xmlns", PUBSUB)) &&
|
||||
!(ev = XMLookForTKV(stanza, "event", "xmlns", PUBSUB "#event")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -101,7 +135,7 @@ PEPManagerHandleEvent(PEPManager *manager, XMLElement *stanza)
|
|||
XMLElement *items = ArrayGet(event->children, i);
|
||||
char *node = HashMapGet(items->attrs, "node");
|
||||
|
||||
if ((call = HashMapGet(manager->node_table, node)))
|
||||
if ((call = HashMapGet(manager->node_table, node)) && *call)
|
||||
{
|
||||
size_t j;
|
||||
/* Use the callback over all items */
|
||||
|
|
@ -109,13 +143,13 @@ PEPManagerHandleEvent(PEPManager *manager, XMLElement *stanza)
|
|||
{
|
||||
for (j = 0; j < ArraySize(items->children); j++)
|
||||
{
|
||||
call(manager, stanza, ArrayGet(items->children, j));
|
||||
(*call)(manager, stanza, ArrayGet(items->children, j));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ... or over "items" specifically. */
|
||||
call(manager, stanza, items);
|
||||
(*call)(manager, stanza, items);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -132,6 +166,11 @@ PEPManagerHandle(PEPManager *manager, XMLElement *stanza)
|
|||
}
|
||||
|
||||
/* Check if it is a PEP stanza */
|
||||
if (IsPubsubRequest(stanza))
|
||||
{
|
||||
Log(LOG_DEBUG, "UNIMPLEMENTED PUBSUB SUBSCRIPTION");
|
||||
/* TODO */
|
||||
}
|
||||
if (PEPManagerHandleEvent(manager, stanza))
|
||||
{
|
||||
return true;
|
||||
|
|
@ -142,12 +181,21 @@ PEPManagerHandle(PEPManager *manager, XMLElement *stanza)
|
|||
void
|
||||
DestroyPEPManager(PEPManager *manager)
|
||||
{
|
||||
char *key;
|
||||
PEPEvent *val;
|
||||
if (!manager)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_destroy(&manager->lock);
|
||||
while (HashMapIterate(manager->node_table, &key, (void **) &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(manager->node_table);
|
||||
|
||||
HashMapFree(manager->followers);
|
||||
|
||||
Free(manager);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,11 +72,7 @@ PEPAvatarEvent(PEPManager *m, XMLElement *stanza, XMLElement *item)
|
|||
char *url = HashMapGet(item->attrs, "url");
|
||||
XMLElement *request = CreateAvatarRequest(from, to, id);
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, request);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
|
||||
XMPPSendStanza(jabber, request, args->config->max_stanza_size);
|
||||
XMLFreeElement(request);
|
||||
|
||||
(void) url; /* TODO */
|
||||
|
|
|
|||
|
|
@ -113,9 +113,7 @@ PEPVCardEvent(PEPManager *m, XMLElement *stanza, XMLElement *item)
|
|||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, reply);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, reply, data->config->max_stanza_size);
|
||||
XMLFreeElement(reply);
|
||||
(void) item;
|
||||
}
|
||||
|
|
|
|||
144
src/XMPPThread/PresenceSub.c
Normal file
144
src/XMPPThread/PresenceSub.c
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
#include "XMPPThread/internal.h"
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Base64.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Db.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static char *
|
||||
SubscriptionHash(char *from, char *to)
|
||||
{
|
||||
uint8_t *sum;
|
||||
char *hash;
|
||||
size_t len;
|
||||
|
||||
len = strlen(from) + 1 + strlen(to);
|
||||
sum = Malloc(len);
|
||||
memset(sum, 0x00, len);
|
||||
memcpy(&sum[0], from, strlen(from));
|
||||
memcpy(&sum[strlen(from) + 1], to, strlen(to));
|
||||
|
||||
hash = Base64Encode((const char *) sum, len);
|
||||
Free(sum);
|
||||
|
||||
return hash;
|
||||
}
|
||||
static void
|
||||
DecodeSubscription(ParseeData *data, char *hash, char **from, char **to)
|
||||
{
|
||||
char *sum;
|
||||
if (!data || !hash || !from || !to)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
sum = Base64Decode(hash, strlen(hash));
|
||||
*from = StrDuplicate(sum);
|
||||
*to = StrDuplicate(sum + strlen(sum) + 1);
|
||||
Free(sum);
|
||||
}
|
||||
|
||||
void
|
||||
AddPresenceSubscriber(ParseeData *data, char *from, char *to)
|
||||
{
|
||||
Db *database;
|
||||
DbRef *ref;
|
||||
char *hash;
|
||||
if (!data || !from || !to)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
database = data->db;
|
||||
hash = SubscriptionHash(from, to);
|
||||
ref = DbCreate(database, 2, "subs", hash);
|
||||
if (!ref)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
HashMapSet(DbJson(ref), "from", JsonValueString(from));
|
||||
HashMapSet(DbJson(ref), "to", JsonValueString(to));
|
||||
/* I don't think we need more information right now */
|
||||
|
||||
end:
|
||||
DbUnlock(database, ref);
|
||||
Free(hash);
|
||||
}
|
||||
|
||||
bool
|
||||
IsSubscribed(ParseeData *data, char *user, char *to)
|
||||
{
|
||||
Db *database;
|
||||
char *hash;
|
||||
bool ret;
|
||||
if (!data || !user || !to)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
database = data->db;
|
||||
hash = SubscriptionHash(user, to);
|
||||
ret = DbExists(database, 2, "subs", hash);
|
||||
Free(hash);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ParseeBroadcastStanza(ParseeData *data, char *from, XMLElement *stanza)
|
||||
{
|
||||
XMPPComponent *jabber = data ? data->jabber : NULL;
|
||||
Array *entries;
|
||||
size_t i;
|
||||
if (!data || !from || !stanza)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Copy our stanza so that we can freely modify it */
|
||||
stanza = XMLCopy(stanza);
|
||||
|
||||
/* Start doing a storm on Mt. Subs. */
|
||||
entries = DbList(data->db, 1, "subs");
|
||||
for (i = 0; i < ArraySize(entries); i++)
|
||||
{
|
||||
char *entry = ArrayGet(entries, i);
|
||||
char *entry_from = NULL, *entry_to = NULL;
|
||||
char *storm_id; /* ooe */
|
||||
XMLElement *sub;
|
||||
|
||||
DecodeSubscription(data, entry, &entry_from, &entry_to);
|
||||
|
||||
if (!StrEquals(entry_to, from))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
Log(LOG_DEBUG,
|
||||
"PRESENCE SYSTEM: "
|
||||
"We should be brotkasting straight to %s (from %s)",
|
||||
entry_from, from
|
||||
);
|
||||
sub = XMLCopy(stanza);
|
||||
XMLAddAttr(sub, "from", from);
|
||||
XMLAddAttr(sub, "to", entry_from);
|
||||
|
||||
/* TODO: Should we store IDs somewhere? */
|
||||
XMLAddAttr(sub, "id", (storm_id = StrRandom(16)));
|
||||
|
||||
XMPPSendStanza(jabber, sub, data->config->max_stanza_size);
|
||||
XMLFreeElement(sub);
|
||||
Free(storm_id);
|
||||
end:
|
||||
Free(entry_from);
|
||||
Free(entry_to);
|
||||
}
|
||||
DbListFree(entries);
|
||||
XMLFreeElement(stanza);
|
||||
|
||||
}
|
||||
|
|
@ -4,7 +4,6 @@
|
|||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
|
|
@ -57,6 +56,8 @@ XMPPDispatcher(void *argp)
|
|||
|
||||
if (!stanza)
|
||||
{
|
||||
/* TODO: We shouldn't be busywaiting. Even with a sleep call.
|
||||
*/
|
||||
UtilSleepMillis(10);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -66,9 +67,15 @@ XMPPDispatcher(void *argp)
|
|||
Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name);
|
||||
}
|
||||
|
||||
if (ManageMUCStanza(args->muc, stanza))
|
||||
{
|
||||
XMLFreeElement(stanza);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StrEquals(stanza->name, "presence"))
|
||||
{
|
||||
PresenceStanza(args, stanza);
|
||||
PresenceStanza(args, stanza, thread);
|
||||
XMLFreeElement(stanza);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -127,6 +134,49 @@ ParseeCongestion(void)
|
|||
|
||||
return congestion;
|
||||
}
|
||||
bool
|
||||
XMPPCommandFilter(XMPPCommandManager *m, char *id, XMLElement *stanza)
|
||||
{
|
||||
ParseeData *args = XMPPGetManagerCookie(m);
|
||||
char *trimmed_from;
|
||||
char *from;
|
||||
char *chat_id;
|
||||
bool is_muc;
|
||||
if (!m || !id || !stanza)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
trimmed_from = ParseeTrimJID(from);
|
||||
is_muc = !!(chat_id = ParseeGetFromMUCID(args, trimmed_from));
|
||||
Free(trimmed_from);
|
||||
Free(chat_id);
|
||||
#define XMPP_COMMAND(f,l,n,t,s) \
|
||||
if (StrEquals(n, id)) \
|
||||
{ \
|
||||
if (l == XMPPCMD_ALL) \
|
||||
{ \
|
||||
return true; \
|
||||
} \
|
||||
else if (l == XMPPCMD_MUC) \
|
||||
{ \
|
||||
return is_muc; \
|
||||
} \
|
||||
else if (l == XMPPCMD_ADMINS) \
|
||||
{ \
|
||||
bool is_admin; \
|
||||
trimmed_from = ParseeTrimJID(from); \
|
||||
is_admin = ParseeIsAdmin(args, trimmed_from); \
|
||||
Free(trimmed_from); \
|
||||
return is_admin; \
|
||||
} \
|
||||
}
|
||||
XMPPCOMMANDS
|
||||
#undef XMPP_COMMAND
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *
|
||||
ParseeXMPPThread(void *argp)
|
||||
|
|
@ -134,16 +184,20 @@ ParseeXMPPThread(void *argp)
|
|||
ParseeData *args = argp;
|
||||
XMPPComponent *jabber = args->jabber;
|
||||
XMLElement *stanza = NULL;
|
||||
HashMap *await_table2;
|
||||
size_t i;
|
||||
|
||||
bool error = false;
|
||||
|
||||
/* Initialise the await table */
|
||||
await_table = HashMapCreate();
|
||||
|
||||
/* Initialise the managers, and add all handlers. */
|
||||
info.m = XMPPCreateManager(args);
|
||||
XMPPManagerSetFilter(info.m, XMPPCommandFilter);
|
||||
{
|
||||
XMPPCommand *cmd;
|
||||
#define XMPP_COMMAND(f,n,t,s) \
|
||||
#define XMPP_COMMAND(f,l,n,t,s) \
|
||||
cmd = XMPPBasicCmd( \
|
||||
n, t, f \
|
||||
); \
|
||||
|
|
@ -190,56 +244,95 @@ ParseeXMPPThread(void *argp)
|
|||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
while (!args->halted)
|
||||
{
|
||||
char *id;
|
||||
|
||||
stanza = XMLDecode(jabber->stream, false);
|
||||
if (!stanza)
|
||||
while (true)
|
||||
{
|
||||
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
|
||||
char *id;
|
||||
|
||||
stanza = XMLDecode(jabber->stream, false);
|
||||
if (!stanza)
|
||||
{
|
||||
Log(LOG_DEBUG, "RECEIVED EOF.");
|
||||
/* Try to check if an error is abound */
|
||||
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "RECEIVED EOF.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (args->verbosity >= PARSEE_VERBOSE_STANZA)
|
||||
{
|
||||
Stream *output = StreamStderr();
|
||||
StreamPrintf(output, "-------STANZA BEGIN-------" "\n");
|
||||
XMLEncode(output, stanza);
|
||||
StreamPrintf(output, "\n--------STANZA END--------" "\n");
|
||||
StreamFlush(output);
|
||||
}
|
||||
|
||||
id = HashMapGet(stanza->attrs, "id");
|
||||
if (id)
|
||||
{
|
||||
XMPPAwait *await;
|
||||
/* Lock out the table to see if we're awaiting. */
|
||||
pthread_mutex_lock(&await_lock);
|
||||
if ((await = HashMapGet(await_table, id)))
|
||||
if (args->verbosity >= PARSEE_VERBOSE_STANZA)
|
||||
{
|
||||
pthread_mutex_lock(&await->cond_lock);
|
||||
await->stanza = stanza;
|
||||
pthread_cond_signal(&await->condition);
|
||||
pthread_mutex_unlock(&await->cond_lock);
|
||||
Stream *output = StreamStderr();
|
||||
StreamPrintf(output, "-------STANZA BEGIN-------" "\n");
|
||||
XMLEncode(output, stanza);
|
||||
StreamPrintf(output, "\n--------STANZA END--------" "\n");
|
||||
StreamFlush(output);
|
||||
}
|
||||
|
||||
HashMapDelete(await_table, id);
|
||||
id = HashMapGet(stanza->attrs, "id");
|
||||
if (id)
|
||||
{
|
||||
XMPPAwait *await;
|
||||
/* Lock out the table to see if we're awaiting. */
|
||||
pthread_mutex_lock(&await_lock);
|
||||
if ((await = HashMapGet(await_table, id)))
|
||||
{
|
||||
pthread_mutex_lock(&await->cond_lock);
|
||||
await->stanza = stanza;
|
||||
pthread_cond_signal(&await->condition);
|
||||
pthread_mutex_unlock(&await->cond_lock);
|
||||
|
||||
HashMapDelete(await_table, id);
|
||||
|
||||
pthread_mutex_unlock(&await_lock);
|
||||
continue;
|
||||
}
|
||||
pthread_mutex_unlock(&await_lock);
|
||||
continue;
|
||||
}
|
||||
pthread_mutex_unlock(&await_lock);
|
||||
}
|
||||
|
||||
/* Push it into the stanza FIFO. A dispatcher thread should then
|
||||
* be able to freely grab a value(locked by a mutex). We can't make
|
||||
* dispatchers read stanzas on their own, since that's _not_ how
|
||||
* streams work, but this should mitigate some issues, and allow a
|
||||
* few threads to be busy, while the rest of Parsee works. */
|
||||
PushStanza(&info, stanza);
|
||||
/* Push it into the stanza FIFO. A dispatcher thread should then
|
||||
* be able to freely grab a value(locked by a mutex). We can't make
|
||||
* dispatchers read stanzas on their own, since that's _not_ how
|
||||
* streams work, but this should mitigate some issues, and allow a
|
||||
* few threads to be busy, while the rest of Parsee works. */
|
||||
PushStanza(&info, stanza);
|
||||
}
|
||||
pthread_mutex_lock(&args->halt_lock);
|
||||
if (!args->halted)
|
||||
{
|
||||
Log(LOG_WARNING, "XMPP server is closing stream...");
|
||||
for (size_t i = 0; i < 50; i++)
|
||||
{
|
||||
UtilSleepMillis(100); /* Wait a bit so that temporary failures don't fuck everything up */
|
||||
|
||||
Log(LOG_WARNING, "Restarting XMPP stream.");
|
||||
/* This is the part where a new connection is being considered */
|
||||
XMPPFinishCompStream(jabber);
|
||||
XMPPEndCompStream(jabber);
|
||||
|
||||
args->jabber = XMPPInitialiseCompStream(
|
||||
args->config->component_addr,
|
||||
args->config->component_host,
|
||||
args->config->component_port
|
||||
);
|
||||
jabber = args->jabber;
|
||||
if (!jabber || !XMPPAuthenticateCompStream(jabber, args->config->shared_comp_secret))
|
||||
{
|
||||
/* Oops, there is something wrong! */
|
||||
Log(LOG_ERR, "Couldn't authenticate to XMPP server");
|
||||
XMPPEndCompStream(jabber);
|
||||
args->jabber = NULL;
|
||||
jabber = NULL;
|
||||
if (i == 4)
|
||||
{
|
||||
pthread_mutex_unlock(&args->halt_lock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&args->halt_lock);
|
||||
}
|
||||
|
||||
info.running = false;
|
||||
|
|
@ -268,12 +361,18 @@ ParseeXMPPThread(void *argp)
|
|||
}
|
||||
ArrayFree(info.stanzas);
|
||||
|
||||
HashMapFree(await_table);
|
||||
await_table2 = await_table;
|
||||
await_table = NULL;
|
||||
HashMapFree(await_table2);
|
||||
|
||||
pthread_mutex_destroy(&info.lock);
|
||||
|
||||
DestroyPEPManager(info.pep_manager);
|
||||
XMPPFreeManager(info.m);
|
||||
if (error)
|
||||
{
|
||||
HttpServerStop(args->server); /* minor trolling */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#include <Matrix.h>
|
||||
#include <AS.h>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
|
@ -32,7 +33,7 @@ TrimBase64(char *b64)
|
|||
while (*b64)
|
||||
{
|
||||
char ch[2] = { *b64, 0 };
|
||||
if (isspace(*b64))
|
||||
if (isspace((int) *b64))
|
||||
{
|
||||
b64++;
|
||||
continue;
|
||||
|
|
@ -46,35 +47,106 @@ TrimBase64(char *b64)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
AvatarGrab(ParseeData *data, char *mxc, char **mime, char **b64, size_t *len)
|
||||
{
|
||||
char *mimei = NULL, *outi = NULL, *b64i = NULL;
|
||||
size_t sizei;
|
||||
if (!data || !mxc || !mime || !b64 || !len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ASGrab(data->config, mxc, &mimei, &outi, &sizei))
|
||||
{
|
||||
Free(mimei);
|
||||
Free(outi);
|
||||
return false;
|
||||
}
|
||||
|
||||
b64i = Base64Encode(outi, sizei);
|
||||
Free(outi);
|
||||
if (!b64i)
|
||||
{
|
||||
Free(mimei);
|
||||
b64i = StrDuplicate(media_unknown); /* TODO: Different assets! */
|
||||
mimei = StrDuplicate("image/png");
|
||||
}
|
||||
|
||||
*mime = mimei;
|
||||
*b64 = b64i;
|
||||
*len = sizei;
|
||||
return true;
|
||||
}
|
||||
|
||||
XMLElement *
|
||||
GenerateAvatarData(ParseeData *data, char *mxid)
|
||||
{
|
||||
char *mxc = NULL, *mime = NULL, *b64 = NULL;
|
||||
XMLElement *elem = NULL, *type, *binval;
|
||||
size_t len = 0;
|
||||
if (!data || !mxid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Use the right room for the avatar! */
|
||||
mxc = ASGetAvatar(data->config, NULL, mxid);
|
||||
if (!mxc || !AvatarGrab(data, mxc, &mime, &b64, &len))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
elem = XMLCreateTag("PHOTO");
|
||||
type = XMLCreateTag("TYPE");
|
||||
binval = XMLCreateTag("BINVAL");
|
||||
|
||||
XMLAddChild(type, XMLCreateText(mime));
|
||||
XMLAddChild(binval, XMLCreateText(b64));
|
||||
|
||||
XMLAddChild(elem, type);
|
||||
XMLAddChild(elem, binval);
|
||||
|
||||
end:
|
||||
Free(mime);
|
||||
Free(mxc);
|
||||
Free(b64);
|
||||
return elem;
|
||||
}
|
||||
|
||||
#define DISCO "http://jabber.org/protocol/disco#info"
|
||||
static XMLElement *
|
||||
IQGenerateQuery(void)
|
||||
IQGenerateQuery(IQFeatures *features)
|
||||
{
|
||||
XMLElement *query = XMLCreateTag("query");
|
||||
XMLElement *query;
|
||||
if (!features)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
query = XMLCreateTag("query");
|
||||
XMLAddAttr(query, "xmlns", DISCO);
|
||||
{
|
||||
XMLElement *feature;
|
||||
#define IdentitySimple(c,t,n) do \
|
||||
{ \
|
||||
feature = XMLCreateTag("identity"); \
|
||||
XMLAddAttr(feature, "category", c); \
|
||||
XMLAddAttr(feature, "type", t); \
|
||||
XMLAddAttr(feature, "name", n); \
|
||||
XMLAddChild(query, feature); \
|
||||
} \
|
||||
while (0);
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(f) do \
|
||||
{ \
|
||||
feature = XMLCreateTag("feature"); \
|
||||
XMLAddAttr(feature, "var", f); \
|
||||
XMLAddChild(query, feature); \
|
||||
} \
|
||||
while (0);
|
||||
size_t i;
|
||||
for (i = 0; i < ArraySize(features->identity); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
feature = XMLCreateTag("identity");
|
||||
XMLAddAttr(feature, "category", identity->category);
|
||||
XMLAddAttr(feature, "type", identity->type);
|
||||
XMLAddAttr(feature, "name", identity->name);
|
||||
|
||||
XMLAddChild(query, feature);
|
||||
}
|
||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||
{
|
||||
char *var = ArrayGet(features->adverts, i);
|
||||
|
||||
feature = XMLCreateTag("feature");
|
||||
XMLAddAttr(feature, "var", var);
|
||||
XMLAddChild(query, feature);
|
||||
}
|
||||
}
|
||||
|
||||
return query;
|
||||
|
|
@ -84,6 +156,7 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
|||
{
|
||||
char *from, *to, *id;
|
||||
XMLElement *iq_reply, *query;
|
||||
IQFeatures *features;
|
||||
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
to = HashMapGet(stanza->attrs, "to");
|
||||
|
|
@ -96,9 +169,10 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
|||
XMLAddAttr(iq_reply, "type", "result");
|
||||
XMLAddAttr(iq_reply, "id", id);
|
||||
|
||||
query = IQGenerateQuery();
|
||||
features = LookupJIDFeatures(to);
|
||||
query = IQGenerateQuery(features);
|
||||
{
|
||||
char *ver = XMPPGenerateVer();
|
||||
char *ver = XMPPGenerateVer(features);
|
||||
char *node = StrConcat(3, REPOSITORY, "#", ver);
|
||||
XMLAddAttr(query, "node", node);
|
||||
|
||||
|
|
@ -106,13 +180,12 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
|
|||
Free(ver);
|
||||
}
|
||||
XMLAddChild(iq_reply, query);
|
||||
FreeIQFeatures(features);
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq_reply);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
|
||||
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
|
||||
XMLFreeElement(iq_reply);
|
||||
|
||||
(void) args;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -301,7 +374,7 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
bool
|
||||
IQIsCommandList(ParseeData *args, XMLElement *stanza)
|
||||
{
|
||||
char *parsee = NULL;
|
||||
char *parsee = NULL, *to;
|
||||
XMLElement *query = XMLookForTKV(
|
||||
stanza, "query", "xmlns",
|
||||
"http://jabber.org/protocol/disco#items"
|
||||
|
|
@ -316,15 +389,27 @@ IQIsCommandList(ParseeData *args, XMLElement *stanza)
|
|||
}
|
||||
|
||||
parsee = ParseeJID(args);
|
||||
ret = StrEquals(HashMapGet(stanza->attrs, "to"), parsee);
|
||||
to = HashMapGet(stanza->attrs, "to");
|
||||
ret = StrEquals(to, parsee) || StrEquals(to, args->config->component_host);
|
||||
Free(parsee);
|
||||
|
||||
return ret;
|
||||
}
|
||||
static bool
|
||||
IsInMUC(ParseeData *data, char *jid)
|
||||
{
|
||||
char *trimmed = ParseeTrimJID(jid);
|
||||
char *chat_id = ParseeGetFromMUCID(data, trimmed);
|
||||
|
||||
Free(trimmed);
|
||||
Free(chat_id);
|
||||
return !!chat_id;
|
||||
}
|
||||
void
|
||||
IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||
{
|
||||
XMPPComponent *jabber = args->jabber;
|
||||
XMLElement *pubsub;
|
||||
char *from = HashMapGet(stanza->attrs, "from");
|
||||
char *to = HashMapGet(stanza->attrs, "to");
|
||||
char *id = HashMapGet(stanza->attrs, "id");
|
||||
|
|
@ -334,33 +419,38 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
if (IQIsCommandList(args, stanza))
|
||||
{
|
||||
XMLElement *iq_reply = XMLCreateTag("iq");
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
||||
XMLAddAttr(iq_reply, "type", "result");
|
||||
XMLAddAttr(iq_reply, "from", to);
|
||||
XMLAddAttr(iq_reply, "to", from);
|
||||
XMLAddAttr(iq_reply, "id", id);
|
||||
{
|
||||
XMLElement *q = XMLCreateTag("query");
|
||||
char *parsee_muc_jid = StrConcat(3, trimmed, "/", "parsee");
|
||||
XMLAddAttr(q, "xmlns", "http://jabber.org/protocol/disco#items");
|
||||
XMLAddAttr(q, "node", "http://jabber.org/protocol/commands");
|
||||
XMPPShoveCommandList(thr->info->m, to, q);
|
||||
XMPPShoveCommandList(thr->info->m,
|
||||
IsInMUC(args, from) ? parsee_muc_jid : to,
|
||||
q, stanza
|
||||
);
|
||||
XMLAddChild(iq_reply, q);
|
||||
Free(parsee_muc_jid);
|
||||
}
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq_reply);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
|
||||
XMLFreeElement(iq_reply);
|
||||
Free(trimmed);
|
||||
}
|
||||
else if (XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"))
|
||||
{
|
||||
Log(LOG_INFO, "vCard information GET for %s", to);
|
||||
char *to_matrix = ParseeGetBridgedUser(args, stanza);
|
||||
char *name = ASGetName(args->config, NULL, to_matrix);
|
||||
XMLElement *iqVCard;
|
||||
Log(LOG_DEBUG, "vCard information GET for %s (%s)", to, to_matrix);
|
||||
|
||||
/* TODO: "a compliant server MUST respond on behalf of the
|
||||
* requestor and not forward the IQ to the requestee's
|
||||
* connected resource". */
|
||||
if (!strncmp(to, "parsee@", 7))
|
||||
{
|
||||
XMLElement *iqVCard = XMLCreateTag("iq");
|
||||
iqVCard = XMLCreateTag("iq");
|
||||
XMLAddAttr(iqVCard, "from", to);
|
||||
XMLAddAttr(iqVCard, "to", from);
|
||||
XMLAddAttr(iqVCard, "id", id);
|
||||
|
|
@ -386,13 +476,97 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
XMLAddChild(iqVCard, vCard);
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iqVCard);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, iqVCard, args->config->max_stanza_size);
|
||||
XMLFreeElement(iqVCard);
|
||||
Free(to_matrix);
|
||||
Free(name);
|
||||
return;
|
||||
}
|
||||
|
||||
Free(to_matrix);
|
||||
to_matrix = ParseeDecodeMXID(to);
|
||||
|
||||
iqVCard = XMLCreateTag("iq");
|
||||
XMLAddAttr(iqVCard, "from", to);
|
||||
XMLAddAttr(iqVCard, "to", from);
|
||||
XMLAddAttr(iqVCard, "id", id);
|
||||
XMLAddAttr(iqVCard, "type", "result");
|
||||
{
|
||||
XMLElement *vCard = XMLCreateTag("vCard");
|
||||
char *mto_link = ParseeGenerateMTO(to_matrix);
|
||||
XMLAddAttr(vCard, "xmlns", "vcard-temp");
|
||||
{
|
||||
XMLAddChild(vCard, GenerateAvatarData(args, to_matrix));
|
||||
|
||||
Free(mto_link);
|
||||
}
|
||||
XMLAddChild(iqVCard, vCard);
|
||||
}
|
||||
|
||||
XMPPSendStanza(jabber, iqVCard, args->config->max_stanza_size);
|
||||
XMLFreeElement(iqVCard);
|
||||
Free(to_matrix);
|
||||
Free(name);
|
||||
}
|
||||
#define PS "http://jabber.org/protocol/pubsub"
|
||||
else if ((pubsub = XMLookForTKV(stanza, "pubsub", "xmlns", PS)))
|
||||
{
|
||||
/* TODO: Pass this through the PEP manager */
|
||||
XMLElement *a_items = XMLookForTKV(pubsub,
|
||||
"items", "node", "urn:xmpp:avatar:data"
|
||||
);
|
||||
if (a_items)
|
||||
{
|
||||
/* Do, without regret, start shoving an avatar out the bus.
|
||||
* NOTE: I explicitely choose to not do any manipulation
|
||||
* because messing with random user images is inherently a
|
||||
* risk I do *not* want to take. */
|
||||
char *to_matrix = ParseeDecodeMXID(to);
|
||||
char *avatar = ASGetAvatar(args->config, NULL, to_matrix);
|
||||
char *mime = NULL;
|
||||
char *b64 = NULL;
|
||||
size_t len = 0;
|
||||
XMLElement *reply;
|
||||
|
||||
AvatarGrab(args, avatar, &mime, &b64, &len);
|
||||
|
||||
Log(LOG_DEBUG, "IQ-GET: PUBSUB AVATAR OF=%s", to_matrix);
|
||||
/* Strike back with a response */
|
||||
reply = XMLCreateTag("iq");
|
||||
XMLAddAttr(reply, "type", "result");
|
||||
XMLAddAttr(reply, "to", from);
|
||||
XMLAddAttr(reply, "from", to);
|
||||
XMLAddAttr(reply, "id", HashMapGet(stanza->attrs, "id"));
|
||||
{
|
||||
XMLElement *ps = XMLCreateTag("pubsub");
|
||||
XMLElement *items = XMLCreateTag("items");
|
||||
XMLAddAttr(ps, "xmlns", PS);
|
||||
XMLAddAttr(items, "node", "urn:xmpp:avatar:data");
|
||||
{
|
||||
XMLElement *item = XMLCreateTag("item");
|
||||
XMLElement *data = XMLCreateTag("data");
|
||||
XMLAddAttr(item, "id", "TODO");
|
||||
XMLAddAttr(data, "xmlns", "urn:xmpp:avatar:data");
|
||||
|
||||
XMLAddChild(data, XMLCreateText(b64));
|
||||
|
||||
XMLAddChild(item, data);
|
||||
XMLAddChild(items, item);
|
||||
}
|
||||
XMLAddChild(ps, items);
|
||||
XMLAddChild(reply, ps);
|
||||
}
|
||||
|
||||
XMPPSendStanza(jabber, reply, args->config->max_stanza_size);
|
||||
XMLFreeElement(reply);
|
||||
|
||||
Free(to_matrix);
|
||||
Free(avatar);
|
||||
Free(mime);
|
||||
Free(b64);
|
||||
}
|
||||
}
|
||||
#undef PS
|
||||
else if (XMLookForTKV(stanza, "query", "xmlns", DISCO))
|
||||
{
|
||||
IQDiscoGet(args, jabber, stanza);
|
||||
|
|
@ -400,7 +574,7 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version"))
|
||||
{
|
||||
XMLElement *iq_reply, *query;
|
||||
XMLElement *name, *version;
|
||||
XMLElement *name, *version, *os;
|
||||
|
||||
iq_reply = XMLCreateTag("iq");
|
||||
XMLAddAttr(iq_reply, "to", from);
|
||||
|
|
@ -411,28 +585,34 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
query = XMLCreateTag("query");
|
||||
XMLAddAttr(query, "xmlns", "jabber:iq:version");
|
||||
{
|
||||
struct utsname info;
|
||||
name = XMLCreateTag("name");
|
||||
version = XMLCreateTag("version");
|
||||
os = XMLCreateTag("os");
|
||||
uname(&info);
|
||||
|
||||
XMLAddChild(name, XMLCreateText(NAME));
|
||||
XMLAddChild(version, XMLCreateText(VERSION "[" CODE "]"));
|
||||
XMLAddChild(os, XMLCreateText(info.sysname));
|
||||
}
|
||||
XMLAddChild(query, name);
|
||||
XMLAddChild(query, version);
|
||||
XMLAddChild(query, os);
|
||||
XMLAddChild(iq_reply, query);
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq_reply);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
|
||||
XMLFreeElement(iq_reply);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *buf = NULL;
|
||||
Stream *s = StrStreamWriter(&buf);
|
||||
Log(LOG_WARNING, "Unknown I/Q received:");
|
||||
XMLEncode(StreamStdout(), stanza);
|
||||
StreamPrintf(StreamStdout(),"\n");
|
||||
StreamFlush(StreamStdout());
|
||||
XMLEncode(s, stanza);
|
||||
StreamFlush(s);
|
||||
StreamClose(s);
|
||||
Log(LOG_WARNING, "%s", buf);
|
||||
Free(buf);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -440,6 +620,9 @@ void
|
|||
IQError(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||
{
|
||||
/* TODO */
|
||||
(void) args;
|
||||
(void) stanza;
|
||||
(void) thr;
|
||||
}
|
||||
void
|
||||
IQSet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,82 @@
|
|||
#include "XMPPThread/internal.h"
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Unistring.h>
|
||||
#include <Matrix.h>
|
||||
#include <AS.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void
|
||||
LazyRegister(ParseeData *data, char *mxid, char *name)
|
||||
{
|
||||
DbRef *ref;
|
||||
char *hash = ParseeHMACS(data->id, mxid);
|
||||
char *dbname;
|
||||
if (!(ref = DbLock(data->db, 2, "users", hash)))
|
||||
{
|
||||
ASRegisterUser(data->config, mxid);
|
||||
ref = DbCreate(data->db, 2, "users", hash);
|
||||
if (ref)
|
||||
{
|
||||
HashMapSet(DbJson(ref), "mxid", JsonValueString(mxid));
|
||||
HashMapSet(DbJson(ref),
|
||||
"ts", JsonValueInteger(UtilTsMillis())
|
||||
);
|
||||
}
|
||||
}
|
||||
dbname = GrabString(DbJson(ref), 1, "name");
|
||||
if (name && !StrEquals(dbname, name))
|
||||
{
|
||||
ASSetName(data->config, mxid, name);
|
||||
if (ref)
|
||||
{
|
||||
JsonValueFree(HashMapSet(
|
||||
DbJson(ref), "name", JsonValueString(name)
|
||||
));
|
||||
}
|
||||
}
|
||||
DbUnlock(data->db, ref);
|
||||
Free(hash);
|
||||
}
|
||||
static char *
|
||||
LazySend(ParseeData *args, char *mxid, char *mroom_id, char *type, HashMap *ev, uint64_t ts)
|
||||
{
|
||||
HashMap *duplicate;
|
||||
char *event;
|
||||
if (!args || !mxid || !mroom_id || !ev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!type)
|
||||
{
|
||||
type = "m.room.message";
|
||||
}
|
||||
|
||||
duplicate = JsonDuplicate(ev);
|
||||
event = ASSend(
|
||||
args->config, mroom_id, mxid,
|
||||
type, ev, ts
|
||||
);
|
||||
if (event)
|
||||
{
|
||||
JsonFree(duplicate);
|
||||
return event;
|
||||
}
|
||||
|
||||
ASInvite(args->config, mroom_id, mxid);
|
||||
Free(ASJoin(args->config, mroom_id, mxid));
|
||||
|
||||
return ASSend(
|
||||
args->config, mroom_id, mxid,
|
||||
type, duplicate, ts
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
ProcessChatstates(ParseeData *args, XMLElement *stanza)
|
||||
{
|
||||
|
|
@ -44,7 +112,24 @@ ProcessChatstates(ParseeData *args, XMLElement *stanza)
|
|||
from_matrix = NULL;
|
||||
}
|
||||
if (XMLookForTKV(stanza, "paused", "xmlns", CHAT_STATES) ||
|
||||
XMLookForTKV(stanza, "received", "xmlns", "urn:xmpp:receipts") ||
|
||||
XMLookForTKV(stanza, "inactive", "xmlns", CHAT_STATES))
|
||||
{
|
||||
char *latest = NULL;
|
||||
from_matrix = ParseeGetBridgedUser(args, stanza);
|
||||
mroom_id = ParseeGetBridgedRoom(args, stanza);
|
||||
|
||||
latest = ParseeLookupHead(mroom_id);
|
||||
|
||||
ASType(args->config, from_matrix, mroom_id, false);
|
||||
ASPresence(args->config, from_matrix, mroom_id, latest);
|
||||
|
||||
Free(from_matrix);
|
||||
Free(latest);
|
||||
Free(mroom_id);
|
||||
mroom_id = NULL;
|
||||
from_matrix = NULL;
|
||||
}
|
||||
if (XMLookForTKV(stanza, "received", "xmlns", "urn:xmpp:receipts") ||
|
||||
XMLookForTKV(stanza, "displayed", "xmlns", "urn:xmpp:chat-markers:0"))
|
||||
{
|
||||
/* TODO: Use stanza ID if possible */
|
||||
|
|
@ -65,6 +150,16 @@ ProcessChatstates(ParseeData *args, XMLElement *stanza)
|
|||
}
|
||||
#undef CHAT_STATES
|
||||
}
|
||||
|
||||
static float
|
||||
TimeElapsed(uint64_t *rectime, uint64_t v)
|
||||
{
|
||||
uint64_t time = UtilTsMillis();
|
||||
float t = ((time-v)/1000.f);
|
||||
*rectime = time;
|
||||
|
||||
return t;
|
||||
}
|
||||
bool
|
||||
MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||
{
|
||||
|
|
@ -83,18 +178,29 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
char *type = HashMapGet(stanza->attrs, "type");
|
||||
bool chat = StrEquals(type, "chat");
|
||||
size_t i;
|
||||
uint64_t time, rectime;
|
||||
#define Elapsed(v) (TimeElapsed(&rectime, v))
|
||||
|
||||
|
||||
to = NULL;
|
||||
from = NULL;
|
||||
decode_from = NULL;
|
||||
from_matrix = NULL;
|
||||
Log(LOG_DEBUG, "<message> usage=%d", MemoryAllocated());
|
||||
time = UtilTsMillis();
|
||||
rectime = time;
|
||||
|
||||
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
if (ParseeManageBan(args, from, NULL))
|
||||
{
|
||||
XMLFreeElement(stanza);
|
||||
Log(LOG_DEBUG, "<message/> usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__);
|
||||
Log(LOG_DEBUG,
|
||||
"<message/> time=%f "
|
||||
"usage=%d (%s:%d)",
|
||||
Elapsed(time),
|
||||
MemoryAllocated(), __FILE__, __LINE__
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -107,18 +213,25 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
char *occ_id = occupant ? HashMapGet(occupant->attrs, "id") : NULL;
|
||||
if (occ_id)
|
||||
{
|
||||
Log(LOG_DEBUG,
|
||||
"'%s' has support for XEP-421, fetching OID=%s",
|
||||
from, occ_id
|
||||
);
|
||||
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG,
|
||||
"'%s' has support for XEP-421, fetching OID=%s",
|
||||
from, occ_id
|
||||
);
|
||||
}
|
||||
ParseePushOIDTable(from, occ_id);
|
||||
}
|
||||
}
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
Log(LOG_DEBUG, "XEP-421: %fs", Elapsed(rectime));
|
||||
}
|
||||
|
||||
if (StrEquals(type, "error"))
|
||||
{
|
||||
char *type, *text, *user, *parsee;
|
||||
char *message, *room;
|
||||
char *type = NULL, *text = NULL, *user = NULL, *parsee = NULL;
|
||||
char *message = NULL, *room = NULL;
|
||||
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
to = HashMapGet(stanza->attrs, "to");
|
||||
|
|
@ -139,10 +252,10 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
|||
|
||||
message = StrConcat(3, type, ": ", text);
|
||||
room = ParseeGetBridgedRoom(args, stanza);
|
||||
Free(ASSend(
|
||||
args->config, room, parsee,
|
||||
"m.room.message",
|
||||
MatrixCreateNotice(message)
|
||||
Free(LazySend(
|
||||
args, parsee, room, NULL,
|
||||
MatrixCreateNotice(message),
|
||||
time
|
||||
));
|
||||
|
||||
end_error:
|
||||
|
|
@ -151,11 +264,20 @@ end_error:
|
|||
Free(parsee);
|
||||
Free(room);
|
||||
Free(user);
|
||||
Log(LOG_DEBUG, "<message/> usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__);
|
||||
Log(LOG_DEBUG,
|
||||
"<message/> time=%f "
|
||||
"usage=%d (%s:%d)",
|
||||
Elapsed(time),
|
||||
MemoryAllocated(), __FILE__, __LINE__
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
Log(LOG_DEBUG, "Error management: %fs", Elapsed(rectime));
|
||||
}
|
||||
|
||||
if (moderated)
|
||||
if (moderated && !(!chat && strncmp(HashMapGet(stanza->attrs, "to"), "parsee@", 7)))
|
||||
{
|
||||
/* TODO: Parsee MUST check if it is a valid MUC */
|
||||
char *resource = ParseeGetResource(from);
|
||||
|
|
@ -181,29 +303,39 @@ end_error:
|
|||
body = XMLookForUnique(stanza, "body");
|
||||
|
||||
PEPManagerHandle(thr->info->pep_manager, stanza);
|
||||
|
||||
ProcessChatstates(args, stanza);
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
Log(LOG_DEBUG, "PEP management: %fs", Elapsed(rectime));
|
||||
}
|
||||
|
||||
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
|
||||
decode_from = ParseeLookupJID(from);
|
||||
from_matrix = ParseeEncodeJID(args->config, decode_from, true);
|
||||
room = ParseeFindDMRoom(args, to, from);
|
||||
data = body ? ArrayGet(body->children, 0) : NULL;
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
Log(LOG_DEBUG, "Fetching user info: %fs", Elapsed(rectime));
|
||||
}
|
||||
|
||||
/* TODO: CLEAN THAT UP INTO A CREATEDM FUNCTION */
|
||||
mroom_id = ParseeGetBridgedRoom(args, stanza);
|
||||
Log(LOG_DEBUG, "Bridging event to '%s'...", mroom_id);
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
Log(LOG_DEBUG, "Fetching bridge: %fs", Elapsed(rectime));
|
||||
}
|
||||
char *trim = ParseeTrimJID(from);
|
||||
if (!mroom_id && !room && !XMPPIsParseeStanza(stanza) &&
|
||||
to && *to == '@')
|
||||
to && *to == '@' && !XMPPQueryMUC(jabber, trim, NULL))
|
||||
{
|
||||
DbRef *room_ref;
|
||||
HashMap *room_json;
|
||||
char *from = HashMapGet(stanza->attrs, "from");
|
||||
|
||||
ASRegisterUser(args->config, from_matrix);
|
||||
LazyRegister(args, from_matrix, NULL);
|
||||
room = ASCreateDM(args->config, from_matrix, to);
|
||||
mroom_id = StrDuplicate(room);
|
||||
Log(LOG_INFO, "Creating a DM to '%s'(%s)...", to, mroom_id);
|
||||
if (room)
|
||||
{
|
||||
room_ref = DbCreate(args->db, 3, "rooms", room, "data");
|
||||
|
|
@ -214,12 +346,15 @@ end_error:
|
|||
ParseePushDMRoom(args, to, from, room);
|
||||
}
|
||||
}
|
||||
Free(trim);
|
||||
|
||||
/* TODO: THIS IS A HACK. THIS CODE IGNORES ALL MUC MESSAGES EVER
|
||||
* SENT TO NON-PARSEE PUPPETS, AS A "FIX" TO THE MULTITHREADED
|
||||
* ISSUE.
|
||||
*
|
||||
* I HATE THIS. I NEED TO FIND A BETTER WAY. */
|
||||
* I HATE THIS. I NEED TO FIND A BETTER WAY.
|
||||
*
|
||||
* Actually, is it even _that_ bad? */
|
||||
if (!chat && strncmp(HashMapGet(stanza->attrs, "to"), "parsee@", 7))
|
||||
{
|
||||
goto end;
|
||||
|
|
@ -238,20 +373,18 @@ end_error:
|
|||
ps = CreatePubsubRequest(
|
||||
parsee, decode_from, "urn:xmpp:avatar:metadata"
|
||||
);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, ps);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, ps, args->config->max_stanza_size);
|
||||
XMLFreeElement(ps);
|
||||
|
||||
ps = CreatePubsubRequest(
|
||||
parsee, trim, "urn:xmpp:avatar:metadata"
|
||||
);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, ps);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMPPSendStanza(jabber, ps, args->config->max_stanza_size);
|
||||
XMLFreeElement(ps);
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
Log(LOG_DEBUG, "Subscription to XEP-0084: %fs", Elapsed(rectime));
|
||||
}
|
||||
|
||||
Free(parsee);
|
||||
Free(trim);
|
||||
|
|
@ -260,15 +393,26 @@ end_error:
|
|||
if (ParseeVerifyAllStanza(args, stanza) && !replaced)
|
||||
{
|
||||
XMLElement *oob, *oob_data;
|
||||
|
||||
char *chat_id = ParseeGetFromMUCID(args, from);
|
||||
bool media_enabled = chat_id ?
|
||||
ParseeIsMediaEnabled(args, chat_id) :
|
||||
true ;
|
||||
bool is_parsee;
|
||||
/* Note that chat_id still has meaningful info as a boolean
|
||||
* despite being freed */
|
||||
Free(chat_id);
|
||||
pthread_mutex_unlock(&thr->info->chk_lock);
|
||||
ASRegisterUser(args->config, encoded);
|
||||
if (!chat)
|
||||
|
||||
parsee = ParseeJID(args);
|
||||
is_parsee = StrEquals(to, parsee);
|
||||
Free(parsee);
|
||||
parsee = NULL;
|
||||
|
||||
LazyRegister(args, encoded, !chat ? res : NULL);
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
ASSetName(args->config, encoded, res);
|
||||
Log(LOG_DEBUG, "Matrix registration: %fs", Elapsed(rectime));
|
||||
}
|
||||
ASInvite(args->config, mroom_id, encoded);
|
||||
Free(ASJoin(args->config, mroom_id, encoded));
|
||||
|
||||
reactions = XMLookForTKV(stanza,
|
||||
"reactions", "xmlns", "urn:xmpp:reactions:0"
|
||||
|
|
@ -276,7 +420,12 @@ end_error:
|
|||
|
||||
/* Check if it is a media link */
|
||||
oob = XMLookForTKV(stanza, "x", "xmlns", "jabber:x:oob");
|
||||
if (oob && data)
|
||||
if (chat_id && StrEquals(type, "chat") && !is_parsee)
|
||||
{
|
||||
/* Very clearly a MUC DM. */
|
||||
event_id = NULL;
|
||||
}
|
||||
else if (oob && data && media_enabled)
|
||||
{
|
||||
char *mxc, *mime = NULL;
|
||||
HashMap *content = NULL;
|
||||
|
|
@ -286,24 +435,27 @@ end_error:
|
|||
|
||||
if (oob_data)
|
||||
{
|
||||
FileInfo *info = FileInfoFromXMPP(stanza);
|
||||
mxc = ASReupload(args->config, oob_data->data, &mime);
|
||||
if (mxc)
|
||||
{
|
||||
content = MatrixCreateMedia(mxc, data->data, mime);
|
||||
content = MatrixCreateMedia(
|
||||
mxc, data->data, mime, info
|
||||
);
|
||||
|
||||
/* Yeah, no, I'm not modifying the media creation code. */
|
||||
HashMapSet(content,
|
||||
"at.kappach.at.parsee.external",
|
||||
JsonValueString(oob_data->data)
|
||||
);
|
||||
ShoveStanza(content, stanza);
|
||||
|
||||
event_id = ASSend(
|
||||
args->config, mroom_id, encoded,
|
||||
"m.room.message", content
|
||||
event_id = LazySend(
|
||||
args, encoded, mroom_id, NULL,
|
||||
content, time
|
||||
);
|
||||
Free(mxc);
|
||||
}
|
||||
FileInfoFree(info);
|
||||
Free(mime);
|
||||
}
|
||||
}
|
||||
|
|
@ -340,13 +492,13 @@ end_error:
|
|||
reaction = ArrayGet(react_child, i);
|
||||
react_data = ArrayGet(reaction->children, 0);
|
||||
|
||||
Free(ASSend(
|
||||
args->config, mroom_id, encoded,
|
||||
"m.reaction",
|
||||
Free(LazySend(
|
||||
args, encoded, mroom_id, "m.reaction",
|
||||
ShoveStanza(
|
||||
MatrixCreateReact(event_id, react_data->data),
|
||||
stanza
|
||||
)
|
||||
),
|
||||
time
|
||||
));
|
||||
}
|
||||
Free(event_id);
|
||||
|
|
@ -369,6 +521,15 @@ end_error:
|
|||
* too. Go figure. */
|
||||
size_t off =
|
||||
reply_to ? ParseeFindDatastart(data->data) : 0;
|
||||
size_t stanzaoff = XMPPGetReplyOffset(stanza);
|
||||
if (reply_to && stanzaoff != -1)
|
||||
{
|
||||
off = UnistrGetUTFOffset(data->data, stanzaoff);
|
||||
}
|
||||
if (data->data && off >= strlen(data->data))
|
||||
{
|
||||
off = 0;
|
||||
}
|
||||
HashMap *ev = MatrixCreateMessage(data->data + off);
|
||||
if (reply_to)
|
||||
{
|
||||
|
|
@ -377,9 +538,9 @@ end_error:
|
|||
Free(reply_id);
|
||||
}
|
||||
ShoveStanza(ev, stanza);
|
||||
event_id = ASSend(
|
||||
args->config, mroom_id, encoded,
|
||||
"m.room.message", ev
|
||||
event_id = LazySend(
|
||||
args, encoded, mroom_id, NULL,
|
||||
ev, time
|
||||
);
|
||||
}
|
||||
pthread_mutex_lock(&thr->info->chk_lock);
|
||||
|
|
@ -400,9 +561,9 @@ end_error:
|
|||
);
|
||||
|
||||
Log(LOG_DEBUG, "Replacing events in %s(%s)", mroom_id, event_id);
|
||||
Free(ASSend(
|
||||
args->config, mroom_id, encoded,
|
||||
"m.room.message", ev
|
||||
Free(LazySend(
|
||||
args, encoded, mroom_id, NULL,
|
||||
ev, time
|
||||
));
|
||||
ParseePushAllStanza(args, stanza, event_id);
|
||||
pthread_mutex_unlock(&thr->info->chk_lock);
|
||||
|
|
@ -428,6 +589,12 @@ end_error:
|
|||
}
|
||||
}
|
||||
|
||||
ProcessChatstates(args, stanza);
|
||||
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
|
||||
{
|
||||
Log(LOG_DEBUG, "Chatstate management: %fs", Elapsed(rectime));
|
||||
}
|
||||
|
||||
end:
|
||||
Free(mroom_id);
|
||||
mroom_id = NULL;
|
||||
|
|
@ -435,7 +602,12 @@ end:
|
|||
Free(decode_from);
|
||||
Free(room);
|
||||
Free(to);
|
||||
Log(LOG_DEBUG, "<message/> usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__);
|
||||
Log(LOG_DEBUG,
|
||||
"<message/> time=%f "
|
||||
"usage=%d (%s:%d)",
|
||||
(UtilTsMillis()-time)/1000.f,
|
||||
MemoryAllocated(), __FILE__, __LINE__
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,8 +56,62 @@ GuessStatus(XMLElement *stanza)
|
|||
}
|
||||
return USER_STATUS_ONLINE;
|
||||
}
|
||||
|
||||
static Array *
|
||||
GetStatuses(XMLElement *info)
|
||||
{
|
||||
XMLElement *child;
|
||||
Array *ret;
|
||||
size_t i;
|
||||
if (!info)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = ArrayCreate();
|
||||
for (i = 0; i < ArraySize(info->children); i++)
|
||||
{
|
||||
child = ArrayGet(info->children, i);
|
||||
if (StrEquals(child->name, "status"))
|
||||
{
|
||||
ArrayAdd(ret, HashMapGet(child->attrs, "code"));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
FreeStatuses(Array *statuses)
|
||||
{
|
||||
if (!statuses)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayFree(statuses);
|
||||
}
|
||||
static bool
|
||||
HasStatus(Array *statuses, const char *code)
|
||||
{
|
||||
size_t i;
|
||||
if (!statuses || !code)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(statuses); i++)
|
||||
{
|
||||
if (StrEquals(ArrayGet(statuses, i), code))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
void
|
||||
PresenceStanza(ParseeData *args, XMLElement *stanza)
|
||||
PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
|
||||
{
|
||||
#define MUC_USER_NS "http://jabber.org/protocol/muc#user"
|
||||
XMLElement *user_info;
|
||||
|
|
@ -65,6 +119,20 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
XMLElement *status = XMLookForUnique(stanza, "status");
|
||||
|
||||
char *oid = HashMapGet(stanza->attrs, "from");
|
||||
char *dst = HashMapGet(stanza->attrs, "to");
|
||||
char *type = HashMapGet(stanza->attrs, "type");
|
||||
|
||||
if (StrEquals(type, "subscribe"))
|
||||
{
|
||||
Log(LOG_WARNING, "!PRESENCE SUBSCRIPTION REQUEST! (%s:%s)", oid, dst);
|
||||
AddPresenceSubscriber(args, oid, dst); /* TODO: Send presence updates
|
||||
* whenever possible. */
|
||||
}
|
||||
|
||||
if (PEPManagerHandle(thr->info->pep_manager, stanza))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ServerHasXEP421(args, oid))
|
||||
{
|
||||
|
|
@ -75,10 +143,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
char *occ_id = occupant ? HashMapGet(occupant->attrs, "id") : NULL;
|
||||
if (occ_id)
|
||||
{
|
||||
Log(LOG_DEBUG,
|
||||
"'%s' has support for XEP-421, fetching OID=%s",
|
||||
oid, occ_id
|
||||
);
|
||||
ParseePushOIDTable(oid, occ_id);
|
||||
}
|
||||
}
|
||||
|
|
@ -86,27 +150,33 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS)))
|
||||
{
|
||||
XMLElement *item = XMLookForUnique(user_info, "item");
|
||||
XMLElement *status = XMLookForUnique(user_info, "status");
|
||||
#define IsStatus(code) (status && \
|
||||
StrEquals(HashMapGet(status->attrs, "code"), #code))
|
||||
Array *statuses = GetStatuses(user_info);
|
||||
#define IsStatus(code) (HasStatus(statuses, #code))
|
||||
char *jid = item ? HashMapGet(item->attrs, "jid") : NULL;
|
||||
char *trim = ParseeTrimJID(jid);
|
||||
char *from = NULL;
|
||||
char *type = HashMapGet(stanza->attrs, "type");
|
||||
char *room = ParseeGetBridgedRoom(args, stanza);
|
||||
char *decode_from, *real_matrix;
|
||||
char *decode_from = NULL, *real_matrix = NULL;
|
||||
char *matrix_user_pl = ParseeEncodeJID(args->config, trim, false);
|
||||
char *affiliation = HashMapGet(item->attrs, "affiliation");
|
||||
char *role = HashMapGet(item->attrs, "role");
|
||||
char *affiliation = item ? HashMapGet(item->attrs, "affiliation") : NULL;
|
||||
char *role = item ? HashMapGet(item->attrs, "role") : NULL;
|
||||
int power_level = 0;
|
||||
char *parsee = ParseeMXID(args);
|
||||
char *parsee_j = ParseeJID(args);
|
||||
char *muc = ParseeTrimJID(HashMapGet(stanza->attrs, "from"));
|
||||
char *parsee_muc = StrConcat(3, muc, "/", "parsee");
|
||||
|
||||
Free(trim);
|
||||
if (!item)
|
||||
{
|
||||
goto end_item;
|
||||
}
|
||||
|
||||
if (jid)
|
||||
{
|
||||
ParseePushJIDTable(oid, jid);
|
||||
}
|
||||
ParseePushAffiliationTable(oid, affiliation, role);
|
||||
decode_from = ParseeLookupJID(oid);
|
||||
real_matrix = ParseeDecodeMXID(decode_from);
|
||||
|
||||
|
|
@ -135,22 +205,16 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
|
||||
if (StrEquals(role, "visitor"))
|
||||
{
|
||||
char *parsee = ParseeJID(args);
|
||||
if (!StrEquals(HashMapGet(stanza->attrs, "to"), parsee) &&
|
||||
if (!StrEquals(HashMapGet(stanza->attrs, "to"), parsee_j) &&
|
||||
IsStatus(110))
|
||||
{
|
||||
char *muc = ParseeTrimJID(HashMapGet(stanza->attrs, "from"));
|
||||
char *usr = HashMapGet(stanza->attrs, "to");
|
||||
|
||||
/* Ask for voice in a visitor self-presence. We do not notify
|
||||
* the user, as an error MUST occur, which is handled and
|
||||
* logged out. */
|
||||
XMPPRequestVoice(args->jabber, usr, muc);
|
||||
|
||||
Free(muc);
|
||||
}
|
||||
|
||||
Free(parsee);
|
||||
}
|
||||
|
||||
/* Set the user's PL
|
||||
|
|
@ -190,7 +254,7 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
{
|
||||
ASBan(args->config, room, real_matrix);
|
||||
}
|
||||
else if (IsStatus(307))
|
||||
else if (IsStatus(307) && !IsStatus(333))
|
||||
{
|
||||
ASKick(args->config, room, real_matrix);
|
||||
}
|
||||
|
|
@ -204,16 +268,37 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
ASLeave(args->config, room, real_matrix);
|
||||
}
|
||||
}
|
||||
if (StrEquals(type, "unavailable") &&
|
||||
(StrEquals(jid, parsee_muc) || StrEquals(jid, parsee_j))
|
||||
&& IsStatus(301))
|
||||
{
|
||||
char *chat_id = ParseeGetFromRoomID(args, room);
|
||||
|
||||
ParseeUnlinkRoom(args, chat_id);
|
||||
|
||||
Free(ASSend(
|
||||
args->config, room, parsee,
|
||||
"m.room.message",
|
||||
MatrixCreateNotice("This room has been unlinked."),
|
||||
0
|
||||
));
|
||||
ASLeave(args->config, room, parsee);
|
||||
Free(chat_id);
|
||||
}
|
||||
|
||||
end_item:
|
||||
Free(muc);
|
||||
Free(from);
|
||||
Free(parsee_muc);
|
||||
Free(decode_from);
|
||||
Free(real_matrix);
|
||||
Free(matrix_user_pl);
|
||||
Free(parsee_j);
|
||||
Free(parsee);
|
||||
Free(room);
|
||||
FreeStatuses(statuses);
|
||||
}
|
||||
if (status)
|
||||
if (status && !StrEquals(type, "unavailable"))
|
||||
{
|
||||
XMLElement *status_data = ArrayGet(status->children, 0);
|
||||
char *decode_from = ParseeLookupJID(oid);
|
||||
|
|
@ -224,6 +309,8 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
status_str = status_data->data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* TODO: "The server will automatically set a user's presence to
|
||||
* unavailable if their last active time was over a threshold value
|
||||
* (e.g. 5 minutes)."
|
||||
|
|
@ -299,11 +386,8 @@ PresenceStanza(ParseeData *args, XMLElement *stanza)
|
|||
vcard_request = CreateVCardRequest(
|
||||
from, HashMapGet(stanza->attrs, "from")
|
||||
);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, vcard_request);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
|
||||
XMPPSendStanza(jabber, vcard_request, args->config->max_stanza_size);
|
||||
XMLFreeElement(vcard_request);
|
||||
Free(from);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,13 +32,17 @@
|
|||
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
|
||||
IdentitySimple("component", "generic", "Parsee's component")
|
||||
|
||||
typedef struct PEPManager PEPManager;
|
||||
typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item);
|
||||
|
||||
typedef struct IQFeatures {
|
||||
Array *identity;
|
||||
Array *adverts;
|
||||
} IQFeatures;
|
||||
typedef struct XMPPIdentity {
|
||||
char *category, *type, *lang, *name;
|
||||
} XMPPIdentity;
|
||||
|
||||
typedef struct PEPManager PEPManager;
|
||||
typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item);
|
||||
|
||||
typedef struct XMPPThread XMPPThread;
|
||||
typedef struct XMPPThreadInfo {
|
||||
/* A FIFO of stanzas inbound, to be read by dispatcher
|
||||
|
|
@ -65,6 +69,29 @@ struct XMPPThread {
|
|||
int ICollate(unsigned char *cata, unsigned char *catb);
|
||||
int IdentitySort(void *idap, void *idbp);
|
||||
|
||||
IQFeatures * CreateIQFeatures(void);
|
||||
void AddIQIdentity(IQFeatures *, char *category, char *lang, char *type, char *name);
|
||||
void AdvertiseIQFeature(IQFeatures *, char *feature);
|
||||
void FreeIQFeatures(IQFeatures *);
|
||||
|
||||
/**
|
||||
* Looks up the features supported by a JID
|
||||
* ----------------------
|
||||
* Returns: an IQ featureset[LA:HEAP] | NULL
|
||||
* Thrasher: FreeIQFeatures
|
||||
* Modifies: NOTHING */
|
||||
IQFeatures * LookupJIDFeatures(char *jid);
|
||||
|
||||
/** Generate the B64-encoded SHA-256 hash for the 'ver' field in caps.
|
||||
* --------
|
||||
* Returns: A base64 encoded ver hash[LA:HEAP]
|
||||
* Modifies: NOTHING
|
||||
* See-Also: https://xmpp.org/extensions/xep-0115.html */
|
||||
char * XMPPGenerateVer(IQFeatures *features);
|
||||
|
||||
/* Annotates a presence with https://xmpp.org/extensions/xep-0115.html */
|
||||
void XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features);
|
||||
|
||||
char * ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza);
|
||||
char * ParseeGetEventFromID(ParseeData *data, XMLElement *stanza, char *id);
|
||||
char * ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza);
|
||||
|
|
@ -78,7 +105,7 @@ XMLElement * CreatePubsubRequest(char *from, char *to, char *node);
|
|||
|
||||
bool MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
|
||||
void IQStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
|
||||
void PresenceStanza(ParseeData *args, XMLElement *stanza);
|
||||
void PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
|
||||
|
||||
bool ServerHasXEP421(ParseeData *data, char *from);
|
||||
|
||||
|
|
@ -89,6 +116,7 @@ PEPManager * CreatePEPManager(ParseeData *data, void *cookie);
|
|||
void * PEPManagerCookie(PEPManager *manager);
|
||||
void PEPManagerAddEvent(PEPManager *manager, char *node, PEPEvent event);
|
||||
bool PEPManagerHandle(PEPManager *manager, XMLElement *stanza);
|
||||
void PEPManagerBroadcast(PEPManager *manager, char *as, XMLElement *item);
|
||||
void DestroyPEPManager(PEPManager *manager);
|
||||
|
||||
/* PEP callbacks for the handler */
|
||||
|
|
@ -96,3 +124,7 @@ void PEPAvatarEvent(PEPManager *m, XMLElement *stanza, XMLElement *item);
|
|||
void PEPVCardEvent(PEPManager *m, XMLElement *stanza, XMLElement *item);
|
||||
|
||||
char * ScrambleOID(ParseeData *data, char *opaque_oid);
|
||||
|
||||
/* Presence management */
|
||||
void AddPresenceSubscriber(ParseeData *data, char *from, char *to);
|
||||
bool IsSubscribed(ParseeData *data, char *user, char *to);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ extern void ASAuthenticateRequest(const ParseeConfig *, HttpClientContext *);
|
|||
extern bool ASRegisterUser(const ParseeConfig *, char *);
|
||||
|
||||
/* Pings the homeserver to get attention. */
|
||||
extern void ASPing(const ParseeConfig *);
|
||||
extern bool ASPing(const ParseeConfig *);
|
||||
|
||||
/** Joins a room from an {id} and a given {user} we want to masquerade
|
||||
* as.
|
||||
|
|
@ -67,7 +67,8 @@ extern HashMap * ASFind(const ParseeConfig *, char *, char *);
|
|||
|
||||
/* Sends a message event with a specific type and body.
|
||||
* Said body is freed during the function's execution. */
|
||||
extern char * ASSend(const ParseeConfig *, char *, char *, char *, HashMap *);
|
||||
extern char *
|
||||
ASSend(const ParseeConfig *, char *, char *, char *, HashMap *, uint64_t ts);
|
||||
extern void ASType(const ParseeConfig *, char *, char *, bool);
|
||||
extern void ASPresence(const ParseeConfig *, char *, char *, char *);
|
||||
|
||||
|
|
@ -141,6 +142,14 @@ extern void ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, ch
|
|||
* Modifies: NOTHING */
|
||||
extern char * ASGetName(const ParseeConfig *c, char *room, char *user);
|
||||
|
||||
/** Returns the user's avatar in a room, or a the global user avatar, to be
|
||||
* Free'd
|
||||
* -------------
|
||||
* Returns: The user's name in the [HEAP] | NULL
|
||||
* Thrasher: Free
|
||||
* Modifies: NOTHING */
|
||||
extern char * ASGetAvatar(const ParseeConfig *c, char *room, char *user);
|
||||
|
||||
/** Uploads data to Matrix to be used later
|
||||
* ----------------
|
||||
* Returns: A valid MXC URI[HEAP] | NULL
|
||||
|
|
@ -170,4 +179,16 @@ extern Array * ASGetRelations(const ParseeConfig *c, size_t n, char *room, char
|
|||
* Thrashes: {relations}
|
||||
* See-Also: ASGetRelations */
|
||||
extern void ASFreeRelations(Array *relations);
|
||||
|
||||
/** Returns the MIME and SHA-1 hash of a media entry, in one fell swoop.
|
||||
* -----------------
|
||||
* Returns: whenever the media exists
|
||||
* Modifies: {mime}[HEAP], {sha}[HEAP] */
|
||||
extern bool ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha);
|
||||
|
||||
/** Retrieves media off an MXC link.
|
||||
* ------------
|
||||
* Returns: whenever the media exists
|
||||
* Modifies {mime}[HEAP], {out}[HEAP], {len} */
|
||||
extern bool ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
Free(ASSend( \
|
||||
data->config, id, profile, \
|
||||
"m.room.message", \
|
||||
MatrixCreateNotice(rep) \
|
||||
MatrixCreateNotice(rep), 0 \
|
||||
)); \
|
||||
} \
|
||||
while(0)
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
Free(ASSend( \
|
||||
data->config, id, profile, \
|
||||
"m.room.message", \
|
||||
MatrixCreateNotice(formatted) \
|
||||
MatrixCreateNotice(formatted), 0 \
|
||||
)); \
|
||||
Free(formatted); \
|
||||
} \
|
||||
|
|
|
|||
23
src/include/FileInfo.h
Normal file
23
src/include/FileInfo.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef PARSEE_FILEINFO_H
|
||||
#define PARSEE_FILEINFO_H
|
||||
|
||||
#include "XML.h"
|
||||
|
||||
typedef struct FileInfo {
|
||||
int width;
|
||||
int height;
|
||||
int size;
|
||||
} FileInfo;
|
||||
|
||||
/** Grab file metadata through SIMS.
|
||||
* ----------------
|
||||
* Returns: File information(SIMS)[HEAP]
|
||||
* Thrasher: FileInfoFree */
|
||||
extern FileInfo * FileInfoFromXMPP(XMLElement *stanza);
|
||||
|
||||
/** Frees all information related with file metadata.
|
||||
* --------------
|
||||
* Thrashes: info */
|
||||
extern void FileInfoFree(FileInfo *info);
|
||||
|
||||
#endif
|
||||
39
src/include/MUCServ.h
Normal file
39
src/include/MUCServ.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#ifndef PARSEE_MUCSERV_H
|
||||
#define PARSEE_MUCSERV_H
|
||||
|
||||
/*-* An easy interface for handling MUCs.
|
||||
*/
|
||||
|
||||
typedef struct MUCServer MUCServer;
|
||||
|
||||
#include <Parsee.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/** Creates a MUC server handle given a ParseeData handle.
|
||||
* This handle shall be used for all MUC-related operations.
|
||||
* ----------------------------------------------------------
|
||||
* Returns: a MUC server handle[HEAP] | NULL
|
||||
* Thrasher: FreeMUCServer
|
||||
* See-Also: https://xmpp.org/extensions/xep-0045.html */
|
||||
extern MUCServer * CreateMUCServer(ParseeData *data);
|
||||
|
||||
/** Manages a stanza from a MUC, and returns true if the stanza
|
||||
* was actually processed by the MUC server.
|
||||
* -------------------------------------------------------------
|
||||
* See-Also: CreateMUCServer */
|
||||
extern bool ManageMUCStanza(MUCServer *serv, XMLElement *stanza);
|
||||
|
||||
/** Checks if a MUC(from its localpart, so
|
||||
* <code>'#foobar@bridge.blow.hole' -> 'foobar'</code>) exists.
|
||||
* --------------
|
||||
* Returns: whenever the MUC exists */
|
||||
extern bool MUCServerExists(MUCServer *serv, char *muc);
|
||||
|
||||
/** Destroys all memory and references from a MUC server handle.
|
||||
* --------------
|
||||
* Thrashes: CreateMUCServer
|
||||
* See-Also: https://xmpp.org/extensions/xep-0045.html */
|
||||
extern void FreeMUCServer(MUCServer *serv);
|
||||
|
||||
#endif
|
||||
|
|
@ -2,6 +2,9 @@
|
|||
#define PARSEE_MATRIX_H
|
||||
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include "FileInfo.h"
|
||||
|
||||
/* A simple representation of everything. It is not as complex as
|
||||
* Telodendria's CommonID parser, simply because Parsee does not
|
||||
|
|
@ -18,6 +21,12 @@ typedef struct UserID {
|
|||
* Thrasher: Free */
|
||||
extern UserID * MatrixParseID(char *user);
|
||||
|
||||
/** Attempts to parse a Matrix ID from a matrix.to URL.
|
||||
* -------------------------------------------------
|
||||
* Returns: a valid user ID[HEAP] | NULL
|
||||
* Thrasher: Free */
|
||||
extern UserID * MatrixParseIDFromMTO(Uri *uri);
|
||||
|
||||
/* Creates an error message JSON, with the specified code and message. */
|
||||
extern HashMap * MatrixCreateError(char *err, char *msg);
|
||||
|
||||
|
|
@ -33,7 +42,7 @@ extern HashMap * MatrixCreateReact(char *event, char *body);
|
|||
extern HashMap * MatrixCreateReplace(char *event, char *body);
|
||||
|
||||
/* Creates the content for a media file. */
|
||||
extern HashMap * MatrixCreateMedia(char *mxc, char *body, char *mime);
|
||||
extern HashMap * MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info);
|
||||
|
||||
/* Creates the content for a m.room.name state event */
|
||||
extern HashMap * MatrixCreateNameState(char *name);
|
||||
|
|
|
|||
|
|
@ -18,9 +18,12 @@ typedef struct ParseeData ParseeData;
|
|||
#include <Command.h>
|
||||
#include <XMPP.h>
|
||||
|
||||
#include <MUCServ.h>
|
||||
|
||||
#define PARSEE_VERBOSE_NONE 0
|
||||
#define PARSEE_VERBOSE_LOG 1
|
||||
#define PARSEE_VERBOSE_STANZA 2
|
||||
#define PARSEE_VERBOSE_TIMINGS 2
|
||||
#define PARSEE_VERBOSE_STANZA 3
|
||||
#define PARSEE_VERBOSE_COMICAL 53 /* MINUTES */
|
||||
|
||||
typedef struct ParseeConfig {
|
||||
|
|
@ -38,12 +41,18 @@ typedef struct ParseeConfig {
|
|||
/* Homeserver port info */
|
||||
char *homeserver_host;
|
||||
int homeserver_port;
|
||||
int homeserver_tls;
|
||||
|
||||
bool accept_pings;
|
||||
bool ignore_bots;
|
||||
|
||||
|
||||
/* ------- JABBER -------- */
|
||||
char *component_addr;
|
||||
char *component_host;
|
||||
char *shared_comp_secret;
|
||||
int component_port;
|
||||
size_t max_stanza_size;
|
||||
|
||||
/* ------- DB -------- */
|
||||
char *db_path;
|
||||
|
|
@ -58,7 +67,9 @@ typedef struct ParseeData {
|
|||
HttpRouter *router;
|
||||
CommandRouter *handler;
|
||||
|
||||
HttpServer *server;
|
||||
XMPPComponent *jabber;
|
||||
MUCServer *muc;
|
||||
|
||||
Db *db;
|
||||
char *id;
|
||||
|
|
@ -67,6 +78,10 @@ typedef struct ParseeData {
|
|||
|
||||
HashMap *oid_servers;
|
||||
pthread_mutex_t oidl;
|
||||
|
||||
/* If Parsee was intentionally halted */
|
||||
bool halted;
|
||||
pthread_mutex_t halt_lock;
|
||||
} ParseeData;
|
||||
|
||||
typedef struct Argument {
|
||||
|
|
@ -79,6 +94,7 @@ typedef struct Argument {
|
|||
const char *description;
|
||||
} Argument;
|
||||
|
||||
/* A few helpful macros to make JSON less of a PITA */
|
||||
#define GrabString(obj, ...) JsonValueAsString(JsonGet(obj, __VA_ARGS__))
|
||||
#define GrabInteger(obj, ...) JsonValueAsInteger(JsonGet(obj, __VA_ARGS__))
|
||||
#define GrabBoolean(obj, ...) JsonValueAsBoolean(JsonGet(obj, __VA_ARGS__))
|
||||
|
|
@ -99,9 +115,12 @@ typedef struct Argument {
|
|||
|
||||
/* A base64-encoded Parsee logo */
|
||||
extern const char media_parsee_logo[];
|
||||
/* "Unknown avatar" */
|
||||
extern const char media_unknown[];
|
||||
|
||||
/* An ASCII-art rendition of "小橋" */
|
||||
#define PARSEE_ASCII_LINES 9
|
||||
/* An ASCII-art rendition of "小橋".
|
||||
* I'm sorry for its quality. If anyone wants to redraw it, feel free. */
|
||||
#define PARSEE_ASCII_LINES 8
|
||||
extern const char *parsee_ascii[PARSEE_ASCII_LINES];
|
||||
|
||||
/**
|
||||
|
|
@ -112,6 +131,9 @@ extern void ParseePrintASCII(void);
|
|||
|
||||
/**
|
||||
* Checks if two versions of Parsee can be considered "compatible".
|
||||
* This is mainly used for things like database operations. TODO:
|
||||
* Make an auto-upgrade system to comply with the (undocumented)
|
||||
* rule of "Don't Make The Sysadmin Think About Parsee Too Much".
|
||||
* ---------------
|
||||
* Modifies: NOTHING */
|
||||
extern bool ParseeIsCompatible(char *ver1, char *ver2);
|
||||
|
|
@ -262,6 +284,47 @@ extern char * ParseeGetRoomID(ParseeData *, char *chat_id);
|
|||
/* Finds the MUC JID from a chat ID */
|
||||
extern char * ParseeGetMUCID(ParseeData *, char *chat_id);
|
||||
|
||||
/** Fetches a configuration value from a key in a chat(given a Chat ID),
|
||||
* as a string or NULL. Keys are to be stored like Java packages(reveres DNS).
|
||||
* Parsee has the right over any key with the <code>'p.'</code> prefix.
|
||||
* -----------------------------------
|
||||
* Returns: a valid string[HEAP] | NULL
|
||||
* Modifies: NOTHING
|
||||
* See-Also: ParseeGetFromMUCID, ParseeGetFromRoomID, ParseeSetChatSetting */
|
||||
extern char *
|
||||
ParseeGetChatSetting(ParseeData *data, char *chat, char *key);
|
||||
|
||||
/** Fetches the entire configuration in a chat(given a Chat ID), as an hashmap
|
||||
* of strings.
|
||||
* -----------------------------------
|
||||
* Returns: a valid string[HEAP] | NULL
|
||||
* Modifies: NOTHING
|
||||
* Thrasher: ParseeFreeChatSettings
|
||||
* See-Also: ParseeGetFromMUCID, ParseeGetFromRoomID, ParseeSetChatSetting, ParseeGetChatSetting */
|
||||
extern HashMap *
|
||||
ParseeGetChatSettings(ParseeData *data, char *chat);
|
||||
|
||||
/** Destroys memory allocated from a call to {ParseeGetChatSettings}.
|
||||
* -----------------------
|
||||
* Returns: NOTHING
|
||||
* Modifies: {settings}
|
||||
* Thrashes: {settings}
|
||||
* See-Also: ParseeGetChatSettings */
|
||||
extern void
|
||||
ParseeFreeChatSettings(HashMap *settings);
|
||||
|
||||
/** Replaces a configuration key-value pair within the chat's context, which
|
||||
* can be read with {ParseeGetChatSetting}.
|
||||
* -------------------------------------
|
||||
* Returns: NOTHING
|
||||
* Modifies: the chat context
|
||||
* See-Also: ParseeGetFromMUCID, ParseeGetFromRoomID, ParseeGetChatSetting */
|
||||
extern void
|
||||
ParseeSetChatSetting(ParseeData *data, char *chat, char *key, char *val);
|
||||
|
||||
extern bool
|
||||
ParseeIsMediaEnabled(ParseeData *data, char *chat_id);
|
||||
|
||||
/* Pushes a stanza ID to a chat ID */
|
||||
extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *origin_id, char *event, char *sender);
|
||||
extern void ParseePushDMStanza(ParseeData *, char *room_id, char *stanza_id, char *origin_id, char *event, char *sender);
|
||||
|
|
@ -278,7 +341,11 @@ extern bool ParseeGetDMOrigin(ParseeData *data, char *chat_id, char *ev, char **
|
|||
/* Sends presence requests for every MUC around as a fake JID */
|
||||
extern void ParseeSendPresence(ParseeData *);
|
||||
|
||||
extern bool ParseeInitialiseSignals(HttpServer *, pthread_t, XMPPComponent *);
|
||||
/** Initialises signal-handling code within Parsee.
|
||||
* --------------------
|
||||
* Modifies: the signal handler
|
||||
* Returns: whenever it has properly been setup */
|
||||
extern bool ParseeInitialiseSignals(ParseeData *data, pthread_t xmpp);
|
||||
|
||||
/* Job used to cleanup Parsee data that isn't necessary anymore. */
|
||||
extern void ParseeCleanup(void *data);
|
||||
|
|
@ -286,9 +353,13 @@ extern void ParseeCleanup(void *data);
|
|||
/* Finds the offset of the first line without a '>' at its start. */
|
||||
extern int ParseeFindDatastart(char *data);
|
||||
|
||||
/* Finds the offset of the first line without a '>' at its start(as
|
||||
* Unicode codepoints). */
|
||||
extern int ParseeFindDatastartU(char *data);
|
||||
|
||||
|
||||
/* XMPP-ifies a message event to XEP-0393 if possible. */
|
||||
extern char * ParseeXMPPify(HashMap *event);
|
||||
extern char * ParseeXMPPify(ParseeData *data, HashMap *event);
|
||||
|
||||
/* Finds an event ID from an ID in the stanza's attributes */
|
||||
extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id);
|
||||
|
|
@ -298,13 +369,18 @@ extern char * ParseeDMEventFromID(ParseeData *d, char *r_id, char *ori_id);
|
|||
extern char * ParseeDMEventFromSID(ParseeData *d, char *r_id, char *ori_id);
|
||||
|
||||
/* Gets a Parsee "shim" link to an MXC, usable as unauth for a limited time */
|
||||
extern char * ParseeToUnauth(ParseeData *data, char *mxc);
|
||||
extern char * ParseeToUnauth(ParseeData *data, char *mxc, char *filename);
|
||||
|
||||
extern void ParseeInitialiseOIDTable(void);
|
||||
extern void ParseePushOIDTable(char *muc, char *occupant);
|
||||
extern char *ParseeLookupOID(char *muc);
|
||||
extern void ParseeDestroyOIDTable(void);
|
||||
|
||||
extern void ParseeInitialiseAffiliationTable(void);
|
||||
extern void ParseePushAffiliationTable(char *user, char *affiliation, char *role);
|
||||
extern bool ParseeLookupAffiliation(char *muc, char **affiliation, char **role);
|
||||
extern void ParseeDestroyAffiliationTable(void);
|
||||
|
||||
extern void ParseeInitialiseJIDTable(void);
|
||||
extern void ParseePushJIDTable(char *muc, char *bare);
|
||||
extern char *ParseeLookupJID(char *muc);
|
||||
|
|
@ -324,10 +400,17 @@ extern void ParseeDestroyNickTable(void);
|
|||
* to ban them from rooms where Parsee has the ability to do so ("noflying").
|
||||
* ---------------
|
||||
* Returns: NOTHING
|
||||
* See-Also: ParseeManageBan
|
||||
* See-Also: ParseeManageBan, ParseeGlobalUnban
|
||||
* Modifies: the database */
|
||||
extern void ParseeGlobalBan(ParseeData *, char *user, char *reason);
|
||||
|
||||
/** Revokes a user/room/MUC's nofly status
|
||||
* ---------------
|
||||
* Returns: NOTHING
|
||||
* See-Also: ParseeManageBan, ParseeGlobalBan
|
||||
* Modifies: the database */
|
||||
extern void ParseeGlobalUnban(ParseeData *, char *user);
|
||||
|
||||
/** Verifies if a user was banned globally. If so (and if {room} is set),
|
||||
* tries to ban the user from it.
|
||||
* ---------------
|
||||
|
|
@ -340,7 +423,8 @@ extern bool ParseeManageBan(ParseeData *, char *user, char *room);
|
|||
extern bool ParseeVerifyDMStanza(ParseeData *data, char *room_id, char *id);
|
||||
|
||||
|
||||
/** Checks if a Matrix/XMPP user is considered as "administrator" by Parsee.
|
||||
/** Checks if a Matrix/XMPP user is considered as "administrator" by
|
||||
* Parsee.
|
||||
* ----------------------
|
||||
* Returns: (whenever the user is an admin)
|
||||
* Modifies: NOTHING */
|
||||
|
|
@ -401,4 +485,22 @@ extern void ParseeSetThreads(int xmpp, int http);
|
|||
extern char * ParseeHMAC(char *key, uint8_t *msg, size_t msglen);
|
||||
#define ParseeHMACS(key, msg) ParseeHMAC(key, (uint8_t *) msg, strlen(msg))
|
||||
|
||||
/** Broadcasts a stanza from a user to any that may be interested by it
|
||||
* (PEP or subscription)
|
||||
* -------------------------------------
|
||||
* Returns: NOTHING */
|
||||
extern void ParseeBroadcastStanza(ParseeData *data, char *from, XMLElement *s);
|
||||
|
||||
/** Destroys the mapping between a MUC and a room from a chat ID.
|
||||
* ----------------
|
||||
* Modifies: the DB's room mappings
|
||||
* Returns: NOTHING */
|
||||
extern void ParseeUnlinkRoom(ParseeData *data, char *chat_id);
|
||||
|
||||
/** Verifies if there is no whitelists OR that a MUC's server is whitelisted.
|
||||
* ----------------------
|
||||
* Returns: if a MUC is to be allowed
|
||||
* Modifies: NOTHING */
|
||||
extern bool ParseeIsMUCWhitelisted(ParseeData *data, char *muc);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@ typedef struct ParseeCmdArg {
|
|||
"ban-list", CmdNoFlyList, \
|
||||
"Globally bans a user from using Parsee" \
|
||||
) \
|
||||
X_COMMAND( \
|
||||
"unban-list", CmdNoFlyListDel, \
|
||||
"Globally unbans a user from using Parsee" \
|
||||
) \
|
||||
X_COMMAND( \
|
||||
"nuke-muc", CmdUnlinkMUC, \
|
||||
"Removes a MUC. Users should then run " \
|
||||
|
|
@ -74,7 +78,9 @@ typedef struct ParseeCmdArg {
|
|||
X_ROUTE("/_matrix/app/v1/transactions/(.*)", RouteTxns) \
|
||||
X_ROUTE("/_matrix/app/v1/users/(.*)", RouteUserAck) \
|
||||
X_ROUTE("/_matrix/app/v1/rooms/(.*)", RouteRoomAck) \
|
||||
X_ROUTE("/_matrix/client/v1/media/download/(.*)/(.*)", RouteMedia)
|
||||
X_ROUTE("/_matrix/app/v1/ping", RoutePing) \
|
||||
X_ROUTE("/media/(.*)/(.*)", RouteMedia) \
|
||||
X_ROUTE("/media/(.*)/(.*)/(.*)", RouteMedia)
|
||||
|
||||
#define X_ROUTE(path, name) extern void * name(Array *, void *);
|
||||
ROUTES
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ extern StanzaBuilder * SetStanzaID(StanzaBuilder *builder, char *id);
|
|||
extern StanzaBuilder * SetStanzaXParsee(StanzaBuilder *builder, HashMap *e);
|
||||
extern StanzaBuilder * AddStanzaElem(StanzaBuilder *builder, XMLElement *item);
|
||||
extern XMLElement * ExportStanza(StanzaBuilder *builder);
|
||||
extern void WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber);
|
||||
extern void WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber, size_t max);
|
||||
extern void DestroyStanzaBuilder(StanzaBuilder *builder);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include <Cytoplasm/Stream.h>
|
||||
|
||||
/* Creates a string stream writer. The referenced buffer must be in the heap,
|
||||
* or NULL. */
|
||||
* or NULL for an empty string. */
|
||||
extern Stream * StrStreamWriter(char **buffer);
|
||||
|
||||
/* Creates a string stream reader. The referenced buffer may be everywhere. */
|
||||
|
|
|
|||
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