Compare commits

..

No commits in common. "master" and "v0.1" have entirely different histories.

114 changed files with 1658 additions and 5491 deletions

View file

@ -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

View file

@ -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'

15
.gitignore vendored
View file

@ -4,11 +4,8 @@ parsee*
parsee
*.swp
.*
data*
data*/*
Makefile
configure
gmon.out
data
data/*
tools/out
tools/out/*
@ -22,11 +19,3 @@ tags
# Whitelists
!etc/*
!etc/**
!.forgejo
!.forgejo/*
!.forgejo/**
!.guix
!.guix/*
!.guix/**

View file

@ -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

View file

@ -6,73 +6,7 @@ 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.

View file

@ -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

View file

@ -1,3 +1,7 @@
Some dates for Parsee-related events. They mostly serve as LDA's TODOs with
a strict deadline:
- Get star-of-hope out by November 8
- ~September 2024[tomboyish-bridges-adventure]:
Get Parsee into the _Phantasmagoria of Bug View_ stage (essentially
v0.0.1 for a public testing) once I can afford `yama`, and start
bridging the Matrix room alongside a shiny XMPP MUC, bridged by
yours truly.

View file

@ -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/Parsee/HMAC.c, src/XML/*, tools/*, src/include/XML.h, etc/*, and Makefile,
see COPYING.CC0.
For any other file in src/, see COPYING.AGPL as the primary license(AGPL-3.0-or-later)
For 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

90
Makefile Normal file
View file

@ -0,0 +1,90 @@
# (GNU)Makefile for building Parsee
# ================================
# TODO: Consider making something akin to a configure script that checks
# for dependencies, or maybe even use *autoconf* (the devil!)
# =========================== Parsee Flags =============================
include build.conf
REPOSITORY=$(shell git remote get-url origin)
# =========================== Compilation Flags ============================
CYTO_INC ?=/usr/local/include/ # Where Cytoplasm's include path is
# located.
CYTO_LIB ?=/usr/local/lib # Where's Cytoplasm's library is
# located.
PREFIX ?=/usr/local
AYAS=ayaya
ETC=etc
FCFLAGS=-I$(SOURCE) -I$(INCLUDES) -I$(CYTO_INC) -DNAME="\"$(NAME)\"" -DVERSION="\"$(VERSION)\"" -DREPOSITORY=\"$(REPOSITORY)\" -DCODE=\"$(CODE)\" $(CFLAGS)
FLDFLAGS=-L $(CYTO_LIB) -lCytoplasm $(LDFLAGS)
AFLAGS=-C "$(ETC)/ayadoc/style.css" -p "$(NAME)"
# ============================ Compilation =================================
SRC_FILES:=$(shell find $(SOURCE) -name '*.c') $(shell find $(ETC)/media -name '*.png')
OBJ_FILES:=${subst $(ETC)/media/,$(OBJECT)/,${subst $(SOURCE)/,$(OBJECT)/,$(patsubst %.png, %.o, $(patsubst %.c, %.o, $(SRC_FILES)))}}
CPP_FILES:=$(shell find $(INCLUDES) -name '*.h')
AYA_FILES:=${subst $(INCLUDES)/,$(AYAS)/,$(patsubst %.h, %.html, $(CPP_FILES))}
all: utils binary
binary: $(OBJ_FILES)
$(CC) $(FLDFLAGS) $(OBJ_FILES) -o $(BINARY)
tags: $(SRC_FILES)
@ctags --recurse $(SOURCE)/
clean:
rm -rf $(OBJECT) $(BINARY) $(AYAS)
$(OBJECT)/%.o: $(ETC)/media/%.png
@mkdir -p $(shell dirname "$@")
@echo "const char media_$(shell basename $< .png)[] =" > $@.c
@base64 $< | \
sed -e 's/^\(.*\)$$/ "\1"/' | \
sed -e '$$ s/^\(.*\)$$/\1;/' >> $@.c
$(CC) -c $(FCFLAGS) $@.c -o $@
$(OBJECT)/%.o: $(SOURCE)/%.c
@mkdir -p $(shell dirname "$@")
$(CC) -c $(FCFLAGS) $< -o $@
utils:
(cd tools && make)
ayadoc: utils $(AYA_FILES)
$(AYAS)/%.html: $(INCLUDES)/%.h
@mkdir -p $(shell dirname "$@")
tools/out/aya $(AFLAGS) -i $< -o $@
# Installs everything.
install: binary utils ayadoc install_setup install_parsee install_tools install_aya install_man
@echo Installed $(NAME) to $(PREFIX)!
install_setup:
install -dm755 "$(PREFIX)/bin"
install -dm755 "$(PREFIX)/share/doc"
install -dm755 "$(PREFIX)/man"
install_parsee:
install -Dm755 "$(BINARY)" "$(PREFIX)/bin/$(BINARY)"
TOOLS:=$(shell find 'tools/out' -name '*')
ITOOL:=${subst tools/out/,$(PREFIX)/bin/,$(patsubst tools/out/%, tools/out/$(BINARY)-%, $(TOOLS))}
install_tools: $(ITOOL)
$(PREFIX)/bin/$(BINARY)-%: tools/out/%
install -Dm755 "$<" "$@"
IHTML:=${subst $(AYAS)/,$(PREFIX)/share/doc/$(BINARY)/,$(AYA_FILES)}
install_aya: $(IHTML)
$(PREFIX)/share/doc/$(BINARY)/%: $(AYAS)/%
install -Dm644 "$<" "$@"
MPAGE:=$(shell find 'etc/man' -name '*.*')
PPAGE:=${subst etc/man/,$(PREFIX)/man/,$(MPAGE)}
install_man: $(PPAGE)
$(PREFIX)/man/%: etc/man/%
install -Dm644 "$<" "$@"

View file

@ -1,55 +1,45 @@
# Parsee - the jealous XMPP<=>Matrix bridge
Parsee is a Matrix<=>XMPP bridge written in C99, with Cytoplasm, similar to Bifrost, but it is
NOT a drop-in replacment.
Currently, it is *alpha* stage, which means that I wouldn't recommend using this in production,
as I can change anything, at any time, and it may behave strangely at times.
## Why?
### Naming
The name 'Parsee' is actually a reference to [Parsee Mizuhashi](https://en.touhouwiki.net/wiki/Parsee_Mizuhashi),
a "*bridge* princess". The other name you actually can sometimes see explains itself, so I won't
be talking about it.
a "*bridge* princess".
### Reasoning (personal to LDA)
I hate Bifrost. I also wanted to dip my toes in XMPP, XML, and bridges a bit. Also, as a sister
project to KappaChat, this means that I can integrate Parsee with KappaChat however I wish it
to be, which allows me to mess around with a codebase I'm already familiar with.
A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*,
and maybe as a testing ground for Cytoplasm features I sometimes add.
A more "up-to-date" reason may be to have a small, 'Just Werks' bridging solution *that is good*.
(Well, I'm *trying* to do that, at least.
Please scream at me if that fails(or just doesn't run on a overclocked Raspberry Pi 4B))
Well, I'm *trying* to do that, at least.
Please scream at me if that fails(or just doesn't run on a overclocked Raspberry
Pi 4B, which, by the way, is literally where Parsee+XMPP is running for now.)
### "Why not just use Matrix lol"
### "Why not just use XMPP lol"
These two having the same answer should be enough information.
These two having the same answer should be enough information. Also can I *just* have fun?
One could also argue that both sides need to migrate(onboard) the other side, so
a bridge may be a good way to start.
## BUILDING
```sh
$ cc configure.c -o configure # that or use tcc -run to consolidate these two steps.
$ ./configure # use -s if you want static Parsee+MbedTLS, use -s -l if LMDB is needed
$ make
$ make [PREFIX=...] install # run as root if on a protected dir like /usr
$ make # This generates a 'parsee' executable.
$ cd tools # If you want to build more tools
$ make && cd ..
$ make ayadoc # If you want to build HTML documentation
$ make [PREFIX=(install path)] install # To install Parsee.
```
If there are any Cytoplasm-related build failures, you may want to check the Makefile to
change a few variables (you can set `CYTO_INC` and `CYTO_LIB` for Cytoplasm's include and
library paths specifically.)
If you build with MbedTLS, please mind setting the `CYTO_TLS_CA` env to Parsee.
### DEPENDENCIES
Parsee tries to avoid dependencies aside from [Cytoplasm](https://git.telodendria.io/Telodendria/Cytoplasm).
Itself optionally depends on a good POSIX implementation, and optionally OpenSSL/LMDB (highly recommended, but
you can get away without those if you're adventurous).
### BUILDING WITH GUIX
If you have [Guix](https://guix.gnu.org/) installed, you can build Parsee using `guix package -f guix.scm`, or test it
using `guix shell -f guix.scm`. You can also generate a Docker/OCI image.
```sh
guix pack -f docker -S /bin=bin -L.guix/modules parsee
```
## RUNNING
First off, you may want to configure Parsee by running the `config` tool(generally named
`parsee-config` in most cases), with the correct flags, like here.
@ -61,8 +51,7 @@ parsee-config \
-H 'blow.hole' \ # Matrix homeserver name
-J 'parsee.blow.hole' \ # XMPP component host, must be reachable
-s 'A very secure XMPP component secret' \
-p 5347 \
-M 65535 # Maximum stanza size. Stanzas larger than this from Parsee will be dropped to avoid stream closures. Leave this empty if you're unsure.
-p 5347
```
If everything goes well, it should generate a `parsee.json` file.
@ -75,44 +64,33 @@ returns with a landing page, then this side works. You can read it for some more
## DOCS
Currently, the main sources of documentation are the Ayadocs(for headers) and the manpages
(see `etc/man`).
(see `etc/man`)
## TODOS before 1.0 rolls around
- Make Parsee actually go *vroooooooooommmmmmm*.
- Make sure Parsee can easily run on just about any reasonable POSIX
system.
- Avoid making 'back-puppets' from Matrix as much as possible
- Extension support. I'd need to design a good system, and maybe do it
with either shared libraries(`dlopen`/`dlclose` on POSIX) or use a
language like Janet or Lua.
- Add [libomemo](https://github.com/gkdr/libomemo) or something as an optional dependency.
- It depends on more stuff anyways, and I don't want to weigh down the
dependency list of Parsee for that.
- Matrix's libolm is deprecated. They replaced it with a Rust version that
pulls in *way too many* dependencies, and that lacks a C binding. We may
~~put in the work of either forking off libolm or~~ be making a binding with
KappaChat(when I get around to remaking UI :p).
put in the work of either forking off libolm or making a binding to KappaChat.
- Josh did infact tell me that maybe C bindings may happen. I'd be
willing to help out, but IDK. In any case, this will at best be an
extension packagers may integrate properly.
- Get rid of the '?'-syntax and use another invalid Matrix char/valid XMPP char
('$'?) for escaped?
- PROPER FUCKING AVATARS
XMPP->Matrix is decent, Matrix->XMPP is effectiveny not done
- Consider making room/MUC admins/owners be able to plumb instead of it being
restricted to Parsee admins, with permission from MUC owners, too
- Limiting to admins may be a way to "control" consent for both, but this is
only if Parsee admins are good-willed, which we must assume such statment to
be false by default.
- Currently, MUC owners may kick Parsee out, with the effect of unlinking the
MUC.
- Look at XEPS-TBD.TXT for XEPs to be done
- Add a MUC server to Parsee, such that it may be able to hook onto it and therefore
support XMPP->Matrix bridging.
- Manage MUC DMs in a reasonable manner. Thanks `@freeoffers4u:matrix.org` for being
a fucking annoyance and DMing an old Parsee semi-anon user for no clear reason.
- 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.
- Deadlocks. It's always deadlocks.
## DONATING/CONTRIBUTING
If you know things about XMPP or Matrix, yet aren't familiar with C99, or just
@ -129,7 +107,7 @@ You may also donate to [the LiberaPay](https://en.liberapay.com/Parsee), alongsi
currently maintaining Cytoplasm.
## IM chats
Please avoid asking for help/issues there. If you *really* want, you may just open
Please avoid asking for help/issues here. If you *really* want, you may just open
an issue and link it over it. Basic respect for others/not being an asshat is required.
(Also, these are temporary room aliases.)
- [#parsee:tedomum.net](https://matrix.to/#/%23parsee:tedomum.net)

View file

@ -21,14 +21,10 @@ Somewhat implemented XEPs:
This allows reactions, which Matrix also has support to. The two
systems don't seem *too* restrictive on one-another (unlike some
IM platforms I won't mention), so this doesn't sound too bad to do
TODO: Add support from Matrix.
HALF-IMPLEMENTED: Removing reacts won't work.
~ https://xmpp.org/extensions/xep-0184.html
Only Matrix->XMPP as of now. Requesting data from Matrix ASes without
/sync seems like a non-option as of now, which _sucks_.
~ https://xmpp.org/extensions/xep-0084.html
Avatar support would be extremely useful, if just a QoL improvment.
Matrix and XMPP both have support for these.
XEP-0084 is a pain in the ass to implement and seems generally just
For future XEPs:
- https://xmpp.org/extensions/xep-0449.html
@ -38,7 +34,12 @@ For future XEPs:
which is used along PEP, it seems, and meanwhile Matrix has ''support''
for packs too, tracking them is between "annoyance" and "yeah, no.".
On Standby:
ON STANDBY BECAUSE THESE HAVE BEEN TERRIBLE TO DEAL WITH AND WHO KEEPS WRITING
THESE I WANT TO SEND THEM A NICE, BRIGHT GIFT:
x https://xmpp.org/extensions/xep-0084.html
Avatar support would be extremely useful, if just a QoL improvment.
Matrix and XMPP both have support for these.
XEP-0084 is a pain in the ass to implement and seems generally just
unreliable, however.
x https://xmpp.org/extensions/xep-0080.html
Can't think of a good analogy to these...
@ -47,7 +48,7 @@ On Standby:
Not XEPs, but ideas that _needs_ to be added:
~ "GIVE THE PUPPETS APPROPRIATE PLS/ROLES" - Hydro/t4d
Happens on Matrix. I'll need to handle that on XMPP as well.
~ Standalone/Static Parsee, ideally as small as it can be(if not as APE).
- Standalone/Static Parsee, ideally as small as it can be(if not as APE).
- Kappa-like extension system(maybe bridging more than just Matrix-XMPP.)
- https://www.youtube.com/watch?v=InL414iDZmY

863
build.c Normal file
View file

@ -0,0 +1,863 @@
/* build.c - Simple, POSIX, non-Cytoplasm utility to build out
* the entirety of Parsee from scratch, without any Makefiles.
*
* The main reason why this tool exists is merely because the
* current Make-based building is not POSIX compliant, and I
* am simply not porting it to be. The Makefile shall stay
* supported however, but if possible, use build.c.
* To run it, just build it with:
* cc build.c -o /tmp/build && /tmp/build
*
* TODO: Parallel jobs, CFLAGS and LDFLAGS.
* Note that this bit is formatted differently.
* --------------------------------
* LICENSE: CC0
* Written-By: LDA [lda@freetards.xyz] [@fourier:ari.lt] */
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#define DEFAULT_BUILD_PATH "build.conf"
#define DEFAULT_COMPILER "cc"
#define Compiler(info) (info.basic.cc ? info.basic.cc : DEFAULT_COMPILER)
static char *
string_rep_ext(char *in, char *ext1, char *ext2)
{
char *end;
size_t inLen;
if (!in || !ext1 || !ext2)
{
return NULL;
}
inLen = strlen(in);
end = inLen + in;
while (end >= in)
{
if (!strcmp(end, ext1))
{
size_t cpyLen = end - in;
size_t extLen = strlen(ext2);
char *ret = malloc(cpyLen + extLen + 1);
memcpy(ret, in, cpyLen);
memcpy(ret + cpyLen, ext2, extLen);
ret[cpyLen + extLen] = '\0';
return ret;
}
end--;
}
return NULL;
}
static char *
string_dup(char *in)
{
char *out;
size_t len;
if (!in)
{
return NULL;
}
len = strlen(in);
out = malloc(len + 1);
memcpy(out, in, len);
out[len] = '\0';
return out;
}
static char *
string_cat(char *in1, char *in2)
{
char *out;
size_t len1, len2;
if (!in1)
{
return string_dup(in2);
}
if (!in2)
{
return string_dup(in1);
}
len1 = strlen(in1);
len2 = strlen(in2);
out = malloc(len1 + len2 + 1);
memcpy(out, in1, len1);
memcpy(out + len1, in2, len2);
out[len1 + len2] = '\0';
return out;
}
static char *
trim_nl(char *in)
{
char *tc;
if (!in)
{
return NULL;
}
while ((tc = strrchr(in, '\n')))
{
*tc = '\0';
}
return in;
}
typedef struct str_array {
char **values;
size_t quantity;
} str_array_t;
static str_array_t *
str_array_create(void)
{
str_array_t *ret = malloc(sizeof(*ret));
ret->values = NULL;
ret->quantity = 0;
return ret;
}
static void
str_array_free(str_array_t *arr)
{
size_t i;
if (!arr)
{
return;
}
for (i = 0; i < arr->quantity; i++)
{
if (arr->values[i]) free(arr->values[i]);
}
if (arr->values) free(arr->values);
free(arr);
}
static void
str_array_set(str_array_t *arr, size_t i, char *str)
{
if (!arr)
{
return;
}
if (i >= arr->quantity)
{
size_t size = (i+1) * sizeof(*arr->values);
size_t j;
arr->values = realloc(arr->values, size);
for (j = arr->quantity; j <= i; j++)
{
arr->values[j] = NULL;
}
arr->quantity = i + 1 ;
}
if (arr->values[i]) free(arr->values[i]);
arr->values[i] = string_dup(str);
}
static void
str_array_add(str_array_t *arr, char *str)
{
if (!arr)
{
return;
}
str_array_set(arr, arr->quantity, str);
}
static char *
str_array_get(str_array_t *arr, size_t i)
{
if (!arr)
{
return NULL;
}
return i < arr->quantity ? arr->values[i] : NULL;
}
static size_t
str_array_len(str_array_t *arr)
{
return arr ? arr->quantity : 0;
}
typedef struct buildinfo {
struct basic {
char *codename;
char *version;
char *name;
char *binary;
char *src;
char *inc;
char *obj;
char *cflags, *ldflags;
char *cc;
} basic;
char *repo;
} buildinfo_t;
static void
destroy_buildinfo(buildinfo_t *info)
{
if (!info)
{
return;
}
#define FreeIfExistent(v) do \
{ \
if (v) \
{ \
free(v); \
v = NULL; \
} \
} while (0)
FreeIfExistent(info->basic.codename);
FreeIfExistent(info->basic.version);
FreeIfExistent(info->basic.ldflags);
FreeIfExistent(info->basic.binary);
FreeIfExistent(info->basic.cflags);
FreeIfExistent(info->basic.name);
FreeIfExistent(info->basic.src);
FreeIfExistent(info->basic.inc);
FreeIfExistent(info->basic.obj);
FreeIfExistent(info->basic.cc);
FreeIfExistent(info->repo);
}
static char *
cmd_stdout(char *cmd)
{
FILE *f;
char *line = NULL;
size_t size;
if (!cmd)
{
return NULL;
}
if (!(f = popen(cmd, "r")))
{
return NULL;
}
getline(&line, &size, f);
pclose(f);
return line;
}
static int
exec_code(char *program, char *argv[])
{
pid_t forkRet;
if (!program || !argv)
{
return -1;
}
forkRet = fork();
if (forkRet == 0)
{
/* Child */
execvp(program, argv);
exit(0);
}
else
{
/* Parent */
int status;
if (waitpid(forkRet, &status, 0) == -1)
{
return -1;
}
return status;
}
/* We're not meant to ever be there, but TCC is stupid. */
return -1;
}
static char *
strchrn(char *s, char c)
{
s = strchr(s, c);
return s ? s+1 : NULL;
}
static char *
strchrl(char *s, char c)
{
char *s1 = NULL;
while ((s = strchr(s, c)))
{
s1 = s;
s++;
}
return s1;
}
static void
mkdir_rec(char *dir)
{
char tmp[PATH_MAX];
char *start;
if (!dir || strlen(dir) >= PATH_MAX - 1)
{
return;
}
memset(tmp, '\0', sizeof(tmp));
for (start = dir; start && *start; start = strchrn(start, '/'))
{
char subtmp[PATH_MAX];
char *next = strchr(start, '/');
if (!next)
{
next = start + strlen(start);
}
memcpy(subtmp, start, next - start);
subtmp[next - start] = '\0';
{
memcpy(tmp, dir, start - dir);
tmp[strlen(tmp) - 1] = '\0';
mkdir(tmp, 0770);
}
}
}
static str_array_t *
split(char *dir)
{
str_array_t *ret;
char *start;
if (!dir || strlen(dir) >= PATH_MAX - 1)
{
return NULL;
}
ret = str_array_create();
for (start = dir; start; start = strchrn(start, ' '))
{
char subtmp[PATH_MAX];
char *next = strchr(start, ' ');
if (!next)
{
next = start + strlen(start);
}
memcpy(subtmp, start, next - start);
subtmp[next - start] = '\0';
str_array_add(ret, subtmp);
}
return ret;
}
static time_t
mod_date(char *file)
{
struct stat s;
if (stat(file, &s))
{
return (time_t) 0;
}
return s.st_mtime;
}
static bool
build_file(char *cSource, buildinfo_t info, bool isTool)
{
str_array_t *args, *cflags;
char *oFileName, *objPath, *oFile;
int ret, i = 0;
int srclen;
char *code, *name, *vers, *repo;
if (!cSource)
{
return false;
}
if (!isTool)
{
srclen = strncmp(cSource, "src", 3) ?
strlen(info.basic.obj) + 1 :
strlen(info.basic.src) + 1 ;
objPath = string_cat(info.basic.obj, "/");
oFile = string_rep_ext(cSource + srclen, ".c", ".o");
oFileName = string_cat(objPath, oFile);
}
else
{
srclen = 6;
objPath = string_dup("tools/out/");
oFile = string_rep_ext(cSource + srclen, ".c", "");
oFileName = string_cat(objPath, oFile);
}
mkdir_rec(oFileName);
if (!isTool && (mod_date(cSource) < mod_date(oFileName)))
{
free(objPath);
free(oFileName);
free(oFile);
return true;
}
args = str_array_create();
if (!isTool)
{
printf("\tCC %s...\n", cSource);
}
str_array_add(args, Compiler(info));
if (isTool)
{
str_array_add(args, "-lCytoplasm");
str_array_add(args, "-I.");
}
else
{
str_array_add(args, "-c");
}
str_array_add(args, cSource);
cflags = split(info.basic.cflags);
for (i = 0; i < str_array_len(cflags); i++)
{
str_array_add(args, str_array_get(cflags, i));
}
str_array_free(cflags);
str_array_add(args, "-o");
str_array_add(args, oFileName);
str_array_add(args, "-I");
str_array_add(args, info.basic.inc);
str_array_add(args, "-I");
str_array_add(args, info.basic.src);
{
char *pre = string_cat("\"", info.basic.version);
char *pos = string_cat(pre, "\"");
vers = string_cat("-DVERSION=", pos);
str_array_add(args, vers);
free(pos);
free(pre);
}
{
char *pre = string_cat("\"", info.basic.name);
char *pos = string_cat(pre, "\"");
name = string_cat("-DNAME=", pos);
str_array_add(args, name);
free(pos);
free(pre);
}
{
char *pre = string_cat("\"", info.basic.codename);
char *pos = string_cat(pre, "\"");
code = string_cat("-DCODE=", pos);
str_array_add(args, code);
free(pos);
free(pre);
}
{
char *pre = string_cat("\"", info.repo);
char *pos = string_cat(pre, "\"");
repo = string_cat("-DREPOSITORY=", pos);
str_array_add(args, repo);
free(pos);
free(pre);
}
str_array_add(args, NULL);
ret = exec_code(Compiler(info), args->values);
str_array_free(args);
free(objPath);
free(oFileName);
free(oFile);
free(vers);
free(name);
free(code);
free(repo);
return ret == EXIT_SUCCESS;
}
static bool
finalise_file(str_array_t *arr, buildinfo_t info)
{
str_array_t *flags, *ldflags;
size_t i;
bool ret = true;
if (!arr)
{
return false;
}
flags = str_array_create();
str_array_add(flags, Compiler(info));
ldflags = split(info.basic.cflags);
for (i = 0; i < str_array_len(ldflags); i++)
{
str_array_add(flags, str_array_get(ldflags, i));
}
str_array_free(ldflags);
str_array_add(flags, "-lCytoplasm");
for (i = 0; i < str_array_len(arr); i++)
{
char *file = str_array_get(arr, i);
if (file)
{
size_t srclen = strncmp(file, "src", 3) ?
strlen(info.basic.obj) + 1 :
strlen(info.basic.src) + 1 ;
char *objPath = string_cat(info.basic.obj, "/");
char *oFile = string_rep_ext(file + srclen, ".c", ".o");
char *oFileName = string_cat(objPath, oFile);
str_array_add(flags, oFileName);
free(oFileName);
free(oFile);
free(objPath);
}
}
str_array_add(flags, "-o");
str_array_add(flags, info.basic.binary);
str_array_add(flags, NULL);
ret = exec_code(Compiler(info), flags->values);
str_array_free(flags);
return ret;
}
static str_array_t *
collect_sources(char *dir, bool head, char *ext)
{
DIR *handle;
str_array_t *ret;
struct dirent *ent;
if (!dir)
{
return NULL;
}
ret = str_array_create();
handle = opendir(dir);
if (!handle)
{
printf("error: cannot open directory '%s'\n", dir);
return ret;
}
while ((ent = readdir(handle)))
{
char *name = ent->d_name;
if (*name == '.') continue;
if (strlen(name) > strlen(ext) &&
!strcmp(name + strlen(name) - strlen(ext), ext))
{
char *d1 = string_cat(dir, "/");
char *na = string_cat(d1, name);
str_array_add(ret, na);
free(d1);
free(na);
continue;
}
if (!strchr(name, '.') &&
strcmp(name, "out") &&
strcmp(name, "Makefile"))
{
str_array_t *sub;
char *d1 = string_cat(dir, "/");
char *d2 = string_cat(d1, name);
size_t i;
sub = collect_sources(d2, false, ext);
for (i = 0; i < str_array_len(sub); i++)
{
char *file = str_array_get(sub, i);
str_array_add(ret, file);
}
str_array_free(sub);
free(d2);
free(d1);
}
}
closedir(handle);
return ret;
}
static char *
process_png(char *png, buildinfo_t info)
{
size_t i = 0;
char *symbol;
char *cFile, *oFile;
char *pcFile, *poFile;
char *pcFile1, *poFile1;
char *filename, *symbol1;
char *arguments[8] = { 0 };
if (!png)
{
return NULL;
}
if (!(filename = strchrl(png, '/')))
{
return NULL;
}
filename++;
pcFile1= string_cat(info.basic.obj, "/");
pcFile = string_cat(pcFile1, filename);
cFile = string_rep_ext(pcFile, ".png", ".c");
free(pcFile);
free(pcFile1);
poFile1= string_cat(info.basic.obj, "/");
poFile = string_cat(poFile1, filename);
oFile = string_rep_ext(poFile, ".png", ".o");
free(poFile);
free(poFile1);
symbol1 = string_rep_ext(filename, ".png", "");
symbol = string_cat("media_", symbol1);
free(symbol1);
mkdir_rec(oFile);
mkdir_rec(cFile);
/* Build the image into Base64 */
arguments[i++] = "tools/out/b64";
arguments[i++] = png;
arguments[i++] = symbol;
arguments[i++] = cFile;
arguments[i++] = NULL;
if (exec_code(arguments[0], arguments))
{
free(symbol);
free(cFile);
free(oFile);
return NULL;
}
/* Compile it out */
i = 0;
arguments[i++] = Compiler(info);
arguments[i++] = "-c";
arguments[i++] = cFile;
arguments[i++] = "-o";
arguments[i++] = oFile;
arguments[i++] = NULL;
if (exec_code(arguments[0], arguments))
{
free(symbol);
free(cFile);
free(oFile);
return NULL;
}
free(symbol);
free(oFile);
return cFile;
}
/* Builds the entirety of Parsee. */
static int
main_build(int argc, char *argv[])
{
buildinfo_t info = { 0 };
FILE *buildinfo = NULL;
char *line = NULL;
size_t size, i;
ssize_t nread;
str_array_t *sources, *images;
/* Step 1: Get all basic information from build.conf */
if (!(buildinfo = fopen(DEFAULT_BUILD_PATH, "r")))
{
printf("error: cannot open '%s'\n", DEFAULT_BUILD_PATH);
goto fail;
}
while ((nread = getline(&line, &size, buildinfo)) != -1)
{
char *eq = strchr(line, '=');
char *end = strchr(line, '\n');
char *key, *val;
if (!eq)
{
free(line);
printf(
"error: line in '%s' does not contain '='\n",
DEFAULT_BUILD_PATH
);
goto fail;
}
/* Set delimiters */
*eq = '\0';
if (end) *end = '\0';
key = line;
val = eq + 1;
/* Now, we have KV mappings. */
#define If(k, v, d) do \
{ \
if (!strcmp(key, k) && !info.v) \
{ \
info.v = string_dup(val); \
printf("%s: %s\n", d, val); \
} \
} while (0)
If("CODE", basic.codename, "Codename");
If("NAME", basic.name, "Name");
If("VERSION", basic.version, "Version");
If("BINARY", basic.binary, "Binary name");
If("CFLAGS", basic.cflags, "C compiler arguments");
If("LDFLAGS", basic.ldflags, "Linker arguments");
If("INCLUDES", basic.inc, "Include path");
If("SOURCE", basic.src, "Source path");
If("OBJECT", basic.obj, "Object path");
If("CC", basic.cc, "Compiler");
}
if (line)
{
free(line);
line = NULL;
}
fclose(buildinfo);
buildinfo = NULL;
/* Step 2: Get all information from commands. */
if (!(info.repo = cmd_stdout("git remote get-url origin")))
{
printf("error: cannot find origins url\n");
goto fail;
}
info.repo = trim_nl(info.repo);
if (argc >= 2 && !strcmp(argv[1], "clean"))
{
char *args[8];
size_t i;
unlink(info.basic.binary);
args[i++] = "rm";
args[i++] = "-r";
args[i++] = info.basic.obj;
args[i++] = NULL;
exec_code(args[0], args);
goto end;
}
/* Step 3: Build all utilities. */
sources = collect_sources("tools", true, ".c");
for (i = 0; i < str_array_len(sources); i++)
{
char *file = str_array_get(sources, i);
printf("\tTOOL %s...\n", file);
if (!build_file(file, info, true))
{
str_array_free(sources);
goto fail;
}
}
str_array_free(sources);
/* Step 4: Build all media files. */
sources = collect_sources(info.basic.src, true, ".c");
images = collect_sources("etc/media", true, ".png");
for (i = 0; i < str_array_len(images); i++)
{
char *file = str_array_get(images, i);
char *out;
out = process_png(file, info);
if (!out)
{
str_array_free(images);
str_array_free(sources);
goto fail;
}
printf("\tPNG %s\n", file);
str_array_add(sources, out);
free(out);
}
str_array_free(images);
/* Step 5: Build all of Parsee itself */
for (i = 0; i < str_array_len(sources); i++)
{
char *file = str_array_get(sources, i);
if (!build_file(file, info, false))
{
str_array_free(sources);
goto fail;
}
}
printf("\tLINK\n");
if (!finalise_file(sources, info))
{
str_array_free(sources);
goto fail;
}
str_array_free(sources);
/* TODO: Step 6: Build every Ayadoc */
end:
destroy_buildinfo(&info);
return EXIT_SUCCESS;
fail:
if (buildinfo)
{
fclose(buildinfo);
buildinfo = NULL;
}
destroy_buildinfo(&info);
return EXIT_FAILURE;
}
int
main(int argc, char *argv[])
{
/* TODO: Multiple flags(build/install/ayadoc/...) */
if ((argc - 1) < 1)
{
/* No arguments, let's just build. */
return main_build(argc, argv);
}
if (!strcmp(argv[1], "build") ||
!strcmp(argv[1], "clean"))
{
return main_build(argc, argv);
}
printf("%s: unknown verb: %s\n", argv[0], argv[1]);
return EXIT_FAILURE;
}

View file

@ -1,10 +1,8 @@
CODE=lunar-rainbow
CODE=tomboyish-bridges-adventure
NAME=Parsee
VERSION=0.3.0
VERSION=0.1.0
BINARY=parsee
SOURCE=src
INCLUDES=src/include
OBJECT=build
CC=cc
CFLAGS=-O3
PREFIX=/usr

View file

@ -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);
}

View file

@ -1,6 +1,6 @@
." The last field is the codename, by the way.
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
.TH parsee-adminify 1 "Parsee Utility" "star-of-hope"
.TH parsee-adminify 1 "Parsee Utility" "tomboyish-bridges-adventure"
.SH NAME
parsee-adminify - bootstrap an admin to a new Parsee server

View file

@ -1,6 +1,6 @@
." The last field is the codename, by the way.
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
.TH parsee-aya 1 "Parsee Utility" "star-of-hope"
.TH parsee-aya 1 "Parsee Utility" "tomboyish-bridges-adventure"
.SH NAME
parsee-aya - generate some nice Ayaya! documentation

View file

@ -1,6 +1,6 @@
." The last field is the codename, by the way.
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
.TH parsee-config 1 "Parsee Utility" "lunar-rainbow"
.TH parsee-config 1 "Parsee Utility" "tomboyish-bridges-adventure"
.SH NAME
parsee-config - generate a basic configuration file
@ -11,10 +11,8 @@ parsee-config
.B [-s SHARED_SECRET]
.B [-m MEDIA_URL]
.B [-J JABBER_HOST]
.B [-j JABBER_ADDR]
.B [-p JABBER_PORT]
.B [-d DATABASE]
.B [-M MAX_STANZA]
.B [-S DATABASE size]
.SH DESCRIPTION
@ -35,7 +33,6 @@ $ parsee-config \\
-H 'blow.hole' \\
-s 'The Dark Shared Secret' \\
-J 'xmpp.blow.hole' \\
-j 'localhost' \\
-S 128
.fi
.if n \{\
@ -57,10 +54,6 @@ For example, if you except Parsee users to be on
.I SHARED_SECRET
is a shared secret known by Parsee and the XMPP component to authenticate.
.TP
.BR -M MAX_STANZA
.I MAX_STANZA
is the maximum stanza size accepted by the XMPP host. If it is less than 10000 bytes, then it shall be set to that limit(the standardised value).
.TP
.BR -m MEDIA_URL
.I MEDIA_URL
is an optional field used by Parsee as an address that points to Matrix
@ -70,12 +63,6 @@ media. It must be publicly accessible (behind a reverse proxy to HTTP:7642)
.I JABBER_HOST
is used as the component host for Parsee.
.TP
.BR -j JABBER_ADDR
.I JABBER_ADDR
can optionally be used to change the hostname Parsee will try to contact
for XMPP. Users should ideally use localhost (or a hostname pointing to
the server itself), as XMPP component streams are not encrypted.
.TP
.BR -p JABBER_PORT
.I JABBER_PORT
is used as the component post for Parsee. Parsee uses it alongside

View file

@ -1,6 +1,6 @@
." The last field is the codename, by the way.
." ALL MANPAGES FOR PARSEE ARE UNDER PUBLIC DOMAIN
.TH parsee 1 "Parsee Utility" "star-of-hope"
.TH parsee 1 "Parsee Utility" "tomboyish-bridges-adventure"
.SH NAME
parsee - the jealous XMPP-Matrix bridge

View file

@ -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)

View file

@ -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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 B

View file

@ -1 +0,0 @@
.guix/modules/parsee.scm

View file

@ -30,9 +30,8 @@ ASType(const ParseeConfig *c, char *user, char *room, bool status)
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));
/* 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);

View file

@ -4,7 +4,6 @@
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <Cytoplasm/Uri.h>
#include <Cytoplasm/Sha.h>
#include <string.h>
#include <stdlib.h>
@ -15,7 +14,7 @@ char *
ASUpload(const ParseeConfig *c, Stream *from, unsigned int size, char *mime)
{
char *size_str, *path, *ret, *user;
unsigned int i;
int i;
HttpClientContext *ctx;
HashMap *reply;
if (!c || !from)
@ -128,93 +127,3 @@ ASReupload(const ParseeConfig *c, char *from, char **mime)
return ret;
}
bool
ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha)
{
HttpClientContext *cctx;
Stream *stream;
Stream *fake;
Uri *uri;
char *path, *buf = NULL;
unsigned char *sha1;
size_t len;
if (!c || !mxc || !mime || !sha)
{
return false;
}
*mime = NULL;
*sha = NULL;
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
{
return false;
}
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
cctx = ParseeCreateRequest(c, HTTP_GET, path);
ASAuthenticateRequest(c, cctx);
HttpRequestSendHeaders(cctx);
HttpRequestSend(cctx);
*mime = StrDuplicate(
HashMapGet(HttpResponseHeaders(cctx), "content-type")
);
stream = HttpClientStream(cctx);
fake = StreamFile(open_memstream(&buf, &len));
StreamCopy(stream, fake);
StreamClose(fake);
sha1 = Sha1Raw((unsigned char *) buf, len);
free(buf);
*sha = ShaToHex(sha1, HASH_SHA1);
Free(sha1);
HttpClientContextFree(cctx);
UriFree(uri);
Free(path);
return true;
}
bool
ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len)
{
HttpClientContext *cctx;
Stream *stream;
Stream *fake;
Uri *uri;
char *path, *buf = NULL;
if (!c || !mxc || !mime || !out || !len)
{
return false;
}
*mime = NULL;
*out = NULL;
*len = 0;
if (!(uri = UriParse(mxc)) || !StrEquals(uri->proto, "mxc"))
{
return false;
}
path = StrConcat(3, "/_matrix/client/v1/media/download/", uri->host, uri->path);
cctx = ParseeCreateRequest(c, HTTP_GET, path);
ASAuthenticateRequest(c, cctx);
HttpRequestSendHeaders(cctx);
HttpRequestSend(cctx);
*mime = StrDuplicate(
HashMapGet(HttpResponseHeaders(cctx), "content-type")
);
stream = HttpClientStream(cctx);
fake = StreamFile(open_memstream(&buf, len));
StreamCopy(stream, fake);
StreamClose(fake);
*out = Malloc(*len);
memcpy(*out, buf, *len);
free(buf);
HttpClientContextFree(cctx);
UriFree(uri);
Free(path);
return true;
}

View file

@ -10,16 +10,15 @@
#include <Matrix.h>
bool
void
ASPing(const ParseeConfig *conf)
{
HttpClientContext *ctx = NULL;
HashMap *json = NULL;
char *path;
bool ret;
if (!conf)
{
return false;
return;
}
path = StrConcat(3,
@ -34,9 +33,7 @@ ASPing(const ParseeConfig *conf)
Free(path);
json = HashMapCreate();
ASAuthenticateRequest(conf, ctx);
ret = ParseeSetRequestJSON(ctx, json) == HTTP_OK;
ParseeSetRequestJSON(ctx, json);
HttpClientContextFree(ctx);
JsonFree(json);
return ret;
}

View file

@ -136,71 +136,3 @@ ASGetName(const ParseeConfig *c, char *room, char *user)
}
return ret;
}
char *
ASGetAvatar(const ParseeConfig *c, char *room, char *user)
{
HttpClientContext *ctx;
HashMap *reply;
char *path = NULL, *ret = NULL;
char *u2 = user;
if (!c || !user)
{
return NULL;
}
if (room)
{
user = HttpUrlEncode(user);
room = HttpUrlEncode(room);
path = StrConcat(4,
"/_matrix/client/v3/rooms/", room,
"/state/m.room.member/", user
);
ctx = ParseeCreateRequest(c, HTTP_GET, path);
Free(user);
Free(room);
ASAuthenticateRequest(c, ctx);
HttpRequestSendHeaders(ctx);
HttpRequestSend(ctx);
reply = JsonDecode(HttpClientStream(ctx));
ret = StrDuplicate(
JsonValueAsString(HashMapGet(reply, "avatar_url"))
);
HttpClientContextFree(ctx);
JsonFree(reply);
Free(path);
user = u2;
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from room, got %s", ret);
}
if (!ret)
{
user = HttpUrlEncode(user);
path = StrConcat(3,
"/_matrix/client/v3/profile/", user, "/avatar_url"
);
ctx = ParseeCreateRequest(c, HTTP_GET, path);
Free(user);
user = u2;
ASAuthenticateRequest(c, ctx);
HttpRequestSendHeaders(ctx);
HttpRequestSend(ctx);
reply = JsonDecode(HttpClientStream(ctx));
ret = StrDuplicate(
JsonValueAsString(HashMapGet(reply, "avatar_url"))
);
HttpClientContextFree(ctx);
JsonFree(reply);
Free(path);
Log(LOG_DEBUG, "ASGetAvatar: trying to grab avatar from profile, got %s", ret);
}
return ret;
}

View file

@ -25,15 +25,7 @@ ASGetRelations(const ParseeConfig *c, size_t n, char *room, char *event, char *t
}
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
if (event)
{
path = StrConcat(6,
"/_matrix/client/v1/rooms/", room,
@ -41,6 +33,13 @@ ASGetRelations(const ParseeConfig *c, size_t n, char *room, char *event, char *t
"?user_id=", user
);
}
else
{
path = StrConcat(4,
"/_matrix/client/v1/rooms/", room,
"/relations?user_id=", user
);
}
Free(user);
ctx = ParseeCreateRequest(c, HTTP_GET, path);

View file

@ -25,13 +25,11 @@ ASInvite(const ParseeConfig *conf, char *id, char *invited)
"@", conf->sender_localpart,
":", conf->server_base
);
id = HttpUrlEncode(id);
path = StrConcat(5,
"/_matrix/client/v3/rooms/", id, "/invite",
"?user_id=", bridge
);
Free(bridge);
Free(id);
ctx = ParseeCreateRequest(
conf,
@ -62,13 +60,11 @@ ASBan(const ParseeConfig *conf, char *id, char *banned)
"@", conf->sender_localpart,
":", conf->server_base
);
id = HttpUrlEncode(id);
path = StrConcat(5,
"/_matrix/client/v3/rooms/", id, "/ban",
"?user_id=", bridge
);
Free(bridge);
Free(id);
ctx = ParseeCreateRequest(
conf,
@ -77,7 +73,7 @@ ASBan(const ParseeConfig *conf, char *id, char *banned)
Free(path);
json = HashMapCreate();
HashMapSet(json, "user_id", JsonValueString(banned));
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
ASAuthenticateRequest(conf, ctx);
ParseeSetRequestJSON(ctx, json);
@ -99,13 +95,11 @@ ASKick(const ParseeConfig *conf, char *id, char *banned)
"@", conf->sender_localpart,
":", conf->server_base
);
id = HttpUrlEncode(id);
path = StrConcat(5,
"/_matrix/client/v3/rooms/", id, "/kick",
"?user_id=", bridge
);
Free(bridge);
Free(id);
ctx = ParseeCreateRequest(
conf,
@ -114,7 +108,7 @@ ASKick(const ParseeConfig *conf, char *id, char *banned)
Free(path);
json = HashMapCreate();
HashMapSet(json, "user_id", JsonValueString(banned));
HashMapSet(json, "reason", JsonValueString(NAME " felt jealous."));
HashMapSet(json, "reason", JsonValueString("Parsee felt jealous."));
ASAuthenticateRequest(conf, ctx);
ParseeSetRequestJSON(ctx, json);
@ -126,8 +120,7 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
{
HttpClientContext *ctx = NULL;
HashMap *json = NULL;
char *path, *ret, *serv;
int status;
char *path, *ret;
if (!conf || !id)
{
return NULL;
@ -146,11 +139,6 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
{
masquerade = HttpUrlEncode(masquerade);
}
serv = strchr(id, ':');
if (serv)
{
serv = serv + 1;
}
id = HttpUrlEncode(id);
path = StrConcat(5,
"/_matrix/client/v3/join/", id, "?",
@ -164,7 +152,7 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
Free(path);
json = HashMapCreate();
ASAuthenticateRequest(conf, ctx);
status = ParseeSetRequestJSON(ctx, json);
ParseeSetRequestJSON(ctx, json);
JsonFree(json);
json = JsonDecode(HttpClientStream(ctx));
@ -175,8 +163,6 @@ ASJoin(const ParseeConfig *conf, char *id, char *masquerade)
Free(masquerade);
Free(id);
(void) serv; // TODO
return ret;
}
void

View file

@ -1,43 +1,21 @@
#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)
ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c)
{
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)
{
@ -45,27 +23,18 @@ ASSend(const ParseeConfig *conf, char *id, char *user, char *type, HashMap *c, u
return NULL;
}
if (!ts)
{
ts = UtilTsMillis();
}
ts_str = TSToStr(ts);
txn = StrRandom(16);
id = HttpUrlEncode(id);
path = StrConcat(11,
path = StrConcat(9,
"/_matrix/client/v3/rooms/",
id, "/send/", type, "/", txn, "?",
"user_id=", user, "&ts=", ts_str
"user_id=", user
);
Free(id);
Free(txn);
Free(ts_str);
ctx = ParseeCreateRequest(conf, HTTP_PUT, path);
Free(path);
ASAuthenticateRequest(conf, ctx);
status = ParseeSetRequestJSON(ctx, c);
ParseeSetRequestJSON(ctx, c);
reply = JsonDecode(HttpClientStream(ctx));
ret = StrDuplicate(JsonValueAsString(HashMapGet(reply, "event_id")));

View file

@ -25,7 +25,7 @@ CommandParse(char *cmd)
}
end_data = strchr(cmd, ' ');
if (!end_data || (cmd > end_data))
if (!end_data)
{
ret = Malloc(sizeof(*ret));
ret->command = StrDuplicate(cmd);
@ -53,7 +53,7 @@ CommandParse(char *cmd)
switch (state)
{
case STATE_WHITE:
if (!isblank((int) c))
if (!isblank(c))
{
state = STATE_NAME;
namestart = cur;
@ -84,7 +84,7 @@ CommandParse(char *cmd)
{
char c = *cur;
char cb[2] = { c, '\0' };
if ((type && c == char_type) || (!type && isblank((int) c)))
if ((type && c == char_type) || (!type && isblank(c)))
{
break;
}

View file

@ -15,47 +15,35 @@ CommandCreateRouter(void)
void
CommandAddCommand(CommandRouter *rter, char *c, CommandRoute rte)
{
CommandRoute *indirect;
if (!rter || !c || !rte)
{
return;
}
/* Little dirty trick to force C99 into submission, and since
* some architectures may separate data/code. Still don't like it... */
indirect = Malloc(sizeof(rte));
*indirect = rte;
HashMapSet(rter->routes, c, (void *) indirect);
HashMapSet(rter->routes, c, rte);
}
void
RouteCommand(CommandRouter *rter, Command *cmd, void *d)
{
CommandRoute *route;
CommandRoute route;
if (!rter || !cmd)
{
return;
}
route = HashMapGet(rter->routes, cmd->command);
if (route && *route)
if (route)
{
(*route)(cmd, d);
route(cmd, d);
}
}
void
CommandFreeRouter(CommandRouter *rter)
{
char *key;
CommandRoute *val;
if (!rter)
{
return;
}
while (HashMapIterate(rter->routes, &key, (void **) &val))
{
Free(val);
}
HashMapFree(rter->routes);
Free(rter);
}

View file

@ -22,28 +22,11 @@ CommandHead(CmdBanUser, cmd, argp)
BotDestroy();
return;
}
ASBan(data->config, room, user);
ReplySprintf("Banning %s from '%s'...", user, room);
BotDestroy();
}
CommandHead(CmdNoFlyListDel, cmd, argp)
{
ParseeCmdArg *args = argp;
ParseeData *data = args->data;
HashMap *event = args->event;
char *user = HashMapGet(cmd->arguments, "user");
BotInitialise();
if (!user)
{
BotDestroy();
return;
}
ReplySprintf("Unbanning %s", user);
ParseeGlobalUnban(data, user);
ReplySprintf("Banning %s from '%s'...",
user, room
);
BotDestroy();
}

View file

@ -25,6 +25,4 @@ CommandHead(CmdHelp, cmd, argp)
);
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
BotDestroy();
(void) cmd;
}

View file

@ -32,6 +32,4 @@ CommandHead(CmdListBans, cmd, argp)
DbUnlock(data->db, listed);
BotDestroy();
(void) cmd;
}

View file

@ -24,8 +24,7 @@ CommandHead(CmdPlumb, cmd, argp)
BotRequired(room);
/* Check MUC viability */
if (ParseeManageBan(args->data, muc, NULL) ||
!ParseeIsMUCWhitelisted(args->data, muc))
if (ParseeManageBan(args->data, muc, NULL))
{
ReplySprintf("MUC '%s' is not allowed on this bridge.", muc);
goto end;
@ -63,7 +62,7 @@ CommandHead(CmdPlumb, cmd, argp)
if (chat_id)
{
char *rev = StrConcat(2, muc, "/parsee");
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
XMPPJoinMUC(args->data->jabber, "parsee", rev, false);
Free(rev);
}

View file

@ -40,6 +40,4 @@ CommandHead(CmdStats, cmd, argp)
ReplyBasic("*Written with a shoelace and UHU glue by LDA <3 !*");
BotDestroy();
(void) cmd;
}

View file

@ -9,60 +9,43 @@
#include <stdlib.h>
static bool
Grab(ParseeData *data, Command *cmd, char **muc, char **chat_id, char **room)
{
if (HashMapGet(cmd->arguments, "muc"))
{
*muc = HashMapGet(cmd->arguments, "muc");
*chat_id = ParseeGetFromMUCID(data, *muc);
*room = ParseeGetRoomID(data, *chat_id);
if (!chat_id || !room)
{
return false;
}
return true;
}
else if (HashMapGet(cmd->arguments, "room"))
{
*room = HashMapGet(cmd->arguments, "room");
*chat_id = ParseeGetFromRoomID(data, *room);
*muc = ParseeGetMUCID(data, *chat_id);
if (!chat_id || !muc)
{
return false;
}
return true;
}
return false;
}
CommandHead(CmdUnlinkMUC, cmd, argp)
{
ParseeCmdArg *args = argp;
ParseeData *data = args->data;
HashMap *event = args->event;
HashMap *json, *event = args->event, *mucs;
DbRef *ref;
char *muc = NULL, *chat_id = NULL, *room = NULL;
BotInitialise();
if (!Grab(data, cmd, &muc, &chat_id, &room))
muc = HashMapGet(cmd->arguments, "muc");
if (!muc)
{
ReplyBasic("`muc`|`room` REQUIRED");
ReplyBasic("`muc` field REQUIRED.");
goto end;
}
chat_id = ParseeGetFromMUCID(data, muc);
room = ParseeGetRoomID(data, chat_id);
ref = DbLock(data->db, 1, "chats");
json = DbJson(ref);
chat_id = StrDuplicate(GrabString(json, 2, "mucs", muc));
if (!chat_id)
{
ReplySprintf("No internal mapping to '%s'.", muc);
goto end;
}
mucs = GrabObject(json, 1, "mucs");
JsonValueFree(HashMapDelete(mucs, muc));
DbUnlock(data->db, ref);
ParseeUnlinkRoom(data, chat_id);
room = ParseeGetRoomID(data, chat_id);
ref = DbLock(data->db, 1, "chats");
json = DbJson(ref);
mucs = GrabObject(json, 1, "rooms");
JsonValueFree(HashMapDelete(mucs, room));
DbUnlock(data->db, ref);
DbDelete(data->db, 2, "chats", chat_id);
/* TODO: Do it automatically, if *not plumbed* */
ReplySprintf("The MUC %s is now *unlinked*.", muc);

View file

@ -79,7 +79,7 @@ MatrixCreateNickChange(char *nick)
return map;
}
HashMap *
MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
MatrixCreateMedia(char *mxc, char *body, char *mime)
{
HashMap *map;
char *mime_type = NULL, *matrix_type = NULL;
@ -91,10 +91,9 @@ MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
matrix_type = "m.file";
if (mime)
{
size_t i, len;
size_t i;
mime_type = StrDuplicate(mime);
len = strlen(mime);
for (i = 0; i < len; i++)
for (i = 0; i < strlen(mime); i++)
{
if (mime_type[i] == '/')
{
@ -120,12 +119,6 @@ MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info)
}
map = HashMapCreate();
JsonSet(map, JsonValueString(mime), 2, "info", "mimetype");
if (info && info->width && info->height)
{
JsonSet(map, JsonValueInteger(info->width), 2, "info", "w");
JsonSet(map, JsonValueInteger(info->height), 2, "info", "h");
}
HashMapSet(map, "msgtype", JsonValueString(matrix_type));
HashMapSet(map, "mimetype", JsonValueString(mime));
HashMapSet(map, "body", JsonValueString(body));

View file

@ -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);
}

View file

@ -73,7 +73,7 @@ ParseeCreateRequest(const ParseeConfig *conf, HttpRequestMethod meth, char *path
ctx = HttpRequest(
meth,
conf->homeserver_tls ? HTTP_FLAG_TLS : HTTP_FLAG_NONE,
HTTP_FLAG_TLS,
conf->homeserver_port, conf->homeserver_host,
path
);

View file

@ -1,6 +1,5 @@
#include <Cytoplasm/HttpServer.h>
#include <Cytoplasm/Cytoplasm.h>
#include <Cytoplasm/Platform.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Util.h>
#include <Cytoplasm/Cron.h>
@ -13,7 +12,6 @@
#include <signal.h>
#include <stdlib.h>
#include <StanzaBuilder.h>
#include <Parsee.h>
#include <XMPP.h>
#include <AS.h>
@ -48,7 +46,7 @@ static const Argument arguments[] =
"Generates a parsee.yaml AS file before exiting")
Arg('v', false, NULL,
"Forces Parsee to print in a more verbose fashion "
"(-vvv prints stanzas to stderr)")
"(-vv prints stanzas to stderr)")
Arg('h', false, NULL,
"Generates an help screen(this one!)")
@ -58,50 +56,6 @@ static const Argument arguments[] =
#undef Argument
};
void
ParseeCheckMatrix(void *datp)
{
static volatile uint64_t streak = 0;
ParseeData *data = datp;
if (data->config->accept_pings && !ASPing(data->config))
{
Log(LOG_ERR, "Cannot reach '%s' properly...", data->config->homeserver_host);
if (++streak == 10)
{
DbRef *ref = DbLockIntent(data->db, DB_HINT_READONLY, 1, "chats");
HashMap *json = DbJson(ref);
HashMap *mucs = GrabObject(json, 1, "mucs");
char *muc;
void *ignored;
/* Notify any potential MUCs about this */
while (HashMapIterate(mucs, &muc, &ignored))
{
char *id = StrRandom(32);
char *sender = StrConcat(3, "parsee@", data->jabber->host, "/parsee");
StanzaBuilder *b = CreateStanzaBuilder(sender, muc, id);
SetStanzaType(b, "groupchat");
SetStanzaBody(b,
"This bridge hasn't been able to reach the Matrix host, and "
"as such, some messages may not have been sent over."
);
WriteoutStanza(b, data->jabber, 0);
DestroyStanzaBuilder(b);
Free(sender);
Free(id);
}
(void) ignored;
DbUnlock(data->db, ref);
}
return;
}
streak = 0;
}
int
Main(Array *args, HashMap *env)
{
@ -124,15 +78,6 @@ Main(Array *args, HashMap *env)
);
ParseePrintASCII();
Log(LOG_INFO, "=======================");
Log(LOG_INFO, "(C)opyright 2024-2025 LDA and other contributors");
Log(LOG_INFO, "(This program is free software, see LICENSE.)");
#ifdef PLATFORM_IPHONE
Log(LOG_WARNING, "Wait. Are you running this on an iPhone?");
Log(LOG_WARNING, "You *ought* to have spoofed this, haven't you?");
Log(LOG_WARNING, "Simply jealous of you for doing this.");
#endif
LogConfigIndent(LogConfigGlobal());
{
@ -158,8 +103,6 @@ Main(Array *args, HashMap *env)
/* Write out the config file to a YAML document */
Log(LOG_INFO, "Generating YAML...");
yaml = StreamOpen("parsee.yaml", "w");
ParseeConfigLoad(configuration);
ParseeConfigInit();
ParseeExportConfigYAML(yaml);
StreamClose(yaml);
Free(opts);
@ -170,9 +113,6 @@ Main(Array *args, HashMap *env)
case PARSEE_VERBOSE_LOG:
LogConfigLevelSet(LogConfigGlobal(), LOG_DEBUG);
break;
case PARSEE_VERBOSE_TIMINGS:
Log(LOG_DEBUG, "Logging bench information.");
break;
case PARSEE_VERBOSE_STANZA:
Log(LOG_DEBUG, "Enabling stanza printing.");
break;
@ -205,6 +145,7 @@ Main(Array *args, HashMap *env)
}
}
Free(opts);
ParseeSetThreads(xmpp, http);
}
if (verbose >= PARSEE_VERBOSE_COMICAL)
@ -218,16 +159,13 @@ Main(Array *args, HashMap *env)
{
goto end;
}
ParseeSetThreads(xmpp, http);
Log(LOG_NOTICE, "Connecting to XMPP...");
jabber = XMPPInitialiseCompStream(
parsee_conf->component_addr,
parsee_conf->component_host,
parsee_conf->component_port
);
Log(LOG_NOTICE, "Connecting to XMPP... %p", jabber);
if (!XMPPAuthenticateCompStream(
jabber,
parsee_conf->shared_comp_secret
@ -268,12 +206,6 @@ Main(Array *args, HashMap *env)
}
ParseeInitialiseNickTable();
if (verbose >= PARSEE_VERBOSE_COMICAL)
{
Log(LOG_DEBUG, "Initialising affiliation table");
}
ParseeInitialiseAffiliationTable();
conf.port = parsee_conf->port;
conf.threads = parsee_conf->http_threads;
conf.maxConnections = conf.threads << 2;
@ -291,21 +223,18 @@ Main(Array *args, HashMap *env)
if (ASRegisterUser(parsee_conf, parsee_conf->sender_localpart))
{
char *parsee = ParseeMXID(conf.handlerArgs);
ASSetAvatar(parsee_conf,
parsee,
"mxc://tedomum.net/"
"7e228734ec8e792960bb5633e43f0cb845f709f61825130490034651136"
);
ASSetName(parsee_conf, parsee, "Parsee bridge");
Free(parsee);
}
Log(LOG_NOTICE, "Starting up local cronjobs...");
cron = CronCreate(10 SECONDS);
CronEvery(cron, 5 MINUTES, ParseeCleanup, conf.handlerArgs);
CronEvery(cron, 10 SECONDS, ParseeCheckMatrix, conf.handlerArgs);
ParseeCleanup(conf.handlerArgs);
CronStart(cron);
@ -318,9 +247,8 @@ Main(Array *args, HashMap *env)
}
server = HttpServerCreate(&conf);
((ParseeData *) conf.handlerArgs)->server = server;
if (!ParseeInitialiseSignals(conf.handlerArgs, xmpp_thr))
if (!ParseeInitialiseSignals(server, xmpp_thr, jabber))
{
goto end;
}
@ -344,12 +272,9 @@ end:
CronStop(cron);
CronFree(cron);
ParseeFreeData(conf.handlerArgs);
ParseeDestroyAffiliationTable();
ParseeDestroyNickTable();
ParseeDestroyOIDTable();
ParseeDestroyHeadTable();
ParseeDestroyJIDTable();
(void) env;
return 0;
}

View file

@ -8,29 +8,24 @@
#include <stdlib.h>
#include <StanzaBuilder.h>
#include <Unistring.h>
#include <Matrix.h>
#include <AS.h>
#include <ctype.h>
static const char *
GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to);
static char *
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char *hash)
static void
JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name)
{
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 *nick = StrDuplicate(name);
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)
while (!XMPPJoinMUC(data->jabber, jid, rev, true) && nonce < 32)
{
char *nonce_str = StrInt(nonce);
char *input = StrConcat(3, sender, name, nonce_str);
@ -55,7 +50,7 @@ JoinMUC(ParseeData *data, HashMap *event, char *jid, char *muc, char *name, char
ParseePushNickTable(muc, sender, nick);
Free(nick);
return (rev);
Free(rev);
}
static void
@ -98,95 +93,20 @@ ParseeMemberHandler(ParseeData *data, HashMap *event)
else if (StrEquals(membership, "join") && !ParseeIsPuppet(conf, state_key))
{
char *jid = ParseeEncodeMXID(state_key);
char *sha = NULL, *mime = NULL;
char *avatar = ASGetAvatar(data->config, NULL, state_key);
char *url = ParseeToUnauth(data, avatar, NULL);
chat_id = ParseeGetFromRoomID(data, room_id);
ASGetMIMESHA(data->config, avatar, &mime, &sha);
Free(avatar);
avatar = NULL;
if (chat_id)
{
char *muc = ParseeGetMUCID(data, chat_id);
char *name = ASGetName(data->config, room_id, state_key);
char *jabber = JoinMUC(data, event, jid, muc, name, sha);
avatar = ASGetAvatar(data->config, NULL, state_key);
Log(LOG_DEBUG, "MATRIX: Joining as '%s' (avatar=%s)", jabber, avatar);
Free(jabber);
Free(avatar);
JoinMUC(data, event, jid, muc, name);
Free(name);
Free(muc);
avatar = NULL;
/* TODO: XEP-0084 magic to advertise a new avatar if possible. */
}
else
{
char *full_jid = StrConcat(3,
jid, "@", data->config->component_host
);
XMLElement *elem, *pevent, *items, *item, *meta, *info;
Log(LOG_DEBUG, "MATRIX: Got local user '%s'(mxid=%s)", jid, state_key);
elem = XMLCreateTag("message");
{
#define PUBSUB "http://jabber.org/protocol/pubsub"
#define AVATAR "urn:xmpp:avatar:metadata"
pevent = XMLCreateTag("event");
XMLAddAttr(pevent, "xmlns", PUBSUB "#event");
{
items = XMLCreateTag("items");
item = XMLCreateTag("item");
XMLAddAttr(items, "node", AVATAR);
XMLAddAttr(item, "id", sha);
{
meta = XMLCreateTag("metadata");
info = XMLCreateTag("info");
XMLAddAttr(meta, "xmlns", AVATAR);
XMLAddAttr(info, "id", sha);
XMLAddAttr(info, "url", url);
XMLAddAttr(info, "type", mime);
XMLAddChild(meta, info);
XMLAddChild(item, meta);
}
XMLAddChild(items, item);
XMLAddChild(pevent, items);
}
XMLAddChild(elem, pevent);
#undef PUBSUB
}
/* TODO: Broadcast PEP avatar change */
ParseeBroadcastStanza(data, full_jid, elem);
XMLFreeElement(elem);
elem = XMLCreateTag("presence");
{
XMLElement *x = XMLCreateTag("x");
XMLElement *photo;
XMLAddAttr(x, "xmlns", "vcard-temp:x:update");
photo = XMLCreateTag("photo");
XMLAddChild(photo, XMLCreateText(sha));
XMLAddChild(x, photo);
XMLAddChild(elem, x);
}
ParseeBroadcastStanza(data, full_jid, elem);
XMLFreeElement(elem);
Free(full_jid);
}
Free(chat_id);
Free(avatar);
Free(mime);
Free(sha);
Free(jid);
Free(url);
Free(chat_id);
}
else if ((StrEquals(membership, "leave") ||
StrEquals(membership, "ban"))
@ -255,9 +175,14 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
return;
}
if (!body || *body != '!')
if (*body != '!')
{
/* All commands are to be marked with a ! */
Free(ASSend(
data->config, id, profile,
"m.room.message",
MatrixCreateNotice("Please enter a valid command")
));
Free(profile);
return;
}
@ -267,7 +192,7 @@ ParseeBotHandler(ParseeData *data, HashMap *event)
Free(ASSend(
data->config, id, profile,
"m.room.message",
MatrixCreateNotice("You are not authorised to do this."), 0
MatrixCreateNotice("You are not authorised to do this.")
));
Free(profile);
return;
@ -289,10 +214,10 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
char *room_id = GrabString(event, 1, "room_id");
char *matrix_sender = GrabString(event, 1, "sender");
char *chat_id = NULL, *muc_id = NULL;
char *user = NULL;
char *user;
DbRef *room_data = NULL;
HashMap *data_json = NULL;
DbRef *room_data;
HashMap *data_json;
bool direct = false;
if (!data || !event || !from || !to)
@ -324,8 +249,7 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
}
else
{
char *matrix_name = NULL, *matrix_avatar = NULL;
char *mime = NULL, *sha = NULL;
char *matrix_name;
muc_id = ParseeGetMUCID(data, chat_id);
if (!chat_id)
@ -339,16 +263,10 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
}
matrix_name = ASGetName(data->config, room_id, matrix_sender);
matrix_avatar = ASGetAvatar(data->config, NULL, matrix_sender);
ASGetMIMESHA(data->config, matrix_avatar, &mime, &sha);
Free(JoinMUC(data, event, *from, muc_id, matrix_name, sha));
JoinMUC(data, event, *from, muc_id, matrix_name);
*to = muc_id;
Free(matrix_avatar);
Free(matrix_name);
Free(mime);
Free(sha);
}
Free(chat_id);
@ -358,47 +276,28 @@ GetXMPPInformation(ParseeData *data, HashMap *event, char **from, char **to)
static void
ParseeMessageHandler(ParseeData *data, HashMap *event)
{
if (!data || !event)
{
return;
}
XMPPComponent *jabber = data->jabber;
StanzaBuilder *builder = NULL;
StanzaBuilder *builder;
DbRef *ref = NULL;
HashMap *json = NULL;
HashMap *json;
char *msgtype = GrabString(event, 2, "content", "msgtype");
char *m_sender = GrabString(event, 1, "sender");
char *unedited_id = NULL;
char *body = GrabString(event, 2, "content", "body");
char *id = GrabString(event, 1, "room_id");
char *ev_id = GrabString(event, 1, "event_id");
char *chat_id = NULL, *muc_id = NULL;
char *m_sender = GrabString(event, 1, "sender");
char *chat_id, *muc_id;
char *reply_id = MatrixGetReply(event);
char *xepd = ParseeXMPPify(data, event);
char *xepd = ParseeXMPPify(event);
char *type, *user, *xmppified_user = NULL, *to = NULL;
char *unauth = NULL;
char *origin_id = NULL, *stanza = NULL;
char *sender = NULL;
char *unedited_id = MatrixGetEdit(event);
char *url = GrabString(event, 2, "content", "url");
char *encoded_from = NULL;
bool direct = false;
unedited_id = MatrixGetEdit(event);
if (unedited_id)
{
char *new_content = GrabString(event, 3, "content", "m.new_content", "body");
if (new_content) body = new_content;
}
if (data->config->ignore_bots && StrEquals(msgtype, "m.notice"))
{
Free(reply_id);
Free(xepd);
Free(unedited_id);
return;
}
if (ParseeIsPuppet(data->config, m_sender) ||
ParseeManageBan(data, m_sender, id))
{
@ -429,7 +328,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
type = direct ? "chat" : "groupchat";
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,
@ -443,8 +342,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
}
else
{
char *name, *mime = NULL, *sha = NULL;
char *avatar;
char *name;
/* Try to find the chat ID */
muc_id = ParseeGetMUCID(data, chat_id);
if (!chat_id)
@ -452,21 +350,17 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
goto end;
}
/* TODO: Avoid using the AS endpoints */
/* TODO: Check the name's validity.
* Is there a good way to check for that that isn't
* just "await on join and try again?" */
name = ASGetName(data->config, id, m_sender);
avatar = ASGetAvatar(data->config, NULL, m_sender);
ASGetMIMESHA(data->config, avatar, &mime, &sha);
Free(JoinMUC(data, event, encoded_from, muc_id, name, sha));
JoinMUC(data, event, encoded_from, muc_id, name);
to = muc_id;
Free(sha);
Free(mime);
Free(name);
Free(avatar);
}
if (reply_id)
{
/* TODO: Monocles chat DM users HATE this trick!
@ -500,7 +394,7 @@ ParseeMessageHandler(ParseeData *data, HashMap *event)
SetStanzaEdit(builder, origin_id);
SetStanzaXParsee(builder, event);
WriteoutStanza(builder, jabber, data->config->max_stanza_size);
WriteoutStanza(builder, jabber);
DestroyStanzaBuilder(builder);
if (direct)
@ -523,8 +417,8 @@ end:
Free(stanza);
Free(sender);
Free(unauth);
Free(encoded_from);
Free(unedited_id);
Free(encoded_from);
DbUnlock(data->db, ref);
ref = NULL;
@ -555,7 +449,8 @@ ParseeEventHandler(ParseeData *data, HashMap *event)
return;
}
else if (StrEquals(event_type, "m.room.message") ||
StrEquals(event_type, "m.sticker"))
StrEquals(event_type, "m.sticker")) /* TODO: Actual sticker
* support here... */
{
ParseeMessageHandler(data, event);
Free(parsee);

View file

@ -1,9 +1,6 @@
#include <Matrix.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Http.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <string.h>
@ -35,34 +32,3 @@ MatrixParseID(char *user)
return ret;
}
UserID *
MatrixParseIDFromMTO(Uri *uri)
{
UserID *id = NULL;
char *path, *params, *decoded;
if (!uri)
{
return NULL;
}
if (!StrEquals(uri->proto, "https") || !StrEquals(uri->host, "matrix.to"))
{
return NULL;
}
if (strncmp(uri->path, "/#/", 3))
{
return NULL;
}
path = StrDuplicate(uri->path + 3);
params = path ? strchr(path, '?') : NULL;
if (params)
{
*params = '\0';
}
decoded = HttpUrlDecode(path);
id = MatrixParseID(decoded);
Free(decoded);
Free(path);
return id;
}

View file

@ -30,18 +30,12 @@ ParseeConfigLoad(char *conf)
{
return;
}
stream = StreamOpen(conf ? conf : "parsee.json", "r");
stream = StreamOpen("parsee.json", "r");
if (!stream)
{
return;
}
json = JsonDecode(stream);
if (!json)
{
Log(LOG_ERR, "Could not parse config JSON");
StreamClose(stream);
return;
}
config = Malloc(sizeof(*config));
#define CopyToStr(to, str) config->to = StrDuplicate( \
@ -50,9 +44,6 @@ ParseeConfigLoad(char *conf)
#define CopyToInt(to, str) config->to = (int) ( \
JsonValueAsInteger(HashMapGet(json, str)) \
)
#define CopyToBool(to, str) config->to = (int) ( \
JsonValueAsBoolean(HashMapGet(json, str)) \
)
config->http_threads = 8;
config->xmpp_threads = 8;
@ -67,25 +58,10 @@ ParseeConfigLoad(char *conf)
CopyToStr(server_base, "hs_base");
CopyToStr(homeserver_host, "hs_host");
CopyToInt(homeserver_port, "hs_port");
CopyToBool(homeserver_tls, "hs_tls");
if (!HashMapGet(json, "hs_tls"))
{
config->homeserver_tls = true;
}
CopyToBool(accept_pings, "accept_pings");
CopyToInt(component_port, "component_port");
CopyToStr(component_addr, "component_addr");
CopyToStr(component_host, "component_host");
CopyToStr(shared_comp_secret, "shared_secret");
CopyToInt(max_stanza_size, "max_stanza_size");
if (!config->max_stanza_size)
{
/* Standard XMPP "minimum" maximum */
config->max_stanza_size = 10000;
}
CopyToBool(ignore_bots, "ignore_bots");
CopyToStr(media_base, "media_base");
@ -101,7 +77,6 @@ ParseeSetThreads(int xmpp, int http)
{
if (!config)
{
Achievement("THREAD COUNT REQUEST WITHOUT CONFIG", true);
return;
}
config->http_threads = http;
@ -113,7 +88,6 @@ ParseeExportConfigYAML(Stream *stream)
{
if (!stream || !config)
{
Achievement("YAML EXPORT REQUEST WITHOUT CONFIG", true);
return;
}
StreamPrintf(stream, "# Autogenerated YAML AS entry for %s\n", NAME);
@ -127,7 +101,6 @@ ParseeExportConfigYAML(Stream *stream)
StreamPrintf(stream, "hs_token: \"%s\"\n", config->hs_token);
StreamPrintf(stream, "sender_localpart: \"%s\"\n", config->sender_localpart);
StreamPrintf(stream, "protocols: [\"xmpp\", \"jabber\"]\n");
StreamPrintf(stream, "receive_ephemeral: true\n"); /* TODO: Actually use that field */
StreamPrintf(stream, "\n");
StreamPrintf(stream, "namespaces: \n");
StreamPrintf(stream, " users:\n");
@ -136,7 +109,6 @@ ParseeExportConfigYAML(Stream *stream)
StreamPrintf(stream, " aliases:\n");
StreamPrintf(stream, " - exclusive: true\n");
StreamPrintf(stream, " regex: \"#%s_.*\"\n", config->namespace_base);
StreamFlush(stream);
}
void
@ -147,7 +119,6 @@ ParseeConfigFree(void)
return;
}
Free(config->component_host);
Free(config->component_addr);
Free(config->shared_comp_secret);
Free(config->db_path);
Free(config->homeserver_host);

View file

@ -7,7 +7,6 @@
#include <Cytoplasm/Str.h>
#include <stdlib.h>
#include <string.h>
#include <Routes.h>
#include <AS.h>
@ -27,15 +26,11 @@ ParseeInitData(XMPPComponent *comp)
data->config = ParseeConfigGet();
data->router = HttpRouterCreate();
data->jabber = comp;
data->muc = CreateMUCServer(data);
data->handler = CommandCreateRouter();
data->oid_servers = HashMapCreate();
pthread_mutex_init(&data->oidl, NULL);
data->halted = false;
pthread_mutex_init(&data->halt_lock, NULL);
if (data->config->db_size)
{
data->db = DbOpenLMDB(data->config->db_path, data->config->db_size);
@ -55,16 +50,14 @@ ParseeInitData(XMPPComponent *comp)
char *id = StrRandom(64);
ref = DbCreate(data->db, 1, "info");
HashMapSet(DbJson(ref), "identifier", JsonValueString(id));
HashMapSet(DbJson(ref), "version", JsonValueString(VERSION));
Free(id);
}
version = GrabString(DbJson(ref), 1, "version");
if (version && !ParseeIsCompatible(VERSION, version))
if (!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);
@ -114,10 +107,8 @@ ParseeFreeData(ParseeData *data)
}
HashMapFree(data->oid_servers);
pthread_mutex_destroy(&data->oidl);
pthread_mutex_destroy(&data->halt_lock);
Free(data->id);
XMPPEndCompStream(data->jabber);
FreeMUCServer(data->muc);
DbClose(data->db);
HttpRouterFree(data->router);
CommandFreeRouter(data->handler);
@ -132,6 +123,9 @@ ParseeCleanup(void *datp)
char *chat;
size_t i;
uint64_t ts = UtilTsMillis();
size_t entries = 0;
Log(LOG_DEBUG, "Cleaning up...");
chats = DbList(data->db, 1, "chats");
@ -175,6 +169,7 @@ ParseeCleanup(void *datp)
if (cleaned > threshold) \
{ \
DbDelete(data->db, 4, "chats", chat, #field"s", field); \
entries++; \
} \
Free(field); \
} \
@ -182,12 +177,9 @@ ParseeCleanup(void *datp)
} \
while (0)
/* TODO: Custom retention period for any 1.0 */
CleanupField(stanza, 30 MINUTES, 500);
CleanupField(event, 30 MINUTES, 500);
CleanupField(id, 30 MINUTES, 500);
/* TODO: Also cleanup user cache information */
CleanupField(stanza, 30 MINUTES, 50);
CleanupField(event, 30 MINUTES, 50);
CleanupField(id, 30 MINUTES, 50);
#undef CleanupField
}
DbListFree(chats);
@ -231,6 +223,7 @@ ParseeCleanup(void *datp)
if (cleaned > threshold) \
{ \
JsonValueFree(HashMapDelete(field##s, field)); \
entries++; \
} \
Free(field); \
} \
@ -238,13 +231,14 @@ ParseeCleanup(void *datp)
} \
while (0)
CleanupField(stanza, 3 HOURS, 500);
CleanupField(event, 3 HOURS, 500);
CleanupField(id, 3 HOURS, 500);
CleanupField(stanza, 3 HOURS, 50);
CleanupField(event, 3 HOURS, 50);
CleanupField(id, 3 HOURS, 50);
DbUnlock(data->db, ref);
}
DbListFree(chats);
Log(LOG_DEBUG, "Cleant up %d entries...", entries);
}
void
@ -339,15 +333,6 @@ ParseePushStanza(ParseeData *data, char *chat_id, char *stanza_id, char *id, cha
}
/* TODO */
{
ref = DbLock(data->db, 2, "chats", chat_id);
j = DbJson(ref);
if (j)
{
JsonValueFree(HashMapSet(j, "ts", JsonValueInteger(age)));
}
DbUnlock(data->db, ref);
}
{
ref = DbCreate(data->db, 4, "chats", chat_id, "stanzas", stanza_id);
j = DbJson(ref);
@ -550,178 +535,3 @@ end:
return ret;
}
void
ParseeUnlinkRoom(ParseeData *data, char *chat_id)
{
char *muc, *room;
DbRef *ref;
if (!data || !chat_id)
{
return;
}
muc = ParseeGetMUCID(data, chat_id);
room = ParseeGetRoomID(data, chat_id);
if (!muc || !room)
{
Free(muc);
Free(room);
return;
}
ref = DbLock(data->db, 1, "chats");
JsonValueFree(HashMapDelete(
GrabObject(DbJson(ref), 1, "rooms"),
room
));
JsonValueFree(HashMapDelete(
GrabObject(DbJson(ref), 1, "mucs"),
muc
));
DbUnlock(data->db, ref);
DbDelete(data->db, 2, "chats", chat_id);
Free(muc);
Free(room);
}
bool
ParseeIsMUCWhitelisted(ParseeData *data, char *muc)
{
char *server, *serv_start, *postserv;
DbRef *ref;
bool ret;
if (!data || !muc)
{
return false;
}
if (!DbExists(data->db, 1, "whitelist"))
{
return true;
}
serv_start = strchr(muc, '@');
serv_start = serv_start ? serv_start : muc;
server = StrDuplicate(serv_start + 1);
postserv = server ? strchr(server, '/') : NULL;
if (postserv) /* GCC doesn't know strchr is pure. */
{
*postserv = '\0';
}
ref = DbLockIntent(data->db,
DB_HINT_READONLY,
1, "whitelist"
);
ret = HashMapGet(DbJson(ref), server);
DbUnlock(data->db, ref);
Free(server);
return ret;
}
extern HashMap *
ParseeGetChatSettings(ParseeData *data, char *chat)
{
HashMap *ret, *json;
DbRef *ref;
char *key;
JsonValue *value;
if (!data || !chat)
{
return NULL;
}
ref = DbLockIntent(data->db, DB_HINT_READONLY,
3, "chats", chat, "settings"
);
json = DbJson(ref);
if (!ref)
{
return HashMapCreate();
}
ret = HashMapCreate();
while (HashMapIterate(json, &key, (void **) &value))
{
char *str = JsonValueAsString(value);
HashMapSet(ret, key, StrDuplicate(str));
}
DbUnlock(data->db, ref);
return ret;
}
void
ParseeFreeChatSettings(HashMap *settings)
{
char *key;
void *val;
if (!settings)
{
return;
}
while (HashMapIterate(settings, &key, &val))
{
Free(val);
}
HashMapFree(settings);
}
char *
ParseeGetChatSetting(ParseeData *data, char *chat, char *key)
{
HashMap *map;
char *ret;
if (!data || !chat || !key)
{
return NULL;
}
map = ParseeGetChatSettings(data, chat);
ret = StrDuplicate(HashMapGet(map, key));
ParseeFreeChatSettings(map);
return ret;
}
void
ParseeSetChatSetting(ParseeData *data, char *chat, char *key, char *val)
{
DbRef *ref;
HashMap *json;
if (!data || !chat || !key || !val)
{
return;
}
ref = DbLockIntent(data->db, DB_HINT_WRITE,
3, "chats", chat, "settings"
);
if (!ref)
{
ref = DbCreate(data->db, 3, "chats", chat, "settings");
}
json = DbJson(ref);
JsonValueFree(HashMapSet(json, key, JsonValueString(val)));
DbUnlock(data->db, ref);
return;
}
bool
ParseeIsMediaEnabled(ParseeData *data, char *chat_id)
{
char *value;
bool ret;
if (!data || !chat_id)
{
return false;
}
ret = !StrEquals(
(value = ParseeGetChatSetting(data, chat_id, "p.media.enabled")),
"false"
);
Free(value);
return ret;
}

View file

@ -4,10 +4,11 @@
const char *parsee_ascii[PARSEE_ASCII_LINES] =
{
"----------------------------",
" =+======",
" || | _ _/__----",
" / || \\ ==+= _/_____\\_",
" | || | -|- L___J ",
" | || | -|- L___J",
"_/ || \\_ ||| .______\\",
" || | | | |.____.|",
" || / | \\ |L____||",

View file

@ -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);
}

View file

@ -1,7 +1,6 @@
#include <Parsee.h>
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Http.h>
#include <Cytoplasm/Json.h>
#include <Cytoplasm/Util.h>
#include <Cytoplasm/Str.h>
@ -148,15 +147,14 @@ char *
ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
{
char *ret, *tmp;
size_t i, len;
size_t i;
if (!c || !jid)
{
return NULL;
}
ret = StrConcat(2, c->namespace_base, "_l_");
len = strlen(jid);
for (i = 0; i < len; i++)
for (i = 0; i < strlen(jid); i++)
{
char cpy = jid[i];
char cs[4] = { 0 };
@ -166,7 +164,7 @@ ParseeEncodeJID(const ParseeConfig *c, char *jid, bool trim)
/* RID: Break everything and die. */
break;
}
if (islower((int) *cs) || isalnum((int) *cs) || *cs == '_' ||
if (islower(*cs) || isalnum(*cs) || *cs == '_' ||
*cs == '=' || *cs == '-' || *cs == '/' ||
*cs == '+' || *cs == '.')
{
@ -195,7 +193,7 @@ char *
ParseeGetLocal(char *mxid)
{
char *cpy;
size_t i, len;
size_t i;
if (!mxid)
{
return NULL;
@ -205,14 +203,12 @@ ParseeGetLocal(char *mxid)
return StrDuplicate(mxid);
}
len = strlen(mxid);
mxid++;
cpy = Malloc(len + 1);
memset(cpy, '\0', len + 1);
memcpy(cpy, mxid, len);
cpy = Malloc(strlen(mxid) + 1);
memset(cpy, '\0', strlen(mxid) + 1);
memcpy(cpy, mxid, strlen(mxid));
for (i = 0; i < len; i++)
for (i = 0; i < strlen(mxid); i++)
{
if (cpy[i] == ':')
{
@ -228,16 +224,15 @@ char *
ParseeEncodeMXID(char *mxid)
{
char *ret;
size_t i, j, len;
size_t i, j;
if (!mxid)
{
return NULL;
}
/* Worst case scenario of 3-bytes the char */
len = strlen(mxid);
ret = Malloc(len * 3 + 1);
for (i = 0, j = 0; i < len; i++)
ret = Malloc(strlen(mxid) * 3 + 1);
for (i = 0, j = 0; i < strlen(mxid); i++)
{
char src = mxid[i];
@ -361,6 +356,8 @@ ParseePushDMRoom(ParseeData *d, char *mxid, char *jid, char *r)
void
ParseeDeleteDM(ParseeData *d, char *mxid, char *jid)
{
DbRef *ref;
HashMap *j;
char *dmid;
if (!d || !mxid || !jid)
{
@ -377,15 +374,14 @@ char *
ParseeTrimJID(char *jid)
{
char *ret;
size_t i, len;
size_t i;
if (!jid)
{
return NULL;
}
ret = StrDuplicate(jid);
len = strlen(ret);
for (i = 0; i < len; i++)
for (i = 0; i < strlen(ret); i++)
{
if (ret[i] == '/')
{
@ -539,6 +535,7 @@ ParseeGetMUCID(ParseeData *data, char *chat_id)
return ret;
}
void
ParseeSendPresence(ParseeData *data)
{
@ -557,16 +554,10 @@ ParseeSendPresence(ParseeData *data)
while (HashMapIterate(mucs, &muc, (void **) &val))
{
char *rev = StrConcat(2, muc, "/parsee");
char *chat_id = ParseeGetFromMUCID(data, muc);
DbRef *chat = DbLockIntent(data->db, DB_HINT_READONLY, 2, "chats", chat_id);
uint64_t ts = GrabInteger(DbJson(chat), 1, "ts");
int diff = ts ? (int) ((UtilTsMillis() - ts) / 1000) : -1;
/* Make a fake user join the MUC */
Log(LOG_NOTICE, "Sending presence to %s", rev);
XMPPJoinMUC(data->jabber, "parsee", rev, NULL, diff, false);
XMPPJoinMUC(data->jabber, "parsee", rev, false);
DbUnlock(data->db, chat);
Free(chat_id);
Free(rev);
}
DbUnlock(data->db, ref);
@ -688,13 +679,12 @@ end:
#include <Cytoplasm/Uri.h>
char *
ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
ParseeToUnauth(ParseeData *data, char *mxc)
{
Uri *url = NULL;
char *ret;
char *key, *hmac;
#define PAT "%s/media/%s%s?hmac=%s"
#define PATF "%s/media/%s%s/%s?hmac=%s"
#define PAT "%s/_matrix/client/v1/media/download/%s%s?hmac=%s"
size_t l;
if (!data || !mxc)
{
@ -715,45 +705,19 @@ ParseeToUnauth(ParseeData *data, char *mxc, char *filename)
hmac = ParseeHMACS(data->id, key);
Free(key);
if (!filename)
{
l = snprintf(NULL, 0,
PAT,
data->config->media_base,
url->host, url->path,
hmac
);
}
else
{
char *encoded = HttpUrlEncode(filename);
l = snprintf(NULL, 0,
PATF,
data->config->media_base,
url->host, url->path, encoded,
hmac
);
Free(encoded);
}
l = snprintf(NULL, 0,
PAT,
data->config->media_base,
url->host, url->path,
hmac
);
ret = Malloc(l + 3);
if (!filename)
{
snprintf(ret, l + 1,
PAT,
data->config->media_base,
url->host, url->path,
hmac
);
}
else
{
snprintf(ret, l + 1,
PATF,
data->config->media_base,
url->host, url->path, filename,
hmac
);
}
snprintf(ret, l + 1,
PAT,
data->config->media_base,
url->host, url->path,
hmac
);
UriFree(url);
Free(hmac);
return ret;

View file

@ -5,14 +5,11 @@
#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>
@ -20,7 +17,7 @@ typedef struct XMPPFlags {
bool quote;
} XMPPFlags;
static char *
XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPFlags flags)
XMPPifyElement(HashMap *event, XMLElement *elem, XMPPFlags flags)
{
char *xepd = NULL, *tmp = NULL;
@ -58,7 +55,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
} \
} \
while (0)
switch (elem ? elem->type : -1)
switch (elem->type)
{
case XML_ELEMENT_DATA:
Concat(elem->data);
@ -70,7 +67,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
for (i = 0; i < ArraySize(elem->children); i++)
{
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(conf, event, child, flags);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
@ -83,7 +80,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
for (i = 0; i < ArraySize(elem->children); i++)
{
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(conf, event, child, flags);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
@ -96,7 +93,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
for (i = 0; i < ArraySize(elem->children); i++)
{
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(conf, event, child, flags);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
@ -129,7 +126,7 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
for (i = 0; i < ArraySize(elem->children); i++)
{
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(conf, event, child, flags);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
@ -144,70 +141,35 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
for (i = 0; i < ArraySize(elem->children); i++)
{
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(conf, event, child, flags);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
}
if (i != 0)
{
Concat("\n");
}
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"))
Concat("(");
for (i = 0; i < ArraySize(elem->children); i++)
{
/* 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);
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
}
Concat(" < ");
Concat(href);
Concat(" >");
Concat(subxep);
Free(subxep);
}
UriFree(pref);
Concat(" points to ");
Concat(href);
Concat(" )");
}
else
{
for (i = 0; i < ArraySize(elem->children); i++)
{
child = ArrayGet(elem->children, i);
subxep = XMPPifyElement(conf, event, child, flags);
subxep = XMPPifyElement(event, child, flags);
Concat(subxep);
Free(subxep);
@ -219,45 +181,8 @@ XMPPifyElement(const ParseeConfig *conf, HashMap *event, XMLElement *elem, XMPPF
}
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)
ParseeXMPPify(HashMap *event)
{
char *type, *format, *html;
char *xepd = NULL;
@ -276,28 +201,20 @@ ParseeXMPPify(ParseeData *data, HashMap *event)
return NULL;
}
if (!StrEquals(GetBodyFormat(event), "org.matrix.custom.html"))
format = JsonValueAsString(JsonGet(event, 2, "content", "format"));
if (!StrEquals(format, "org.matrix.custom.html"))
{
/* Settle for the raw body instead. */
char *body = GetRawBody(event);
char *body = JsonValueAsString(JsonGet(event, 2, "content", "body"));
return StrDuplicate(body);
}
html = GetHTMLBody(event);
html = JsonValueAsString(JsonGet(event, 2, "content", "formatted_body"));
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);
xepd = XMPPifyElement(event, elem, flags);
XMLFreeElement(elem);
Free(html);
@ -314,7 +231,6 @@ ParseeGenerateMTO(char *common_id)
return NULL;
}
/* TODO: Is HttpUrlEncode okay? */
common_id = HttpUrlEncode(common_id);
matrix_to = StrConcat(2, "https://matrix.to/#/", common_id);
Free(common_id);

View file

@ -6,28 +6,6 @@
#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)
{

View file

@ -3,8 +3,6 @@
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Str.h>
#include <Unistring.h>
#include <stdbool.h>
#include <string.h>
@ -39,22 +37,6 @@ ParseeFindDatastart(char *data)
return (int) (startline - data);
}
int
ParseeFindDatastartU(char *data)
{
Unistr *str;
size_t ret;
if (!data)
{
return 0;
}
str = UnistrCreate(data);
ret = UnistrGetOffset(str, (uint32_t) '>');
UnistrFree(str);
return (int) ret;
}
char *
ParseeStringifyDate(uint64_t millis)

View file

@ -10,37 +10,6 @@
#include <string.h>
static HttpClientContext *
TryDownload(ParseeData *data, char *server, char *identi)
{
HttpClientContext *cctx;
char *path;
server = HttpUrlEncode(server);
identi = HttpUrlEncode(identi);
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
ASAuthenticateRequest(data->config, cctx);
Free(path);
HttpRequestSendHeaders(cctx);
if (HttpRequestSend(cctx) != HTTP_OK)
{
Log(LOG_WARNING, "Failing back.");
HttpClientContextFree(cctx);
path = StrConcat(4, "/_matrix/client/v1/media/download/", server, "/", identi);
cctx = ParseeCreateRequest(data->config, HTTP_GET, path);
ASAuthenticateRequest(data->config, cctx);
Free(path);
HttpRequestSendHeaders(cctx);
HttpRequestSend(cctx);
}
Free(server);
Free(identi);
return cctx;
}
RouteHead(RouteMedia, arr, argp)
{
ParseeHttpArg *args = argp;
@ -48,7 +17,7 @@ RouteHead(RouteMedia, arr, argp)
HashMap *reqh, *params;
char *server = ArrayGet(arr, 0);
char *identi = ArrayGet(arr, 1);
char *key, *val;
char *path, *key, *val;
char *hmac, *chkmak = NULL;
params = HttpRequestParams(args->ctx);
@ -75,7 +44,15 @@ RouteHead(RouteMedia, arr, argp)
/* Proxy the media through an authenticated endpoint if the HMAC
* is valid. */
cctx = TryDownload(args->data, server, identi);
server = HttpUrlEncode(server);
identi = HttpUrlEncode(identi);
path = StrConcat(4, "/_matrix/media/v3/download/", server, "/", identi);
cctx = ParseeCreateRequest(args->data->config, HTTP_GET, path);
ASAuthenticateRequest(args->data->config, cctx);
Free(path);
HttpRequestSendHeaders(cctx);
HttpRequestSend(cctx);
reqh = HttpResponseHeaders(cctx);
while (HashMapIterate(reqh, &key, (void **) &val))
{
@ -88,6 +65,8 @@ RouteHead(RouteMedia, arr, argp)
}
HttpClientContextFree(cctx);
Free(server);
Free(identi);
return NULL;
}

View file

@ -1,36 +0,0 @@
#include <Routes.h>
#include <Cytoplasm/Log.h>
#include <Matrix.h>
#include <AS.h>
RouteHead(RoutePing, arr, argp)
{
ParseeHttpArg *args = argp;
HashMap *request = NULL;
HashMap *response = NULL;
response = ASVerifyRequest(args);
if (response)
{
goto end;
}
if (HttpRequestMethodGet(args->ctx) != HTTP_POST)
{
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
response = MatrixCreateError(
"M_UNRECOGNIZED",
"Path /ping only accepts POST as a valid method."
);
goto end;
}
RequestJSON();
response = HashMapCreate();
end:
(void) arr;
JsonFree(request);
return response;
}

View file

@ -54,13 +54,7 @@ GetRandomQuote(void)
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 !"
"Go take a look at your objects!"
};
const size_t count = sizeof(quotes)/sizeof(*quotes);
@ -90,13 +84,11 @@ RouteHead(RouteRoot, arr, argp)
P("color: #eee;");
P("font-family: sans-serif;");
P("}");
P("#ascii {");
P("text-align: center;");
P("color: #be1337;");
P("}");
P("#ascii pre {");
P("display: inline-block;");
P("text-align: left;");
P("#cols {");
P("column-count: 3;");
P("min-width: 100%;");
P("max-width: 100%;");
P("width: 100%;");
P("}");
P("img {");
P("image-rendering: pixelated;");
@ -123,7 +115,6 @@ RouteHead(RouteRoot, arr, argp)
P("<body>");
{
size_t i;
P("<center>");
P("<h1>Your %s is running, all with that %s!</h1>", NAME, CODE);
P("</center>");
@ -133,15 +124,6 @@ RouteHead(RouteRoot, arr, argp)
P("%s", GetRandomQuote());
}
P("</i></blockquote>");
P("<pre id='ascii'><code>");
for (i = 0; i < PARSEE_ASCII_LINES; i++)
{
XMLElement *e = XMLCreateText((char *) parsee_ascii[i]);
XMLEncode(args->stream, e);
XMLFreeElement(e);
P("<br/>");
}
P("</code></pre>");
P("<p>");
{
@ -179,7 +161,7 @@ RouteHead(RouteRoot, arr, argp)
P("<p>");
{
P("More information available at ");
P("<a ");
P("<a");
P("href='https://kappach.at/parsee'");
P(">the actual page</a>.");
}
@ -254,6 +236,5 @@ RouteHead(RouteRoot, arr, argp)
P("</html>");
#undef P
(void) arr;
return NULL;
}

View file

@ -42,7 +42,6 @@ RouteHead(RouteTxns, arr, argp)
response = HashMapCreate();
end:
(void) arr;
JsonFree(request);
return response;
}

View file

@ -67,8 +67,7 @@ RouteHead(RouteRoomAck, arr, argp)
}
muc = ParseeDecodeLocalMUC(args->data->config, room);
if (ParseeManageBan(args->data, muc, NULL) ||
ParseeIsMUCWhitelisted(args->data, muc))
if (ParseeManageBan(args->data, muc, NULL))
{
HttpResponseStatus(args->ctx, HTTP_METHOD_NOT_ALLOWED);
response = MatrixCreateError(
@ -131,7 +130,7 @@ RouteHead(RouteRoomAck, arr, argp)
{
char *rev = StrConcat(2, muc, "/parsee");
Log(LOG_NOTICE, "Sending presence to %s", rev);
XMPPJoinMUC(args->data->jabber, "parsee", rev, NULL, -1, false);
XMPPJoinMUC(args->data->jabber, "parsee", rev, false);
Free(rev);
}

View file

@ -6,35 +6,32 @@
#include <XMPP.h>
static ParseeData *data;
static HttpServer *server = NULL;
static pthread_t xmpp_thr;
static XMPPComponent *jabber = NULL;
static void
SignalHandler(int signal)
{
if (data->server && (signal == SIGTERM || signal == SIGINT))
if (server && (signal == SIGTERM || signal == SIGINT))
{
Log(LOG_INFO, "Killing thread...");
pthread_mutex_lock(&data->halt_lock);
data->halted = true;
pthread_mutex_unlock(&data->halt_lock);
XMPPFinishCompStream(data->jabber);
XMPPFinishCompStream(jabber);
pthread_join(xmpp_thr, NULL);
Log(LOG_INFO, "Stopping server...");
HttpServerStop(data->server);
HttpServerStop(server);
return;
}
}
bool
ParseeInitialiseSignals(ParseeData *d, pthread_t xmpp)
ParseeInitialiseSignals(HttpServer *s, pthread_t xmpp, XMPPComponent *j)
{
struct sigaction sa;
data = d;
server = s;
xmpp_thr = xmpp;
jabber = j;
sigfillset(&sa.sa_mask);
sa.sa_handler = SignalHandler;

View file

@ -40,7 +40,6 @@ CreateStanzaBuilder(char *from, char *to, char *id)
builder->replying_to_stanza = NULL;
builder->replying_to_sender = NULL;
builder->editing = NULL;
builder->type = NULL;
builder->body = NULL;
builder->oob = NULL;
@ -185,7 +184,7 @@ ExportStanza(StanzaBuilder *builder)
builder->replying_to_sender &&
builder->body)
{
int off = ParseeFindDatastartU(builder->body);
int off = ParseeFindDatastart(builder->body);
char *ostr = StrInt(off);
XMLElement *reply = XMLCreateTag("reply");
XMLElement *fallback = XMLCreateTag("fallback");
@ -228,21 +227,22 @@ ExportStanza(StanzaBuilder *builder)
}
void
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber, size_t max)
WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber)
{
XMLElement *elem;
if (!builder || !jabber)
{
return;
}
if (!max)
{
max = 10000; /* XMPP recommended limit */
}
elem = ExportStanza(builder);
XMPPSendStanza(jabber, elem, max);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, elem);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(elem);
return;
}
StanzaBuilder *
@ -258,6 +258,7 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
{
XMLElement *parsee_version, *ver_elem;
XMLElement *parsee_link, *link_elem;
XMLElement *parsee_text, *text_elem;
XMLElement *parsee_event, *event_elem;
XMLElement *parsee_json, *json_elem;
char *event_id = GrabString(e, 1, "event_id");
@ -284,6 +285,16 @@ SetStanzaXParsee(StanzaBuilder *builder, HashMap *e)
XMLAddChild(parsee_link, link_elem);
XMLAddChild(parsee, parsee_link);
parsee_text = XMLCreateTag("zayds-note");
text_elem = XMLCreateText("\"LDA HANG YOURSELF\" - Zayd");
XMLAddChild(parsee_text, text_elem);
XMLAddChild(parsee, parsee_text);
parsee_text = XMLCreateTag("mcnebs-note");
text_elem = XMLCreateText("LDA will never beat the allegations");
XMLAddChild(parsee_text, text_elem);
XMLAddChild(parsee, parsee_text);
if (event_id)
{
parsee_event = XMLCreateTag("event-id");

View file

@ -7,6 +7,7 @@
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
char **
StrSplitLines(char *text)
@ -116,7 +117,7 @@ StrFullRect(char **split)
char
StrGet(StringRect *rect, int line, int col)
{
size_t actual_line, actual_col;
int actual_line, actual_col;
char *linep;
if (!rect || !rect->source_lines)
{
@ -149,7 +150,7 @@ StrGet(StringRect *rect, int line, int col)
size_t
StrViewChars(StringRect rect, int line)
{
size_t actual_line;
int actual_line;
char *linep;
if (!rect.source_lines)
{
@ -173,7 +174,7 @@ StrViewChars(StringRect rect, int line)
StringRect
StrGetl(StringRect *rect, int line, bool extend)
{
size_t actual_line;
int actual_line;
StringRect ret;
if (!rect->source_lines)
{
@ -203,7 +204,7 @@ StrGetl(StringRect *rect, int line, bool extend)
StringRect
StrShift(StringRect rect, int n)
{
size_t new = rect.start_char + n;
int new = rect.start_char + n;
if (new > rect.end_char)
{
new = rect.end_char;

View file

@ -10,10 +10,10 @@
Stream *
StrStreamReaderN(char *buffer, int n)
{
if (!buffer || n < 0)
if (!buffer)
{
return NULL;
}
return StreamFile(fmemopen(buffer, n ? (size_t) n : strlen(buffer), "rb"));
return StreamFile(fmemopen(buffer, n ? n : strlen(buffer), "rb"));
}

View file

@ -11,9 +11,6 @@ static ssize_t
ReadStreamWriter(void *coop, void *to, size_t n)
{
/* Reading from a stream writer is silly. */
(void) coop;
(void) to;
(void) n;
return 0;
}
static ssize_t
@ -36,9 +33,6 @@ static off_t
SeekStreamWriter(void *coop, off_t mag, int sgn)
{
/* TODO: Seeking would be useful, though not supported yet. */
(void) coop;
(void) mag;
(void) sgn;
return 0;
}
@ -46,11 +40,10 @@ static int
CloseStreamWriter(void *coop)
{
/* Nothing to free as of now. */
(void) coop;
return 0;
}
static const IoFunctions Functions = {
const static IoFunctions Functions = {
.read = ReadStreamWriter,
.seek = SeekStreamWriter,
.write = WriteStreamWriter,

View file

@ -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;
}

View file

@ -87,7 +87,7 @@ DecodeQuote(StringRect rect, size_t *skip)
* > four but that's for Nerds
* > seasons See, Touhou reference!
* concealing!) */
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace((int) ch))
while ((ch = StrGet(&rect, lines - 1, shift_by)) && isspace(ch))
{
shift_by++;
}
@ -132,7 +132,7 @@ DecodeSpan(StringRect rect, char del, size_t *skip)
{
return StrFullRect(NULL);
}
if (!ret.source_lines && isspace((int) c))
if (!ret.source_lines && isspace(c))
{
return StrFullRect(NULL);
}
@ -329,8 +329,8 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
break;
case XEP393_MONO:
head = XMLCreateTag("code");
XMLAddChild(xmlparent, XMLCreateText("`"));
XMLAddChild(xmlparent, head);
XMLAddChild(head, XMLCreateText("`"));
break;
case XEP393_SRKE:
head = XMLCreateTag("s");
@ -372,7 +372,7 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
XMLAddChild(head, XMLCreateText("_"));
break;
case XEP393_MONO:
XMLAddChild(xmlparent, XMLCreateText("`"));
XMLAddChild(head, XMLCreateText("`"));
break;
case XEP393_SRKE:
XMLAddChild(head, XMLCreateText("~"));
@ -399,37 +399,21 @@ ShoveXML(XEP393Element *element, XMLElement *xmlparent)
char *
XEP393ToXMLString(XEP393Element *xepd)
{
XMLElement *root, *act_root;
XMLElement *child;
XMLElement *root;
Stream *writer;
char *ret = NULL;
size_t i, children;
if (!xepd)
{
return NULL;
}
root = XMLCreateTag("span");
act_root = root;
ShoveXML(xepd, root);
writer = StrStreamWriter(&ret);
children = ArraySize(root->children);
child = ArrayGet(root->children, 0);
if (children == 1 && StrEquals(child->name, "p"))
{
children = ArraySize(child->children);
root = child;
}
for (i = 0; i < children; i++)
{
child = ArrayGet(root->children, i);
XMLEncode(writer, child);
}
XMLFreeElement(act_root);
XMLEncode(writer, root);
XMLFreeElement(root);
StreamFlush(writer);
StreamClose(writer);

View file

@ -31,12 +31,6 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
bool flag = false;
switch (event->type)
{
case XML_ERROR:
XMLFreeEvent(event);
XMLFreeElement(ret);
ArrayFree(stack);
XMLFreeLexer(lexer);
return NULL;
case XML_LEXER_STARTELEM:
/* Create a new element that will populated. */
top = XMLCreateTag(event->element);
@ -120,19 +114,9 @@ XMLCDecode(Stream *stream, bool autofree, bool html)
void
XMLEncodeString(Stream *stream, char *data)
{
size_t i, len;
size_t i;
if (!stream || !data)
{
return;
}
/* TODO: I should write a "Parsee Best Practice" guideline and make sure
* people understand to NOT constantly recompute lengths parameter on
* these kinds of loops. ArraySize is fine(since its indirection), but
* operations like strlen take time! */
len = strlen(data);
for (i = 0; i < len; i++)
for (i = 0; i < strlen(data); i++)
{
char c = data[i];
if (c == '<')
@ -161,9 +145,6 @@ XMLEncodeString(Stream *stream, char *data)
continue;
}
StreamPrintf(stream, "%c", c);
/* TODO: Maybe consider Unistrings and encode arbitrary Unicode
* codepoints * with special XML. Oughta make it printable, you know?
*/
}
}
void

View file

@ -58,7 +58,6 @@ static char * XMLPopElement(XMLexer *lexer);
static XMLEvent * XMLCreateEmptyElem(XMLexer *lexer, HashMap *attrs);
static XMLEvent * XMLCreateStart(XMLexer *lexer, HashMap *attrs);
static XMLEvent * XMLCreateRelax(XMLexer *lexer);
static XMLEvent * XMLCreateError(XMLexer *lexer);
static XMLEvent * XMLCreateEnd(XMLexer *lexer, char *end);
static XMLEvent * XMLCreateData(XMLexer *lexer);
@ -199,9 +198,7 @@ XMLCrank(XMLexer *lexer)
else if (XMLookahead(lexer, "--", false))
{
/* Throw error */
XMLFreeEvent(event);
event = XMLCreateError(lexer);
break;
return NULL;
}
break;
case XML_STATE_PI:
@ -218,9 +215,6 @@ XMLCrank(XMLexer *lexer)
if (!attrname)
{
/* TODO: Throw error */
XMLFreeEvent(event);
event = XMLCreateError(lexer);
break;
}
XMLPushElement(lexer, attrname);
@ -247,10 +241,7 @@ XMLCrank(XMLexer *lexer)
}
else if (XMLookahead(lexer, "'", true))
{
//while (true); uh oh
XMLFreeEvent(event);
event = XMLCreateError(lexer);
break;
while (true);
}
break;
case XML_STATE_ATTRTAIL:
@ -259,8 +250,6 @@ XMLCrank(XMLexer *lexer)
if (!XMLookahead(lexer, ">", true))
{
/* TODO: Throw error. */
XMLFreeEvent(event);
event = XMLCreateError(lexer);
break;
}
lexer->state = XML_STATE_NONE;
@ -269,8 +258,6 @@ XMLCrank(XMLexer *lexer)
break;
default:
/* TODO */
XMLFreeEvent(event);
event = XMLCreateError(lexer);
break;
}
/* TODO: Crank our XML parser. */
@ -308,7 +295,7 @@ static bool
XMLookahead(XMLexer *lexer, const char *str, bool skip)
{
int *stack;
size_t top, i, len;
size_t top, i;
ssize_t ntop;
bool ret = false;
if (!lexer || !str)
@ -317,10 +304,9 @@ XMLookahead(XMLexer *lexer, const char *str, bool skip)
}
top = 0;
len = strlen(str);
stack = Malloc(len * sizeof(*stack));
stack = Malloc(strlen(str) * sizeof(*stack));
for (i = 0; i < len; i++)
for (i = 0; i < strlen(str); i++)
{
char c = str[i];
int getc = XMLGetc(lexer);
@ -350,8 +336,8 @@ seekback:
return ret;
}
#define IsNamestart(c) ((c == ':') || isalpha((int) c) || (c == '_'))
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit((int) c))
#define IsNamestart(c) ((c == ':') || isalpha(c) || (c == '_'))
#define IsNamepart(c) (IsNamestart(c) || (c == '-') || isdigit(c))
static char *
XMLParseName(XMLexer *lexer)
{
@ -596,8 +582,6 @@ XMLCreateEnd(XMLexer *lexer, char *end)
event->col = 0;
event->offset = 0;
(void) lexer;
return event;
}
static XMLEvent *
@ -706,26 +690,6 @@ XMLCreateData(XMLexer *lexer)
return event;
}
static XMLEvent *
XMLCreateError(XMLexer *lexer)
{
XMLEvent *event = Malloc(sizeof(*event));
size_t elements = ArraySize(lexer->data.elements);
event->type = XML_ERROR;
event->element = elements ?
StrDuplicate(ArrayGet(lexer->data.elements, elements - 1)) :
NULL;
event->attrs = NULL;
event->data = NULL;
/* TODO */
event->line = 0;
event->col = 0;
event->offset = 0;
return event;
}
static XMLEvent *
XMLCreateRelax(XMLexer *lexer)
{
XMLEvent *event = Malloc(sizeof(*event));

View file

@ -11,7 +11,6 @@
#include <errno.h>
#include <netdb.h>
#include <StringStream.h>
#include <Parsee.h>
#include <XML.h>
@ -19,7 +18,7 @@
#define DEFAULT_PROSODY_PORT 5347
XMPPComponent *
XMPPInitialiseCompStream(char *addr, char *host, int port)
XMPPInitialiseCompStream(char *host, int port)
{
int sd = -1;
struct addrinfo hints, *res, *res0;
@ -28,17 +27,12 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
Stream *stream;
XMPPComponent *comp;
if (!addr)
{
addr = host;
}
snprintf(serv, sizeof(serv), "%hu", port ? port : DEFAULT_PROSODY_PORT);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(addr, serv, &hints, &res0);
error = getaddrinfo(host, serv, &hints, &res0);
if (error)
{
const char *error_str = gai_strerror(error);
@ -61,10 +55,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
if (connect(sd, res->ai_addr, res->ai_addrlen) < 0)
{
Log(LOG_ERR,
"%s: cannot connect to '%s': %s", __func__,
host, strerror(errno)
);
close(sd);
sd = -1;
continue;
@ -75,10 +65,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
if (sd < 0)
{
Log(LOG_ERR,
"%s: cannot connect to '%s': no socket available", __func__,
host
);
return NULL;
}
freeaddrinfo(res0);
@ -86,10 +72,6 @@ XMPPInitialiseCompStream(char *addr, char *host, int port)
stream = StreamFd(sd);
if (!stream)
{
Log(LOG_ERR,
"%s: cannot connect to '%s': %s", __func__,
host, "couldn't create a Cytoplasm stream"
);
close(sd);
return NULL;
}
@ -152,7 +134,7 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
}
break;
}
if (!ev || ev->type != XML_LEXER_STARTELEM ||
if (ev->type != XML_LEXER_STARTELEM ||
!StrEquals(ev->element, "stream:stream"))
{
Log(LOG_ERR, "Excepted stream:stream element.");
@ -177,54 +159,12 @@ XMPPAuthenticateCompStream(XMPPComponent *comp, char *shared)
}
break;
}
if (!ev || ev->type != XML_LEXER_ELEM ||
if (ev->type != XML_LEXER_ELEM ||
!StrEquals(ev->element, "handshake"))
{
Log(LOG_DEBUG, "type=%d elem='%s'", ev->type, ev->element);
Log(LOG_ERR, "Excepted empty handshake reply, got nonsense.");
Log(LOG_ERR, "Another service (possibly Parsee) may have taken over.");
while ((ev = XMLCrank(sax)))
{
char *key, *val;
switch (ev->type)
{
case XML_LEXER_STARTELEM:
Log(LOG_DEBUG, "<%s>", ev->element);
LogConfigIndent(LogConfigGlobal());
LogConfigIndent(LogConfigGlobal());
/* TODO: Log out attributes a little better */
while (HashMapIterate(ev->attrs, &key, (void **) &val))
{
Log(LOG_DEBUG, "(%s=%s)", key, val);
}
LogConfigUnindent(LogConfigGlobal());
LogConfigUnindent(LogConfigGlobal());
LogConfigIndent(LogConfigGlobal());
break;
case XML_LEXER_ELEM:
Log(LOG_DEBUG, "<%s/>", ev->element);
LogConfigIndent(LogConfigGlobal());
LogConfigIndent(LogConfigGlobal());
/* TODO: Log out attributes a little better */
while (HashMapIterate(ev->attrs, &key, (void **) &val))
{
Log(LOG_DEBUG, "(%s=%s)", key, val);
}
LogConfigUnindent(LogConfigGlobal());
LogConfigUnindent(LogConfigGlobal());
break;
case XML_LEXER_ENDELEM:
LogConfigUnindent(LogConfigGlobal());
Log(LOG_DEBUG, "</%s>", ev->element);
break;
case XML_LEXER_DATA:
Log(LOG_DEBUG, "%s", ev->data);
break;
}
XMLFreeEvent(ev);
}
LogConfigIndentSet(LogConfigGlobal(), 0);
Log(LOG_ERR, "");
Log(LOG_ERR, "Simply jealous of that other service...");
Free(stream_id);
@ -273,35 +213,3 @@ XMPPEndCompStream(XMPPComponent *comp)
Free(comp->host);
Free(comp);
}
void
XMPPSendStanza(XMPPComponent *comp, XMLElement *stanza, size_t max)
{
size_t len;
char *c = NULL;
Stream *stringWriter;
if (!comp || !stanza)
{
return;
}
stringWriter = StrStreamWriter(&c);
XMLEncode(stringWriter, stanza);
StreamFlush(stringWriter);
StreamClose(stringWriter);
if (c && max && (len = strlen(c)) > max)
{
Log(LOG_WARNING,
"Unexceptedly large stanza received (len=%d max=%d).",
(int) len, (int) max
);
Free(c);
return;
}
pthread_mutex_lock(&comp->write_lock);
StreamPrintf(comp->stream, "%s", c);
StreamFlush(comp->stream);
pthread_mutex_unlock(&comp->write_lock);
Free(c);
}

View file

@ -8,8 +8,6 @@
#include <Parsee.h>
#include <XML.h>
#include "XMPPThread/internal.h"
bool
XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
{
@ -20,6 +18,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
return false;
}
pthread_mutex_lock(&jabber->write_lock);
iq_query = XMLCreateTag("iq");
query = XMLCreateTag("query");
@ -35,7 +35,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
{
XMLElement *identity;
XMPPSendStanza(jabber, iq_query, 10000);
XMLEncode(jabber->stream, iq_query);
StreamFlush(jabber->stream);
XMLFreeElement(iq_query);
/* Except an IQ reply. 10 seconds of timeout is pretty
@ -44,8 +45,8 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
Free(uuid);
if (!iq_query || !StrEquals(iq_query->name, "iq"))
{
Log(LOG_ERR, "Didn't receive an <iq> stanza");
XMLFreeElement(iq_query);
pthread_mutex_unlock(&jabber->write_lock);
return false;
}
query = XMLookForUnique(iq_query, "query");
@ -56,11 +57,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
"conference"))
{
XMLFreeElement(iq_query);
Log(LOG_DEBUG, "MUC INFO ERROR");
Log(LOG_DEBUG,
"identityp=%p category=%s", identity,
identity ? HashMapGet(identity->attrs, "category") : NULL
);
pthread_mutex_unlock(&jabber->write_lock);
return false;
}
@ -76,6 +73,7 @@ XMPPQueryMUC(XMPPComponent *jabber, char *muc, MUCInfo *out)
XMLFreeElement(iq_query);
}
}
pthread_mutex_unlock(&jabber->write_lock);
return true;
}
@ -118,6 +116,7 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
return;
}
pthread_mutex_lock(&jabber->write_lock);
stanza = XMLCreateTag("message");
XMLAddAttr(stanza, "id", (identifier = StrRandom(32)));
XMLAddAttr(stanza, "from", from);
@ -161,68 +160,44 @@ XMPPRequestVoice(XMPPComponent *jabber, char *from, char *muc)
}
XMLAddChild(stanza, x);
}
XMPPSendStanza(jabber, stanza, 10000);
XMLEncode(jabber->stream, stanza);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(stanza);
Free(identifier);
}
bool
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, char *hash, int time, bool ret)
XMPPJoinMUC(XMPPComponent *comp, char *fr, char *muc, bool care)
{
XMLElement *presence, *x, *reply, *history, *photo;
IQFeatures *features;
char *from, *id, *stime = "3600";
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");
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);
XMPPAnnotatePresence(presence);
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);
}
XMLEncode(comp->stream, presence);
StreamFlush(comp->stream);
XMPPSendStanza(comp, presence, 10000);
XMLFreeElement(presence);
Free(from);
if (ret && (reply = ParseeAwaitStanza(id, 500)))
pthread_mutex_unlock(&comp->write_lock);
if (care && (reply = ParseeAwaitStanza(id, 500)))
{
bool exit_code = true;
@ -242,13 +217,14 @@ void
XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
{
XMLElement *presence;
IQFeatures *features;
char *from, *id;
if (!comp || !fr || !muc)
{
return;
}
pthread_mutex_lock(&comp->write_lock);
presence = XMLCreateTag("presence");
XMLAddAttr(presence, "from", (from = StrConcat(3, fr, "@", comp->host)));
XMLAddAttr(presence, "to", muc);
@ -264,20 +240,14 @@ XMPPLeaveMUC(XMPPComponent *comp, char *fr, char *muc, char *reason)
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);
XMPPAnnotatePresence(presence);
XMPPSendStanza(comp, presence, 10000);
XMLEncode(comp->stream, presence);
StreamFlush(comp->stream);
XMLFreeElement(presence);
Free(from);
Free(id);
pthread_mutex_unlock(&comp->write_lock);
}

View file

@ -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);
}

View file

@ -7,8 +7,6 @@
#include <Parsee.h>
#include <XML.h>
#include <stdlib.h>
void
XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
@ -68,9 +66,13 @@ XMPPRetract(XMPPComponent *comp, char *fr, char *to, char *type, char *redact)
}
XMPPSendStanza(comp, message, 10000);
pthread_mutex_lock(&comp->write_lock);
XMLEncode(comp->stream, message);
StreamFlush(comp->stream);
XMLFreeElement(message);
pthread_mutex_unlock(&comp->write_lock);
Free(from);
Free(ident);
}
@ -83,8 +85,6 @@ XMPPIsParseeStanza(XMLElement *stanza)
return false;
}
/* TODO: Check if the user is a trustworthy Parsee puppet instead of some
* guy sending random stanzas */
return !!XMLookForUnique(stanza, "x-parsee");
}
@ -161,31 +161,27 @@ XMPPGetReply(XMLElement *elem)
return HashMapGet(rep->attrs, "id");
}
ssize_t
XMPPGetReplyOffset(XMLElement *elem)
void
XMPPAnnotatePresence(XMLElement *presence)
{
if (!elem)
XMLElement *c;
char *ver;
if (!presence)
{
return -1;
return;
}
for (size_t i = 0; i < ArraySize(elem->children); i++)
{
XMLElement *child = ArrayGet(elem->children, i);
char *xmlns = HashMapGet(child->attrs, "xmlns");
char *xfor = HashMapGet(child->attrs, "for");
if (StrEquals(child->name, "fallback") &&
StrEquals(xmlns, "urn:xmpp:feature-fallback:0") &&
StrEquals(xfor, "urn:xmpp:reply:0"))
{
XMLElement *body = XMLookForUnique(child, "body");
if (body && HashMapGet(body->attrs, "end"))
{
return strtol(HashMapGet(body->attrs, "end"), NULL, 10);
}
}
}
return -1;
ver = XMPPGenerateVer();
c = XMLCreateTag("c");
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
XMLAddAttr(c, "hash", "sha-1");
XMLAddAttr(c, "node", REPOSITORY);
XMLAddAttr(c, "ver", ver);
Free(ver);
XMLAddChild(presence, c);
}
char *
XMPPGetModeration(XMLElement *stanza)
{
@ -270,7 +266,10 @@ XMPPSendDisco(ParseeData *data, char *from, char *to)
XMLAddChild(iq, query);
}
XMPPSendStanza(jabber, iq, 10000);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq);
ret = ParseeAwaitStanza(identifier, 1.25 SECONDS);

View file

@ -34,17 +34,7 @@ struct XMPPCommandManager {
HashMap *sessions;
void *cookie;
XMPPCmdFilter filter;
};
static bool
XMPPDefaultFilter(XMPPCommandManager *manager, char *id, XMLElement *stanza)
{
(void) manager;
(void) stanza;
(void) id;
return true;
}
static void
XMPPDestroySession(XMPPSession *session)
{
@ -128,7 +118,6 @@ XMPPCreateManager(void *cookie)
ret->commands = HashMapCreate();
ret->sessions = HashMapCreate();
ret->cookie = cookie;
ret->filter = XMPPDefaultFilter;
return ret;
}
@ -172,12 +161,12 @@ XMPPFreeManager(XMPPCommandManager *manager)
Free(manager);
}
void
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement *s)
XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p)
{
char *node_name;
XMPPCommand *val;
XMLElement *item;
if (!m || !p || !jid || !s)
if (!m || !p || !jid)
{
return;
}
@ -185,14 +174,11 @@ XMPPShoveCommandList(XMPPCommandManager *m, char *jid, XMLElement *p, XMLElement
pthread_mutex_lock(&m->lock);
while (HashMapIterate(m->commands, &node_name, (void **) &val))
{
if (m->filter(m, node_name, s))
{
item = XMLCreateTag("item");
XMLAddAttr(item, "jid", jid);
XMLAddAttr(item, "node", node_name);
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
XMLAddChild(p, item);
}
item = XMLCreateTag("item");
XMLAddAttr(item, "jid", jid);
XMLAddAttr(item, "node", node_name);
XMLAddAttr(item, "name", XMPPGetCommandDesc(val));
XMLAddChild(p, item);
}
pthread_mutex_unlock(&m->lock);
}
@ -220,16 +206,6 @@ XMPPVerifySession(XMPPCommandManager *mgr, char *s_id, char *from, char *to)
return ret;
}
void
XMPPManagerSetFilter(XMPPCommandManager *manager, XMPPCmdFilter filter)
{
if (!manager || !filter)
{
return;
}
manager->filter = filter;
}
bool
XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
{
@ -266,7 +242,7 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
/* This is an execution. */
cmd = HashMapGet(m->commands, node);
if (!cmd || !m->filter(m, node, stanza))
if (!cmd)
{
/* TODO: Set an error note */
goto end;
@ -298,7 +274,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
x = XMPPFormifyCommand(m, cmd, from);
XMLAddChild(command_xml, x);
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq);
goto end;
@ -326,7 +305,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
XMPPExecuteCommand(m, cmd, from, command_xml, NULL);
XMLAddChild(iq, command_xml);
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq);
InvalidateSession(m, session_id);
@ -366,7 +348,10 @@ XMPPManageCommand(XMPPCommandManager *m, XMLElement *stanza, ParseeData *data)
XMPPExecuteCommand(m, cmd, from, command_xml, x_form);
XMLAddChild(iq, command_xml);
XMPPSendStanza(jabber, iq, data->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq);
InvalidateSession(m, session_given);

View file

@ -15,11 +15,19 @@ void
AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
{
ParseeData *data = XMPPGetManagerCookie(m);
char *trimmed = ParseeTrimJID(from);
size_t i;
XMLElement *x;
XMLElement *title;
XMLElement *reported, *item, *field, *value, *txt;
if (!ParseeIsAdmin(data, trimmed))
{
SetNote("error", "User is not authorised to execute command.");
Free(trimmed);
return;
}
Free(trimmed);
x = XMLCreateTag("x");
title = XMLCreateTag("title");
@ -50,7 +58,4 @@ AdminsCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
DbUnlock(data->db, ref);
}
XMLAddChild(out, x);
(void) form;
(void) from;
}

View file

@ -29,6 +29,4 @@ CleanCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
ParseeCleanup(data);
/* TODO: Cleanup old sessions? */
SetNote("info", "Parsee data was sucessfully cleant up.");
(void) form;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -87,6 +87,4 @@ NoflyCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *o
}
DbUnlock(data->db, ref);
}
(void) form;
}

View file

@ -14,6 +14,7 @@
void
StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *out)
{
ParseeData *data = XMPPGetManagerCookie(m);
char *trimmed = ParseeTrimJID(from);
size_t alloc = MemoryAllocated();
size_t kb = alloc >> 10;
@ -25,6 +26,13 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
XMLElement *x;
XMLElement *title, *txt;
if (!ParseeIsAdmin(data, trimmed))
{
SetNote("error", "User is not authorised to execute command.");
Free(trimmed);
return;
}
Free(trimmed);
x = XMLCreateTag("x");
title = XMLCreateTag("title");
@ -67,7 +75,4 @@ StatusCallback(XMPPCommandManager *m, char *from, XMLElement *form, XMLElement *
EndItem();
}
XMLAddChild(out, x);
(void) form;
(void) m;
}

View file

@ -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;
}

View file

@ -121,7 +121,7 @@ ParseeVerifyAllStanza(ParseeData *args, XMLElement *stanza)
bool
ServerHasXEP421(ParseeData *data, char *from)
{
char *server = NULL, *postserv, *parsee;
char *server = NULL, *parsee;
XMLElement *disco;
bool ret = false;
if (!data || !from)
@ -140,10 +140,9 @@ ServerHasXEP421(ParseeData *data, char *from)
}
server = StrDuplicate(server);
postserv = server ? strchr(server, '/') : NULL;
if (postserv)
if (strchr(server, '/'))
{
*postserv = '\0';
*(strchr(server, '/')) = '\0';
}
parsee = ParseeJID(data);
@ -227,6 +226,7 @@ ParseeGetBridgedUserI(ParseeData *data, XMLElement *stanza, char *force)
{
ParseePushOIDTable(xmpp_from, occ_id);
}
Log(LOG_DEBUG, "Trying Occ ID for %s{%s}", xmpp_from, occ_id);
}
if (!occ_id)

View file

@ -3,7 +3,6 @@
#include <Cytoplasm/Util.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Sha.h>
#include <Cytoplasm/Log.h>
#include <StringStream.h>
#include <XMPPCommand.h>
@ -13,86 +12,33 @@
#include "XMPPThread/internal.h"
IQFeatures *
CreateIQFeatures(void)
{
IQFeatures *ret = Malloc(sizeof(*ret));
ret->identity = ArrayCreate();
ret->adverts = ArrayCreate();
return ret;
}
void
FreeIQFeatures(IQFeatures *features)
{
size_t i;
if (!features)
{
return;
}
for (i = 0; i < ArraySize(features->adverts); i++)
{
Free(ArrayGet(features->adverts, i));
}
ArrayFree(features->adverts);
for (i = 0; i < ArraySize(features->identity); i++)
{
XMPPIdentity *identity = ArrayGet(features->identity, i);
Free(identity->category);
Free(identity->type);
Free(identity->lang);
Free(identity->name);
Free(identity);
}
ArrayFree(features->identity);
Free(features);
}
void
AdvertiseIQFeature(IQFeatures *f, char *feature)
{
if (!f || !feature)
{
return;
}
ArrayAdd(f->adverts, StrDuplicate(feature));
}
void
AddIQIdentity(IQFeatures *f, char *cat, char *lang, char *type, char *name)
{
XMPPIdentity *identity;
if (!f)
{
return;
}
identity = Malloc(sizeof(*identity));
identity->category = StrDuplicate(cat);
identity->type = StrDuplicate(type);
identity->lang = StrDuplicate(lang);
identity->name = StrDuplicate(name);
ArrayAdd(f->identity, identity);
}
/* Generates a SHA-256 hash of the ver field. */
char *
XMPPGenerateVer(IQFeatures *features)
XMPPGenerateVer(void)
{
char *S = NULL;
unsigned char *Sha = NULL;
Array *identities = ArrayCreate();
Array *features = ArrayCreate();
size_t i;
/* Initialise identity table, to be sorted */
ArraySort(features->identity, IdentitySort);
for (i = 0; i < ArraySize(features->identity); i++)
#define IdentitySimple(cat, Type, Name) { \
XMPPIdentity *id = Malloc(sizeof(*id)); \
id->category = cat; \
id->lang = NULL; \
id->type = Type; \
id->name = Name; \
ArrayAdd(identities, id); }
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(feature) ArrayAdd(features, feature);
IQ_ADVERT
#undef AdvertiseSimple
ArraySort(identities, IdentitySort);
for (i = 0; i < ArraySize(identities); i++)
{
XMPPIdentity *identity = ArrayGet(features->identity, i);
XMPPIdentity *identity = ArrayGet(identities, i);
char *id_chunk = StrConcat(7,
identity->category, "/",
identity->type, "/",
@ -104,10 +50,10 @@ XMPPGenerateVer(IQFeatures *features)
Free(id_chunk);
}
ArraySort(features->adverts, ((int (*) (void *, void *)) ICollate));
for (i = 0; i < ArraySize(features->adverts); i++)
ArraySort(features, ((int (*) (void *, void *)) ICollate));
for (i = 0; i < ArraySize(features); i++)
{
char *feature = ArrayGet(features->adverts, i);
char *feature = ArrayGet(features, i);
char *tmp = S;
S = StrConcat(3, S, feature, "<");
Free(tmp);
@ -118,64 +64,16 @@ XMPPGenerateVer(IQFeatures *features)
S = Base64Encode((const char *) Sha, 20);
Free(Sha);
ArrayFree(features);
for (i = 0; i < ArraySize(identities); i++)
{
XMPPIdentity *identity = ArrayGet(identities, i);
/* We don't have to do anything here. */
Free(identity);
}
ArrayFree(identities);
return S;
}
void
XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features)
{
XMLElement *c;
char *ver;
if (!presence || !features)
{
return;
}
ver = XMPPGenerateVer(features);
c = XMLCreateTag("c");
XMLAddAttr(c, "xmlns", "http://jabber.org/protocol/caps");
XMLAddAttr(c, "hash", "sha-1");
XMLAddAttr(c, "node", REPOSITORY);
XMLAddAttr(c, "ver", ver);
Free(ver);
XMLAddChild(presence, c);
}
IQFeatures *
LookupJIDFeatures(char *jid)
{
IQFeatures *features;
if (!jid)
{
return NULL;
}
features = CreateIQFeatures();
if (*jid == '#')
{
/* This is a MUC. As such, we need to advertise MUCs */
#define ID(...) AddIQIdentity(features, __VA_ARGS__)
#define AD(var) AdvertiseIQFeature(features, var)
ID("gateway", NULL, "matrix", "Parsee MUC gateway");
ID("conference", NULL, "text", "Parsee MUC gateway");
ID("component", NULL, "generic", "Parsee component");
AD("http://jabber.org/protocol/muc");
#undef AD
#undef ID
}
else
{
#define IdentitySimple(cat, Type, Name) AddIQIdentity(features, cat, NULL, Type, Name);
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(feature) AdvertiseIQFeature(features, feature);
IQ_ADVERT
#undef AdvertiseSimple
}
return features;
}

View file

@ -10,8 +10,6 @@
#include <XMPP.h>
#include <XML.h>
#define PUBSUB "http://jabber.org/protocol/pubsub"
XMLElement *
CreatePubsubRequest(char *from, char *to, char *node)
{
@ -24,7 +22,7 @@ CreatePubsubRequest(char *from, char *to, char *node)
XMLAddAttr(iq_req, "type", "set");
pubsub = XMLCreateTag("pubsub");
XMLAddAttr(pubsub, "xmlns", PUBSUB);
XMLAddAttr(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
XMLAddChild(iq_req, pubsub);
sub = XMLCreateTag("subscribe");
@ -36,40 +34,12 @@ CreatePubsubRequest(char *from, char *to, char *node)
return iq_req;
}
static bool
IsPubsubRequest(XMLElement *stanza)
{
char *type = HashMapGet(stanza ? stanza->attrs : NULL, "type");
XMLElement *pubsub;
if (!stanza)
{
return false;
}
if (!StrEquals(stanza->name, "iq") ||
!StrEquals(type, "set"))
{
return false;
}
pubsub = XMLookForTKV(stanza, "pubsub", "xmlns", PUBSUB);
if (!pubsub)
{
return false;
}
return XMLookForUnique(pubsub, "subscribe");
}
struct PEPManager {
pthread_mutex_t lock;
ParseeData *data;
HashMap *node_table;
HashMap *followers;
void *cookie;
};
@ -86,7 +56,6 @@ CreatePEPManager(ParseeData *data, void *cookie)
ret->cookie = cookie;
ret->data = data;
ret->node_table = HashMapCreate();
ret->followers = HashMapCreate();
pthread_mutex_init(&ret->lock, NULL);
return ret;
@ -99,32 +68,29 @@ PEPManagerCookie(PEPManager *manager)
void
PEPManagerAddEvent(PEPManager *manager, char *node, PEPEvent event)
{
PEPEvent *indirect;
if (!manager || !node || !event)
{
return;
}
indirect = Malloc(sizeof(event));
*indirect = event;
pthread_mutex_lock(&manager->lock);
HashMapSet(manager->node_table, node, indirect);
HashMapSet(manager->node_table, node, event);
pthread_mutex_unlock(&manager->lock);
}
static bool
PEPManagerHandleEvent(PEPManager *manager, XMLElement *stanza)
{
PEPEvent *call = NULL;
XMLElement *event = NULL, *ps = NULL, *ev = NULL;
PEPEvent call = NULL;
XMLElement *event, *ps, *ev;
size_t i;
if (!manager || !stanza)
{
return false;
}
if (!(ps = XMLookForTKV(stanza, "pubsub", "xmlns", PUBSUB)) &&
!(ev = XMLookForTKV(stanza, "event", "xmlns", PUBSUB "#event")))
#define PEP_NS "http://jabber.org/protocol/pubsub"
if (!(ps = XMLookForTKV(stanza, "pubsub", "xmlns", PEP_NS)) &&
!(ev = XMLookForTKV(stanza, "event", "xmlns", PEP_NS "#event")))
{
return false;
}
@ -135,7 +101,7 @@ PEPManagerHandleEvent(PEPManager *manager, XMLElement *stanza)
XMLElement *items = ArrayGet(event->children, i);
char *node = HashMapGet(items->attrs, "node");
if ((call = HashMapGet(manager->node_table, node)) && *call)
if ((call = HashMapGet(manager->node_table, node)))
{
size_t j;
/* Use the callback over all items */
@ -143,13 +109,13 @@ PEPManagerHandleEvent(PEPManager *manager, XMLElement *stanza)
{
for (j = 0; j < ArraySize(items->children); j++)
{
(*call)(manager, stanza, ArrayGet(items->children, j));
call(manager, stanza, ArrayGet(items->children, j));
}
return true;
}
/* ... or over "items" specifically. */
(*call)(manager, stanza, items);
call(manager, stanza, items);
return true;
}
}
@ -166,11 +132,6 @@ PEPManagerHandle(PEPManager *manager, XMLElement *stanza)
}
/* Check if it is a PEP stanza */
if (IsPubsubRequest(stanza))
{
Log(LOG_DEBUG, "UNIMPLEMENTED PUBSUB SUBSCRIPTION");
/* TODO */
}
if (PEPManagerHandleEvent(manager, stanza))
{
return true;
@ -181,21 +142,12 @@ PEPManagerHandle(PEPManager *manager, XMLElement *stanza)
void
DestroyPEPManager(PEPManager *manager)
{
char *key;
PEPEvent *val;
if (!manager)
{
return;
}
pthread_mutex_destroy(&manager->lock);
while (HashMapIterate(manager->node_table, &key, (void **) &val))
{
Free(val);
}
HashMapFree(manager->node_table);
HashMapFree(manager->followers);
Free(manager);
}

View file

@ -72,7 +72,11 @@ PEPAvatarEvent(PEPManager *m, XMLElement *stanza, XMLElement *item)
char *url = HashMapGet(item->attrs, "url");
XMLElement *request = CreateAvatarRequest(from, to, id);
XMPPSendStanza(jabber, request, args->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, request);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(request);
(void) url; /* TODO */

View file

@ -113,7 +113,9 @@ PEPVCardEvent(PEPManager *m, XMLElement *stanza, XMLElement *item)
}
}
XMPPSendStanza(jabber, reply, data->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, reply);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(reply);
(void) item;
}

View file

@ -1,144 +0,0 @@
#include "XMPPThread/internal.h"
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Base64.h>
#include <Cytoplasm/Json.h>
#include <Cytoplasm/Log.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Db.h>
#include <string.h>
static char *
SubscriptionHash(char *from, char *to)
{
uint8_t *sum;
char *hash;
size_t len;
len = strlen(from) + 1 + strlen(to);
sum = Malloc(len);
memset(sum, 0x00, len);
memcpy(&sum[0], from, strlen(from));
memcpy(&sum[strlen(from) + 1], to, strlen(to));
hash = Base64Encode((const char *) sum, len);
Free(sum);
return hash;
}
static void
DecodeSubscription(ParseeData *data, char *hash, char **from, char **to)
{
char *sum;
if (!data || !hash || !from || !to)
{
return;
}
sum = Base64Decode(hash, strlen(hash));
*from = StrDuplicate(sum);
*to = StrDuplicate(sum + strlen(sum) + 1);
Free(sum);
}
void
AddPresenceSubscriber(ParseeData *data, char *from, char *to)
{
Db *database;
DbRef *ref;
char *hash;
if (!data || !from || !to)
{
return;
}
database = data->db;
hash = SubscriptionHash(from, to);
ref = DbCreate(database, 2, "subs", hash);
if (!ref)
{
goto end;
}
HashMapSet(DbJson(ref), "from", JsonValueString(from));
HashMapSet(DbJson(ref), "to", JsonValueString(to));
/* I don't think we need more information right now */
end:
DbUnlock(database, ref);
Free(hash);
}
bool
IsSubscribed(ParseeData *data, char *user, char *to)
{
Db *database;
char *hash;
bool ret;
if (!data || !user || !to)
{
return false;
}
database = data->db;
hash = SubscriptionHash(user, to);
ret = DbExists(database, 2, "subs", hash);
Free(hash);
return ret;
}
void
ParseeBroadcastStanza(ParseeData *data, char *from, XMLElement *stanza)
{
XMPPComponent *jabber = data ? data->jabber : NULL;
Array *entries;
size_t i;
if (!data || !from || !stanza)
{
return;
}
/* Copy our stanza so that we can freely modify it */
stanza = XMLCopy(stanza);
/* Start doing a storm on Mt. Subs. */
entries = DbList(data->db, 1, "subs");
for (i = 0; i < ArraySize(entries); i++)
{
char *entry = ArrayGet(entries, i);
char *entry_from = NULL, *entry_to = NULL;
char *storm_id; /* ooe */
XMLElement *sub;
DecodeSubscription(data, entry, &entry_from, &entry_to);
if (!StrEquals(entry_to, from))
{
goto end;
}
Log(LOG_DEBUG,
"PRESENCE SYSTEM: "
"We should be brotkasting straight to %s (from %s)",
entry_from, from
);
sub = XMLCopy(stanza);
XMLAddAttr(sub, "from", from);
XMLAddAttr(sub, "to", entry_from);
/* TODO: Should we store IDs somewhere? */
XMLAddAttr(sub, "id", (storm_id = StrRandom(16)));
XMPPSendStanza(jabber, sub, data->config->max_stanza_size);
XMLFreeElement(sub);
Free(storm_id);
end:
Free(entry_from);
Free(entry_to);
}
DbListFree(entries);
XMLFreeElement(stanza);
}

View file

@ -4,6 +4,7 @@
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <time.h>
@ -56,8 +57,6 @@ XMPPDispatcher(void *argp)
if (!stanza)
{
/* TODO: We shouldn't be busywaiting. Even with a sleep call.
*/
UtilSleepMillis(10);
continue;
}
@ -67,15 +66,9 @@ XMPPDispatcher(void *argp)
Log(LOG_DEBUG, "Received stanza='%s'.", stanza->name);
}
if (ManageMUCStanza(args->muc, stanza))
{
XMLFreeElement(stanza);
continue;
}
if (StrEquals(stanza->name, "presence"))
{
PresenceStanza(args, stanza, thread);
PresenceStanza(args, stanza);
XMLFreeElement(stanza);
continue;
}
@ -134,49 +127,6 @@ ParseeCongestion(void)
return congestion;
}
bool
XMPPCommandFilter(XMPPCommandManager *m, char *id, XMLElement *stanza)
{
ParseeData *args = XMPPGetManagerCookie(m);
char *trimmed_from;
char *from;
char *chat_id;
bool is_muc;
if (!m || !id || !stanza)
{
return false;
}
from = HashMapGet(stanza->attrs, "from");
trimmed_from = ParseeTrimJID(from);
is_muc = !!(chat_id = ParseeGetFromMUCID(args, trimmed_from));
Free(trimmed_from);
Free(chat_id);
#define XMPP_COMMAND(f,l,n,t,s) \
if (StrEquals(n, id)) \
{ \
if (l == XMPPCMD_ALL) \
{ \
return true; \
} \
else if (l == XMPPCMD_MUC) \
{ \
return is_muc; \
} \
else if (l == XMPPCMD_ADMINS) \
{ \
bool is_admin; \
trimmed_from = ParseeTrimJID(from); \
is_admin = ParseeIsAdmin(args, trimmed_from); \
Free(trimmed_from); \
return is_admin; \
} \
}
XMPPCOMMANDS
#undef XMPP_COMMAND
return false;
}
void *
ParseeXMPPThread(void *argp)
@ -184,20 +134,16 @@ ParseeXMPPThread(void *argp)
ParseeData *args = argp;
XMPPComponent *jabber = args->jabber;
XMLElement *stanza = NULL;
HashMap *await_table2;
size_t i;
bool error = false;
/* Initialise the await table */
await_table = HashMapCreate();
/* Initialise the managers, and add all handlers. */
info.m = XMPPCreateManager(args);
XMPPManagerSetFilter(info.m, XMPPCommandFilter);
{
XMPPCommand *cmd;
#define XMPP_COMMAND(f,l,n,t,s) \
#define XMPP_COMMAND(f,n,t,s) \
cmd = XMPPBasicCmd( \
n, t, f \
); \
@ -244,95 +190,56 @@ ParseeXMPPThread(void *argp)
}
}
while (!args->halted)
while (true)
{
while (true)
char *id;
stanza = XMLDecode(jabber->stream, false);
if (!stanza)
{
char *id;
stanza = XMLDecode(jabber->stream, false);
if (!stanza)
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
{
/* Try to check if an error is abound */
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
{
Log(LOG_DEBUG, "RECEIVED EOF.");
}
break;
Log(LOG_DEBUG, "RECEIVED EOF.");
}
break;
}
if (args->verbosity >= PARSEE_VERBOSE_STANZA)
if (args->verbosity >= PARSEE_VERBOSE_STANZA)
{
Stream *output = StreamStderr();
StreamPrintf(output, "-------STANZA BEGIN-------" "\n");
XMLEncode(output, stanza);
StreamPrintf(output, "\n--------STANZA END--------" "\n");
StreamFlush(output);
}
id = HashMapGet(stanza->attrs, "id");
if (id)
{
XMPPAwait *await;
/* Lock out the table to see if we're awaiting. */
pthread_mutex_lock(&await_lock);
if ((await = HashMapGet(await_table, id)))
{
Stream *output = StreamStderr();
StreamPrintf(output, "-------STANZA BEGIN-------" "\n");
XMLEncode(output, stanza);
StreamPrintf(output, "\n--------STANZA END--------" "\n");
StreamFlush(output);
}
pthread_mutex_lock(&await->cond_lock);
await->stanza = stanza;
pthread_cond_signal(&await->condition);
pthread_mutex_unlock(&await->cond_lock);
id = HashMapGet(stanza->attrs, "id");
if (id)
{
XMPPAwait *await;
/* Lock out the table to see if we're awaiting. */
pthread_mutex_lock(&await_lock);
if ((await = HashMapGet(await_table, id)))
{
pthread_mutex_lock(&await->cond_lock);
await->stanza = stanza;
pthread_cond_signal(&await->condition);
pthread_mutex_unlock(&await->cond_lock);
HashMapDelete(await_table, id);
HashMapDelete(await_table, id);
pthread_mutex_unlock(&await_lock);
continue;
}
pthread_mutex_unlock(&await_lock);
continue;
}
/* Push it into the stanza FIFO. A dispatcher thread should then
* be able to freely grab a value(locked by a mutex). We can't make
* dispatchers read stanzas on their own, since that's _not_ how
* streams work, but this should mitigate some issues, and allow a
* few threads to be busy, while the rest of Parsee works. */
PushStanza(&info, stanza);
pthread_mutex_unlock(&await_lock);
}
pthread_mutex_lock(&args->halt_lock);
if (!args->halted)
{
Log(LOG_WARNING, "XMPP server is closing stream...");
for (size_t i = 0; i < 50; i++)
{
UtilSleepMillis(100); /* Wait a bit so that temporary failures don't fuck everything up */
Log(LOG_WARNING, "Restarting XMPP stream.");
/* This is the part where a new connection is being considered */
XMPPFinishCompStream(jabber);
XMPPEndCompStream(jabber);
args->jabber = XMPPInitialiseCompStream(
args->config->component_addr,
args->config->component_host,
args->config->component_port
);
jabber = args->jabber;
if (!jabber || !XMPPAuthenticateCompStream(jabber, args->config->shared_comp_secret))
{
/* Oops, there is something wrong! */
Log(LOG_ERR, "Couldn't authenticate to XMPP server");
XMPPEndCompStream(jabber);
args->jabber = NULL;
jabber = NULL;
if (i == 4)
{
pthread_mutex_unlock(&args->halt_lock);
break;
}
}
}
}
pthread_mutex_unlock(&args->halt_lock);
/* Push it into the stanza FIFO. A dispatcher thread should then
* be able to freely grab a value(locked by a mutex). We can't make
* dispatchers read stanzas on their own, since that's _not_ how
* streams work, but this should mitigate some issues, and allow a
* few threads to be busy, while the rest of Parsee works. */
PushStanza(&info, stanza);
}
info.running = false;
@ -361,18 +268,12 @@ ParseeXMPPThread(void *argp)
}
ArrayFree(info.stanzas);
await_table2 = await_table;
await_table = NULL;
HashMapFree(await_table2);
HashMapFree(await_table);
pthread_mutex_destroy(&info.lock);
DestroyPEPManager(info.pep_manager);
XMPPFreeManager(info.m);
if (error)
{
HttpServerStop(args->server); /* minor trolling */
}
return NULL;
}

View file

@ -9,7 +9,6 @@
#include <Matrix.h>
#include <AS.h>
#include <sys/utsname.h>
#include <string.h>
#include <ctype.h>
@ -33,7 +32,7 @@ TrimBase64(char *b64)
while (*b64)
{
char ch[2] = { *b64, 0 };
if (isspace((int) *b64))
if (isspace(*b64))
{
b64++;
continue;
@ -47,106 +46,35 @@ TrimBase64(char *b64)
return ret;
}
static bool
AvatarGrab(ParseeData *data, char *mxc, char **mime, char **b64, size_t *len)
{
char *mimei = NULL, *outi = NULL, *b64i = NULL;
size_t sizei;
if (!data || !mxc || !mime || !b64 || !len)
{
return false;
}
if (!ASGrab(data->config, mxc, &mimei, &outi, &sizei))
{
Free(mimei);
Free(outi);
return false;
}
b64i = Base64Encode(outi, sizei);
Free(outi);
if (!b64i)
{
Free(mimei);
b64i = StrDuplicate(media_unknown); /* TODO: Different assets! */
mimei = StrDuplicate("image/png");
}
*mime = mimei;
*b64 = b64i;
*len = sizei;
return true;
}
XMLElement *
GenerateAvatarData(ParseeData *data, char *mxid)
{
char *mxc = NULL, *mime = NULL, *b64 = NULL;
XMLElement *elem = NULL, *type, *binval;
size_t len = 0;
if (!data || !mxid)
{
return NULL;
}
/* TODO: Use the right room for the avatar! */
mxc = ASGetAvatar(data->config, NULL, mxid);
if (!mxc || !AvatarGrab(data, mxc, &mime, &b64, &len))
{
goto end;
}
elem = XMLCreateTag("PHOTO");
type = XMLCreateTag("TYPE");
binval = XMLCreateTag("BINVAL");
XMLAddChild(type, XMLCreateText(mime));
XMLAddChild(binval, XMLCreateText(b64));
XMLAddChild(elem, type);
XMLAddChild(elem, binval);
end:
Free(mime);
Free(mxc);
Free(b64);
return elem;
}
#define DISCO "http://jabber.org/protocol/disco#info"
static XMLElement *
IQGenerateQuery(IQFeatures *features)
IQGenerateQuery(void)
{
XMLElement *query;
if (!features)
{
return NULL;
}
query = XMLCreateTag("query");
XMLElement *query = XMLCreateTag("query");
XMLAddAttr(query, "xmlns", DISCO);
{
XMLElement *feature;
size_t i;
for (i = 0; i < ArraySize(features->identity); i++)
{
XMPPIdentity *identity = ArrayGet(features->identity, i);
#define IdentitySimple(c,t,n) do \
{ \
feature = XMLCreateTag("identity"); \
XMLAddAttr(feature, "category", c); \
XMLAddAttr(feature, "type", t); \
XMLAddAttr(feature, "name", n); \
XMLAddChild(query, feature); \
} \
while (0);
IQ_IDENTITY
#undef IdentitySimple
#define AdvertiseSimple(f) do \
{ \
feature = XMLCreateTag("feature"); \
XMLAddAttr(feature, "var", f); \
XMLAddChild(query, feature); \
} \
while (0);
feature = XMLCreateTag("identity");
XMLAddAttr(feature, "category", identity->category);
XMLAddAttr(feature, "type", identity->type);
XMLAddAttr(feature, "name", identity->name);
XMLAddChild(query, feature);
}
for (i = 0; i < ArraySize(features->adverts); i++)
{
char *var = ArrayGet(features->adverts, i);
feature = XMLCreateTag("feature");
XMLAddAttr(feature, "var", var);
XMLAddChild(query, feature);
}
IQ_ADVERT
#undef AdvertiseSimple
}
return query;
@ -156,7 +84,6 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
{
char *from, *to, *id;
XMLElement *iq_reply, *query;
IQFeatures *features;
from = HashMapGet(stanza->attrs, "from");
to = HashMapGet(stanza->attrs, "to");
@ -169,10 +96,9 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
XMLAddAttr(iq_reply, "type", "result");
XMLAddAttr(iq_reply, "id", id);
features = LookupJIDFeatures(to);
query = IQGenerateQuery(features);
query = IQGenerateQuery();
{
char *ver = XMPPGenerateVer(features);
char *ver = XMPPGenerateVer();
char *node = StrConcat(3, REPOSITORY, "#", ver);
XMLAddAttr(query, "node", node);
@ -180,12 +106,13 @@ IQDiscoGet(ParseeData *args, XMPPComponent *jabber, XMLElement *stanza)
Free(ver);
}
XMLAddChild(iq_reply, query);
FreeIQFeatures(features);
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq_reply);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq_reply);
(void) args;
}
void
@ -374,7 +301,7 @@ IQResult(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
bool
IQIsCommandList(ParseeData *args, XMLElement *stanza)
{
char *parsee = NULL, *to;
char *parsee = NULL;
XMLElement *query = XMLookForTKV(
stanza, "query", "xmlns",
"http://jabber.org/protocol/disco#items"
@ -389,27 +316,15 @@ IQIsCommandList(ParseeData *args, XMLElement *stanza)
}
parsee = ParseeJID(args);
to = HashMapGet(stanza->attrs, "to");
ret = StrEquals(to, parsee) || StrEquals(to, args->config->component_host);
ret = StrEquals(HashMapGet(stanza->attrs, "to"), parsee);
Free(parsee);
return ret;
}
static bool
IsInMUC(ParseeData *data, char *jid)
{
char *trimmed = ParseeTrimJID(jid);
char *chat_id = ParseeGetFromMUCID(data, trimmed);
Free(trimmed);
Free(chat_id);
return !!chat_id;
}
void
IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
XMPPComponent *jabber = args->jabber;
XMLElement *pubsub;
char *from = HashMapGet(stanza->attrs, "from");
char *to = HashMapGet(stanza->attrs, "to");
char *id = HashMapGet(stanza->attrs, "id");
@ -419,38 +334,33 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
if (IQIsCommandList(args, stanza))
{
XMLElement *iq_reply = XMLCreateTag("iq");
char *trimmed = ParseeTrimJID(from);
XMLAddAttr(iq_reply, "type", "result");
XMLAddAttr(iq_reply, "from", to);
XMLAddAttr(iq_reply, "to", from);
XMLAddAttr(iq_reply, "id", id);
{
XMLElement *q = XMLCreateTag("query");
char *parsee_muc_jid = StrConcat(3, trimmed, "/", "parsee");
XMLAddAttr(q, "xmlns", "http://jabber.org/protocol/disco#items");
XMLAddAttr(q, "node", "http://jabber.org/protocol/commands");
XMPPShoveCommandList(thr->info->m,
IsInMUC(args, from) ? parsee_muc_jid : to,
q, stanza
);
XMPPShoveCommandList(thr->info->m, to, q);
XMLAddChild(iq_reply, q);
Free(parsee_muc_jid);
}
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq_reply);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq_reply);
Free(trimmed);
}
else if (XMLookForTKV(stanza, "vCard", "xmlns", "vcard-temp"))
{
char *to_matrix = ParseeGetBridgedUser(args, stanza);
char *name = ASGetName(args->config, NULL, to_matrix);
XMLElement *iqVCard;
Log(LOG_DEBUG, "vCard information GET for %s (%s)", to, to_matrix);
Log(LOG_INFO, "vCard information GET for %s", to);
/* TODO: "a compliant server MUST respond on behalf of the
* requestor and not forward the IQ to the requestee's
* connected resource". */
if (!strncmp(to, "parsee@", 7))
{
iqVCard = XMLCreateTag("iq");
XMLElement *iqVCard = XMLCreateTag("iq");
XMLAddAttr(iqVCard, "from", to);
XMLAddAttr(iqVCard, "to", from);
XMLAddAttr(iqVCard, "id", id);
@ -476,97 +386,13 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
XMLAddChild(iqVCard, vCard);
}
XMPPSendStanza(jabber, iqVCard, args->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iqVCard);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iqVCard);
Free(to_matrix);
Free(name);
return;
}
Free(to_matrix);
to_matrix = ParseeDecodeMXID(to);
iqVCard = XMLCreateTag("iq");
XMLAddAttr(iqVCard, "from", to);
XMLAddAttr(iqVCard, "to", from);
XMLAddAttr(iqVCard, "id", id);
XMLAddAttr(iqVCard, "type", "result");
{
XMLElement *vCard = XMLCreateTag("vCard");
char *mto_link = ParseeGenerateMTO(to_matrix);
XMLAddAttr(vCard, "xmlns", "vcard-temp");
{
XMLAddChild(vCard, GenerateAvatarData(args, to_matrix));
Free(mto_link);
}
XMLAddChild(iqVCard, vCard);
}
XMPPSendStanza(jabber, iqVCard, args->config->max_stanza_size);
XMLFreeElement(iqVCard);
Free(to_matrix);
Free(name);
}
#define PS "http://jabber.org/protocol/pubsub"
else if ((pubsub = XMLookForTKV(stanza, "pubsub", "xmlns", PS)))
{
/* TODO: Pass this through the PEP manager */
XMLElement *a_items = XMLookForTKV(pubsub,
"items", "node", "urn:xmpp:avatar:data"
);
if (a_items)
{
/* Do, without regret, start shoving an avatar out the bus.
* NOTE: I explicitely choose to not do any manipulation
* because messing with random user images is inherently a
* risk I do *not* want to take. */
char *to_matrix = ParseeDecodeMXID(to);
char *avatar = ASGetAvatar(args->config, NULL, to_matrix);
char *mime = NULL;
char *b64 = NULL;
size_t len = 0;
XMLElement *reply;
AvatarGrab(args, avatar, &mime, &b64, &len);
Log(LOG_DEBUG, "IQ-GET: PUBSUB AVATAR OF=%s", to_matrix);
/* Strike back with a response */
reply = XMLCreateTag("iq");
XMLAddAttr(reply, "type", "result");
XMLAddAttr(reply, "to", from);
XMLAddAttr(reply, "from", to);
XMLAddAttr(reply, "id", HashMapGet(stanza->attrs, "id"));
{
XMLElement *ps = XMLCreateTag("pubsub");
XMLElement *items = XMLCreateTag("items");
XMLAddAttr(ps, "xmlns", PS);
XMLAddAttr(items, "node", "urn:xmpp:avatar:data");
{
XMLElement *item = XMLCreateTag("item");
XMLElement *data = XMLCreateTag("data");
XMLAddAttr(item, "id", "TODO");
XMLAddAttr(data, "xmlns", "urn:xmpp:avatar:data");
XMLAddChild(data, XMLCreateText(b64));
XMLAddChild(item, data);
XMLAddChild(items, item);
}
XMLAddChild(ps, items);
XMLAddChild(reply, ps);
}
XMPPSendStanza(jabber, reply, args->config->max_stanza_size);
XMLFreeElement(reply);
Free(to_matrix);
Free(avatar);
Free(mime);
Free(b64);
}
}
#undef PS
else if (XMLookForTKV(stanza, "query", "xmlns", DISCO))
{
IQDiscoGet(args, jabber, stanza);
@ -574,7 +400,7 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
else if (XMLookForTKV(stanza, "query", "xmlns", "jabber:iq:version"))
{
XMLElement *iq_reply, *query;
XMLElement *name, *version, *os;
XMLElement *name, *version;
iq_reply = XMLCreateTag("iq");
XMLAddAttr(iq_reply, "to", from);
@ -585,34 +411,28 @@ IQGet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
query = XMLCreateTag("query");
XMLAddAttr(query, "xmlns", "jabber:iq:version");
{
struct utsname info;
name = XMLCreateTag("name");
version = XMLCreateTag("version");
os = XMLCreateTag("os");
uname(&info);
XMLAddChild(name, XMLCreateText(NAME));
XMLAddChild(version, XMLCreateText(VERSION "[" CODE "]"));
XMLAddChild(os, XMLCreateText(info.sysname));
}
XMLAddChild(query, name);
XMLAddChild(query, version);
XMLAddChild(query, os);
XMLAddChild(iq_reply, query);
XMPPSendStanza(jabber, iq_reply, args->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, iq_reply);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(iq_reply);
}
else
{
char *buf = NULL;
Stream *s = StrStreamWriter(&buf);
Log(LOG_WARNING, "Unknown I/Q received:");
XMLEncode(s, stanza);
StreamFlush(s);
StreamClose(s);
Log(LOG_WARNING, "%s", buf);
Free(buf);
XMLEncode(StreamStdout(), stanza);
StreamPrintf(StreamStdout(),"\n");
StreamFlush(StreamStdout());
}
}
@ -620,9 +440,6 @@ void
IQError(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
/* TODO */
(void) args;
(void) stanza;
(void) thr;
}
void
IQSet(ParseeData *args, XMLElement *stanza, XMPPThread *thr)

View file

@ -1,82 +1,14 @@
#include "XMPPThread/internal.h"
#include <Cytoplasm/Memory.h>
#include <Cytoplasm/Util.h>
#include <Cytoplasm/Str.h>
#include <Cytoplasm/Log.h>
#include <Unistring.h>
#include <Matrix.h>
#include <AS.h>
#include <string.h>
static void
LazyRegister(ParseeData *data, char *mxid, char *name)
{
DbRef *ref;
char *hash = ParseeHMACS(data->id, mxid);
char *dbname;
if (!(ref = DbLock(data->db, 2, "users", hash)))
{
ASRegisterUser(data->config, mxid);
ref = DbCreate(data->db, 2, "users", hash);
if (ref)
{
HashMapSet(DbJson(ref), "mxid", JsonValueString(mxid));
HashMapSet(DbJson(ref),
"ts", JsonValueInteger(UtilTsMillis())
);
}
}
dbname = GrabString(DbJson(ref), 1, "name");
if (name && !StrEquals(dbname, name))
{
ASSetName(data->config, mxid, name);
if (ref)
{
JsonValueFree(HashMapSet(
DbJson(ref), "name", JsonValueString(name)
));
}
}
DbUnlock(data->db, ref);
Free(hash);
}
static char *
LazySend(ParseeData *args, char *mxid, char *mroom_id, char *type, HashMap *ev, uint64_t ts)
{
HashMap *duplicate;
char *event;
if (!args || !mxid || !mroom_id || !ev)
{
return NULL;
}
if (!type)
{
type = "m.room.message";
}
duplicate = JsonDuplicate(ev);
event = ASSend(
args->config, mroom_id, mxid,
type, ev, ts
);
if (event)
{
JsonFree(duplicate);
return event;
}
ASInvite(args->config, mroom_id, mxid);
Free(ASJoin(args->config, mroom_id, mxid));
return ASSend(
args->config, mroom_id, mxid,
type, duplicate, ts
);
}
static void
ProcessChatstates(ParseeData *args, XMLElement *stanza)
{
@ -112,24 +44,7 @@ ProcessChatstates(ParseeData *args, XMLElement *stanza)
from_matrix = NULL;
}
if (XMLookForTKV(stanza, "paused", "xmlns", CHAT_STATES) ||
XMLookForTKV(stanza, "inactive", "xmlns", CHAT_STATES))
{
char *latest = NULL;
from_matrix = ParseeGetBridgedUser(args, stanza);
mroom_id = ParseeGetBridgedRoom(args, stanza);
latest = ParseeLookupHead(mroom_id);
ASType(args->config, from_matrix, mroom_id, false);
ASPresence(args->config, from_matrix, mroom_id, latest);
Free(from_matrix);
Free(latest);
Free(mroom_id);
mroom_id = NULL;
from_matrix = NULL;
}
if (XMLookForTKV(stanza, "received", "xmlns", "urn:xmpp:receipts") ||
XMLookForTKV(stanza, "received", "xmlns", "urn:xmpp:receipts") ||
XMLookForTKV(stanza, "displayed", "xmlns", "urn:xmpp:chat-markers:0"))
{
/* TODO: Use stanza ID if possible */
@ -150,16 +65,6 @@ ProcessChatstates(ParseeData *args, XMLElement *stanza)
}
#undef CHAT_STATES
}
static float
TimeElapsed(uint64_t *rectime, uint64_t v)
{
uint64_t time = UtilTsMillis();
float t = ((time-v)/1000.f);
*rectime = time;
return t;
}
bool
MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
@ -178,29 +83,18 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
char *type = HashMapGet(stanza->attrs, "type");
bool chat = StrEquals(type, "chat");
size_t i;
uint64_t time, rectime;
#define Elapsed(v) (TimeElapsed(&rectime, v))
to = NULL;
from = NULL;
decode_from = NULL;
from_matrix = NULL;
Log(LOG_DEBUG, "<message> usage=%d", MemoryAllocated());
time = UtilTsMillis();
rectime = time;
from = HashMapGet(stanza->attrs, "from");
if (ParseeManageBan(args, from, NULL))
{
XMLFreeElement(stanza);
Log(LOG_DEBUG,
"<message/> time=%f "
"usage=%d (%s:%d)",
Elapsed(time),
MemoryAllocated(), __FILE__, __LINE__
);
Log(LOG_DEBUG, "<message/> usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__);
return false;
}
@ -213,25 +107,18 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
char *occ_id = occupant ? HashMapGet(occupant->attrs, "id") : NULL;
if (occ_id)
{
if (args->verbosity >= PARSEE_VERBOSE_COMICAL)
{
Log(LOG_DEBUG,
"'%s' has support for XEP-421, fetching OID=%s",
from, occ_id
);
}
Log(LOG_DEBUG,
"'%s' has support for XEP-421, fetching OID=%s",
from, occ_id
);
ParseePushOIDTable(from, occ_id);
}
}
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
{
Log(LOG_DEBUG, "XEP-421: %fs", Elapsed(rectime));
}
if (StrEquals(type, "error"))
{
char *type = NULL, *text = NULL, *user = NULL, *parsee = NULL;
char *message = NULL, *room = NULL;
char *type, *text, *user, *parsee;
char *message, *room;
from = HashMapGet(stanza->attrs, "from");
to = HashMapGet(stanza->attrs, "to");
@ -252,10 +139,10 @@ MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
message = StrConcat(3, type, ": ", text);
room = ParseeGetBridgedRoom(args, stanza);
Free(LazySend(
args, parsee, room, NULL,
MatrixCreateNotice(message),
time
Free(ASSend(
args->config, room, parsee,
"m.room.message",
MatrixCreateNotice(message)
));
end_error:
@ -264,20 +151,11 @@ end_error:
Free(parsee);
Free(room);
Free(user);
Log(LOG_DEBUG,
"<message/> time=%f "
"usage=%d (%s:%d)",
Elapsed(time),
MemoryAllocated(), __FILE__, __LINE__
);
Log(LOG_DEBUG, "<message/> usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__);
return false;
}
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
{
Log(LOG_DEBUG, "Error management: %fs", Elapsed(rectime));
}
if (moderated && !(!chat && strncmp(HashMapGet(stanza->attrs, "to"), "parsee@", 7)))
if (moderated)
{
/* TODO: Parsee MUST check if it is a valid MUC */
char *resource = ParseeGetResource(from);
@ -303,39 +181,29 @@ end_error:
body = XMLookForUnique(stanza, "body");
PEPManagerHandle(thr->info->pep_manager, stanza);
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
{
Log(LOG_DEBUG, "PEP management: %fs", Elapsed(rectime));
}
ProcessChatstates(args, stanza);
to = ParseeDecodeMXID(HashMapGet(stanza->attrs, "to"));
decode_from = ParseeLookupJID(from);
from_matrix = ParseeEncodeJID(args->config, decode_from, true);
room = ParseeFindDMRoom(args, to, from);
data = body ? ArrayGet(body->children, 0) : NULL;
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
{
Log(LOG_DEBUG, "Fetching user info: %fs", Elapsed(rectime));
}
/* TODO: CLEAN THAT UP INTO A CREATEDM FUNCTION */
mroom_id = ParseeGetBridgedRoom(args, stanza);
Log(LOG_DEBUG, "Bridging event to '%s'...", mroom_id);
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
{
Log(LOG_DEBUG, "Fetching bridge: %fs", Elapsed(rectime));
}
char *trim = ParseeTrimJID(from);
if (!mroom_id && !room && !XMPPIsParseeStanza(stanza) &&
to && *to == '@' && !XMPPQueryMUC(jabber, trim, NULL))
to && *to == '@')
{
DbRef *room_ref;
HashMap *room_json;
char *from = HashMapGet(stanza->attrs, "from");
LazyRegister(args, from_matrix, NULL);
ASRegisterUser(args->config, from_matrix);
room = ASCreateDM(args->config, from_matrix, to);
mroom_id = StrDuplicate(room);
Log(LOG_INFO, "Creating a DM to '%s'(%s)...", to, mroom_id);
if (room)
{
room_ref = DbCreate(args->db, 3, "rooms", room, "data");
@ -346,15 +214,12 @@ end_error:
ParseePushDMRoom(args, to, from, room);
}
}
Free(trim);
/* TODO: THIS IS A HACK. THIS CODE IGNORES ALL MUC MESSAGES EVER
* SENT TO NON-PARSEE PUPPETS, AS A "FIX" TO THE MULTITHREADED
* ISSUE.
*
* I HATE THIS. I NEED TO FIND A BETTER WAY.
*
* Actually, is it even _that_ bad? */
* I HATE THIS. I NEED TO FIND A BETTER WAY. */
if (!chat && strncmp(HashMapGet(stanza->attrs, "to"), "parsee@", 7))
{
goto end;
@ -373,18 +238,20 @@ end_error:
ps = CreatePubsubRequest(
parsee, decode_from, "urn:xmpp:avatar:metadata"
);
XMPPSendStanza(jabber, ps, args->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, ps);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(ps);
ps = CreatePubsubRequest(
parsee, trim, "urn:xmpp:avatar:metadata"
);
XMPPSendStanza(jabber, ps, args->config->max_stanza_size);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, ps);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMLFreeElement(ps);
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
{
Log(LOG_DEBUG, "Subscription to XEP-0084: %fs", Elapsed(rectime));
}
Free(parsee);
Free(trim);
@ -393,26 +260,15 @@ end_error:
if (ParseeVerifyAllStanza(args, stanza) && !replaced)
{
XMLElement *oob, *oob_data;
char *chat_id = ParseeGetFromMUCID(args, from);
bool media_enabled = chat_id ?
ParseeIsMediaEnabled(args, chat_id) :
true ;
bool is_parsee;
/* Note that chat_id still has meaningful info as a boolean
* despite being freed */
Free(chat_id);
pthread_mutex_unlock(&thr->info->chk_lock);
parsee = ParseeJID(args);
is_parsee = StrEquals(to, parsee);
Free(parsee);
parsee = NULL;
LazyRegister(args, encoded, !chat ? res : NULL);
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
ASRegisterUser(args->config, encoded);
if (!chat)
{
Log(LOG_DEBUG, "Matrix registration: %fs", Elapsed(rectime));
ASSetName(args->config, encoded, res);
}
ASInvite(args->config, mroom_id, encoded);
Free(ASJoin(args->config, mroom_id, encoded));
reactions = XMLookForTKV(stanza,
"reactions", "xmlns", "urn:xmpp:reactions:0"
@ -420,12 +276,7 @@ end_error:
/* Check if it is a media link */
oob = XMLookForTKV(stanza, "x", "xmlns", "jabber:x:oob");
if (chat_id && StrEquals(type, "chat") && !is_parsee)
{
/* Very clearly a MUC DM. */
event_id = NULL;
}
else if (oob && data && media_enabled)
if (oob && data)
{
char *mxc, *mime = NULL;
HashMap *content = NULL;
@ -435,27 +286,24 @@ end_error:
if (oob_data)
{
FileInfo *info = FileInfoFromXMPP(stanza);
mxc = ASReupload(args->config, oob_data->data, &mime);
if (mxc)
{
content = MatrixCreateMedia(
mxc, data->data, mime, info
);
content = MatrixCreateMedia(mxc, data->data, mime);
/* Yeah, no, I'm not modifying the media creation code. */
HashMapSet(content,
"at.kappach.at.parsee.external",
JsonValueString(oob_data->data)
);
ShoveStanza(content, stanza);
event_id = LazySend(
args, encoded, mroom_id, NULL,
content, time
event_id = ASSend(
args->config, mroom_id, encoded,
"m.room.message", content
);
Free(mxc);
}
FileInfoFree(info);
Free(mime);
}
}
@ -492,13 +340,13 @@ end_error:
reaction = ArrayGet(react_child, i);
react_data = ArrayGet(reaction->children, 0);
Free(LazySend(
args, encoded, mroom_id, "m.reaction",
Free(ASSend(
args->config, mroom_id, encoded,
"m.reaction",
ShoveStanza(
MatrixCreateReact(event_id, react_data->data),
stanza
),
time
)
));
}
Free(event_id);
@ -521,15 +369,6 @@ end_error:
* too. Go figure. */
size_t off =
reply_to ? ParseeFindDatastart(data->data) : 0;
size_t stanzaoff = XMPPGetReplyOffset(stanza);
if (reply_to && stanzaoff != -1)
{
off = UnistrGetUTFOffset(data->data, stanzaoff);
}
if (data->data && off >= strlen(data->data))
{
off = 0;
}
HashMap *ev = MatrixCreateMessage(data->data + off);
if (reply_to)
{
@ -538,9 +377,9 @@ end_error:
Free(reply_id);
}
ShoveStanza(ev, stanza);
event_id = LazySend(
args, encoded, mroom_id, NULL,
ev, time
event_id = ASSend(
args->config, mroom_id, encoded,
"m.room.message", ev
);
}
pthread_mutex_lock(&thr->info->chk_lock);
@ -561,9 +400,9 @@ end_error:
);
Log(LOG_DEBUG, "Replacing events in %s(%s)", mroom_id, event_id);
Free(LazySend(
args, encoded, mroom_id, NULL,
ev, time
Free(ASSend(
args->config, mroom_id, encoded,
"m.room.message", ev
));
ParseePushAllStanza(args, stanza, event_id);
pthread_mutex_unlock(&thr->info->chk_lock);
@ -589,12 +428,6 @@ end_error:
}
}
ProcessChatstates(args, stanza);
if (args->verbosity >= PARSEE_VERBOSE_TIMINGS)
{
Log(LOG_DEBUG, "Chatstate management: %fs", Elapsed(rectime));
}
end:
Free(mroom_id);
mroom_id = NULL;
@ -602,12 +435,7 @@ end:
Free(decode_from);
Free(room);
Free(to);
Log(LOG_DEBUG,
"<message/> time=%f "
"usage=%d (%s:%d)",
(UtilTsMillis()-time)/1000.f,
MemoryAllocated(), __FILE__, __LINE__
);
Log(LOG_DEBUG, "<message/> usage=%d (%s:%d)", MemoryAllocated(), __FILE__, __LINE__);
return true;
}

View file

@ -56,62 +56,8 @@ GuessStatus(XMLElement *stanza)
}
return USER_STATUS_ONLINE;
}
static Array *
GetStatuses(XMLElement *info)
{
XMLElement *child;
Array *ret;
size_t i;
if (!info)
{
return NULL;
}
ret = ArrayCreate();
for (i = 0; i < ArraySize(info->children); i++)
{
child = ArrayGet(info->children, i);
if (StrEquals(child->name, "status"))
{
ArrayAdd(ret, HashMapGet(child->attrs, "code"));
}
}
return ret;
}
static void
FreeStatuses(Array *statuses)
{
if (!statuses)
{
return;
}
ArrayFree(statuses);
}
static bool
HasStatus(Array *statuses, const char *code)
{
size_t i;
if (!statuses || !code)
{
return false;
}
for (i = 0; i < ArraySize(statuses); i++)
{
if (StrEquals(ArrayGet(statuses, i), code))
{
return true;
}
}
return false;
}
void
PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
PresenceStanza(ParseeData *args, XMLElement *stanza)
{
#define MUC_USER_NS "http://jabber.org/protocol/muc#user"
XMLElement *user_info;
@ -119,20 +65,6 @@ PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
XMLElement *status = XMLookForUnique(stanza, "status");
char *oid = HashMapGet(stanza->attrs, "from");
char *dst = HashMapGet(stanza->attrs, "to");
char *type = HashMapGet(stanza->attrs, "type");
if (StrEquals(type, "subscribe"))
{
Log(LOG_WARNING, "!PRESENCE SUBSCRIPTION REQUEST! (%s:%s)", oid, dst);
AddPresenceSubscriber(args, oid, dst); /* TODO: Send presence updates
* whenever possible. */
}
if (PEPManagerHandle(thr->info->pep_manager, stanza))
{
return;
}
if (ServerHasXEP421(args, oid))
{
@ -143,6 +75,10 @@ PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
char *occ_id = occupant ? HashMapGet(occupant->attrs, "id") : NULL;
if (occ_id)
{
Log(LOG_DEBUG,
"'%s' has support for XEP-421, fetching OID=%s",
oid, occ_id
);
ParseePushOIDTable(oid, occ_id);
}
}
@ -150,33 +86,27 @@ PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
if ((user_info = XMLookForTKV(stanza, "x", "xmlns", MUC_USER_NS)))
{
XMLElement *item = XMLookForUnique(user_info, "item");
Array *statuses = GetStatuses(user_info);
#define IsStatus(code) (HasStatus(statuses, #code))
XMLElement *status = XMLookForUnique(user_info, "status");
#define IsStatus(code) (status && \
StrEquals(HashMapGet(status->attrs, "code"), #code))
char *jid = item ? HashMapGet(item->attrs, "jid") : NULL;
char *trim = ParseeTrimJID(jid);
char *from = NULL;
char *type = HashMapGet(stanza->attrs, "type");
char *room = ParseeGetBridgedRoom(args, stanza);
char *decode_from = NULL, *real_matrix = NULL;
char *decode_from, *real_matrix;
char *matrix_user_pl = ParseeEncodeJID(args->config, trim, false);
char *affiliation = item ? HashMapGet(item->attrs, "affiliation") : NULL;
char *role = item ? HashMapGet(item->attrs, "role") : NULL;
char *affiliation = HashMapGet(item->attrs, "affiliation");
char *role = HashMapGet(item->attrs, "role");
int power_level = 0;
char *parsee = ParseeMXID(args);
char *parsee_j = ParseeJID(args);
char *muc = ParseeTrimJID(HashMapGet(stanza->attrs, "from"));
char *parsee_muc = StrConcat(3, muc, "/", "parsee");
Free(trim);
if (!item)
{
goto end_item;
}
if (jid)
{
ParseePushJIDTable(oid, jid);
}
ParseePushAffiliationTable(oid, affiliation, role);
decode_from = ParseeLookupJID(oid);
real_matrix = ParseeDecodeMXID(decode_from);
@ -205,16 +135,22 @@ PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
if (StrEquals(role, "visitor"))
{
if (!StrEquals(HashMapGet(stanza->attrs, "to"), parsee_j) &&
char *parsee = ParseeJID(args);
if (!StrEquals(HashMapGet(stanza->attrs, "to"), parsee) &&
IsStatus(110))
{
char *muc = ParseeTrimJID(HashMapGet(stanza->attrs, "from"));
char *usr = HashMapGet(stanza->attrs, "to");
/* Ask for voice in a visitor self-presence. We do not notify
* the user, as an error MUST occur, which is handled and
* logged out. */
XMPPRequestVoice(args->jabber, usr, muc);
Free(muc);
}
Free(parsee);
}
/* Set the user's PL
@ -254,7 +190,7 @@ PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
{
ASBan(args->config, room, real_matrix);
}
else if (IsStatus(307) && !IsStatus(333))
else if (IsStatus(307))
{
ASKick(args->config, room, real_matrix);
}
@ -268,37 +204,16 @@ PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr)
ASLeave(args->config, room, real_matrix);
}
}
if (StrEquals(type, "unavailable") &&
(StrEquals(jid, parsee_muc) || StrEquals(jid, parsee_j))
&& IsStatus(301))
{
char *chat_id = ParseeGetFromRoomID(args, room);
ParseeUnlinkRoom(args, chat_id);
Free(ASSend(
args->config, room, parsee,
"m.room.message",
MatrixCreateNotice("This room has been unlinked."),
0
));
ASLeave(args->config, room, parsee);
Free(chat_id);
}
end_item:
Free(muc);
Free(from);
Free(parsee_muc);
Free(decode_from);
Free(real_matrix);
Free(matrix_user_pl);
Free(parsee_j);
Free(parsee);
Free(room);
FreeStatuses(statuses);
}
if (status && !StrEquals(type, "unavailable"))
if (status)
{
XMLElement *status_data = ArrayGet(status->children, 0);
char *decode_from = ParseeLookupJID(oid);
@ -309,8 +224,6 @@ end_item:
status_str = status_data->data;
}
/* TODO: "The server will automatically set a user's presence to
* unavailable if their last active time was over a threshold value
* (e.g. 5 minutes)."
@ -386,8 +299,11 @@ end_item:
vcard_request = CreateVCardRequest(
from, HashMapGet(stanza->attrs, "from")
);
pthread_mutex_lock(&jabber->write_lock);
XMLEncode(jabber->stream, vcard_request);
StreamFlush(jabber->stream);
pthread_mutex_unlock(&jabber->write_lock);
XMPPSendStanza(jabber, vcard_request, args->config->max_stanza_size);
XMLFreeElement(vcard_request);
Free(from);
}

View file

@ -32,17 +32,13 @@
IdentitySimple("client", "pc", NAME " v" VERSION " bridge") \
IdentitySimple("component", "generic", "Parsee's component")
typedef struct IQFeatures {
Array *identity;
Array *adverts;
} IQFeatures;
typedef struct PEPManager PEPManager;
typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item);
typedef struct XMPPIdentity {
char *category, *type, *lang, *name;
} XMPPIdentity;
typedef struct PEPManager PEPManager;
typedef void (*PEPEvent)(PEPManager *m, XMLElement *stanza, XMLElement *item);
typedef struct XMPPThread XMPPThread;
typedef struct XMPPThreadInfo {
/* A FIFO of stanzas inbound, to be read by dispatcher
@ -69,29 +65,6 @@ struct XMPPThread {
int ICollate(unsigned char *cata, unsigned char *catb);
int IdentitySort(void *idap, void *idbp);
IQFeatures * CreateIQFeatures(void);
void AddIQIdentity(IQFeatures *, char *category, char *lang, char *type, char *name);
void AdvertiseIQFeature(IQFeatures *, char *feature);
void FreeIQFeatures(IQFeatures *);
/**
* Looks up the features supported by a JID
* ----------------------
* Returns: an IQ featureset[LA:HEAP] | NULL
* Thrasher: FreeIQFeatures
* Modifies: NOTHING */
IQFeatures * LookupJIDFeatures(char *jid);
/** Generate the B64-encoded SHA-256 hash for the 'ver' field in caps.
* --------
* Returns: A base64 encoded ver hash[LA:HEAP]
* Modifies: NOTHING
* See-Also: https://xmpp.org/extensions/xep-0115.html */
char * XMPPGenerateVer(IQFeatures *features);
/* Annotates a presence with https://xmpp.org/extensions/xep-0115.html */
void XMPPAnnotatePresence(XMLElement *presence, IQFeatures *features);
char * ParseeGetBridgedRoom(ParseeData *data, XMLElement *stanza);
char * ParseeGetEventFromID(ParseeData *data, XMLElement *stanza, char *id);
char * ParseeGetReactedEvent(ParseeData *data, XMLElement *stanza);
@ -105,7 +78,7 @@ XMLElement * CreatePubsubRequest(char *from, char *to, char *node);
bool MessageStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
void IQStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
void PresenceStanza(ParseeData *args, XMLElement *stanza, XMPPThread *thr);
void PresenceStanza(ParseeData *args, XMLElement *stanza);
bool ServerHasXEP421(ParseeData *data, char *from);
@ -116,7 +89,6 @@ PEPManager * CreatePEPManager(ParseeData *data, void *cookie);
void * PEPManagerCookie(PEPManager *manager);
void PEPManagerAddEvent(PEPManager *manager, char *node, PEPEvent event);
bool PEPManagerHandle(PEPManager *manager, XMLElement *stanza);
void PEPManagerBroadcast(PEPManager *manager, char *as, XMLElement *item);
void DestroyPEPManager(PEPManager *manager);
/* PEP callbacks for the handler */
@ -124,7 +96,3 @@ void PEPAvatarEvent(PEPManager *m, XMLElement *stanza, XMLElement *item);
void PEPVCardEvent(PEPManager *m, XMLElement *stanza, XMLElement *item);
char * ScrambleOID(ParseeData *data, char *opaque_oid);
/* Presence management */
void AddPresenceSubscriber(ParseeData *data, char *from, char *to);
bool IsSubscribed(ParseeData *data, char *user, char *to);

View file

@ -29,7 +29,7 @@ extern void ASAuthenticateRequest(const ParseeConfig *, HttpClientContext *);
extern bool ASRegisterUser(const ParseeConfig *, char *);
/* Pings the homeserver to get attention. */
extern bool ASPing(const ParseeConfig *);
extern void ASPing(const ParseeConfig *);
/** Joins a room from an {id} and a given {user} we want to masquerade
* as.
@ -67,8 +67,7 @@ extern HashMap * ASFind(const ParseeConfig *, char *, char *);
/* Sends a message event with a specific type and body.
* Said body is freed during the function's execution. */
extern char *
ASSend(const ParseeConfig *, char *, char *, char *, HashMap *, uint64_t ts);
extern char * ASSend(const ParseeConfig *, char *, char *, char *, HashMap *);
extern void ASType(const ParseeConfig *, char *, char *, bool);
extern void ASPresence(const ParseeConfig *, char *, char *, char *);
@ -142,14 +141,6 @@ extern void ASSetStatus(const ParseeConfig *c, char *user, UserStatus status, ch
* Modifies: NOTHING */
extern char * ASGetName(const ParseeConfig *c, char *room, char *user);
/** Returns the user's avatar in a room, or a the global user avatar, to be
* Free'd
* -------------
* Returns: The user's name in the [HEAP] | NULL
* Thrasher: Free
* Modifies: NOTHING */
extern char * ASGetAvatar(const ParseeConfig *c, char *room, char *user);
/** Uploads data to Matrix to be used later
* ----------------
* Returns: A valid MXC URI[HEAP] | NULL
@ -179,16 +170,4 @@ extern Array * ASGetRelations(const ParseeConfig *c, size_t n, char *room, char
* Thrashes: {relations}
* See-Also: ASGetRelations */
extern void ASFreeRelations(Array *relations);
/** Returns the MIME and SHA-1 hash of a media entry, in one fell swoop.
* -----------------
* Returns: whenever the media exists
* Modifies: {mime}[HEAP], {sha}[HEAP] */
extern bool ASGetMIMESHA(const ParseeConfig *c, char *mxc, char **mime, char **sha);
/** Retrieves media off an MXC link.
* ------------
* Returns: whenever the media exists
* Modifies {mime}[HEAP], {out}[HEAP], {len} */
extern bool ASGrab(const ParseeConfig *c, char *mxc, char **mime, char **out, size_t *len);
#endif

View file

@ -26,7 +26,7 @@
Free(ASSend( \
data->config, id, profile, \
"m.room.message", \
MatrixCreateNotice(rep), 0 \
MatrixCreateNotice(rep) \
)); \
} \
while(0)
@ -45,7 +45,7 @@
Free(ASSend( \
data->config, id, profile, \
"m.room.message", \
MatrixCreateNotice(formatted), 0 \
MatrixCreateNotice(formatted) \
)); \
Free(formatted); \
} \

View file

@ -1,23 +0,0 @@
#ifndef PARSEE_FILEINFO_H
#define PARSEE_FILEINFO_H
#include "XML.h"
typedef struct FileInfo {
int width;
int height;
int size;
} FileInfo;
/** Grab file metadata through SIMS.
* ----------------
* Returns: File information(SIMS)[HEAP]
* Thrasher: FileInfoFree */
extern FileInfo * FileInfoFromXMPP(XMLElement *stanza);
/** Frees all information related with file metadata.
* --------------
* Thrashes: info */
extern void FileInfoFree(FileInfo *info);
#endif

View file

@ -1,39 +0,0 @@
#ifndef PARSEE_MUCSERV_H
#define PARSEE_MUCSERV_H
/*-* An easy interface for handling MUCs.
*/
typedef struct MUCServer MUCServer;
#include <Parsee.h>
#include <stdbool.h>
/** Creates a MUC server handle given a ParseeData handle.
* This handle shall be used for all MUC-related operations.
* ----------------------------------------------------------
* Returns: a MUC server handle[HEAP] | NULL
* Thrasher: FreeMUCServer
* See-Also: https://xmpp.org/extensions/xep-0045.html */
extern MUCServer * CreateMUCServer(ParseeData *data);
/** Manages a stanza from a MUC, and returns true if the stanza
* was actually processed by the MUC server.
* -------------------------------------------------------------
* See-Also: CreateMUCServer */
extern bool ManageMUCStanza(MUCServer *serv, XMLElement *stanza);
/** Checks if a MUC(from its localpart, so
* <code>'#foobar@bridge.blow.hole' -> 'foobar'</code>) exists.
* --------------
* Returns: whenever the MUC exists */
extern bool MUCServerExists(MUCServer *serv, char *muc);
/** Destroys all memory and references from a MUC server handle.
* --------------
* Thrashes: CreateMUCServer
* See-Also: https://xmpp.org/extensions/xep-0045.html */
extern void FreeMUCServer(MUCServer *serv);
#endif

View file

@ -2,9 +2,6 @@
#define PARSEE_MATRIX_H
#include <Cytoplasm/Json.h>
#include <Cytoplasm/Uri.h>
#include "FileInfo.h"
/* A simple representation of everything. It is not as complex as
* Telodendria's CommonID parser, simply because Parsee does not
@ -21,12 +18,6 @@ typedef struct UserID {
* Thrasher: Free */
extern UserID * MatrixParseID(char *user);
/** Attempts to parse a Matrix ID from a matrix.to URL.
* -------------------------------------------------
* Returns: a valid user ID[HEAP] | NULL
* Thrasher: Free */
extern UserID * MatrixParseIDFromMTO(Uri *uri);
/* Creates an error message JSON, with the specified code and message. */
extern HashMap * MatrixCreateError(char *err, char *msg);
@ -42,7 +33,7 @@ extern HashMap * MatrixCreateReact(char *event, char *body);
extern HashMap * MatrixCreateReplace(char *event, char *body);
/* Creates the content for a media file. */
extern HashMap * MatrixCreateMedia(char *mxc, char *body, char *mime, FileInfo *info);
extern HashMap * MatrixCreateMedia(char *mxc, char *body, char *mime);
/* Creates the content for a m.room.name state event */
extern HashMap * MatrixCreateNameState(char *name);

View file

@ -18,12 +18,9 @@ typedef struct ParseeData ParseeData;
#include <Command.h>
#include <XMPP.h>
#include <MUCServ.h>
#define PARSEE_VERBOSE_NONE 0
#define PARSEE_VERBOSE_LOG 1
#define PARSEE_VERBOSE_TIMINGS 2
#define PARSEE_VERBOSE_STANZA 3
#define PARSEE_VERBOSE_STANZA 2
#define PARSEE_VERBOSE_COMICAL 53 /* MINUTES */
typedef struct ParseeConfig {
@ -41,18 +38,12 @@ typedef struct ParseeConfig {
/* Homeserver port info */
char *homeserver_host;
int homeserver_port;
int homeserver_tls;
bool accept_pings;
bool ignore_bots;
/* ------- JABBER -------- */
char *component_addr;
char *component_host;
char *shared_comp_secret;
int component_port;
size_t max_stanza_size;
/* ------- DB -------- */
char *db_path;
@ -67,9 +58,7 @@ typedef struct ParseeData {
HttpRouter *router;
CommandRouter *handler;
HttpServer *server;
XMPPComponent *jabber;
MUCServer *muc;
Db *db;
char *id;
@ -78,10 +67,6 @@ typedef struct ParseeData {
HashMap *oid_servers;
pthread_mutex_t oidl;
/* If Parsee was intentionally halted */
bool halted;
pthread_mutex_t halt_lock;
} ParseeData;
typedef struct Argument {
@ -94,7 +79,6 @@ typedef struct Argument {
const char *description;
} Argument;
/* A few helpful macros to make JSON less of a PITA */
#define GrabString(obj, ...) JsonValueAsString(JsonGet(obj, __VA_ARGS__))
#define GrabInteger(obj, ...) JsonValueAsInteger(JsonGet(obj, __VA_ARGS__))
#define GrabBoolean(obj, ...) JsonValueAsBoolean(JsonGet(obj, __VA_ARGS__))
@ -115,12 +99,9 @@ typedef struct Argument {
/* A base64-encoded Parsee logo */
extern const char media_parsee_logo[];
/* "Unknown avatar" */
extern const char media_unknown[];
/* An ASCII-art rendition of "小橋".
* I'm sorry for its quality. If anyone wants to redraw it, feel free. */
#define PARSEE_ASCII_LINES 8
/* An ASCII-art rendition of "小橋" */
#define PARSEE_ASCII_LINES 9
extern const char *parsee_ascii[PARSEE_ASCII_LINES];
/**
@ -131,9 +112,6 @@ extern void ParseePrintASCII(void);
/**
* Checks if two versions of Parsee can be considered "compatible".
* This is mainly used for things like database operations. TODO:
* Make an auto-upgrade system to comply with the (undocumented)
* rule of "Don't Make The Sysadmin Think About Parsee Too Much".
* ---------------
* Modifies: NOTHING */
extern bool ParseeIsCompatible(char *ver1, char *ver2);
@ -284,47 +262,6 @@ extern char * ParseeGetRoomID(ParseeData *, char *chat_id);
/* Finds the MUC JID from a chat ID */
extern char * ParseeGetMUCID(ParseeData *, char *chat_id);
/** Fetches a configuration value from a key in a chat(given a Chat ID),
* as a string or NULL. Keys are to be stored like Java packages(reveres DNS).
* Parsee has the right over any key with the <code>'p.'</code> prefix.
* -----------------------------------
* Returns: a valid string[HEAP] | NULL
* Modifies: NOTHING
* See-Also: ParseeGetFromMUCID, ParseeGetFromRoomID, ParseeSetChatSetting */
extern char *
ParseeGetChatSetting(ParseeData *data, char *chat, char *key);
/** Fetches the entire configuration in a chat(given a Chat ID), as an hashmap
* of strings.
* -----------------------------------
* Returns: a valid string[HEAP] | NULL
* Modifies: NOTHING
* Thrasher: ParseeFreeChatSettings
* See-Also: ParseeGetFromMUCID, ParseeGetFromRoomID, ParseeSetChatSetting, ParseeGetChatSetting */
extern HashMap *
ParseeGetChatSettings(ParseeData *data, char *chat);
/** Destroys memory allocated from a call to {ParseeGetChatSettings}.
* -----------------------
* Returns: NOTHING
* Modifies: {settings}
* Thrashes: {settings}
* See-Also: ParseeGetChatSettings */
extern void
ParseeFreeChatSettings(HashMap *settings);
/** Replaces a configuration key-value pair within the chat's context, which
* can be read with {ParseeGetChatSetting}.
* -------------------------------------
* Returns: NOTHING
* Modifies: the chat context
* See-Also: ParseeGetFromMUCID, ParseeGetFromRoomID, ParseeGetChatSetting */
extern void
ParseeSetChatSetting(ParseeData *data, char *chat, char *key, char *val);
extern bool
ParseeIsMediaEnabled(ParseeData *data, char *chat_id);
/* Pushes a stanza ID to a chat ID */
extern void ParseePushStanza(ParseeData *, char *chat_id, char *stanza_id, char *origin_id, char *event, char *sender);
extern void ParseePushDMStanza(ParseeData *, char *room_id, char *stanza_id, char *origin_id, char *event, char *sender);
@ -341,11 +278,7 @@ extern bool ParseeGetDMOrigin(ParseeData *data, char *chat_id, char *ev, char **
/* Sends presence requests for every MUC around as a fake JID */
extern void ParseeSendPresence(ParseeData *);
/** Initialises signal-handling code within Parsee.
* --------------------
* Modifies: the signal handler
* Returns: whenever it has properly been setup */
extern bool ParseeInitialiseSignals(ParseeData *data, pthread_t xmpp);
extern bool ParseeInitialiseSignals(HttpServer *, pthread_t, XMPPComponent *);
/* Job used to cleanup Parsee data that isn't necessary anymore. */
extern void ParseeCleanup(void *data);
@ -353,13 +286,9 @@ extern void ParseeCleanup(void *data);
/* Finds the offset of the first line without a '>' at its start. */
extern int ParseeFindDatastart(char *data);
/* Finds the offset of the first line without a '>' at its start(as
* Unicode codepoints). */
extern int ParseeFindDatastartU(char *data);
/* XMPP-ifies a message event to XEP-0393 if possible. */
extern char * ParseeXMPPify(ParseeData *data, HashMap *event);
extern char * ParseeXMPPify(HashMap *event);
/* Finds an event ID from an ID in the stanza's attributes */
extern char * ParseeEventFromID(ParseeData *d, char *c_id, char *ori_id);
@ -369,18 +298,13 @@ extern char * ParseeDMEventFromID(ParseeData *d, char *r_id, char *ori_id);
extern char * ParseeDMEventFromSID(ParseeData *d, char *r_id, char *ori_id);
/* Gets a Parsee "shim" link to an MXC, usable as unauth for a limited time */
extern char * ParseeToUnauth(ParseeData *data, char *mxc, char *filename);
extern char * ParseeToUnauth(ParseeData *data, char *mxc);
extern void ParseeInitialiseOIDTable(void);
extern void ParseePushOIDTable(char *muc, char *occupant);
extern char *ParseeLookupOID(char *muc);
extern void ParseeDestroyOIDTable(void);
extern void ParseeInitialiseAffiliationTable(void);
extern void ParseePushAffiliationTable(char *user, char *affiliation, char *role);
extern bool ParseeLookupAffiliation(char *muc, char **affiliation, char **role);
extern void ParseeDestroyAffiliationTable(void);
extern void ParseeInitialiseJIDTable(void);
extern void ParseePushJIDTable(char *muc, char *bare);
extern char *ParseeLookupJID(char *muc);
@ -400,17 +324,10 @@ extern void ParseeDestroyNickTable(void);
* to ban them from rooms where Parsee has the ability to do so ("noflying").
* ---------------
* Returns: NOTHING
* See-Also: ParseeManageBan, ParseeGlobalUnban
* See-Also: ParseeManageBan
* Modifies: the database */
extern void ParseeGlobalBan(ParseeData *, char *user, char *reason);
/** Revokes a user/room/MUC's nofly status
* ---------------
* Returns: NOTHING
* See-Also: ParseeManageBan, ParseeGlobalBan
* Modifies: the database */
extern void ParseeGlobalUnban(ParseeData *, char *user);
/** Verifies if a user was banned globally. If so (and if {room} is set),
* tries to ban the user from it.
* ---------------
@ -423,8 +340,7 @@ extern bool ParseeManageBan(ParseeData *, char *user, char *room);
extern bool ParseeVerifyDMStanza(ParseeData *data, char *room_id, char *id);
/** Checks if a Matrix/XMPP user is considered as "administrator" by
* Parsee.
/** Checks if a Matrix/XMPP user is considered as "administrator" by Parsee.
* ----------------------
* Returns: (whenever the user is an admin)
* Modifies: NOTHING */
@ -485,22 +401,4 @@ extern void ParseeSetThreads(int xmpp, int http);
extern char * ParseeHMAC(char *key, uint8_t *msg, size_t msglen);
#define ParseeHMACS(key, msg) ParseeHMAC(key, (uint8_t *) msg, strlen(msg))
/** Broadcasts a stanza from a user to any that may be interested by it
* (PEP or subscription)
* -------------------------------------
* Returns: NOTHING */
extern void ParseeBroadcastStanza(ParseeData *data, char *from, XMLElement *s);
/** Destroys the mapping between a MUC and a room from a chat ID.
* ----------------
* Modifies: the DB's room mappings
* Returns: NOTHING */
extern void ParseeUnlinkRoom(ParseeData *data, char *chat_id);
/** Verifies if there is no whitelists OR that a MUC's server is whitelisted.
* ----------------------
* Returns: if a MUC is to be allowed
* Modifies: NOTHING */
extern bool ParseeIsMUCWhitelisted(ParseeData *data, char *muc);
#endif

View file

@ -27,10 +27,6 @@ typedef struct ParseeCmdArg {
"ban-list", CmdNoFlyList, \
"Globally bans a user from using Parsee" \
) \
X_COMMAND( \
"unban-list", CmdNoFlyListDel, \
"Globally unbans a user from using Parsee" \
) \
X_COMMAND( \
"nuke-muc", CmdUnlinkMUC, \
"Removes a MUC. Users should then run " \
@ -78,9 +74,7 @@ typedef struct ParseeCmdArg {
X_ROUTE("/_matrix/app/v1/transactions/(.*)", RouteTxns) \
X_ROUTE("/_matrix/app/v1/users/(.*)", RouteUserAck) \
X_ROUTE("/_matrix/app/v1/rooms/(.*)", RouteRoomAck) \
X_ROUTE("/_matrix/app/v1/ping", RoutePing) \
X_ROUTE("/media/(.*)/(.*)", RouteMedia) \
X_ROUTE("/media/(.*)/(.*)/(.*)", RouteMedia)
X_ROUTE("/_matrix/client/v1/media/download/(.*)/(.*)", RouteMedia)
#define X_ROUTE(path, name) extern void * name(Array *, void *);
ROUTES

View file

@ -16,7 +16,7 @@ extern StanzaBuilder * SetStanzaID(StanzaBuilder *builder, char *id);
extern StanzaBuilder * SetStanzaXParsee(StanzaBuilder *builder, HashMap *e);
extern StanzaBuilder * AddStanzaElem(StanzaBuilder *builder, XMLElement *item);
extern XMLElement * ExportStanza(StanzaBuilder *builder);
extern void WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber, size_t max);
extern void WriteoutStanza(StanzaBuilder *builder, XMPPComponent *jabber);
extern void DestroyStanzaBuilder(StanzaBuilder *builder);
#endif

View file

@ -4,7 +4,7 @@
#include <Cytoplasm/Stream.h>
/* Creates a string stream writer. The referenced buffer must be in the heap,
* or NULL for an empty string. */
* or NULL. */
extern Stream * StrStreamWriter(char **buffer);
/* Creates a string stream reader. The referenced buffer may be everywhere. */

Some files were not shown because too many files have changed in this diff Show more