mirror of
https://forge.fsky.io/lda/Parsee.git
synced 2026-03-13 21:15:11 +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
|
parsee
|
||||||
*.swp
|
*.swp
|
||||||
.*
|
.*
|
||||||
data*
|
data
|
||||||
data*/*
|
data/*
|
||||||
Makefile
|
|
||||||
configure
|
|
||||||
gmon.out
|
|
||||||
|
|
||||||
tools/out
|
tools/out
|
||||||
tools/out/*
|
tools/out/*
|
||||||
|
|
||||||
ayaya/*
|
ayaya/*
|
||||||
ayaya
|
ayaya
|
||||||
|
|
||||||
#ctags
|
|
||||||
tags
|
|
||||||
|
|
||||||
# Whitelists
|
|
||||||
!etc/*
|
|
||||||
!etc/**
|
|
||||||
!.forgejo
|
|
||||||
!.forgejo/*
|
|
||||||
!.forgejo/**
|
|
||||||
|
|
||||||
!.guix
|
|
||||||
!.guix/*
|
|
||||||
!.guix/**
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
Some dates for Parsee-related events. They mostly serve as LDA's TODOs with
|
||||||
a strict deadline:
|
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,
|
For the files src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile, see COPYING.CC0
|
||||||
see COPYING.CC0.
|
to be present.
|
||||||
For any other file in src/, see COPYING.AGPL as the primary license(AGPL-3.0-or-later)
|
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
|
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 - the jealous XMPP<=>Matrix bridge
|
||||||
Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is
|
Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is NOT a drop-in replacment.
|
||||||
NOT a drop-in replacment.
|
|
||||||
Currently, it is *alpha* stage, which means that I wouldn't recommend using this in production,
|
|
||||||
as I can change anything, at any time, and it may behave strangely at times.
|
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
### Naming
|
### Naming
|
||||||
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi),
|
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi), a
|
||||||
a "*bridge* princess". The other name you actually can sometimes see explains itself, so I won't
|
"*bridge* princess".
|
||||||
be talking about it.
|
|
||||||
|
|
||||||
### Reasoning (personal to LDA)
|
### Reasoning (personal to LDA)
|
||||||
I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister
|
I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister project to KappaChat,
|
||||||
project to KappaChat, this means that I can integrate Parsee with KappaChat however I wish it
|
this means that I can integrate Parsee with KappaChat however I wish it to be, which allows me to mess around with a
|
||||||
to be, which allows me to mess around with a codebase I'm already familiar with.
|
codebase I'm already familiar with.
|
||||||
A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*,
|
A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*.
|
||||||
and maybe as a testing ground for Cytoplasm features I sometimes add.
|
Well, I'm *trying* to do that, at least. Please scream at me if that fails(or just doesn't run
|
||||||
|
on a overclocked Raspberry Pi 4B, which, by the way, was literally where Parsee+XMPP ran for
|
||||||
(Well, I'm *trying* to do that, at least.
|
a good chunk of Parsee's start.)
|
||||||
Please scream at me if that fails(or just doesn't run on a overclocked Raspberry Pi 4B))
|
|
||||||
|
|
||||||
### "Why not just use Matrix lol"
|
### "Why not just use Matrix lol"
|
||||||
### "Why not just use XMPP lol"
|
### "Why not just use XMPP lol"
|
||||||
These two having the same answer should be enough information.
|
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
|
One could also argue that both sides need to migrate(onboard) the other side, so
|
||||||
a bridge may be a good way to start.
|
a bridge may be a good way to start.
|
||||||
|
|
||||||
## BUILDING
|
## BUILDING
|
||||||
```sh
|
```sh
|
||||||
$ cc configure.c -o configure # that or use tcc -run to consolidate these two steps.
|
$ make # This generates a 'parsee' executable.
|
||||||
$ ./configure # use -s if you want static Parsee+MbedTLS, use -s -l if LMDB is needed
|
$ cd tools # If you want to build more tools
|
||||||
$ make
|
$ make && cd ..
|
||||||
$ make [PREFIX=...] install # run as root if on a protected dir like /usr
|
$ 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
|
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
|
change a few variables (you can set `CYTO_INC` and `CYTO_LIB`)
|
||||||
library paths specifically.)
|
|
||||||
If you build with MbedTLS, please mind setting the `CYTO_TLS_CA` env to Parsee.
|
|
||||||
|
|
||||||
### DEPENDENCIES
|
### DEPENDENCIES
|
||||||
Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendria.io/Telodendria/Cytoplasm).
|
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).
|
||||||
Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but
|
|
||||||
you can get away without those if you're adventurous).
|
|
||||||
|
|
||||||
### BUILDING WITH GUIX
|
|
||||||
If you have [Guix](https://guix.gnu.org/) installed, you can build Parsee using `guix package -f guix.scm`, or test it
|
|
||||||
using `guix shell -f guix.scm`. You can also generate a Docker/OCI image.
|
|
||||||
```sh
|
|
||||||
guix pack -f docker -S /bin=bin -L.guix/modules parsee
|
|
||||||
```
|
|
||||||
|
|
||||||
## RUNNING
|
## RUNNING
|
||||||
First off, you may want to configure Parsee by running the `config` tool(generally named
|
First off, you may want to configure Parsee by running the `config` tool(generally named
|
||||||
|
|
@ -61,8 +46,7 @@ parsee-config \
|
||||||
-H 'blow.hole' \ # Matrix homeserver name
|
-H 'blow.hole' \ # Matrix homeserver name
|
||||||
-J 'parsee.blow.hole' \ # XMPP component host, must be reachable
|
-J 'parsee.blow.hole' \ # XMPP component host, must be reachable
|
||||||
-s 'A very secure XMPP component secret' \
|
-s 'A very secure XMPP component secret' \
|
||||||
-p 5347 \
|
-p 5347
|
||||||
-M 65535 # Maximum stanza size. Stanzas larger than this from Parsee will be dropped to avoid stream closures. Leave this empty if you're unsure.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If everything goes well, it should generate a `parsee.json` file.
|
If everything goes well, it should generate a `parsee.json` file.
|
||||||
|
|
@ -75,44 +59,31 @@ returns with a landing page, then this side works. You can read it for some more
|
||||||
|
|
||||||
## DOCS
|
## DOCS
|
||||||
Currently, the main sources of documentation are the Ayadocs(for headers) and the manpages
|
Currently, the main sources of documentation are the Ayadocs(for headers) and the manpages
|
||||||
(see `etc/man`).
|
(see `etc/man`)
|
||||||
|
|
||||||
## TODOS before 1.0 rolls around
|
## TODOS
|
||||||
- Make Parsee actually go *vroooooooooommmmmmm*.
|
- Add [libomemo](https://github.com/gkdr/libomemo) as an optional dependency.
|
||||||
- Make sure Parsee can easily run on just about any reasonable POSIX
|
|
||||||
system.
|
|
||||||
- Avoid making 'back-puppets' from Matrix as much as possible
|
|
||||||
- Extension support. I'd need to design a good system, and maybe do it
|
|
||||||
with either shared libraries(`dlopen`/`dlclose` on POSIX) or use a
|
|
||||||
language like Janet or Lua.
|
|
||||||
- Add [libomemo](https://github.com/gkdr/libomemo) or something as an optional dependency.
|
|
||||||
- It depends on more stuff anyways, and I don't want to weigh down the
|
- It depends on more stuff anyways, and I don't want to weigh down the
|
||||||
dependency list of Parsee for that.
|
dependency list of Parsee for that.
|
||||||
- Matrix's libolm is deprecated. They replaced it with a Rust version that
|
- Matrix's libolm is deprecated. They replaced it with a Rust version that
|
||||||
pulls in *way too many* dependencies, and that lacks a C binding. We may
|
pulls in *way too many* dependencies, and that lacks a C binding. We may
|
||||||
~~put in the work of either forking off libolm or~~ be making a binding with
|
put in the work of either forking off libolm or making a binding to KappaChat.
|
||||||
KappaChat(when I get around to remaking UI :p).
|
|
||||||
- Josh did infact tell me that maybe C bindings may happen. I'd be
|
|
||||||
willing to help out, but IDK. In any case, this will at best be an
|
|
||||||
extension packagers may integrate properly.
|
|
||||||
- Get rid of the '?'-syntax and use another invalid Matrix char/valid XMPP char
|
- Get rid of the '?'-syntax and use another invalid Matrix char/valid XMPP char
|
||||||
('$'?) for escaped?
|
('$'?) for escaped?
|
||||||
|
- PROPER FUCKING AVATARS
|
||||||
|
XEP-0084 IS THE WORST PIECE OF SHIT KNOWN TO MAN. If any Jabberbros want to
|
||||||
|
look at terrible code/XML and suggest things to have *proper* avatar support,
|
||||||
|
I'm all in.
|
||||||
- Consider making room/MUC admins/owners be able to plumb instead of it being
|
- Consider making room/MUC admins/owners be able to plumb instead of it being
|
||||||
restricted to Parsee admins, with permission from MUC owners, too
|
restricted to Parsee admins, with permission from MUC owners, too
|
||||||
- Limiting to admins may be a way to "control" consent for both, but this is
|
- Limiting to admins may be a way to "control" consent for both, but this is
|
||||||
only if Parsee admins are good-willed, which we must assume such statment to
|
only if Parsee admins are good-willed, which we must assume such statment to
|
||||||
be false by default.
|
be false by default.
|
||||||
- Currently, MUC owners may kick Parsee out, with the effect of unlinking the
|
|
||||||
MUC.
|
|
||||||
- Look at XEPS-TBD.TXT for XEPs to be done
|
- Look at XEPS-TBD.TXT for XEPs to be done
|
||||||
- Add a MUC server to Parsee, such that it may be able to hook onto it and therefore
|
- Add a MUC server to Parsee, such that it may be able to hook onto it and therefore
|
||||||
support XMPP->Matrix bridging.
|
support XMPP->Matrix bridging.
|
||||||
- Manage MUC DMs in a reasonable manner. Thanks `@freeoffers4u:matrix.org` for being
|
- Manage MUC DMs in a reasonable manner. Thanks `@freeoffers4u:matrix.org` for being
|
||||||
a fucking annoyance and DMing an old Parsee semi-anon user for no clear reason.
|
a fucking annoyance and DMing an old Parsee semi-anon user for no clear reason.
|
||||||
- Make Parsee cope with stream closures(i.e: XMPP server turning off) better. As of
|
|
||||||
now, it just kills itself when that happens, instead of trying to negociate a new
|
|
||||||
connection, which would be a better method that would actually fit Parsee's own
|
|
||||||
principles.
|
|
||||||
|
|
||||||
## DONATING/CONTRIBUTING
|
## DONATING/CONTRIBUTING
|
||||||
If you know things about XMPP or Matrix, yet aren't familiar with C99, or just
|
If you know things about XMPP or Matrix, yet aren't familiar with C99, or just
|
||||||
|
|
@ -129,7 +100,7 @@ You may also donate to [the LiberaPay](https://en.liberapay.com/Parsee), alongsi
|
||||||
currently maintaining Cytoplasm.
|
currently maintaining Cytoplasm.
|
||||||
|
|
||||||
## IM chats
|
## IM chats
|
||||||
Please avoid asking for help/issues 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.
|
an issue and link it over it. Basic respect for others/not being an asshat is required.
|
||||||
(Also, these are temporary room aliases.)
|
(Also, these are temporary room aliases.)
|
||||||
- [#parsee:tedomum.net](https://matrix.to/#/%23parsee:tedomum.net)
|
- [#parsee:tedomum.net](https://matrix.to/#/%23parsee:tedomum.net)
|
||||||
|
|
|
||||||
42
XEPS-TBD.TXT
42
XEPS-TBD.TXT
|
|
@ -1,19 +1,6 @@
|
||||||
XEPs current supported are in src/XMPPThread, at the IQ disco advertising.
|
XEPs current supported are in src/XMPPThread, at the IQ disco advertising.
|
||||||
|
|
||||||
Somewhat implemented XEPs:
|
Somewhat implemented XEPs:
|
||||||
v https://xmpp.org/extensions/xep-0050.html
|
|
||||||
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
|
||||||
also a nice to have.
|
|
||||||
There are commands, but not a lot of them as of now, and localisation
|
|
||||||
is missing.
|
|
||||||
v https://xmpp.org/extensions/xep-0421.html
|
|
||||||
Using the occupant ID in semi-anonymous MUCs is a desirable property.
|
|
||||||
I dont know of a lot of places that don't use the occupant ID anymore
|
|
||||||
within Parsee.
|
|
||||||
v "also it [Bifrost] doesn't respect voice either"
|
|
||||||
As far as I am aware, works.
|
|
||||||
v https://xmpp.org/extensions/xep-0425.html
|
|
||||||
As mentionned in #2, moderation _needs_ to be done.
|
|
||||||
~ https://xmpp.org/extensions/xep-0085.html
|
~ https://xmpp.org/extensions/xep-0085.html
|
||||||
Only XMPP->Matrix at the moment. Still need to figure out how to
|
Only XMPP->Matrix at the moment. Still need to figure out how to
|
||||||
get typing indicators as an AS.
|
get typing indicators as an AS.
|
||||||
|
|
@ -21,14 +8,21 @@ Somewhat implemented XEPs:
|
||||||
This allows reactions, which Matrix also has support to. The two
|
This allows reactions, which Matrix also has support to. The two
|
||||||
systems don't seem *too* restrictive on one-another (unlike some
|
systems don't seem *too* restrictive on one-another (unlike some
|
||||||
IM platforms I won't mention), so this doesn't sound too bad to do
|
IM platforms I won't mention), so this doesn't sound too bad to do
|
||||||
TODO: Add support from Matrix.
|
HALF-IMPLEMENTED: Removing reacts won't work.
|
||||||
~ https://xmpp.org/extensions/xep-0184.html
|
~ https://xmpp.org/extensions/xep-0184.html
|
||||||
Only Matrix->XMPP as of now. Requesting data from Matrix ASes without
|
Only Matrix->XMPP as of now. Requesting data from Matrix ASes without
|
||||||
/sync seems like a non-option as of now, which _sucks_.
|
/sync seems like a non-option as of now, which _sucks_.
|
||||||
~ https://xmpp.org/extensions/xep-0084.html
|
~ https://xmpp.org/extensions/xep-0050.html
|
||||||
Avatar support would be extremely useful, if just a QoL improvment.
|
Ad-hoc commands that bridge maintainers can deal with XMPP-style are
|
||||||
Matrix and XMPP both have support for these.
|
also a nice to have.
|
||||||
XEP-0084 is a pain in the ass to implement and seems generally just
|
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:
|
For future XEPs:
|
||||||
- https://xmpp.org/extensions/xep-0449.html
|
- 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''
|
which is used along PEP, it seems, and meanwhile Matrix has ''support''
|
||||||
for packs too, tracking them is between "annoyance" and "yeah, no.".
|
for packs too, tracking them is between "annoyance" and "yeah, no.".
|
||||||
|
|
||||||
On Standby:
|
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.
|
unreliable, however.
|
||||||
x https://xmpp.org/extensions/xep-0080.html
|
x https://xmpp.org/extensions/xep-0080.html
|
||||||
Can't think of a good analogy to these...
|
Can't think of a good analogy to these...
|
||||||
|
|
||||||
|
|
||||||
Not XEPs, but ideas that _needs_ to be added:
|
Not XEPs, but ideas that _needs_ to be added:
|
||||||
|
v "also it [Bifrost] doesn't respect voice either" -> Send a form on moderated
|
||||||
|
MUCs (which is standard, so its not too bad!). Currently WIP, and barely tested.
|
||||||
~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d
|
~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d
|
||||||
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.)
|
- Kappa-like extension system(maybe bridging more than just Matrix-XMPP.)
|
||||||
- https://www.youtube.com/watch?v=InL414iDZmY
|
- https://www.youtube.com/watch?v=InL414iDZmY
|
||||||
|
|
||||||
|
|
|
||||||
10
build.conf
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);
|
HttpRequestHeader(ctx, "Authorization", bearer);
|
||||||
Free(bearer);
|
Free(bearer);
|
||||||
}
|
}
|
||||||
|
bool
|
||||||
|
ASRegisterUser(const ParseeConfig *conf, char *user)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
HttpStatus status;
|
||||||
|
if (!conf || !user)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create user. We don't actually care about the value as we can
|
||||||
|
* masquerade, as long as it exists. */
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, "/_matrix/client/v3/register"
|
||||||
|
);
|
||||||
|
json = HashMapCreate();
|
||||||
|
|
||||||
|
HashMapSet(json,"type",JsonValueString("m.login.application_service"));
|
||||||
|
|
||||||
|
user = ParseeGetLocal(user);
|
||||||
|
HashMapSet(json,"username",JsonValueString(user));
|
||||||
|
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
status = ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
return status == HTTP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ASPing(const ParseeConfig *conf)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path;
|
||||||
|
if (!conf)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v1/appservice/",
|
||||||
|
"Parsee%20XMPP",
|
||||||
|
"/ping"
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASInvite(const ParseeConfig *conf, char *id, char *invited)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *bridge;
|
||||||
|
if (!conf || !id || !invited)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/invite",
|
||||||
|
"?user_id=", bridge
|
||||||
|
);
|
||||||
|
Free(bridge);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "user_id", JsonValueString(invited));
|
||||||
|
HashMapSet(json, "reason", JsonValueString("Pass over."));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASBan(const ParseeConfig *conf, char *id, char *banned)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *bridge;
|
||||||
|
if (!conf || !id || !banned)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/ban",
|
||||||
|
"?user_id=", bridge
|
||||||
|
);
|
||||||
|
Free(bridge);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||||
|
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASKick(const ParseeConfig *conf, char *id, char *banned)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *bridge;
|
||||||
|
if (!conf || !id || !banned)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bridge = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/kick",
|
||||||
|
"?user_id=", bridge
|
||||||
|
);
|
||||||
|
Free(bridge);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "user_id", JsonValueString(banned));
|
||||||
|
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *ret;
|
||||||
|
if (!conf || !id)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!masquerade)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
masquerade = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
masquerade = HttpUrlEncode(masquerade);
|
||||||
|
}
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/join/", id, "?",
|
||||||
|
"user_id=", masquerade
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
ret = StrDuplicate(GrabString(json, 1, "room_id"));
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(masquerade);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASLeave(const ParseeConfig *conf, char *id, char *masquerade)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !id)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!masquerade)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", conf->sender_localpart,
|
||||||
|
":", conf->server_base
|
||||||
|
);
|
||||||
|
masquerade = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
masquerade = HttpUrlEncode(masquerade);
|
||||||
|
}
|
||||||
|
id = HttpUrlEncode(id);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/leave?",
|
||||||
|
"user_id=", masquerade
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(masquerade);
|
||||||
|
Free(id);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetState(const ParseeConfig *conf, char *id, char *type, char *key, char *mask, HashMap *state)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !id || !type || !mask || !state)
|
||||||
|
{
|
||||||
|
JsonFree(state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(9,
|
||||||
|
"/_matrix/client/v3/rooms/", id, "/state/",
|
||||||
|
type, "/", key, "?", "user_id=", mask
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, state);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(state);
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
char *path;
|
||||||
|
char *txn, *ret;
|
||||||
|
HashMap *reply;
|
||||||
|
if (!conf || !id || !type || !user || !c)
|
||||||
|
{
|
||||||
|
JsonFree(c);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
txn = StrRandom(16);
|
||||||
|
path = StrConcat(9,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
id, "/send/", type, "/", txn, "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
Free(txn);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, c);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));
|
||||||
|
JsonFree(reply);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(c);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASCreateRoom(const ParseeConfig *conf, char *by, char *alias)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *id;
|
||||||
|
if (!conf || !by)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v3/createRoom",
|
||||||
|
"?user_id=", by
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
if (alias)
|
||||||
|
{
|
||||||
|
char *trimmed = StrDuplicate(alias);
|
||||||
|
if (*alias == '#')
|
||||||
|
{
|
||||||
|
char *tmp, cb[2] = { 0 };
|
||||||
|
alias++;
|
||||||
|
Free(trimmed);
|
||||||
|
trimmed = NULL;
|
||||||
|
|
||||||
|
while (*alias && *alias != ':')
|
||||||
|
{
|
||||||
|
cb[0] = *alias;
|
||||||
|
tmp = trimmed;
|
||||||
|
trimmed = StrConcat(2, trimmed, cb);
|
||||||
|
Free(tmp);
|
||||||
|
alias ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HashMapSet(json, "room_alias_name", JsonValueString(trimmed));
|
||||||
|
Free(trimmed);
|
||||||
|
}
|
||||||
|
HashMapSet(json, "visibility", JsonValueString("public"));
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
JsonFree(json);
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASCreateDM(const ParseeConfig *conf, char *by, char *with)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json = NULL;
|
||||||
|
char *path, *id;
|
||||||
|
if (!conf || !by || !with)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v3/createRoom",
|
||||||
|
"?user_id=", by
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(
|
||||||
|
conf,
|
||||||
|
HTTP_POST, path
|
||||||
|
);
|
||||||
|
Free(path);
|
||||||
|
json = HashMapCreate();
|
||||||
|
{
|
||||||
|
Array *invitees = ArrayCreate();
|
||||||
|
|
||||||
|
ArrayAdd(invitees, JsonValueString(with));
|
||||||
|
HashMapSet(json, "invite", JsonValueArray(invitees));
|
||||||
|
HashMapSet(json, "is_direct", JsonValueBoolean(true));
|
||||||
|
}
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
JsonFree(json);
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
id = StrDuplicate(JsonValueAsString(HashMapGet(json, "room_id")));
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetAvatar(const ParseeConfig *conf, char *user, char *mxc)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !user || !mxc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/profile/",
|
||||||
|
user, "/avatar_url", "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "avatar_url", JsonValueString(mxc));
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetName(const ParseeConfig *conf, char *user, char *name)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!conf || !user || !name)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/profile/",
|
||||||
|
user, "/displayname", "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "displayname", JsonValueString(name));
|
||||||
|
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(conf, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(json);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
HashMap *
|
||||||
|
ASFind(const ParseeConfig *c, char *room, char *event)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path, *user;
|
||||||
|
if (!c || !room || !event)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = StrConcat(4, "@", c->sender_localpart, ":", c->server_base);
|
||||||
|
path = StrConcat(7,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/event/", event, "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASGetName(const ParseeConfig *c, char *room, char *user)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
HashMap *reply;
|
||||||
|
char *path, *ret;
|
||||||
|
char *u2 = user;
|
||||||
|
if (!c || !user)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!room)
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(3,
|
||||||
|
"/_matrix/client/v3/profile/", user, "/displayname"
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(user);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||||
|
);
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
ret = StrDuplicate(u2);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
path = StrConcat(4,
|
||||||
|
"/_matrix/client/v3/rooms/", room,
|
||||||
|
"/state/m.room.member/", user
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(user);
|
||||||
|
Free(room);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "displayname"))
|
||||||
|
);
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
ret = StrDuplicate(u2);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
HashMap *
|
||||||
|
ASGetPL(const ParseeConfig *c, char *room)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
HashMap *reply;
|
||||||
|
if (!c || !room)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
path = StrConcat(4,
|
||||||
|
"/_matrix/client/v3/rooms/", room,
|
||||||
|
"/state/m.room.power_levels/", ""
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(room);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetPL(const ParseeConfig *conf, char *id, HashMap *m)
|
||||||
|
{
|
||||||
|
char *user;
|
||||||
|
if (!conf || !id || !m)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
user = StrConcat(4,
|
||||||
|
"@",conf->sender_localpart,
|
||||||
|
":",conf->server_base
|
||||||
|
);
|
||||||
|
ASSetState(conf, id, "m.room.power_levels", "", user, m);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime)
|
||||||
|
{
|
||||||
|
char *size_str, *path, *ret, *user;
|
||||||
|
int i;
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
HashMap *reply;
|
||||||
|
if (!c || !from)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_str = StrInt(size);
|
||||||
|
user = StrConcat(4, "@",c->sender_localpart,":",c->server_base);
|
||||||
|
path = StrConcat(2,
|
||||||
|
"/_matrix/media/v3/upload?user_id=", user
|
||||||
|
);
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
if (size)
|
||||||
|
{
|
||||||
|
HttpRequestHeader(ctx, "Content-Length", size_str);
|
||||||
|
}
|
||||||
|
if (mime)
|
||||||
|
{
|
||||||
|
HttpRequestHeader(ctx, "Content-Type", mime);
|
||||||
|
}
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
|
||||||
|
for (i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
int ch = StreamGetc(from);
|
||||||
|
if (ch == EOF)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
StreamPutc(HttpClientStream(ctx), ch);
|
||||||
|
}
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
reply = JsonDecode(HttpClientStream(ctx));
|
||||||
|
ret = StrDuplicate(
|
||||||
|
JsonValueAsString(HashMapGet(reply, "content_uri"))
|
||||||
|
);
|
||||||
|
if (!ret)
|
||||||
|
{
|
||||||
|
JsonEncode(reply, StreamStdout(), JSON_PRETTY);
|
||||||
|
StreamFlush(StreamStdout());
|
||||||
|
}
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
JsonFree(reply);
|
||||||
|
Free(size_str);
|
||||||
|
Free(path);
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ASReupload(const ParseeConfig *c, char *from, char **mime)
|
||||||
|
{
|
||||||
|
Uri *uri;
|
||||||
|
HttpClientContext *ctx;
|
||||||
|
unsigned short port;
|
||||||
|
int size = 0, flags = HTTP_FLAG_NONE;
|
||||||
|
char *ret, *content_len;
|
||||||
|
|
||||||
|
if (!c || !from)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri = UriParse(from);
|
||||||
|
if (!uri)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (uri->port)
|
||||||
|
{
|
||||||
|
port = uri->port;
|
||||||
|
}
|
||||||
|
else if (StrEquals(uri->proto, "https"))
|
||||||
|
{
|
||||||
|
port = 443;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
port = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StrEquals(uri->proto, "https"))
|
||||||
|
{
|
||||||
|
flags |= HTTP_FLAG_TLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = HttpRequest(
|
||||||
|
HTTP_GET, flags, port, uri->host, uri->path
|
||||||
|
);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
if (mime)
|
||||||
|
{
|
||||||
|
*mime = HashMapGet(HttpResponseHeaders(ctx), "content-type");
|
||||||
|
*mime = StrDuplicate(*mime);
|
||||||
|
}
|
||||||
|
|
||||||
|
content_len = HashMapGet(HttpResponseHeaders(ctx), "content-length");
|
||||||
|
if (content_len)
|
||||||
|
{
|
||||||
|
size = strtol(content_len, NULL, 10);
|
||||||
|
}
|
||||||
|
ret = ASUpload(c, HttpClientStream(ctx), size, mime ? *mime : NULL);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
UriFree(uri);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASType(const ParseeConfig *c, char *user, char *room, bool status)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!c || !user || !room)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/typing/", user,
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
HashMapSet(json, "typing", JsonValueBoolean(status));
|
||||||
|
/* If someone types for 10 minutes straight, they got something weird man. */
|
||||||
|
HashMapSet(json, "timeout", JsonValueBoolean(10 MINUTES));
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ASPresence(const ParseeConfig *c, char *user, char *room, char *ev)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!c || !user || !room || !ev)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
ev = HttpUrlEncode(ev);
|
||||||
|
path = StrConcat(6,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/receipt/m.read/", ev,
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
json = HashMapCreate();
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_POST, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, json);
|
||||||
|
JsonFree(json);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
Free(room);
|
||||||
|
Free(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap *
|
||||||
|
ASGetUserConfig(const ParseeConfig *c, char *user, char *key)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *json;
|
||||||
|
char *path;
|
||||||
|
if (!c || !key)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", c->sender_localpart,
|
||||||
|
":", c->server_base
|
||||||
|
);
|
||||||
|
user = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
}
|
||||||
|
path = StrConcat(7,
|
||||||
|
"/_matrix/client/v3/user/",
|
||||||
|
user, "/account_data/", key, "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_GET, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
HttpRequestSendHeaders(ctx);
|
||||||
|
HttpRequestSend(ctx);
|
||||||
|
|
||||||
|
json = JsonDecode(HttpClientStream(ctx));
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetUserConfig(const ParseeConfig *c, char *user, char *key, HashMap *map)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
char *path;
|
||||||
|
if (!c || !key || !map)
|
||||||
|
{
|
||||||
|
JsonFree(map);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", c->sender_localpart,
|
||||||
|
":", c->server_base
|
||||||
|
);
|
||||||
|
user = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
path = StrConcat(7,
|
||||||
|
"/_matrix/client/v3/user/",
|
||||||
|
user, "/account_data/", key, "?",
|
||||||
|
"user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, map);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
JsonFree(map);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASRedact(const ParseeConfig *c, char *room, char *user, char *e_id)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *request;
|
||||||
|
char *path, *txn;
|
||||||
|
if (!c || !room || !e_id)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
{
|
||||||
|
char *raw = StrConcat(4,
|
||||||
|
"@", c->sender_localpart,
|
||||||
|
":", c->server_base
|
||||||
|
);
|
||||||
|
user = HttpUrlEncode(raw);
|
||||||
|
Free(raw);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
}
|
||||||
|
room = HttpUrlEncode(room);
|
||||||
|
e_id = HttpUrlEncode(e_id);
|
||||||
|
txn = StrRandom(16);
|
||||||
|
|
||||||
|
path = StrConcat(9,
|
||||||
|
"/_matrix/client/v3/rooms/",
|
||||||
|
room, "/redact/", e_id, "/", txn,
|
||||||
|
"?", "user_id=", user
|
||||||
|
);
|
||||||
|
|
||||||
|
request = HashMapCreate();
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
Free(path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, request);
|
||||||
|
JsonFree(request);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(user);
|
||||||
|
Free(room);
|
||||||
|
Free(e_id);
|
||||||
|
Free(txn);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
void
|
||||||
|
ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, char *msg)
|
||||||
|
{
|
||||||
|
HttpClientContext *ctx = NULL;
|
||||||
|
HashMap *request;
|
||||||
|
char *path;
|
||||||
|
char *status_str = NULL;
|
||||||
|
if (!c || !user)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case USER_STATUS_ONLINE: status_str = "online"; break;
|
||||||
|
case USER_STATUS_OFFLINE: status_str = "offline"; break;
|
||||||
|
case USER_STATUS_UNAVAILABLE: status_str = "unavailable"; break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = HttpUrlEncode(user);
|
||||||
|
path = StrConcat(5,
|
||||||
|
"/_matrix/client/v3/presence/",user,"/status",
|
||||||
|
"?user_id=", user
|
||||||
|
);
|
||||||
|
Free(user);
|
||||||
|
|
||||||
|
request = HashMapCreate();
|
||||||
|
HashMapSet(request, "presence", JsonValueString(status_str));
|
||||||
|
if (msg)
|
||||||
|
{
|
||||||
|
HashMapSet(request, "status_msg", JsonValueString(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = ParseeCreateRequest(c, HTTP_PUT, path);
|
||||||
|
ASAuthenticateRequest(c, ctx);
|
||||||
|
ParseeSetRequestJSON(ctx, request);
|
||||||
|
JsonFree(request);
|
||||||
|
|
||||||
|
HttpClientContextFree(ctx);
|
||||||
|
Free(path);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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, ' ');
|
end_data = strchr(cmd, ' ');
|
||||||
if (!end_data || (cmd > end_data))
|
if (!end_data)
|
||||||
{
|
{
|
||||||
ret = Malloc(sizeof(*ret));
|
ret = Malloc(sizeof(*ret));
|
||||||
ret->command = StrDuplicate(cmd);
|
ret->command = StrDuplicate(cmd);
|
||||||
|
|
@ -49,11 +49,11 @@ CommandParse(char *cmd)
|
||||||
char c = *cur;
|
char c = *cur;
|
||||||
char *tmp;
|
char *tmp;
|
||||||
bool type;
|
bool type;
|
||||||
char char_type = '\0';
|
char char_type;
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case STATE_WHITE:
|
case STATE_WHITE:
|
||||||
if (!isblank((int) c))
|
if (!isblank(c))
|
||||||
{
|
{
|
||||||
state = STATE_NAME;
|
state = STATE_NAME;
|
||||||
namestart = cur;
|
namestart = cur;
|
||||||
|
|
@ -84,7 +84,7 @@ CommandParse(char *cmd)
|
||||||
{
|
{
|
||||||
char c = *cur;
|
char c = *cur;
|
||||||
char cb[2] = { c, '\0' };
|
char cb[2] = { c, '\0' };
|
||||||
if ((type && c == char_type) || (!type && isblank((int) c)))
|
if ((type && c == char_type) || (!type && isblank(c)))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,47 +15,35 @@ CommandCreateRouter(void)
|
||||||
void
|
void
|
||||||
CommandAddCommand(CommandRouter *rter, char *c, CommandRoute rte)
|
CommandAddCommand(CommandRouter *rter, char *c, CommandRoute rte)
|
||||||
{
|
{
|
||||||
CommandRoute *indirect;
|
|
||||||
if (!rter || !c || !rte)
|
if (!rter || !c || !rte)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
HashMapSet(rter->routes, c, rte);
|
||||||
/* Little dirty trick to force C99 into submission, and since
|
|
||||||
* some architectures may separate data/code. Still don't like it... */
|
|
||||||
indirect = Malloc(sizeof(rte));
|
|
||||||
*indirect = rte;
|
|
||||||
HashMapSet(rter->routes, c, (void *) indirect);
|
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
RouteCommand(CommandRouter *rter, Command *cmd, void *d)
|
RouteCommand(CommandRouter *rter, Command *cmd, void *d)
|
||||||
{
|
{
|
||||||
CommandRoute *route;
|
CommandRoute route;
|
||||||
if (!rter || !cmd)
|
if (!rter || !cmd)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
route = HashMapGet(rter->routes, cmd->command);
|
route = HashMapGet(rter->routes, cmd->command);
|
||||||
if (route && *route)
|
if (route)
|
||||||
{
|
{
|
||||||
(*route)(cmd, d);
|
route(cmd, d);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
CommandFreeRouter(CommandRouter *rter)
|
CommandFreeRouter(CommandRouter *rter)
|
||||||
{
|
{
|
||||||
char *key;
|
|
||||||
CommandRoute *val;
|
|
||||||
if (!rter)
|
if (!rter)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (HashMapIterate(rter->routes, &key, (void **) &val))
|
|
||||||
{
|
|
||||||
Free(val);
|
|
||||||
}
|
|
||||||
HashMapFree(rter->routes);
|
HashMapFree(rter->routes);
|
||||||
Free(rter);
|
Free(rter);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,28 +22,11 @@ CommandHead(CmdBanUser, cmd, argp)
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASBan(data->config, room, user);
|
ASBan(data->config, room, user);
|
||||||
ReplySprintf("Banning %s from '%s'...", user, room);
|
|
||||||
|
|
||||||
BotDestroy();
|
ReplySprintf("Banning %s from '%s'...",
|
||||||
}
|
user, room
|
||||||
CommandHead(CmdNoFlyListDel, cmd, argp)
|
);
|
||||||
{
|
|
||||||
ParseeCmdArg *args = argp;
|
|
||||||
ParseeData *data = args->data;
|
|
||||||
HashMap *event = args->event;
|
|
||||||
char *user = HashMapGet(cmd->arguments, "user");
|
|
||||||
BotInitialise();
|
|
||||||
|
|
||||||
if (!user)
|
|
||||||
{
|
|
||||||
BotDestroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReplySprintf("Unbanning %s", user);
|
|
||||||
ParseeGlobalUnban(data, user);
|
|
||||||
|
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,4 @@ CommandHead(CmdHelp, cmd, argp)
|
||||||
);
|
);
|
||||||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
|
|
||||||
(void) cmd;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,4 @@ CommandHead(CmdListBans, cmd, argp)
|
||||||
|
|
||||||
DbUnlock(data->db, listed);
|
DbUnlock(data->db, listed);
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
|
|
||||||
(void) cmd;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,7 @@ CommandHead(CmdPlumb, cmd, argp)
|
||||||
BotRequired(room);
|
BotRequired(room);
|
||||||
|
|
||||||
/* Check MUC viability */
|
/* Check MUC viability */
|
||||||
if (ParseeManageBan(args->data, muc, NULL) ||
|
if (ParseeManageBan(args->data, muc, NULL))
|
||||||
!ParseeIsMUCWhitelisted(args->data, muc))
|
|
||||||
{
|
{
|
||||||
ReplySprintf("MUC '%s' is not allowed on this bridge.", muc);
|
ReplySprintf("MUC '%s' is not allowed on this bridge.", muc);
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -63,7 +62,7 @@ CommandHead(CmdPlumb, cmd, argp)
|
||||||
if (chat_id)
|
if (chat_id)
|
||||||
{
|
{
|
||||||
char *rev = StrConcat(2, muc, "/parsee");
|
char *rev = StrConcat(2, muc, "/parsee");
|
||||||
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
|
XMPPJoinMUC(args->data->jabber, "parsee", rev);
|
||||||
|
|
||||||
Free(rev);
|
Free(rev);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ CommandHead(CmdStats, cmd, argp)
|
||||||
BotInitialise();
|
BotInitialise();
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO: Separate these into different "categories" */
|
||||||
ReplySprintf("Information for %s v%s (Cytoplasm %s)",
|
ReplySprintf("Information for %s v%s (Cytoplasm %s)",
|
||||||
NAME, VERSION, CytoplasmGetVersionStr()
|
NAME, VERSION, CytoplasmGetVersionStr()
|
||||||
);
|
);
|
||||||
|
|
@ -40,6 +41,4 @@ CommandHead(CmdStats, cmd, argp)
|
||||||
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
|
||||||
|
|
||||||
BotDestroy();
|
BotDestroy();
|
||||||
|
|
||||||
(void) cmd;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,60 +9,43 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
static bool
|
|
||||||
Grab(ParseeData *data, Command *cmd, char **muc, char **chat_id, char **room)
|
|
||||||
{
|
|
||||||
if (HashMapGet(cmd->arguments, "muc"))
|
|
||||||
{
|
|
||||||
*muc = HashMapGet(cmd->arguments, "muc");
|
|
||||||
|
|
||||||
*chat_id = ParseeGetFromMUCID(data, *muc);
|
|
||||||
*room = ParseeGetRoomID(data, *chat_id);
|
|
||||||
if (!chat_id || !room)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (HashMapGet(cmd->arguments, "room"))
|
|
||||||
{
|
|
||||||
*room = HashMapGet(cmd->arguments, "room");
|
|
||||||
|
|
||||||
*chat_id = ParseeGetFromRoomID(data, *room);
|
|
||||||
*muc = ParseeGetMUCID(data, *chat_id);
|
|
||||||
if (!chat_id || !muc)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandHead(CmdUnlinkMUC, cmd, argp)
|
CommandHead(CmdUnlinkMUC, cmd, argp)
|
||||||
{
|
{
|
||||||
ParseeCmdArg *args = argp;
|
ParseeCmdArg *args = argp;
|
||||||
ParseeData *data = args->data;
|
ParseeData *data = args->data;
|
||||||
HashMap *event = args->event;
|
HashMap *json, *event = args->event, *mucs;
|
||||||
|
DbRef *ref;
|
||||||
char *muc = NULL, *chat_id = NULL, *room = NULL;
|
char *muc = NULL, *chat_id = NULL, *room = NULL;
|
||||||
|
|
||||||
BotInitialise();
|
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;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
chat_id = ParseeGetFromMUCID(data, muc);
|
ref = DbLock(data->db, 1, "chats");
|
||||||
room = ParseeGetRoomID(data, chat_id);
|
json = DbJson(ref);
|
||||||
|
chat_id = StrDuplicate(GrabString(json, 2, "mucs", muc));
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
{
|
{
|
||||||
ReplySprintf("No internal mapping to '%s'.", muc);
|
ReplySprintf("No internal mapping to '%s'.", muc);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
mucs = GrabObject(json, 1, "mucs");
|
||||||
|
JsonValueFree(HashMapDelete(mucs, muc));
|
||||||
|
DbUnlock(data->db, ref);
|
||||||
|
|
||||||
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* */
|
/* TODO: Do it automatically, if *not plumbed* */
|
||||||
ReplySprintf("The MUC %s is now *unlinked*.", muc);
|
ReplySprintf("The MUC %s is now *unlinked*.", muc);
|
||||||
|
|
|
||||||
27
src/Events.c
27
src/Events.c
|
|
@ -37,10 +37,12 @@ MatrixCreateMessage(char *body)
|
||||||
HashMapSet(map, "msgtype", JsonValueString("m.text"));
|
HashMapSet(map, "msgtype", JsonValueString("m.text"));
|
||||||
HashMapSet(map, "body", JsonValueString(body));
|
HashMapSet(map, "body", JsonValueString(body));
|
||||||
{
|
{
|
||||||
|
/* TODO */
|
||||||
XEP393Element *e = XEP393(body);
|
XEP393Element *e = XEP393(body);
|
||||||
text = XEP393ToXMLString(e);
|
text = XEP393ToXMLString(e);
|
||||||
XEP393FreeElement(e);
|
XEP393FreeElement(e);
|
||||||
|
|
||||||
|
|
||||||
HashMapSet(map, "formatted_body", JsonValueString(text));
|
HashMapSet(map, "formatted_body", JsonValueString(text));
|
||||||
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
||||||
Free(text);
|
Free(text);
|
||||||
|
|
@ -79,7 +81,7 @@ MatrixCreateNickChange(char *nick)
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
HashMap *
|
HashMap *
|
||||||
MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
MatrixCreateMedia(char *mxc, char *body, char *mime)
|
||||||
{
|
{
|
||||||
HashMap *map;
|
HashMap *map;
|
||||||
char *mime_type = NULL, *matrix_type = NULL;
|
char *mime_type = NULL, *matrix_type = NULL;
|
||||||
|
|
@ -91,10 +93,9 @@ MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
||||||
matrix_type = "m.file";
|
matrix_type = "m.file";
|
||||||
if (mime)
|
if (mime)
|
||||||
{
|
{
|
||||||
size_t i, len;
|
size_t i;
|
||||||
mime_type = StrDuplicate(mime);
|
mime_type = StrDuplicate(mime);
|
||||||
len = strlen(mime);
|
for (i = 0; i < strlen(mime); i++)
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
{
|
||||||
if (mime_type[i] == '/')
|
if (mime_type[i] == '/')
|
||||||
{
|
{
|
||||||
|
|
@ -120,12 +121,6 @@ MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
|
||||||
}
|
}
|
||||||
|
|
||||||
map = HashMapCreate();
|
map = HashMapCreate();
|
||||||
JsonSet(map, JsonValueString(mime), 2, "info", "mimetype");
|
|
||||||
if (info && info->width && info->height)
|
|
||||||
{
|
|
||||||
JsonSet(map, JsonValueInteger(info->width), 2, "info", "w");
|
|
||||||
JsonSet(map, JsonValueInteger(info->height), 2, "info", "h");
|
|
||||||
}
|
|
||||||
HashMapSet(map, "msgtype", JsonValueString(matrix_type));
|
HashMapSet(map, "msgtype", JsonValueString(matrix_type));
|
||||||
HashMapSet(map, "mimetype", JsonValueString(mime));
|
HashMapSet(map, "mimetype", JsonValueString(mime));
|
||||||
HashMapSet(map, "body", JsonValueString(body));
|
HashMapSet(map, "body", JsonValueString(body));
|
||||||
|
|
@ -187,18 +182,6 @@ MatrixCreateReplace(char *event, char *body)
|
||||||
HashMapSet(map, "m.new_content", JsonValueObject(new));
|
HashMapSet(map, "m.new_content", JsonValueObject(new));
|
||||||
HashMapSet(map, "m.relates_to", JsonValueObject(rel));
|
HashMapSet(map, "m.relates_to", JsonValueObject(rel));
|
||||||
|
|
||||||
{
|
|
||||||
XEP393Element *e = XEP393(body);
|
|
||||||
char *text = XEP393ToXMLString(e);
|
|
||||||
XEP393FreeElement(e);
|
|
||||||
|
|
||||||
HashMapSet(map, "formatted_body", JsonValueString(text));
|
|
||||||
HashMapSet(map, "format", JsonValueString("org.matrix.custom.html"));
|
|
||||||
HashMapSet(new, "formatted_body", JsonValueString(text));
|
|
||||||
HashMapSet(new, "format", JsonValueString("org.matrix.custom.html"));
|
|
||||||
Free(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
HashMap *
|
HashMap *
|
||||||
|
|
|
||||||
|
|
@ -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)
|
switch (c1)
|
||||||
{
|
{
|
||||||
case '*':
|
case '*':
|
||||||
|
/* TODO */
|
||||||
while ((c2 = *string) && (c2 != next))
|
while ((c2 = *string) && (c2 != next))
|
||||||
{
|
{
|
||||||
string++;
|
string++;
|
||||||
|
|
|
||||||
|
|
@ -29,18 +29,19 @@ ParseeRequest(HttpServerContext *ctx, void *argp)
|
||||||
|
|
||||||
arg.stream = stream;
|
arg.stream = stream;
|
||||||
|
|
||||||
Log(LOG_DEBUG, "%s %s",
|
Log(LOG_NOTICE, "%s %s",
|
||||||
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
||||||
path
|
path
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!HttpRouterRoute(data->router, path, &arg, (void **) &response))
|
if (!HttpRouterRoute(data->router, path, &arg, (void **) &response))
|
||||||
{
|
{
|
||||||
Log(LOG_DEBUG, "Couldn't route %s", path);
|
Log(LOG_NOTICE, "Couldn't route %s", path);
|
||||||
HttpResponseStatus(ctx, HTTP_NOT_FOUND);
|
HttpResponseStatus(ctx, HTTP_NOT_FOUND);
|
||||||
JsonFree(response);
|
JsonFree(response);
|
||||||
|
|
||||||
response = MatrixCreateError("M_NOT_FOUND", "Route not found.");
|
response = MatrixCreateError("M_NOT_FOUND", "Route not found.");
|
||||||
|
/* TODO: Set a thing */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Whatever, we routed a thing. */
|
/* Whatever, we routed a thing. */
|
||||||
|
|
@ -55,11 +56,8 @@ ParseeRequest(HttpServerContext *ctx, void *argp)
|
||||||
HttpSendHeaders(ctx);
|
HttpSendHeaders(ctx);
|
||||||
JsonEncode(response, stream, JSON_DEFAULT);
|
JsonEncode(response, stream, JSON_DEFAULT);
|
||||||
JsonFree(response);
|
JsonFree(response);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
Log(LOG_DEBUG, "%s %s (%d)",
|
|
||||||
HttpRequestMethodToString(HttpRequestMethodGet(ctx)),
|
|
||||||
path, HttpResponseStatusGet(ctx)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClientContext *
|
HttpClientContext *
|
||||||
|
|
@ -73,7 +71,7 @@ ParseeCreateRequest(const ParseeConfig *conf, HttpRequestMethod meth, char *path
|
||||||
|
|
||||||
ctx = HttpRequest(
|
ctx = HttpRequest(
|
||||||
meth,
|
meth,
|
||||||
conf->homeserver_tls ? HTTP_FLAG_TLS : HTTP_FLAG_NONE,
|
HTTP_FLAG_TLS,
|
||||||
conf->homeserver_port, conf->homeserver_host,
|
conf->homeserver_port, conf->homeserver_host,
|
||||||
path
|
path
|
||||||
);
|
);
|
||||||
|
|
|
||||||
228
src/Main.c
228
src/Main.c
|
|
@ -1,6 +1,5 @@
|
||||||
#include <Cytoplasm/HttpServer.h>
|
#include <Cytoplasm/HttpServer.h>
|
||||||
#include <Cytoplasm/Cytoplasm.h>
|
#include <Cytoplasm/Cytoplasm.h>
|
||||||
#include <Cytoplasm/Platform.h>
|
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Cron.h>
|
#include <Cytoplasm/Cron.h>
|
||||||
|
|
@ -13,7 +12,6 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <StanzaBuilder.h>
|
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
@ -29,79 +27,6 @@ ParseeUptime(void)
|
||||||
return UtilTsMillis() - start;
|
return UtilTsMillis() - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Argument arguments[] =
|
|
||||||
{
|
|
||||||
#define Arg(c, req, vdesc, desc) \
|
|
||||||
{ \
|
|
||||||
.end = false, \
|
|
||||||
.argument = c, .value_req = req, \
|
|
||||||
.value_descr = vdesc, \
|
|
||||||
.description = desc \
|
|
||||||
},
|
|
||||||
#define EndOfArgs { .end = true }
|
|
||||||
|
|
||||||
Arg('H', true, "number(=8)", "Sets the number of HTTP threads")
|
|
||||||
Arg('J', true, "number(=8)", "Sets the number of XMPP threads")
|
|
||||||
Arg('C', true, "file(='parsee.json')", "Sets the JSON config to use")
|
|
||||||
|
|
||||||
Arg('g', false, NULL,
|
|
||||||
"Generates a parsee.yaml AS file before exiting")
|
|
||||||
Arg('v', false, NULL,
|
|
||||||
"Forces Parsee to print in a more verbose fashion "
|
|
||||||
"(-vvv prints stanzas to stderr)")
|
|
||||||
Arg('h', false, NULL,
|
|
||||||
"Generates an help screen(this one!)")
|
|
||||||
|
|
||||||
EndOfArgs
|
|
||||||
|
|
||||||
#undef EndOfArgs
|
|
||||||
#undef Argument
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
ParseeCheckMatrix(void *datp)
|
|
||||||
{
|
|
||||||
static volatile uint64_t streak = 0;
|
|
||||||
ParseeData *data = datp;
|
|
||||||
if (data->config->accept_pings && !ASPing(data->config))
|
|
||||||
{
|
|
||||||
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
|
|
||||||
if (++streak == 10)
|
|
||||||
{
|
|
||||||
DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats");
|
|
||||||
HashMap *json = DbJson(ref);
|
|
||||||
HashMap *mucs = GrabObject(json, 1, "mucs");
|
|
||||||
char *muc;
|
|
||||||
void *ignored;
|
|
||||||
|
|
||||||
|
|
||||||
/* Notify any potential MUCs about this */
|
|
||||||
while (HashMapIterate(mucs, &muc, &ignored))
|
|
||||||
{
|
|
||||||
char *id = StrRandom(32);
|
|
||||||
char *sender = StrConcat(3, "parsee@", data->jabber->host, "/parsee");
|
|
||||||
StanzaBuilder *b = CreateStanzaBuilder(sender, muc, id);
|
|
||||||
SetStanzaType(b, "groupchat");
|
|
||||||
SetStanzaBody(b,
|
|
||||||
"This bridge hasn't been able to reach the Matrix host, and "
|
|
||||||
"as such, some messages may not have been sent over."
|
|
||||||
);
|
|
||||||
|
|
||||||
WriteoutStanza(b, data->jabber, 0);
|
|
||||||
DestroyStanzaBuilder(b);
|
|
||||||
|
|
||||||
Free(sender);
|
|
||||||
Free(id);
|
|
||||||
}
|
|
||||||
(void) ignored;
|
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
streak = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
Main(Array *args, HashMap *env)
|
Main(Array *args, HashMap *env)
|
||||||
{
|
{
|
||||||
|
|
@ -110,11 +35,6 @@ Main(Array *args, HashMap *env)
|
||||||
Stream *yaml;
|
Stream *yaml;
|
||||||
Cron *cron = NULL;
|
Cron *cron = NULL;
|
||||||
|
|
||||||
char *configuration = "parsee.json";
|
|
||||||
int xmpp = 8;
|
|
||||||
int http = 8;
|
|
||||||
int verbose = 0;
|
|
||||||
|
|
||||||
start = UtilTsMillis();
|
start = UtilTsMillis();
|
||||||
|
|
||||||
memset(&conf, 0, sizeof(conf));
|
memset(&conf, 0, sizeof(conf));
|
||||||
|
|
@ -122,32 +42,24 @@ Main(Array *args, HashMap *env)
|
||||||
"%s - v%s[%s] (Cytoplasm %s)",
|
"%s - v%s[%s] (Cytoplasm %s)",
|
||||||
NAME, VERSION, CODE, CytoplasmGetVersionStr()
|
NAME, VERSION, CODE, CytoplasmGetVersionStr()
|
||||||
);
|
);
|
||||||
ParseePrintASCII();
|
|
||||||
Log(LOG_INFO, "=======================");
|
Log(LOG_INFO, "=======================");
|
||||||
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());
|
LogConfigIndent(LogConfigGlobal());
|
||||||
|
|
||||||
|
ParseeConfigLoad("parsee.json");
|
||||||
|
ParseeConfigInit();
|
||||||
|
parsee_conf = ParseeConfigGet();
|
||||||
|
|
||||||
{
|
{
|
||||||
ArgParseState state;
|
ArgParseState state;
|
||||||
char *opts = ParseeGenerateGetopt(arguments);
|
|
||||||
int flag;
|
int flag;
|
||||||
|
int xmpp = 8;
|
||||||
|
int http = 8;
|
||||||
|
|
||||||
ArgParseStateInit(&state);
|
ArgParseStateInit(&state);
|
||||||
while ((flag = ArgParse(&state, args, opts)) != -1)
|
while ((flag = ArgParse(&state, args, "gH:J:")) != -1)
|
||||||
{
|
{
|
||||||
switch (flag)
|
switch (flag)
|
||||||
{
|
{
|
||||||
case 'h':
|
|
||||||
ParseeGenerateHelp(arguments);
|
|
||||||
Free(opts);
|
|
||||||
goto end;
|
|
||||||
case 'H':
|
case 'H':
|
||||||
http = strtol(state.optArg, NULL, 10);
|
http = strtol(state.optArg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
|
|
@ -158,155 +70,46 @@ Main(Array *args, HashMap *env)
|
||||||
/* Write out the config file to a YAML document */
|
/* Write out the config file to a YAML document */
|
||||||
Log(LOG_INFO, "Generating YAML...");
|
Log(LOG_INFO, "Generating YAML...");
|
||||||
yaml = StreamOpen("parsee.yaml", "w");
|
yaml = StreamOpen("parsee.yaml", "w");
|
||||||
ParseeConfigLoad(configuration);
|
|
||||||
ParseeConfigInit();
|
|
||||||
ParseeExportConfigYAML(yaml);
|
ParseeExportConfigYAML(yaml);
|
||||||
StreamClose(yaml);
|
StreamClose(yaml);
|
||||||
Free(opts);
|
|
||||||
goto end;
|
|
||||||
case 'v':
|
|
||||||
switch (++verbose)
|
|
||||||
{
|
|
||||||
case PARSEE_VERBOSE_LOG:
|
|
||||||
LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG);
|
|
||||||
break;
|
|
||||||
case PARSEE_VERBOSE_TIMINGS:
|
|
||||||
Log(LOG_DEBUG, "Logging bench information.");
|
|
||||||
break;
|
|
||||||
case PARSEE_VERBOSE_STANZA:
|
|
||||||
Log(LOG_DEBUG, "Enabling stanza printing.");
|
|
||||||
break;
|
|
||||||
case PARSEE_VERBOSE_COMICAL:
|
|
||||||
Log(LOG_DEBUG, "What?");
|
|
||||||
Log(LOG_DEBUG, "No, but like, what do you except?");
|
|
||||||
Log(LOG_DEBUG, "Like do you want to log _every_ instruction?");
|
|
||||||
Log(LOG_DEBUG, "Like just every single thing %s does?", NAME);
|
|
||||||
Log(LOG_DEBUG, " ( why??? )");
|
|
||||||
Log(LOG_DEBUG, ".....................................");
|
|
||||||
Log(LOG_DEBUG, "Argh.");
|
|
||||||
Log(LOG_DEBUG, "Alright. I'll do my best.");
|
|
||||||
Log(LOG_DEBUG, "Get what you paid for.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'C':
|
|
||||||
if (!UtilLastModified(state.optArg))
|
|
||||||
{
|
|
||||||
Log(LOG_ERR, "Invalid config: %s", state.optArg);
|
|
||||||
Log(LOG_ERR, "Ignoring.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
configuration = state.optArg;
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
Log(LOG_ERR, "INVALID ARGUMENT GIVEN");
|
|
||||||
Free(opts);
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Free(opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "Loading configuration...");
|
|
||||||
}
|
|
||||||
ParseeConfigLoad(configuration);
|
|
||||||
ParseeConfigInit();
|
|
||||||
parsee_conf = ParseeConfigGet();
|
|
||||||
if (!parsee_conf)
|
|
||||||
{
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
ParseeSetThreads(xmpp, http);
|
ParseeSetThreads(xmpp, http);
|
||||||
|
}
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Connecting to XMPP...");
|
Log(LOG_NOTICE, "Connecting to XMPP...");
|
||||||
jabber = XMPPInitialiseCompStream(
|
jabber = XMPPInitialiseCompStream(
|
||||||
parsee_conf->component_addr,
|
|
||||||
parsee_conf->component_host,
|
parsee_conf->component_host,
|
||||||
parsee_conf->component_port
|
parsee_conf->component_port
|
||||||
);
|
);
|
||||||
Log(LOG_NOTICE, "Connecting to XMPP... %p", jabber);
|
|
||||||
if (!XMPPAuthenticateCompStream(
|
if (!XMPPAuthenticateCompStream(
|
||||||
jabber,
|
jabber,
|
||||||
parsee_conf->shared_comp_secret
|
parsee_conf->shared_comp_secret
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Could not connect to XMPP...");
|
Log(LOG_ERR, "Could not connect to XMPP...");
|
||||||
|
|
||||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "Destroying component...");
|
|
||||||
}
|
|
||||||
XMPPEndCompStream(jabber);
|
XMPPEndCompStream(jabber);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Creating volatile tables...");
|
Log(LOG_NOTICE, "Creating volatile tables...");
|
||||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "Initialising JID table");
|
|
||||||
}
|
|
||||||
ParseeInitialiseJIDTable();
|
ParseeInitialiseJIDTable();
|
||||||
|
|
||||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "Initialising OID table");
|
|
||||||
}
|
|
||||||
ParseeInitialiseOIDTable();
|
ParseeInitialiseOIDTable();
|
||||||
|
|
||||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "Initialising head table");
|
|
||||||
}
|
|
||||||
ParseeInitialiseHeadTable();
|
ParseeInitialiseHeadTable();
|
||||||
|
|
||||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
Log(LOG_NOTICE, "Setting up local Matrix user...");
|
||||||
{
|
ASRegisterUser(parsee_conf, parsee_conf->sender_localpart);
|
||||||
Log(LOG_DEBUG, "Initialising nick table");
|
|
||||||
}
|
|
||||||
ParseeInitialiseNickTable();
|
|
||||||
|
|
||||||
if (verbose >= PARSEE_VERBOSE_COMICAL)
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "Initialising affiliation table");
|
|
||||||
}
|
|
||||||
ParseeInitialiseAffiliationTable();
|
|
||||||
|
|
||||||
conf.port = parsee_conf->port;
|
conf.port = parsee_conf->port;
|
||||||
conf.threads = parsee_conf->http_threads;
|
conf.threads = parsee_conf->http_threads;
|
||||||
conf.maxConnections = conf.threads << 2;
|
conf.maxConnections = conf.threads << 2;
|
||||||
conf.handlerArgs = ParseeInitData(jabber);
|
conf.handlerArgs = ParseeInitData(jabber);
|
||||||
conf.handler = ParseeRequest;
|
conf.handler = ParseeRequest;
|
||||||
if (!conf.handlerArgs)
|
|
||||||
{
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log(LOG_DEBUG, "Verbosity level: %d", verbose);
|
|
||||||
((ParseeData *) conf.handlerArgs)->verbosity = verbose;
|
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Setting up local Matrix user...");
|
|
||||||
if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart))
|
|
||||||
{
|
|
||||||
char *parsee = ParseeMXID(conf.handlerArgs);
|
|
||||||
|
|
||||||
ASSetAvatar(parsee_conf,
|
|
||||||
parsee,
|
|
||||||
"mxc://tedomum.net/"
|
|
||||||
"7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136"
|
|
||||||
);
|
|
||||||
ASSetName(parsee_conf, parsee, "Parsee bridge");
|
|
||||||
|
|
||||||
Free(parsee);
|
|
||||||
}
|
|
||||||
|
|
||||||
Log(LOG_NOTICE, "Starting up local cronjobs...");
|
Log(LOG_NOTICE, "Starting up local cronjobs...");
|
||||||
cron = CronCreate(10 SECONDS);
|
cron = CronCreate( 10 SECONDS );
|
||||||
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
|
||||||
CronEvery(cron, 10 SECONDS, ParseeCheckMatrix, conf.handlerArgs);
|
|
||||||
ParseeCleanup(conf.handlerArgs);
|
|
||||||
|
|
||||||
CronStart(cron);
|
CronStart(cron);
|
||||||
|
|
||||||
|
|
@ -318,9 +121,8 @@ Main(Array *args, HashMap *env)
|
||||||
}
|
}
|
||||||
|
|
||||||
server = HttpServerCreate(&conf);
|
server = HttpServerCreate(&conf);
|
||||||
((ParseeData *) conf.handlerArgs)->server = server;
|
|
||||||
|
|
||||||
if (!ParseeInitialiseSignals(conf.handlerArgs, xmpp_thr))
|
if (!ParseeInitialiseSignals(server, xmpp_thr, jabber))
|
||||||
{
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
@ -344,12 +146,8 @@ end:
|
||||||
CronStop(cron);
|
CronStop(cron);
|
||||||
CronFree(cron);
|
CronFree(cron);
|
||||||
ParseeFreeData(conf.handlerArgs);
|
ParseeFreeData(conf.handlerArgs);
|
||||||
ParseeDestroyAffiliationTable();
|
|
||||||
ParseeDestroyNickTable();
|
|
||||||
ParseeDestroyOIDTable();
|
ParseeDestroyOIDTable();
|
||||||
ParseeDestroyHeadTable();
|
ParseeDestroyHeadTable();
|
||||||
ParseeDestroyJIDTable();
|
ParseeDestroyJIDTable();
|
||||||
|
|
||||||
(void) env;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,56 +8,9 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <StanzaBuilder.h>
|
#include <StanzaBuilder.h>
|
||||||
#include <Unistring.h>
|
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
static const char *
|
|
||||||
GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to);
|
|
||||||
|
|
||||||
static char *
|
|
||||||
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char *hash)
|
|
||||||
{
|
|
||||||
char *sender = GrabString(event, 1, "sender");
|
|
||||||
|
|
||||||
Unistr *uninick = UnistrCreate(name);
|
|
||||||
Unistr *filtered = UnistrFilter(uninick, UnistrIsASCII); /* I'm not even going to try messing with the BMP anymore. */
|
|
||||||
char *nick = UnistrC(filtered);
|
|
||||||
char *rev = StrConcat(3, muc, "/", nick);
|
|
||||||
int nonce = 0;
|
|
||||||
|
|
||||||
UnistrFree(uninick);
|
|
||||||
UnistrFree(filtered);
|
|
||||||
|
|
||||||
/* TODO: vCards! */
|
|
||||||
while (!XMPPJoinMUC(data->jabber, jid, rev, hash, -1, true) && nonce < 32)
|
|
||||||
{
|
|
||||||
char *nonce_str = StrInt(nonce);
|
|
||||||
char *input = StrConcat(3, sender, name, nonce_str);
|
|
||||||
char *hex = ParseeHMACS(data->id, input);
|
|
||||||
|
|
||||||
if (strlen(hex) >= 8)
|
|
||||||
{
|
|
||||||
hex[8] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
Free(nick);
|
|
||||||
Free(rev);
|
|
||||||
|
|
||||||
nick = StrConcat(4, name, "[", hex, "]");
|
|
||||||
rev = StrConcat(3, muc, "/", nick);
|
|
||||||
nonce++;
|
|
||||||
|
|
||||||
Free(nonce_str);
|
|
||||||
Free(input);
|
|
||||||
Free(hex);
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseePushNickTable(muc, sender, nick);
|
|
||||||
Free(nick);
|
|
||||||
return (rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ParseeMemberHandler(ParseeData *data, HashMap *event)
|
ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||||
{
|
{
|
||||||
|
|
@ -98,95 +51,20 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||||
else if (StrEquals(membership, "join") && !ParseeIsPuppet(conf, state_key))
|
else if (StrEquals(membership, "join") && !ParseeIsPuppet(conf, state_key))
|
||||||
{
|
{
|
||||||
char *jid = ParseeEncodeMXID(state_key);
|
char *jid = ParseeEncodeMXID(state_key);
|
||||||
char *sha = NULL, *mime = NULL;
|
|
||||||
char *avatar = ASGetAvatar(data->config, NULL, state_key);
|
|
||||||
char *url = ParseeToUnauth(data, avatar, NULL);
|
|
||||||
chat_id = ParseeGetFromRoomID(data, room_id);
|
chat_id = ParseeGetFromRoomID(data, room_id);
|
||||||
|
|
||||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
|
||||||
Free(avatar);
|
|
||||||
avatar = NULL;
|
|
||||||
if (chat_id)
|
if (chat_id)
|
||||||
{
|
{
|
||||||
char *muc = ParseeGetMUCID(data, chat_id);
|
char *muc = ParseeGetMUCID(data, chat_id);
|
||||||
char *name = ASGetName(data->config, room_id, state_key);
|
char *rev = StrConcat(2, muc, "/parsee");
|
||||||
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);
|
|
||||||
|
|
||||||
Free(jabber);
|
XMPPJoinMUC(data->jabber, jid, rev);
|
||||||
Free(avatar);
|
Free(rev);
|
||||||
Free(name);
|
|
||||||
Free(muc);
|
Free(muc);
|
||||||
avatar = NULL;
|
|
||||||
|
|
||||||
/* TODO: XEP-0084 magic to advertise a new avatar if possible. */
|
/* TODO: XEP-0084 magic to advertise a new avatar if possible. */
|
||||||
}
|
}
|
||||||
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(jid);
|
||||||
Free(url);
|
Free(chat_id);
|
||||||
}
|
}
|
||||||
else if ((StrEquals(membership, "leave") ||
|
else if ((StrEquals(membership, "leave") ||
|
||||||
StrEquals(membership, "ban"))
|
StrEquals(membership, "ban"))
|
||||||
|
|
@ -204,29 +82,14 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
|
||||||
muc_id = ParseeGetMUCID(data, chat_id);
|
muc_id = ParseeGetMUCID(data, chat_id);
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
{
|
{
|
||||||
/* If it can't be found, try to see if it's as a DM */
|
|
||||||
char *info_from = NULL, *info_to = NULL;
|
|
||||||
const char *type = GetXMPPInformation(data, event, &info_from, &info_to);
|
|
||||||
|
|
||||||
if (StrEquals(type, "chat"))
|
|
||||||
{
|
|
||||||
char *jid_to = ParseeTrimJID(info_to);
|
|
||||||
Log(LOG_DEBUG, "('%s'->'%s') is gone.", state_key, info_to);
|
|
||||||
/* TODO: Send a last DM, signifying that all is gone. */
|
|
||||||
ParseeDeleteDM(data, state_key, jid_to);
|
|
||||||
Free(jid_to);
|
|
||||||
}
|
|
||||||
|
|
||||||
Free(info_from);
|
|
||||||
Free(info_to);
|
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
name = StrDuplicate(ParseeLookupNick(muc_id, sender));
|
/* TODO: Check the name's validity */
|
||||||
rev = StrConcat(3, muc_id, "/", name);
|
name = ASGetName(data->config, room_id, state_key);
|
||||||
|
rev = StrConcat(4, muc_id, "/", name, "[p]");
|
||||||
|
|
||||||
XMPPLeaveMUC(jabber, jid, rev, reason);
|
XMPPLeaveMUC(jabber, jid, rev, reason);
|
||||||
ParseePushNickTable(muc_id, sender, NULL);
|
|
||||||
end:
|
end:
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
Free(muc_id);
|
Free(muc_id);
|
||||||
|
|
@ -255,9 +118,14 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!body || *body != '!')
|
if (*body != '!')
|
||||||
{
|
{
|
||||||
/* All commands are to be marked with a ! */
|
/* All commands are to be marked with a ! */
|
||||||
|
Free(ASSend(
|
||||||
|
data->config, id, profile,
|
||||||
|
"m.room.message",
|
||||||
|
MatrixCreateNotice("Please enter a valid command")
|
||||||
|
));
|
||||||
Free(profile);
|
Free(profile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -267,7 +135,7 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
|
||||||
Free(ASSend(
|
Free(ASSend(
|
||||||
data->config, id, profile,
|
data->config, id, profile,
|
||||||
"m.room.message",
|
"m.room.message",
|
||||||
MatrixCreateNotice("You are not authorised to do this."), 0
|
MatrixCreateNotice("You are not authorised to do this.")
|
||||||
));
|
));
|
||||||
Free(profile);
|
Free(profile);
|
||||||
return;
|
return;
|
||||||
|
|
@ -289,10 +157,12 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
char *room_id = GrabString(event, 1, "room_id");
|
char *room_id = GrabString(event, 1, "room_id");
|
||||||
char *matrix_sender = GrabString(event, 1, "sender");
|
char *matrix_sender = GrabString(event, 1, "sender");
|
||||||
char *chat_id = NULL, *muc_id = NULL;
|
char *chat_id = NULL, *muc_id = NULL;
|
||||||
char *user = NULL;
|
char *user;
|
||||||
|
|
||||||
DbRef *room_data = NULL;
|
DbRef *room_data;
|
||||||
HashMap *data_json = NULL;
|
HashMap *data_json;
|
||||||
|
|
||||||
|
XMPPComponent *jabber = data ? data->jabber : NULL;
|
||||||
|
|
||||||
bool direct = false;
|
bool direct = false;
|
||||||
if (!data || !event || !from || !to)
|
if (!data || !event || !from || !to)
|
||||||
|
|
@ -324,8 +194,7 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *matrix_name = NULL, *matrix_avatar = NULL;
|
char *matrix_name, *muc_join_as;
|
||||||
char *mime = NULL, *sha = NULL;
|
|
||||||
|
|
||||||
muc_id = ParseeGetMUCID(data, chat_id);
|
muc_id = ParseeGetMUCID(data, chat_id);
|
||||||
if (!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_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;
|
*to = muc_id;
|
||||||
|
|
||||||
Free(matrix_avatar);
|
|
||||||
Free(matrix_name);
|
Free(matrix_name);
|
||||||
Free(mime);
|
Free(muc_join_as);
|
||||||
Free(sha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
|
|
@ -358,47 +229,27 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
|
||||||
static void
|
static void
|
||||||
ParseeMessageHandler(ParseeData *data, HashMap *event)
|
ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
{
|
{
|
||||||
if (!data || !event)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
XMPPComponent *jabber = data->jabber;
|
XMPPComponent *jabber = data->jabber;
|
||||||
StanzaBuilder *builder = NULL;
|
StanzaBuilder *builder;
|
||||||
DbRef *ref = NULL;
|
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 *body = GrabString(event, 2, "content", "body");
|
||||||
char *id = GrabString(event, 1, "room_id");
|
char *id = GrabString(event, 1, "room_id");
|
||||||
char *ev_id = GrabString(event, 1, "event_id");
|
char *ev_id = GrabString(event, 1, "event_id");
|
||||||
char *chat_id = NULL, *muc_id = NULL;
|
char *m_sender = GrabString(event, 1, "sender");
|
||||||
|
char *chat_id, *muc_id;
|
||||||
char *reply_id = MatrixGetReply(event);
|
char *reply_id = MatrixGetReply(event);
|
||||||
char *xepd = ParseeXMPPify(data, event);
|
char *xepd = ParseeXMPPify(event);
|
||||||
char *type, *user, *xmppified_user = NULL, *to = NULL;
|
char *type, *user, *xmppified_user = NULL, *to = NULL;
|
||||||
char *unauth = NULL;
|
char *unauth = NULL;
|
||||||
char *origin_id = NULL, *stanza = NULL;
|
char *origin_id = NULL, *stanza = NULL;
|
||||||
char *sender = NULL;
|
char *sender = NULL;
|
||||||
|
char *unedited_id = MatrixGetEdit(event);
|
||||||
char *url = GrabString(event, 2, "content", "url");
|
char *url = GrabString(event, 2, "content", "url");
|
||||||
char *encoded_from = NULL;
|
|
||||||
|
|
||||||
bool direct = false;
|
bool direct = false;
|
||||||
unedited_id = MatrixGetEdit(event);
|
|
||||||
|
|
||||||
if (unedited_id)
|
|
||||||
{
|
|
||||||
char *new_content = GrabString(event, 3, "content", "m.new_content", "body");
|
|
||||||
if (new_content) body = new_content;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data->config->ignore_bots && StrEquals(msgtype, "m.notice"))
|
|
||||||
{
|
|
||||||
Free(reply_id);
|
|
||||||
Free(xepd);
|
|
||||||
Free(unedited_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ParseeIsPuppet(data->config, m_sender) ||
|
if (ParseeIsPuppet(data->config, m_sender) ||
|
||||||
ParseeManageBan(data, m_sender, id))
|
ParseeManageBan(data, m_sender, id))
|
||||||
{
|
{
|
||||||
|
|
@ -410,6 +261,9 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
|
|
||||||
chat_id = ParseeGetFromRoomID(data, id);
|
chat_id = ParseeGetFromRoomID(data, id);
|
||||||
|
|
||||||
|
/* TODO: This ref should be marked as read-only,
|
||||||
|
* as LMDB doesn't seem to like having two concurrent RW
|
||||||
|
* transactions running. */
|
||||||
ref = DbLockIntent(data->db, DB_HINT_READONLY, 3, "rooms", id, "data");
|
ref = DbLockIntent(data->db, DB_HINT_READONLY, 3, "rooms", id, "data");
|
||||||
json = DbJson(ref);
|
json = DbJson(ref);
|
||||||
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
|
direct = JsonValueAsBoolean(HashMapGet(json, "is_direct"));
|
||||||
|
|
@ -429,44 +283,38 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
|
|
||||||
type = direct ? "chat" : "groupchat";
|
type = direct ? "chat" : "groupchat";
|
||||||
user = GrabString(json, 1, "xmpp_user");
|
user = GrabString(json, 1, "xmpp_user");
|
||||||
unauth = ParseeToUnauth(data, url, GrabString(event, 2, "content", "filename"));
|
unauth = ParseeToUnauth(data, url);
|
||||||
|
|
||||||
encoded_from = ParseeEncodeMXID(m_sender);
|
|
||||||
xmppified_user = StrConcat(3,
|
|
||||||
encoded_from, "@", jabber->host
|
|
||||||
);
|
|
||||||
if (direct)
|
if (direct)
|
||||||
{
|
{
|
||||||
|
xmppified_user = ParseeEncodeMXID(m_sender);
|
||||||
to = StrDuplicate(user);
|
to = StrDuplicate(user);
|
||||||
|
|
||||||
Free(chat_id);
|
Free(chat_id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *name, *mime = NULL, *sha = NULL;
|
char *name, *rev;
|
||||||
char *avatar;
|
|
||||||
/* Try to find the chat ID */
|
/* Try to find the chat ID */
|
||||||
muc_id = ParseeGetMUCID(data, chat_id);
|
muc_id = ParseeGetMUCID(data, chat_id);
|
||||||
if (!chat_id)
|
if (!chat_id)
|
||||||
{
|
{
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
xmppified_user = ParseeEncodeMXID(m_sender);
|
||||||
|
|
||||||
/* TODO: 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);
|
name = ASGetName(data->config, id, m_sender);
|
||||||
avatar = ASGetAvatar(data->config, NULL, m_sender);
|
rev = StrConcat(4, muc_id, "/", name, "[p]");
|
||||||
ASGetMIMESHA(data->config, avatar, &mime, &sha);
|
|
||||||
|
|
||||||
Free(JoinMUC(data, event, encoded_from, muc_id, name, sha));
|
XMPPJoinMUC(jabber, xmppified_user, rev);
|
||||||
|
|
||||||
to = muc_id;
|
to = muc_id;
|
||||||
|
|
||||||
Free(sha);
|
|
||||||
Free(mime);
|
|
||||||
Free(name);
|
Free(name);
|
||||||
Free(avatar);
|
Free(rev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reply_id)
|
if (reply_id)
|
||||||
{
|
{
|
||||||
/* TODO: Monocles chat DM users HATE this trick!
|
/* TODO: Monocles chat DM users HATE this trick!
|
||||||
|
|
@ -494,13 +342,13 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
|
||||||
char *xmpp_ident = StrRandom(32);
|
char *xmpp_ident = StrRandom(32);
|
||||||
builder = CreateStanzaBuilder(xmppified_user, to, xmpp_ident);
|
builder = CreateStanzaBuilder(xmppified_user, to, xmpp_ident);
|
||||||
SetStanzaType(builder, type);
|
SetStanzaType(builder, type);
|
||||||
SetStanzaBody(builder, unauth ? unauth : (xepd ? xepd : body));
|
SetStanzaBody(builder, xepd ? xepd : body);
|
||||||
SetStanzaReply(builder, stanza, sender);
|
SetStanzaReply(builder, stanza, sender);
|
||||||
SetStanzaLink(builder, unauth);
|
SetStanzaLink(builder, unauth);
|
||||||
SetStanzaEdit(builder, origin_id);
|
SetStanzaEdit(builder, origin_id);
|
||||||
SetStanzaXParsee(builder, event);
|
SetStanzaXParsee(builder, event);
|
||||||
|
|
||||||
WriteoutStanza(builder, jabber, data->config->max_stanza_size);
|
WriteoutStanza(builder, jabber);
|
||||||
DestroyStanzaBuilder(builder);
|
DestroyStanzaBuilder(builder);
|
||||||
|
|
||||||
if (direct)
|
if (direct)
|
||||||
|
|
@ -523,7 +371,6 @@ end:
|
||||||
Free(stanza);
|
Free(stanza);
|
||||||
Free(sender);
|
Free(sender);
|
||||||
Free(unauth);
|
Free(unauth);
|
||||||
Free(encoded_from);
|
|
||||||
Free(unedited_id);
|
Free(unedited_id);
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
|
|
@ -555,7 +402,8 @@ ParseeEventHandler(ParseeData *data, HashMap *event)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (StrEquals(event_type, "m.room.message") ||
|
else if (StrEquals(event_type, "m.room.message") ||
|
||||||
StrEquals(event_type, "m.sticker"))
|
StrEquals(event_type, "m.sticker")) /* TODO: Actual sticker
|
||||||
|
* support here... */
|
||||||
{
|
{
|
||||||
ParseeMessageHandler(data, event);
|
ParseeMessageHandler(data, event);
|
||||||
Free(parsee);
|
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 ParseeConfig *config = NULL;
|
||||||
|
|
||||||
|
static char *
|
||||||
|
GetLine(void)
|
||||||
|
{
|
||||||
|
Stream *input = StreamStdin();
|
||||||
|
char *out = NULL;
|
||||||
|
size_t length;
|
||||||
|
UtilGetLine(&out, &length, input);
|
||||||
|
|
||||||
|
if (out)
|
||||||
|
{
|
||||||
|
char *line = strchr(out, '\n');
|
||||||
|
if (line)
|
||||||
|
{
|
||||||
|
*line = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
static char *
|
||||||
|
PromptString(const char *expression, const char *def, ...)
|
||||||
|
{
|
||||||
|
Stream *output = StreamStdout();
|
||||||
|
char *out = NULL;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
while (!out)
|
||||||
|
{
|
||||||
|
va_start(ap, def);
|
||||||
|
|
||||||
|
StreamVprintf(output, expression, ap);
|
||||||
|
if (def)
|
||||||
|
{
|
||||||
|
StreamPrintf(output, " [%s]", def);
|
||||||
|
}
|
||||||
|
StreamPrintf(output, ": ");
|
||||||
|
StreamFlush(output);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
out = GetLine();
|
||||||
|
if (!*out)
|
||||||
|
{
|
||||||
|
Free(out);
|
||||||
|
out = NULL;
|
||||||
|
if (def)
|
||||||
|
{
|
||||||
|
return StrDuplicate(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log(LOG_INFO, "R=%s", out);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
static int
|
||||||
|
PromptInteger(const char *expression, int def, ...)
|
||||||
|
{
|
||||||
|
Stream *output = StreamStdout();
|
||||||
|
char *out;
|
||||||
|
long l;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, def);
|
||||||
|
|
||||||
|
StreamVprintf(output, expression, ap);
|
||||||
|
if (def >= 0)
|
||||||
|
{
|
||||||
|
StreamPrintf(output, " [%d]", def);
|
||||||
|
}
|
||||||
|
StreamPrintf(output, ": ");
|
||||||
|
StreamFlush(output);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
char *inval;
|
||||||
|
out = GetLine();
|
||||||
|
l = strtol(out, &inval, 10);
|
||||||
|
Free(out);
|
||||||
|
|
||||||
|
/* Not a use-after-free, as we reference only the addresses. */
|
||||||
|
if (l != 0 || inval != out)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (def >= 0)
|
||||||
|
{
|
||||||
|
return def;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Memleaks, galore! */
|
||||||
void
|
void
|
||||||
ParseeConfigInit(void)
|
ParseeConfigInit(void)
|
||||||
{
|
{
|
||||||
|
Stream *stream;
|
||||||
|
HashMap *json;
|
||||||
if (config)
|
if (config)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log(LOG_ERR, "No config file found.");
|
|
||||||
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
|
void
|
||||||
ParseeConfigLoad(char *conf)
|
ParseeConfigLoad(char *conf)
|
||||||
|
|
@ -30,18 +225,12 @@ ParseeConfigLoad(char *conf)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stream = StreamOpen(conf ? conf : "parsee.json", "r");
|
stream = StreamOpen("parsee.json", "r");
|
||||||
if (!stream)
|
if (!stream)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
json = JsonDecode(stream);
|
json = JsonDecode(stream);
|
||||||
if (!json)
|
|
||||||
{
|
|
||||||
Log(LOG_ERR, "Could not parse config JSON");
|
|
||||||
StreamClose(stream);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
config = Malloc(sizeof(*config));
|
config = Malloc(sizeof(*config));
|
||||||
#define CopyToStr(to, str) config->to = StrDuplicate( \
|
#define CopyToStr(to, str) config->to = StrDuplicate( \
|
||||||
|
|
@ -50,9 +239,6 @@ ParseeConfigLoad(char *conf)
|
||||||
#define CopyToInt(to, str) config->to = (int) ( \
|
#define CopyToInt(to, str) config->to = (int) ( \
|
||||||
JsonValueAsInteger(HashMapGet(json, str)) \
|
JsonValueAsInteger(HashMapGet(json, str)) \
|
||||||
)
|
)
|
||||||
#define CopyToBool(to, str) config->to = (int) ( \
|
|
||||||
JsonValueAsBoolean(HashMapGet(json, str)) \
|
|
||||||
)
|
|
||||||
|
|
||||||
config->http_threads = 8;
|
config->http_threads = 8;
|
||||||
config->xmpp_threads = 8;
|
config->xmpp_threads = 8;
|
||||||
|
|
@ -67,25 +253,10 @@ ParseeConfigLoad(char *conf)
|
||||||
CopyToStr(server_base, "hs_base");
|
CopyToStr(server_base, "hs_base");
|
||||||
CopyToStr(homeserver_host, "hs_host");
|
CopyToStr(homeserver_host, "hs_host");
|
||||||
CopyToInt(homeserver_port, "hs_port");
|
CopyToInt(homeserver_port, "hs_port");
|
||||||
CopyToBool(homeserver_tls, "hs_tls");
|
|
||||||
if (!HashMapGet(json, "hs_tls"))
|
|
||||||
{
|
|
||||||
config->homeserver_tls = true;
|
|
||||||
}
|
|
||||||
CopyToBool(accept_pings, "accept_pings");
|
|
||||||
|
|
||||||
CopyToInt(component_port, "component_port");
|
CopyToInt(component_port, "component_port");
|
||||||
CopyToStr(component_addr, "component_addr");
|
|
||||||
CopyToStr(component_host, "component_host");
|
CopyToStr(component_host, "component_host");
|
||||||
CopyToStr(shared_comp_secret, "shared_secret");
|
CopyToStr(shared_comp_secret, "shared_secret");
|
||||||
CopyToInt(max_stanza_size, "max_stanza_size");
|
|
||||||
if (!config->max_stanza_size)
|
|
||||||
{
|
|
||||||
/* Standard XMPP "minimum" maximum */
|
|
||||||
config->max_stanza_size = 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
CopyToBool(ignore_bots, "ignore_bots");
|
|
||||||
|
|
||||||
CopyToStr(media_base, "media_base");
|
CopyToStr(media_base, "media_base");
|
||||||
|
|
||||||
|
|
@ -101,7 +272,6 @@ ParseeSetThreads(int xmpp, int http)
|
||||||
{
|
{
|
||||||
if (!config)
|
if (!config)
|
||||||
{
|
{
|
||||||
Achievement("THREAD COUNT REQUEST WITHOUT CONFIG", true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config->http_threads = http;
|
config->http_threads = http;
|
||||||
|
|
@ -113,7 +283,6 @@ ParseeExportConfigYAML(Stream *stream)
|
||||||
{
|
{
|
||||||
if (!stream || !config)
|
if (!stream || !config)
|
||||||
{
|
{
|
||||||
Achievement("YAML EXPORT REQUEST WITHOUT CONFIG", true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
StreamPrintf(stream, "# Autogenerated YAML AS entry for %s\n", NAME);
|
StreamPrintf(stream, "# Autogenerated YAML AS entry for %s\n", NAME);
|
||||||
|
|
@ -127,7 +296,6 @@ ParseeExportConfigYAML(Stream *stream)
|
||||||
StreamPrintf(stream, "hs_token: \"%s\"\n", config->hs_token);
|
StreamPrintf(stream, "hs_token: \"%s\"\n", config->hs_token);
|
||||||
StreamPrintf(stream, "sender_localpart: \"%s\"\n", config->sender_localpart);
|
StreamPrintf(stream, "sender_localpart: \"%s\"\n", config->sender_localpart);
|
||||||
StreamPrintf(stream, "protocols: [\"xmpp\", \"jabber\"]\n");
|
StreamPrintf(stream, "protocols: [\"xmpp\", \"jabber\"]\n");
|
||||||
StreamPrintf(stream, "receive_ephemeral: true\n"); /* TODO: Actually use that field */
|
|
||||||
StreamPrintf(stream, "\n");
|
StreamPrintf(stream, "\n");
|
||||||
StreamPrintf(stream, "namespaces: \n");
|
StreamPrintf(stream, "namespaces: \n");
|
||||||
StreamPrintf(stream, " users:\n");
|
StreamPrintf(stream, " users:\n");
|
||||||
|
|
@ -136,7 +304,6 @@ ParseeExportConfigYAML(Stream *stream)
|
||||||
StreamPrintf(stream, " aliases:\n");
|
StreamPrintf(stream, " aliases:\n");
|
||||||
StreamPrintf(stream, " - exclusive: true\n");
|
StreamPrintf(stream, " - exclusive: true\n");
|
||||||
StreamPrintf(stream, " regex: \"#%s_.*\"\n", config->namespace_base);
|
StreamPrintf(stream, " regex: \"#%s_.*\"\n", config->namespace_base);
|
||||||
StreamFlush(stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -147,7 +314,6 @@ ParseeConfigFree(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Free(config->component_host);
|
Free(config->component_host);
|
||||||
Free(config->component_addr);
|
|
||||||
Free(config->shared_comp_secret);
|
Free(config->shared_comp_secret);
|
||||||
Free(config->db_path);
|
Free(config->db_path);
|
||||||
Free(config->homeserver_host);
|
Free(config->homeserver_host);
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,17 @@
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <Routes.h>
|
#include <Routes.h>
|
||||||
|
#include <Glob.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
ParseeData *
|
ParseeData *
|
||||||
ParseeInitData(XMPPComponent *comp)
|
ParseeInitData(XMPPComponent *comp)
|
||||||
{
|
{
|
||||||
char *version;
|
|
||||||
ParseeData *data;
|
ParseeData *data;
|
||||||
DbRef *ref;
|
|
||||||
if (!ParseeConfigGet())
|
if (!ParseeConfigGet())
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -27,15 +26,8 @@ ParseeInitData(XMPPComponent *comp)
|
||||||
data->config = ParseeConfigGet();
|
data->config = ParseeConfigGet();
|
||||||
data->router = HttpRouterCreate();
|
data->router = HttpRouterCreate();
|
||||||
data->jabber = comp;
|
data->jabber = comp;
|
||||||
data->muc = CreateMUCServer(data);
|
|
||||||
data->handler = CommandCreateRouter();
|
data->handler = CommandCreateRouter();
|
||||||
|
|
||||||
data->oid_servers = HashMapCreate();
|
|
||||||
pthread_mutex_init(&data->oidl, NULL);
|
|
||||||
|
|
||||||
data->halted = false;
|
|
||||||
pthread_mutex_init(&data->halt_lock, NULL);
|
|
||||||
|
|
||||||
if (data->config->db_size)
|
if (data->config->db_size)
|
||||||
{
|
{
|
||||||
data->db = DbOpenLMDB(data->config->db_path, data->config->db_size);
|
data->db = DbOpenLMDB(data->config->db_path, data->config->db_size);
|
||||||
|
|
@ -43,47 +35,10 @@ ParseeInitData(XMPPComponent *comp)
|
||||||
if (!data->db)
|
if (!data->db)
|
||||||
{
|
{
|
||||||
Log(LOG_WARNING, "LMDB doesn't seem to be setup.");
|
Log(LOG_WARNING, "LMDB doesn't seem to be setup.");
|
||||||
if (data->config->db_size)
|
|
||||||
{
|
|
||||||
Log(LOG_WARNING, "Falling back to flat-file.");
|
Log(LOG_WARNING, "Falling back to flat-file.");
|
||||||
}
|
|
||||||
data->db = DbOpen(data->config->db_path, 0);
|
data->db = DbOpen(data->config->db_path, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(ref = DbLock(data->db, 1, "info")))
|
|
||||||
{
|
|
||||||
char *id = StrRandom(64);
|
|
||||||
ref = DbCreate(data->db, 1, "info");
|
|
||||||
HashMapSet(DbJson(ref), "identifier", JsonValueString(id));
|
|
||||||
HashMapSet(DbJson(ref), "version", JsonValueString(VERSION));
|
|
||||||
Free(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
version = GrabString(DbJson(ref), 1, "version");
|
|
||||||
if (version && !ParseeIsCompatible(VERSION, version))
|
|
||||||
{
|
|
||||||
Log(LOG_WARNING, "Version mismatch(curr=%s db=%s).", VERSION, version);
|
|
||||||
Log(LOG_WARNING, "Yeah. You may want to _not_ do that.");
|
|
||||||
Log(LOG_WARNING, "(Parsee still needs an upgradepath mechanism.)");
|
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
DbClose(data->db);
|
|
||||||
|
|
||||||
HashMapFree(data->oid_servers);
|
|
||||||
pthread_mutex_destroy(&data->oidl);
|
|
||||||
|
|
||||||
XMPPEndCompStream(data->jabber);
|
|
||||||
|
|
||||||
HttpRouterFree(data->router);
|
|
||||||
CommandFreeRouter(data->handler);
|
|
||||||
|
|
||||||
Free(data);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->id = StrDuplicate(GrabString(DbJson(ref), 1, "identifier"));
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
|
|
||||||
#define X_ROUTE(path, func) do {\
|
#define X_ROUTE(path, func) do {\
|
||||||
if (!HttpRouterAdd(data->router, path, func))\
|
if (!HttpRouterAdd(data->router, path, func))\
|
||||||
{\
|
{\
|
||||||
|
|
@ -101,23 +56,12 @@ ParseeInitData(XMPPComponent *comp)
|
||||||
void
|
void
|
||||||
ParseeFreeData(ParseeData *data)
|
ParseeFreeData(ParseeData *data)
|
||||||
{
|
{
|
||||||
char *entity;
|
|
||||||
XMLElement *disco;
|
|
||||||
if (!data)
|
if (!data)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (HashMapIterate(data->oid_servers, &entity, (void **) &disco))
|
|
||||||
{
|
|
||||||
XMLFreeElement(disco);
|
|
||||||
}
|
|
||||||
HashMapFree(data->oid_servers);
|
|
||||||
pthread_mutex_destroy(&data->oidl);
|
|
||||||
pthread_mutex_destroy(&data->halt_lock);
|
|
||||||
Free(data->id);
|
|
||||||
XMPPEndCompStream(data->jabber);
|
XMPPEndCompStream(data->jabber);
|
||||||
FreeMUCServer(data->muc);
|
|
||||||
DbClose(data->db);
|
DbClose(data->db);
|
||||||
HttpRouterFree(data->router);
|
HttpRouterFree(data->router);
|
||||||
CommandFreeRouter(data->handler);
|
CommandFreeRouter(data->handler);
|
||||||
|
|
@ -133,6 +77,8 @@ ParseeCleanup(void *datp)
|
||||||
size_t i;
|
size_t i;
|
||||||
uint64_t ts = UtilTsMillis();
|
uint64_t ts = UtilTsMillis();
|
||||||
|
|
||||||
|
Log(LOG_NOTICE, "Cleaning up...");
|
||||||
|
|
||||||
chats = DbList(data->db, 1, "chats");
|
chats = DbList(data->db, 1, "chats");
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(chats); i++)
|
for (i = 0; i < ArraySize(chats); i++)
|
||||||
|
|
@ -182,12 +128,9 @@ ParseeCleanup(void *datp)
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
/* TODO: Custom retention period for any 1.0 */
|
CleanupField(stanza, 30 MINUTES, 50);
|
||||||
CleanupField(stanza, 30 MINUTES, 500);
|
CleanupField(event, 30 MINUTES, 50);
|
||||||
CleanupField(event, 30 MINUTES, 500);
|
CleanupField(id, 30 MINUTES, 50);
|
||||||
CleanupField(id, 30 MINUTES, 500);
|
|
||||||
|
|
||||||
/* TODO: Also cleanup user cache information */
|
|
||||||
#undef CleanupField
|
#undef CleanupField
|
||||||
}
|
}
|
||||||
DbListFree(chats);
|
DbListFree(chats);
|
||||||
|
|
@ -238,15 +181,252 @@ ParseeCleanup(void *datp)
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
CleanupField(stanza, 3 HOURS, 500);
|
CleanupField(stanza, 3 HOURS, 50);
|
||||||
CleanupField(event, 3 HOURS, 500);
|
CleanupField(event, 3 HOURS, 50);
|
||||||
CleanupField(id, 3 HOURS, 500);
|
CleanupField(id, 3 HOURS, 50);
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
DbListFree(chats);
|
DbListFree(chats);
|
||||||
}
|
}
|
||||||
|
int
|
||||||
|
ParseeFindDatastart(char *data)
|
||||||
|
{
|
||||||
|
char *startline;
|
||||||
|
bool found = false;
|
||||||
|
if (!data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
startline = data;
|
||||||
|
while (startline)
|
||||||
|
{
|
||||||
|
char *endline = strchr(startline, '\n');
|
||||||
|
|
||||||
|
if (*startline != '>')
|
||||||
|
{
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
startline = endline ? endline + 1 : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int) (startline - data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <StringStream.h>
|
||||||
|
typedef struct XMPPFlags {
|
||||||
|
bool quote;
|
||||||
|
} XMPPFlags;
|
||||||
|
static char *
|
||||||
|
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
|
||||||
|
{
|
||||||
|
char *xepd = NULL, *tmp = NULL;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
XMLElement *child;
|
||||||
|
char *reply_id = JsonValueAsString(
|
||||||
|
JsonGet(event, 4,
|
||||||
|
"content", "m.relates_to", "m.in_reply_to", "event_id"
|
||||||
|
));
|
||||||
|
char *room_id = JsonValueAsString(HashMapGet(event, "room_id"));
|
||||||
|
HashMap *referenced;
|
||||||
|
char *subxep;
|
||||||
|
#define Concat(strp) do \
|
||||||
|
{ \
|
||||||
|
size_t cidx; \
|
||||||
|
size_t len = strp ? strlen(strp) : 0; \
|
||||||
|
for (cidx = 0; cidx < len; cidx++) \
|
||||||
|
{ \
|
||||||
|
char cch[2] = { strp[cidx], 0 }; \
|
||||||
|
char nch = *cch ? strp[cidx+1] : '\0'; \
|
||||||
|
bool c = *cch == '\n' && nch != '>'; \
|
||||||
|
if (c && flags.quote) \
|
||||||
|
{ \
|
||||||
|
tmp = xepd; \
|
||||||
|
xepd = StrConcat(2, xepd, "\n>"); \
|
||||||
|
Free(tmp); \
|
||||||
|
continue; \
|
||||||
|
} \
|
||||||
|
tmp = xepd; \
|
||||||
|
xepd = StrConcat(2, xepd, cch); \
|
||||||
|
Free(tmp); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
while (0)
|
||||||
|
switch (elem->type)
|
||||||
|
{
|
||||||
|
case XML_ELEMENT_DATA:
|
||||||
|
Concat(elem->data);
|
||||||
|
break;
|
||||||
|
case XML_ELEMENT_TAG:
|
||||||
|
if (StrEquals(elem->name, "b") || StrEquals(elem->name, "strong"))
|
||||||
|
{
|
||||||
|
Concat("*");
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat("*");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "em"))
|
||||||
|
{
|
||||||
|
Concat("_");
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat("_");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "code"))
|
||||||
|
{
|
||||||
|
Concat("`");
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat("`");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "mx-reply"))
|
||||||
|
{
|
||||||
|
char *str;
|
||||||
|
referenced = ASFind(ParseeConfigGet(), room_id, reply_id);
|
||||||
|
str = JsonValueAsString(
|
||||||
|
JsonGet(referenced, 2, "content", "body")
|
||||||
|
);
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
JsonFree(referenced);
|
||||||
|
return xepd;
|
||||||
|
}
|
||||||
|
Concat(">");
|
||||||
|
flags.quote = true;
|
||||||
|
Concat(str);
|
||||||
|
flags.quote = false;
|
||||||
|
Concat("\n");
|
||||||
|
JsonFree(referenced);
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "blockquote"))
|
||||||
|
{
|
||||||
|
Concat(">");
|
||||||
|
flags.quote = true;
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
flags.quote = false;
|
||||||
|
Concat("\n");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "br"))
|
||||||
|
{
|
||||||
|
Concat("\n");
|
||||||
|
/* HTML fucking SUCKS */
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat("\n");
|
||||||
|
}
|
||||||
|
else if (StrEquals(elem->name, "a"))
|
||||||
|
{
|
||||||
|
char *href = HashMapGet(elem->attrs, "href");
|
||||||
|
Concat("(");
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
Concat(" points to ");
|
||||||
|
Concat(href);
|
||||||
|
Concat(" )");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < ArraySize(elem->children); i++)
|
||||||
|
{
|
||||||
|
child = ArrayGet(elem->children, i);
|
||||||
|
subxep = XMPPifyElement(event, child, flags);
|
||||||
|
|
||||||
|
Concat(subxep);
|
||||||
|
Free(subxep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return xepd;
|
||||||
|
}
|
||||||
|
char *
|
||||||
|
ParseeXMPPify(HashMap *event)
|
||||||
|
{
|
||||||
|
char *type, *format, *html;
|
||||||
|
char *xepd = NULL;
|
||||||
|
XMLElement *elem;
|
||||||
|
|
||||||
|
XMPPFlags flags;
|
||||||
|
if (!event)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if it is a message event. */
|
||||||
|
type = JsonValueAsString(HashMapGet(event, "type"));
|
||||||
|
if (!StrEquals(type, "m.room.message"))
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
|
||||||
|
if (!StrEquals(format, "org.matrix.custom.html"))
|
||||||
|
{
|
||||||
|
/* Settle for the raw body instead. */
|
||||||
|
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
|
||||||
|
return StrDuplicate(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
|
||||||
|
html = StrConcat(3, "<html>", html, "</html>");
|
||||||
|
elem = XMLCDecode(StrStreamReader(html), true, true);
|
||||||
|
|
||||||
|
flags.quote = false;
|
||||||
|
xepd = XMPPifyElement(event, elem, flags);
|
||||||
|
|
||||||
|
XMLFreeElement(elem);
|
||||||
|
Free(html);
|
||||||
|
|
||||||
|
return xepd;
|
||||||
|
}
|
||||||
void
|
void
|
||||||
ParseePushDMStanza(ParseeData *data, char *room_id, char *stanza_id, char *id, char *ev, char *sender)
|
ParseePushDMStanza(ParseeData *data, char *room_id, char *stanza_id, char *id, char *ev, char *sender)
|
||||||
{
|
{
|
||||||
|
|
@ -339,15 +519,6 @@ ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, cha
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO */
|
/* TODO */
|
||||||
{
|
|
||||||
ref = DbLock(data->db, 2, "chats", chat_id);
|
|
||||||
j = DbJson(ref);
|
|
||||||
if (j)
|
|
||||||
{
|
|
||||||
JsonValueFree(HashMapSet(j, "ts", JsonValueInteger(age)));
|
|
||||||
}
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
ref = DbCreate(data->db, 4, "chats", chat_id, "stanzas", stanza_id);
|
ref = DbCreate(data->db, 4, "chats", chat_id, "stanzas", stanza_id);
|
||||||
j = DbJson(ref);
|
j = DbJson(ref);
|
||||||
|
|
@ -550,178 +721,140 @@ end:
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ParseeUnlinkRoom(ParseeData *data, char *chat_id)
|
ParseeGlobalBan(ParseeData *data, char *glob, char *reason)
|
||||||
{
|
{
|
||||||
char *muc, *room;
|
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
if (!data || !chat_id)
|
HashMap *j, *obj;
|
||||||
|
if (!data || !glob)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
muc = ParseeGetMUCID(data, chat_id);
|
ref = DbLock(data->db, 1, "global_bans");
|
||||||
room = ParseeGetRoomID(data, chat_id);
|
if (!ref)
|
||||||
if (!muc || !room)
|
|
||||||
{
|
{
|
||||||
Free(muc);
|
ref = DbCreate(data->db, 1, "global_bans");
|
||||||
Free(room);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ref = DbLock(data->db, 1, "chats");
|
j = DbJson(ref);
|
||||||
JsonValueFree(HashMapDelete(
|
|
||||||
GrabObject(DbJson(ref), 1, "rooms"),
|
obj = HashMapCreate();
|
||||||
room
|
if (reason)
|
||||||
));
|
{
|
||||||
JsonValueFree(HashMapDelete(
|
HashMapSet(obj, "reason", JsonValueString(reason));
|
||||||
GrabObject(DbJson(ref), 1, "mucs"),
|
}
|
||||||
muc
|
HashMapSet(obj, "date", JsonValueInteger(UtilTsMillis()));
|
||||||
));
|
JsonValueFree(HashMapSet(j, glob, JsonValueObject(obj)));
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
DbDelete(data->db, 2, "chats", chat_id);
|
|
||||||
|
|
||||||
Free(muc);
|
|
||||||
Free(room);
|
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
ParseeIsMUCWhitelisted(ParseeData *data, char *muc)
|
ParseeManageBan(ParseeData *data, char *user, char *room)
|
||||||
{
|
{
|
||||||
char *server, *serv_start, *postserv;
|
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
bool ret;
|
HashMap *j;
|
||||||
if (!data || !muc)
|
char *key;
|
||||||
|
JsonValue *val;
|
||||||
|
bool banned = false , matches = false;
|
||||||
|
if (!data || !user)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DbExists(data->db, 1, "whitelist"))
|
ref = DbLock(data->db, 1, "global_bans");
|
||||||
|
j = DbJson(ref);
|
||||||
|
while (HashMapIterate(j, &key, (void **) &val))
|
||||||
{
|
{
|
||||||
return true;
|
HashMap *obj = JsonValueAsObject(val);
|
||||||
|
if (matches)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
if (GlobMatches(key, user))
|
||||||
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';
|
banned = true;
|
||||||
|
matches = true;
|
||||||
|
if (room)
|
||||||
|
{
|
||||||
|
/* TODO: Use the object to set the reason */
|
||||||
|
ASBan(data->config, room, user);
|
||||||
|
(void) obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
DbUnlock(data->db, ref);
|
||||||
return ret;
|
|
||||||
}
|
return banned;
|
||||||
void
|
|
||||||
ParseeFreeChatSettings(HashMap *settings)
|
|
||||||
{
|
|
||||||
char *key;
|
|
||||||
void *val;
|
|
||||||
if (!settings)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (HashMapIterate(settings, &key, &val))
|
|
||||||
{
|
|
||||||
Free(val);
|
|
||||||
}
|
|
||||||
HashMapFree(settings);
|
|
||||||
}
|
}
|
||||||
char *
|
char *
|
||||||
ParseeGetChatSetting(ParseeData *data, char *chat, char *key)
|
ParseeStringifyDate(uint64_t millis)
|
||||||
{
|
{
|
||||||
HashMap *map;
|
uint64_t rest = millis;
|
||||||
char *ret;
|
uint64_t hours, minutes, seconds;
|
||||||
if (!data || !chat || !key)
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
map = ParseeGetChatSettings(data, chat);
|
common_id = HttpUrlEncode(common_id);
|
||||||
ret = StrDuplicate(HashMapGet(map, key));
|
matrix_to = StrConcat(2, "https://matrix.to/#/", common_id);
|
||||||
ParseeFreeChatSettings(map);
|
Free(common_id);
|
||||||
|
|
||||||
return ret;
|
return matrix_to;
|
||||||
}
|
|
||||||
void
|
|
||||||
ParseeSetChatSetting(ParseeData *data, char *chat, char *key, char *val)
|
|
||||||
{
|
|
||||||
DbRef *ref;
|
|
||||||
HashMap *json;
|
|
||||||
if (!data || !chat || !key || !val)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref = DbLockIntent(data->db, DB_HINT_WRITE,
|
|
||||||
3, "chats", chat, "settings"
|
|
||||||
);
|
|
||||||
if (!ref)
|
|
||||||
{
|
|
||||||
ref = DbCreate(data->db, 3, "chats", chat, "settings");
|
|
||||||
}
|
|
||||||
json = DbJson(ref);
|
|
||||||
|
|
||||||
JsonValueFree(HashMapSet(json, key, JsonValueString(val)));
|
|
||||||
|
|
||||||
DbUnlock(data->db, ref);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool
|
|
||||||
ParseeIsMediaEnabled(ParseeData *data, char *chat_id)
|
|
||||||
{
|
|
||||||
char *value;
|
|
||||||
bool ret;
|
|
||||||
if (!data || !chat_id)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = !StrEquals(
|
|
||||||
(value = ParseeGetChatSetting(data, chat_id, "p.media.enabled")),
|
|
||||||
"false"
|
|
||||||
);
|
|
||||||
Free(value);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 <Parsee.h>
|
||||||
|
|
||||||
#include <Cytoplasm/Memory.h>
|
#include <Cytoplasm/Memory.h>
|
||||||
#include <Cytoplasm/Http.h>
|
|
||||||
#include <Cytoplasm/Json.h>
|
#include <Cytoplasm/Json.h>
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <Matrix.h>
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseeIsPuppet(const ParseeConfig *conf, char *user)
|
ParseeIsPuppet(const ParseeConfig *conf, char *user)
|
||||||
{
|
{
|
||||||
UserID *id;
|
char *localpart;
|
||||||
bool flag = true;
|
bool flag = true;
|
||||||
size_t len;
|
size_t len;
|
||||||
if (!user ||
|
if (!user || !conf || *user != '@')
|
||||||
!conf ||
|
|
||||||
*user != '@' ||
|
|
||||||
!(id = MatrixParseID(user)))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
localpart = user + 1;
|
||||||
len = strlen(conf->namespace_base);
|
len = strlen(conf->namespace_base);
|
||||||
if (strncmp(id->localpart, conf->namespace_base, len))
|
if (strncmp(localpart, conf->namespace_base, len))
|
||||||
{
|
{
|
||||||
flag = false;
|
flag = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StrEquals(id->localpart, conf->sender_localpart))
|
len = strlen(conf->sender_localpart);
|
||||||
|
if (!strncmp(localpart, conf->sender_localpart, len))
|
||||||
{
|
{
|
||||||
flag = true;
|
flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
flag = flag && StrEquals(id->server, conf->server_base);
|
/* TODO: Check serverpart. 2 Parsee instances may be running in the same
|
||||||
|
* room. */
|
||||||
Free(id);
|
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
char *
|
char *
|
||||||
|
|
@ -97,7 +93,7 @@ ParseeDecodeLocalJID(const ParseeConfig *c, char *mxid)
|
||||||
data_start = jid_flags;
|
data_start = jid_flags;
|
||||||
while (*data_start && *data_start != '_')
|
while (*data_start && *data_start != '_')
|
||||||
{
|
{
|
||||||
/* TODO: Get rid of this */
|
/* TODO: Make this a macro */
|
||||||
if (*data_start == 'l')
|
if (*data_start == 'l')
|
||||||
{
|
{
|
||||||
plain_jid = true;
|
plain_jid = true;
|
||||||
|
|
@ -127,7 +123,7 @@ ParseeDecodeLocalMUC(const ParseeConfig *c, char *alias)
|
||||||
data_start = jid_flags;
|
data_start = jid_flags;
|
||||||
while (*data_start && *data_start != '_')
|
while (*data_start && *data_start != '_')
|
||||||
{
|
{
|
||||||
/* TODO: Get rid of this */
|
/* TODO: Make this a macro */
|
||||||
if (*data_start == 'm')
|
if (*data_start == 'm')
|
||||||
{
|
{
|
||||||
plain_jid = true;
|
plain_jid = true;
|
||||||
|
|
@ -148,15 +144,14 @@ char *
|
||||||
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
||||||
{
|
{
|
||||||
char *ret, *tmp;
|
char *ret, *tmp;
|
||||||
size_t i, len;
|
size_t i;
|
||||||
if (!c || !jid)
|
if (!c || !jid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = StrConcat(2, c->namespace_base, "_l_");
|
ret = StrConcat(2, c->namespace_base, "_l_");
|
||||||
len = strlen(jid);
|
for (i = 0; i < strlen(jid); i++)
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
{
|
||||||
char cpy = jid[i];
|
char cpy = jid[i];
|
||||||
char cs[4] = { 0 };
|
char cs[4] = { 0 };
|
||||||
|
|
@ -166,7 +161,7 @@ ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
|
||||||
/* RID: Break everything and die. */
|
/* RID: Break everything and die. */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (islower((int) *cs) || isalnum((int) *cs) || *cs == '_' ||
|
if (islower(*cs) || isalnum(*cs) || *cs == '_' ||
|
||||||
*cs == '=' || *cs == '-' || *cs == '/' ||
|
*cs == '=' || *cs == '-' || *cs == '/' ||
|
||||||
*cs == '+' || *cs == '.')
|
*cs == '+' || *cs == '.')
|
||||||
{
|
{
|
||||||
|
|
@ -195,7 +190,7 @@ char *
|
||||||
ParseeGetLocal(char *mxid)
|
ParseeGetLocal(char *mxid)
|
||||||
{
|
{
|
||||||
char *cpy;
|
char *cpy;
|
||||||
size_t i, len;
|
size_t i;
|
||||||
if (!mxid)
|
if (!mxid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -205,14 +200,12 @@ ParseeGetLocal(char *mxid)
|
||||||
return StrDuplicate(mxid);
|
return StrDuplicate(mxid);
|
||||||
}
|
}
|
||||||
|
|
||||||
len = strlen(mxid);
|
|
||||||
|
|
||||||
mxid++;
|
mxid++;
|
||||||
cpy = Malloc(len + 1);
|
cpy = Malloc(strlen(mxid) + 1);
|
||||||
memset(cpy, '\0', len + 1);
|
memset(cpy, '\0', strlen(mxid) + 1);
|
||||||
memcpy(cpy, mxid, len);
|
memcpy(cpy, mxid, strlen(mxid));
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < strlen(mxid); i++)
|
||||||
{
|
{
|
||||||
if (cpy[i] == ':')
|
if (cpy[i] == ':')
|
||||||
{
|
{
|
||||||
|
|
@ -228,19 +221,19 @@ char *
|
||||||
ParseeEncodeMXID(char *mxid)
|
ParseeEncodeMXID(char *mxid)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
size_t i, j, len;
|
size_t i, j;
|
||||||
if (!mxid)
|
if (!mxid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Worst case scenario of 3-bytes the char */
|
/* Worst case scenario of 3-bytes the char */
|
||||||
len = strlen(mxid);
|
ret = Malloc(strlen(mxid) * 3 + 1);
|
||||||
ret = Malloc(len * 3 + 1);
|
for (i = 0, j = 0; i < strlen(mxid); i++)
|
||||||
for (i = 0, j = 0; i < len; i++)
|
|
||||||
{
|
{
|
||||||
char src = mxid[i];
|
char src = mxid[i];
|
||||||
|
|
||||||
|
/* TODO: More robust system */
|
||||||
if (src <= 0x20 ||
|
if (src <= 0x20 ||
|
||||||
(src == '"' || src == '&' ||
|
(src == '"' || src == '&' ||
|
||||||
src == '\'' || src == '/' ||
|
src == '\'' || src == '/' ||
|
||||||
|
|
@ -302,17 +295,19 @@ char *
|
||||||
ParseeGetDMID(char *mxid, char *jid)
|
ParseeGetDMID(char *mxid, char *jid)
|
||||||
{
|
{
|
||||||
char *concat, *sha;
|
char *concat, *sha;
|
||||||
|
unsigned char *raw;
|
||||||
if (!mxid || !jid)
|
if (!mxid || !jid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We really don't care about safety here. */
|
|
||||||
jid = ParseeTrimJID(jid);
|
jid = ParseeTrimJID(jid);
|
||||||
concat = StrConcat(2, mxid, jid);
|
concat = StrConcat(2, mxid, jid);
|
||||||
sha = ParseeSHA1(concat);
|
raw = Sha1(concat);
|
||||||
|
sha = ShaToHex(raw);
|
||||||
|
|
||||||
Free(concat);
|
Free(concat);
|
||||||
|
Free(raw);
|
||||||
Free(jid);
|
Free(jid);
|
||||||
|
|
||||||
return sha;
|
return sha;
|
||||||
|
|
@ -358,34 +353,19 @@ ParseePushDMRoom(ParseeData *d, char *mxid, char *jid, char *r)
|
||||||
Free(dmid);
|
Free(dmid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
void
|
|
||||||
ParseeDeleteDM(ParseeData *d, char *mxid, char *jid)
|
|
||||||
{
|
|
||||||
char *dmid;
|
|
||||||
if (!d || !mxid || !jid)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dmid = ParseeGetDMID(mxid, jid);
|
|
||||||
DbDelete(d->db, 2, "users", dmid);
|
|
||||||
Free(dmid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *
|
char *
|
||||||
ParseeTrimJID(char *jid)
|
ParseeTrimJID(char *jid)
|
||||||
{
|
{
|
||||||
char *ret;
|
char *ret;
|
||||||
size_t i, len;
|
size_t i;
|
||||||
if (!jid)
|
if (!jid)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = StrDuplicate(jid);
|
ret = StrDuplicate(jid);
|
||||||
len = strlen(ret);
|
for (i = 0; i < strlen(ret); i++)
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
{
|
||||||
if (ret[i] == '/')
|
if (ret[i] == '/')
|
||||||
{
|
{
|
||||||
|
|
@ -539,6 +519,7 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ParseeSendPresence(ParseeData *data)
|
ParseeSendPresence(ParseeData *data)
|
||||||
{
|
{
|
||||||
|
|
@ -557,16 +538,10 @@ ParseeSendPresence(ParseeData *data)
|
||||||
while (HashMapIterate(mucs, &muc, (void **) &val))
|
while (HashMapIterate(mucs, &muc, (void **) &val))
|
||||||
{
|
{
|
||||||
char *rev = StrConcat(2, muc, "/parsee");
|
char *rev = StrConcat(2, muc, "/parsee");
|
||||||
char *chat_id = ParseeGetFromMUCID(data, muc);
|
|
||||||
DbRef *chat = DbLockIntent(data->db, DB_HINT_READONLY, 2, "chats", chat_id);
|
|
||||||
uint64_t ts = GrabInteger(DbJson(chat), 1, "ts");
|
|
||||||
int diff = ts ? (int) ((UtilTsMillis() - ts) / 1000) : -1;
|
|
||||||
/* Make a fake user join the MUC */
|
/* Make a fake user join the MUC */
|
||||||
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
||||||
XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false);
|
XMPPJoinMUC(data->jabber, "parsee", rev);
|
||||||
|
|
||||||
DbUnlock(data->db, chat);
|
|
||||||
Free(chat_id);
|
|
||||||
Free(rev);
|
Free(rev);
|
||||||
}
|
}
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
|
|
@ -688,13 +663,11 @@ end:
|
||||||
|
|
||||||
#include <Cytoplasm/Uri.h>
|
#include <Cytoplasm/Uri.h>
|
||||||
char *
|
char *
|
||||||
ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
ParseeToUnauth(ParseeData *data, char *mxc)
|
||||||
{
|
{
|
||||||
Uri *url = NULL;
|
Uri *url = NULL;
|
||||||
char *ret;
|
char *ret;
|
||||||
char *key, *hmac;
|
#define PAT "%s/_matrix/client/v1/media/download/%s%s"
|
||||||
#define PAT "%s/media/%s%s?hmac=%s"
|
|
||||||
#define PATF "%s/media/%s%s/%s?hmac=%s"
|
|
||||||
size_t l;
|
size_t l;
|
||||||
if (!data || !mxc)
|
if (!data || !mxc)
|
||||||
{
|
{
|
||||||
|
|
@ -711,51 +684,19 @@ ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
key = StrConcat(2, url->host, url->path);
|
/* TODO: HTTPS */
|
||||||
hmac = ParseeHMACS(data->id, key);
|
|
||||||
Free(key);
|
|
||||||
|
|
||||||
if (!filename)
|
|
||||||
{
|
|
||||||
l = snprintf(NULL, 0,
|
l = snprintf(NULL, 0,
|
||||||
PAT,
|
PAT,
|
||||||
data->config->media_base,
|
data->config->media_base,
|
||||||
url->host, url->path,
|
url->host, url->path
|
||||||
hmac
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *encoded = HttpUrlEncode(filename);
|
|
||||||
l = snprintf(NULL, 0,
|
|
||||||
PATF,
|
|
||||||
data->config->media_base,
|
|
||||||
url->host, url->path, encoded,
|
|
||||||
hmac
|
|
||||||
);
|
|
||||||
Free(encoded);
|
|
||||||
}
|
|
||||||
ret = Malloc(l + 3);
|
ret = Malloc(l + 3);
|
||||||
if (!filename)
|
|
||||||
{
|
|
||||||
snprintf(ret, l + 1,
|
snprintf(ret, l + 1,
|
||||||
PAT,
|
PAT,
|
||||||
data->config->media_base,
|
data->config->media_base,
|
||||||
url->host, url->path,
|
url->host, url->path
|
||||||
hmac
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(ret, l + 1,
|
|
||||||
PATF,
|
|
||||||
data->config->media_base,
|
|
||||||
url->host, url->path, filename,
|
|
||||||
hmac
|
|
||||||
);
|
|
||||||
}
|
|
||||||
UriFree(url);
|
UriFree(url);
|
||||||
Free(hmac);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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/Memory.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Log.h>
|
|
||||||
|
|
||||||
#include <Matrix.h>
|
#include <Matrix.h>
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <AS.h>
|
#include <AS.h>
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
static HttpClientContext *
|
|
||||||
TryDownload(ParseeData *data, char *server, char *identi)
|
|
||||||
{
|
|
||||||
HttpClientContext *cctx;
|
|
||||||
char *path;
|
|
||||||
server = HttpUrlEncode(server);
|
|
||||||
identi = HttpUrlEncode(identi);
|
|
||||||
|
|
||||||
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
|
||||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
|
||||||
ASAuthenticateRequest(data->config, cctx);
|
|
||||||
Free(path);
|
|
||||||
|
|
||||||
HttpRequestSendHeaders(cctx);
|
|
||||||
if (HttpRequestSend(cctx) != HTTP_OK)
|
|
||||||
{
|
|
||||||
Log(LOG_WARNING, "Failing back.");
|
|
||||||
HttpClientContextFree(cctx);
|
|
||||||
path = StrConcat(4, "/_matrix/client/v1/media/download/", server, "/", identi);
|
|
||||||
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
|
|
||||||
ASAuthenticateRequest(data->config, cctx);
|
|
||||||
Free(path);
|
|
||||||
HttpRequestSendHeaders(cctx);
|
|
||||||
HttpRequestSend(cctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
Free(server);
|
|
||||||
Free(identi);
|
|
||||||
return cctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
RouteHead(RouteMedia, arr, argp)
|
RouteHead(RouteMedia, arr, argp)
|
||||||
{
|
{
|
||||||
ParseeHttpArg *args = argp;
|
ParseeHttpArg *args = argp;
|
||||||
HttpClientContext *cctx;
|
HttpClientContext *cctx;
|
||||||
HashMap *reqh, *params;
|
HashMap *reqh;
|
||||||
char *server = ArrayGet(arr, 0);
|
char *server = ArrayGet(arr, 0);
|
||||||
char *identi = ArrayGet(arr, 1);
|
char *identi = ArrayGet(arr, 1);
|
||||||
char *key, *val;
|
char *path, *key, *val;
|
||||||
char *hmac, *chkmak = NULL;
|
|
||||||
|
|
||||||
params = HttpRequestParams(args->ctx);
|
/* TODO: Make it check the DB for its validicity. "Purging" would be useful.
|
||||||
hmac = HashMapGet(params, "hmac");
|
*/
|
||||||
|
if (!server || !identi)
|
||||||
/* TODO: Make it check the DB for its validicity. "Purging" would be
|
|
||||||
* useful, alongside checking if someone isn't just a little idiotic. */
|
|
||||||
{
|
{
|
||||||
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);
|
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
|
server = HttpUrlEncode(server);
|
||||||
* is valid. */
|
identi = HttpUrlEncode(identi);
|
||||||
cctx = TryDownload(args->data, server, identi);
|
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
|
||||||
|
cctx = ParseeCreateRequest(args->data->config, HTTP_GET, path);
|
||||||
|
ASAuthenticateRequest(args->data->config, cctx);
|
||||||
|
Free(path);
|
||||||
|
|
||||||
|
HttpRequestSendHeaders(cctx);
|
||||||
|
HttpRequestSend(cctx);
|
||||||
reqh = HttpResponseHeaders(cctx);
|
reqh = HttpResponseHeaders(cctx);
|
||||||
while (HashMapIterate(reqh, &key, (void **) &val))
|
while (HashMapIterate(reqh, &key, (void **) &val))
|
||||||
{
|
{
|
||||||
|
|
@ -88,6 +45,8 @@ RouteHead(RouteMedia, arr, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpClientContextFree(cctx);
|
HttpClientContextFree(cctx);
|
||||||
|
Free(server);
|
||||||
|
Free(identi);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,13 @@ RouteHead(RoutePing, arr, argp)
|
||||||
);
|
);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
Log(LOG_INFO, "Pong!");
|
||||||
|
|
||||||
RequestJSON();
|
RequestJSON();
|
||||||
|
|
||||||
|
/* TODO: Load ping info */
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
end:
|
end:
|
||||||
(void) arr;
|
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,26 +41,13 @@ GetRandomQuote(void)
|
||||||
"We truly live in a " CODE "...",
|
"We truly live in a " CODE "...",
|
||||||
"You truly lack the Desire Drive for this!",
|
"You truly lack the Desire Drive for this!",
|
||||||
"Eeh? Bifrost mode? Only bad servers use Bifrost mode!",
|
"Eeh? Bifrost mode? Only bad servers use Bifrost mode!",
|
||||||
"'Wow parsee can save the world' - said a wise guy",
|
|
||||||
"As small as a dwarf, and can run on your pie!",
|
"As small as a dwarf, and can run on your pie!",
|
||||||
"It's all wicked unsafe, memory slow 🚀🚀🚀🚀",
|
"It's all wicked unsafe, memory slow 🚀🚀🚀🚀",
|
||||||
|
|
||||||
"XMPP kinda sucks.",
|
"XMPP kinda sucks.",
|
||||||
"Matrix kinda sucks.",
|
"Matrix kinda sucks.",
|
||||||
|
|
||||||
"Throw jabs!",
|
"Throw jabs!"
|
||||||
"'Won't you hold my <presence/> safe?' - Kanako",
|
|
||||||
|
|
||||||
NAME ": the federated world's little little kobashi",
|
|
||||||
|
|
||||||
"Go take a look at your stanzas!",
|
|
||||||
"Go take a look at your objects!",
|
|
||||||
|
|
||||||
"DEC Alpha AXP-Certified!",
|
|
||||||
|
|
||||||
"this is the moment parsee started parsing or smth idk"
|
|
||||||
" - another wise person",
|
|
||||||
"Ah, merde, mon TGV est en retard de 53 minutes !"
|
|
||||||
};
|
};
|
||||||
const size_t count = sizeof(quotes)/sizeof(*quotes);
|
const size_t count = sizeof(quotes)/sizeof(*quotes);
|
||||||
|
|
||||||
|
|
@ -82,7 +69,6 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
{
|
{
|
||||||
P("<title>%s Lander</title>", NAME);
|
P("<title>%s Lander</title>", NAME);
|
||||||
P("<meta charset='UTF-8'/>");
|
P("<meta charset='UTF-8'/>");
|
||||||
P("<link rel='icon' href='data:image/png;base64,%s'/>", media_parsee_logo);
|
|
||||||
P("<style>");
|
P("<style>");
|
||||||
{
|
{
|
||||||
P("html {");
|
P("html {");
|
||||||
|
|
@ -90,17 +76,6 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("color: #eee;");
|
P("color: #eee;");
|
||||||
P("font-family: sans-serif;");
|
P("font-family: sans-serif;");
|
||||||
P("}");
|
P("}");
|
||||||
P("#ascii {");
|
|
||||||
P("text-align: center;");
|
|
||||||
P("color: #be1337;");
|
|
||||||
P("}");
|
|
||||||
P("#ascii pre {");
|
|
||||||
P("display: inline-block;");
|
|
||||||
P("text-align: left;");
|
|
||||||
P("}");
|
|
||||||
P("img {");
|
|
||||||
P("image-rendering: pixelated;");
|
|
||||||
P("}");
|
|
||||||
P("blockquote {");
|
P("blockquote {");
|
||||||
P("border-left: 2px solid #ccc;");
|
P("border-left: 2px solid #ccc;");
|
||||||
P("margin: 1.5em 10px;");
|
P("margin: 1.5em 10px;");
|
||||||
|
|
@ -123,25 +98,15 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
|
|
||||||
P("<body>");
|
P("<body>");
|
||||||
{
|
{
|
||||||
size_t i;
|
P("<center><h1>");
|
||||||
P("<center>");
|
P("Your %s is running, all with that %s!", NAME, CODE);
|
||||||
P("<h1>Your %s is running, all with that %s!</h1>", NAME, CODE);
|
P("</h1></center>");
|
||||||
P("</center>");
|
|
||||||
P("<hr/>");
|
P("<hr/>");
|
||||||
P("<blockquote><i>");
|
P("<blockquote><i>");
|
||||||
{
|
{
|
||||||
P("%s", GetRandomQuote());
|
P("%s", GetRandomQuote());
|
||||||
}
|
}
|
||||||
P("</i></blockquote>");
|
P("</i></blockquote>");
|
||||||
P("<pre id='ascii'><code>");
|
|
||||||
for (i = 0; i < PARSEE_ASCII_LINES; i++)
|
|
||||||
{
|
|
||||||
XMLElement *e = XMLCreateText((char *) parsee_ascii[i]);
|
|
||||||
XMLEncode(args->stream, e);
|
|
||||||
XMLFreeElement(e);
|
|
||||||
P("<br/>");
|
|
||||||
}
|
|
||||||
P("</code></pre>");
|
|
||||||
|
|
||||||
P("<p>");
|
P("<p>");
|
||||||
{
|
{
|
||||||
|
|
@ -179,7 +144,7 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("<p>");
|
P("<p>");
|
||||||
{
|
{
|
||||||
P("More information available at ");
|
P("More information available at ");
|
||||||
P("<a ");
|
P("<a");
|
||||||
P("href='https://kappach.at/parsee'");
|
P("href='https://kappach.at/parsee'");
|
||||||
P(">the actual page</a>.");
|
P(">the actual page</a>.");
|
||||||
}
|
}
|
||||||
|
|
@ -234,7 +199,7 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("Some clicky links relating to %s:", NAME);
|
P("Some clicky links relating to %s:", NAME);
|
||||||
P("<ul>");
|
P("<ul>");
|
||||||
{
|
{
|
||||||
const char *fedi = "https://tengu.kappach.at/parsee";
|
const char *fedi = "https://ak.ari.lt/parsee";
|
||||||
const char *icon = "https://kappach.at/parsee.gif";
|
const char *icon = "https://kappach.at/parsee.gif";
|
||||||
P("<li><a href='%s'>Repository</a></li>", REPOSITORY);
|
P("<li><a href='%s'>Repository</a></li>", REPOSITORY);
|
||||||
P("<li><a href='%s'>Fediverse</a></li>", fedi);
|
P("<li><a href='%s'>Fediverse</a></li>", fedi);
|
||||||
|
|
@ -254,6 +219,5 @@ RouteHead(RouteRoot, arr, argp)
|
||||||
P("</html>");
|
P("</html>");
|
||||||
#undef P
|
#undef P
|
||||||
|
|
||||||
(void) arr;
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ RouteHead(RouteTxns, arr, argp)
|
||||||
|
|
||||||
RequestJSON();
|
RequestJSON();
|
||||||
|
|
||||||
|
/* TODO: Do a thing with these. */
|
||||||
events = JsonValueAsArray(HashMapGet(request, "events"));
|
events = JsonValueAsArray(HashMapGet(request, "events"));
|
||||||
for (i = 0; i < ArraySize(events); i++)
|
for (i = 0; i < ArraySize(events); i++)
|
||||||
{
|
{
|
||||||
|
|
@ -42,7 +43,6 @@ RouteHead(RouteTxns, arr, argp)
|
||||||
|
|
||||||
response = HashMapCreate();
|
response = HashMapCreate();
|
||||||
end:
|
end:
|
||||||
(void) arr;
|
|
||||||
JsonFree(request);
|
JsonFree(request);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,7 @@ RouteHead(RouteRoomAck, arr, argp)
|
||||||
}
|
}
|
||||||
|
|
||||||
muc = ParseeDecodeLocalMUC(args->data->config, room);
|
muc = ParseeDecodeLocalMUC(args->data->config, room);
|
||||||
if (ParseeManageBan(args->data, muc, NULL) ||
|
if (ParseeManageBan(args->data, muc, NULL))
|
||||||
ParseeIsMUCWhitelisted(args->data, muc))
|
|
||||||
{
|
{
|
||||||
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
|
||||||
response = MatrixCreateError(
|
response = MatrixCreateError(
|
||||||
|
|
@ -131,7 +130,7 @@ RouteHead(RouteRoomAck, arr, argp)
|
||||||
{
|
{
|
||||||
char *rev = StrConcat(2, muc, "/parsee");
|
char *rev = StrConcat(2, muc, "/parsee");
|
||||||
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
Log(LOG_NOTICE, "Sending presence to %s", rev);
|
||||||
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
|
XMPPJoinMUC(args->data->jabber, "parsee", rev);
|
||||||
|
|
||||||
Free(rev);
|
Free(rev);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
19
src/Signal.c
19
src/Signal.c
|
|
@ -6,35 +6,32 @@
|
||||||
|
|
||||||
#include <XMPP.h>
|
#include <XMPP.h>
|
||||||
|
|
||||||
static ParseeData *data;
|
static HttpServer *server = NULL;
|
||||||
static pthread_t xmpp_thr;
|
static pthread_t xmpp_thr;
|
||||||
|
static XMPPComponent *jabber = NULL;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
SignalHandler(int signal)
|
SignalHandler(int signal)
|
||||||
{
|
{
|
||||||
if (data->server && (signal == SIGTERM || signal == SIGINT))
|
if (server && (signal == SIGTERM || signal == SIGINT))
|
||||||
{
|
{
|
||||||
Log(LOG_INFO, "Killing thread...");
|
Log(LOG_INFO, "Killing thread...");
|
||||||
|
XMPPFinishCompStream(jabber);
|
||||||
pthread_mutex_lock(&data->halt_lock);
|
|
||||||
data->halted = true;
|
|
||||||
pthread_mutex_unlock(&data->halt_lock);
|
|
||||||
|
|
||||||
XMPPFinishCompStream(data->jabber);
|
|
||||||
pthread_join(xmpp_thr, NULL);
|
pthread_join(xmpp_thr, NULL);
|
||||||
Log(LOG_INFO, "Stopping server...");
|
Log(LOG_INFO, "Stopping server...");
|
||||||
HttpServerStop(data->server);
|
HttpServerStop(server);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ParseeInitialiseSignals(ParseeData *d, pthread_t xmpp)
|
ParseeInitialiseSignals(HttpServer *s, pthread_t xmpp, XMPPComponent *j)
|
||||||
{
|
{
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
|
|
||||||
data = d;
|
server = s;
|
||||||
xmpp_thr = xmpp;
|
xmpp_thr = xmpp;
|
||||||
|
jabber = j;
|
||||||
|
|
||||||
sigfillset(&sa.sa_mask);
|
sigfillset(&sa.sa_mask);
|
||||||
sa.sa_handler = SignalHandler;
|
sa.sa_handler = SignalHandler;
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ CreateStanzaBuilder(char *from, char *to, char *id)
|
||||||
|
|
||||||
builder->replying_to_stanza = NULL;
|
builder->replying_to_stanza = NULL;
|
||||||
builder->replying_to_sender = NULL;
|
builder->replying_to_sender = NULL;
|
||||||
builder->editing = NULL;
|
|
||||||
builder->type = NULL;
|
builder->type = NULL;
|
||||||
builder->body = NULL;
|
builder->body = NULL;
|
||||||
builder->oob = NULL;
|
builder->oob = NULL;
|
||||||
|
|
@ -185,14 +184,14 @@ ExportStanza(StanzaBuilder *builder)
|
||||||
builder->replying_to_sender &&
|
builder->replying_to_sender &&
|
||||||
builder->body)
|
builder->body)
|
||||||
{
|
{
|
||||||
int off = ParseeFindDatastartU(builder->body);
|
int off = ParseeFindDatastart(builder->body);
|
||||||
char *ostr = StrInt(off);
|
char *ostr = StrInt(off);
|
||||||
XMLElement *reply = XMLCreateTag("reply");
|
XMLElement *reply = XMLCreateTag("reply");
|
||||||
XMLElement *fallback = XMLCreateTag("fallback");
|
XMLElement *fallback = XMLCreateTag("fallback");
|
||||||
XMLElement *fall_body = XMLCreateTag("body");
|
XMLElement *fall_body = XMLCreateTag("body");
|
||||||
|
|
||||||
XMLAddAttr(reply, "to", builder->replying_to_sender);
|
XMLAddAttr(reply, "to", builder->replying_to_stanza);
|
||||||
XMLAddAttr(reply, "id", builder->replying_to_stanza);
|
XMLAddAttr(reply, "id", builder->replying_to_sender);
|
||||||
XMLAddAttr(reply, "xmlns", "urn:xmpp:reply:0");
|
XMLAddAttr(reply, "xmlns", "urn:xmpp:reply:0");
|
||||||
|
|
||||||
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
XMLAddAttr(fallback, "xmlns", "urn:xmpp:fallback:0");
|
||||||
|
|
@ -228,21 +227,22 @@ ExportStanza(StanzaBuilder *builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber, size_t max)
|
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber)
|
||||||
{
|
{
|
||||||
XMLElement *elem;
|
XMLElement *elem;
|
||||||
if (!builder || !jabber)
|
if (!builder || !jabber)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!max)
|
|
||||||
{
|
|
||||||
max = 10000; /* XMPP recommended limit */
|
|
||||||
}
|
|
||||||
|
|
||||||
elem = ExportStanza(builder);
|
elem = ExportStanza(builder);
|
||||||
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);
|
XMLFreeElement(elem);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StanzaBuilder *
|
StanzaBuilder *
|
||||||
|
|
@ -258,6 +258,7 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
|
||||||
{
|
{
|
||||||
XMLElement *parsee_version, *ver_elem;
|
XMLElement *parsee_version, *ver_elem;
|
||||||
XMLElement *parsee_link, *link_elem;
|
XMLElement *parsee_link, *link_elem;
|
||||||
|
XMLElement *parsee_text, *text_elem;
|
||||||
XMLElement *parsee_event, *event_elem;
|
XMLElement *parsee_event, *event_elem;
|
||||||
XMLElement *parsee_json, *json_elem;
|
XMLElement *parsee_json, *json_elem;
|
||||||
char *event_id = GrabString(e, 1, "event_id");
|
char *event_id = GrabString(e, 1, "event_id");
|
||||||
|
|
@ -284,6 +285,16 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
|
||||||
XMLAddChild(parsee_link, link_elem);
|
XMLAddChild(parsee_link, link_elem);
|
||||||
XMLAddChild(parsee, parsee_link);
|
XMLAddChild(parsee, parsee_link);
|
||||||
|
|
||||||
|
parsee_text = XMLCreateTag("zayds-note");
|
||||||
|
text_elem = XMLCreateText("\"LDA HANG YOURSELF\" - Zayd");
|
||||||
|
XMLAddChild(parsee_text, text_elem);
|
||||||
|
XMLAddChild(parsee, parsee_text);
|
||||||
|
|
||||||
|
parsee_text = XMLCreateTag("mcnebs-note");
|
||||||
|
text_elem = XMLCreateText("LDA will never beat the allegations");
|
||||||
|
XMLAddChild(parsee_text, text_elem);
|
||||||
|
XMLAddChild(parsee, parsee_text);
|
||||||
|
|
||||||
if (event_id)
|
if (event_id)
|
||||||
{
|
{
|
||||||
parsee_event = XMLCreateTag("event-id");
|
parsee_event = XMLCreateTag("event-id");
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
char **
|
char **
|
||||||
StrSplitLines(char *text)
|
StrSplitLines(char *text)
|
||||||
|
|
@ -116,7 +117,7 @@ StrFullRect(char **split)
|
||||||
char
|
char
|
||||||
StrGet(StringRect *rect, int line, int col)
|
StrGet(StringRect *rect, int line, int col)
|
||||||
{
|
{
|
||||||
size_t actual_line, actual_col;
|
int actual_line, actual_col;
|
||||||
char *linep;
|
char *linep;
|
||||||
if (!rect || !rect->source_lines)
|
if (!rect || !rect->source_lines)
|
||||||
{
|
{
|
||||||
|
|
@ -149,7 +150,7 @@ StrGet(StringRect *rect, int line, int col)
|
||||||
size_t
|
size_t
|
||||||
StrViewChars(StringRect rect, int line)
|
StrViewChars(StringRect rect, int line)
|
||||||
{
|
{
|
||||||
size_t actual_line;
|
int actual_line;
|
||||||
char *linep;
|
char *linep;
|
||||||
if (!rect.source_lines)
|
if (!rect.source_lines)
|
||||||
{
|
{
|
||||||
|
|
@ -173,7 +174,7 @@ StrViewChars(StringRect rect, int line)
|
||||||
StringRect
|
StringRect
|
||||||
StrGetl(StringRect *rect, int line, bool extend)
|
StrGetl(StringRect *rect, int line, bool extend)
|
||||||
{
|
{
|
||||||
size_t actual_line;
|
int actual_line;
|
||||||
StringRect ret;
|
StringRect ret;
|
||||||
if (!rect->source_lines)
|
if (!rect->source_lines)
|
||||||
{
|
{
|
||||||
|
|
@ -203,7 +204,7 @@ StrGetl(StringRect *rect, int line, bool extend)
|
||||||
StringRect
|
StringRect
|
||||||
StrShift(StringRect rect, int n)
|
StrShift(StringRect rect, int n)
|
||||||
{
|
{
|
||||||
size_t new = rect.start_char + n;
|
int new = rect.start_char + n;
|
||||||
if (new > rect.end_char)
|
if (new > rect.end_char)
|
||||||
{
|
{
|
||||||
new = rect.end_char;
|
new = rect.end_char;
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,125 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef struct ReaderCookie {
|
||||||
|
size_t length;
|
||||||
|
size_t offset;
|
||||||
|
char *buffer;
|
||||||
|
} ReaderCookie;
|
||||||
|
|
||||||
|
#define Remaining() (cook->length - cook->offset)
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
ReadStreamReader(void *coop, void *to, size_t n)
|
||||||
|
{
|
||||||
|
ReaderCookie *cook = coop;
|
||||||
|
size_t remaining;
|
||||||
|
if (!cook)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
remaining = Remaining();
|
||||||
|
if (n > remaining)
|
||||||
|
{
|
||||||
|
memcpy(to, cook->buffer + cook->offset, remaining);
|
||||||
|
cook->offset = cook->length;
|
||||||
|
return remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(to, cook->buffer + cook->offset, n);
|
||||||
|
cook->offset += n;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
static ssize_t
|
||||||
|
WriteStreamReader(void *coop, void *from, size_t n)
|
||||||
|
{
|
||||||
|
/* Writing to a stream reader is silly. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static off_t
|
||||||
|
SeekStreamReader(void *coop, off_t mag, int sgn)
|
||||||
|
{
|
||||||
|
ReaderCookie *cook = coop;
|
||||||
|
if (!cook)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sgn)
|
||||||
|
{
|
||||||
|
case SEEK_SET:
|
||||||
|
if (mag > cook->length)
|
||||||
|
{
|
||||||
|
cook->offset = cook->length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if (mag < 0)
|
||||||
|
{
|
||||||
|
cook->offset = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
cook->offset = mag;
|
||||||
|
return 0;
|
||||||
|
case SEEK_CUR:
|
||||||
|
cook->offset += mag;
|
||||||
|
if (cook->offset > cook->length)
|
||||||
|
{
|
||||||
|
cook->offset = cook->length;
|
||||||
|
}
|
||||||
|
else if (cook->offset < 0)
|
||||||
|
{
|
||||||
|
cook->offset = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
case SEEK_END:
|
||||||
|
cook->offset += cook->length + mag;
|
||||||
|
if (cook->offset > cook->length)
|
||||||
|
{
|
||||||
|
cook->offset = cook->length;
|
||||||
|
}
|
||||||
|
else if (cook->offset < 0)
|
||||||
|
{
|
||||||
|
cook->offset = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
CloseStreamReader(void *coop)
|
||||||
|
{
|
||||||
|
/* Nothing to free as of now. */
|
||||||
|
if (coop)
|
||||||
|
{
|
||||||
|
Free(coop);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const static IoFunctions Functions = {
|
||||||
|
.read = ReadStreamReader,
|
||||||
|
.seek = SeekStreamReader,
|
||||||
|
.write = WriteStreamReader,
|
||||||
|
.close = CloseStreamReader,
|
||||||
|
};
|
||||||
|
|
||||||
Stream *
|
Stream *
|
||||||
StrStreamReaderN(char *buffer, int n)
|
StrStreamReaderN(char *buffer, int n)
|
||||||
{
|
{
|
||||||
if (!buffer || n < 0)
|
Io *raw_io;
|
||||||
|
ReaderCookie *cookie;
|
||||||
|
if (!buffer)
|
||||||
{
|
{
|
||||||
return NULL;
|
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)
|
ReadStreamWriter(void *coop, void *to, size_t n)
|
||||||
{
|
{
|
||||||
/* Reading from a stream writer is silly. */
|
/* Reading from a stream writer is silly. */
|
||||||
(void) coop;
|
|
||||||
(void) to;
|
|
||||||
(void) n;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static ssize_t
|
static ssize_t
|
||||||
|
|
@ -36,9 +33,6 @@ static off_t
|
||||||
SeekStreamWriter(void *coop, off_t mag, int sgn)
|
SeekStreamWriter(void *coop, off_t mag, int sgn)
|
||||||
{
|
{
|
||||||
/* TODO: Seeking would be useful, though not supported yet. */
|
/* TODO: Seeking would be useful, though not supported yet. */
|
||||||
(void) coop;
|
|
||||||
(void) mag;
|
|
||||||
(void) sgn;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,11 +40,10 @@ static int
|
||||||
CloseStreamWriter(void *coop)
|
CloseStreamWriter(void *coop)
|
||||||
{
|
{
|
||||||
/* Nothing to free as of now. */
|
/* Nothing to free as of now. */
|
||||||
(void) coop;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const IoFunctions Functions = {
|
const static IoFunctions Functions = {
|
||||||
.read = ReadStreamWriter,
|
.read = ReadStreamWriter,
|
||||||
.seek = SeekStreamWriter,
|
.seek = SeekStreamWriter,
|
||||||
.write = WriteStreamWriter,
|
.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 = rect;
|
||||||
ret.end_line--;
|
ret.end_line--;
|
||||||
|
|
||||||
/* TODO: You can easily craft strings that conceal data(
|
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace(ch))
|
||||||
* > Please mind the whitespace
|
|
||||||
* > hidden we're concealing data to Matrix
|
|
||||||
* > star in Well, you can still stare at the body and XML
|
|
||||||
* > four but that's for Nerds
|
|
||||||
* > seasons See, Touhou reference!
|
|
||||||
* concealing!) */
|
|
||||||
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace((int) ch))
|
|
||||||
{
|
{
|
||||||
shift_by++;
|
shift_by++;
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +125,7 @@ DecodeSpan(StringRect rect, char del, size_t *skip)
|
||||||
{
|
{
|
||||||
return StrFullRect(NULL);
|
return StrFullRect(NULL);
|
||||||
}
|
}
|
||||||
if (!ret.source_lines && isspace((int) c))
|
if (!ret.source_lines && isspace(c))
|
||||||
{
|
{
|
||||||
return StrFullRect(NULL);
|
return StrFullRect(NULL);
|
||||||
}
|
}
|
||||||
|
|
@ -329,8 +322,8 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
||||||
break;
|
break;
|
||||||
case XEP393_MONO:
|
case XEP393_MONO:
|
||||||
head = XMLCreateTag("code");
|
head = XMLCreateTag("code");
|
||||||
XMLAddChild(xmlparent, XMLCreateText("`"));
|
|
||||||
XMLAddChild(xmlparent, head);
|
XMLAddChild(xmlparent, head);
|
||||||
|
XMLAddChild(head, XMLCreateText("`"));
|
||||||
break;
|
break;
|
||||||
case XEP393_SRKE:
|
case XEP393_SRKE:
|
||||||
head = XMLCreateTag("s");
|
head = XMLCreateTag("s");
|
||||||
|
|
@ -372,7 +365,7 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
||||||
XMLAddChild(head, XMLCreateText("_"));
|
XMLAddChild(head, XMLCreateText("_"));
|
||||||
break;
|
break;
|
||||||
case XEP393_MONO:
|
case XEP393_MONO:
|
||||||
XMLAddChild(xmlparent, XMLCreateText("`"));
|
XMLAddChild(head, XMLCreateText("`"));
|
||||||
break;
|
break;
|
||||||
case XEP393_SRKE:
|
case XEP393_SRKE:
|
||||||
XMLAddChild(head, XMLCreateText("~"));
|
XMLAddChild(head, XMLCreateText("~"));
|
||||||
|
|
@ -399,37 +392,21 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
|
||||||
char *
|
char *
|
||||||
XEP393ToXMLString(XEP393Element *xepd)
|
XEP393ToXMLString(XEP393Element *xepd)
|
||||||
{
|
{
|
||||||
XMLElement *root, *act_root;
|
XMLElement *root;
|
||||||
XMLElement *child;
|
|
||||||
|
|
||||||
Stream *writer;
|
Stream *writer;
|
||||||
char *ret = NULL;
|
char *ret = NULL;
|
||||||
size_t i, children;
|
|
||||||
if (!xepd)
|
if (!xepd)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
root = XMLCreateTag("span");
|
root = XMLCreateTag("span");
|
||||||
act_root = root;
|
|
||||||
ShoveXML(xepd, root);
|
ShoveXML(xepd, root);
|
||||||
|
|
||||||
writer = StrStreamWriter(&ret);
|
writer = StrStreamWriter(&ret);
|
||||||
children = ArraySize(root->children);
|
XMLEncode(writer, root);
|
||||||
|
XMLFreeElement(root);
|
||||||
child = ArrayGet(root->children, 0);
|
|
||||||
if (children == 1 && StrEquals(child->name, "p"))
|
|
||||||
{
|
|
||||||
children = ArraySize(child->children);
|
|
||||||
root = child;
|
|
||||||
}
|
|
||||||
for (i = 0; i < children; i++)
|
|
||||||
{
|
|
||||||
child = ArrayGet(root->children, i);
|
|
||||||
|
|
||||||
XMLEncode(writer, child);
|
|
||||||
}
|
|
||||||
XMLFreeElement(act_root);
|
|
||||||
StreamFlush(writer);
|
StreamFlush(writer);
|
||||||
StreamClose(writer);
|
StreamClose(writer);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,6 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
||||||
bool flag = false;
|
bool flag = false;
|
||||||
switch (event->type)
|
switch (event->type)
|
||||||
{
|
{
|
||||||
case XML_ERROR:
|
|
||||||
XMLFreeEvent(event);
|
|
||||||
XMLFreeElement(ret);
|
|
||||||
ArrayFree(stack);
|
|
||||||
XMLFreeLexer(lexer);
|
|
||||||
return NULL;
|
|
||||||
case XML_LEXER_STARTELEM:
|
case XML_LEXER_STARTELEM:
|
||||||
/* Create a new element that will populated. */
|
/* Create a new element that will populated. */
|
||||||
top = XMLCreateTag(event->element);
|
top = XMLCreateTag(event->element);
|
||||||
|
|
@ -120,19 +114,9 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
|
||||||
void
|
void
|
||||||
XMLEncodeString(Stream *stream, char *data)
|
XMLEncodeString(Stream *stream, char *data)
|
||||||
{
|
{
|
||||||
size_t i, len;
|
size_t i;
|
||||||
|
|
||||||
if (!stream || !data)
|
for (i = 0; i < strlen(data); i++)
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: I should write a "Parsee Best Practice" guideline and make sure
|
|
||||||
* people understand to NOT constantly recompute lengths parameter on
|
|
||||||
* these kinds of loops. ArraySize is fine(since its indirection), but
|
|
||||||
* operations like strlen take time! */
|
|
||||||
len = strlen(data);
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
{
|
{
|
||||||
char c = data[i];
|
char c = data[i];
|
||||||
if (c == '<')
|
if (c == '<')
|
||||||
|
|
@ -161,9 +145,6 @@ XMLEncodeString(Stream *stream, char *data)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
StreamPrintf(stream, "%c", c);
|
StreamPrintf(stream, "%c", c);
|
||||||
/* TODO: Maybe consider Unistrings and encode arbitrary Unicode
|
|
||||||
* codepoints * with special XML. Oughta make it printable, you know?
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@ static char * XMLPopElement(XMLexer *lexer);
|
||||||
static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs);
|
static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs);
|
||||||
static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs);
|
static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs);
|
||||||
static XMLEvent * XMLCreateRelax(XMLexer *lexer);
|
static XMLEvent * XMLCreateRelax(XMLexer *lexer);
|
||||||
static XMLEvent * XMLCreateError(XMLexer *lexer);
|
|
||||||
static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end);
|
static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end);
|
||||||
static XMLEvent * XMLCreateData(XMLexer *lexer);
|
static XMLEvent * XMLCreateData(XMLexer *lexer);
|
||||||
|
|
||||||
|
|
@ -199,9 +198,7 @@ XMLCrank(XMLexer *lexer)
|
||||||
else if (XMLookahead(lexer, "--", false))
|
else if (XMLookahead(lexer, "--", false))
|
||||||
{
|
{
|
||||||
/* Throw error */
|
/* Throw error */
|
||||||
XMLFreeEvent(event);
|
return NULL;
|
||||||
event = XMLCreateError(lexer);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XML_STATE_PI:
|
case XML_STATE_PI:
|
||||||
|
|
@ -218,9 +215,6 @@ XMLCrank(XMLexer *lexer)
|
||||||
if (!attrname)
|
if (!attrname)
|
||||||
{
|
{
|
||||||
/* TODO: Throw error */
|
/* TODO: Throw error */
|
||||||
XMLFreeEvent(event);
|
|
||||||
event = XMLCreateError(lexer);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
XMLPushElement(lexer, attrname);
|
XMLPushElement(lexer, attrname);
|
||||||
|
|
||||||
|
|
@ -247,10 +241,7 @@ XMLCrank(XMLexer *lexer)
|
||||||
}
|
}
|
||||||
else if (XMLookahead(lexer, "'", true))
|
else if (XMLookahead(lexer, "'", true))
|
||||||
{
|
{
|
||||||
//while (true); uh oh
|
while (true);
|
||||||
XMLFreeEvent(event);
|
|
||||||
event = XMLCreateError(lexer);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XML_STATE_ATTRTAIL:
|
case XML_STATE_ATTRTAIL:
|
||||||
|
|
@ -259,8 +250,6 @@ XMLCrank(XMLexer *lexer)
|
||||||
if (!XMLookahead(lexer, ">", true))
|
if (!XMLookahead(lexer, ">", true))
|
||||||
{
|
{
|
||||||
/* TODO: Throw error. */
|
/* TODO: Throw error. */
|
||||||
XMLFreeEvent(event);
|
|
||||||
event = XMLCreateError(lexer);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
lexer->state = XML_STATE_NONE;
|
lexer->state = XML_STATE_NONE;
|
||||||
|
|
@ -269,8 +258,6 @@ XMLCrank(XMLexer *lexer)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* TODO */
|
/* TODO */
|
||||||
XMLFreeEvent(event);
|
|
||||||
event = XMLCreateError(lexer);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* TODO: Crank our XML parser. */
|
/* TODO: Crank our XML parser. */
|
||||||
|
|
@ -308,7 +295,7 @@ static bool
|
||||||
XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
||||||
{
|
{
|
||||||
int *stack;
|
int *stack;
|
||||||
size_t top, i, len;
|
size_t top, i;
|
||||||
ssize_t ntop;
|
ssize_t ntop;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
if (!lexer || !str)
|
if (!lexer || !str)
|
||||||
|
|
@ -317,10 +304,9 @@ XMLookahead(XMLexer *lexer, const char *str, bool skip)
|
||||||
}
|
}
|
||||||
|
|
||||||
top = 0;
|
top = 0;
|
||||||
len = strlen(str);
|
stack = Malloc(strlen(str) * sizeof(*stack));
|
||||||
stack = Malloc(len * sizeof(*stack));
|
|
||||||
|
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < strlen(str); i++)
|
||||||
{
|
{
|
||||||
char c = str[i];
|
char c = str[i];
|
||||||
int getc = XMLGetc(lexer);
|
int getc = XMLGetc(lexer);
|
||||||
|
|
@ -350,8 +336,8 @@ seekback:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define IsNamestart(c) ((c == ':') || isalpha((int) c) || (c == '_'))
|
#define IsNamestart(c) ((c == ':') || isalpha(c) || (c == '_'))
|
||||||
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit((int) c))
|
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit(c))
|
||||||
static char *
|
static char *
|
||||||
XMLParseName(XMLexer *lexer)
|
XMLParseName(XMLexer *lexer)
|
||||||
{
|
{
|
||||||
|
|
@ -596,8 +582,6 @@ XMLCreateEnd(XMLexer *lexer, char *end)
|
||||||
event->col = 0;
|
event->col = 0;
|
||||||
event->offset = 0;
|
event->offset = 0;
|
||||||
|
|
||||||
(void) lexer;
|
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
static XMLEvent *
|
static XMLEvent *
|
||||||
|
|
@ -706,26 +690,6 @@ XMLCreateData(XMLexer *lexer)
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
static XMLEvent *
|
static XMLEvent *
|
||||||
XMLCreateError(XMLexer *lexer)
|
|
||||||
{
|
|
||||||
XMLEvent *event = Malloc(sizeof(*event));
|
|
||||||
size_t elements = ArraySize(lexer->data.elements);
|
|
||||||
|
|
||||||
event->type = XML_ERROR;
|
|
||||||
event->element = elements ?
|
|
||||||
StrDuplicate(ArrayGet(lexer->data.elements, elements - 1)) :
|
|
||||||
NULL;
|
|
||||||
event->attrs = NULL;
|
|
||||||
event->data = NULL;
|
|
||||||
|
|
||||||
/* TODO */
|
|
||||||
event->line = 0;
|
|
||||||
event->col = 0;
|
|
||||||
event->offset = 0;
|
|
||||||
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
static XMLEvent *
|
|
||||||
XMLCreateRelax(XMLexer *lexer)
|
XMLCreateRelax(XMLexer *lexer)
|
||||||
{
|
{
|
||||||
XMLEvent *event = Malloc(sizeof(*event));
|
XMLEvent *event = Malloc(sizeof(*event));
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,13 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
|
||||||
#include <StringStream.h>
|
|
||||||
#include <Parsee.h>
|
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
||||||
/* The default component port Prosody uses. */
|
/* The default component port Prosody uses. */
|
||||||
#define DEFAULT_PROSODY_PORT 5347
|
#define DEFAULT_PROSODY_PORT 5347
|
||||||
|
|
||||||
XMPPComponent *
|
XMPPComponent *
|
||||||
XMPPInitialiseCompStream(char *addr, char *host, int port)
|
XMPPInitialiseCompStream(char *host, int port)
|
||||||
{
|
{
|
||||||
int sd = -1;
|
int sd = -1;
|
||||||
struct addrinfo hints, *res, *res0;
|
struct addrinfo hints, *res, *res0;
|
||||||
|
|
@ -28,26 +26,12 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||||
Stream *stream;
|
Stream *stream;
|
||||||
XMPPComponent *comp;
|
XMPPComponent *comp;
|
||||||
|
|
||||||
if (!addr)
|
|
||||||
{
|
|
||||||
addr = host;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT);
|
snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT);
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
memset(&hints, 0, sizeof(hints));
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
error = getaddrinfo(addr, serv, &hints, &res0);
|
error = getaddrinfo(host, serv, &hints, &res0);
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
const char *error_str = gai_strerror(error);
|
|
||||||
Log(LOG_ERR,
|
|
||||||
"%s: cannot connect to '%s': %s", __func__,
|
|
||||||
host, error_str
|
|
||||||
);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (res = res0; res; res = res->ai_next)
|
for (res = res0; res; res = res->ai_next)
|
||||||
{
|
{
|
||||||
|
|
@ -61,10 +45,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||||
|
|
||||||
if (connect(sd, res->ai_addr, res->ai_addrlen) < 0)
|
if (connect(sd, res->ai_addr, res->ai_addrlen) < 0)
|
||||||
{
|
{
|
||||||
Log(LOG_ERR,
|
|
||||||
"%s: cannot connect to '%s': %s", __func__,
|
|
||||||
host, strerror(errno)
|
|
||||||
);
|
|
||||||
close(sd);
|
close(sd);
|
||||||
sd = -1;
|
sd = -1;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -75,10 +55,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||||
|
|
||||||
if (sd < 0)
|
if (sd < 0)
|
||||||
{
|
{
|
||||||
Log(LOG_ERR,
|
|
||||||
"%s: cannot connect to '%s': no socket available", __func__,
|
|
||||||
host
|
|
||||||
);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
freeaddrinfo(res0);
|
freeaddrinfo(res0);
|
||||||
|
|
@ -86,10 +62,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||||
stream = StreamFd(sd);
|
stream = StreamFd(sd);
|
||||||
if (!stream)
|
if (!stream)
|
||||||
{
|
{
|
||||||
Log(LOG_ERR,
|
|
||||||
"%s: cannot connect to '%s': %s", __func__,
|
|
||||||
host, "couldn't create a Cytoplasm stream"
|
|
||||||
);
|
|
||||||
close(sd);
|
close(sd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -104,15 +76,19 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
|
||||||
return comp;
|
return comp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
static char *
|
static char *
|
||||||
ComputeHandshake(char *shared, char *stream)
|
ComputeHandshake(char *shared, char *stream)
|
||||||
{
|
{
|
||||||
char *source;
|
char *source;
|
||||||
|
unsigned char *raw_sha;
|
||||||
char *sha;
|
char *sha;
|
||||||
|
|
||||||
source = StrConcat(2, stream, shared);
|
source = StrConcat(2, stream, shared);
|
||||||
sha = ParseeSHA1(source);
|
raw_sha = Sha1(source);
|
||||||
|
sha = ShaToHex(raw_sha);
|
||||||
|
|
||||||
|
Free(raw_sha);
|
||||||
Free(source);
|
Free(source);
|
||||||
|
|
||||||
return sha;
|
return sha;
|
||||||
|
|
@ -120,6 +96,7 @@ ComputeHandshake(char *shared, char *stream)
|
||||||
bool
|
bool
|
||||||
XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
{
|
{
|
||||||
|
/* TODO */
|
||||||
XMLexer *sax;
|
XMLexer *sax;
|
||||||
XMLEvent *ev;
|
XMLEvent *ev;
|
||||||
char *stream_id, *handshake;
|
char *stream_id, *handshake;
|
||||||
|
|
@ -152,7 +129,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!ev || ev->type != XML_LEXER_STARTELEM ||
|
if (ev->type != XML_LEXER_STARTELEM ||
|
||||||
!StrEquals(ev->element, "stream:stream"))
|
!StrEquals(ev->element, "stream:stream"))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Excepted stream:stream element.");
|
Log(LOG_ERR, "Excepted stream:stream element.");
|
||||||
|
|
@ -163,7 +140,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
stream_id = StrDuplicate(HashMapGet(ev->attrs, "id"));
|
stream_id = StrDuplicate(HashMapGet(ev->attrs, "id"));
|
||||||
handshake = ComputeHandshake(shared, stream_id);
|
handshake = ComputeHandshake(shared, stream_id);
|
||||||
|
|
||||||
Log(LOG_DEBUG, "- sID='%s'", stream_id);
|
Log(LOG_NOTICE, "- sID='%s'", stream_id);
|
||||||
StreamPrintf(stream, "<handshake>%s</handshake>", handshake);
|
StreamPrintf(stream, "<handshake>%s</handshake>", handshake);
|
||||||
StreamFlush(stream);
|
StreamFlush(stream);
|
||||||
XMLFreeEvent(ev);
|
XMLFreeEvent(ev);
|
||||||
|
|
@ -177,54 +154,11 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!ev || ev->type != XML_LEXER_ELEM ||
|
if (ev->type != XML_LEXER_ELEM ||
|
||||||
!StrEquals(ev->element, "handshake"))
|
!StrEquals(ev->element, "handshake"))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Excepted empty handshake reply, got nonsense.");
|
Log(LOG_ERR, "Excepted empty handshake reply, got nonsense.");
|
||||||
Log(LOG_ERR, "Another service (possibly Parsee) may have taken over.");
|
Log(LOG_ERR, "Another service (possibly Parsee) may have taken over.");
|
||||||
while ((ev = XMLCrank(sax)))
|
|
||||||
{
|
|
||||||
char *key, *val;
|
|
||||||
switch (ev->type)
|
|
||||||
{
|
|
||||||
case XML_LEXER_STARTELEM:
|
|
||||||
Log(LOG_DEBUG, "<%s>", ev->element);
|
|
||||||
|
|
||||||
LogConfigIndent(LogConfigGlobal());
|
|
||||||
LogConfigIndent(LogConfigGlobal());
|
|
||||||
/* TODO: Log out attributes a little better */
|
|
||||||
while (HashMapIterate(ev->attrs, &key, (void **) &val))
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "(%s=%s)", key, val);
|
|
||||||
}
|
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
|
||||||
|
|
||||||
LogConfigIndent(LogConfigGlobal());
|
|
||||||
break;
|
|
||||||
case XML_LEXER_ELEM:
|
|
||||||
Log(LOG_DEBUG, "<%s/>", ev->element);
|
|
||||||
LogConfigIndent(LogConfigGlobal());
|
|
||||||
LogConfigIndent(LogConfigGlobal());
|
|
||||||
/* TODO: Log out attributes a little better */
|
|
||||||
while (HashMapIterate(ev->attrs, &key, (void **) &val))
|
|
||||||
{
|
|
||||||
Log(LOG_DEBUG, "(%s=%s)", key, val);
|
|
||||||
}
|
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
|
||||||
break;
|
|
||||||
case XML_LEXER_ENDELEM:
|
|
||||||
LogConfigUnindent(LogConfigGlobal());
|
|
||||||
Log(LOG_DEBUG, "</%s>", ev->element);
|
|
||||||
break;
|
|
||||||
case XML_LEXER_DATA:
|
|
||||||
Log(LOG_DEBUG, "%s", ev->data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
XMLFreeEvent(ev);
|
|
||||||
}
|
|
||||||
LogConfigIndentSet(LogConfigGlobal(), 0);
|
|
||||||
Log(LOG_ERR, "");
|
Log(LOG_ERR, "");
|
||||||
Log(LOG_ERR, "Simply jealous of that other service...");
|
Log(LOG_ERR, "Simply jealous of that other service...");
|
||||||
Free(stream_id);
|
Free(stream_id);
|
||||||
|
|
@ -235,7 +169,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
|
||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
/* We can uhh, send stanzas, and receive them! */
|
/* We can uhh, send stanzas, and receive them! */
|
||||||
Log(LOG_DEBUG, "Communications to '%s' established.", as);
|
Log(LOG_INFO, "Communications to '%s' established.", as);
|
||||||
|
|
||||||
XMLFreeEvent(ev);
|
XMLFreeEvent(ev);
|
||||||
Free(stream_id);
|
Free(stream_id);
|
||||||
|
|
@ -265,43 +199,8 @@ XMPPEndCompStream(XMPPComponent *comp)
|
||||||
{
|
{
|
||||||
return;
|
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);
|
pthread_mutex_destroy(&comp->write_lock);
|
||||||
|
StreamClose(comp->stream);
|
||||||
Free(comp->host);
|
Free(comp->host);
|
||||||
Free(comp);
|
Free(comp);
|
||||||
}
|
}
|
||||||
void
|
|
||||||
XMPPSendStanza(XMPPComponent *comp, XMLElement *stanza, size_t max)
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
char *c = NULL;
|
|
||||||
Stream *stringWriter;
|
|
||||||
if (!comp || !stanza)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stringWriter = StrStreamWriter(&c);
|
|
||||||
XMLEncode(stringWriter, stanza);
|
|
||||||
StreamFlush(stringWriter);
|
|
||||||
StreamClose(stringWriter);
|
|
||||||
if (c && max && (len = strlen(c)) > max)
|
|
||||||
{
|
|
||||||
Log(LOG_WARNING,
|
|
||||||
"Unexceptedly large stanza received (len=%d max=%d).",
|
|
||||||
(int) len, (int) max
|
|
||||||
);
|
|
||||||
Free(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pthread_mutex_lock(&comp->write_lock);
|
|
||||||
StreamPrintf(comp->stream, "%s", c);
|
|
||||||
StreamFlush(comp->stream);
|
|
||||||
pthread_mutex_unlock(&comp->write_lock);
|
|
||||||
Free(c);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
136
src/XMPP/MUC.c
136
src/XMPP/MUC.c
|
|
@ -8,8 +8,6 @@
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
||||||
#include "XMPPThread/internal.h"
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
{
|
{
|
||||||
|
|
@ -20,6 +18,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&jabber->write_lock);
|
||||||
|
|
||||||
iq_query = XMLCreateTag("iq");
|
iq_query = XMLCreateTag("iq");
|
||||||
query = XMLCreateTag("query");
|
query = XMLCreateTag("query");
|
||||||
|
|
||||||
|
|
@ -35,7 +35,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
|
|
||||||
{
|
{
|
||||||
XMLElement *identity;
|
XMLElement *identity;
|
||||||
XMPPSendStanza(jabber, iq_query, 10000);
|
XMLEncode(jabber->stream, iq_query);
|
||||||
|
StreamFlush(jabber->stream);
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
|
|
||||||
/* Except an IQ reply. 10 seconds of timeout is pretty
|
/* Except an IQ reply. 10 seconds of timeout is pretty
|
||||||
|
|
@ -44,8 +45,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
Free(uuid);
|
Free(uuid);
|
||||||
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
if (!iq_query || !StrEquals(iq_query->name, "iq"))
|
||||||
{
|
{
|
||||||
Log(LOG_ERR, "Didn't receive an <iq> stanza");
|
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
|
pthread_mutex_unlock(&jabber->write_lock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
query = XMLookForUnique(iq_query, "query");
|
query = XMLookForUnique(iq_query, "query");
|
||||||
|
|
@ -56,11 +57,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
"conference"))
|
"conference"))
|
||||||
{
|
{
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
Log(LOG_DEBUG, "MUC INFO ERROR");
|
pthread_mutex_unlock(&jabber->write_lock);
|
||||||
Log(LOG_DEBUG,
|
|
||||||
"identityp=%p category=%s", identity,
|
|
||||||
identity ? HashMapGet(identity->attrs, "category") : NULL
|
|
||||||
);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,6 +73,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
|
||||||
XMLFreeElement(iq_query);
|
XMLFreeElement(iq_query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&jabber->write_lock);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,6 +116,7 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&jabber->write_lock);
|
||||||
stanza = XMLCreateTag("message");
|
stanza = XMLCreateTag("message");
|
||||||
XMLAddAttr(stanza, "id", (identifier = StrRandom(32)));
|
XMLAddAttr(stanza, "id", (identifier = StrRandom(32)));
|
||||||
XMLAddAttr(stanza, "from", from);
|
XMLAddAttr(stanza, "from", from);
|
||||||
|
|
@ -161,123 +160,10 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
|
||||||
}
|
}
|
||||||
XMLAddChild(stanza, x);
|
XMLAddChild(stanza, x);
|
||||||
}
|
}
|
||||||
XMPPSendStanza(jabber, stanza, 10000);
|
XMLEncode(jabber->stream, stanza);
|
||||||
|
StreamFlush(jabber->stream);
|
||||||
|
pthread_mutex_unlock(&jabber->write_lock);
|
||||||
|
|
||||||
XMLFreeElement(stanza);
|
XMLFreeElement(stanza);
|
||||||
Free(identifier);
|
Free(identifier);
|
||||||
}
|
}
|
||||||
bool
|
|
||||||
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret)
|
|
||||||
{
|
|
||||||
XMLElement *presence, *x, *reply, *history, *photo;
|
|
||||||
IQFeatures *features;
|
|
||||||
char *from, *id, *stime = "3600";
|
|
||||||
if (!comp || !fr || !muc)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
presence = XMLCreateTag("presence");
|
|
||||||
x = XMLCreateTag("x");
|
|
||||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
||||||
XMLAddAttr(presence, "to", muc);
|
|
||||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
|
||||||
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
|
||||||
history = XMLCreateTag("history");
|
|
||||||
|
|
||||||
if (time > 0)
|
|
||||||
{
|
|
||||||
stime = StrInt(time);
|
|
||||||
}
|
|
||||||
XMLAddAttr(history, "seconds", stime);
|
|
||||||
if (time > 0)
|
|
||||||
{
|
|
||||||
Free(stime);
|
|
||||||
stime = NULL;
|
|
||||||
}
|
|
||||||
XMLAddChild(x, history);
|
|
||||||
|
|
||||||
XMLAddChild(presence, x);
|
|
||||||
features = LookupJIDFeatures(from);
|
|
||||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
|
||||||
IQ_IDENTITY
|
|
||||||
#undef IdentitySimple
|
|
||||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
|
||||||
IQ_ADVERT
|
|
||||||
#undef AdvertiseSimple
|
|
||||||
XMPPAnnotatePresence(presence, features);
|
|
||||||
FreeIQFeatures(features);
|
|
||||||
|
|
||||||
if (hash)
|
|
||||||
{
|
|
||||||
x = XMLCreateTag("x");
|
|
||||||
XMLAddAttr(x, "xmlns", "vcard-temp:x:update");
|
|
||||||
photo = XMLCreateTag("photo");
|
|
||||||
XMLAddChild(photo, XMLCreateText(hash));
|
|
||||||
XMLAddChild(x, photo);
|
|
||||||
XMLAddChild(presence, x);
|
|
||||||
}
|
|
||||||
|
|
||||||
XMPPSendStanza(comp, presence, 10000);
|
|
||||||
XMLFreeElement(presence);
|
|
||||||
Free(from);
|
|
||||||
|
|
||||||
if (ret && (reply = ParseeAwaitStanza(id, 500)))
|
|
||||||
{
|
|
||||||
bool exit_code = true;
|
|
||||||
|
|
||||||
if (XMPPHasError(reply, "conflict"))
|
|
||||||
{
|
|
||||||
exit_code = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
XMLFreeElement(reply);
|
|
||||||
Free(id);
|
|
||||||
return exit_code;
|
|
||||||
}
|
|
||||||
Free(id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
|
||||||
{
|
|
||||||
XMLElement *presence;
|
|
||||||
IQFeatures *features;
|
|
||||||
char *from, *id;
|
|
||||||
if (!comp || !fr || !muc)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
presence = XMLCreateTag("presence");
|
|
||||||
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
|
||||||
XMLAddAttr(presence, "to", muc);
|
|
||||||
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
|
||||||
XMLAddAttr(presence, "type", "unavailable");
|
|
||||||
|
|
||||||
if (reason)
|
|
||||||
{
|
|
||||||
XMLElement *status = XMLCreateTag("status");
|
|
||||||
XMLElement *string = XMLCreateText(reason);
|
|
||||||
|
|
||||||
XMLAddChild(status, string);
|
|
||||||
XMLAddChild(presence, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
features = LookupJIDFeatures(from);
|
|
||||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
|
||||||
IQ_IDENTITY
|
|
||||||
#undef IdentitySimple
|
|
||||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
|
||||||
IQ_ADVERT
|
|
||||||
#undef AdvertiseSimple
|
|
||||||
XMPPAnnotatePresence(presence, features);
|
|
||||||
FreeIQFeatures(features);
|
|
||||||
|
|
||||||
|
|
||||||
XMPPSendStanza(comp, presence, 10000);
|
|
||||||
|
|
||||||
XMLFreeElement(presence);
|
|
||||||
Free(from);
|
|
||||||
Free(id);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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 <Parsee.h>
|
||||||
#include <XML.h>
|
#include <XML.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
|
||||||
|
|
@ -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);
|
XMLFreeElement(message);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&comp->write_lock);
|
||||||
|
|
||||||
Free(from);
|
Free(from);
|
||||||
Free(ident);
|
Free(ident);
|
||||||
}
|
}
|
||||||
|
void
|
||||||
|
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
|
||||||
|
{
|
||||||
|
XMLElement *presence;
|
||||||
|
char *from, *id;
|
||||||
|
if (!comp || !fr || !muc)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&comp->write_lock);
|
||||||
|
|
||||||
|
presence = XMLCreateTag("presence");
|
||||||
|
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||||
|
XMLAddAttr(presence, "to", muc);
|
||||||
|
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||||
|
XMLAddAttr(presence, "type", "unavailable");
|
||||||
|
|
||||||
|
if (reason)
|
||||||
|
{
|
||||||
|
XMLElement *status = XMLCreateTag("status");
|
||||||
|
XMLElement *string = XMLCreateText(reason);
|
||||||
|
|
||||||
|
XMLAddChild(status, string);
|
||||||
|
XMLAddChild(presence, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
XMPPAnnotatePresence(presence);
|
||||||
|
|
||||||
|
XMLEncode(comp->stream, presence);
|
||||||
|
StreamFlush(comp->stream);
|
||||||
|
|
||||||
|
XMLFreeElement(presence);
|
||||||
|
Free(from);
|
||||||
|
Free(id);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&comp->write_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc)
|
||||||
|
{
|
||||||
|
XMLElement *presence, *x, *reply;
|
||||||
|
char *from, *id;
|
||||||
|
if (!comp || !fr || !muc)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&comp->write_lock);
|
||||||
|
|
||||||
|
presence = XMLCreateTag("presence");
|
||||||
|
x = XMLCreateTag("x");
|
||||||
|
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
|
||||||
|
XMLAddAttr(presence, "to", muc);
|
||||||
|
XMLAddAttr(presence, "id", (id = StrRandom(8)));
|
||||||
|
XMLAddAttr(x, "xmlns", "http://jabber.org/protocol/muc");
|
||||||
|
|
||||||
|
XMLAddChild(presence, x);
|
||||||
|
XMPPAnnotatePresence(presence);
|
||||||
|
|
||||||
|
XMLEncode(comp->stream, presence);
|
||||||
|
StreamFlush(comp->stream);
|
||||||
|
|
||||||
|
XMLFreeElement(presence);
|
||||||
|
Free(from);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&comp->write_lock);
|
||||||
|
|
||||||
|
/*if ((reply = ParseeAwaitStanza(id, 500)))
|
||||||
|
{
|
||||||
|
bool exit_code = true;
|
||||||
|
|
||||||
|
if (XMPPHasError(reply, "conflict"))
|
||||||
|
{
|
||||||
|
Log(LOG_WARNING, "UNIMPLEMENTED NAMING CONFLICT.");
|
||||||
|
exit_code = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLFreeElement(reply);
|
||||||
|
Free(id);
|
||||||
|
return exit_code;
|
||||||
|
}*/
|
||||||
|
(void) reply;
|
||||||
|
Free(id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
bool
|
bool
|
||||||
XMPPIsParseeStanza(XMLElement *stanza)
|
XMPPIsParseeStanza(XMLElement *stanza)
|
||||||
{
|
{
|
||||||
|
|
@ -83,8 +171,6 @@ XMPPIsParseeStanza(XMLElement *stanza)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Check if the user is a trustworthy Parsee puppet instead of some
|
|
||||||
* guy sending random stanzas */
|
|
||||||
return !!XMLookForUnique(stanza, "x-parsee");
|
return !!XMLookForUnique(stanza, "x-parsee");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,31 +247,27 @@ XMPPGetReply(XMLElement *elem)
|
||||||
|
|
||||||
return HashMapGet(rep->attrs, "id");
|
return HashMapGet(rep->attrs, "id");
|
||||||
}
|
}
|
||||||
ssize_t
|
void
|
||||||
XMPPGetReplyOffset(XMLElement *elem)
|
XMPPAnnotatePresence(XMLElement *presence)
|
||||||
{
|
{
|
||||||
if (!elem)
|
XMLElement *c;
|
||||||
|
char *ver;
|
||||||
|
if (!presence)
|
||||||
{
|
{
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < ArraySize(elem->children); i++)
|
|
||||||
{
|
ver = XMPPGenerateVer();
|
||||||
XMLElement *child = ArrayGet(elem->children, i);
|
c = XMLCreateTag("c");
|
||||||
char *xmlns = HashMapGet(child->attrs, "xmlns");
|
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
||||||
char *xfor = HashMapGet(child->attrs, "for");
|
XMLAddAttr(c, "hash", "sha-1");
|
||||||
if (StrEquals(child->name, "fallback") &&
|
XMLAddAttr(c, "node", REPOSITORY);
|
||||||
StrEquals(xmlns, "urn:xmpp:feature-fallback:0") &&
|
XMLAddAttr(c, "ver", ver);
|
||||||
StrEquals(xfor, "urn:xmpp:reply:0"))
|
|
||||||
{
|
Free(ver);
|
||||||
XMLElement *body = XMLookForUnique(child, "body");
|
XMLAddChild(presence, c);
|
||||||
if (body && HashMapGet(body->attrs, "end"))
|
|
||||||
{
|
|
||||||
return strtol(HashMapGet(body->attrs, "end"), NULL, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
XMPPGetModeration(XMLElement *stanza)
|
XMPPGetModeration(XMLElement *stanza)
|
||||||
{
|
{
|
||||||
|
|
@ -239,51 +321,35 @@ XMPPHasError(XMLElement *stanza, char *type)
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLElement *
|
XMLElement *
|
||||||
XMPPSendDisco(ParseeData *data, char *from, char *to)
|
XMPPSendDisco(XMPPComponent *jabber, char *from, char *to)
|
||||||
{
|
{
|
||||||
XMPPComponent *jabber = data ? data->jabber : NULL;
|
|
||||||
XMLElement *ret, *iq;
|
XMLElement *ret, *iq;
|
||||||
char *identifier;
|
char *identifier;
|
||||||
|
|
||||||
if (!data || !jabber || !from || !to)
|
if (!jabber || !from || !to)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&data->oidl);
|
|
||||||
if ((ret = HashMapGet(data->oid_servers, to)))
|
|
||||||
{
|
|
||||||
ret = XMLCopy(ret);
|
|
||||||
pthread_mutex_unlock(&data->oidl);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&data->oidl);
|
|
||||||
|
|
||||||
iq = XMLCreateTag("iq");
|
iq = XMLCreateTag("iq");
|
||||||
XMLAddAttr(iq, "type", "get");
|
XMLAddAttr(iq, "type", "get");
|
||||||
XMLAddAttr(iq, "from", from);
|
XMLAddAttr(iq, "from", from);
|
||||||
XMLAddAttr(iq, "to", to);
|
XMLAddAttr(iq, "to", to);
|
||||||
XMLAddAttr(iq, "id", (identifier = StrRandom(64)));
|
XMLAddAttr(iq, "id", (identifier = StrRandom(69)));
|
||||||
{
|
{
|
||||||
XMLElement *query = XMLCreateTag("query");
|
XMLElement *query = XMLCreateTag("query");
|
||||||
XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info");
|
XMLAddAttr(query, "xmlns", "http://jabber.org/protocol/disco#info");
|
||||||
XMLAddChild(iq, query);
|
XMLAddChild(iq, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
ret = ParseeAwaitStanza(identifier, 1.25 SECONDS);
|
ret = ParseeAwaitStanza(identifier, 1.25 SECONDS);
|
||||||
Free(identifier);
|
Free(identifier);
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
/* TODO: On my own instance, disco replies are _really_ expensive.
|
|
||||||
* About 120KB. This is sad. Really sad. */
|
|
||||||
pthread_mutex_lock(&data->oidl);
|
|
||||||
XMLFreeElement(HashMapSet(data->oid_servers, to, ret));
|
|
||||||
ret = XMLCopy(ret);
|
|
||||||
pthread_mutex_unlock(&data->oidl);
|
|
||||||
}
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
bool
|
bool
|
||||||
|
|
|
||||||
|
|
@ -34,17 +34,7 @@ struct XMPPCommandManager {
|
||||||
HashMap *sessions;
|
HashMap *sessions;
|
||||||
|
|
||||||
void *cookie;
|
void *cookie;
|
||||||
|
|
||||||
XMPPCmdFilter filter;
|
|
||||||
};
|
};
|
||||||
static bool
|
|
||||||
XMPPDefaultFilter(XMPPCommandManager *manager, char *id, XMLElement *stanza)
|
|
||||||
{
|
|
||||||
(void) manager;
|
|
||||||
(void) stanza;
|
|
||||||
(void) id;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
static void
|
static void
|
||||||
XMPPDestroySession(XMPPSession *session)
|
XMPPDestroySession(XMPPSession *session)
|
||||||
{
|
{
|
||||||
|
|
@ -128,7 +118,6 @@ XMPPCreateManager(void *cookie)
|
||||||
ret->commands = HashMapCreate();
|
ret->commands = HashMapCreate();
|
||||||
ret->sessions = HashMapCreate();
|
ret->sessions = HashMapCreate();
|
||||||
ret->cookie = cookie;
|
ret->cookie = cookie;
|
||||||
ret->filter = XMPPDefaultFilter;
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -172,20 +161,18 @@ XMPPFreeManager(XMPPCommandManager *manager)
|
||||||
Free(manager);
|
Free(manager);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s)
|
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
|
||||||
{
|
{
|
||||||
char *node_name;
|
char *node_name;
|
||||||
XMPPCommand *val;
|
XMPPCommand *val;
|
||||||
XMLElement *item;
|
XMLElement *item;
|
||||||
if (!m || !p || !jid || !s)
|
if (!m || !p || !jid)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&m->lock);
|
pthread_mutex_lock(&m->lock);
|
||||||
while (HashMapIterate(m->commands, &node_name, (void **) &val))
|
while (HashMapIterate(m->commands, &node_name, (void **) &val))
|
||||||
{
|
|
||||||
if (m->filter(m, node_name, s))
|
|
||||||
{
|
{
|
||||||
item = XMLCreateTag("item");
|
item = XMLCreateTag("item");
|
||||||
XMLAddAttr(item, "jid", jid);
|
XMLAddAttr(item, "jid", jid);
|
||||||
|
|
@ -193,7 +180,6 @@ XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement
|
||||||
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
|
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
|
||||||
XMLAddChild(p, item);
|
XMLAddChild(p, item);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&m->lock);
|
pthread_mutex_unlock(&m->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -220,16 +206,6 @@ XMPPVerifySession(XMPPCommandManager *mgr, char *s_id, char *from, char *to)
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter)
|
|
||||||
{
|
|
||||||
if (!manager || !filter)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
manager->filter = filter;
|
|
||||||
}
|
|
||||||
bool
|
bool
|
||||||
XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
{
|
{
|
||||||
|
|
@ -266,7 +242,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
/* This is an execution. */
|
/* This is an execution. */
|
||||||
cmd = HashMapGet(m->commands, node);
|
cmd = HashMapGet(m->commands, node);
|
||||||
|
|
||||||
if (!cmd || !m->filter(m, node, stanza))
|
if (!cmd)
|
||||||
{
|
{
|
||||||
/* TODO: Set an error note */
|
/* TODO: Set an error note */
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -298,7 +274,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
x = XMPPFormifyCommand(m, cmd, from);
|
x = XMPPFormifyCommand(m, cmd, from);
|
||||||
XMLAddChild(command_xml, x);
|
XMLAddChild(command_xml, x);
|
||||||
|
|
||||||
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);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
goto end;
|
goto end;
|
||||||
|
|
@ -326,7 +305,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
XMPPExecuteCommand(m, cmd, from, command_xml, NULL);
|
XMPPExecuteCommand(m, cmd, from, command_xml, NULL);
|
||||||
XMLAddChild(iq, command_xml);
|
XMLAddChild(iq, command_xml);
|
||||||
|
|
||||||
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);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
InvalidateSession(m, session_id);
|
InvalidateSession(m, session_id);
|
||||||
|
|
@ -366,7 +348,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
|
||||||
XMPPExecuteCommand(m, cmd, from, command_xml, x_form);
|
XMPPExecuteCommand(m, cmd, from, command_xml, x_form);
|
||||||
XMLAddChild(iq, command_xml);
|
XMLAddChild(iq, command_xml);
|
||||||
|
|
||||||
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);
|
XMLFreeElement(iq);
|
||||||
|
|
||||||
InvalidateSession(m, session_given);
|
InvalidateSession(m, session_given);
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ AddAdminCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement
|
||||||
|
|
||||||
ref = DbLock(data->db, 1, "admins");
|
ref = DbLock(data->db, 1, "admins");
|
||||||
admins = GrabArray(DbJson(ref), 1, "admins");
|
admins = GrabArray(DbJson(ref), 1, "admins");
|
||||||
|
|
||||||
ArrayAdd(admins, JsonValueString(glob));
|
ArrayAdd(admins, JsonValueString(glob));
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,19 @@ void
|
||||||
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
{
|
{
|
||||||
ParseeData *data = XMPPGetManagerCookie(m);
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
|
char *trimmed = ParseeTrimJID(from);
|
||||||
size_t i;
|
size_t i;
|
||||||
XMLElement *x;
|
XMLElement *x;
|
||||||
XMLElement *title;
|
XMLElement *title;
|
||||||
XMLElement *reported, *item, *field, *value, *txt;
|
XMLElement *reported, *item, *field, *value, *txt;
|
||||||
|
|
||||||
|
if (!ParseeIsAdmin(data, trimmed))
|
||||||
|
{
|
||||||
|
SetNote("error", "User is not authorised to execute command.");
|
||||||
|
Free(trimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Free(trimmed);
|
||||||
x = XMLCreateTag("x");
|
x = XMLCreateTag("x");
|
||||||
title = XMLCreateTag("title");
|
title = XMLCreateTag("title");
|
||||||
|
|
||||||
|
|
@ -50,7 +58,4 @@ AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
||||||
DbUnlock(data->db, ref);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
XMLAddChild(out, x);
|
XMLAddChild(out, x);
|
||||||
|
|
||||||
(void) form;
|
|
||||||
(void) from;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,4 @@ CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
|
||||||
ParseeCleanup(data);
|
ParseeCleanup(data);
|
||||||
/* TODO: Cleanup old sessions? */
|
/* TODO: Cleanup old sessions? */
|
||||||
SetNote("info", "Parsee data was sucessfully cleant up.");
|
SetNote("info", "Parsee data was sucessfully cleant up.");
|
||||||
|
|
||||||
(void) form;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
||||||
DbRef *admins;
|
DbRef *admins;
|
||||||
Array *admin_list;
|
Array *admin_list;
|
||||||
size_t i;
|
size_t i;
|
||||||
XMPPOption *admin_opt = NULL;
|
XMPPOption *admin_opt;
|
||||||
char *trimmed = ParseeTrimJID(from);
|
char *trimmed = ParseeTrimJID(from);
|
||||||
|
|
||||||
if (!ParseeIsAdmin(data, trimmed))
|
if (!ParseeIsAdmin(data, trimmed))
|
||||||
|
|
@ -104,17 +104,14 @@ FormDelAdminCallback(XMPPCommandManager *m, XMPPCommand *cmd, char *from)
|
||||||
}
|
}
|
||||||
Free(trimmed);
|
Free(trimmed);
|
||||||
|
|
||||||
|
admin_opt = XMPPCreateList(true, false, "glob", "[NVM!]");
|
||||||
|
|
||||||
admins = DbLock(data->db, 1, "admins");
|
admins = DbLock(data->db, 1, "admins");
|
||||||
admin_list = GrabArray(DbJson(admins), 1, "admins");
|
admin_list = GrabArray(DbJson(admins), 1, "admins");
|
||||||
for (i = 0; i < ArraySize(admin_list); i++)
|
for (i = 0; i < ArraySize(admin_list); i++)
|
||||||
{
|
{
|
||||||
char *glob = JsonValueAsString(ArrayGet(admin_list, i));
|
char *glob = JsonValueAsString(ArrayGet(admin_list, i));
|
||||||
|
|
||||||
if (!admin_opt)
|
|
||||||
{
|
|
||||||
admin_opt = XMPPCreateList(true, false, "glob", glob);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
XMPPAddListOption(admin_opt, glob);
|
XMPPAddListOption(admin_opt, glob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
DbUnlock(data->db, ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void) form;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
void
|
void
|
||||||
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
|
||||||
{
|
{
|
||||||
|
ParseeData *data = XMPPGetManagerCookie(m);
|
||||||
char *trimmed = ParseeTrimJID(from);
|
char *trimmed = ParseeTrimJID(from);
|
||||||
size_t alloc = MemoryAllocated();
|
size_t alloc = MemoryAllocated();
|
||||||
size_t kb = alloc >> 10;
|
size_t kb = alloc >> 10;
|
||||||
|
|
@ -25,6 +26,13 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
||||||
XMLElement *x;
|
XMLElement *x;
|
||||||
XMLElement *title, *txt;
|
XMLElement *title, *txt;
|
||||||
|
|
||||||
|
if (!ParseeIsAdmin(data, trimmed))
|
||||||
|
{
|
||||||
|
SetNote("error", "User is not authorised to execute command.");
|
||||||
|
|
||||||
|
Free(trimmed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
Free(trimmed);
|
Free(trimmed);
|
||||||
x = XMLCreateTag("x");
|
x = XMLCreateTag("x");
|
||||||
title = XMLCreateTag("title");
|
title = XMLCreateTag("title");
|
||||||
|
|
@ -67,7 +75,4 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
|
||||||
EndItem();
|
EndItem();
|
||||||
}
|
}
|
||||||
XMLAddChild(out, x);
|
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/Memory.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Log.h>
|
#include <Cytoplasm/Log.h>
|
||||||
|
#include <Cytoplasm/Sha.h>
|
||||||
|
|
||||||
#include <Parsee.h>
|
#include <Parsee.h>
|
||||||
|
|
||||||
|
|
@ -118,12 +119,13 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO: Cache this information. */
|
||||||
bool
|
bool
|
||||||
ServerHasXEP421(ParseeData *data, char *from)
|
ServerHasXEP421(ParseeData *data, char *from)
|
||||||
{
|
{
|
||||||
char *server = NULL, *postserv, *parsee;
|
char *server = NULL, *parsee;
|
||||||
XMLElement *disco;
|
XMLElement *disco;
|
||||||
bool ret = false;
|
bool ret;
|
||||||
if (!data || !from)
|
if (!data || !from)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -138,16 +140,15 @@ ServerHasXEP421(ParseeData *data, char *from)
|
||||||
{
|
{
|
||||||
server++;
|
server++;
|
||||||
}
|
}
|
||||||
|
|
||||||
server = StrDuplicate(server);
|
server = StrDuplicate(server);
|
||||||
postserv = server ? strchr(server, '/') : NULL;
|
|
||||||
if (postserv)
|
if (strchr(server, '/'))
|
||||||
{
|
{
|
||||||
*postserv = '\0';
|
*(strchr(server, '/')) = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
parsee = ParseeJID(data);
|
parsee = ParseeJID(data);
|
||||||
disco = XMPPSendDisco(data, parsee, server);
|
disco = XMPPSendDisco(data->jabber, parsee, server);
|
||||||
|
|
||||||
ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0");
|
ret = XMPPDiscoAdvertises(disco, "urn:xmpp:occupant-id:0");
|
||||||
|
|
||||||
|
|
@ -162,9 +163,10 @@ ServerHasXEP421(ParseeData *data, char *from)
|
||||||
* into a SHA-256 value
|
* into a SHA-256 value
|
||||||
* > "The recipient MUST consider the occupant identifier to be an opaque
|
* > "The recipient MUST consider the occupant identifier to be an opaque
|
||||||
* > string.". */
|
* > string.". */
|
||||||
char *
|
static char *
|
||||||
ScrambleOID(ParseeData *data, char *opaque_oid)
|
ScrambleOID(ParseeData *data, char *opaque_oid)
|
||||||
{
|
{
|
||||||
|
unsigned char *raw;
|
||||||
char *sha, *mxid;
|
char *sha, *mxid;
|
||||||
const ParseeConfig *c = data->config;
|
const ParseeConfig *c = data->config;
|
||||||
if (!opaque_oid)
|
if (!opaque_oid)
|
||||||
|
|
@ -173,7 +175,9 @@ ScrambleOID(ParseeData *data, char *opaque_oid)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Turns this into a 128-byte long, Matrix-safe value. */
|
/* Turns this into a 128-byte long, Matrix-safe value. */
|
||||||
sha = ParseeSHA256(opaque_oid);
|
raw = Sha256(opaque_oid);
|
||||||
|
sha = ShaToHex(raw);
|
||||||
|
Free(raw);
|
||||||
|
|
||||||
/* TODO: Either mark this specially, or drop Parsee JID flags
|
/* TODO: Either mark this specially, or drop Parsee JID flags
|
||||||
* altogether before any 1.0.0 */
|
* altogether before any 1.0.0 */
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include <Cytoplasm/Util.h>
|
#include <Cytoplasm/Util.h>
|
||||||
#include <Cytoplasm/Str.h>
|
#include <Cytoplasm/Str.h>
|
||||||
#include <Cytoplasm/Sha.h>
|
#include <Cytoplasm/Sha.h>
|
||||||
#include <Cytoplasm/Log.h>
|
|
||||||
|
|
||||||
#include <StringStream.h>
|
#include <StringStream.h>
|
||||||
#include <XMPPCommand.h>
|
#include <XMPPCommand.h>
|
||||||
|
|
@ -13,86 +12,33 @@
|
||||||
|
|
||||||
#include "XMPPThread/internal.h"
|
#include "XMPPThread/internal.h"
|
||||||
|
|
||||||
IQFeatures *
|
|
||||||
CreateIQFeatures(void)
|
|
||||||
{
|
|
||||||
IQFeatures *ret = Malloc(sizeof(*ret));
|
|
||||||
|
|
||||||
ret->identity = ArrayCreate();
|
|
||||||
ret->adverts = ArrayCreate();
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
void
|
|
||||||
FreeIQFeatures(IQFeatures *features)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
if (!features)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
|
||||||
{
|
|
||||||
Free(ArrayGet(features->adverts, i));
|
|
||||||
}
|
|
||||||
ArrayFree(features->adverts);
|
|
||||||
|
|
||||||
for (i = 0; i < ArraySize(features->identity); i++)
|
|
||||||
{
|
|
||||||
XMPPIdentity *identity = ArrayGet(features->identity, i);
|
|
||||||
|
|
||||||
Free(identity->category);
|
|
||||||
Free(identity->type);
|
|
||||||
Free(identity->lang);
|
|
||||||
Free(identity->name);
|
|
||||||
|
|
||||||
Free(identity);
|
|
||||||
}
|
|
||||||
ArrayFree(features->identity);
|
|
||||||
|
|
||||||
Free(features);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
AdvertiseIQFeature(IQFeatures *f, char *feature)
|
|
||||||
{
|
|
||||||
if (!f || !feature)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayAdd(f->adverts, StrDuplicate(feature));
|
|
||||||
}
|
|
||||||
void
|
|
||||||
AddIQIdentity(IQFeatures *f, char *cat, char *lang, char *type, char *name)
|
|
||||||
{
|
|
||||||
XMPPIdentity *identity;
|
|
||||||
if (!f)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
identity = Malloc(sizeof(*identity));
|
|
||||||
identity->category = StrDuplicate(cat);
|
|
||||||
identity->type = StrDuplicate(type);
|
|
||||||
identity->lang = StrDuplicate(lang);
|
|
||||||
identity->name = StrDuplicate(name);
|
|
||||||
ArrayAdd(f->identity, identity);
|
|
||||||
}
|
|
||||||
/* Generates a SHA-256 hash of the ver field. */
|
/* Generates a SHA-256 hash of the ver field. */
|
||||||
char *
|
char *
|
||||||
XMPPGenerateVer(IQFeatures *features)
|
XMPPGenerateVer(void)
|
||||||
{
|
{
|
||||||
char *S = NULL;
|
char *S = NULL;
|
||||||
unsigned char *Sha = NULL;
|
unsigned char *Sha = NULL;
|
||||||
|
Array *identities = ArrayCreate();
|
||||||
|
Array *features = ArrayCreate();
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
/* Initialise identity table, to be sorted */
|
/* Initialise identity table, to be sorted */
|
||||||
ArraySort(features->identity, IdentitySort);
|
#define IdentitySimple(cat, Type, Name) { \
|
||||||
for (i = 0; i < ArraySize(features->identity); i++)
|
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,
|
char *id_chunk = StrConcat(7,
|
||||||
identity->category, "/",
|
identity->category, "/",
|
||||||
identity->type, "/",
|
identity->type, "/",
|
||||||
|
|
@ -104,10 +50,10 @@ XMPPGenerateVer(IQFeatures *features)
|
||||||
Free(id_chunk);
|
Free(id_chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArraySort(features->adverts, ((int (*) (void *, void *)) ICollate));
|
ArraySort(features, ((int (*) (void *, void *)) ICollate));
|
||||||
for (i = 0; i < ArraySize(features->adverts); i++)
|
for (i = 0; i < ArraySize(features); i++)
|
||||||
{
|
{
|
||||||
char *feature = ArrayGet(features->adverts, i);
|
char *feature = ArrayGet(features, i);
|
||||||
char *tmp = S;
|
char *tmp = S;
|
||||||
S = StrConcat(3, S, feature, "<");
|
S = StrConcat(3, S, feature, "<");
|
||||||
Free(tmp);
|
Free(tmp);
|
||||||
|
|
@ -118,64 +64,16 @@ XMPPGenerateVer(IQFeatures *features)
|
||||||
S = Base64Encode((const char *) Sha, 20);
|
S = Base64Encode((const char *) Sha, 20);
|
||||||
Free(Sha);
|
Free(Sha);
|
||||||
|
|
||||||
|
ArrayFree(features);
|
||||||
|
for (i = 0; i < ArraySize(identities); i++)
|
||||||
|
{
|
||||||
|
XMPPIdentity *identity = ArrayGet(identities, i);
|
||||||
|
/* We don't have to do anything here. */
|
||||||
|
Free(identity);
|
||||||
|
}
|
||||||
|
ArrayFree(identities);
|
||||||
|
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features)
|
|
||||||
{
|
|
||||||
XMLElement *c;
|
|
||||||
char *ver;
|
|
||||||
if (!presence || !features)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ver = XMPPGenerateVer(features);
|
|
||||||
c = XMLCreateTag("c");
|
|
||||||
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
|
|
||||||
XMLAddAttr(c, "hash", "sha-1");
|
|
||||||
XMLAddAttr(c, "node", REPOSITORY);
|
|
||||||
XMLAddAttr(c, "ver", ver);
|
|
||||||
|
|
||||||
Free(ver);
|
|
||||||
XMLAddChild(presence, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IQFeatures *
|
|
||||||
LookupJIDFeatures(char *jid)
|
|
||||||
{
|
|
||||||
IQFeatures *features;
|
|
||||||
if (!jid)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
features = CreateIQFeatures();
|
|
||||||
|
|
||||||
if (*jid == '#')
|
|
||||||
{
|
|
||||||
/* This is a MUC. As such, we need to advertise MUCs */
|
|
||||||
#define ID(...) AddIQIdentity(features, __VA_ARGS__)
|
|
||||||
#define AD(var) AdvertiseIQFeature(features, var)
|
|
||||||
ID("gateway", NULL, "matrix", "Parsee MUC gateway");
|
|
||||||
ID("conference", NULL, "text", "Parsee MUC gateway");
|
|
||||||
ID("component", NULL, "generic", "Parsee component");
|
|
||||||
|
|
||||||
AD("http://jabber.org/protocol/muc");
|
|
||||||
#undef AD
|
|
||||||
#undef ID
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
|
|
||||||
IQ_IDENTITY
|
|
||||||
#undef IdentitySimple
|
|
||||||
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
|
|
||||||
IQ_ADVERT
|
|
||||||
#undef AdvertiseSimple
|
|
||||||
}
|
|
||||||
return features;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue