mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 13:45:10 +00:00
Compare commits
No commits in common. "master" and "lmdb" have entirely different histories.
133 changed files with 2915 additions and 8900 deletions
|
|
@ -1,85 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
name: Checks Parsee's correctness on GCC+Debian
|
||||
on: [push]
|
||||
jobs:
|
||||
compile:
|
||||
strategy:
|
||||
matrix:
|
||||
distro:
|
||||
- debian
|
||||
arch:
|
||||
- amd64
|
||||
runs-on:
|
||||
- ${{ matrix.distro }}
|
||||
- ${{ matrix.arch }}
|
||||
container:
|
||||
image: ${{ matrix.distro }}
|
||||
steps:
|
||||
- name: Install LMDB+OpenSSL tools
|
||||
run: |
|
||||
echo $(uname -a) $(env | grep container)
|
||||
apt update -y
|
||||
apt install -y build-essential liblmdb-dev nodejs libssl-dev git
|
||||
- name: Clone/Configure Cytoplasm
|
||||
run: |
|
||||
git clone https://git.kappach.at/KappaChat/Cytoplasm --depth=1
|
||||
cd Cytoplasm
|
||||
./configure --with-lmdb --prefix=/usr
|
||||
- name: Build Cytoplasm
|
||||
run: 'cd Cytoplasm && make -j$(nproc)'
|
||||
- name: Install Cytoplasm
|
||||
run: 'cd Cytoplasm && make install'
|
||||
- name: Clone Parsee
|
||||
uses: actions/checkout@v3
|
||||
- name: Build Parsee
|
||||
run: |
|
||||
cc configure.c -o configure && ./configure
|
||||
echo 'CFLAGS=-Werror -Wall -Wextra -pedantic -fanalyzer' >> build.conf
|
||||
cat build.conf
|
||||
make && make ayadoc
|
||||
- name: Install Parsee
|
||||
run: 'make install'
|
||||
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
|
|
@ -4,29 +4,11 @@ parsee*
|
|||
parsee
|
||||
*.swp
|
||||
.*
|
||||
data*
|
||||
data*/*
|
||||
Makefile
|
||||
configure
|
||||
gmon.out
|
||||
data
|
||||
data/*
|
||||
|
||||
tools/out
|
||||
tools/out/*
|
||||
|
||||
ayaya/*
|
||||
ayaya
|
||||
|
||||
#ctags
|
||||
tags
|
||||
|
||||
# Whitelists
|
||||
!etc/*
|
||||
!etc/**
|
||||
!.forgejo
|
||||
!.forgejo/*
|
||||
!.forgejo/**
|
||||
|
||||
!.guix
|
||||
!.guix/*
|
||||
!.guix/**
|
||||
|
||||
|
|
|
|||
|
|
@ -1,82 +0,0 @@
|
|||
(define-module (parsee)
|
||||
#:use-module (guix packages)
|
||||
#:use-module (guix git-download)
|
||||
#:use-module (guix build-system gnu)
|
||||
#:use-module (guix gexp)
|
||||
#:use-module (guix utils)
|
||||
#:use-module (gnu packages tls)
|
||||
#:use-module (gnu packages databases)
|
||||
#:use-module ((guix licenses) #:prefix license:))
|
||||
|
||||
(define vcs-file?
|
||||
(or (git-predicate
|
||||
(dirname (dirname (current-source-directory))))
|
||||
(const #t)))
|
||||
|
||||
(define-public cytoplasm
|
||||
(let ((commit "32f31fe6d61583630995d956ed7bd7566c4dc14f")
|
||||
(revision "0"))
|
||||
(package
|
||||
(name "cytoplasm")
|
||||
(version (git-version "0.4.1" revision commit))
|
||||
(source
|
||||
(origin
|
||||
(method git-fetch)
|
||||
(uri (git-reference
|
||||
(url "https://git.telodendria.io/Telodendria/Cytoplasm")
|
||||
(commit commit)))
|
||||
(file-name (git-file-name name version))
|
||||
(sha256
|
||||
(base32 "09x5xfswryf3wjs1synh972yr2fmpjmffi7pjyjdzb4asqh4whrv"))))
|
||||
(build-system gnu-build-system)
|
||||
(arguments
|
||||
(list
|
||||
#:tests? #f
|
||||
#:configure-flags #~'("--with-lmdb")
|
||||
#:phases #~(modify-phases %standard-phases
|
||||
(add-before 'configure 'add-ld-flags
|
||||
(lambda _
|
||||
(substitute* "./configure"
|
||||
(("(LDFLAGS=\"\\$\\{LIBS\\} \\$\\{LDFLAGS\\})\"" all flags)
|
||||
(string-append flags " -Wl,-rpath=" #$output "/lib\"")))
|
||||
(mkdir-p (string-append #$output "/lib"))))
|
||||
(replace 'configure
|
||||
(lambda* (#:key configure-flags #:allow-other-keys)
|
||||
(apply invoke `("./configure"
|
||||
,(string-append "--prefix=" #$output)
|
||||
,@configure-flags)))))))
|
||||
(inputs (list openssl lmdb))
|
||||
(home-page "https://git.telodendria.io/Telodendria/Cytoplasm")
|
||||
(synopsis "General-purpose high-level networked C library")
|
||||
(description "Cytoplasm is a general-purpose C library for creating
|
||||
high-level (particularly networked and multi-threaded) C applications.")
|
||||
(license license:expat))))
|
||||
|
||||
(define-public parsee
|
||||
(package
|
||||
(name "parsee")
|
||||
(version "0.0.1-git")
|
||||
(source
|
||||
(local-file "../.." "parsee-checkout"
|
||||
#:recursive? #t
|
||||
#:select? vcs-file?))
|
||||
(build-system gnu-build-system)
|
||||
(arguments
|
||||
(list
|
||||
#:tests? #f
|
||||
#:make-flags #~(list "CC=gcc"
|
||||
(string-append "CYTO_INC=" #$cytoplasm "/include")
|
||||
(string-append "CYTO_LIB=" #$cytoplasm "/lib")
|
||||
(string-append "PREFIX=" #$output))
|
||||
#:phases #~(modify-phases %standard-phases
|
||||
(replace 'configure
|
||||
(lambda* (#:key inputs #:allow-other-keys)
|
||||
(let ((gcc (string-append (assoc-ref inputs "gcc") "/bin/gcc")))
|
||||
(invoke gcc "configure.c" "-o" "configure")
|
||||
(invoke "./configure")))))))
|
||||
(home-page "https://git.kappach.at/lda/Parsee")
|
||||
(synopsis "Jealous Matrix to XMPP bridge")
|
||||
(description "Parsee is a Matrix-XMPP bridge written in C99 with Cytoplasm.")
|
||||
(license license:agpl3+)))
|
||||
|
||||
parsee
|
||||
84
CHANGELOG.md
84
CHANGELOG.md
|
|
@ -1,84 +0,0 @@
|
|||
# Parsee changelogs
|
||||
|
||||
This document holds changes logged between versions, ever
|
||||
since `tomboyish-bridges-adventure`'s release.
|
||||
Dates are to be written as DD/MM/YYYY. Please update the
|
||||
changelog as you go, no one wants to keep track of every
|
||||
commit done between releases.
|
||||
|
||||
## Release
|
||||
*There is currently no full releases of Parsee*
|
||||
|
||||
## Beta
|
||||
*There is currently no beta releases of Parsee*
|
||||
|
||||
## Alpha
|
||||
|
||||
### v0.3.0[lunar-rainbow] <XX/XX/2025>
|
||||
This is the first release of 2025!
|
||||
TBD
|
||||
#### New things
|
||||
- Allow admins to remove users from the nofly list.
|
||||
- Parsee can now access the homeserver/component locally (rather than over the network)
|
||||
- The endpoint for media has been changed to '/media/[SERV]/[ID]?hmac=...'
|
||||
- Add parsee-plumb tool to manage plumbing from outside Parsee if it is not running.
|
||||
- Add packaging for Guix (see #18)
|
||||
- Parsee is now dependent on authenticated media to function.
|
||||
(Please, Matrix, do _not_ touch anything, I do _not_ want to mess with this anymore)
|
||||
#### Bugfixes
|
||||
- Fix potential infinite loops when processing some messages.
|
||||
- Parsee now handles pinging puppets from Matrix more sanely.
|
||||
|
||||
### v0.2.0[star-of-hope] <8/11/2024>
|
||||
Fixes some media metadata things, replaces the build system,
|
||||
tries out avatar support some more, MUC contexts, and speeds
|
||||
up Parsee just a bit. MbedTLS support is still highly unstable.
|
||||
#### New things
|
||||
- Start dealing with some basic PEP and vCard-based avatar
|
||||
support from both sides.
|
||||
- Banning Parsee from a XMPP MUC effectively unlinks it from
|
||||
the MUC.
|
||||
- Start adding basic documentation to Parsee, through the
|
||||
wiki page.
|
||||
- Add MUC whitelists for plumbing, alongside a `whitelist` tool
|
||||
- Add parameter for setting the max stanza size allowed, with
|
||||
the default being the XMPP minimum of 10000 bytes.
|
||||
- Allows experimental MbedTLS through a specific Cytoplasm
|
||||
patch (though still unstable AND slow).
|
||||
- Does basic work towards NetBSD support(especially with DEC Alpha)
|
||||
- Start contextualising XMPP commands(all/Parsee admins/MUCs).
|
||||
|
||||
#### Bugfixes
|
||||
- Adds more information to media events so that clients can
|
||||
behave.
|
||||
- Fixes issues where SIGPIPE can actually just kill Parsee.
|
||||
- "Lone" XMPP messages no longer render weirdly on Element
|
||||
Android's weird rendering.
|
||||
- Start fixing bug where Parsee takes several seconds to send
|
||||
a message coming from XMPP with MbedTLS(it is still slow and
|
||||
unstable)
|
||||
- Fix issue where the XMPP server would kill Parsee if avatars
|
||||
are too large.
|
||||
- Refactor some of the code to abstract sending stanzas down
|
||||
the wire to a specific function, thus allowing us to check
|
||||
for certain conditions more easily(for example, verifying the
|
||||
size on the fly, or having extensions being able to postprocess
|
||||
the stanza).
|
||||
- Parsee now stops when a stream error is received, instead of
|
||||
being in a limbo state.
|
||||
- The format for links has been changed to be slighlty *less*
|
||||
annoying.
|
||||
|
||||
#### Deprecated features
|
||||
- The old `build.c` and `Makefile`s used for building are removed,
|
||||
and replaced by the `configure.c` C file(/script via TCC).
|
||||
|
||||
### v0.1.0[tomboyish-bridges-adventure] <9/9/2024>
|
||||
Nothing much to say, but this is the first alpha release
|
||||
of Parsee. May occasionally deadlock.
|
||||
#### New things
|
||||
*NONE*
|
||||
#### Bugfixes
|
||||
*NONE*
|
||||
#### Deprecated features
|
||||
*NONE*
|
||||
14
CONTRIBUTORS
14
CONTRIBUTORS
|
|
@ -1,14 +0,0 @@
|
|||
# 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,3 +1,7 @@
|
|||
Some dates for Parsee-related events. They mostly serve as LDA's TODOs with
|
||||
a strict deadline:
|
||||
- Get star-of-hope out by November 8
|
||||
- ~September 2024[star-of-hope]:
|
||||
Get Parsee into the _Phantasmagoria of Bug View_ stage (essentially
|
||||
v0.0.1 for a public testing) once I can afford `yama`, and start
|
||||
bridging the Matrix room alongside a shiny XMPP MUC, bridged by
|
||||
yours truly.
|
||||
|
|
|
|||
6
LICENSE
6
LICENSE
|
|
@ -1,6 +1,6 @@
|
|||
For the files src/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(AGPL-3.0-or-later)
|
||||
For the files src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile, see COPYING.CC0
|
||||
to be present.
|
||||
For any other file in src/, see COPYING.AGPL as the primary license.
|
||||
|
||||
As Parsee depends on Cytoplasm, its license is left here in COPYING.CYTO
|
||||
|
||||
|
|
|
|||
89
Makefile
Normal file
89
Makefile
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Makefile for building Parsee
|
||||
# ================================
|
||||
# TODO: Consider making something akin to a configure script that checks
|
||||
# for dependencies, or maybe even use *autoconf* (the devil!)
|
||||
|
||||
|
||||
# =========================== Parsee Flags =============================
|
||||
|
||||
# phantasmagoria - test runs without an actual code
|
||||
CODE=phantasmagoria
|
||||
NAME=Parsee
|
||||
VERSION=0.0.0
|
||||
|
||||
REPOSITORY=$(shell git remote get-url origin)
|
||||
|
||||
# =========================== Compilation Flags ============================
|
||||
CYTO_INC ?=/usr/local/include/ # Where Cytoplasm's include path is
|
||||
# located.
|
||||
CYTO_LIB ?=/usr/local/lib # Where's Cytoplasm's library is
|
||||
# located.
|
||||
PREFIX ?=/usr/local
|
||||
|
||||
SOURCE=src
|
||||
OBJECT=build
|
||||
AYAS=ayaya
|
||||
ETC=etc
|
||||
INCLUDES=src/include
|
||||
CC=cc
|
||||
CFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" -g -ggdb -Wall -Werror
|
||||
LDFLAGS=-L $(CYTO_LIB) -lCytoplasm -g -ggdb
|
||||
AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)"
|
||||
BINARY=parsee
|
||||
# ============================ Compilation =================================
|
||||
SRC_FILES:=$(shell find $(SOURCE) -name '*.c')
|
||||
OBJ_FILES:=${subst $(SOURCE)/,$(OBJECT)/,$(patsubst %.c, %.o, $(SRC_FILES))}
|
||||
|
||||
CPP_FILES:=$(shell find $(INCLUDES) -name '*.h')
|
||||
AYA_FILES:=${subst $(INCLUDES)/,$(AYAS)/,$(patsubst %.h, %.html, $(CPP_FILES))}
|
||||
|
||||
all: binary utils
|
||||
|
||||
binary: $(OBJ_FILES)
|
||||
$(CC) $(LDFLAGS) $(OBJ_FILES) -o $(BINARY)
|
||||
|
||||
clean:
|
||||
rm -rf $(OBJECT) $(BINARY) $(AYAS)
|
||||
|
||||
$(OBJECT)/%.o: $(SOURCE)/%.c
|
||||
@mkdir -p $(shell dirname "$@")
|
||||
$(CC) -c $(CFLAGS) $< -o $@
|
||||
|
||||
utils:
|
||||
(cd tools && make)
|
||||
|
||||
ayadoc: utils $(AYA_FILES)
|
||||
|
||||
$(AYAS)/%.html: $(INCLUDES)/%.h
|
||||
@mkdir -p $(shell dirname "$@")
|
||||
tools/out/aya $(AFLAGS) -i $< -o $@
|
||||
|
||||
|
||||
# Installs everything.
|
||||
install: binary utils ayadoc install_setup install_parsee install_tools install_aya install_man
|
||||
@echo Installed $(NAME) to $(PREFIX)!
|
||||
|
||||
install_setup:
|
||||
install -dm755 "$(PREFIX)/bin"
|
||||
install -dm755 "$(PREFIX)/share/doc"
|
||||
install -dm755 "$(PREFIX)/man"
|
||||
|
||||
install_parsee:
|
||||
install -Dm755 "$(BINARY)" "$(PREFIX)/bin/$(BINARY)"
|
||||
|
||||
TOOLS:=$(shell find 'tools/out' -name '*')
|
||||
ITOOL:=${subst tools/out/,$(PREFIX)/bin/,$(patsubst tools/out/%, tools/out/$(BINARY)-%, $(TOOLS))}
|
||||
install_tools: $(ITOOL)
|
||||
$(PREFIX)/bin/$(BINARY)-%: tools/out/%
|
||||
install -Dm755 "$<" "$@"
|
||||
|
||||
IHTML:=${subst $(AYAS)/,$(PREFIX)/share/doc/$(BINARY)/,$(AYA_FILES)}
|
||||
install_aya: $(IHTML)
|
||||
$(PREFIX)/share/doc/$(BINARY)/%: $(AYAS)/%
|
||||
install -Dm644 "$<" "$@"
|
||||
|
||||
MPAGE:=$(shell find 'etc/man' -name '*.*')
|
||||
PPAGE:=${subst etc/man/,$(PREFIX)/man/,$(MPAGE)}
|
||||
install_man: $(PPAGE)
|
||||
$(PREFIX)/man/%: etc/man/%
|
||||
install -Dm644 "$<" "$@"
|
||||
85
README.MD
85
README.MD
|
|
@ -1,54 +1,39 @@
|
|||
# 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.
|
||||
Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is NOT a drop-in replacment.
|
||||
|
||||
## Why?
|
||||
### Naming
|
||||
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi),
|
||||
a "*bridge* princess". The other name you actually can sometimes see explains itself, so I won't
|
||||
be talking about it.
|
||||
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi), a
|
||||
"*bridge* princess".
|
||||
|
||||
### 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*,
|
||||
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))
|
||||
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*.
|
||||
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, was literally where Parsee+XMPP ran for
|
||||
a good chunk of Parsee's start.)
|
||||
|
||||
### "Why not just use Matrix lol"
|
||||
### "Why not just use XMPP lol"
|
||||
These two having the same answer should be enough information.
|
||||
These two having the same answer should be enough information. Also can I *just* have fun?
|
||||
One could also argue that both sides need to migrate(onboard) the other side, so
|
||||
a bridge may be a good way to start.
|
||||
|
||||
## BUILDING
|
||||
```sh
|
||||
$ 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
|
||||
$ 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.
|
||||
```
|
||||
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.
|
||||
change a few variables (you can set `CYTO_INC` and `CYTO_LIB`)
|
||||
|
||||
### 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
|
||||
```
|
||||
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).
|
||||
|
||||
## RUNNING
|
||||
First off, you may want to configure Parsee by running the `config` tool(generally named
|
||||
|
|
@ -61,8 +46,7 @@ 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 \
|
||||
-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.
|
||||
-p 5347
|
||||
```
|
||||
|
||||
If everything goes well, it should generate a `parsee.json` file.
|
||||
|
|
@ -75,44 +59,31 @@ 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.
|
||||
## TODOS
|
||||
- Add [libomemo](https://github.com/gkdr/libomemo) 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~~ 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.
|
||||
put in the work of either forking off libolm or making a binding to KappaChat.
|
||||
- Get rid of the '?'-syntax and use another invalid Matrix char/valid XMPP char
|
||||
('$'?) for escaped?
|
||||
- PROPER FUCKING AVATARS
|
||||
XEP-0084 IS THE WORST PIECE OF SHIT KNOWN TO MAN. If any Jabberbros want to
|
||||
look at terrible code/XML and suggest things to have *proper* avatar support,
|
||||
I'm all in.
|
||||
- Consider making room/MUC admins/owners be able to plumb instead of it being
|
||||
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.
|
||||
- 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
|
||||
|
|
@ -129,7 +100,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 there. If you *really* want, you may just open
|
||||
Please avoid asking for help/issues here. 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)
|
||||
|
|
|
|||
42
XEPS-TBD.TXT
42
XEPS-TBD.TXT
|
|
@ -1,19 +1,6 @@
|
|||
XEPs current supported are in src/XMPPThread, at the IQ disco advertising.
|
||||
|
||||
Somewhat implemented XEPs:
|
||||
v https://xmpp.org/extensions/xep-0050.html
|
||||
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
||||
also a nice to have.
|
||||
There are commands, but not a lot of them as of now, and localisation
|
||||
is missing.
|
||||
v https://xmpp.org/extensions/xep-0421.html
|
||||
Using the occupant ID in semi-anonymous MUCs is a desirable property.
|
||||
I dont know of a lot of places that don't use the occupant ID anymore
|
||||
within Parsee.
|
||||
v "also it [Bifrost] doesn't respect voice either"
|
||||
As far as I am aware, works.
|
||||
v https://xmpp.org/extensions/xep-0425.html
|
||||
As mentionned in #2, moderation _needs_ to be done.
|
||||
~ https://xmpp.org/extensions/xep-0085.html
|
||||
Only XMPP->Matrix at the moment. Still need to figure out how to
|
||||
get typing indicators as an AS.
|
||||
|
|
@ -21,14 +8,21 @@ 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
|
||||
TODO: Add support from Matrix.
|
||||
HALF-IMPLEMENTED: Removing reacts won't work.
|
||||
~ 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
|
||||
~ https://xmpp.org/extensions/xep-0050.html
|
||||
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
||||
also a nice to have.
|
||||
There are commands, but not a lot of them as of now, and localisation
|
||||
is missing.
|
||||
~ https://xmpp.org/extensions/xep-0421.html
|
||||
Using the occupant ID in semi-anonymous MUCs is a desirable property.
|
||||
I dont know of a lot of places that don't use the occupant ID anymore
|
||||
within Parsee.
|
||||
~ https://xmpp.org/extensions/xep-0425.html
|
||||
As mentionned in #2, moderation _needs_ to be done.
|
||||
|
||||
For future XEPs:
|
||||
- https://xmpp.org/extensions/xep-0449.html
|
||||
|
|
@ -38,16 +32,22 @@ 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:
|
||||
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
|
||||
unreliable, however.
|
||||
x https://xmpp.org/extensions/xep-0080.html
|
||||
Can't think of a good analogy to these...
|
||||
|
||||
|
||||
Not XEPs, but ideas that _needs_ to be added:
|
||||
v "also it [Bifrost] doesn't respect voice either" -> Send a form on moderated
|
||||
MUCs (which is standard, so its not too bad!). Currently WIP, and barely tested.
|
||||
~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d
|
||||
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
|
||||
|
||||
|
|
|
|||
10
build.conf
10
build.conf
|
|
@ -1,10 +0,0 @@
|
|||
CODE=lunar-rainbow
|
||||
NAME=Parsee
|
||||
VERSION=0.3.0
|
||||
BINARY=parsee
|
||||
SOURCE=src
|
||||
INCLUDES=src/include
|
||||
OBJECT=build
|
||||
CC=cc
|
||||
CFLAGS=-O3
|
||||
PREFIX=/usr
|
||||
727
configure.c
727
configure.c
|
|
@ -1,727 +0,0 @@
|
|||
/* 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,40 +0,0 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee-adminify 1 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee-adminify - bootstrap an admin to a new Parsee server
|
||||
|
||||
.SH SYNOPSIS
|
||||
parsee-adminify
|
||||
.B [DB path]
|
||||
.B [user]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.I parsee-adminify
|
||||
is a tool to add/list the Parsee administrator list. It currently only
|
||||
allows you to see/add admins to it, using simplified globrules.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B [config]
|
||||
The configuration file used by the Parsee daemon.
|
||||
.TP
|
||||
.B [user]
|
||||
If set, adds the glob
|
||||
.I [user]
|
||||
as a Parsee administrator. Otherwise, lists all administrators in
|
||||
the Parsee instance.
|
||||
|
||||
.SH BUGS
|
||||
.PP
|
||||
None as I know of. If anyone notices any issues with this, please
|
||||
let me know.
|
||||
|
||||
.SH LICENSE
|
||||
Unlike Parsee,
|
||||
.B parsee-adminify
|
||||
is under the CC0/PD.
|
||||
|
||||
.SH SEE ALSO
|
||||
.B parsee-config(1), parsee-aya(1), parsee(1)
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee-aya 1 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee-aya - generate some nice Ayaya! documentation
|
||||
|
||||
.SH SYNOPSIS
|
||||
parsee-aya
|
||||
.B [-i HEADER]
|
||||
.B [-o HTML]
|
||||
.B [-C STYLESHEET]
|
||||
.B [-p NAME]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.I parsee-aya
|
||||
is a tool to generate Ayadocs(HTML documentation) from a formatted
|
||||
header file. See a Parsee header file for an example of the inner usage.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR -i HEADER
|
||||
The input header file to process out.
|
||||
.TP
|
||||
.BR -o HTML
|
||||
The HTML file to write out the Ayadoc.
|
||||
.TP
|
||||
.BR -C STYLESHEET
|
||||
A stylesheet file to use for Ayadocs.
|
||||
.TP
|
||||
.BR -p NAME
|
||||
The project's name. If unavailable, defaults to Ayaya!
|
||||
|
||||
.SH LICENSE
|
||||
Unlike Parsee,
|
||||
.B parsee-aya
|
||||
is under the CC0/PD.
|
||||
|
||||
.SH SEE ALSO
|
||||
.B parsee-config(1), parsee-adminify(1), parsee(1)
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee-config 1 "Parsee Utility" "lunar-rainbow"
|
||||
|
||||
.SH NAME
|
||||
parsee-config - generate a basic configuration file
|
||||
|
||||
.SH SYNOPSIS
|
||||
parsee-config
|
||||
.B [-H HOMESERVER_NAME]
|
||||
.B [-s SHARED_SECRET]
|
||||
.B [-m MEDIA_URL]
|
||||
.B [-J JABBER_HOST]
|
||||
.B [-j JABBER_ADDR]
|
||||
.B [-p JABBER_PORT]
|
||||
.B [-d DATABASE]
|
||||
.B [-M MAX_STANZA]
|
||||
.B [-S DATABASE size]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.I parsee-config
|
||||
creates a basic configuration file to initialise a Parsee instance with the
|
||||
given input flags, and makes a basic database. A basic example of
|
||||
.I parsee-config
|
||||
would be this(for a blow.hole server, with a xmpp.blow.hole JCP on Prosody,
|
||||
and a 128MB LMDB backend)
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
.\}
|
||||
.nf
|
||||
$ parsee-config \\
|
||||
-d '/var/lib/parsee' \\
|
||||
-m 'https://pmedia.blow.hole' \\
|
||||
-H 'blow.hole' \\
|
||||
-s 'The Dark Shared Secret' \\
|
||||
-J 'xmpp.blow.hole' \\
|
||||
-j 'localhost' \\
|
||||
-S 128
|
||||
.fi
|
||||
.if n \{\
|
||||
.RE
|
||||
.\}
|
||||
.sp
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR -H HOMESERVER_NAME
|
||||
.I HOMESERVER_NAME
|
||||
points to the homeserver name, without delegation (which is autosensed).
|
||||
For example, if you except Parsee users to be on
|
||||
.IR @something:blow.hole
|
||||
, then it should be set to
|
||||
.IR blow.hole .
|
||||
.TP
|
||||
.BR -s SHARED_SECRET
|
||||
.I SHARED_SECRET
|
||||
is a shared secret known by Parsee and the XMPP component to authenticate.
|
||||
.TP
|
||||
.BR -M MAX_STANZA
|
||||
.I MAX_STANZA
|
||||
is the maximum stanza size accepted by the XMPP host. If it is less than 10000 bytes, then it shall be set to that limit(the standardised value).
|
||||
.TP
|
||||
.BR -m MEDIA_URL
|
||||
.I MEDIA_URL
|
||||
is an optional field used by Parsee as an address that points to Matrix
|
||||
media. It must be publicly accessible (behind a reverse proxy to HTTP:7642)
|
||||
.TP
|
||||
.BR -J JABBER_HOST
|
||||
.I JABBER_HOST
|
||||
is used as the component host for Parsee.
|
||||
.TP
|
||||
.BR -j JABBER_ADDR
|
||||
.I JABBER_ADDR
|
||||
can optionally be used to change the hostname Parsee will try to contact
|
||||
for XMPP. Users should ideally use localhost (or a hostname pointing to
|
||||
the server itself), as XMPP component streams are not encrypted.
|
||||
.TP
|
||||
.BR -p JABBER_PORT
|
||||
.I JABBER_PORT
|
||||
is used as the component post for Parsee. Parsee uses it alongside
|
||||
.I JABBER_HOST
|
||||
to connect to
|
||||
.I JABBER_HOST:JABBER_PORT
|
||||
over TCP.
|
||||
.TP
|
||||
.BR -d DATABASE
|
||||
The directory inwhich Parsee stores its database(LMDB/flat-file). Whenever the
|
||||
database is flat or LMDB is dictated by the presence of the -S flag, and
|
||||
whenever Cytoplasm has LMDB enabled.
|
||||
.TP
|
||||
.BR -S SIZE
|
||||
If set, enables LMDB in Parsee, and sets its max DB size to
|
||||
.BR SIZE MiB .
|
||||
|
||||
|
||||
.SH LICENSE
|
||||
Unlike Parsee,
|
||||
.B parsee-config
|
||||
is under the CC0/PD.
|
||||
|
||||
.SH SEE ALSO
|
||||
.B parsee-aya(1), parsee-adminify(1), parsee(1)
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee 1 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee - the jealous XMPP-Matrix bridge
|
||||
|
||||
.SH SYNOPSIS
|
||||
parsee
|
||||
.B [-J num]
|
||||
.B [-H num]
|
||||
.B [-C file]
|
||||
.B [-g]
|
||||
.B [-v]
|
||||
.B [-h]
|
||||
|
||||
.SH DESCRIPTION
|
||||
Parsee is a
|
||||
.B bridging program
|
||||
, that is, it connects two chat protocols together (here, XMPP/Jabber,
|
||||
and Matrix).
|
||||
.PP
|
||||
As such, a user on XMPP can communicate with Matrix users, and
|
||||
vice-versa with it.
|
||||
.PP
|
||||
Currently, Parsee is under
|
||||
.BI alpha .
|
||||
This means that it is still subject to bugs, flaws, and may change in a
|
||||
backwards-incompatible manner at any time.
|
||||
|
||||
.SH FIRST RUN
|
||||
To start with a new install of Parsee(assuming you have a homeserver
|
||||
with AS support and a XMPP server with JCP support, and that you know
|
||||
the shared secret/domain for that component. Guides for that are
|
||||
outside the scope of this manpage.), start by running
|
||||
.B parsee-config(1)
|
||||
with the flags provided in it, according to your configuration.
|
||||
.PP
|
||||
This should generate a
|
||||
.I parsee.json
|
||||
file, which is the configuration files for Parsee, and a directory where
|
||||
the Parsee database may reside. You can then run
|
||||
.I parsee -g
|
||||
in that directory, which should generate a
|
||||
.I parsee.yaml
|
||||
file, this time with the generated AS configuration file, which you can
|
||||
apply to your homeserver(how is implementation-dependent, but it is
|
||||
generally a matter of modifying a configuration file to add the path
|
||||
to such file).
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR -J num
|
||||
Set the number of threads used for XMPP stanza processing to
|
||||
.I num\[char46]
|
||||
.TP
|
||||
.BR -H num
|
||||
Set the number of threads used for HTTP request processing to
|
||||
.I num\[char46]
|
||||
.TP
|
||||
.BR -C file
|
||||
Sets the configuration JSON path to
|
||||
.I file\[char46]
|
||||
.PP
|
||||
Defaults to
|
||||
.I parsee.json
|
||||
if none can be found.
|
||||
.TP
|
||||
.BR -g
|
||||
Generates a
|
||||
.I parsee.yaml
|
||||
file to be used as an Application-Service entry within Matrix, and
|
||||
exits.
|
||||
.TP
|
||||
.BR -v
|
||||
Verbose logging of Parsee's behaviour. Recommended when testing Parsee
|
||||
for bugs/hints.
|
||||
.TP
|
||||
.BR -h
|
||||
Prints the command list with descriptions.
|
||||
|
||||
.SH BUGS
|
||||
.PP
|
||||
Sometimes Parsee will not respond to ^C requests properly, which
|
||||
causes a system administrator to have to invoke SIGKILL.
|
||||
.PP
|
||||
Parsee seems to grow slowly in memory usage when messages are bridged
|
||||
which, in the long run, could cause a memory error. All of the memory is
|
||||
tracked, however, which means that this isn't exactly a leak.
|
||||
.PP
|
||||
Some important features still aren't implemented yet(e.g being able to join
|
||||
a MUC from XMPP)
|
||||
|
||||
.SH CHATROOMS
|
||||
You may talk about Parsee on these rooms(on Matrix and XMPP):
|
||||
.RE
|
||||
.IP xmpp:parsee@conference.monocles.eu?join
|
||||
for XMPP, which is bridged along Matrix
|
||||
.IP #parsee:tedomum.net
|
||||
for Matrix, which is bridged along Parsee
|
||||
.RS
|
||||
|
||||
.SH AUTHORS
|
||||
." Contributors, feel free to put your names here in a PR, as long as
|
||||
." it is acceptable
|
||||
. PP
|
||||
.BR LDA:
|
||||
main maintainer of Parsee, accessible over XMPP at lda@freetards.xyz
|
||||
and over Matrix as @fourier:ari.lt.
|
||||
|
||||
.SH LICENSE
|
||||
Parsee is available under the AGPL3, but has some code under CC0/PD, and
|
||||
some from Cytoplasm itself. Please see
|
||||
.I https://git.kappach.at/lda/Parsee
|
||||
for more information.
|
||||
|
||||
.SH SEE ALSO
|
||||
.B parsee-config(1), parsee-adminify(1), parsee-aya(1)
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,70 +0,0 @@
|
|||
." The last field is the codename, by the way.
|
||||
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
|
||||
.TH parsee-bridge-guidebook 7 "Parsee Utility" "star-of-hope"
|
||||
|
||||
.SH NAME
|
||||
parsee-bridge-guidebook - A short guidebook on running a Parsee bridge
|
||||
|
||||
.SH INTRODUCTION
|
||||
.P
|
||||
This manpage is intended to be a guidebook for Parsee administrators. It
|
||||
is meant to show how to create an instance with an XMPP-Matrix server
|
||||
(though it cannot be specific, due to their ecosystem diversity), how to
|
||||
plumb rooms, and moderate them through.
|
||||
.P
|
||||
It also assumes Parsee is properly built and installed, in which case you
|
||||
are seeing this from
|
||||
.I man
|
||||
itself.
|
||||
|
||||
.SH CONVENTIONS
|
||||
This page shall assume a few things that
|
||||
.B must
|
||||
be changed to fit your configuration. Please read those carefully, or it
|
||||
will come and bite at you!
|
||||
|
||||
.P
|
||||
First off, it assumes that you have a domain at
|
||||
.I blow.hole
|
||||
and that any other domains related to Parsee are it's subdomains, as
|
||||
you'll see.
|
||||
|
||||
.P
|
||||
We also assume you're planning on making the XMPP component available at
|
||||
.I j.blow.hole ,
|
||||
and that Parsee can reach it by using
|
||||
.I localhost:1234 .
|
||||
It is highly recommended that Parsee can reach the component locally, as
|
||||
the stream cannot be encrypted!
|
||||
|
||||
.P
|
||||
The Parsee HTTP server (which is used for media and the appservice) shall
|
||||
be reached through
|
||||
.I https://p.blow.hole/
|
||||
via a reverse proxy. This manual shall only show you what port to make
|
||||
available, as there are many reverse proxy options available.
|
||||
|
||||
.P
|
||||
Finally, the Matrix server will be publicly known as
|
||||
.I m.blow.hole
|
||||
.
|
||||
|
||||
That is, if
|
||||
.B bob
|
||||
is in it, then they shall be known as
|
||||
.I @bob:m.blow.hole .
|
||||
|
||||
.SH SETTING UP
|
||||
Setting up Parsee mainly involves creating a valid configuration file
|
||||
and the database. Most of this however is dealt with by
|
||||
.I parsee-config(1)
|
||||
TODO
|
||||
|
||||
.P
|
||||
|
||||
.SH LICENSE
|
||||
This document is under public domain, or CC0 if not allowed by local law.
|
||||
|
||||
.SH SEE ALSO
|
||||
.B parsee(1), parsee-cmd-syntax(7)
|
||||
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
." 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)
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
This directory is for any PNG media that needs to be integrated into Parsee.
|
||||
Each file here is Base64-encoded then defined as a const char[] symbol with
|
||||
the name being a function of the PNG filename: N(FILE.png)=media_FILE
|
||||
|
||||
NOTE: Medias should be about ~1024B MAX. Avoid to stride for anything larger.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 871 B |
Binary file not shown.
|
Before Width: | Height: | Size: 542 B |
1
guix.scm
1
guix.scm
|
|
@ -1 +0,0 @@
|
|||
.guix/modules/parsee.scm
|
||||
971
src/AS.c
971
src/AS.c
|
|
@ -55,5 +55,976 @@ ASAuthenticateRequest(const ParseeConfig *data, HttpClientContext *ctx)
|
|||
HttpRequestHeader(ctx, "Authorization", bearer);
|
||||
Free(bearer);
|
||||
}
|
||||
bool
|
||||
ASRegisterUser(const ParseeConfig *conf, char *user)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
HttpStatus status;
|
||||
if (!conf || !user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create user. We don't actually care about the value as we can
|
||||
* masquerade, as long as it exists. */
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, "/_matrix/client/v3/register"
|
||||
);
|
||||
json = HashMapCreate();
|
||||
|
||||
HashMapSet(json,"type",JsonValueString("m.login.application_service"));
|
||||
|
||||
user = ParseeGetLocal(user);
|
||||
HashMapSet(json,"username",JsonValueString(user));
|
||||
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
status = ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
Free(user);
|
||||
|
||||
return status == HTTP_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ASPing(const ParseeConfig *conf)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path;
|
||||
if (!conf)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v1/appservice/",
|
||||
"Parsee%20XMPP",
|
||||
"/ping"
|
||||
);
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
}
|
||||
void
|
||||
ASInvite(const ParseeConfig *conf, char *id, char *invited)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *bridge;
|
||||
if (!conf || !id || !invited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bridge = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/invite",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(invited));
|
||||
HashMapSet(json, "reason", JsonValueString("Pass over."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
}
|
||||
void
|
||||
ASBan(const ParseeConfig *conf, char *id, char *banned)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *bridge;
|
||||
if (!conf || !id || !banned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bridge = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/ban",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
}
|
||||
void
|
||||
ASKick(const ParseeConfig *conf, char *id, char *banned)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *bridge;
|
||||
if (!conf || !id || !banned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bridge = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/kick",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
}
|
||||
char *
|
||||
ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *ret;
|
||||
if (!conf || !id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!masquerade)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
masquerade = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
masquerade = HttpUrlEncode(masquerade);
|
||||
}
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/join/", id, "?",
|
||||
"user_id=", masquerade
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(GrabString(json, 1, "room_id"));
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(masquerade);
|
||||
Free(id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
ASLeave(const ParseeConfig *conf, char *id, char *masquerade)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path;
|
||||
if (!conf || !id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!masquerade)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
masquerade = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
masquerade = HttpUrlEncode(masquerade);
|
||||
}
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/leave?",
|
||||
"user_id=", masquerade
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(masquerade);
|
||||
Free(id);
|
||||
}
|
||||
void
|
||||
ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
char *path;
|
||||
if (!conf || !id || !type || !mask || !state)
|
||||
{
|
||||
JsonFree(state);
|
||||
return;
|
||||
}
|
||||
|
||||
path = StrConcat(9,
|
||||
"/_matrix/client/v3/rooms/", id, "/state/",
|
||||
type, "/", key, "?", "user_id=", mask
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, state);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(state);
|
||||
}
|
||||
char *
|
||||
ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
char *path;
|
||||
char *txn, *ret;
|
||||
HashMap *reply;
|
||||
if (!conf || !id || !type || !user || !c)
|
||||
{
|
||||
JsonFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
txn = StrRandom(16);
|
||||
path = StrConcat(9,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
id, "/send/", type, "/", txn, "?",
|
||||
"user_id=", user
|
||||
);
|
||||
Free(txn);
|
||||
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, c);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
||||
JsonFree(reply);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
char *
|
||||
ASCreateRoom(const ParseeConfig *conf, char *by, char *alias)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *id;
|
||||
if (!conf || !by)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/createRoom",
|
||||
"?user_id=", by
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
if (alias)
|
||||
{
|
||||
char *trimmed = StrDuplicate(alias);
|
||||
if (*alias == '#')
|
||||
{
|
||||
char *tmp, cb[2] = { 0 };
|
||||
alias++;
|
||||
Free(trimmed);
|
||||
trimmed = NULL;
|
||||
|
||||
while (*alias && *alias != ':')
|
||||
{
|
||||
cb[0] = *alias;
|
||||
tmp = trimmed;
|
||||
trimmed = StrConcat(2, trimmed, cb);
|
||||
Free(tmp);
|
||||
alias ++;
|
||||
}
|
||||
}
|
||||
HashMapSet(json, "room_alias_name", JsonValueString(trimmed));
|
||||
Free(trimmed);
|
||||
}
|
||||
HashMapSet(json, "visibility", JsonValueString("public"));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
JsonFree(json);
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
return id;
|
||||
}
|
||||
char *
|
||||
ASCreateDM(const ParseeConfig *conf, char *by, char *with)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *id;
|
||||
if (!conf || !by || !with)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/createRoom",
|
||||
"?user_id=", by
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
{
|
||||
Array *invitees = ArrayCreate();
|
||||
|
||||
ArrayAdd(invitees, JsonValueString(with));
|
||||
HashMapSet(json, "invite", JsonValueArray(invitees));
|
||||
HashMapSet(json, "is_direct", JsonValueBoolean(true));
|
||||
}
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
JsonFree(json);
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
return id;
|
||||
}
|
||||
void
|
||||
ASSetAvatar(const ParseeConfig *conf, char *user, char *mxc)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!conf || !user || !mxc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/profile/",
|
||||
user, "/avatar_url", "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "avatar_url", JsonValueString(mxc));
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
Free(user);
|
||||
}
|
||||
void
|
||||
ASSetName(const ParseeConfig *conf, char *user, char *name)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!conf || !user || !name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/profile/",
|
||||
user, "/displayname", "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "displayname", JsonValueString(name));
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
Free(user);
|
||||
}
|
||||
HashMap *
|
||||
ASFind(const ParseeConfig *c, char *room, char *event)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path, *user;
|
||||
if (!c || !room || !event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
||||
path = StrConcat(7,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/event/", event, "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
|
||||
return json;
|
||||
}
|
||||
char *
|
||||
ASGetName(const ParseeConfig *c, char *room, char *user)
|
||||
{
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
char *path, *ret;
|
||||
char *u2 = user;
|
||||
if (!c || !user)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!room)
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/profile/", user, "/displayname"
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(user);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||
);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
ret = StrDuplicate(u2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
user = HttpUrlEncode(user);
|
||||
room = HttpUrlEncode(room);
|
||||
path = StrConcat(4,
|
||||
"/_matrix/client/v3/rooms/", room,
|
||||
"/state/m.room.member/", user
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(user);
|
||||
Free(room);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||
);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
ret = StrDuplicate(u2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
HashMap *
|
||||
ASGetPL(const ParseeConfig *c, char *room)
|
||||
{
|
||||
char *path;
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
if (!c || !room)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
room = HttpUrlEncode(room);
|
||||
path = StrConcat(4,
|
||||
"/_matrix/client/v3/rooms/", room,
|
||||
"/state/m.room.power_levels/", ""
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(room);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(path);
|
||||
|
||||
return reply;
|
||||
}
|
||||
void
|
||||
ASSetPL(const ParseeConfig *conf, char *id, HashMap *m)
|
||||
{
|
||||
char *user;
|
||||
if (!conf || !id || !m)
|
||||
{
|
||||
return;
|
||||
}
|
||||
user = StrConcat(4,
|
||||
"@",conf->sender_localpart,
|
||||
":",conf->server_base
|
||||
);
|
||||
ASSetState(conf, id, "m.room.power_levels", "", user, m);
|
||||
Free(user);
|
||||
}
|
||||
|
||||
char *
|
||||
ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime)
|
||||
{
|
||||
char *size_str, *path, *ret, *user;
|
||||
int i;
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
if (!c || !from)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_str = StrInt(size);
|
||||
user = StrConcat(4, "@",c->sender_localpart,":",c->server_base);
|
||||
path = StrConcat(2,
|
||||
"/_matrix/media/v3/upload?user_id=", user
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
if (size)
|
||||
{
|
||||
HttpRequestHeader(ctx, "Content-Length", size_str);
|
||||
}
|
||||
if (mime)
|
||||
{
|
||||
HttpRequestHeader(ctx, "Content-Type", mime);
|
||||
}
|
||||
HttpRequestSendHeaders(ctx);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
int ch = StreamGetc(from);
|
||||
if (ch == EOF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
StreamPutc(HttpClientStream(ctx), ch);
|
||||
}
|
||||
HttpRequestSend(ctx);
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "content_uri"))
|
||||
);
|
||||
if (!ret)
|
||||
{
|
||||
JsonEncode(reply, StreamStdout(), JSON_PRETTY);
|
||||
StreamFlush(StreamStdout());
|
||||
}
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(size_str);
|
||||
Free(path);
|
||||
Free(user);
|
||||
|
||||
return ret;
|
||||
}
|
||||
char *
|
||||
ASReupload(const ParseeConfig *c, char *from, char **mime)
|
||||
{
|
||||
Uri *uri;
|
||||
HttpClientContext *ctx;
|
||||
unsigned short port;
|
||||
int size = 0, flags = HTTP_FLAG_NONE;
|
||||
char *ret, *content_len;
|
||||
|
||||
if (!c || !from)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uri = UriParse(from);
|
||||
if (!uri)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (uri->port)
|
||||
{
|
||||
port = uri->port;
|
||||
}
|
||||
else if (StrEquals(uri->proto, "https"))
|
||||
{
|
||||
port = 443;
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
|
||||
if (StrEquals(uri->proto, "https"))
|
||||
{
|
||||
flags |= HTTP_FLAG_TLS;
|
||||
}
|
||||
|
||||
ctx = HttpRequest(
|
||||
HTTP_GET, flags, port, uri->host, uri->path
|
||||
);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
if (mime)
|
||||
{
|
||||
*mime = HashMapGet(HttpResponseHeaders(ctx), "content-type");
|
||||
*mime = StrDuplicate(*mime);
|
||||
}
|
||||
|
||||
content_len = HashMapGet(HttpResponseHeaders(ctx), "content-length");
|
||||
if (content_len)
|
||||
{
|
||||
size = strtol(content_len, NULL, 10);
|
||||
}
|
||||
ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
UriFree(uri);
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
ASType(const ParseeConfig *c, char *user, char *room, bool status)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!c || !user || !room)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/typing/", user,
|
||||
"?user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "typing", JsonValueBoolean(status));
|
||||
/* If someone types for 10 minutes straight, they got something weird man. */
|
||||
HashMapSet(json, "timeout", JsonValueBoolean(10 MINUTES));
|
||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
}
|
||||
|
||||
void
|
||||
ASPresence(const ParseeConfig *c, char *user, char *room, char *ev)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!c || !user || !room || !ev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
room = HttpUrlEncode(room);
|
||||
ev = HttpUrlEncode(ev);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/receipt/m.read/", ev,
|
||||
"?user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
Free(room);
|
||||
Free(ev);
|
||||
}
|
||||
|
||||
HashMap *
|
||||
ASGetUserConfig(const ParseeConfig *c, char *user, char *key)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!c || !key)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!user)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", c->sender_localpart,
|
||||
":", c->server_base
|
||||
);
|
||||
user = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
}
|
||||
path = StrConcat(7,
|
||||
"/_matrix/client/v3/user/",
|
||||
user, "/account_data/", key, "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
|
||||
return json;
|
||||
}
|
||||
void
|
||||
ASSetUserConfig(const ParseeConfig *c, char *user, char *key, HashMap *map)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
char *path;
|
||||
if (!c || !key || !map)
|
||||
{
|
||||
JsonFree(map);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", c->sender_localpart,
|
||||
":", c->server_base
|
||||
);
|
||||
user = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
}
|
||||
|
||||
path = StrConcat(7,
|
||||
"/_matrix/client/v3/user/",
|
||||
user, "/account_data/", key, "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, map);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
JsonFree(map);
|
||||
|
||||
return;
|
||||
}
|
||||
void
|
||||
ASRedact(const ParseeConfig *c, char *room, char *user, char *e_id)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *request;
|
||||
char *path, *txn;
|
||||
if (!c || !room || !e_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", c->sender_localpart,
|
||||
":", c->server_base
|
||||
);
|
||||
user = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
}
|
||||
room = HttpUrlEncode(room);
|
||||
e_id = HttpUrlEncode(e_id);
|
||||
txn = StrRandom(16);
|
||||
|
||||
path = StrConcat(9,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/redact/", e_id, "/", txn,
|
||||
"?", "user_id=", user
|
||||
);
|
||||
|
||||
request = HashMapCreate();
|
||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, request);
|
||||
JsonFree(request);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
Free(room);
|
||||
Free(e_id);
|
||||
Free(txn);
|
||||
|
||||
return;
|
||||
}
|
||||
void
|
||||
ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *request;
|
||||
char *path;
|
||||
char *status_str = NULL;
|
||||
if (!c || !user)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case USER_STATUS_ONLINE: status_str = "online"; break;
|
||||
case USER_STATUS_OFFLINE: status_str = "offline"; break;
|
||||
case USER_STATUS_UNAVAILABLE: status_str = "unavailable"; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/presence/",user,"/status",
|
||||
"?user_id=", user
|
||||
);
|
||||
Free(user);
|
||||
|
||||
request = HashMapCreate();
|
||||
HashMapSet(request, "presence", JsonValueString(status_str));
|
||||
if (msg)
|
||||
{
|
||||
HashMapSet(request, "status_msg", JsonValueString(msg));
|
||||
}
|
||||
|
||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, request);
|
||||
JsonFree(request);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,93 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
HashMap *
|
||||
ASFind(const ParseeConfig *c, char *room, char *event)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path, *user;
|
||||
if (!c || !room || !event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
||||
path = StrConcat(7,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/event/", event, "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
void
|
||||
ASRedact(const ParseeConfig *c, char *room, char *user, char *e_id)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *request;
|
||||
char *path, *txn;
|
||||
if (!c || !room || !e_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", c->sender_localpart,
|
||||
":", c->server_base
|
||||
);
|
||||
user = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
}
|
||||
room = HttpUrlEncode(room);
|
||||
e_id = HttpUrlEncode(e_id);
|
||||
txn = StrRandom(16);
|
||||
|
||||
path = StrConcat(9,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/redact/", e_id, "/", txn,
|
||||
"?", "user_id=", user
|
||||
);
|
||||
|
||||
request = HashMapCreate();
|
||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, request);
|
||||
JsonFree(request);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
Free(room);
|
||||
Free(e_id);
|
||||
Free(txn);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
void
|
||||
ASType(const ParseeConfig *c, char *user, char *room, bool status)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!c || !user || !room)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/typing/", user,
|
||||
"?user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "typing", JsonValueBoolean(status));
|
||||
/* If someone types for 5 minutes straight, they got something
|
||||
* weird man. */
|
||||
HashMapSet(json, "timeout", JsonValueInteger(5 MINUTES));
|
||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
}
|
||||
|
||||
void
|
||||
ASPresence(const ParseeConfig *c, char *user, char *room, char *ev)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!c || !user || !room || !ev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
room = HttpUrlEncode(room);
|
||||
ev = HttpUrlEncode(ev);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
room, "/receipt/m.read/", ev,
|
||||
"?user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(user);
|
||||
Free(room);
|
||||
Free(ev);
|
||||
}
|
||||
|
||||
void
|
||||
ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *request;
|
||||
char *path;
|
||||
char *status_str = NULL;
|
||||
if (!c || !user)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case USER_STATUS_ONLINE: status_str = "online"; break;
|
||||
case USER_STATUS_OFFLINE: status_str = "offline"; break;
|
||||
case USER_STATUS_UNAVAILABLE: status_str = "unavailable"; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/presence/",user,"/status",
|
||||
"?user_id=", user
|
||||
);
|
||||
Free(user);
|
||||
|
||||
request = HashMapCreate();
|
||||
HashMapSet(request, "presence", JsonValueString(status_str));
|
||||
if (msg)
|
||||
{
|
||||
HashMapSet(request, "status_msg", JsonValueString(msg));
|
||||
}
|
||||
|
||||
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
ParseeSetRequestJSON(ctx, request);
|
||||
JsonFree(request);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(path);
|
||||
}
|
||||
220
src/AS/Media.c
220
src/AS/Media.c
|
|
@ -1,220 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
char *
|
||||
ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime)
|
||||
{
|
||||
char *size_str, *path, *ret, *user;
|
||||
unsigned int i;
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
if (!c || !from)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_str = StrInt(size);
|
||||
user = StrConcat(4, "@",c->sender_localpart,":",c->server_base);
|
||||
path = StrConcat(2,
|
||||
"/_matrix/media/v3/upload?user_id=", user
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
if (size)
|
||||
{
|
||||
HttpRequestHeader(ctx, "Content-Length", size_str);
|
||||
}
|
||||
if (mime)
|
||||
{
|
||||
HttpRequestHeader(ctx, "Content-Type", mime);
|
||||
}
|
||||
HttpRequestSendHeaders(ctx);
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
int ch = StreamGetc(from);
|
||||
if (ch == EOF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
StreamPutc(HttpClientStream(ctx), ch);
|
||||
}
|
||||
HttpRequestSend(ctx);
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "content_uri"))
|
||||
);
|
||||
if (!ret)
|
||||
{
|
||||
JsonEncode(reply, StreamStdout(), JSON_PRETTY);
|
||||
StreamFlush(StreamStdout());
|
||||
}
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(size_str);
|
||||
Free(path);
|
||||
Free(user);
|
||||
|
||||
return ret;
|
||||
}
|
||||
char *
|
||||
ASReupload(const ParseeConfig *c, char *from, char **mime)
|
||||
{
|
||||
Uri *uri;
|
||||
HttpClientContext *ctx;
|
||||
unsigned short port;
|
||||
int size = 0, flags = HTTP_FLAG_NONE;
|
||||
char *ret, *content_len;
|
||||
|
||||
if (!c || !from)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uri = UriParse(from);
|
||||
if (!uri)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (uri->port)
|
||||
{
|
||||
port = uri->port;
|
||||
}
|
||||
else if (StrEquals(uri->proto, "https"))
|
||||
{
|
||||
port = 443;
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
|
||||
if (StrEquals(uri->proto, "https"))
|
||||
{
|
||||
flags |= HTTP_FLAG_TLS;
|
||||
}
|
||||
|
||||
ctx = HttpRequest(
|
||||
HTTP_GET, flags, port, uri->host, uri->path
|
||||
);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
if (mime)
|
||||
{
|
||||
*mime = HashMapGet(HttpResponseHeaders(ctx), "content-type");
|
||||
*mime = StrDuplicate(*mime);
|
||||
}
|
||||
|
||||
content_len = HashMapGet(HttpResponseHeaders(ctx), "content-length");
|
||||
if (content_len)
|
||||
{
|
||||
size = strtol(content_len, NULL, 10);
|
||||
}
|
||||
ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
UriFree(uri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha)
|
||||
{
|
||||
HttpClientContext *cctx;
|
||||
Stream *stream;
|
||||
Stream *fake;
|
||||
Uri *uri;
|
||||
char *path, *buf = NULL;
|
||||
unsigned char *sha1;
|
||||
size_t len;
|
||||
if (!c || !mxc || !mime || !sha)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*mime = NULL;
|
||||
*sha = NULL;
|
||||
|
||||
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
|
||||
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
ASAuthenticateRequest(c, cctx);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
|
||||
*mime = StrDuplicate(
|
||||
HashMapGet(HttpResponseHeaders(cctx), "content-type")
|
||||
);
|
||||
stream = HttpClientStream(cctx);
|
||||
fake = StreamFile(open_memstream(&buf, &len));
|
||||
StreamCopy(stream, fake);
|
||||
StreamClose(fake);
|
||||
|
||||
sha1 = Sha1Raw((unsigned char *) buf, len);
|
||||
free(buf);
|
||||
*sha = ShaToHex(sha1, HASH_SHA1);
|
||||
Free(sha1);
|
||||
|
||||
HttpClientContextFree(cctx);
|
||||
UriFree(uri);
|
||||
Free(path);
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len)
|
||||
{
|
||||
HttpClientContext *cctx;
|
||||
Stream *stream;
|
||||
Stream *fake;
|
||||
Uri *uri;
|
||||
char *path, *buf = NULL;
|
||||
if (!c || !mxc || !mime || !out || !len)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*mime = NULL;
|
||||
*out = NULL;
|
||||
*len = 0;
|
||||
|
||||
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
|
||||
cctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
ASAuthenticateRequest(c, cctx);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
|
||||
*mime = StrDuplicate(
|
||||
HashMapGet(HttpResponseHeaders(cctx), "content-type")
|
||||
);
|
||||
stream = HttpClientStream(cctx);
|
||||
fake = StreamFile(open_memstream(&buf, len));
|
||||
StreamCopy(stream, fake);
|
||||
StreamClose(fake);
|
||||
|
||||
*out = Malloc(*len);
|
||||
memcpy(*out, buf, *len);
|
||||
free(buf);
|
||||
|
||||
HttpClientContextFree(cctx);
|
||||
UriFree(uri);
|
||||
Free(path);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
bool
|
||||
ASPing(const ParseeConfig *conf)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path;
|
||||
bool ret;
|
||||
if (!conf)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v1/appservice/",
|
||||
"Parsee%20XMPP",
|
||||
"/ping"
|
||||
);
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ret = ParseeSetRequestJSON(ctx, json) == HTTP_OK;
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
return ret;
|
||||
}
|
||||
206
src/AS/Profile.c
206
src/AS/Profile.c
|
|
@ -1,206 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
void
|
||||
ASSetAvatar(const ParseeConfig *conf, char *user, char *mxc)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!conf || !user || !mxc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/profile/",
|
||||
user, "/avatar_url", "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "avatar_url", JsonValueString(mxc));
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
Free(user);
|
||||
}
|
||||
void
|
||||
ASSetName(const ParseeConfig *conf, char *user, char *name)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json;
|
||||
char *path;
|
||||
if (!conf || !user || !name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v3/profile/",
|
||||
user, "/displayname", "?",
|
||||
"user_id=", user
|
||||
);
|
||||
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "displayname", JsonValueString(name));
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
Free(user);
|
||||
}
|
||||
|
||||
char *
|
||||
ASGetName(const ParseeConfig *c, char *room, char *user)
|
||||
{
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
char *path, *ret;
|
||||
char *u2 = user;
|
||||
if (!c || !user)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!room)
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/profile/", user, "/displayname"
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(user);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||
);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
ret = StrDuplicate(u2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
user = HttpUrlEncode(user);
|
||||
room = HttpUrlEncode(room);
|
||||
path = StrConcat(4,
|
||||
"/_matrix/client/v3/rooms/", room,
|
||||
"/state/m.room.member/", user
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(user);
|
||||
Free(room);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||
);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
ret = StrDuplicate(u2);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
char *
|
||||
ASGetAvatar(const ParseeConfig *c, char *room, char *user)
|
||||
{
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
char *path = NULL, *ret = NULL;
|
||||
char *u2 = user;
|
||||
if (!c || !user)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (room)
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
room = HttpUrlEncode(room);
|
||||
path = StrConcat(4,
|
||||
"/_matrix/client/v3/rooms/", room,
|
||||
"/state/m.room.member/", user
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(user);
|
||||
Free(room);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "avatar_url"))
|
||||
);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
user = u2;
|
||||
|
||||
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from room, got %s", ret);
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
user = HttpUrlEncode(user);
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/profile/", user, "/avatar_url"
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(user);
|
||||
user = u2;
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
ret = StrDuplicate(
|
||||
JsonValueAsString(HashMapGet(reply, "avatar_url"))
|
||||
);
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(reply);
|
||||
Free(path);
|
||||
|
||||
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from profile, got %s", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
bool
|
||||
ASRegisterUser(const ParseeConfig *conf, char *user)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
HttpStatus status;
|
||||
if (!conf || !user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Create user. We don't actually care about the value as we can
|
||||
* masquerade, as long as it exists. */
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, "/_matrix/client/v3/register"
|
||||
);
|
||||
json = HashMapCreate();
|
||||
|
||||
HashMapSet(json,"type",JsonValueString("m.login.application_service"));
|
||||
|
||||
user = ParseeGetLocal(user);
|
||||
HashMapSet(json,"username",JsonValueString(user));
|
||||
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
status = ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
Free(user);
|
||||
|
||||
return status == HTTP_OK;
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
Array *
|
||||
ASGetRelations(const ParseeConfig *c, size_t n, char *room, char *event, char *type)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
Array *ret, *chunk;
|
||||
HashMap *json = NULL;
|
||||
char *path;
|
||||
char *user;
|
||||
size_t i;
|
||||
if (!c || !n || !room || !event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
||||
if (type)
|
||||
{
|
||||
path = StrConcat(8,
|
||||
"/_matrix/client/v1/rooms/", room,
|
||||
"/relations/", event, "/", type,
|
||||
"?user_id=", user
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = StrConcat(6,
|
||||
"/_matrix/client/v1/rooms/", room,
|
||||
"/relations/", event,
|
||||
"?user_id=", user
|
||||
);
|
||||
}
|
||||
Free(user);
|
||||
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
ret = ArrayCreate();
|
||||
chunk = GrabArray(json, 1, "chunk");
|
||||
for (i = 0; i < ArraySize(chunk); i++)
|
||||
{
|
||||
HashMap *obj = JsonValueAsObject(ArrayGet(chunk, i));
|
||||
ArrayAdd(ret, JsonDuplicate(obj));
|
||||
}
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ASFreeRelations(Array *relations)
|
||||
{
|
||||
size_t i;
|
||||
if (!relations)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(relations); i++)
|
||||
{
|
||||
JsonFree(ArrayGet(relations, i));
|
||||
}
|
||||
|
||||
ArrayFree(relations);
|
||||
}
|
||||
366
src/AS/Room.c
366
src/AS/Room.c
|
|
@ -1,366 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
void
|
||||
ASInvite(const ParseeConfig *conf, char *id, char *invited)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *bridge;
|
||||
if (!conf || !id || !invited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bridge = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/invite",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
Free(id);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(invited));
|
||||
HashMapSet(json, "reason", JsonValueString("Pass over."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
}
|
||||
void
|
||||
ASBan(const ParseeConfig *conf, char *id, char *banned)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *bridge;
|
||||
if (!conf || !id || !banned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bridge = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/ban",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
Free(id);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
}
|
||||
void
|
||||
ASKick(const ParseeConfig *conf, char *id, char *banned)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *bridge;
|
||||
if (!conf || !id || !banned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bridge = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/kick",
|
||||
"?user_id=", bridge
|
||||
);
|
||||
Free(bridge);
|
||||
Free(id);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
}
|
||||
char *
|
||||
ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *ret, *serv;
|
||||
int status;
|
||||
if (!conf || !id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!masquerade)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
masquerade = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
masquerade = HttpUrlEncode(masquerade);
|
||||
}
|
||||
serv = strchr(id, ':');
|
||||
if (serv)
|
||||
{
|
||||
serv = serv + 1;
|
||||
}
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/join/", id, "?",
|
||||
"user_id=", masquerade
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
status = ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(GrabString(json, 1, "room_id"));
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(masquerade);
|
||||
Free(id);
|
||||
|
||||
(void) serv; // TODO
|
||||
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
ASLeave(const ParseeConfig *conf, char *id, char *masquerade)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path;
|
||||
if (!conf || !id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!masquerade)
|
||||
{
|
||||
char *raw = StrConcat(4,
|
||||
"@", conf->sender_localpart,
|
||||
":", conf->server_base
|
||||
);
|
||||
masquerade = HttpUrlEncode(raw);
|
||||
Free(raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
masquerade = HttpUrlEncode(masquerade);
|
||||
}
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(5,
|
||||
"/_matrix/client/v3/rooms/", id, "/leave?",
|
||||
"user_id=", masquerade
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
JsonFree(json);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(masquerade);
|
||||
Free(id);
|
||||
}
|
||||
|
||||
char *
|
||||
ASCreateRoom(const ParseeConfig *conf, char *by, char *alias)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *id;
|
||||
if (!conf || !by)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/createRoom",
|
||||
"?user_id=", by
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
if (alias)
|
||||
{
|
||||
char *trimmed = StrDuplicate(alias);
|
||||
if (*alias == '#')
|
||||
{
|
||||
char *tmp, cb[2] = { 0 };
|
||||
alias++;
|
||||
Free(trimmed);
|
||||
trimmed = NULL;
|
||||
|
||||
while (*alias && *alias != ':')
|
||||
{
|
||||
cb[0] = *alias;
|
||||
tmp = trimmed;
|
||||
trimmed = StrConcat(2, trimmed, cb);
|
||||
Free(tmp);
|
||||
alias ++;
|
||||
}
|
||||
}
|
||||
HashMapSet(json, "room_alias_name", JsonValueString(trimmed));
|
||||
Free(trimmed);
|
||||
}
|
||||
HashMapSet(json, "visibility", JsonValueString("public"));
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
JsonFree(json);
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
return id;
|
||||
}
|
||||
char *
|
||||
ASCreateDM(const ParseeConfig *conf, char *by, char *with)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
HashMap *json = NULL;
|
||||
char *path, *id;
|
||||
if (!conf || !by || !with)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = StrConcat(3,
|
||||
"/_matrix/client/v3/createRoom",
|
||||
"?user_id=", by
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(
|
||||
conf,
|
||||
HTTP_POST, path
|
||||
);
|
||||
Free(path);
|
||||
json = HashMapCreate();
|
||||
{
|
||||
Array *invitees = ArrayCreate();
|
||||
|
||||
ArrayAdd(invitees, JsonValueString(with));
|
||||
HashMapSet(json, "invite", JsonValueArray(invitees));
|
||||
HashMapSet(json, "is_direct", JsonValueBoolean(true));
|
||||
}
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, json);
|
||||
|
||||
JsonFree(json);
|
||||
json = JsonDecode(HttpClientStream(ctx));
|
||||
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(json);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
HashMap *
|
||||
ASGetPL(const ParseeConfig *c, char *room)
|
||||
{
|
||||
char *path;
|
||||
HttpClientContext *ctx;
|
||||
HashMap *reply;
|
||||
if (!c || !room)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
room = HttpUrlEncode(room);
|
||||
path = StrConcat(4,
|
||||
"/_matrix/client/v3/rooms/", room,
|
||||
"/state/m.room.power_levels/", ""
|
||||
);
|
||||
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||
Free(room);
|
||||
ASAuthenticateRequest(c, ctx);
|
||||
HttpRequestSendHeaders(ctx);
|
||||
HttpRequestSend(ctx);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
Free(path);
|
||||
|
||||
return reply;
|
||||
}
|
||||
void
|
||||
ASSetPL(const ParseeConfig *conf, char *id, HashMap *m)
|
||||
{
|
||||
char *user;
|
||||
if (!conf || !id || !m)
|
||||
{
|
||||
return;
|
||||
}
|
||||
user = StrConcat(4,
|
||||
"@",conf->sender_localpart,
|
||||
":",conf->server_base
|
||||
);
|
||||
ASSetState(conf, id, "m.room.power_levels", "", user, m);
|
||||
Free(user);
|
||||
}
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
static char *
|
||||
TSToStr(uint64_t ts)
|
||||
{
|
||||
size_t len;
|
||||
char *str;
|
||||
|
||||
len = snprintf(NULL, 0, "%"PRIu64, ts);
|
||||
str = Malloc(len+1);
|
||||
snprintf(str, len+1, "%"PRIu64, ts);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *
|
||||
ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c, uint64_t ts)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
char *path;
|
||||
char *txn, *ret;
|
||||
char *ts_str;
|
||||
HttpStatus status;
|
||||
if (!ret)
|
||||
{
|
||||
Log(LOG_ERR, "%", ret);
|
||||
}
|
||||
HashMap *reply;
|
||||
if (!conf || !id || !type || !user || !c)
|
||||
{
|
||||
JsonFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!ts)
|
||||
{
|
||||
ts = UtilTsMillis();
|
||||
}
|
||||
ts_str = TSToStr(ts);
|
||||
|
||||
txn = StrRandom(16);
|
||||
id = HttpUrlEncode(id);
|
||||
path = StrConcat(11,
|
||||
"/_matrix/client/v3/rooms/",
|
||||
id, "/send/", type, "/", txn, "?",
|
||||
"user_id=", user, "&ts=", ts_str
|
||||
);
|
||||
Free(id);
|
||||
Free(txn);
|
||||
Free(ts_str);
|
||||
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
status = ParseeSetRequestJSON(ctx, c);
|
||||
|
||||
reply = JsonDecode(HttpClientStream(ctx));
|
||||
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
||||
JsonFree(reply);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(c);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
#include <AS.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
void
|
||||
ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state)
|
||||
{
|
||||
HttpClientContext *ctx = NULL;
|
||||
char *path;
|
||||
if (!conf || !id || !type || !mask || !state)
|
||||
{
|
||||
JsonFree(state);
|
||||
return;
|
||||
}
|
||||
|
||||
path = StrConcat(9,
|
||||
"/_matrix/client/v3/rooms/", id, "/state/",
|
||||
type, "/", key, "?", "user_id=", mask
|
||||
);
|
||||
|
||||
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||
Free(path);
|
||||
ASAuthenticateRequest(conf, ctx);
|
||||
ParseeSetRequestJSON(ctx, state);
|
||||
|
||||
HttpClientContextFree(ctx);
|
||||
JsonFree(state);
|
||||
}
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ CommandParse(char *cmd)
|
|||
}
|
||||
|
||||
end_data = strchr(cmd, ' ');
|
||||
if (!end_data || (cmd > end_data))
|
||||
if (!end_data)
|
||||
{
|
||||
ret = Malloc(sizeof(*ret));
|
||||
ret->command = StrDuplicate(cmd);
|
||||
|
|
@ -49,11 +49,11 @@ CommandParse(char *cmd)
|
|||
char c = *cur;
|
||||
char *tmp;
|
||||
bool type;
|
||||
char char_type = '\0';
|
||||
char char_type;
|
||||
switch (state)
|
||||
{
|
||||
case STATE_WHITE:
|
||||
if (!isblank((int) c))
|
||||
if (!isblank(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((int) c)))
|
||||
if ((type && c == char_type) || (!type && isblank(c)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,47 +15,35 @@ CommandCreateRouter(void)
|
|||
void
|
||||
CommandAddCommand(CommandRouter *rter, char *c, CommandRoute rte)
|
||||
{
|
||||
CommandRoute *indirect;
|
||||
if (!rter || !c || !rte)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
HashMapSet(rter->routes, c, rte);
|
||||
}
|
||||
void
|
||||
RouteCommand(CommandRouter *rter, Command *cmd, void *d)
|
||||
{
|
||||
CommandRoute *route;
|
||||
CommandRoute route;
|
||||
if (!rter || !cmd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
route = HashMapGet(rter->routes, cmd->command);
|
||||
if (route && *route)
|
||||
if (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,28 +22,11 @@ CommandHead(CmdBanUser, cmd, argp)
|
|||
BotDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
ReplySprintf("Banning %s from '%s'...",
|
||||
user, room
|
||||
);
|
||||
|
||||
BotDestroy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,4 @@ CommandHead(CmdHelp, cmd, argp)
|
|||
);
|
||||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||
BotDestroy();
|
||||
|
||||
(void) cmd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,4 @@ CommandHead(CmdListBans, cmd, argp)
|
|||
|
||||
DbUnlock(data->db, listed);
|
||||
BotDestroy();
|
||||
|
||||
(void) cmd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ CommandHead(CmdPlumb, cmd, argp)
|
|||
BotRequired(room);
|
||||
|
||||
/* Check MUC viability */
|
||||
if (ParseeManageBan(args->data, muc, NULL) ||
|
||||
!ParseeIsMUCWhitelisted(args->data, muc))
|
||||
if (ParseeManageBan(args->data, muc, NULL))
|
||||
{
|
||||
ReplySprintf("MUC '%s' is not allowed on this bridge.", muc);
|
||||
goto end;
|
||||
|
|
@ -63,7 +62,7 @@ CommandHead(CmdPlumb, cmd, argp)
|
|||
if (chat_id)
|
||||
{
|
||||
char *rev = StrConcat(2, muc, "/parsee");
|
||||
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
|
||||
XMPPJoinMUC(args->data->jabber, "parsee", rev);
|
||||
|
||||
Free(rev);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ CommandHead(CmdStats, cmd, argp)
|
|||
BotInitialise();
|
||||
|
||||
|
||||
/* TODO: Separate these into different "categories" */
|
||||
ReplySprintf("Information for %s v%s (Cytoplasm %s)",
|
||||
NAME, VERSION, CytoplasmGetVersionStr()
|
||||
);
|
||||
|
|
@ -40,6 +41,4 @@ CommandHead(CmdStats, cmd, argp)
|
|||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||
|
||||
BotDestroy();
|
||||
|
||||
(void) cmd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,60 +9,43 @@
|
|||
|
||||
#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 *event = args->event;
|
||||
HashMap *json, *event = args->event, *mucs;
|
||||
DbRef *ref;
|
||||
char *muc = NULL, *chat_id = NULL, *room = NULL;
|
||||
|
||||
BotInitialise();
|
||||
|
||||
if (!Grab(data, cmd, &muc, &chat_id, &room))
|
||||
muc = HashMapGet(cmd->arguments, "muc");
|
||||
if (!muc)
|
||||
{
|
||||
ReplyBasic("`muc`|`room` REQUIRED");
|
||||
ReplyBasic("`muc` field REQUIRED.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
chat_id = ParseeGetFromMUCID(data, muc);
|
||||
room = ParseeGetRoomID(data, chat_id);
|
||||
ref = DbLock(data->db, 1, "chats");
|
||||
json = DbJson(ref);
|
||||
chat_id = StrDuplicate(GrabString(json, 2, "mucs", muc));
|
||||
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);
|
||||
|
||||
ParseeUnlinkRoom(data, chat_id);
|
||||
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);
|
||||
|
||||
/* TODO: Do it automatically, if *not plumbed* */
|
||||
ReplySprintf("The MUC %s is now *unlinked*.", muc);
|
||||
|
|
|
|||
27
src/Events.c
27
src/Events.c
|
|
@ -37,10 +37,12 @@ MatrixCreateMessage(char *body)
|
|||
HashMapSet(map, "msgtype", JsonValueString("m.text"));
|
||||
HashMapSet(map, "body", JsonValueString(body));
|
||||
{
|
||||
/* TODO */
|
||||
XEP393Element *e = XEP393(body);
|
||||
text = XEP393ToXMLString(e);
|
||||
XEP393FreeElement(e);
|
||||
|
||||
|
||||
HashMapSet(map, "formatted_body", JsonValueString(text));
|
||||
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
||||
Free(text);
|
||||
|
|
@ -79,7 +81,7 @@ MatrixCreateNickChange(char *nick)
|
|||
return map;
|
||||
}
|
||||
HashMap *
|
||||
MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
||||
MatrixCreateMedia(char *mxc, char *body, char *mime)
|
||||
{
|
||||
HashMap *map;
|
||||
char *mime_type = NULL, *matrix_type = NULL;
|
||||
|
|
@ -91,10 +93,9 @@ MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
|||
matrix_type = "m.file";
|
||||
if (mime)
|
||||
{
|
||||
size_t i, len;
|
||||
size_t i;
|
||||
mime_type = StrDuplicate(mime);
|
||||
len = strlen(mime);
|
||||
for (i = 0; i < len; i++)
|
||||
for (i = 0; i < strlen(mime); i++)
|
||||
{
|
||||
if (mime_type[i] == '/')
|
||||
{
|
||||
|
|
@ -120,12 +121,6 @@ MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
|||
}
|
||||
|
||||
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));
|
||||
|
|
@ -187,18 +182,6 @@ MatrixCreateReplace(char *event, char *body)
|
|||
HashMapSet(map, "m.new_content", JsonValueObject(new));
|
||||
HashMapSet(map, "m.relates_to", JsonValueObject(rel));
|
||||
|
||||
{
|
||||
XEP393Element *e = XEP393(body);
|
||||
char *text = XEP393ToXMLString(e);
|
||||
XEP393FreeElement(e);
|
||||
|
||||
HashMapSet(map, "formatted_body", JsonValueString(text));
|
||||
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
||||
HashMapSet(new, "formatted_body", JsonValueString(text));
|
||||
HashMapSet(new, "format", JsonValueString("org.matrix.custom.html"));
|
||||
Free(text);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
HashMap *
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
#include <FileInfo.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static int
|
||||
GetField(XMLElement *elem)
|
||||
{
|
||||
XMLElement *child;
|
||||
if (!elem || ArraySize(elem->children) != 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
child = ArrayGet(elem->children, 0);
|
||||
|
||||
return strtol(child->data, NULL, 10);
|
||||
}
|
||||
|
||||
FileInfo *
|
||||
FileInfoFromXMPP(XMLElement *stanza)
|
||||
{
|
||||
FileInfo *info;
|
||||
XMLElement *reference, *sims, *file;
|
||||
|
||||
if (!stanza)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
reference = XMLookForTKV(stanza,
|
||||
"reference", "xmlns", "urn:xmpp:reference:0"
|
||||
);
|
||||
sims = XMLookForTKV(reference,
|
||||
"media-sharing", "xmlns", "urn:xmpp:sims:1"
|
||||
);
|
||||
file = XMLookForTKV(sims,
|
||||
"file", "xmlns", "urn:xmpp:jingle:apps:file-transfer:5"
|
||||
);
|
||||
/* TODO: We'll definitely need MIME types to do things like
|
||||
* WebXDC */
|
||||
if (!file)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = Malloc(sizeof(*info));
|
||||
|
||||
|
||||
info->width = GetField(XMLookForUnique(file, "width"));
|
||||
info->height = GetField(XMLookForUnique(file, "height"));
|
||||
info->size = GetField(XMLookForUnique(file, "size"));
|
||||
return info;
|
||||
}
|
||||
|
||||
void
|
||||
FileInfoFree(FileInfo *info)
|
||||
{
|
||||
if (!info)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Free(info);
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ GlobMatches(char *rule, char *string)
|
|||
switch (c1)
|
||||
{
|
||||
case '*':
|
||||
/* TODO */
|
||||
while ((c2 = *string) && (c2 != next))
|
||||
{
|
||||
string++;
|
||||
|
|
|
|||
|
|
@ -29,18 +29,19 @@ ParseeRequest(HttpServerContext *ctx, void *argp)
|
|||
|
||||
arg.stream = stream;
|
||||
|
||||
Log(LOG_DEBUG, "%s %s",
|
||||
Log(LOG_NOTICE, "%s %s",
|
||||
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
||||
path
|
||||
);
|
||||
|
||||
if (!HttpRouterRoute(data->router, path, &arg, (void **) &response))
|
||||
{
|
||||
Log(LOG_DEBUG, "Couldn't route %s", path);
|
||||
Log(LOG_NOTICE, "Couldn't route %s", path);
|
||||
HttpResponseStatus(ctx, HTTP_NOT_FOUND);
|
||||
JsonFree(response);
|
||||
|
||||
response = MatrixCreateError("M_NOT_FOUND", "Route not found.");
|
||||
/* TODO: Set a thing */
|
||||
}
|
||||
|
||||
/* Whatever, we routed a thing. */
|
||||
|
|
@ -55,11 +56,8 @@ ParseeRequest(HttpServerContext *ctx, void *argp)
|
|||
HttpSendHeaders(ctx);
|
||||
JsonEncode(response, stream, JSON_DEFAULT);
|
||||
JsonFree(response);
|
||||
return;
|
||||
}
|
||||
Log(LOG_DEBUG, "%s %s (%d)",
|
||||
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
||||
path, HttpResponseStatusGet(ctx)
|
||||
);
|
||||
}
|
||||
|
||||
HttpClientContext *
|
||||
|
|
@ -73,7 +71,7 @@ ParseeCreateRequest(const ParseeConfig *conf, HttpRequestMethod meth, char *path
|
|||
|
||||
ctx = HttpRequest(
|
||||
meth,
|
||||
conf->homeserver_tls ? HTTP_FLAG_TLS : HTTP_FLAG_NONE,
|
||||
HTTP_FLAG_TLS,
|
||||
conf->homeserver_port, conf->homeserver_host,
|
||||
path
|
||||
);
|
||||
|
|
|
|||
230
src/Main.c
230
src/Main.c
|
|
@ -1,6 +1,5 @@
|
|||
#include <Cytoplasm/HttpServer.h>
|
||||
#include <Cytoplasm/Cytoplasm.h>
|
||||
#include <Cytoplasm/Platform.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Cron.h>
|
||||
|
|
@ -13,7 +12,6 @@
|
|||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <StanzaBuilder.h>
|
||||
#include <Parsee.h>
|
||||
#include <XMPP.h>
|
||||
#include <AS.h>
|
||||
|
|
@ -29,79 +27,6 @@ ParseeUptime(void)
|
|||
return UtilTsMillis() - start;
|
||||
}
|
||||
|
||||
static const Argument arguments[] =
|
||||
{
|
||||
#define Arg(c, req, vdesc, desc) \
|
||||
{ \
|
||||
.end = false, \
|
||||
.argument = c, .value_req = req, \
|
||||
.value_descr = vdesc, \
|
||||
.description = desc \
|
||||
},
|
||||
#define EndOfArgs { .end = true }
|
||||
|
||||
Arg('H', true, "number(=8)", "Sets the number of HTTP threads")
|
||||
Arg('J', true, "number(=8)", "Sets the number of XMPP threads")
|
||||
Arg('C', true, "file(='parsee.json')", "Sets the JSON config to use")
|
||||
|
||||
Arg('g', false, NULL,
|
||||
"Generates a parsee.yaml AS file before exiting")
|
||||
Arg('v', false, NULL,
|
||||
"Forces Parsee to print in a more verbose fashion "
|
||||
"(-vvv prints stanzas to stderr)")
|
||||
Arg('h', false, NULL,
|
||||
"Generates an help screen(this one!)")
|
||||
|
||||
EndOfArgs
|
||||
|
||||
#undef EndOfArgs
|
||||
#undef Argument
|
||||
};
|
||||
|
||||
void
|
||||
ParseeCheckMatrix(void *datp)
|
||||
{
|
||||
static volatile uint64_t streak = 0;
|
||||
ParseeData *data = datp;
|
||||
if (data->config->accept_pings && !ASPing(data->config))
|
||||
{
|
||||
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
|
||||
if (++streak == 10)
|
||||
{
|
||||
DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats");
|
||||
HashMap *json = DbJson(ref);
|
||||
HashMap *mucs = GrabObject(json, 1, "mucs");
|
||||
char *muc;
|
||||
void *ignored;
|
||||
|
||||
|
||||
/* Notify any potential MUCs about this */
|
||||
while (HashMapIterate(mucs, &muc, &ignored))
|
||||
{
|
||||
char *id = StrRandom(32);
|
||||
char *sender = StrConcat(3, "parsee@", data->jabber->host, "/parsee");
|
||||
StanzaBuilder *b = CreateStanzaBuilder(sender, muc, id);
|
||||
SetStanzaType(b, "groupchat");
|
||||
SetStanzaBody(b,
|
||||
"This bridge hasn't been able to reach the Matrix host, and "
|
||||
"as such, some messages may not have been sent over."
|
||||
);
|
||||
|
||||
WriteoutStanza(b, data->jabber, 0);
|
||||
DestroyStanzaBuilder(b);
|
||||
|
||||
Free(sender);
|
||||
Free(id);
|
||||
}
|
||||
(void) ignored;
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
streak = 0;
|
||||
}
|
||||
|
||||
int
|
||||
Main(Array *args, HashMap *env)
|
||||
{
|
||||
|
|
@ -110,11 +35,6 @@ Main(Array *args, HashMap *env)
|
|||
Stream *yaml;
|
||||
Cron *cron = NULL;
|
||||
|
||||
char *configuration = "parsee.json";
|
||||
int xmpp = 8;
|
||||
int http = 8;
|
||||
int verbose = 0;
|
||||
|
||||
start = UtilTsMillis();
|
||||
|
||||
memset(&conf, 0, sizeof(conf));
|
||||
|
|
@ -122,32 +42,24 @@ Main(Array *args, HashMap *env)
|
|||
"%s - v%s[%s] (Cytoplasm %s)",
|
||||
NAME, VERSION, CODE, CytoplasmGetVersionStr()
|
||||
);
|
||||
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());
|
||||
|
||||
ParseeConfigLoad("parsee.json");
|
||||
ParseeConfigInit();
|
||||
parsee_conf = ParseeConfigGet();
|
||||
|
||||
{
|
||||
ArgParseState state;
|
||||
char *opts = ParseeGenerateGetopt(arguments);
|
||||
int flag;
|
||||
int xmpp = 8;
|
||||
int http = 8;
|
||||
|
||||
ArgParseStateInit(&state);
|
||||
while ((flag = ArgParse(&state, args, opts)) != -1)
|
||||
while ((flag = ArgParse(&state, args, "gH:J:")) != -1)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case 'h':
|
||||
ParseeGenerateHelp(arguments);
|
||||
Free(opts);
|
||||
goto end;
|
||||
case 'H':
|
||||
http = strtol(state.optArg, NULL, 10);
|
||||
break;
|
||||
|
|
@ -158,155 +70,46 @@ 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);
|
||||
goto end;
|
||||
case 'v':
|
||||
switch (++verbose)
|
||||
{
|
||||
case PARSEE_VERBOSE_LOG:
|
||||
LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG);
|
||||
break;
|
||||
case PARSEE_VERBOSE_TIMINGS:
|
||||
Log(LOG_DEBUG, "Logging bench information.");
|
||||
break;
|
||||
case PARSEE_VERBOSE_STANZA:
|
||||
Log(LOG_DEBUG, "Enabling stanza printing.");
|
||||
break;
|
||||
case PARSEE_VERBOSE_COMICAL:
|
||||
Log(LOG_DEBUG, "What?");
|
||||
Log(LOG_DEBUG, "No, but like, what do you except?");
|
||||
Log(LOG_DEBUG, "Like do you want to log _every_ instruction?");
|
||||
Log(LOG_DEBUG, "Like just every single thing %s does?", NAME);
|
||||
Log(LOG_DEBUG, " ( why??? )");
|
||||
Log(LOG_DEBUG, ".....................................");
|
||||
Log(LOG_DEBUG, "Argh.");
|
||||
Log(LOG_DEBUG, "Alright. I'll do my best.");
|
||||
Log(LOG_DEBUG, "Get what you paid for.");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
if (!UtilLastModified(state.optArg))
|
||||
{
|
||||
Log(LOG_ERR, "Invalid config: %s", state.optArg);
|
||||
Log(LOG_ERR, "Ignoring.");
|
||||
break;
|
||||
}
|
||||
configuration = state.optArg;
|
||||
break;
|
||||
case '?':
|
||||
Log(LOG_ERR, "INVALID ARGUMENT GIVEN");
|
||||
Free(opts);
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
Free(opts);
|
||||
ParseeSetThreads(xmpp, http);
|
||||
}
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Loading configuration...");
|
||||
}
|
||||
ParseeConfigLoad(configuration);
|
||||
ParseeConfigInit();
|
||||
parsee_conf = ParseeConfigGet();
|
||||
if (!parsee_conf)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
ParseeSetThreads(xmpp, http);
|
||||
|
||||
|
||||
Log(LOG_NOTICE, "Connecting to XMPP...");
|
||||
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
|
||||
))
|
||||
{
|
||||
Log(LOG_ERR, "Could not connect to XMPP...");
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Destroying component...");
|
||||
}
|
||||
XMPPEndCompStream(jabber);
|
||||
goto end;
|
||||
}
|
||||
|
||||
Log(LOG_NOTICE, "Creating volatile tables...");
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Initialising JID table");
|
||||
}
|
||||
ParseeInitialiseJIDTable();
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Initialising OID table");
|
||||
}
|
||||
ParseeInitialiseOIDTable();
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Initialising head table");
|
||||
}
|
||||
ParseeInitialiseHeadTable();
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Initialising nick table");
|
||||
}
|
||||
ParseeInitialiseNickTable();
|
||||
|
||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
||||
{
|
||||
Log(LOG_DEBUG, "Initialising affiliation table");
|
||||
}
|
||||
ParseeInitialiseAffiliationTable();
|
||||
Log(LOG_NOTICE, "Setting up local Matrix user...");
|
||||
ASRegisterUser(parsee_conf, parsee_conf->sender_localpart);
|
||||
|
||||
conf.port = parsee_conf->port;
|
||||
conf.threads = parsee_conf->http_threads;
|
||||
conf.maxConnections = conf.threads << 2;
|
||||
conf.handlerArgs = ParseeInitData(jabber);
|
||||
conf.handler = ParseeRequest;
|
||||
if (!conf.handlerArgs)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
||||
Log(LOG_DEBUG, "Verbosity level: %d", verbose);
|
||||
((ParseeData *) conf.handlerArgs)->verbosity = verbose;
|
||||
|
||||
Log(LOG_NOTICE, "Setting up local Matrix user...");
|
||||
if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart))
|
||||
{
|
||||
char *parsee = ParseeMXID(conf.handlerArgs);
|
||||
|
||||
ASSetAvatar(parsee_conf,
|
||||
parsee,
|
||||
"mxc://tedomum.net/"
|
||||
"7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136"
|
||||
);
|
||||
ASSetName(parsee_conf, parsee, "Parsee bridge");
|
||||
|
||||
Free(parsee);
|
||||
}
|
||||
|
||||
Log(LOG_NOTICE, "Starting up local cronjobs...");
|
||||
cron = CronCreate(10 SECONDS);
|
||||
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
||||
CronEvery(cron, 10 SECONDS, ParseeCheckMatrix, conf.handlerArgs);
|
||||
ParseeCleanup(conf.handlerArgs);
|
||||
cron = CronCreate( 10 SECONDS );
|
||||
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
||||
|
||||
CronStart(cron);
|
||||
|
||||
|
|
@ -318,9 +121,8 @@ Main(Array *args, HashMap *env)
|
|||
}
|
||||
|
||||
server = HttpServerCreate(&conf);
|
||||
((ParseeData *) conf.handlerArgs)->server = server;
|
||||
|
||||
if (!ParseeInitialiseSignals(conf.handlerArgs, xmpp_thr))
|
||||
if (!ParseeInitialiseSignals(server, xmpp_thr, jabber))
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
|
|
@ -344,12 +146,8 @@ end:
|
|||
CronStop(cron);
|
||||
CronFree(cron);
|
||||
ParseeFreeData(conf.handlerArgs);
|
||||
ParseeDestroyAffiliationTable();
|
||||
ParseeDestroyNickTable();
|
||||
ParseeDestroyOIDTable();
|
||||
ParseeDestroyHeadTable();
|
||||
ParseeDestroyJIDTable();
|
||||
|
||||
(void) env;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,56 +8,9 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <StanzaBuilder.h>
|
||||
#include <Unistring.h>
|
||||
#include <Matrix.h>
|
||||
#include <AS.h>
|
||||
|
||||
static const char *
|
||||
GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to);
|
||||
|
||||
static char *
|
||||
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char *hash)
|
||||
{
|
||||
char *sender = GrabString(event, 1, "sender");
|
||||
|
||||
Unistr *uninick = UnistrCreate(name);
|
||||
Unistr *filtered = UnistrFilter(uninick, UnistrIsASCII); /* I'm not even going to try messing with the BMP anymore. */
|
||||
char *nick = UnistrC(filtered);
|
||||
char *rev = StrConcat(3, muc, "/", nick);
|
||||
int nonce = 0;
|
||||
|
||||
UnistrFree(uninick);
|
||||
UnistrFree(filtered);
|
||||
|
||||
/* TODO: vCards! */
|
||||
while (!XMPPJoinMUC(data->jabber, jid, rev, hash, -1, true) && nonce < 32)
|
||||
{
|
||||
char *nonce_str = StrInt(nonce);
|
||||
char *input = StrConcat(3, sender, name, nonce_str);
|
||||
char *hex = ParseeHMACS(data->id, input);
|
||||
|
||||
if (strlen(hex) >= 8)
|
||||
{
|
||||
hex[8] = '\0';
|
||||
}
|
||||
|
||||
Free(nick);
|
||||
Free(rev);
|
||||
|
||||
nick = StrConcat(4, name, "[", hex, "]");
|
||||
rev = StrConcat(3, muc, "/", nick);
|
||||
nonce++;
|
||||
|
||||
Free(nonce_str);
|
||||
Free(input);
|
||||
Free(hex);
|
||||
}
|
||||
|
||||
ParseePushNickTable(muc, sender, nick);
|
||||
Free(nick);
|
||||
return (rev);
|
||||
}
|
||||
|
||||
static void
|
||||
ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||
{
|
||||
|
|
@ -98,95 +51,20 @@ 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);
|
||||
char *rev = StrConcat(2, muc, "/parsee");
|
||||
|
||||
Free(jabber);
|
||||
Free(avatar);
|
||||
Free(name);
|
||||
XMPPJoinMUC(data->jabber, jid, rev);
|
||||
Free(rev);
|
||||
Free(muc);
|
||||
avatar = NULL;
|
||||
|
||||
/* TODO: XEP-0084 magic to advertise a new avatar if possible. */
|
||||
}
|
||||
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);
|
||||
Free(chat_id);
|
||||
}
|
||||
else if ((StrEquals(membership, "leave") ||
|
||||
StrEquals(membership, "ban"))
|
||||
|
|
@ -204,29 +82,14 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
|||
muc_id = ParseeGetMUCID(data, chat_id);
|
||||
if (!chat_id)
|
||||
{
|
||||
/* If it can't be found, try to see if it's as a DM */
|
||||
char *info_from = NULL, *info_to = NULL;
|
||||
const char *type = GetXMPPInformation(data, event, &info_from, &info_to);
|
||||
|
||||
if (StrEquals(type, "chat"))
|
||||
{
|
||||
char *jid_to = ParseeTrimJID(info_to);
|
||||
Log(LOG_DEBUG, "('%s'->'%s') is gone.", state_key, info_to);
|
||||
/* TODO: Send a last DM, signifying that all is gone. */
|
||||
ParseeDeleteDM(data, state_key, jid_to);
|
||||
Free(jid_to);
|
||||
}
|
||||
|
||||
Free(info_from);
|
||||
Free(info_to);
|
||||
goto end;
|
||||
}
|
||||
|
||||
name = StrDuplicate(ParseeLookupNick(muc_id, sender));
|
||||
rev = StrConcat(3, muc_id, "/", name);
|
||||
/* TODO: Check the name's validity */
|
||||
name = ASGetName(data->config, room_id, state_key);
|
||||
rev = StrConcat(4, muc_id, "/", name, "[p]");
|
||||
|
||||
XMPPLeaveMUC(jabber, jid, rev, reason);
|
||||
ParseePushNickTable(muc_id, sender, NULL);
|
||||
end:
|
||||
Free(chat_id);
|
||||
Free(muc_id);
|
||||
|
|
@ -255,9 +118,14 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!body || *body != '!')
|
||||
if (*body != '!')
|
||||
{
|
||||
/* All commands are to be marked with a ! */
|
||||
Free(ASSend(
|
||||
data->config, id, profile,
|
||||
"m.room.message",
|
||||
MatrixCreateNotice("Please enter a valid command")
|
||||
));
|
||||
Free(profile);
|
||||
return;
|
||||
}
|
||||
|
|
@ -267,7 +135,7 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
|||
Free(ASSend(
|
||||
data->config, id, profile,
|
||||
"m.room.message",
|
||||
MatrixCreateNotice("You are not authorised to do this."), 0
|
||||
MatrixCreateNotice("You are not authorised to do this.")
|
||||
));
|
||||
Free(profile);
|
||||
return;
|
||||
|
|
@ -289,10 +157,12 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
char *room_id = GrabString(event, 1, "room_id");
|
||||
char *matrix_sender = GrabString(event, 1, "sender");
|
||||
char *chat_id = NULL, *muc_id = NULL;
|
||||
char *user = NULL;
|
||||
char *user;
|
||||
|
||||
DbRef *room_data = NULL;
|
||||
HashMap *data_json = NULL;
|
||||
DbRef *room_data;
|
||||
HashMap *data_json;
|
||||
|
||||
XMPPComponent *jabber = data ? data->jabber : NULL;
|
||||
|
||||
bool direct = false;
|
||||
if (!data || !event || !from || !to)
|
||||
|
|
@ -324,8 +194,7 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
}
|
||||
else
|
||||
{
|
||||
char *matrix_name = NULL, *matrix_avatar = NULL;
|
||||
char *mime = NULL, *sha = NULL;
|
||||
char *matrix_name, *muc_join_as;
|
||||
|
||||
muc_id = ParseeGetMUCID(data, chat_id);
|
||||
if (!chat_id)
|
||||
|
|
@ -339,16 +208,18 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
}
|
||||
|
||||
matrix_name = ASGetName(data->config, room_id, matrix_sender);
|
||||
matrix_avatar = ASGetAvatar(data->config, NULL, matrix_sender);
|
||||
muc_join_as = StrConcat(4, muc_id, "/", matrix_name, "[p]");
|
||||
|
||||
/* TODO: Manage name conflicts. That would have been an easy
|
||||
* task(try the original one, and use a counter if it fails),
|
||||
* but that'd involve modifying the rest of the code, which
|
||||
* I'm not doing at 01:39 ... */
|
||||
XMPPJoinMUC(jabber, *from, muc_join_as);
|
||||
|
||||
ASGetMIMESHA(data->config, matrix_avatar, &mime, &sha);
|
||||
Free(JoinMUC(data, event, *from, muc_id, matrix_name, sha));
|
||||
*to = muc_id;
|
||||
|
||||
Free(matrix_avatar);
|
||||
Free(matrix_name);
|
||||
Free(mime);
|
||||
Free(sha);
|
||||
Free(muc_join_as);
|
||||
}
|
||||
|
||||
Free(chat_id);
|
||||
|
|
@ -358,47 +229,27 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
|||
static void
|
||||
ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||
{
|
||||
if (!data || !event)
|
||||
{
|
||||
return;
|
||||
}
|
||||
XMPPComponent *jabber = data->jabber;
|
||||
StanzaBuilder *builder = NULL;
|
||||
StanzaBuilder *builder;
|
||||
DbRef *ref = NULL;
|
||||
HashMap *json = NULL;
|
||||
HashMap *json;
|
||||
|
||||
char *msgtype = GrabString(event, 2, "content", "msgtype");
|
||||
char *m_sender = GrabString(event, 1, "sender");
|
||||
char *unedited_id = NULL;
|
||||
char *body = GrabString(event, 2, "content", "body");
|
||||
char *id = GrabString(event, 1, "room_id");
|
||||
char *ev_id = GrabString(event, 1, "event_id");
|
||||
char *chat_id = NULL, *muc_id = NULL;
|
||||
char *m_sender = GrabString(event, 1, "sender");
|
||||
char *chat_id, *muc_id;
|
||||
char *reply_id = MatrixGetReply(event);
|
||||
char *xepd = ParseeXMPPify(data, event);
|
||||
char *xepd = ParseeXMPPify(event);
|
||||
char *type, *user, *xmppified_user = NULL, *to = NULL;
|
||||
char *unauth = NULL;
|
||||
char *origin_id = NULL, *stanza = NULL;
|
||||
char *sender = NULL;
|
||||
char *unedited_id = MatrixGetEdit(event);
|
||||
char *url = GrabString(event, 2, "content", "url");
|
||||
char *encoded_from = NULL;
|
||||
|
||||
bool direct = false;
|
||||
unedited_id = MatrixGetEdit(event);
|
||||
|
||||
if (unedited_id)
|
||||
{
|
||||
char *new_content = GrabString(event, 3, "content", "m.new_content", "body");
|
||||
if (new_content) body = new_content;
|
||||
}
|
||||
|
||||
if (data->config->ignore_bots && StrEquals(msgtype, "m.notice"))
|
||||
{
|
||||
Free(reply_id);
|
||||
Free(xepd);
|
||||
Free(unedited_id);
|
||||
return;
|
||||
}
|
||||
if (ParseeIsPuppet(data->config, m_sender) ||
|
||||
ParseeManageBan(data, m_sender, id))
|
||||
{
|
||||
|
|
@ -410,6 +261,9 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
|
||||
chat_id = ParseeGetFromRoomID(data, id);
|
||||
|
||||
/* TODO: This ref should be marked as read-only,
|
||||
* as LMDB doesn't seem to like having two concurrent RW
|
||||
* transactions running. */
|
||||
ref = DbLockIntent(data->db, DB_HINT_READONLY, 3, "rooms", id, "data");
|
||||
json = DbJson(ref);
|
||||
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
|
||||
|
|
@ -429,44 +283,38 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
|
||||
type = direct ? "chat" : "groupchat";
|
||||
user = GrabString(json, 1, "xmpp_user");
|
||||
unauth = ParseeToUnauth(data, url, GrabString(event, 2, "content", "filename"));
|
||||
|
||||
encoded_from = ParseeEncodeMXID(m_sender);
|
||||
xmppified_user = StrConcat(3,
|
||||
encoded_from, "@", jabber->host
|
||||
);
|
||||
unauth = ParseeToUnauth(data, url);
|
||||
if (direct)
|
||||
{
|
||||
xmppified_user = ParseeEncodeMXID(m_sender);
|
||||
to = StrDuplicate(user);
|
||||
|
||||
Free(chat_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *name, *mime = NULL, *sha = NULL;
|
||||
char *avatar;
|
||||
char *name, *rev;
|
||||
/* Try to find the chat ID */
|
||||
muc_id = ParseeGetMUCID(data, chat_id);
|
||||
if (!chat_id)
|
||||
{
|
||||
goto end;
|
||||
}
|
||||
xmppified_user = ParseeEncodeMXID(m_sender);
|
||||
|
||||
/* TODO: Avoid using the AS endpoints */
|
||||
/* 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?" */
|
||||
name = ASGetName(data->config, id, m_sender);
|
||||
avatar = ASGetAvatar(data->config, NULL, m_sender);
|
||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
||||
rev = StrConcat(4, muc_id, "/", name, "[p]");
|
||||
|
||||
Free(JoinMUC(data, event, encoded_from, muc_id, name, sha));
|
||||
XMPPJoinMUC(jabber, xmppified_user, rev);
|
||||
|
||||
to = muc_id;
|
||||
|
||||
Free(sha);
|
||||
Free(mime);
|
||||
Free(name);
|
||||
Free(avatar);
|
||||
Free(rev);
|
||||
}
|
||||
|
||||
if (reply_id)
|
||||
{
|
||||
/* TODO: Monocles chat DM users HATE this trick!
|
||||
|
|
@ -494,13 +342,13 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
|||
char *xmpp_ident = StrRandom(32);
|
||||
builder = CreateStanzaBuilder(xmppified_user, to, xmpp_ident);
|
||||
SetStanzaType(builder, type);
|
||||
SetStanzaBody(builder, unauth ? unauth : (xepd ? xepd : body));
|
||||
SetStanzaBody(builder, xepd ? xepd : body);
|
||||
SetStanzaReply(builder, stanza, sender);
|
||||
SetStanzaLink(builder, unauth);
|
||||
SetStanzaEdit(builder, origin_id);
|
||||
SetStanzaXParsee(builder, event);
|
||||
|
||||
WriteoutStanza(builder, jabber, data->config->max_stanza_size);
|
||||
WriteoutStanza(builder, jabber);
|
||||
DestroyStanzaBuilder(builder);
|
||||
|
||||
if (direct)
|
||||
|
|
@ -523,7 +371,6 @@ end:
|
|||
Free(stanza);
|
||||
Free(sender);
|
||||
Free(unauth);
|
||||
Free(encoded_from);
|
||||
Free(unedited_id);
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
|
|
@ -555,7 +402,8 @@ ParseeEventHandler(ParseeData *data, HashMap *event)
|
|||
return;
|
||||
}
|
||||
else if (StrEquals(event_type, "m.room.message") ||
|
||||
StrEquals(event_type, "m.sticker"))
|
||||
StrEquals(event_type, "m.sticker")) /* TODO: Actual sticker
|
||||
* support here... */
|
||||
{
|
||||
ParseeMessageHandler(data, event);
|
||||
Free(parsee);
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
#include <Matrix.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
UserID *
|
||||
MatrixParseID(char *user)
|
||||
{
|
||||
UserID *ret = NULL;
|
||||
char *localstart, *serverstart;
|
||||
if (!user || *user != '@')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
localstart = user + 1;
|
||||
serverstart = strchr(user, ':');
|
||||
if (!*localstart || !serverstart || localstart == serverstart)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (!*++serverstart)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = Malloc(sizeof(*ret));
|
||||
memset(ret, '\0', sizeof(*ret));
|
||||
memcpy(ret->localpart, localstart, serverstart - localstart - 1);
|
||||
memcpy(ret->server, serverstart, strlen(serverstart));
|
||||
|
||||
return ret;
|
||||
}
|
||||
UserID *
|
||||
MatrixParseIDFromMTO(Uri *uri)
|
||||
{
|
||||
UserID *id = NULL;
|
||||
char *path, *params, *decoded;
|
||||
if (!uri)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!StrEquals(uri->proto, "https") || !StrEquals(uri->host, "matrix.to"))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (strncmp(uri->path, "/#/", 3))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
path = StrDuplicate(uri->path + 3);
|
||||
params = path ? strchr(path, '?') : NULL;
|
||||
if (params)
|
||||
{
|
||||
*params = '\0';
|
||||
}
|
||||
decoded = HttpUrlDecode(path);
|
||||
id = MatrixParseID(decoded);
|
||||
Free(decoded);
|
||||
Free(path);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
|
@ -11,15 +11,210 @@
|
|||
|
||||
static ParseeConfig *config = NULL;
|
||||
|
||||
static char *
|
||||
GetLine(void)
|
||||
{
|
||||
Stream *input = StreamStdin();
|
||||
char *out = NULL;
|
||||
size_t length;
|
||||
UtilGetLine(&out, &length, input);
|
||||
|
||||
if (out)
|
||||
{
|
||||
char *line = strchr(out, '\n');
|
||||
if (line)
|
||||
{
|
||||
*line = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#include <stdarg.h>
|
||||
static char *
|
||||
PromptString(const char *expression, const char *def, ...)
|
||||
{
|
||||
Stream *output = StreamStdout();
|
||||
char *out = NULL;
|
||||
va_list ap;
|
||||
|
||||
while (!out)
|
||||
{
|
||||
va_start(ap, def);
|
||||
|
||||
StreamVprintf(output, expression, ap);
|
||||
if (def)
|
||||
{
|
||||
StreamPrintf(output, " [%s]", def);
|
||||
}
|
||||
StreamPrintf(output, ": ");
|
||||
StreamFlush(output);
|
||||
|
||||
va_end(ap);
|
||||
|
||||
out = GetLine();
|
||||
if (!*out)
|
||||
{
|
||||
Free(out);
|
||||
out = NULL;
|
||||
if (def)
|
||||
{
|
||||
return StrDuplicate(def);
|
||||
}
|
||||
}
|
||||
Log(LOG_INFO, "R=%s", out);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
static int
|
||||
PromptInteger(const char *expression, int def, ...)
|
||||
{
|
||||
Stream *output = StreamStdout();
|
||||
char *out;
|
||||
long l;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, def);
|
||||
|
||||
StreamVprintf(output, expression, ap);
|
||||
if (def >= 0)
|
||||
{
|
||||
StreamPrintf(output, " [%d]", def);
|
||||
}
|
||||
StreamPrintf(output, ": ");
|
||||
StreamFlush(output);
|
||||
|
||||
va_end(ap);
|
||||
while (true)
|
||||
{
|
||||
char *inval;
|
||||
out = GetLine();
|
||||
l = strtol(out, &inval, 10);
|
||||
Free(out);
|
||||
|
||||
/* Not a use-after-free, as we reference only the addresses. */
|
||||
if (l != 0 || inval != out)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (def >= 0)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
/* TODO: Memleaks, galore! */
|
||||
void
|
||||
ParseeConfigInit(void)
|
||||
{
|
||||
Stream *stream;
|
||||
HashMap *json;
|
||||
if (config)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Log(LOG_ERR, "No config file found.");
|
||||
Log(LOG_ERR, "Please use parsee-config to initialise %s.", NAME);
|
||||
|
||||
/* TODO: Give the user an achievement at the end, just because they're
|
||||
* cool. */
|
||||
Log(LOG_NOTICE, "It seems like it is the first time you have configured ");
|
||||
Log(LOG_NOTICE, "Parsee.");
|
||||
Log(LOG_NOTICE, "As such, I need to ask you a couple of questions before ");
|
||||
Log(LOG_NOTICE, "being able to use it.");
|
||||
Log(LOG_NOTICE, "(don't worry; it won't take too long.)");
|
||||
Log(LOG_NOTICE, "");
|
||||
Log(LOG_NOTICE, "");
|
||||
|
||||
config = Malloc(sizeof(*config));
|
||||
config->as_token = StrRandom(32);
|
||||
config->hs_token = StrRandom(32);
|
||||
config->http_threads = 8;
|
||||
config->xmpp_threads = 8;
|
||||
config->db_size = 64 MB;
|
||||
|
||||
/* TODO: This is NOT user friendly, and I know it! */
|
||||
config->sender_localpart = PromptString(
|
||||
"Name of the bridge bot, used for commands and bridged rooms",
|
||||
"_parsee_bridge"
|
||||
);
|
||||
config->namespace_base = PromptString(
|
||||
"Base namespace for Parsee (so foo@bar.com => @[NS]_foo=40bar.com)",
|
||||
"_jabber"
|
||||
);
|
||||
|
||||
config->listen_as = StrDuplicate("localhost");
|
||||
config->port = PromptInteger(
|
||||
"Matrix port for the AS service to use",
|
||||
7642 /* proposed by Saint */
|
||||
);
|
||||
|
||||
|
||||
config->component_host = PromptString(
|
||||
"XMPP component to be used for the configuration",
|
||||
NULL
|
||||
);
|
||||
config->component_port = PromptInteger(
|
||||
"XMPP port for to use for '%s'",
|
||||
5347, config->component_host
|
||||
);
|
||||
config->shared_comp_secret = PromptString(
|
||||
"%s's shared secret",
|
||||
NULL, config->component_host
|
||||
);
|
||||
|
||||
config->homeserver_host = PromptString(
|
||||
"Delegated homeserver to be used for the configuration",
|
||||
NULL
|
||||
);
|
||||
config->homeserver_port = PromptInteger(
|
||||
"HTTP port for to use for '%s'",
|
||||
443, config->homeserver_host
|
||||
);
|
||||
|
||||
config->db_path = PromptString(
|
||||
"Base directory for Parsee data",
|
||||
NULL
|
||||
);
|
||||
config->media_base = PromptString(
|
||||
"Base media URL for bridged media",
|
||||
NULL
|
||||
);
|
||||
|
||||
/* TODO: Make that configurable. */
|
||||
config->server_base = StrDuplicate(config->homeserver_host);
|
||||
|
||||
Log(LOG_NOTICE, "Done! Please look over to the parsee.yaml file, ");
|
||||
Log(LOG_NOTICE, "and follow the instructions listed in it. Then, ");
|
||||
Log(LOG_NOTICE, "restart Parsee. ");
|
||||
Log(LOG_NOTICE, "------------------------------------------------");
|
||||
|
||||
stream = StreamOpen("parsee.json", "w");
|
||||
json = HashMapCreate();
|
||||
HashMapSet(json, "as_token", JsonValueString(config->as_token));
|
||||
HashMapSet(json, "hs_token", JsonValueString(config->hs_token));
|
||||
|
||||
HashMapSet(json, "sender", JsonValueString(config->sender_localpart));
|
||||
HashMapSet(json, "namespace", JsonValueString(config->namespace_base));
|
||||
HashMapSet(json, "listen_as", JsonValueString(config->listen_as));
|
||||
HashMapSet(json, "port", JsonValueInteger(config->port));
|
||||
|
||||
HashMapSet(json, "hs_base", JsonValueString(config->server_base));
|
||||
HashMapSet(json, "hs_host", JsonValueString(config->homeserver_host));
|
||||
HashMapSet(json, "hs_port", JsonValueInteger(config->homeserver_port));
|
||||
|
||||
HashMapSet(json, "component_host", JsonValueString(config->component_host));
|
||||
HashMapSet(json, "component_port", JsonValueInteger(config->component_port));
|
||||
HashMapSet(json, "shared_comp_secret", JsonValueString(config->shared_comp_secret));
|
||||
HashMapSet(json, "db", JsonValueString(config->db_path));
|
||||
|
||||
JsonEncode(json, stream, JSON_PRETTY);
|
||||
JsonFree(json);
|
||||
StreamClose(stream);
|
||||
}
|
||||
void
|
||||
ParseeConfigLoad(char *conf)
|
||||
|
|
@ -30,18 +225,12 @@ ParseeConfigLoad(char *conf)
|
|||
{
|
||||
return;
|
||||
}
|
||||
stream = StreamOpen(conf ? conf : "parsee.json", "r");
|
||||
stream = StreamOpen("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( \
|
||||
|
|
@ -50,9 +239,6 @@ ParseeConfigLoad(char *conf)
|
|||
#define CopyToInt(to, str) config->to = (int) ( \
|
||||
JsonValueAsInteger(HashMapGet(json, str)) \
|
||||
)
|
||||
#define CopyToBool(to, str) config->to = (int) ( \
|
||||
JsonValueAsBoolean(HashMapGet(json, str)) \
|
||||
)
|
||||
|
||||
config->http_threads = 8;
|
||||
config->xmpp_threads = 8;
|
||||
|
|
@ -67,25 +253,10 @@ 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");
|
||||
|
||||
|
|
@ -101,7 +272,6 @@ ParseeSetThreads(int xmpp, int http)
|
|||
{
|
||||
if (!config)
|
||||
{
|
||||
Achievement("THREAD COUNT REQUEST WITHOUT CONFIG", true);
|
||||
return;
|
||||
}
|
||||
config->http_threads = http;
|
||||
|
|
@ -113,7 +283,6 @@ ParseeExportConfigYAML(Stream *stream)
|
|||
{
|
||||
if (!stream || !config)
|
||||
{
|
||||
Achievement("YAML EXPORT REQUEST WITHOUT CONFIG", true);
|
||||
return;
|
||||
}
|
||||
StreamPrintf(stream, "# Autogenerated YAML AS entry for %s\n", NAME);
|
||||
|
|
@ -127,7 +296,6 @@ 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");
|
||||
|
|
@ -136,7 +304,6 @@ ParseeExportConfigYAML(Stream *stream)
|
|||
StreamPrintf(stream, " aliases:\n");
|
||||
StreamPrintf(stream, " - exclusive: true\n");
|
||||
StreamPrintf(stream, " regex: \"#%s_.*\"\n", config->namespace_base);
|
||||
StreamFlush(stream);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -147,7 +314,6 @@ ParseeConfigFree(void)
|
|||
return;
|
||||
}
|
||||
Free(config->component_host);
|
||||
Free(config->component_addr);
|
||||
Free(config->shared_comp_secret);
|
||||
Free(config->db_path);
|
||||
Free(config->homeserver_host);
|
||||
|
|
|
|||
|
|
@ -6,18 +6,17 @@
|
|||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <Routes.h>
|
||||
#include <Glob.h>
|
||||
#include <AS.h>
|
||||
|
||||
ParseeData *
|
||||
ParseeInitData(XMPPComponent *comp)
|
||||
{
|
||||
char *version;
|
||||
ParseeData *data;
|
||||
DbRef *ref;
|
||||
if (!ParseeConfigGet())
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -27,15 +26,8 @@ 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);
|
||||
|
|
@ -43,47 +35,10 @@ ParseeInitData(XMPPComponent *comp)
|
|||
if (!data->db)
|
||||
{
|
||||
Log(LOG_WARNING, "LMDB doesn't seem to be setup.");
|
||||
if (data->config->db_size)
|
||||
{
|
||||
Log(LOG_WARNING, "Falling back to flat-file.");
|
||||
}
|
||||
Log(LOG_WARNING, "Falling back to flat-file.");
|
||||
data->db = DbOpen(data->config->db_path, 0);
|
||||
}
|
||||
|
||||
if (!(ref = DbLock(data->db, 1, "info")))
|
||||
{
|
||||
char *id = StrRandom(64);
|
||||
ref = DbCreate(data->db, 1, "info");
|
||||
HashMapSet(DbJson(ref), "identifier", JsonValueString(id));
|
||||
HashMapSet(DbJson(ref), "version", JsonValueString(VERSION));
|
||||
Free(id);
|
||||
}
|
||||
|
||||
version = GrabString(DbJson(ref), 1, "version");
|
||||
if (version && !ParseeIsCompatible(VERSION, version))
|
||||
{
|
||||
Log(LOG_WARNING, "Version mismatch(curr=%s db=%s).", VERSION, version);
|
||||
Log(LOG_WARNING, "Yeah. You may want to _not_ do that.");
|
||||
Log(LOG_WARNING, "(Parsee still needs an upgradepath mechanism.)");
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
DbClose(data->db);
|
||||
|
||||
HashMapFree(data->oid_servers);
|
||||
pthread_mutex_destroy(&data->oidl);
|
||||
|
||||
XMPPEndCompStream(data->jabber);
|
||||
|
||||
HttpRouterFree(data->router);
|
||||
CommandFreeRouter(data->handler);
|
||||
|
||||
Free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data->id = StrDuplicate(GrabString(DbJson(ref), 1, "identifier"));
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
#define X_ROUTE(path, func) do {\
|
||||
if (!HttpRouterAdd(data->router, path, func))\
|
||||
{\
|
||||
|
|
@ -101,23 +56,12 @@ ParseeInitData(XMPPComponent *comp)
|
|||
void
|
||||
ParseeFreeData(ParseeData *data)
|
||||
{
|
||||
char *entity;
|
||||
XMLElement *disco;
|
||||
if (!data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (HashMapIterate(data->oid_servers, &entity, (void **) &disco))
|
||||
{
|
||||
XMLFreeElement(disco);
|
||||
}
|
||||
HashMapFree(data->oid_servers);
|
||||
pthread_mutex_destroy(&data->oidl);
|
||||
pthread_mutex_destroy(&data->halt_lock);
|
||||
Free(data->id);
|
||||
XMPPEndCompStream(data->jabber);
|
||||
FreeMUCServer(data->muc);
|
||||
DbClose(data->db);
|
||||
HttpRouterFree(data->router);
|
||||
CommandFreeRouter(data->handler);
|
||||
|
|
@ -133,6 +77,8 @@ ParseeCleanup(void *datp)
|
|||
size_t i;
|
||||
uint64_t ts = UtilTsMillis();
|
||||
|
||||
Log(LOG_NOTICE, "Cleaning up...");
|
||||
|
||||
chats = DbList(data->db, 1, "chats");
|
||||
|
||||
for (i = 0; i < ArraySize(chats); i++)
|
||||
|
|
@ -182,12 +128,9 @@ ParseeCleanup(void *datp)
|
|||
} \
|
||||
while (0)
|
||||
|
||||
/* 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 */
|
||||
CleanupField(stanza, 30 MINUTES, 50);
|
||||
CleanupField(event, 30 MINUTES, 50);
|
||||
CleanupField(id, 30 MINUTES, 50);
|
||||
#undef CleanupField
|
||||
}
|
||||
DbListFree(chats);
|
||||
|
|
@ -238,15 +181,252 @@ ParseeCleanup(void *datp)
|
|||
} \
|
||||
while (0)
|
||||
|
||||
CleanupField(stanza, 3 HOURS, 500);
|
||||
CleanupField(event, 3 HOURS, 500);
|
||||
CleanupField(id, 3 HOURS, 500);
|
||||
CleanupField(stanza, 3 HOURS, 50);
|
||||
CleanupField(event, 3 HOURS, 50);
|
||||
CleanupField(id, 3 HOURS, 50);
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
DbListFree(chats);
|
||||
}
|
||||
int
|
||||
ParseeFindDatastart(char *data)
|
||||
{
|
||||
char *startline;
|
||||
bool found = false;
|
||||
if (!data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
startline = data;
|
||||
while (startline)
|
||||
{
|
||||
char *endline = strchr(startline, '\n');
|
||||
|
||||
if (*startline != '>')
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
startline = endline ? endline + 1 : NULL;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) (startline - data);
|
||||
}
|
||||
|
||||
#include <StringStream.h>
|
||||
typedef struct XMPPFlags {
|
||||
bool quote;
|
||||
} XMPPFlags;
|
||||
static char *
|
||||
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||
{
|
||||
char *xepd = NULL, *tmp = NULL;
|
||||
|
||||
size_t i;
|
||||
XMLElement *child;
|
||||
char *reply_id = JsonValueAsString(
|
||||
JsonGet(event, 4,
|
||||
"content", "m.relates_to", "m.in_reply_to", "event_id"
|
||||
));
|
||||
char *room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
||||
HashMap *referenced;
|
||||
char *subxep;
|
||||
#define Concat(strp) do \
|
||||
{ \
|
||||
size_t cidx; \
|
||||
size_t len = strp ? strlen(strp) : 0; \
|
||||
for (cidx = 0; cidx < len; cidx++) \
|
||||
{ \
|
||||
char cch[2] = { strp[cidx], 0 }; \
|
||||
char nch = *cch ? strp[cidx+1] : '\0'; \
|
||||
bool c = *cch == '\n' && nch != '>'; \
|
||||
if (c && flags.quote) \
|
||||
{ \
|
||||
tmp = xepd; \
|
||||
xepd = StrConcat(2, xepd, "\n>"); \
|
||||
Free(tmp); \
|
||||
continue; \
|
||||
} \
|
||||
tmp = xepd; \
|
||||
xepd = StrConcat(2, xepd, cch); \
|
||||
Free(tmp); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
switch (elem->type)
|
||||
{
|
||||
case XML_ELEMENT_DATA:
|
||||
Concat(elem->data);
|
||||
break;
|
||||
case XML_ELEMENT_TAG:
|
||||
if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong"))
|
||||
{
|
||||
Concat("*");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("*");
|
||||
}
|
||||
else if (StrEquals(elem->name, "em"))
|
||||
{
|
||||
Concat("_");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("_");
|
||||
}
|
||||
else if (StrEquals(elem->name, "code"))
|
||||
{
|
||||
Concat("`");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("`");
|
||||
}
|
||||
else if (StrEquals(elem->name, "mx-reply"))
|
||||
{
|
||||
char *str;
|
||||
referenced = ASFind(ParseeConfigGet(), room_id, reply_id);
|
||||
str = JsonValueAsString(
|
||||
JsonGet(referenced, 2, "content", "body")
|
||||
);
|
||||
if (!str)
|
||||
{
|
||||
JsonFree(referenced);
|
||||
return xepd;
|
||||
}
|
||||
Concat(">");
|
||||
flags.quote = true;
|
||||
Concat(str);
|
||||
flags.quote = false;
|
||||
Concat("\n");
|
||||
JsonFree(referenced);
|
||||
}
|
||||
else if (StrEquals(elem->name, "blockquote"))
|
||||
{
|
||||
Concat(">");
|
||||
flags.quote = true;
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
flags.quote = false;
|
||||
Concat("\n");
|
||||
}
|
||||
else if (StrEquals(elem->name, "br"))
|
||||
{
|
||||
Concat("\n");
|
||||
/* HTML fucking SUCKS */
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("\n");
|
||||
}
|
||||
else if (StrEquals(elem->name, "a"))
|
||||
{
|
||||
char *href = HashMapGet(elem->attrs, "href");
|
||||
Concat("(");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat(" points to ");
|
||||
Concat(href);
|
||||
Concat(" )");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return xepd;
|
||||
}
|
||||
char *
|
||||
ParseeXMPPify(HashMap *event)
|
||||
{
|
||||
char *type, *format, *html;
|
||||
char *xepd = NULL;
|
||||
XMLElement *elem;
|
||||
|
||||
XMPPFlags flags;
|
||||
if (!event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if it is a message event. */
|
||||
type = JsonValueAsString(HashMapGet(event, "type"));
|
||||
if (!StrEquals(type, "m.room.message"))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
|
||||
if (!StrEquals(format, "org.matrix.custom.html"))
|
||||
{
|
||||
/* Settle for the raw body instead. */
|
||||
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
|
||||
return StrDuplicate(body);
|
||||
}
|
||||
|
||||
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
|
||||
html = StrConcat(3, "<html>", html, "</html>");
|
||||
elem = XMLCDecode(StrStreamReader(html), true, true);
|
||||
|
||||
flags.quote = false;
|
||||
xepd = XMPPifyElement(event, elem, flags);
|
||||
|
||||
XMLFreeElement(elem);
|
||||
Free(html);
|
||||
|
||||
return xepd;
|
||||
}
|
||||
void
|
||||
ParseePushDMStanza(ParseeData *data, char *room_id, char *stanza_id, char *id, char *ev, char *sender)
|
||||
{
|
||||
|
|
@ -339,15 +519,6 @@ 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);
|
||||
|
|
@ -550,178 +721,140 @@ 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)
|
||||
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *json;
|
||||
if (!data || !chat || !key || !val)
|
||||
HashMap *j, *obj;
|
||||
if (!data || !glob)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref = DbLockIntent(data->db, DB_HINT_WRITE,
|
||||
3, "chats", chat, "settings"
|
||||
);
|
||||
ref = DbLock(data->db, 1, "global_bans");
|
||||
if (!ref)
|
||||
{
|
||||
ref = DbCreate(data->db, 3, "chats", chat, "settings");
|
||||
ref = DbCreate(data->db, 1, "global_bans");
|
||||
}
|
||||
json = DbJson(ref);
|
||||
|
||||
JsonValueFree(HashMapSet(json, key, JsonValueString(val)));
|
||||
j = DbJson(ref);
|
||||
|
||||
obj = HashMapCreate();
|
||||
if (reason)
|
||||
{
|
||||
HashMapSet(obj, "reason", JsonValueString(reason));
|
||||
}
|
||||
HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis()));
|
||||
JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj)));
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
return;
|
||||
}
|
||||
bool
|
||||
ParseeIsMediaEnabled(ParseeData *data, char *chat_id)
|
||||
ParseeManageBan(ParseeData *data, char *user, char *room)
|
||||
{
|
||||
char *value;
|
||||
bool ret;
|
||||
if (!data || !chat_id)
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *key;
|
||||
JsonValue *val;
|
||||
bool banned = false , matches = false;
|
||||
if (!data || !user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = !StrEquals(
|
||||
(value = ParseeGetChatSetting(data, chat_id, "p.media.enabled")),
|
||||
"false"
|
||||
);
|
||||
Free(value);
|
||||
|
||||
return ret;
|
||||
ref = DbLock(data->db, 1, "global_bans");
|
||||
j = DbJson(ref);
|
||||
while (HashMapIterate(j, &key, (void **) &val))
|
||||
{
|
||||
HashMap *obj = JsonValueAsObject(val);
|
||||
if (matches)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (GlobMatches(key, user))
|
||||
{
|
||||
banned = true;
|
||||
matches = true;
|
||||
if (room)
|
||||
{
|
||||
/* TODO: Use the object to set the reason */
|
||||
ASBan(data->config, room, user);
|
||||
(void) obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
return banned;
|
||||
}
|
||||
char *
|
||||
ParseeStringifyDate(uint64_t millis)
|
||||
{
|
||||
uint64_t rest = millis;
|
||||
uint64_t hours, minutes, seconds;
|
||||
char *hs, *ms, *ss, *out;
|
||||
|
||||
hours = rest / (1 HOURS);
|
||||
rest = rest % (1 HOURS);
|
||||
|
||||
minutes = rest / (1 MINUTES);
|
||||
rest = rest % (1 MINUTES);
|
||||
|
||||
seconds = rest / (1 SECONDS);
|
||||
|
||||
hs = StrInt(hours);
|
||||
ms = StrInt(minutes);
|
||||
ss = StrInt(seconds);
|
||||
|
||||
out = StrConcat(8,
|
||||
hours ? hs : "",
|
||||
hours ? " hours" : "",
|
||||
(hours && minutes) ? ", " : "",
|
||||
|
||||
minutes ? ms : "",
|
||||
minutes ? " minutes" : "",
|
||||
(minutes && seconds) ? ", " : "",
|
||||
|
||||
seconds ? ss : "",
|
||||
seconds ? " seconds" : ""
|
||||
);
|
||||
Free(hs);
|
||||
Free(ms);
|
||||
Free(ss);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void
|
||||
ParseeAchievement(const char *func, const char *msg, bool die)
|
||||
{
|
||||
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
||||
Log(LOG_ERR, "%s: %s.", func, msg);
|
||||
Log(LOG_ERR, "THIS IS, LET'S SAY, NOT GOOD, AND A SIGN OF ");
|
||||
Log(LOG_ERR, "A PROGRAMMER ERROR. PLEASE KILLALL -9 PARSEE.");
|
||||
Log(LOG_ERR, "");
|
||||
Log(LOG_ERR, "YOU, HOWEVER, GET TO WIN AN AWARD FOR THIS.");
|
||||
Log(LOG_ERR, "I AM SIMPLY JEALOUS OF YOU GETTING SUCH A ");
|
||||
Log(LOG_ERR, "GOOD ERROR MESSAGE.");
|
||||
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
||||
|
||||
if (die)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
char *
|
||||
ParseeGenerateMTO(char *common_id)
|
||||
{
|
||||
char *matrix_to;
|
||||
if (!common_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
common_id = HttpUrlEncode(common_id);
|
||||
matrix_to = StrConcat(2, "https://matrix.to/#/", common_id);
|
||||
Free(common_id);
|
||||
|
||||
return matrix_to;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
/* CC0 implementation of a HMAC system based on Cytoplasm.
|
||||
* Ignore the scary "Parsee.h", its practically unused. */
|
||||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void
|
||||
ComputeKPad(char *key, uint8_t pad, uint8_t *kopad)
|
||||
{
|
||||
size_t klen;
|
||||
uint8_t *kp;
|
||||
uint8_t kpi[64] = { 0 };
|
||||
size_t i;
|
||||
if ((klen = strlen(key)) <= 64)
|
||||
{
|
||||
kp = Malloc(klen * sizeof(uint8_t));
|
||||
memcpy(kp, key, klen);
|
||||
}
|
||||
else
|
||||
{
|
||||
kp = (uint8_t *) Sha256(key);
|
||||
klen = 32;
|
||||
}
|
||||
|
||||
memset(kpi, 0x00, 64);
|
||||
memcpy(kpi, kp, klen);
|
||||
Free(kp);
|
||||
|
||||
/* Now that we have K', lets compute it XORd with opad */
|
||||
for (i = 0; i < 64; i++)
|
||||
{
|
||||
uint8_t byte = kpi[i];
|
||||
kopad[i] = byte ^ pad;
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeHMAC(char *key, uint8_t *msg, size_t msglen)
|
||||
{
|
||||
uint8_t opad[64], ipad[64 + msglen];
|
||||
uint8_t outer[64 + 32];
|
||||
|
||||
uint8_t *innersha;
|
||||
uint8_t *sha;
|
||||
char *str;
|
||||
if (!key || !msg || !msglen)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialise K' XOR opad and K' XOR ipad */
|
||||
ComputeKPad(key, 0x5C, opad);
|
||||
ComputeKPad(key, 0x36, ipad);
|
||||
|
||||
/* Compute H((K' XOR ipad) || msg) */
|
||||
memcpy(ipad + 64, msg, msglen);
|
||||
innersha = Sha256Raw(ipad, 64 + msglen);
|
||||
|
||||
/* Compute (K' XOR opad) || H((K' XOR ipad) || msg) */
|
||||
memcpy(outer, opad, 64);
|
||||
memcpy(outer + 64, innersha, 32);
|
||||
|
||||
/* Compute H((K' XOR opad) || H((K' XOR ipad) || msg)) */
|
||||
sha = Sha256Raw(outer, 64 + 32);
|
||||
str = ShaToHex(sha, HASH_SHA256);
|
||||
|
||||
Free(innersha);
|
||||
Free(sha);
|
||||
return str;
|
||||
}
|
||||
194
src/Parsee/JIDTable.c
Normal file
194
src/Parsee/JIDTable.c
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t lock;
|
||||
static HashMap *jid_table = NULL;
|
||||
|
||||
void
|
||||
ParseeInitialiseJIDTable(void)
|
||||
{
|
||||
if (jid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
pthread_mutex_lock(&lock);
|
||||
jid_table = HashMapCreate();
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
void
|
||||
ParseePushJIDTable(char *muc, char *bare)
|
||||
{
|
||||
if (!muc || !bare || !jid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
bare = ParseeTrimJID(bare);
|
||||
Free(HashMapSet(jid_table, muc, bare));
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
char *
|
||||
ParseeLookupJID(char *muc)
|
||||
{
|
||||
char *bare;
|
||||
if (!muc || !jid_table)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
bare = StrDuplicate(HashMapGet(jid_table, muc));
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
if (!bare)
|
||||
{
|
||||
bare = StrDuplicate(muc);
|
||||
}
|
||||
return bare;
|
||||
}
|
||||
void
|
||||
ParseeDestroyJIDTable(void)
|
||||
{
|
||||
char *key;
|
||||
void *val;
|
||||
if (!jid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
while (HashMapIterate(jid_table, &key, &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(jid_table);
|
||||
jid_table = NULL;
|
||||
pthread_mutex_unlock(&lock);
|
||||
pthread_mutex_destroy(&lock);
|
||||
}
|
||||
|
||||
static pthread_mutex_t head_lock;
|
||||
static HashMap *head_table = NULL;
|
||||
void
|
||||
ParseeInitialiseHeadTable(void)
|
||||
{
|
||||
if (head_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&head_lock, NULL);
|
||||
pthread_mutex_lock(&head_lock);
|
||||
head_table = HashMapCreate();
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
}
|
||||
void
|
||||
ParseePushHeadTable(char *room, char *event)
|
||||
{
|
||||
if (!room || !event || !head_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&head_lock);
|
||||
event = StrDuplicate(event);
|
||||
Free(HashMapSet(head_table, room, event));
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
}
|
||||
char *
|
||||
ParseeLookupHead(char *room)
|
||||
{
|
||||
char *event;
|
||||
if (!room || !head_table)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&head_lock);
|
||||
event = StrDuplicate(HashMapGet(head_table, room));
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
|
||||
return event;
|
||||
}
|
||||
void
|
||||
ParseeDestroyHeadTable(void)
|
||||
{
|
||||
char *key;
|
||||
void *val;
|
||||
if (!head_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&head_lock);
|
||||
while (HashMapIterate(head_table, &key, &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(head_table);
|
||||
head_table = NULL;
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
pthread_mutex_destroy(&head_lock);
|
||||
}
|
||||
|
||||
|
||||
static pthread_mutex_t oid_lock;
|
||||
static HashMap *oid_table = NULL;
|
||||
|
||||
void
|
||||
ParseeInitialiseOIDTable(void)
|
||||
{
|
||||
if (oid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&oid_lock, NULL);
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
oid_table = HashMapCreate();
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
}
|
||||
void
|
||||
ParseePushOIDTable(char *muc, char *bare)
|
||||
{
|
||||
if (!muc || !bare || !oid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
bare = StrDuplicate(bare);
|
||||
Free(HashMapSet(oid_table, muc, bare));
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
}
|
||||
char *
|
||||
ParseeLookupOID(char *muc)
|
||||
{
|
||||
char *bare;
|
||||
if (!muc || !oid_table)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
bare = StrDuplicate(HashMapGet(oid_table, muc));
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
|
||||
return bare;
|
||||
}
|
||||
void
|
||||
ParseeDestroyOIDTable(void)
|
||||
{
|
||||
char *key;
|
||||
void *val;
|
||||
if (!oid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
while (HashMapIterate(oid_table, &key, &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(oid_table);
|
||||
oid_table = NULL;
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
pthread_mutex_destroy(&oid_lock);
|
||||
}
|
||||
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
const char *parsee_ascii[PARSEE_ASCII_LINES] =
|
||||
{
|
||||
" =+======",
|
||||
" || | _ _/__----",
|
||||
" / || \\ ==+= _/_____\\_",
|
||||
" | || | -|- L___J ",
|
||||
"_/ || \\_ ||| .______\\",
|
||||
" || | | | |.____.|",
|
||||
" || / | \\ |L____||",
|
||||
" _// | | J"
|
||||
};
|
||||
|
||||
void
|
||||
ParseePrintASCII(void)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < PARSEE_ASCII_LINES; i++)
|
||||
{
|
||||
Log(LOG_INFO, "%s", parsee_ascii[i]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
static char *
|
||||
ToHex(unsigned char *input, size_t length)
|
||||
{
|
||||
char *hex = Malloc(length * 2 + 1);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
const char *table =
|
||||
"0123456789abcdef";
|
||||
unsigned char byte = input[i];
|
||||
hex[2 * i] = table[(byte >> 4) & 0xF];
|
||||
hex[2*i+1] = table[(byte >> 0) & 0xF];
|
||||
}
|
||||
|
||||
hex[length * 2] = '\0';
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeSHA256(char *string)
|
||||
{
|
||||
unsigned char *sha;
|
||||
char *returnString;
|
||||
if (!string)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sha = Sha256(string);
|
||||
returnString = ToHex(sha, 32);
|
||||
Free(sha);
|
||||
|
||||
return returnString;
|
||||
}
|
||||
char *
|
||||
ParseeSHA1(char *string)
|
||||
{
|
||||
unsigned char *sha;
|
||||
char *returnString;
|
||||
if (!string)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sha = Sha1(string);
|
||||
returnString = ToHex(sha, 20);
|
||||
Free(sha);
|
||||
|
||||
return returnString;
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
#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,67 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t head_lock;
|
||||
static HashMap *head_table = NULL;
|
||||
void
|
||||
ParseeInitialiseHeadTable(void)
|
||||
{
|
||||
if (head_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&head_lock, NULL);
|
||||
pthread_mutex_lock(&head_lock);
|
||||
head_table = HashMapCreate();
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
}
|
||||
void
|
||||
ParseePushHeadTable(char *room, char *event)
|
||||
{
|
||||
if (!room || !event || !head_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&head_lock);
|
||||
event = StrDuplicate(event);
|
||||
Free(HashMapSet(head_table, room, event));
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
}
|
||||
char *
|
||||
ParseeLookupHead(char *room)
|
||||
{
|
||||
char *event;
|
||||
if (!room || !head_table)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&head_lock);
|
||||
event = StrDuplicate(HashMapGet(head_table, room));
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
|
||||
return event;
|
||||
}
|
||||
void
|
||||
ParseeDestroyHeadTable(void)
|
||||
{
|
||||
char *key;
|
||||
void *val;
|
||||
if (!head_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&head_lock);
|
||||
while (HashMapIterate(head_table, &key, &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(head_table);
|
||||
head_table = NULL;
|
||||
pthread_mutex_unlock(&head_lock);
|
||||
pthread_mutex_destroy(&head_lock);
|
||||
}
|
||||
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t lock;
|
||||
static HashMap *jid_table = NULL;
|
||||
|
||||
void
|
||||
ParseeInitialiseJIDTable(void)
|
||||
{
|
||||
if (jid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&lock, NULL);
|
||||
pthread_mutex_lock(&lock);
|
||||
jid_table = HashMapCreate();
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
void
|
||||
ParseePushJIDTable(char *muc, char *bare)
|
||||
{
|
||||
if (!muc || !bare || !jid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
bare = ParseeTrimJID(bare);
|
||||
Free(HashMapSet(jid_table, muc, bare));
|
||||
pthread_mutex_unlock(&lock);
|
||||
}
|
||||
char *
|
||||
ParseeLookupJID(char *muc)
|
||||
{
|
||||
char *bare;
|
||||
if (!muc || !jid_table)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
bare = StrDuplicate(HashMapGet(jid_table, muc));
|
||||
pthread_mutex_unlock(&lock);
|
||||
|
||||
if (!bare)
|
||||
{
|
||||
bare = StrDuplicate(muc);
|
||||
}
|
||||
return bare;
|
||||
}
|
||||
void
|
||||
ParseeDestroyJIDTable(void)
|
||||
{
|
||||
char *key;
|
||||
void *val;
|
||||
if (!jid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&lock);
|
||||
while (HashMapIterate(jid_table, &key, &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(jid_table);
|
||||
jid_table = NULL;
|
||||
pthread_mutex_unlock(&lock);
|
||||
pthread_mutex_destroy(&lock);
|
||||
}
|
||||
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t nick_lock;
|
||||
static HashMap *nick_table = NULL;
|
||||
|
||||
static char *
|
||||
GenerateKey(char *muc, char *mxid)
|
||||
{
|
||||
char *concatStr;
|
||||
char *hexDigest;
|
||||
if (!muc || !mxid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
concatStr = StrConcat(3, muc, ":", mxid);
|
||||
hexDigest = ParseeSHA256(concatStr);
|
||||
|
||||
Free (concatStr);
|
||||
return hexDigest;
|
||||
}
|
||||
|
||||
void
|
||||
ParseeInitialiseNickTable(void)
|
||||
{
|
||||
if (nick_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&nick_lock, NULL);
|
||||
pthread_mutex_lock(&nick_lock);
|
||||
nick_table = HashMapCreate();
|
||||
pthread_mutex_unlock(&nick_lock);
|
||||
}
|
||||
void
|
||||
ParseePushNickTable(char *muc, char *mxid, char *nick)
|
||||
{
|
||||
char *key;
|
||||
if (!muc || !mxid || !nick_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&nick_lock);
|
||||
|
||||
key = GenerateKey(muc, mxid);
|
||||
nick = StrDuplicate(nick);
|
||||
if (nick)
|
||||
{
|
||||
Free(HashMapSet(nick_table, key, nick));
|
||||
}
|
||||
else
|
||||
{
|
||||
Free(HashMapDelete(nick_table, key));
|
||||
}
|
||||
Free(key);
|
||||
|
||||
pthread_mutex_unlock(&nick_lock);
|
||||
}
|
||||
char *
|
||||
ParseeLookupNick(char *muc, char *mxid)
|
||||
{
|
||||
char *ret, *key;
|
||||
if (!muc || !nick_table)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&nick_lock);
|
||||
|
||||
key = GenerateKey(muc, mxid);
|
||||
ret = HashMapGet(nick_table, key);
|
||||
Free(key);
|
||||
|
||||
pthread_mutex_unlock(&nick_lock);
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
ParseeDestroyNickTable(void)
|
||||
{
|
||||
char *key;
|
||||
void *val;
|
||||
if (!nick_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&nick_lock);
|
||||
while (HashMapIterate(nick_table, &key, &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(nick_table);
|
||||
nick_table = NULL;
|
||||
pthread_mutex_unlock(&nick_lock);
|
||||
pthread_mutex_destroy(&nick_lock);
|
||||
}
|
||||
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static pthread_mutex_t oid_lock;
|
||||
static HashMap *oid_table = NULL;
|
||||
|
||||
void
|
||||
ParseeInitialiseOIDTable(void)
|
||||
{
|
||||
if (oid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_init(&oid_lock, NULL);
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
oid_table = HashMapCreate();
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
}
|
||||
void
|
||||
ParseePushOIDTable(char *muc, char *bare)
|
||||
{
|
||||
if (!muc || !bare || !oid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
bare = StrDuplicate(bare);
|
||||
Free(HashMapSet(oid_table, muc, bare));
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
}
|
||||
char *
|
||||
ParseeLookupOID(char *muc)
|
||||
{
|
||||
char *bare;
|
||||
if (!muc || !oid_table)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
bare = StrDuplicate(HashMapGet(oid_table, muc));
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
|
||||
return bare;
|
||||
}
|
||||
void
|
||||
ParseeDestroyOIDTable(void)
|
||||
{
|
||||
char *key;
|
||||
void *val;
|
||||
if (!oid_table)
|
||||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&oid_lock);
|
||||
while (HashMapIterate(oid_table, &key, &val))
|
||||
{
|
||||
Free(val);
|
||||
}
|
||||
HashMapFree(oid_table);
|
||||
oid_table = NULL;
|
||||
pthread_mutex_unlock(&oid_lock);
|
||||
pthread_mutex_destroy(&oid_lock);
|
||||
}
|
||||
|
||||
|
|
@ -1,46 +1,42 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
|
||||
bool
|
||||
ParseeIsPuppet(const ParseeConfig *conf, char *user)
|
||||
{
|
||||
UserID *id;
|
||||
char *localpart;
|
||||
bool flag = true;
|
||||
size_t len;
|
||||
if (!user ||
|
||||
!conf ||
|
||||
*user != '@' ||
|
||||
!(id = MatrixParseID(user)))
|
||||
if (!user || !conf || *user != '@')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
localpart = user + 1;
|
||||
len = strlen(conf->namespace_base);
|
||||
if (strncmp(id->localpart, conf->namespace_base, len))
|
||||
if (strncmp(localpart, conf->namespace_base, len))
|
||||
{
|
||||
flag = false;
|
||||
}
|
||||
|
||||
if (StrEquals(id->localpart, conf->sender_localpart))
|
||||
len = strlen(conf->sender_localpart);
|
||||
if (!strncmp(localpart, conf->sender_localpart, len))
|
||||
{
|
||||
flag = true;
|
||||
}
|
||||
|
||||
flag = flag && StrEquals(id->server, conf->server_base);
|
||||
|
||||
Free(id);
|
||||
/* TODO: Check serverpart. 2 Parsee instances may be running in the same
|
||||
* room. */
|
||||
return flag;
|
||||
}
|
||||
char *
|
||||
|
|
@ -97,7 +93,7 @@ ParseeDecodeLocalJID(const ParseeConfig *c, char *mxid)
|
|||
data_start = jid_flags;
|
||||
while (*data_start && *data_start != '_')
|
||||
{
|
||||
/* TODO: Get rid of this */
|
||||
/* TODO: Make this a macro */
|
||||
if (*data_start == 'l')
|
||||
{
|
||||
plain_jid = true;
|
||||
|
|
@ -127,7 +123,7 @@ ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias)
|
|||
data_start = jid_flags;
|
||||
while (*data_start && *data_start != '_')
|
||||
{
|
||||
/* TODO: Get rid of this */
|
||||
/* TODO: Make this a macro */
|
||||
if (*data_start == 'm')
|
||||
{
|
||||
plain_jid = true;
|
||||
|
|
@ -148,15 +144,14 @@ char *
|
|||
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
||||
{
|
||||
char *ret, *tmp;
|
||||
size_t i, len;
|
||||
size_t i;
|
||||
if (!c || !jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = StrConcat(2, c->namespace_base, "_l_");
|
||||
len = strlen(jid);
|
||||
for (i = 0; i < len; i++)
|
||||
for (i = 0; i < strlen(jid); i++)
|
||||
{
|
||||
char cpy = jid[i];
|
||||
char cs[4] = { 0 };
|
||||
|
|
@ -166,7 +161,7 @@ ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
|||
/* RID: Break everything and die. */
|
||||
break;
|
||||
}
|
||||
if (islower((int) *cs) || isalnum((int) *cs) || *cs == '_' ||
|
||||
if (islower(*cs) || isalnum(*cs) || *cs == '_' ||
|
||||
*cs == '=' || *cs == '-' || *cs == '/' ||
|
||||
*cs == '+' || *cs == '.')
|
||||
{
|
||||
|
|
@ -195,7 +190,7 @@ char *
|
|||
ParseeGetLocal(char *mxid)
|
||||
{
|
||||
char *cpy;
|
||||
size_t i, len;
|
||||
size_t i;
|
||||
if (!mxid)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -205,14 +200,12 @@ ParseeGetLocal(char *mxid)
|
|||
return StrDuplicate(mxid);
|
||||
}
|
||||
|
||||
len = strlen(mxid);
|
||||
|
||||
mxid++;
|
||||
cpy = Malloc(len + 1);
|
||||
memset(cpy, '\0', len + 1);
|
||||
memcpy(cpy, mxid, len);
|
||||
cpy = Malloc(strlen(mxid) + 1);
|
||||
memset(cpy, '\0', strlen(mxid) + 1);
|
||||
memcpy(cpy, mxid, strlen(mxid));
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
for (i = 0; i < strlen(mxid); i++)
|
||||
{
|
||||
if (cpy[i] == ':')
|
||||
{
|
||||
|
|
@ -228,19 +221,19 @@ char *
|
|||
ParseeEncodeMXID(char *mxid)
|
||||
{
|
||||
char *ret;
|
||||
size_t i, j, len;
|
||||
size_t i, j;
|
||||
if (!mxid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Worst case scenario of 3-bytes the char */
|
||||
len = strlen(mxid);
|
||||
ret = Malloc(len * 3 + 1);
|
||||
for (i = 0, j = 0; i < len; i++)
|
||||
ret = Malloc(strlen(mxid) * 3 + 1);
|
||||
for (i = 0, j = 0; i < strlen(mxid); i++)
|
||||
{
|
||||
char src = mxid[i];
|
||||
|
||||
/* TODO: More robust system */
|
||||
if (src <= 0x20 ||
|
||||
(src == '"' || src == '&' ||
|
||||
src == '\'' || src == '/' ||
|
||||
|
|
@ -302,17 +295,19 @@ char *
|
|||
ParseeGetDMID(char *mxid, char *jid)
|
||||
{
|
||||
char *concat, *sha;
|
||||
unsigned char *raw;
|
||||
if (!mxid || !jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* We really don't care about safety here. */
|
||||
jid = ParseeTrimJID(jid);
|
||||
concat = StrConcat(2, mxid, jid);
|
||||
sha = ParseeSHA1(concat);
|
||||
raw = Sha1(concat);
|
||||
sha = ShaToHex(raw);
|
||||
|
||||
Free(concat);
|
||||
Free(raw);
|
||||
Free(jid);
|
||||
|
||||
return sha;
|
||||
|
|
@ -358,34 +353,19 @@ ParseePushDMRoom(ParseeData *d, char *mxid, char *jid, char *r)
|
|||
Free(dmid);
|
||||
return;
|
||||
}
|
||||
void
|
||||
ParseeDeleteDM(ParseeData *d, char *mxid, char *jid)
|
||||
{
|
||||
char *dmid;
|
||||
if (!d || !mxid || !jid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dmid = ParseeGetDMID(mxid, jid);
|
||||
DbDelete(d->db, 2, "users", dmid);
|
||||
Free(dmid);
|
||||
return;
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeTrimJID(char *jid)
|
||||
{
|
||||
char *ret;
|
||||
size_t i, len;
|
||||
size_t i;
|
||||
if (!jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = StrDuplicate(jid);
|
||||
len = strlen(ret);
|
||||
for (i = 0; i < len; i++)
|
||||
for (i = 0; i < strlen(ret); i++)
|
||||
{
|
||||
if (ret[i] == '/')
|
||||
{
|
||||
|
|
@ -539,6 +519,7 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ParseeSendPresence(ParseeData *data)
|
||||
{
|
||||
|
|
@ -557,16 +538,10 @@ 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, NULL, diff, false);
|
||||
XMPPJoinMUC(data->jabber, "parsee", rev);
|
||||
|
||||
DbUnlock(data->db, chat);
|
||||
Free(chat_id);
|
||||
Free(rev);
|
||||
}
|
||||
DbUnlock(data->db, ref);
|
||||
|
|
@ -688,13 +663,11 @@ end:
|
|||
|
||||
#include <Cytoplasm/Uri.h>
|
||||
char *
|
||||
ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
||||
ParseeToUnauth(ParseeData *data, char *mxc)
|
||||
{
|
||||
Uri *url = NULL;
|
||||
char *ret;
|
||||
char *key, *hmac;
|
||||
#define PAT "%s/media/%s%s?hmac=%s"
|
||||
#define PATF "%s/media/%s%s/%s?hmac=%s"
|
||||
#define PAT "%s/_matrix/client/v1/media/download/%s%s"
|
||||
size_t l;
|
||||
if (!data || !mxc)
|
||||
{
|
||||
|
|
@ -711,51 +684,19 @@ ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
key = StrConcat(2, url->host, url->path);
|
||||
hmac = ParseeHMACS(data->id, key);
|
||||
Free(key);
|
||||
|
||||
if (!filename)
|
||||
{
|
||||
l = snprintf(NULL, 0,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *encoded = HttpUrlEncode(filename);
|
||||
l = snprintf(NULL, 0,
|
||||
PATF,
|
||||
data->config->media_base,
|
||||
url->host, url->path, encoded,
|
||||
hmac
|
||||
);
|
||||
Free(encoded);
|
||||
}
|
||||
/* TODO: HTTPS */
|
||||
l = snprintf(NULL, 0,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path
|
||||
);
|
||||
ret = Malloc(l + 3);
|
||||
if (!filename)
|
||||
{
|
||||
snprintf(ret, l + 1,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path,
|
||||
hmac
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(ret, l + 1,
|
||||
PATF,
|
||||
data->config->media_base,
|
||||
url->host, url->path, filename,
|
||||
hmac
|
||||
);
|
||||
}
|
||||
snprintf(ret, l + 1,
|
||||
PAT,
|
||||
data->config->media_base,
|
||||
url->host, url->path
|
||||
);
|
||||
UriFree(url);
|
||||
Free(hmac);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,26 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void
|
||||
ParseeAchievement(const char *func, const char *msg, bool die)
|
||||
{
|
||||
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
||||
Log(LOG_ERR, "%s: %s.", func, msg);
|
||||
Log(LOG_ERR, "THIS IS, LET'S SAY, NOT GOOD, AND A SIGN OF ");
|
||||
Log(LOG_ERR, "A PROGRAMMER ERROR. PLEASE KILLALL -9 PARSEE.");
|
||||
Log(LOG_ERR, "");
|
||||
Log(LOG_ERR, "YOU, HOWEVER, GET TO WIN AN AWARD FOR THIS.");
|
||||
Log(LOG_ERR, "I AM SIMPLY JEALOUS OF YOU GETTING SUCH A ");
|
||||
Log(LOG_ERR, "GOOD ERROR MESSAGE.");
|
||||
Log(LOG_ERR, "=========== Achievement GET! ===========");
|
||||
|
||||
if (die)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
void
|
||||
ParseeGenerateHelp(const Argument *list)
|
||||
{
|
||||
if (!list)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (!list->end)
|
||||
{
|
||||
char *str = list->value_req ?
|
||||
StrConcat(3, " [", list->value_descr, "]") :
|
||||
StrDuplicate("");
|
||||
Log(LOG_INFO, "-%c%s", list->argument, str);
|
||||
LogConfigIndent(LogConfigGlobal());
|
||||
Log(LOG_INFO, "%s", list->description);
|
||||
LogConfigUnindent(LogConfigGlobal());
|
||||
list++;
|
||||
|
||||
Free(str);
|
||||
}
|
||||
return;
|
||||
}
|
||||
char *
|
||||
ParseeGenerateGetopt(const Argument *list)
|
||||
{
|
||||
char *ret = NULL, *tmp = NULL;
|
||||
if (!list)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (!list->end)
|
||||
{
|
||||
char buffer[3] = {
|
||||
list->argument, list->value_req ? ':' : '\0',
|
||||
'\0'
|
||||
};
|
||||
|
||||
tmp = ret;
|
||||
ret = StrConcat(2, ret, buffer);
|
||||
Free(tmp);
|
||||
|
||||
list++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1,323 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Http.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Uri.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <Matrix.h>
|
||||
#include <XML.h>
|
||||
#include <AS.h>
|
||||
|
||||
typedef struct XMPPFlags {
|
||||
bool quote;
|
||||
} XMPPFlags;
|
||||
static char *
|
||||
XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||
{
|
||||
char *xepd = NULL, *tmp = NULL;
|
||||
|
||||
size_t i;
|
||||
XMLElement *child;
|
||||
char *reply_id = GrabString(
|
||||
event,
|
||||
4, "content",
|
||||
"m.relates_to", "m.in_reply_to", "event_id"
|
||||
);
|
||||
char *room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
||||
HashMap *referenced;
|
||||
char *subxep;
|
||||
#define Concat(strp) do \
|
||||
{ \
|
||||
size_t cidx; \
|
||||
size_t len = strp ? strlen(strp) : 0; \
|
||||
for (cidx = 0; cidx < len; cidx++) \
|
||||
{ \
|
||||
char cch[2]; \
|
||||
cch[0] = strp[cidx]; \
|
||||
cch[1] = '\0'; \
|
||||
char nch = *cch ? strp[cidx+1] : '\0'; \
|
||||
bool c = *cch == '\n' && nch != '>'; \
|
||||
if (c && flags.quote) \
|
||||
{ \
|
||||
tmp = xepd; \
|
||||
xepd = StrConcat(2, xepd, "\n>"); \
|
||||
Free(tmp); \
|
||||
continue; \
|
||||
} \
|
||||
tmp = xepd; \
|
||||
xepd = StrConcat(2, xepd, cch); \
|
||||
Free(tmp); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
switch (elem ? elem->type : -1)
|
||||
{
|
||||
case XML_ELEMENT_DATA:
|
||||
Concat(elem->data);
|
||||
break;
|
||||
case XML_ELEMENT_TAG:
|
||||
if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong"))
|
||||
{
|
||||
Concat("*");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("*");
|
||||
}
|
||||
else if (StrEquals(elem->name, "em"))
|
||||
{
|
||||
Concat("_");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("_");
|
||||
}
|
||||
else if (StrEquals(elem->name, "code"))
|
||||
{
|
||||
Concat("`");
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat("`");
|
||||
}
|
||||
else if (StrEquals(elem->name, "mx-reply"))
|
||||
{
|
||||
char *str;
|
||||
referenced = ASFind(ParseeConfigGet(), room_id, reply_id);
|
||||
str = JsonValueAsString(
|
||||
JsonGet(referenced, 2, "content", "body")
|
||||
);
|
||||
if (!str)
|
||||
{
|
||||
JsonFree(referenced);
|
||||
return xepd;
|
||||
}
|
||||
Concat(">");
|
||||
flags.quote = true;
|
||||
Concat(str);
|
||||
flags.quote = false;
|
||||
Concat("\n");
|
||||
JsonFree(referenced);
|
||||
}
|
||||
else if (StrEquals(elem->name, "blockquote"))
|
||||
{
|
||||
Concat(">");
|
||||
flags.quote = true;
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
flags.quote = false;
|
||||
Concat("\n");
|
||||
}
|
||||
else if (StrEquals(elem->name, "br"))
|
||||
{
|
||||
Concat("\n");
|
||||
/* HTML fucking SUCKS */
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
if (i != 0)
|
||||
{
|
||||
Concat("\n");
|
||||
}
|
||||
}
|
||||
else if (StrEquals(elem->name, "a"))
|
||||
{
|
||||
char *href = HashMapGet(elem->attrs, "href");
|
||||
Uri *pref = UriParse(href);
|
||||
if (pref && StrEquals(pref->host, "matrix.to"))
|
||||
{
|
||||
/* TODO: Check if the element here is a Matrix.TO
|
||||
* pointing to a Parsee user. */
|
||||
UserID *id = MatrixParseIDFromMTO(pref);
|
||||
if (id)
|
||||
{
|
||||
char *real_id = StrConcat(4, "@", id->localpart, ":", id->server);
|
||||
/* TODO: Detect if it already is a Parsee user */
|
||||
if (ParseeIsPuppet(conf, real_id))
|
||||
{
|
||||
char *name = ASGetName(conf, NULL, real_id);
|
||||
Concat((name ? name : real_id));
|
||||
Free(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
Concat(real_id);
|
||||
}
|
||||
Free(real_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
Concat(href);
|
||||
}
|
||||
|
||||
Free(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
Concat(" < ");
|
||||
Concat(href);
|
||||
Concat(" >");
|
||||
}
|
||||
UriFree(pref);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
child = ArrayGet(elem->children, i);
|
||||
subxep = XMPPifyElement(conf, event, child, flags);
|
||||
|
||||
Concat(subxep);
|
||||
Free(subxep);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return xepd;
|
||||
}
|
||||
static char *
|
||||
GetRawBody(HashMap *event)
|
||||
{
|
||||
void *id;
|
||||
if ((id = MatrixGetEdit(event)))
|
||||
{
|
||||
char *new = GrabString(event, 3, "content", "m.new_content", "body");
|
||||
Free(id);
|
||||
if (new)
|
||||
{
|
||||
return new;
|
||||
}
|
||||
}
|
||||
return GrabString(event, 2, "content", "body");
|
||||
}
|
||||
static char *
|
||||
GetHTMLBody(HashMap *event)
|
||||
{
|
||||
if (MatrixGetEdit(event))
|
||||
{
|
||||
char *new = GrabString(event, 3, "content", "m.new_content", "formatted_body");
|
||||
if (new)
|
||||
{
|
||||
return new;
|
||||
}
|
||||
}
|
||||
return GrabString(event, 2, "content", "formatted_body");
|
||||
}
|
||||
static char *
|
||||
GetBodyFormat(HashMap *event)
|
||||
{
|
||||
if (MatrixGetEdit(event))
|
||||
{
|
||||
return GrabString(event, 3, "content", "m.new_content", "format");
|
||||
}
|
||||
return GrabString(event, 2, "content", "format");
|
||||
}
|
||||
char *
|
||||
ParseeXMPPify(ParseeData *data, HashMap *event)
|
||||
{
|
||||
char *type, *format, *html;
|
||||
char *xepd = NULL;
|
||||
XMLElement *elem;
|
||||
|
||||
XMPPFlags flags;
|
||||
if (!event)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check if it is a message event. */
|
||||
type = JsonValueAsString(HashMapGet(event, "type"));
|
||||
if (!StrEquals(type, "m.room.message"))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!StrEquals(GetBodyFormat(event), "org.matrix.custom.html"))
|
||||
{
|
||||
/* Settle for the raw body instead. */
|
||||
char *body = GetRawBody(event);
|
||||
return StrDuplicate(body);
|
||||
}
|
||||
|
||||
html = GetHTMLBody(event);
|
||||
|
||||
html = StrConcat(3, "<html>", html, "</html>");
|
||||
elem = XMLCDecode(StrStreamReader(html), true, true);
|
||||
if (!elem)
|
||||
{
|
||||
/* Settle for the raw body instead.
|
||||
* TODO: Have the parser be more leinent on errors in HTML mode. */
|
||||
char *body = GetRawBody(event);
|
||||
Free(html);
|
||||
return StrDuplicate(body);
|
||||
}
|
||||
|
||||
flags.quote = false;
|
||||
xepd = XMPPifyElement(data ? data->config : NULL, event, elem, flags);
|
||||
|
||||
XMLFreeElement(elem);
|
||||
Free(html);
|
||||
|
||||
return xepd;
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeGenerateMTO(char *common_id)
|
||||
{
|
||||
char *matrix_to;
|
||||
if (!common_id)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* TODO: Is HttpUrlEncode okay? */
|
||||
common_id = HttpUrlEncode(common_id);
|
||||
matrix_to = StrConcat(2, "https://matrix.to/#/", common_id);
|
||||
Free(common_id);
|
||||
|
||||
return matrix_to;
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
|
||||
#include <Glob.h>
|
||||
#include <AS.h>
|
||||
|
||||
void
|
||||
ParseeGlobalUnban(ParseeData *data, char *glob)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
if (!data || !glob)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref = DbLock(data->db, 1, "global_bans");
|
||||
if (!ref)
|
||||
{
|
||||
ref = DbCreate(data->db, 1, "global_bans");
|
||||
}
|
||||
|
||||
j = DbJson(ref);
|
||||
|
||||
JsonValueFree(HashMapDelete(j, glob));
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
void
|
||||
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j, *obj;
|
||||
if (!data || !glob)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ref = DbLock(data->db, 1, "global_bans");
|
||||
if (!ref)
|
||||
{
|
||||
ref = DbCreate(data->db, 1, "global_bans");
|
||||
}
|
||||
|
||||
j = DbJson(ref);
|
||||
|
||||
obj = HashMapCreate();
|
||||
if (reason)
|
||||
{
|
||||
HashMapSet(obj, "reason", JsonValueString(reason));
|
||||
}
|
||||
HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis()));
|
||||
JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj)));
|
||||
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
bool
|
||||
ParseeManageBan(ParseeData *data, char *user, char *room)
|
||||
{
|
||||
DbRef *ref;
|
||||
HashMap *j;
|
||||
char *key;
|
||||
JsonValue *val;
|
||||
bool banned = false , matches = false;
|
||||
if (!data || !user)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "global_bans");
|
||||
j = DbJson(ref);
|
||||
while (HashMapIterate(j, &key, (void **) &val))
|
||||
{
|
||||
HashMap *obj = JsonValueAsObject(val);
|
||||
if (matches)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (GlobMatches(key, user))
|
||||
{
|
||||
banned = true;
|
||||
matches = true;
|
||||
if (room)
|
||||
{
|
||||
/* TODO: Use the object to set the reason */
|
||||
ASBan(data->config, room, user);
|
||||
(void) obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
return banned;
|
||||
}
|
||||
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
#include <Unistring.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
int
|
||||
ParseeFindDatastart(char *data)
|
||||
{
|
||||
char *startline;
|
||||
bool found = false;
|
||||
if (!data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
startline = data;
|
||||
while (startline)
|
||||
{
|
||||
char *endline = strchr(startline, '\n');
|
||||
|
||||
if (*startline != '>')
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
startline = endline ? endline + 1 : NULL;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) (startline - data);
|
||||
}
|
||||
int
|
||||
ParseeFindDatastartU(char *data)
|
||||
{
|
||||
Unistr *str;
|
||||
size_t ret;
|
||||
if (!data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
str = UnistrCreate(data);
|
||||
ret = UnistrGetOffset(str, (uint32_t) '>');
|
||||
UnistrFree(str);
|
||||
|
||||
return (int) ret;
|
||||
}
|
||||
|
||||
char *
|
||||
ParseeStringifyDate(uint64_t millis)
|
||||
{
|
||||
uint64_t rest = millis;
|
||||
uint64_t hours, minutes, seconds;
|
||||
char *hs, *ms, *ss, *out;
|
||||
|
||||
hours = rest / (1 HOURS);
|
||||
rest = rest % (1 HOURS);
|
||||
|
||||
minutes = rest / (1 MINUTES);
|
||||
rest = rest % (1 MINUTES);
|
||||
|
||||
seconds = rest / (1 SECONDS);
|
||||
|
||||
hs = StrInt(hours);
|
||||
ms = StrInt(minutes);
|
||||
ss = StrInt(seconds);
|
||||
|
||||
out = StrConcat(8,
|
||||
hours ? hs : "",
|
||||
hours ? " hours" : "",
|
||||
(hours && minutes) ? ", " : "",
|
||||
|
||||
minutes ? ms : "",
|
||||
minutes ? " minutes" : "",
|
||||
(minutes && seconds) ? ", " : "",
|
||||
|
||||
seconds ? ss : "",
|
||||
seconds ? " seconds" : ""
|
||||
);
|
||||
Free(hs);
|
||||
Free(ms);
|
||||
Free(ss);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#include <Parsee.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
|
||||
bool
|
||||
ParseeIsCompatible(char *ver1, char *ver2)
|
||||
{
|
||||
char *major1 = NULL;
|
||||
char *major2 = NULL;
|
||||
char *tmp;
|
||||
if (!ver1 || !ver2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check if one of them is a "flipped"(joke) version. If so,
|
||||
* then the user should definitely NOT try funny things with
|
||||
* their data. */
|
||||
if (*ver1 == '-' || *ver2 == '-')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#define GetMajor(v) do \
|
||||
{ \
|
||||
while (*ver##v != '.') \
|
||||
{ \
|
||||
char cb[2]; \
|
||||
cb[0] = *ver##v; \
|
||||
cb[1] = '\0'; \
|
||||
tmp = major##v; \
|
||||
major##v = StrConcat(2, major##v, cb); \
|
||||
Free(tmp); \
|
||||
ver##v++; \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
GetMajor(1);
|
||||
GetMajor(2);
|
||||
|
||||
if (!StrEquals(major1, major2))
|
||||
{
|
||||
Free(major1);
|
||||
Free(major2);
|
||||
return false;
|
||||
}
|
||||
|
||||
Free(major1);
|
||||
Free(major2);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2,80 +2,37 @@
|
|||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <Matrix.h>
|
||||
#include <Parsee.h>
|
||||
#include <AS.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static HttpClientContext *
|
||||
TryDownload(ParseeData *data, char *server, char *identi)
|
||||
{
|
||||
HttpClientContext *cctx;
|
||||
char *path;
|
||||
server = HttpUrlEncode(server);
|
||||
identi = HttpUrlEncode(identi);
|
||||
|
||||
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(data->config, cctx);
|
||||
Free(path);
|
||||
|
||||
HttpRequestSendHeaders(cctx);
|
||||
if (HttpRequestSend(cctx) != HTTP_OK)
|
||||
{
|
||||
Log(LOG_WARNING, "Failing back.");
|
||||
HttpClientContextFree(cctx);
|
||||
path = StrConcat(4, "/_matrix/client/v1/media/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(data->config, cctx);
|
||||
Free(path);
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
}
|
||||
|
||||
Free(server);
|
||||
Free(identi);
|
||||
return cctx;
|
||||
}
|
||||
|
||||
RouteHead(RouteMedia, arr, argp)
|
||||
{
|
||||
ParseeHttpArg *args = argp;
|
||||
HttpClientContext *cctx;
|
||||
HashMap *reqh, *params;
|
||||
HashMap *reqh;
|
||||
char *server = ArrayGet(arr, 0);
|
||||
char *identi = ArrayGet(arr, 1);
|
||||
char *key, *val;
|
||||
char *hmac, *chkmak = NULL;
|
||||
char *path, *key, *val;
|
||||
|
||||
params = HttpRequestParams(args->ctx);
|
||||
hmac = HashMapGet(params, "hmac");
|
||||
|
||||
/* TODO: Make it check the DB for its validicity. "Purging" would be
|
||||
* useful, alongside checking if someone isn't just a little idiotic. */
|
||||
/* TODO: Make it check the DB for its validicity. "Purging" would be useful.
|
||||
*/
|
||||
if (!server || !identi)
|
||||
{
|
||||
char *concat = StrConcat(3, server, "/", identi);
|
||||
chkmak = ParseeHMACS(args->data->id, concat);
|
||||
Free(concat);
|
||||
}
|
||||
if (!server || !identi || !hmac || !StrEquals(hmac, chkmak))
|
||||
{
|
||||
char *err =
|
||||
hmac && StrEquals(hmac, chkmak) ?
|
||||
"No server/identifier/HMAC code" :
|
||||
"Hah! You _dirty_ little liar! Try a little harder!";
|
||||
Free(chkmak);
|
||||
HttpResponseStatus(args->ctx, HTTP_BAD_REQUEST);
|
||||
return MatrixCreateError("M_NOT_YET_UPLOADED", err);
|
||||
return MatrixCreateError("M_NOT_YET_UPLOADED", "No server/identifier");
|
||||
}
|
||||
Free(chkmak);
|
||||
|
||||
/* Proxy the media through an authenticated endpoint if the HMAC
|
||||
* is valid. */
|
||||
cctx = TryDownload(args->data, server, identi);
|
||||
server = HttpUrlEncode(server);
|
||||
identi = HttpUrlEncode(identi);
|
||||
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
||||
cctx = ParseeCreateRequest(args->data->config, HTTP_GET, path);
|
||||
ASAuthenticateRequest(args->data->config, cctx);
|
||||
Free(path);
|
||||
|
||||
HttpRequestSendHeaders(cctx);
|
||||
HttpRequestSend(cctx);
|
||||
reqh = HttpResponseHeaders(cctx);
|
||||
while (HashMapIterate(reqh, &key, (void **) &val))
|
||||
{
|
||||
|
|
@ -88,6 +45,8 @@ RouteHead(RouteMedia, arr, argp)
|
|||
}
|
||||
|
||||
HttpClientContextFree(cctx);
|
||||
Free(server);
|
||||
Free(identi);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,13 @@ RouteHead(RoutePing, arr, argp)
|
|||
);
|
||||
goto end;
|
||||
}
|
||||
Log(LOG_INFO, "Pong!");
|
||||
|
||||
RequestJSON();
|
||||
|
||||
/* TODO: Load ping info */
|
||||
response = HashMapCreate();
|
||||
end:
|
||||
(void) arr;
|
||||
JsonFree(request);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,26 +41,13 @@ GetRandomQuote(void)
|
|||
"We truly live in a " CODE "...",
|
||||
"You truly lack the Desire Drive for this!",
|
||||
"Eeh? Bifrost mode? Only bad servers use Bifrost mode!",
|
||||
"'Wow parsee can save the world' - said a wise guy",
|
||||
"As small as a dwarf, and can run on your pie!",
|
||||
"It's all wicked unsafe, memory slow 🚀🚀🚀🚀",
|
||||
|
||||
"XMPP kinda sucks.",
|
||||
"Matrix kinda sucks.",
|
||||
|
||||
"Throw jabs!",
|
||||
"'Won't you hold my <presence/> safe?' - Kanako",
|
||||
|
||||
NAME ": the federated world's little little kobashi",
|
||||
|
||||
"Go take a look at your stanzas!",
|
||||
"Go take a look at your objects!",
|
||||
|
||||
"DEC Alpha AXP-Certified!",
|
||||
|
||||
"this is the moment parsee started parsing or smth idk"
|
||||
" - another wise person",
|
||||
"Ah, merde, mon TGV est en retard de 53 minutes !"
|
||||
"Throw jabs!"
|
||||
};
|
||||
const size_t count = sizeof(quotes)/sizeof(*quotes);
|
||||
|
||||
|
|
@ -82,7 +69,6 @@ RouteHead(RouteRoot, arr, argp)
|
|||
{
|
||||
P("<title>%s Lander</title>", NAME);
|
||||
P("<meta charset='UTF-8'/>");
|
||||
P("<link rel='icon' href='data:image/png;base64,%s'/>", media_parsee_logo);
|
||||
P("<style>");
|
||||
{
|
||||
P("html {");
|
||||
|
|
@ -90,17 +76,6 @@ RouteHead(RouteRoot, arr, argp)
|
|||
P("color: #eee;");
|
||||
P("font-family: sans-serif;");
|
||||
P("}");
|
||||
P("#ascii {");
|
||||
P("text-align: center;");
|
||||
P("color: #be1337;");
|
||||
P("}");
|
||||
P("#ascii pre {");
|
||||
P("display: inline-block;");
|
||||
P("text-align: left;");
|
||||
P("}");
|
||||
P("img {");
|
||||
P("image-rendering: pixelated;");
|
||||
P("}");
|
||||
P("blockquote {");
|
||||
P("border-left: 2px solid #ccc;");
|
||||
P("margin: 1.5em 10px;");
|
||||
|
|
@ -123,25 +98,15 @@ 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>");
|
||||
P("<center><h1>");
|
||||
P("Your %s is running, all with that %s!", NAME, CODE);
|
||||
P("</h1></center>");
|
||||
P("<hr/>");
|
||||
P("<blockquote><i>");
|
||||
{
|
||||
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>");
|
||||
{
|
||||
|
|
@ -179,7 +144,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>.");
|
||||
}
|
||||
|
|
@ -234,7 +199,7 @@ RouteHead(RouteRoot, arr, argp)
|
|||
P("Some clicky links relating to %s:", NAME);
|
||||
P("<ul>");
|
||||
{
|
||||
const char *fedi = "https://tengu.kappach.at/parsee";
|
||||
const char *fedi = "https://ak.ari.lt/parsee";
|
||||
const char *icon = "https://kappach.at/parsee.gif";
|
||||
P("<li><a href='%s'>Repository</a></li>", REPOSITORY);
|
||||
P("<li><a href='%s'>Fediverse</a></li>", fedi);
|
||||
|
|
@ -254,6 +219,5 @@ RouteHead(RouteRoot, arr, argp)
|
|||
P("</html>");
|
||||
#undef P
|
||||
|
||||
(void) arr;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ RouteHead(RouteTxns, arr, argp)
|
|||
|
||||
RequestJSON();
|
||||
|
||||
/* TODO: Do a thing with these. */
|
||||
events = JsonValueAsArray(HashMapGet(request, "events"));
|
||||
for (i = 0; i < ArraySize(events); i++)
|
||||
{
|
||||
|
|
@ -42,7 +43,6 @@ RouteHead(RouteTxns, arr, argp)
|
|||
|
||||
response = HashMapCreate();
|
||||
end:
|
||||
(void) arr;
|
||||
JsonFree(request);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,8 +67,7 @@ RouteHead(RouteRoomAck, arr, argp)
|
|||
}
|
||||
|
||||
muc = ParseeDecodeLocalMUC(args->data->config, room);
|
||||
if (ParseeManageBan(args->data, muc, NULL) ||
|
||||
ParseeIsMUCWhitelisted(args->data, muc))
|
||||
if (ParseeManageBan(args->data, muc, NULL))
|
||||
{
|
||||
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
||||
response = MatrixCreateError(
|
||||
|
|
@ -131,7 +130,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, NULL, -1, false);
|
||||
XMPPJoinMUC(args->data->jabber, "parsee", rev);
|
||||
|
||||
Free(rev);
|
||||
}
|
||||
|
|
|
|||
19
src/Signal.c
19
src/Signal.c
|
|
@ -6,35 +6,32 @@
|
|||
|
||||
#include <XMPP.h>
|
||||
|
||||
static ParseeData *data;
|
||||
static HttpServer *server = NULL;
|
||||
static pthread_t xmpp_thr;
|
||||
static XMPPComponent *jabber = NULL;
|
||||
|
||||
static void
|
||||
SignalHandler(int signal)
|
||||
{
|
||||
if (data->server && (signal == SIGTERM || signal == SIGINT))
|
||||
if (server && (signal == SIGTERM || signal == SIGINT))
|
||||
{
|
||||
Log(LOG_INFO, "Killing thread...");
|
||||
|
||||
pthread_mutex_lock(&data->halt_lock);
|
||||
data->halted = true;
|
||||
pthread_mutex_unlock(&data->halt_lock);
|
||||
|
||||
XMPPFinishCompStream(data->jabber);
|
||||
XMPPFinishCompStream(jabber);
|
||||
pthread_join(xmpp_thr, NULL);
|
||||
Log(LOG_INFO, "Stopping server...");
|
||||
HttpServerStop(data->server);
|
||||
HttpServerStop(server);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ParseeInitialiseSignals(ParseeData *d, pthread_t xmpp)
|
||||
ParseeInitialiseSignals(HttpServer *s, pthread_t xmpp, XMPPComponent *j)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
data = d;
|
||||
server = s;
|
||||
xmpp_thr = xmpp;
|
||||
jabber = j;
|
||||
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_handler = SignalHandler;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ 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;
|
||||
|
|
@ -185,14 +184,14 @@ ExportStanza(StanzaBuilder *builder)
|
|||
builder->replying_to_sender &&
|
||||
builder->body)
|
||||
{
|
||||
int off = ParseeFindDatastartU(builder->body);
|
||||
int off = ParseeFindDatastart(builder->body);
|
||||
char *ostr = StrInt(off);
|
||||
XMLElement *reply = XMLCreateTag("reply");
|
||||
XMLElement *fallback = XMLCreateTag("fallback");
|
||||
XMLElement *fall_body = XMLCreateTag("body");
|
||||
|
||||
XMLAddAttr(reply, "to", builder->replying_to_sender);
|
||||
XMLAddAttr(reply, "id", builder->replying_to_stanza);
|
||||
XMLAddAttr(reply, "to", builder->replying_to_stanza);
|
||||
XMLAddAttr(reply, "id", builder->replying_to_sender);
|
||||
XMLAddAttr(reply, "xmlns", "urn:xmpp:reply:0");
|
||||
|
||||
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
||||
|
|
@ -228,21 +227,22 @@ ExportStanza(StanzaBuilder *builder)
|
|||
}
|
||||
|
||||
void
|
||||
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber, size_t max)
|
||||
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber)
|
||||
{
|
||||
XMLElement *elem;
|
||||
if (!builder || !jabber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!max)
|
||||
{
|
||||
max = 10000; /* XMPP recommended limit */
|
||||
}
|
||||
|
||||
elem = ExportStanza(builder);
|
||||
XMPPSendStanza(jabber, elem, max);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, elem);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
|
||||
XMLFreeElement(elem);
|
||||
return;
|
||||
}
|
||||
|
||||
StanzaBuilder *
|
||||
|
|
@ -258,6 +258,7 @@ 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");
|
||||
|
|
@ -284,6 +285,16 @@ 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,6 +7,7 @@
|
|||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
char **
|
||||
StrSplitLines(char *text)
|
||||
|
|
@ -116,7 +117,7 @@ StrFullRect(char **split)
|
|||
char
|
||||
StrGet(StringRect *rect, int line, int col)
|
||||
{
|
||||
size_t actual_line, actual_col;
|
||||
int actual_line, actual_col;
|
||||
char *linep;
|
||||
if (!rect || !rect->source_lines)
|
||||
{
|
||||
|
|
@ -149,7 +150,7 @@ StrGet(StringRect *rect, int line, int col)
|
|||
size_t
|
||||
StrViewChars(StringRect rect, int line)
|
||||
{
|
||||
size_t actual_line;
|
||||
int actual_line;
|
||||
char *linep;
|
||||
if (!rect.source_lines)
|
||||
{
|
||||
|
|
@ -173,7 +174,7 @@ StrViewChars(StringRect rect, int line)
|
|||
StringRect
|
||||
StrGetl(StringRect *rect, int line, bool extend)
|
||||
{
|
||||
size_t actual_line;
|
||||
int actual_line;
|
||||
StringRect ret;
|
||||
if (!rect->source_lines)
|
||||
{
|
||||
|
|
@ -203,7 +204,7 @@ StrGetl(StringRect *rect, int line, bool extend)
|
|||
StringRect
|
||||
StrShift(StringRect rect, int n)
|
||||
{
|
||||
size_t new = rect.start_char + n;
|
||||
int new = rect.start_char + n;
|
||||
if (new > rect.end_char)
|
||||
{
|
||||
new = rect.end_char;
|
||||
|
|
|
|||
|
|
@ -7,13 +7,125 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
typedef struct ReaderCookie {
|
||||
size_t length;
|
||||
size_t offset;
|
||||
char *buffer;
|
||||
} ReaderCookie;
|
||||
|
||||
#define Remaining() (cook->length - cook->offset)
|
||||
|
||||
static ssize_t
|
||||
ReadStreamReader(void *coop, void *to, size_t n)
|
||||
{
|
||||
ReaderCookie *cook = coop;
|
||||
size_t remaining;
|
||||
if (!cook)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
remaining = Remaining();
|
||||
if (n > remaining)
|
||||
{
|
||||
memcpy(to, cook->buffer + cook->offset, remaining);
|
||||
cook->offset = cook->length;
|
||||
return remaining;
|
||||
}
|
||||
|
||||
memcpy(to, cook->buffer + cook->offset, n);
|
||||
cook->offset += n;
|
||||
return n;
|
||||
}
|
||||
static ssize_t
|
||||
WriteStreamReader(void *coop, void *from, size_t n)
|
||||
{
|
||||
/* Writing to a stream reader is silly. */
|
||||
return 0;
|
||||
}
|
||||
static off_t
|
||||
SeekStreamReader(void *coop, off_t mag, int sgn)
|
||||
{
|
||||
ReaderCookie *cook = coop;
|
||||
if (!cook)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (sgn)
|
||||
{
|
||||
case SEEK_SET:
|
||||
if (mag > cook->length)
|
||||
{
|
||||
cook->offset = cook->length;
|
||||
return 0;
|
||||
}
|
||||
else if (mag < 0)
|
||||
{
|
||||
cook->offset = 0;
|
||||
return 0;
|
||||
}
|
||||
cook->offset = mag;
|
||||
return 0;
|
||||
case SEEK_CUR:
|
||||
cook->offset += mag;
|
||||
if (cook->offset > cook->length)
|
||||
{
|
||||
cook->offset = cook->length;
|
||||
}
|
||||
else if (cook->offset < 0)
|
||||
{
|
||||
cook->offset = 0;
|
||||
}
|
||||
return 0;
|
||||
case SEEK_END:
|
||||
cook->offset += cook->length + mag;
|
||||
if (cook->offset > cook->length)
|
||||
{
|
||||
cook->offset = cook->length;
|
||||
}
|
||||
else if (cook->offset < 0)
|
||||
{
|
||||
cook->offset = 0;
|
||||
}
|
||||
return 0;
|
||||
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
CloseStreamReader(void *coop)
|
||||
{
|
||||
/* Nothing to free as of now. */
|
||||
if (coop)
|
||||
{
|
||||
Free(coop);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const static IoFunctions Functions = {
|
||||
.read = ReadStreamReader,
|
||||
.seek = SeekStreamReader,
|
||||
.write = WriteStreamReader,
|
||||
.close = CloseStreamReader,
|
||||
};
|
||||
|
||||
Stream *
|
||||
StrStreamReaderN(char *buffer, int n)
|
||||
{
|
||||
if (!buffer || n < 0)
|
||||
Io *raw_io;
|
||||
ReaderCookie *cookie;
|
||||
if (!buffer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return StreamFile(fmemopen(buffer, n ? (size_t) n : strlen(buffer), "rb"));
|
||||
cookie = Malloc(sizeof(*cookie));
|
||||
cookie->buffer = buffer;
|
||||
cookie->length = n ? n : strlen(buffer);
|
||||
cookie->offset = 0;
|
||||
raw_io = IoCreate(cookie, Functions);
|
||||
return StreamIo(raw_io);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,9 +11,6 @@ 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
|
||||
|
|
@ -36,9 +33,6 @@ 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;
|
||||
}
|
||||
|
||||
|
|
@ -46,11 +40,10 @@ static int
|
|||
CloseStreamWriter(void *coop)
|
||||
{
|
||||
/* Nothing to free as of now. */
|
||||
(void) coop;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const IoFunctions Functions = {
|
||||
const static IoFunctions Functions = {
|
||||
.read = ReadStreamWriter,
|
||||
.seek = SeekStreamWriter,
|
||||
.write = WriteStreamWriter,
|
||||
|
|
|
|||
314
src/Unistr.c
314
src/Unistr.c
|
|
@ -1,314 +0,0 @@
|
|||
#include <Unistring.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
struct Unistr {
|
||||
size_t length;
|
||||
uint32_t *codepoints;
|
||||
};
|
||||
|
||||
void
|
||||
UnistrAddch(Unistr *unistr, uint32_t u)
|
||||
{
|
||||
if (!unistr || !u)
|
||||
{
|
||||
return;
|
||||
}
|
||||
unistr->length++;
|
||||
unistr->codepoints = Realloc(
|
||||
unistr->codepoints,
|
||||
unistr->length * sizeof(*unistr->codepoints)
|
||||
);
|
||||
|
||||
unistr->codepoints[unistr->length - 1] = u;
|
||||
}
|
||||
|
||||
static bool
|
||||
UTFIsN(char *off, size_t available, size_t n, uint8_t pc)
|
||||
{
|
||||
size_t i;
|
||||
uint8_t *offu = (uint8_t *) off;
|
||||
if (((available < n) || ((*offu >> (8-n-1)) != pc)) && (n >= 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < n - 1; i++)
|
||||
{
|
||||
if ((offu[i+1] >> 6) != 0x2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Unistr *
|
||||
UnistrCreate(char *src)
|
||||
{
|
||||
size_t len, i;
|
||||
Unistr *str;
|
||||
if (!src)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
len = strlen(src);
|
||||
str = Malloc(sizeof(*str));
|
||||
str->length = 0;
|
||||
str->codepoints = NULL;
|
||||
|
||||
/* We can't just set the length to {len}. */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
char byte = src[i];
|
||||
size_t available = len - i;
|
||||
if ((byte & 0x80) == 0)
|
||||
{
|
||||
/* This is a regular codepoint */
|
||||
UnistrAddch(str, byte & 0x7F);
|
||||
continue;
|
||||
}
|
||||
else if (UTFIsN(&src[i], available, 2, 0x06))
|
||||
{
|
||||
char a = src[i+0] & 0x1F;
|
||||
char b = src[i+1] & 0x3F;
|
||||
uint32_t u = (a << (6 * 1)) | b;
|
||||
|
||||
/* Overlongs are errors. */
|
||||
if (u < 0x0080 || u > 0x07FF)
|
||||
{
|
||||
UnistrFree(str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UnistrAddch(str, u);
|
||||
i += 2 - 1;
|
||||
continue;
|
||||
}
|
||||
else if (UTFIsN(&src[i], available, 3, 0x0E))
|
||||
{
|
||||
char a = src[i+0] & 0x0F;
|
||||
char b = src[i+1] & 0x3F;
|
||||
char c = src[i+2] & 0x3F;
|
||||
uint32_t u =
|
||||
(a << (6 * 2)) |
|
||||
(b << (6 * 1)) |
|
||||
(c << (6 * 0)) ;
|
||||
|
||||
/* Overlongs are errors. */
|
||||
if (u < 0x0800 || u > 0xFFFF)
|
||||
{
|
||||
UnistrFree(str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UnistrAddch(str, u);
|
||||
i += 3 - 1;
|
||||
continue;
|
||||
}
|
||||
else if (UTFIsN(&src[i], available, 4, 0x1E))
|
||||
{
|
||||
char a = src[i+0] & 0x07;
|
||||
char b = src[i+1] & 0x3F;
|
||||
char c = src[i+2] & 0x3F;
|
||||
char d = src[i+3] & 0x3F;
|
||||
uint32_t u =
|
||||
(a << (6 * 3)) |
|
||||
(b << (6 * 2)) |
|
||||
(c << (6 * 1)) |
|
||||
(d << (6 * 0)) ;
|
||||
|
||||
/* Overlongs are errors. */
|
||||
if (u < 0x10000 || u > 0x10FFFF)
|
||||
{
|
||||
UnistrFree(str);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UnistrAddch(str, u);
|
||||
i += 4 - 1;
|
||||
continue;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
void
|
||||
UnistrFree(Unistr *unistr)
|
||||
{
|
||||
if (!unistr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Free(unistr->codepoints);
|
||||
Free(unistr);
|
||||
}
|
||||
char *
|
||||
UnistrC(Unistr *unistr)
|
||||
{
|
||||
char *ret, *tmp, *utf;
|
||||
size_t i;
|
||||
if (!unistr)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = NULL;
|
||||
for (i = 0; i < unistr->length; i++)
|
||||
{
|
||||
uint32_t code = unistr->codepoints[i];
|
||||
utf = StrUtf8Encode(code);
|
||||
|
||||
tmp = ret;
|
||||
ret = StrConcat(2, ret, utf);
|
||||
Free(tmp);
|
||||
Free(utf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
size_t
|
||||
UnistrSize(Unistr *unistr)
|
||||
{
|
||||
return unistr ? unistr->length : 0;
|
||||
}
|
||||
uint32_t
|
||||
UnistrGetch(Unistr *unistr, size_t i)
|
||||
{
|
||||
if (!unistr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return i < unistr->length ? unistr->codepoints[i] : 0;
|
||||
}
|
||||
bool
|
||||
UnistrIsASCII(uint32_t u)
|
||||
{
|
||||
if (u == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return u < 0x7F;
|
||||
}
|
||||
bool
|
||||
UnistrIsBMP(uint32_t u)
|
||||
{
|
||||
if (u == 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return u <= 0xFFFF;
|
||||
}
|
||||
Unistr *
|
||||
UnistrFilter(Unistr *str, UnistrFilterFunc filter)
|
||||
{
|
||||
Unistr *unistr;
|
||||
size_t i;
|
||||
if (!str || !filter)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
unistr = UnistrCreate("");
|
||||
for (i = 0; i < UnistrSize(str); i++)
|
||||
{
|
||||
uint32_t code = UnistrGetch(str, i);
|
||||
if (!filter(code))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
UnistrAddch(unistr, code);
|
||||
}
|
||||
|
||||
return unistr;
|
||||
}
|
||||
|
||||
Unistr *
|
||||
UnistrConcat(size_t n, ...)
|
||||
{
|
||||
va_list list;
|
||||
size_t i;
|
||||
Unistr *ret = UnistrCreate("");
|
||||
|
||||
va_start(list, n);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
Unistr *to_concat = va_arg(list, Unistr *);
|
||||
size_t j;
|
||||
for (j = 0; j < UnistrSize(to_concat); j++)
|
||||
{
|
||||
UnistrAddch(ret, UnistrGetch(to_concat, j));
|
||||
}
|
||||
}
|
||||
|
||||
va_end(list);
|
||||
return ret;
|
||||
}
|
||||
size_t
|
||||
UnistrGetOffset(Unistr *str, uint32_t sep)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t prev = 0x0A;
|
||||
if (!str || !sep)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < str->length; i++)
|
||||
{
|
||||
uint32_t curr = str->codepoints[i];
|
||||
if (prev == 0x0A && curr != sep)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
prev = curr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
size_t
|
||||
UnistrGetUTFOffset(char *cstr, size_t unicode)
|
||||
{
|
||||
Unistr *tmp;
|
||||
size_t ret = 0;
|
||||
if (!cstr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmp = UnistrCreate(cstr);
|
||||
for (size_t i = 0; i < unicode && i < tmp->length; i++)
|
||||
{
|
||||
uint32_t codepoint = tmp->codepoints[i];
|
||||
if (codepoint >= 0x0000 && codepoint <= 0x007F)
|
||||
{
|
||||
ret += 1;
|
||||
}
|
||||
else if (codepoint >= 0x0080 && codepoint <= 0x07FF)
|
||||
{
|
||||
ret += 2;
|
||||
}
|
||||
else if (codepoint >= 0x0800 && codepoint <= 0xFFFF)
|
||||
{
|
||||
ret += 3;
|
||||
}
|
||||
else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF)
|
||||
{
|
||||
ret += 4;
|
||||
}
|
||||
}
|
||||
end:
|
||||
Free(tmp);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -80,14 +80,7 @@ DecodeQuote(StringRect rect, size_t *skip)
|
|||
ret = rect;
|
||||
ret.end_line--;
|
||||
|
||||
/* TODO: You can easily craft strings that conceal data(
|
||||
* > Please mind the whitespace
|
||||
* > hidden we're concealing data to Matrix
|
||||
* > star in Well, you can still stare at the body and XML
|
||||
* > four but that's for Nerds
|
||||
* > seasons See, Touhou reference!
|
||||
* concealing!) */
|
||||
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace((int) ch))
|
||||
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace(ch))
|
||||
{
|
||||
shift_by++;
|
||||
}
|
||||
|
|
@ -132,7 +125,7 @@ DecodeSpan(StringRect rect, char del, size_t *skip)
|
|||
{
|
||||
return StrFullRect(NULL);
|
||||
}
|
||||
if (!ret.source_lines && isspace((int) c))
|
||||
if (!ret.source_lines && isspace(c))
|
||||
{
|
||||
return StrFullRect(NULL);
|
||||
}
|
||||
|
|
@ -329,8 +322,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 +365,7 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
|||
XMLAddChild(head, XMLCreateText("_"));
|
||||
break;
|
||||
case XEP393_MONO:
|
||||
XMLAddChild(xmlparent, XMLCreateText("`"));
|
||||
XMLAddChild(head, XMLCreateText("`"));
|
||||
break;
|
||||
case XEP393_SRKE:
|
||||
XMLAddChild(head, XMLCreateText("~"));
|
||||
|
|
@ -399,37 +392,21 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
|||
char *
|
||||
XEP393ToXMLString(XEP393Element *xepd)
|
||||
{
|
||||
XMLElement *root, *act_root;
|
||||
XMLElement *child;
|
||||
XMLElement *root;
|
||||
|
||||
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);
|
||||
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);
|
||||
XMLEncode(writer, root);
|
||||
XMLFreeElement(root);
|
||||
StreamFlush(writer);
|
||||
StreamClose(writer);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,12 +31,6 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
|||
bool flag = false;
|
||||
switch (event->type)
|
||||
{
|
||||
case XML_ERROR:
|
||||
XMLFreeEvent(event);
|
||||
XMLFreeElement(ret);
|
||||
ArrayFree(stack);
|
||||
XMLFreeLexer(lexer);
|
||||
return NULL;
|
||||
case XML_LEXER_STARTELEM:
|
||||
/* Create a new element that will populated. */
|
||||
top = XMLCreateTag(event->element);
|
||||
|
|
@ -120,19 +114,9 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
|||
void
|
||||
XMLEncodeString(Stream *stream, char *data)
|
||||
{
|
||||
size_t i, len;
|
||||
size_t 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++)
|
||||
for (i = 0; i < strlen(data); i++)
|
||||
{
|
||||
char c = data[i];
|
||||
if (c == '<')
|
||||
|
|
@ -161,9 +145,6 @@ 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,7 +58,6 @@ static char * XMLPopElement(XMLexer *lexer);
|
|||
static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs);
|
||||
static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs);
|
||||
static XMLEvent * XMLCreateRelax(XMLexer *lexer);
|
||||
static XMLEvent * XMLCreateError(XMLexer *lexer);
|
||||
static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end);
|
||||
static XMLEvent * XMLCreateData(XMLexer *lexer);
|
||||
|
||||
|
|
@ -199,9 +198,7 @@ XMLCrank(XMLexer *lexer)
|
|||
else if (XMLookahead(lexer, "--", false))
|
||||
{
|
||||
/* Throw error */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case XML_STATE_PI:
|
||||
|
|
@ -218,9 +215,6 @@ XMLCrank(XMLexer *lexer)
|
|||
if (!attrname)
|
||||
{
|
||||
/* TODO: Throw error */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
XMLPushElement(lexer, attrname);
|
||||
|
||||
|
|
@ -247,10 +241,7 @@ XMLCrank(XMLexer *lexer)
|
|||
}
|
||||
else if (XMLookahead(lexer, "'", true))
|
||||
{
|
||||
//while (true); uh oh
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
while (true);
|
||||
}
|
||||
break;
|
||||
case XML_STATE_ATTRTAIL:
|
||||
|
|
@ -259,8 +250,6 @@ XMLCrank(XMLexer *lexer)
|
|||
if (!XMLookahead(lexer, ">", true))
|
||||
{
|
||||
/* TODO: Throw error. */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
lexer->state = XML_STATE_NONE;
|
||||
|
|
@ -269,8 +258,6 @@ XMLCrank(XMLexer *lexer)
|
|||
break;
|
||||
default:
|
||||
/* TODO */
|
||||
XMLFreeEvent(event);
|
||||
event = XMLCreateError(lexer);
|
||||
break;
|
||||
}
|
||||
/* TODO: Crank our XML parser. */
|
||||
|
|
@ -308,7 +295,7 @@ static bool
|
|||
XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
||||
{
|
||||
int *stack;
|
||||
size_t top, i, len;
|
||||
size_t top, i;
|
||||
ssize_t ntop;
|
||||
bool ret = false;
|
||||
if (!lexer || !str)
|
||||
|
|
@ -317,10 +304,9 @@ XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
|||
}
|
||||
|
||||
top = 0;
|
||||
len = strlen(str);
|
||||
stack = Malloc(len * sizeof(*stack));
|
||||
stack = Malloc(strlen(str) * sizeof(*stack));
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
for (i = 0; i < strlen(str); i++)
|
||||
{
|
||||
char c = str[i];
|
||||
int getc = XMLGetc(lexer);
|
||||
|
|
@ -350,8 +336,8 @@ seekback:
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define IsNamestart(c) ((c == ':') || isalpha((int) c) || (c == '_'))
|
||||
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit((int) c))
|
||||
#define IsNamestart(c) ((c == ':') || isalpha(c) || (c == '_'))
|
||||
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit(c))
|
||||
static char *
|
||||
XMLParseName(XMLexer *lexer)
|
||||
{
|
||||
|
|
@ -596,8 +582,6 @@ XMLCreateEnd(XMLexer *lexer, char *end)
|
|||
event->col = 0;
|
||||
event->offset = 0;
|
||||
|
||||
(void) lexer;
|
||||
|
||||
return event;
|
||||
}
|
||||
static XMLEvent *
|
||||
|
|
@ -706,26 +690,6 @@ XMLCreateData(XMLexer *lexer)
|
|||
return event;
|
||||
}
|
||||
static XMLEvent *
|
||||
XMLCreateError(XMLexer *lexer)
|
||||
{
|
||||
XMLEvent *event = Malloc(sizeof(*event));
|
||||
size_t elements = ArraySize(lexer->data.elements);
|
||||
|
||||
event->type = XML_ERROR;
|
||||
event->element = elements ?
|
||||
StrDuplicate(ArrayGet(lexer->data.elements, elements - 1)) :
|
||||
NULL;
|
||||
event->attrs = NULL;
|
||||
event->data = NULL;
|
||||
|
||||
/* TODO */
|
||||
event->line = 0;
|
||||
event->col = 0;
|
||||
event->offset = 0;
|
||||
|
||||
return event;
|
||||
}
|
||||
static XMLEvent *
|
||||
XMLCreateRelax(XMLexer *lexer)
|
||||
{
|
||||
XMLEvent *event = Malloc(sizeof(*event));
|
||||
|
|
|
|||
|
|
@ -11,15 +11,13 @@
|
|||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
/* The default component port Prosody uses. */
|
||||
#define DEFAULT_PROSODY_PORT 5347
|
||||
|
||||
XMPPComponent *
|
||||
XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||
XMPPInitialiseCompStream(char *host, int port)
|
||||
{
|
||||
int sd = -1;
|
||||
struct addrinfo hints, *res, *res0;
|
||||
|
|
@ -28,26 +26,12 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
|||
Stream *stream;
|
||||
XMPPComponent *comp;
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = host;
|
||||
}
|
||||
|
||||
snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
error = getaddrinfo(addr, serv, &hints, &res0);
|
||||
if (error)
|
||||
{
|
||||
const char *error_str = gai_strerror(error);
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': %s", __func__,
|
||||
host, error_str
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
error = getaddrinfo(host, serv, &hints, &res0);
|
||||
|
||||
for (res = res0; res; res = res->ai_next)
|
||||
{
|
||||
|
|
@ -61,10 +45,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
|||
|
||||
if (connect(sd, res->ai_addr, res->ai_addrlen) < 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': %s", __func__,
|
||||
host, strerror(errno)
|
||||
);
|
||||
close(sd);
|
||||
sd = -1;
|
||||
continue;
|
||||
|
|
@ -75,10 +55,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
|||
|
||||
if (sd < 0)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': no socket available", __func__,
|
||||
host
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
freeaddrinfo(res0);
|
||||
|
|
@ -86,10 +62,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
|||
stream = StreamFd(sd);
|
||||
if (!stream)
|
||||
{
|
||||
Log(LOG_ERR,
|
||||
"%s: cannot connect to '%s': %s", __func__,
|
||||
host, "couldn't create a Cytoplasm stream"
|
||||
);
|
||||
close(sd);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -104,15 +76,19 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
|||
return comp;
|
||||
}
|
||||
|
||||
#include <Cytoplasm/Sha.h>
|
||||
static char *
|
||||
ComputeHandshake(char *shared, char *stream)
|
||||
{
|
||||
char *source;
|
||||
unsigned char *raw_sha;
|
||||
char *sha;
|
||||
|
||||
source = StrConcat(2, stream, shared);
|
||||
sha = ParseeSHA1(source);
|
||||
raw_sha = Sha1(source);
|
||||
sha = ShaToHex(raw_sha);
|
||||
|
||||
Free(raw_sha);
|
||||
Free(source);
|
||||
|
||||
return sha;
|
||||
|
|
@ -120,6 +96,7 @@ ComputeHandshake(char *shared, char *stream)
|
|||
bool
|
||||
XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||
{
|
||||
/* TODO */
|
||||
XMLexer *sax;
|
||||
XMLEvent *ev;
|
||||
char *stream_id, *handshake;
|
||||
|
|
@ -152,7 +129,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (!ev || ev->type != XML_LEXER_STARTELEM ||
|
||||
if (ev->type != XML_LEXER_STARTELEM ||
|
||||
!StrEquals(ev->element, "stream:stream"))
|
||||
{
|
||||
Log(LOG_ERR, "Excepted stream:stream element.");
|
||||
|
|
@ -163,7 +140,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
|||
stream_id = StrDuplicate(HashMapGet(ev->attrs, "id"));
|
||||
handshake = ComputeHandshake(shared, stream_id);
|
||||
|
||||
Log(LOG_DEBUG, "- sID='%s'", stream_id);
|
||||
Log(LOG_NOTICE, "- sID='%s'", stream_id);
|
||||
StreamPrintf(stream, "<handshake>%s</handshake>", handshake);
|
||||
StreamFlush(stream);
|
||||
XMLFreeEvent(ev);
|
||||
|
|
@ -177,54 +154,11 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (!ev || ev->type != XML_LEXER_ELEM ||
|
||||
if (ev->type != XML_LEXER_ELEM ||
|
||||
!StrEquals(ev->element, "handshake"))
|
||||
{
|
||||
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);
|
||||
|
|
@ -235,7 +169,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
|||
|
||||
ret = true;
|
||||
/* We can uhh, send stanzas, and receive them! */
|
||||
Log(LOG_DEBUG, "Communications to '%s' established.", as);
|
||||
Log(LOG_INFO, "Communications to '%s' established.", as);
|
||||
|
||||
XMLFreeEvent(ev);
|
||||
Free(stream_id);
|
||||
|
|
@ -265,43 +199,8 @@ XMPPEndCompStream(XMPPComponent *comp)
|
|||
{
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&comp->write_lock);
|
||||
StreamClose(comp->stream);
|
||||
comp->stream = NULL;
|
||||
pthread_mutex_unlock(&comp->write_lock);
|
||||
pthread_mutex_destroy(&comp->write_lock);
|
||||
StreamClose(comp->stream);
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
136
src/XMPP/MUC.c
136
src/XMPP/MUC.c
|
|
@ -8,8 +8,6 @@
|
|||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
#include "XMPPThread/internal.h"
|
||||
|
||||
bool
|
||||
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||
{
|
||||
|
|
@ -20,6 +18,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
|
||||
iq_query = XMLCreateTag("iq");
|
||||
query = XMLCreateTag("query");
|
||||
|
||||
|
|
@ -35,7 +35,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
|
||||
{
|
||||
XMLElement *identity;
|
||||
XMPPSendStanza(jabber, iq_query, 10000);
|
||||
XMLEncode(jabber->stream, iq_query);
|
||||
StreamFlush(jabber->stream);
|
||||
XMLFreeElement(iq_query);
|
||||
|
||||
/* Except an IQ reply. 10 seconds of timeout is pretty
|
||||
|
|
@ -44,8 +45,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");
|
||||
|
|
@ -56,11 +57,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
"conference"))
|
||||
{
|
||||
XMLFreeElement(iq_query);
|
||||
Log(LOG_DEBUG, "MUC INFO ERROR");
|
||||
Log(LOG_DEBUG,
|
||||
"identityp=%p category=%s", identity,
|
||||
identity ? HashMapGet(identity->attrs, "category") : NULL
|
||||
);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +73,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
|||
XMLFreeElement(iq_query);
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -118,6 +116,7 @@ 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);
|
||||
|
|
@ -161,123 +160,10 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
|
|||
}
|
||||
XMLAddChild(stanza, x);
|
||||
}
|
||||
XMPPSendStanza(jabber, stanza, 10000);
|
||||
XMLEncode(jabber->stream, stanza);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
|
||||
XMLFreeElement(stanza);
|
||||
Free(identifier);
|
||||
}
|
||||
bool
|
||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret)
|
||||
{
|
||||
XMLElement *presence, *x, *reply, *history, *photo;
|
||||
IQFeatures *features;
|
||||
char *from, *id, *stime = "3600";
|
||||
if (!comp || !fr || !muc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
presence = XMLCreateTag("presence");
|
||||
x = XMLCreateTag("x");
|
||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||
XMLAddAttr(presence, "to", muc);
|
||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
||||
history = XMLCreateTag("history");
|
||||
|
||||
if (time > 0)
|
||||
{
|
||||
stime = StrInt(time);
|
||||
}
|
||||
XMLAddAttr(history, "seconds", stime);
|
||||
if (time > 0)
|
||||
{
|
||||
Free(stime);
|
||||
stime = NULL;
|
||||
}
|
||||
XMLAddChild(x, history);
|
||||
|
||||
XMLAddChild(presence, x);
|
||||
features = LookupJIDFeatures(from);
|
||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
XMPPAnnotatePresence(presence, features);
|
||||
FreeIQFeatures(features);
|
||||
|
||||
if (hash)
|
||||
{
|
||||
x = XMLCreateTag("x");
|
||||
XMLAddAttr(x, "xmlns", "vcard-temp:x:update");
|
||||
photo = XMLCreateTag("photo");
|
||||
XMLAddChild(photo, XMLCreateText(hash));
|
||||
XMLAddChild(x, photo);
|
||||
XMLAddChild(presence, x);
|
||||
}
|
||||
|
||||
XMPPSendStanza(comp, presence, 10000);
|
||||
XMLFreeElement(presence);
|
||||
Free(from);
|
||||
|
||||
if (ret && (reply = ParseeAwaitStanza(id, 500)))
|
||||
{
|
||||
bool exit_code = true;
|
||||
|
||||
if (XMPPHasError(reply, "conflict"))
|
||||
{
|
||||
exit_code = false;
|
||||
}
|
||||
|
||||
XMLFreeElement(reply);
|
||||
Free(id);
|
||||
return exit_code;
|
||||
}
|
||||
Free(id);
|
||||
return true;
|
||||
}
|
||||
void
|
||||
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||
{
|
||||
XMLElement *presence;
|
||||
IQFeatures *features;
|
||||
char *from, *id;
|
||||
if (!comp || !fr || !muc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
presence = XMLCreateTag("presence");
|
||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||
XMLAddAttr(presence, "to", muc);
|
||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||
XMLAddAttr(presence, "type", "unavailable");
|
||||
|
||||
if (reason)
|
||||
{
|
||||
XMLElement *status = XMLCreateTag("status");
|
||||
XMLElement *string = XMLCreateText(reason);
|
||||
|
||||
XMLAddChild(status, string);
|
||||
XMLAddChild(presence, status);
|
||||
}
|
||||
|
||||
features = LookupJIDFeatures(from);
|
||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
XMPPAnnotatePresence(presence, features);
|
||||
FreeIQFeatures(features);
|
||||
|
||||
|
||||
XMPPSendStanza(comp, presence, 10000);
|
||||
|
||||
XMLFreeElement(presence);
|
||||
Free(from);
|
||||
Free(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,154 +0,0 @@
|
|||
#include <MUCServ.h>
|
||||
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MUCNS "http://jabber.org/protocol/muc"
|
||||
|
||||
struct MUCServer {
|
||||
pthread_mutex_t mut;
|
||||
|
||||
ParseeData *data;
|
||||
};
|
||||
|
||||
static char *
|
||||
GetMUCName(char *jid)
|
||||
{
|
||||
char *name, *at;
|
||||
size_t len;
|
||||
if (!jid || *jid != '#')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jid++;
|
||||
|
||||
at = strchr(jid, '@');
|
||||
if (!at)
|
||||
{
|
||||
return StrDuplicate(jid);
|
||||
}
|
||||
len = at - jid;
|
||||
name = Malloc(len + 1);
|
||||
memset(name, '\0', len + 1);
|
||||
memcpy(name, jid, len);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
MUCServer *
|
||||
CreateMUCServer(ParseeData *data)
|
||||
{
|
||||
MUCServer *server;
|
||||
if (!data)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
server = Malloc(sizeof(*server));
|
||||
pthread_mutex_init(&server->mut, NULL);
|
||||
server->data = data;
|
||||
return server;
|
||||
}
|
||||
|
||||
static bool
|
||||
MUCManagePresence(MUCServer *serv, XMLElement *stanza, char *from, char *to)
|
||||
{
|
||||
char *name;
|
||||
XMLElement *x = XMLookForTKV(stanza, "x", "xmlns", MUCNS);
|
||||
char *type = HashMapGet(stanza->attrs, "type");
|
||||
if (x && !type)
|
||||
{
|
||||
/* The user is trying to join the MUC */
|
||||
name = GetMUCName(to);
|
||||
Log(LOG_WARNING, "%s is trying to join MUC '%s'", from, name);
|
||||
|
||||
/* TODO: Check if the user should be joining. If so, make them.
|
||||
* Implementing MUCs is gonna be fun. */
|
||||
|
||||
/* TODO: Presence broadcast hell. */
|
||||
Free(name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
MUCManageMessage(MUCServer *serv, XMLElement *stanza, char *from, char *to)
|
||||
{
|
||||
Log(LOG_WARNING, "MUCSERV: got a message %s->%s", from, to);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ManageMUCStanza(MUCServer *serv, XMLElement *stanza)
|
||||
{
|
||||
char *stype, *from, *to;
|
||||
bool ret;
|
||||
if (!serv || !stanza)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
from = HashMapGet(stanza->attrs, "from");
|
||||
to = HashMapGet(stanza->attrs, "to");
|
||||
stype = stanza->name;
|
||||
|
||||
if (to && *to != '#')
|
||||
{
|
||||
/* We aren't interacting with a MUC at all. Don't do anything. */
|
||||
return false;
|
||||
}
|
||||
if (StrEquals(stype, "iq"))
|
||||
{
|
||||
/* TODO: Worry about IQ later */
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = false;
|
||||
pthread_mutex_lock(&serv->mut);
|
||||
if (StrEquals(stype, "presence"))
|
||||
{
|
||||
ret = MUCManagePresence(serv, stanza, from, to);
|
||||
}
|
||||
else if (StrEquals(stype, "message"))
|
||||
{
|
||||
ret = MUCManageMessage(serv, stanza, from, to);
|
||||
}
|
||||
/* TODO: Do stuff while locked */
|
||||
pthread_mutex_unlock(&serv->mut);
|
||||
|
||||
/* TODO: Verify the destination, and make sure we aren't doing
|
||||
* anything stupid(especially with discovery) */
|
||||
(void) ret;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
MUCServerExists(MUCServer *serv, char *muc)
|
||||
{
|
||||
if (!serv || !muc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
FreeMUCServer(MUCServer *serv)
|
||||
{
|
||||
if (!serv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&serv->mut);
|
||||
/* TODO */
|
||||
pthread_mutex_unlock(&serv->mut);
|
||||
pthread_mutex_destroy(&serv->mut);
|
||||
Free(serv);
|
||||
}
|
||||
|
|
@ -7,8 +7,6 @@
|
|||
#include <Parsee.h>
|
||||
#include <XML.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
void
|
||||
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
||||
|
|
@ -68,13 +66,103 @@ XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
|||
|
||||
}
|
||||
|
||||
XMPPSendStanza(comp, message, 10000);
|
||||
pthread_mutex_lock(&comp->write_lock);
|
||||
XMLEncode(comp->stream, message);
|
||||
StreamFlush(comp->stream);
|
||||
XMLFreeElement(message);
|
||||
|
||||
pthread_mutex_unlock(&comp->write_lock);
|
||||
|
||||
Free(from);
|
||||
Free(ident);
|
||||
}
|
||||
void
|
||||
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||
{
|
||||
XMLElement *presence;
|
||||
char *from, *id;
|
||||
if (!comp || !fr || !muc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&comp->write_lock);
|
||||
|
||||
presence = XMLCreateTag("presence");
|
||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||
XMLAddAttr(presence, "to", muc);
|
||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||
XMLAddAttr(presence, "type", "unavailable");
|
||||
|
||||
if (reason)
|
||||
{
|
||||
XMLElement *status = XMLCreateTag("status");
|
||||
XMLElement *string = XMLCreateText(reason);
|
||||
|
||||
XMLAddChild(status, string);
|
||||
XMLAddChild(presence, status);
|
||||
}
|
||||
|
||||
XMPPAnnotatePresence(presence);
|
||||
|
||||
XMLEncode(comp->stream, presence);
|
||||
StreamFlush(comp->stream);
|
||||
|
||||
XMLFreeElement(presence);
|
||||
Free(from);
|
||||
Free(id);
|
||||
|
||||
pthread_mutex_unlock(&comp->write_lock);
|
||||
}
|
||||
|
||||
bool
|
||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
|
||||
{
|
||||
XMLElement *presence, *x, *reply;
|
||||
char *from, *id;
|
||||
if (!comp || !fr || !muc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&comp->write_lock);
|
||||
|
||||
presence = XMLCreateTag("presence");
|
||||
x = XMLCreateTag("x");
|
||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||
XMLAddAttr(presence, "to", muc);
|
||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
||||
|
||||
XMLAddChild(presence, x);
|
||||
XMPPAnnotatePresence(presence);
|
||||
|
||||
XMLEncode(comp->stream, presence);
|
||||
StreamFlush(comp->stream);
|
||||
|
||||
XMLFreeElement(presence);
|
||||
Free(from);
|
||||
|
||||
pthread_mutex_unlock(&comp->write_lock);
|
||||
|
||||
/*if ((reply = ParseeAwaitStanza(id, 500)))
|
||||
{
|
||||
bool exit_code = true;
|
||||
|
||||
if (XMPPHasError(reply, "conflict"))
|
||||
{
|
||||
Log(LOG_WARNING, "UNIMPLEMENTED NAMING CONFLICT.");
|
||||
exit_code = false;
|
||||
}
|
||||
|
||||
XMLFreeElement(reply);
|
||||
Free(id);
|
||||
return exit_code;
|
||||
}*/
|
||||
(void) reply;
|
||||
Free(id);
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
XMPPIsParseeStanza(XMLElement *stanza)
|
||||
{
|
||||
|
|
@ -83,8 +171,6 @@ 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,31 +247,27 @@ XMPPGetReply(XMLElement *elem)
|
|||
|
||||
return HashMapGet(rep->attrs, "id");
|
||||
}
|
||||
ssize_t
|
||||
XMPPGetReplyOffset(XMLElement *elem)
|
||||
void
|
||||
XMPPAnnotatePresence(XMLElement *presence)
|
||||
{
|
||||
if (!elem)
|
||||
XMLElement *c;
|
||||
char *ver;
|
||||
if (!presence)
|
||||
{
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < ArraySize(elem->children); i++)
|
||||
{
|
||||
XMLElement *child = ArrayGet(elem->children, i);
|
||||
char *xmlns = HashMapGet(child->attrs, "xmlns");
|
||||
char *xfor = HashMapGet(child->attrs, "for");
|
||||
if (StrEquals(child->name, "fallback") &&
|
||||
StrEquals(xmlns, "urn:xmpp:feature-fallback:0") &&
|
||||
StrEquals(xfor, "urn:xmpp:reply:0"))
|
||||
{
|
||||
XMLElement *body = XMLookForUnique(child, "body");
|
||||
if (body && HashMapGet(body->attrs, "end"))
|
||||
{
|
||||
return strtol(HashMapGet(body->attrs, "end"), NULL, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
|
||||
ver = XMPPGenerateVer();
|
||||
c = XMLCreateTag("c");
|
||||
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
||||
XMLAddAttr(c, "hash", "sha-1");
|
||||
XMLAddAttr(c, "node", REPOSITORY);
|
||||
XMLAddAttr(c, "ver", ver);
|
||||
|
||||
Free(ver);
|
||||
XMLAddChild(presence, c);
|
||||
}
|
||||
|
||||
char *
|
||||
XMPPGetModeration(XMLElement *stanza)
|
||||
{
|
||||
|
|
@ -239,51 +321,35 @@ XMPPHasError(XMLElement *stanza, char *type)
|
|||
}
|
||||
|
||||
XMLElement *
|
||||
XMPPSendDisco(ParseeData *data, char *from, char *to)
|
||||
XMPPSendDisco(XMPPComponent *jabber, char *from, char *to)
|
||||
{
|
||||
XMPPComponent *jabber = data ? data->jabber : NULL;
|
||||
XMLElement *ret, *iq;
|
||||
char *identifier;
|
||||
|
||||
if (!data || !jabber || !from || !to)
|
||||
if (!jabber || !from || !to)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&data->oidl);
|
||||
if ((ret = HashMapGet(data->oid_servers, to)))
|
||||
{
|
||||
ret = XMLCopy(ret);
|
||||
pthread_mutex_unlock(&data->oidl);
|
||||
return ret;
|
||||
}
|
||||
pthread_mutex_unlock(&data->oidl);
|
||||
|
||||
iq = XMLCreateTag("iq");
|
||||
XMLAddAttr(iq, "type", "get");
|
||||
XMLAddAttr(iq, "from", from);
|
||||
XMLAddAttr(iq, "to", to);
|
||||
XMLAddAttr(iq, "id", (identifier = StrRandom(64)));
|
||||
XMLAddAttr(iq, "id", (identifier = StrRandom(69)));
|
||||
{
|
||||
XMLElement *query = XMLCreateTag("query");
|
||||
XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info");
|
||||
XMLAddChild(iq, query);
|
||||
}
|
||||
|
||||
XMPPSendStanza(jabber, iq, 10000);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
ret = ParseeAwaitStanza(identifier, 1.25 SECONDS);
|
||||
Free(identifier);
|
||||
if (ret)
|
||||
{
|
||||
/* TODO: On my own instance, disco replies are _really_ expensive.
|
||||
* About 120KB. This is sad. Really sad. */
|
||||
pthread_mutex_lock(&data->oidl);
|
||||
XMLFreeElement(HashMapSet(data->oid_servers, to, ret));
|
||||
ret = XMLCopy(ret);
|
||||
pthread_mutex_unlock(&data->oidl);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -34,17 +34,7 @@ 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)
|
||||
{
|
||||
|
|
@ -128,7 +118,6 @@ XMPPCreateManager(void *cookie)
|
|||
ret->commands = HashMapCreate();
|
||||
ret->sessions = HashMapCreate();
|
||||
ret->cookie = cookie;
|
||||
ret->filter = XMPPDefaultFilter;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -172,12 +161,12 @@ XMPPFreeManager(XMPPCommandManager *manager)
|
|||
Free(manager);
|
||||
}
|
||||
void
|
||||
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s)
|
||||
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
|
||||
{
|
||||
char *node_name;
|
||||
XMPPCommand *val;
|
||||
XMLElement *item;
|
||||
if (!m || !p || !jid || !s)
|
||||
if (!m || !p || !jid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -185,14 +174,11 @@ XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement
|
|||
pthread_mutex_lock(&m->lock);
|
||||
while (HashMapIterate(m->commands, &node_name, (void **) &val))
|
||||
{
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
|
@ -220,16 +206,6 @@ 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)
|
||||
{
|
||||
|
|
@ -266,7 +242,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
/* This is an execution. */
|
||||
cmd = HashMapGet(m->commands, node);
|
||||
|
||||
if (!cmd || !m->filter(m, node, stanza))
|
||||
if (!cmd)
|
||||
{
|
||||
/* TODO: Set an error note */
|
||||
goto end;
|
||||
|
|
@ -298,7 +274,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
x = XMPPFormifyCommand(m, cmd, from);
|
||||
XMLAddChild(command_xml, x);
|
||||
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
goto end;
|
||||
|
|
@ -326,7 +305,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
XMPPExecuteCommand(m, cmd, from, command_xml, NULL);
|
||||
XMLAddChild(iq, command_xml);
|
||||
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
InvalidateSession(m, session_id);
|
||||
|
|
@ -366,7 +348,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
|||
XMPPExecuteCommand(m, cmd, from, command_xml, x_form);
|
||||
XMLAddChild(iq, command_xml);
|
||||
|
||||
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
|
||||
pthread_mutex_lock(&jabber->write_lock);
|
||||
XMLEncode(jabber->stream, iq);
|
||||
StreamFlush(jabber->stream);
|
||||
pthread_mutex_unlock(&jabber->write_lock);
|
||||
XMLFreeElement(iq);
|
||||
|
||||
InvalidateSession(m, session_given);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement
|
|||
|
||||
ref = DbLock(data->db, 1, "admins");
|
||||
admins = GrabArray(DbJson(ref), 1, "admins");
|
||||
|
||||
ArrayAdd(admins, JsonValueString(glob));
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,19 @@ 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");
|
||||
|
||||
|
|
@ -50,7 +58,4 @@ AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
|||
DbUnlock(data->db, ref);
|
||||
}
|
||||
XMLAddChild(out, x);
|
||||
|
||||
(void) form;
|
||||
(void) from;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,4 @@ 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
|||
DbRef *admins;
|
||||
Array *admin_list;
|
||||
size_t i;
|
||||
XMPPOption *admin_opt = NULL;
|
||||
XMPPOption *admin_opt;
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
||||
if (!ParseeIsAdmin(data, trimmed))
|
||||
|
|
@ -104,17 +104,14 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
|||
}
|
||||
Free(trimmed);
|
||||
|
||||
admin_opt = XMPPCreateList(true, false, "glob", "[NVM!]");
|
||||
|
||||
admins = DbLock(data->db, 1, "admins");
|
||||
admin_list = GrabArray(DbJson(admins), 1, "admins");
|
||||
for (i = 0; i < ArraySize(admin_list); i++)
|
||||
{
|
||||
char *glob = JsonValueAsString(ArrayGet(admin_list, i));
|
||||
|
||||
if (!admin_opt)
|
||||
{
|
||||
admin_opt = XMPPCreateList(true, false, "glob", glob);
|
||||
continue;
|
||||
}
|
||||
XMPPAddListOption(admin_opt, glob);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
#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;
|
||||
}
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#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,6 +87,4 @@ NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
|
|||
}
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
|
||||
(void) form;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
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;
|
||||
|
|
@ -25,6 +26,13 @@ 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");
|
||||
|
|
@ -67,7 +75,4 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
|||
EndItem();
|
||||
}
|
||||
XMLAddChild(out, x);
|
||||
|
||||
(void) form;
|
||||
(void) m;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,142 +0,0 @@
|
|||
#include <Cytoplasm/HashMap.h>
|
||||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Json.h>
|
||||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <XMPPFormTool.h>
|
||||
#include <XMPPCommand.h>
|
||||
#include <Parsee.h>
|
||||
#include <XMPP.h>
|
||||
#include <XML.h>
|
||||
|
||||
void
|
||||
ClearWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
|
||||
if (!ParseeIsAdmin(data, trimmed))
|
||||
{
|
||||
SetNote("error", "User is not authorised to execute command.");
|
||||
|
||||
Free(trimmed);
|
||||
return;
|
||||
}
|
||||
Free(trimmed);
|
||||
|
||||
if (!DbDelete(data->db, 1, "whitelist"))
|
||||
{
|
||||
SetNote("error", "Parsee whitelist was non-existent or could not be removed.");
|
||||
return;
|
||||
}
|
||||
/* TODO: Cleanup old sessions? */
|
||||
SetNote("info", "Parsee whitelist was removed.");
|
||||
|
||||
(void) form;
|
||||
}
|
||||
void
|
||||
AddWhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
char *entity = NULL;
|
||||
DbRef *ref;
|
||||
|
||||
GetFieldValue(entity, "entity", form);
|
||||
|
||||
if (!ParseeIsAdmin(data, trimmed))
|
||||
{
|
||||
SetNote("error", "User is not authorised to execute command.");
|
||||
|
||||
Free(trimmed);
|
||||
return;
|
||||
}
|
||||
if (!entity)
|
||||
{
|
||||
SetNote("error", "No entity found.");
|
||||
Free(trimmed);
|
||||
return;
|
||||
}
|
||||
|
||||
Free(trimmed);
|
||||
|
||||
ref = DbLock(data->db, 1, "whitelist");
|
||||
if (!ref)
|
||||
{
|
||||
ref = DbCreate(data->db, 1, "whitelist");
|
||||
}
|
||||
if (!ref)
|
||||
{
|
||||
SetNote("error", "Couldn't get a database entry. You're cooked.");
|
||||
return;
|
||||
}
|
||||
JsonValueFree(HashMapSet(
|
||||
DbJson(ref),
|
||||
entity, JsonValueObject(HashMapCreate())
|
||||
));
|
||||
DbUnlock(data->db, ref);
|
||||
|
||||
SetNote("info", "Server successfully whitelisted.");
|
||||
}
|
||||
void
|
||||
WhitelistCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||
{
|
||||
ParseeData *data = XMPPGetManagerCookie(m);
|
||||
char *trimmed = ParseeTrimJID(from);
|
||||
XMLElement *x;
|
||||
XMLElement *title;
|
||||
XMLElement *reported, *item, *field, *value, *txt;
|
||||
|
||||
if (!ParseeIsAdmin(data, trimmed))
|
||||
{
|
||||
SetNote("error", "User is not authorised to execute command.");
|
||||
|
||||
Free(trimmed);
|
||||
return;
|
||||
}
|
||||
|
||||
x = XMLCreateTag("x");
|
||||
XMLAddAttr(x, "xmlns", "jabber:x:data");
|
||||
title = XMLCreateTag("title");
|
||||
XMLAddChild(x, title);
|
||||
XMLAddChild(out, x);
|
||||
|
||||
Free(trimmed);
|
||||
|
||||
SetTitle(x, NAME " chat whitelist");
|
||||
|
||||
XMLAddAttr(x, "type", "result");
|
||||
{
|
||||
DbRef *ref = DbLock(data->db, 1, "whitelist");
|
||||
HashMap *obj;
|
||||
char *serv;
|
||||
JsonValue *obj_val;
|
||||
reported = XMLCreateTag("reported");
|
||||
XMLAddChild(x, reported);
|
||||
|
||||
if (!ref)
|
||||
{
|
||||
ref = DbCreate(data->db, 1, "global_bans");
|
||||
}
|
||||
|
||||
obj = DbJson(ref);
|
||||
|
||||
/* Report */
|
||||
Report("server", "Allowed servers");
|
||||
|
||||
/* Set */
|
||||
while (HashMapIterate(obj, &serv, (void **) &obj_val))
|
||||
{
|
||||
BeginItem();
|
||||
SetField("server", serv);
|
||||
EndItem();
|
||||
|
||||
(void) obj_val;
|
||||
}
|
||||
DbUnlock(data->db, ref);
|
||||
}
|
||||
|
||||
(void) form;
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
#include <Cytoplasm/Memory.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
|
||||
#include <Parsee.h>
|
||||
|
||||
|
|
@ -118,12 +119,13 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: Cache this information. */
|
||||
bool
|
||||
ServerHasXEP421(ParseeData *data, char *from)
|
||||
{
|
||||
char *server = NULL, *postserv, *parsee;
|
||||
char *server = NULL, *parsee;
|
||||
XMLElement *disco;
|
||||
bool ret = false;
|
||||
bool ret;
|
||||
if (!data || !from)
|
||||
{
|
||||
return false;
|
||||
|
|
@ -138,16 +140,15 @@ ServerHasXEP421(ParseeData *data, char *from)
|
|||
{
|
||||
server++;
|
||||
}
|
||||
|
||||
server = StrDuplicate(server);
|
||||
postserv = server ? strchr(server, '/') : NULL;
|
||||
if (postserv)
|
||||
|
||||
if (strchr(server, '/'))
|
||||
{
|
||||
*postserv = '\0';
|
||||
*(strchr(server, '/')) = '\0';
|
||||
}
|
||||
|
||||
parsee = ParseeJID(data);
|
||||
disco = XMPPSendDisco(data, parsee, server);
|
||||
disco = XMPPSendDisco(data->jabber, parsee, server);
|
||||
|
||||
ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0");
|
||||
|
||||
|
|
@ -162,9 +163,10 @@ ServerHasXEP421(ParseeData *data, char *from)
|
|||
* into a SHA-256 value
|
||||
* > "The recipient MUST consider the occupant identifier to be an opaque
|
||||
* > string.". */
|
||||
char *
|
||||
static char *
|
||||
ScrambleOID(ParseeData *data, char *opaque_oid)
|
||||
{
|
||||
unsigned char *raw;
|
||||
char *sha, *mxid;
|
||||
const ParseeConfig *c = data->config;
|
||||
if (!opaque_oid)
|
||||
|
|
@ -173,7 +175,9 @@ ScrambleOID(ParseeData *data, char *opaque_oid)
|
|||
}
|
||||
|
||||
/* Turns this into a 128-byte long, Matrix-safe value. */
|
||||
sha = ParseeSHA256(opaque_oid);
|
||||
raw = Sha256(opaque_oid);
|
||||
sha = ShaToHex(raw);
|
||||
Free(raw);
|
||||
|
||||
/* TODO: Either mark this specially, or drop Parsee JID flags
|
||||
* altogether before any 1.0.0 */
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include <Cytoplasm/Util.h>
|
||||
#include <Cytoplasm/Str.h>
|
||||
#include <Cytoplasm/Sha.h>
|
||||
#include <Cytoplasm/Log.h>
|
||||
|
||||
#include <StringStream.h>
|
||||
#include <XMPPCommand.h>
|
||||
|
|
@ -13,86 +12,33 @@
|
|||
|
||||
#include "XMPPThread/internal.h"
|
||||
|
||||
IQFeatures *
|
||||
CreateIQFeatures(void)
|
||||
{
|
||||
IQFeatures *ret = Malloc(sizeof(*ret));
|
||||
|
||||
ret->identity = ArrayCreate();
|
||||
ret->adverts = ArrayCreate();
|
||||
|
||||
return ret;
|
||||
}
|
||||
void
|
||||
FreeIQFeatures(IQFeatures *features)
|
||||
{
|
||||
size_t i;
|
||||
if (!features)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||
{
|
||||
Free(ArrayGet(features->adverts, i));
|
||||
}
|
||||
ArrayFree(features->adverts);
|
||||
|
||||
for (i = 0; i < ArraySize(features->identity); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||
|
||||
Free(identity->category);
|
||||
Free(identity->type);
|
||||
Free(identity->lang);
|
||||
Free(identity->name);
|
||||
|
||||
Free(identity);
|
||||
}
|
||||
ArrayFree(features->identity);
|
||||
|
||||
Free(features);
|
||||
}
|
||||
|
||||
void
|
||||
AdvertiseIQFeature(IQFeatures *f, char *feature)
|
||||
{
|
||||
if (!f || !feature)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayAdd(f->adverts, StrDuplicate(feature));
|
||||
}
|
||||
void
|
||||
AddIQIdentity(IQFeatures *f, char *cat, char *lang, char *type, char *name)
|
||||
{
|
||||
XMPPIdentity *identity;
|
||||
if (!f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
identity = Malloc(sizeof(*identity));
|
||||
identity->category = StrDuplicate(cat);
|
||||
identity->type = StrDuplicate(type);
|
||||
identity->lang = StrDuplicate(lang);
|
||||
identity->name = StrDuplicate(name);
|
||||
ArrayAdd(f->identity, identity);
|
||||
}
|
||||
/* Generates a SHA-256 hash of the ver field. */
|
||||
char *
|
||||
XMPPGenerateVer(IQFeatures *features)
|
||||
XMPPGenerateVer(void)
|
||||
{
|
||||
char *S = NULL;
|
||||
unsigned char *Sha = NULL;
|
||||
Array *identities = ArrayCreate();
|
||||
Array *features = ArrayCreate();
|
||||
size_t i;
|
||||
|
||||
/* Initialise identity table, to be sorted */
|
||||
ArraySort(features->identity, IdentitySort);
|
||||
for (i = 0; i < ArraySize(features->identity); i++)
|
||||
#define IdentitySimple(cat, Type, Name) { \
|
||||
XMPPIdentity *id = Malloc(sizeof(*id)); \
|
||||
id->category = cat; \
|
||||
id->lang = NULL; \
|
||||
id->type = Type; \
|
||||
id->name = Name; \
|
||||
ArrayAdd(identities, id); }
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) ArrayAdd(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
ArraySort(identities, IdentitySort);
|
||||
for (i = 0; i < ArraySize(identities); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
||||
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||
char *id_chunk = StrConcat(7,
|
||||
identity->category, "/",
|
||||
identity->type, "/",
|
||||
|
|
@ -104,10 +50,10 @@ XMPPGenerateVer(IQFeatures *features)
|
|||
Free(id_chunk);
|
||||
}
|
||||
|
||||
ArraySort(features->adverts, ((int (*) (void *, void *)) ICollate));
|
||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
||||
ArraySort(features, ((int (*) (void *, void *)) ICollate));
|
||||
for (i = 0; i < ArraySize(features); i++)
|
||||
{
|
||||
char *feature = ArrayGet(features->adverts, i);
|
||||
char *feature = ArrayGet(features, i);
|
||||
char *tmp = S;
|
||||
S = StrConcat(3, S, feature, "<");
|
||||
Free(tmp);
|
||||
|
|
@ -118,64 +64,16 @@ XMPPGenerateVer(IQFeatures *features)
|
|||
S = Base64Encode((const char *) Sha, 20);
|
||||
Free(Sha);
|
||||
|
||||
ArrayFree(features);
|
||||
for (i = 0; i < ArraySize(identities); i++)
|
||||
{
|
||||
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||
/* We don't have to do anything here. */
|
||||
Free(identity);
|
||||
}
|
||||
ArrayFree(identities);
|
||||
|
||||
return S;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features)
|
||||
{
|
||||
XMLElement *c;
|
||||
char *ver;
|
||||
if (!presence || !features)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ver = XMPPGenerateVer(features);
|
||||
c = XMLCreateTag("c");
|
||||
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
||||
XMLAddAttr(c, "hash", "sha-1");
|
||||
XMLAddAttr(c, "node", REPOSITORY);
|
||||
XMLAddAttr(c, "ver", ver);
|
||||
|
||||
Free(ver);
|
||||
XMLAddChild(presence, c);
|
||||
}
|
||||
|
||||
|
||||
IQFeatures *
|
||||
LookupJIDFeatures(char *jid)
|
||||
{
|
||||
IQFeatures *features;
|
||||
if (!jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
features = CreateIQFeatures();
|
||||
|
||||
if (*jid == '#')
|
||||
{
|
||||
/* This is a MUC. As such, we need to advertise MUCs */
|
||||
#define ID(...) AddIQIdentity(features, __VA_ARGS__)
|
||||
#define AD(var) AdvertiseIQFeature(features, var)
|
||||
ID("gateway", NULL, "matrix", "Parsee MUC gateway");
|
||||
ID("conference", NULL, "text", "Parsee MUC gateway");
|
||||
ID("component", NULL, "generic", "Parsee component");
|
||||
|
||||
AD("http://jabber.org/protocol/muc");
|
||||
#undef AD
|
||||
#undef ID
|
||||
}
|
||||
else
|
||||
{
|
||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
||||
IQ_IDENTITY
|
||||
#undef IdentitySimple
|
||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
||||
IQ_ADVERT
|
||||
#undef AdvertiseSimple
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
|
|
|||
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