Compare commits

..

1 Commits

Author SHA1 Message Date
5e72c9caf4 build: add husky to automatically format code
- husky installs a git hook to run make format every time you commit new code
- if `make format` fails (if a dependency is missing or prettier throws an error), the hook will still succeed as to not block people for dumb reasons
- pin prettier and husky to 3.2.5 and 9.0.11 respectively
- add prettier as a dependency for the `make format` rule
2024-06-04 15:08:10 +02:00
78 changed files with 1328 additions and 2846 deletions

View File

@@ -1,40 +0,0 @@
Categories:
- Internet
License: MIT
AutoName: tildefriends
AuthorName: Cory McWilliams
AuthorEmail: cory@tildefriends.net
RepoType: git
Repo: https://dev.tildefriends.net/cory/tildefriends.git
Builds:
- versionName: 0.0.21-wip
versionCode: 22
commit: 09b6a00731d45fa160b23a2c44be6def98d92d6a
subdir: src/android
submodules: true
sudo:
- apt-get update
- apt-get install -y ant make zip
androidupdate:
- no
scandelete:
- deps/libuv/docs/src/static/diagrams.key/Index.zip
- deps/openssl_src/cloudflare-quiche/*
- deps/openssl_src/fuzz/*
- deps/openssl_src/gost-engine/*
- deps/openssl_src/test/*
- deps/openssl_src/tlslite-ng/*
prebuild:
- sdkmanager "platforms;android-34" "build-tools;34.0.0"
build:
- mkdir bin/
- ANDROID_SDK=$$SDK$$ ANDROID_NDK=$$NDK$$ ANDROID_NDK_ROOT=$$NDK$$ make -C ../../ -j`nproc` fdroid
ndk: r26d
AutoUpdateMode: Version ^v[0-9\.]+$
UpdateCheckMode: Tags
CurrentVersion: 0.0.21-wip
CurrentVersionCode: 22

View File

@@ -1,15 +0,0 @@
name: Build Tilde Friends
run-name: ${{ gitea.actor }} running 🚀
on: [push]
jobs:
Build-All:
runs-on: ubuntu-latest
steps:
- name: check out code
uses: actions/checkout@v4
with:
submodules: true
- run: sudo apt update && sudo apt install -y doxygen mingw-w64
- run: make all -j`nproc` docs
- run: docker build .

8
.gitignore vendored
View File

@@ -1,17 +1,11 @@
build/
*.core
db.* db.*
deps/ios_toolchain/ deps/ios_toolchain/
deps/openssl/ deps/openssl/
dist/ dist/
.keys .keys
logs/
**/node_modules **/node_modules
out out
repo/
result
*.swo *.swo
*.swp *.swp
tmp/
unsigned/
.zsign_cache/ .zsign_cache/
result

4
.gitmodules vendored
View File

@@ -19,7 +19,3 @@
[submodule "deps/picohttpparser"] [submodule "deps/picohttpparser"]
path = deps/picohttpparser path = deps/picohttpparser
url = https://github.com/h2o/picohttpparser.git url = https://github.com/h2o/picohttpparser.git
[submodule "deps/openssl_src"]
path = deps/openssl_src
url = https://github.com/openssl/openssl.git
shallow = true

1
.husky/pre-commit Normal file
View File

@@ -0,0 +1 @@
make format || exit 0

View File

@@ -3,13 +3,12 @@
MAKEFLAGS += --warn-undefined-variables MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules MAKEFLAGS += --no-builtin-rules
VERSION_CODE := 25 VERSION_CODE := 20
VERSION_NUMBER := 0.0.21.1 VERSION_NUMBER := 0.0.20-wip
VERSION_NAME := What are we even doing here? VERSION_NAME := One word all lowercase four words all uppercase.
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460000.zip SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460000.zip
LIBUV_URL := https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz LIBUV_URL := https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
PROJECT = tildefriends PROJECT = tildefriends
BUILD_DIR ?= out BUILD_DIR ?= out
@@ -17,13 +16,9 @@ UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m) UNAME_M := $(shell uname -m)
ANDROID_SDK ?= ~/Android/Sdk ANDROID_SDK ?= ~/Android/Sdk
BUNDLETOOL = out/bundletool.jar
HAVE_WIN := 0 HAVE_WIN := 0
export SOURCE_DATE_EPOCH=1
export TZ=UTC
ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_S),Darwin)
BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease
else ifeq ($(UNAME_S),Linux) else ifeq ($(UNAME_S),Linux)
@@ -60,17 +55,13 @@ CFLAGS += \
-ffunction-sections \ -ffunction-sections \
-fdata-sections \ -fdata-sections \
-fno-exceptions \ -fno-exceptions \
-g \ -g
-flto
LDFLAGS += \
-flto=auto \
-Wno-attributes
ANDROID_MIN_SDK_VERSION := 24 ANDROID_MIN_SDK_VERSION := 24
ANDROID_TARGET_SDK_VERSION := 34 ANDROID_TARGET_SDK_VERSION := 34
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0 ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-$(ANDROID_TARGET_SDK_VERSION) ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-$(ANDROID_TARGET_SDK_VERSION)
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.3.11579264 ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.2.11394342
ANDROID_ARMV7A_TARGETS := \ ANDROID_ARMV7A_TARGETS := \
out/androiddebug-armv7a/tildefriends \ out/androiddebug-armv7a/tildefriends \
@@ -99,7 +90,7 @@ BUILD_TYPES += \
androidrelease-x86 \ androidrelease-x86 \
androiddebug-x86_64 \ androiddebug-x86_64 \
androidrelease-x86_64 androidrelease-x86_64
all: out/TildeFriends-arm-debug.apk out/TildeFriends-arm-release.apk out/TildeFriends-x86-debug.apk out/TildeFriends-x86-release.apk out/TildeFriends-release.fdroid.apk all: out/TildeFriends-arm-debug.apk out/TildeFriends-arm-release.apk out/TildeFriends-x86-debug.apk out/TildeFriends-x86-release.apk
endif endif
WINDOWS_TARGETS := \ WINDOWS_TARGETS := \
@@ -159,10 +150,9 @@ ANDROID_RELEASE_TARGETS := $(filter-out $(DEBUG_TARGETS),$(ANDROID_TARGETS))
NONANDROID_RELEASE_TARGETS := $(filter-out $(ANDROID_ARM64_TARGETS),$(RELEASE_TARGETS)) NONANDROID_RELEASE_TARGETS := $(filter-out $(ANDROID_ARM64_TARGETS),$(RELEASE_TARGETS))
NONANDROID_TARGETS := $(filter-out $(ANDROID_TARGETS),$(ALL_TARGETS)) NONANDROID_TARGETS := $(filter-out $(ANDROID_TARGETS),$(ALL_TARGETS))
NONMACOS_TARGETS := $(filter-out $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS),$(ALL_TARGETS)) NONMACOS_TARGETS := $(filter-out $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS),$(ALL_TARGETS))
DEADSTRIP_TARGETS := $(filter-out $(ANDROID_TARGETS),$(NONMACOS_TARGETS))
$(NONANDROID_TARGETS): CFLAGS += -fno-omit-frame-pointer $(NONANDROID_TARGETS): CFLAGS += -fno-omit-frame-pointer
$(filter-out $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += -rdynamic $(filter-out $(ANDROID_TARGETS) $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += -rdynamic
$(ANDROID_TARGETS): CFLAGS += \ $(ANDROID_TARGETS): CFLAGS += \
--sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot \ --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
-fPIC \ -fPIC \
@@ -215,7 +205,7 @@ $(ANDROID_X86_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86/usr/local/lib
$(ANDROID_X86_64_TARGETS): CFLAGS += -Ideps/openssl/android/x86_64/usr/local/include $(ANDROID_X86_64_TARGETS): CFLAGS += -Ideps/openssl/android/x86_64/usr/local/include
$(ANDROID_X86_64_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86_64/usr/local/lib $(ANDROID_X86_64_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86_64/usr/local/lib
$(NONMACOS_TARGETS): CFLAGS += -Wno-cast-function-type $(NONMACOS_TARGETS): CFLAGS += -Wno-cast-function-type
$(DEADSTRIP_TARGETS): LDFLAGS += -Wl,--gc-sections $(NONMACOS_TARGETS): LDFLAGS += -Wl,--gc-sections
$(IOS_TARGETS): CFLAGS += -mios-version-min=9.0 -Ideps/openssl/ios/ios64-xcrun/usr/local/include $(IOS_TARGETS): CFLAGS += -mios-version-min=9.0 -Ideps/openssl/ios/ios64-xcrun/usr/local/include
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib $(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib
$(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include $(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include
@@ -626,7 +616,7 @@ $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
unix: debug release unix: debug release
win: windebug winrelease win: windebug winrelease
all: $(BUILD_TYPES) all: $(BUILD_TYPES) default.nix
.PHONY: all win unix .PHONY: all win unix
ALL_APP_OBJS := \ ALL_APP_OBJS := \
@@ -683,6 +673,10 @@ src/android/AndroidManifest.xml : $(firstword $(MAKEFILE_LIST))
-e 's/android:targetSdkVersion="[[:digit:]]*"/android:targetSdkVersion="$(ANDROID_TARGET_SDK_VERSION)"/' \ -e 's/android:targetSdkVersion="[[:digit:]]*"/android:targetSdkVersion="$(ANDROID_TARGET_SDK_VERSION)"/' \
$@ $@
default.nix : $(firstword $(MAKEFILE_LIST))
@echo "[version] $@"
@sed -i -e 's/version = ".*";/version = "$(VERSION_NUMBER)";/' $@
# Android support. # Android support.
out/res/layout_activity_main.xml.flat: src/android/res/layout/activity_main.xml out/res/layout_activity_main.xml.flat: src/android/res/layout/activity_main.xml
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
@@ -695,25 +689,8 @@ out/res/drawable_icon.xml.flat: src/android/res/drawable/icon.xml
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/drawable/icon.xml @$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/drawable/icon.xml
out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
@echo [aapt2 link] res.apk @mkdir -p $(dir $@)
@mkdir -p out/apk/ @$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat --manifest src/android/AndroidManifest.xml -o out/apk/res.apk --java out/gen/
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
--manifest src/android/AndroidManifest.xml \
-o out/apk/res.apk \
--java out/gen/
out/apk/res.fdroid.apk out/gen_fdroid/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
@echo [aapt2 link] res.fdroid.apk
@mkdir -p out/apk/
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
--rename-manifest-package com.unprompted.tildefriends.fdroid \
--manifest src/android/AndroidManifest.xml \
-o out/apk/res.fdroid.apk \
--java out/gen_fdroid/
JAVA_FILES := out/gen/com/unprompted/tildefriends/R.java $(wildcard src/android/com/unprompted/tildefriends/*.java) JAVA_FILES := out/gen/com/unprompted/tildefriends/R.java $(wildcard src/android/com/unprompted/tildefriends/*.java)
CLASS_FILES := $(foreach src,$(JAVA_FILES),out/classes/com/unprompted/tildefriends/$(notdir $(src:.java=.class))) CLASS_FILES := $(foreach src,$(JAVA_FILES),out/classes/com/unprompted/tildefriends/$(notdir $(src:.java=.class)))
@@ -725,7 +702,7 @@ $(CLASS_FILES) &: $(JAVA_FILES)
out/apk/classes.dex: $(CLASS_FILES) out/apk/classes.dex: $(CLASS_FILES)
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
@echo "[d8] $@" @echo "[d8] $@"
@$(ANDROID_BUILD_TOOLS)/d8 --lib $(ANDROID_PLATFORM)/android.jar --output $(dir $@) out/classes/com/unprompted/tildefriends/*.class @$(ANDROID_BUILD_TOOLS)/d8 --$(BUILD_TYPE) --lib $(ANDROID_PLATFORM)/android.jar --output $(dir $@) out/classes/com/unprompted/tildefriends/*.class
PACKAGE_DIRS := \ PACKAGE_DIRS := \
apps/ \ apps/ \
@@ -734,79 +711,25 @@ PACKAGE_DIRS := \
deps/prettier/ \ deps/prettier/ \
deps/lit/ deps/lit/
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f))) RAW_FILES := $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f))
out/apk/TildeFriends-arm-debug.unsigned.apk: BUILD_TYPE := debug out/apk/TildeFriends-arm-debug.unsigned.apk: BUILD_TYPE := debug
out/apk/TildeFriends-arm-release.unsigned.apk: BUILD_TYPE := release out/apk/TildeFriends-arm-release.unsigned.apk: BUILD_TYPE := release
out/apk/TildeFriends-x86-debug.unsigned.apk: BUILD_TYPE := debug out/apk/TildeFriends-x86-debug.unsigned.apk: BUILD_TYPE := debug
out/apk/TildeFriends-x86-release.unsigned.apk: BUILD_TYPE := release out/apk/TildeFriends-x86-release.unsigned.apk: BUILD_TYPE := release
out/apk/TildeFriends-release.fdroid.unsigned.apk: BUILD_TYPE := release
out/apk/TildeFriends-arm-debug.unsigned.apk: out/apk/classes.dex out/androiddebug/tildefriends out/androiddebug-armv7a/tildefriends $(RAW_FILES) out/apk/res.apk out/apk/TildeFriends-arm-debug.unsigned.apk: out/apk/classes.dex out/androiddebug/tildefriends out/androiddebug-armv7a/tildefriends $(RAW_FILES) out/apk/res.apk
out/apk/TildeFriends-arm-release.unsigned.apk: out/apk/classes.dex out/androidrelease/tildefriends out/androidrelease-armv7a/tildefriends $(RAW_FILES) out/apk/res.apk out/apk/TildeFriends-arm-release.unsigned.apk: out/apk/classes.dex out/androidrelease/tildefriends out/androidrelease-armv7a/tildefriends $(RAW_FILES) out/apk/res.apk
out/apk/TildeFriends-x86-debug.unsigned.apk: out/apk/classes.dex out/androiddebug-x86_64/tildefriends out/androiddebug-x86/tildefriends $(RAW_FILES) out/apk/res.apk out/apk/TildeFriends-x86-debug.unsigned.apk: out/apk/classes.dex out/androiddebug-x86_64/tildefriends out/androiddebug-x86/tildefriends $(RAW_FILES) out/apk/res.apk
out/apk/TildeFriends-x86-release.unsigned.apk: out/apk/classes.dex out/androidrelease-x86_64/tildefriends out/androidrelease-x86/tildefriends $(RAW_FILES) out/apk/res.apk out/apk/TildeFriends-x86-release.unsigned.apk: out/apk/classes.dex out/androidrelease-x86_64/tildefriends out/androidrelease-x86/tildefriends $(RAW_FILES) out/apk/res.apk
out/apk/TildeFriends-release.fdroid.unsigned.apk: out/apk/classes.dex out/androidrelease/tildefriends out/androidrelease-armv7a/tildefriends out/androidrelease-x86_64/tildefriends out/androidrelease-x86/tildefriends $(RAW_FILES) out/apk/res.fdroid.apk
$(BUNDLETOOL):
@echo [curl] $(BUNDLETOOL_URL) TO $@
@curl -q -L --create-dirs -o $@ $(BUNDLETOOL_URL)
out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGETS)) $(RAW_FILES) out/apk/res.apk src/android/AndroidManifest.xml $(BUNDLETOOL)
@rm -rf out/aab/staging/
@mkdir -p out/aab/staging
@$(ANDROID_BUILD_TOOLS)/aapt2 link --proto-format -o out/aab/temporary.apk \
-I $(ANDROID_PLATFORM)/android.jar \
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
--manifest src/android/AndroidManifest.xml \
-R out/res/layout_activity_main.xml.flat \
-R out/res/drawable_icon.xml.flat \
--auto-add-overlay
@unzip out/aab/temporary.apk -d out/aab/staging/
@mkdir -p out/aab/staging/root/deps
@mkdir -p out/aab/staging/classes
@mkdir -p out/aab/staging/dex
@mkdir -p out/aab/staging/manifest
@mv out/aab/staging/AndroidManifest.xml out/aab/staging/manifest/AndroidManifest.xml
@cp out/apk/classes.dex out/aab/staging/dex/
@rm -fv out/base.zip
@mkdir -p out/aab/staging/lib/arm64-v8a out/aab/staging/lib/armeabi-v7a out/aab/staging/lib/x86_64 out/aab/staging/lib/x86
@cp out/androidrelease/tildefriends out/aab/staging/lib/arm64-v8a/libtildefriends.so
@cp out/androidrelease-armv7a/tildefriends out/aab/staging/lib/armeabi-v7a/libtildefriends.so
@cp out/androidrelease-x86_64/tildefriends out/aab/staging/lib/x86_64/libtildefriends.so
@cp out/androidrelease-x86/tildefriends out/aab/staging/lib/x86/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/arm64-v8a/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/armeabi-v7a/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/x86_64/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/x86/libtildefriends.so
@cp -r apps/ out/aab/staging/root/
@rm -rf out/aab/staging/root/apps/welcome*
@cp -r core/ out/aab/staging/root/
@cp -r deps/prettier/ out/aab/staging/root/deps/
@cp -r deps/lit/ out/aab/staging/root/deps/
@cp -r deps/codemirror/ out/aab/staging/root/deps/
@cd out/aab/staging/; zip -r ../base.zip *; cd ../../../
@java -jar $(BUNDLETOOL) build-bundle --overwrite --config=src/android/BundleConfig.json --modules=out/aab/base.zip --output=$@
@jarsigner -keystore .keys/android.jks $@ androidKey -storepass android
aab: out/TildeFriends.aab
.PHONY: aab
out/TildeFriends.apks: out/TildeFriends.aab $(BUNDLETOOL)
@java -jar $(BUNDLETOOL) build-apks --bundle out/TildeFriends.aab --overwrite --output $@ --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android
aabgo: out/TildeFriends.apks $(BUNDLETOOL)
@java -jar $(BUNDLETOOL) install-apks --apks out/TildeFriends.apks
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
out/apk/TildeFriends-arm-%.unsigned.apk: out/apk/TildeFriends-arm-%.unsigned.apk:
@mkdir -p $(dir $@) out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/ out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/ @mkdir -p $(dir $@) out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/ out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/
@echo "[aapt] $@" @echo "[aapt] $@"
@cp out/android$(BUILD_TYPE)/tildefriends out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/libtildefriends.so @cp out/android$(BUILD_TYPE)/tildefriends out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/tildefriends.so
@cp out/android$(BUILD_TYPE)-armv7a/tildefriends out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/libtildefriends.so @cp out/android$(BUILD_TYPE)-armv7a/tildefriends out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/tildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/libtildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/arm64-v8a/tildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/libtildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-arm-$(BUILD_TYPE)/lib/armeabi-v7a/tildefriends.so
@cp out/apk/res.apk $@.zip @cp out/apk/res.apk $@.zip
@cp out/apk/classes.dex out/apk-arm-$(BUILD_TYPE)/ @cp out/apk/classes.dex out/apk-arm-$(BUILD_TYPE)/
@cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../ @cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
@@ -816,38 +739,16 @@ out/apk/TildeFriends-arm-%.unsigned.apk:
out/apk/TildeFriends-x86-%.unsigned.apk: out/apk/TildeFriends-x86-%.unsigned.apk:
@mkdir -p $(dir $@) out/apk-x86-$(BUILD_TYPE)/lib/x86_64/ out/apk-x86-$(BUILD_TYPE)/lib/x86/ @mkdir -p $(dir $@) out/apk-x86-$(BUILD_TYPE)/lib/x86_64/ out/apk-x86-$(BUILD_TYPE)/lib/x86/
@echo "[aapt] $@" @echo "[aapt] $@"
@cp out/android$(BUILD_TYPE)-x86_64/tildefriends out/apk-x86-$(BUILD_TYPE)/lib/x86_64/libtildefriends.so @cp out/android$(BUILD_TYPE)-x86_64/tildefriends out/apk-x86-$(BUILD_TYPE)/lib/x86_64/tildefriends.so
@cp out/android$(BUILD_TYPE)-x86/tildefriends out/apk-x86-$(BUILD_TYPE)/lib/x86/libtildefriends.so @cp out/android$(BUILD_TYPE)-x86/tildefriends out/apk-x86-$(BUILD_TYPE)/lib/x86/tildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86_64/libtildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86_64/tildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86/libtildefriends.so @$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-x86-$(BUILD_TYPE)/lib/x86/tildefriends.so
@cp out/apk/res.apk $@.zip @cp out/apk/res.apk $@.zip
@cp out/apk/classes.dex out/apk-x86-$(BUILD_TYPE)/ @cp out/apk/classes.dex out/apk-x86-$(BUILD_TYPE)/
@cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../ @cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
@zip -u $@.zip -q -9 $(RAW_FILES) @zip -u $@.zip -q -9 $(RAW_FILES)
@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@ @$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@
out/apk/TildeFriends-%.fdroid.unsigned.apk:
@rm -rf out/apk-fdroid-$(BUILD_TYPE) out/apk-fdroid-$(BUILD_TYPE)-raw
@mkdir -p $(dir $@) out/apk-fdroid-$(BUILD_TYPE)/lib/x86_64/ out/apk-fdroid-$(BUILD_TYPE)/lib/x86/ out/apk-fdroid-$(BUILD_TYPE)/lib/arm64-v8a/ out/apk-fdroid-$(BUILD_TYPE)/lib/armeabi-v7a/
@echo "[aapt] $@"
@cp out/android$(BUILD_TYPE)-x86_64/tildefriends out/apk-fdroid-$(BUILD_TYPE)/lib/x86_64/libtildefriends.so
@cp out/android$(BUILD_TYPE)-x86/tildefriends out/apk-fdroid-$(BUILD_TYPE)/lib/x86/libtildefriends.so
@cp out/android$(BUILD_TYPE)/tildefriends out/apk-fdroid-$(BUILD_TYPE)/lib/arm64-v8a/libtildefriends.so
@cp out/android$(BUILD_TYPE)-armv7a/tildefriends out/apk-fdroid-$(BUILD_TYPE)/lib/armeabi-v7a/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-fdroid-$(BUILD_TYPE)/lib/x86_64/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-fdroid-$(BUILD_TYPE)/lib/x86/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-fdroid-$(BUILD_TYPE)/lib/arm64-v8a/libtildefriends.so
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/apk-fdroid-$(BUILD_TYPE)/lib/armeabi-v7a/libtildefriends.so
@cp out/apk/res.fdroid.apk $@.zip
@cp out/apk/classes.dex out/apk-fdroid-$(BUILD_TYPE)/classes.dex
@touch -d @0 out/apk-fdroid-$(BUILD_TYPE)/classes.dex out/apk-fdroid-$(BUILD_TYPE)/lib/*/libtildefriends.so
@chmod 755 out/apk-fdroid-$(BUILD_TYPE)/classes.dex out/apk-fdroid-$(BUILD_TYPE)/lib/*/libtildefriends.so
@cd out/apk-fdroid-$(BUILD_TYPE) && zip -X -u ../../$@.zip -q classes.dex lib/*/libtildefriends.so && cd ../../
@mkdir out/apk-fdroid-$(BUILD_TYPE)-raw
@for i in $(RAW_FILES); do mkdir -p $$(dirname out/apk-fdroid-$(BUILD_TYPE)-raw/$$i) && cp $$i out/apk-fdroid-$(BUILD_TYPE)-raw/$$i && touch -d @0 out/apk-fdroid-$(BUILD_TYPE)-raw/$$i && chmod 644 out/apk-fdroid-$(BUILD_TYPE)-raw/$$i; done
@cd out/apk-fdroid-$(BUILD_TYPE)-raw && zip -X -u ../../$@.zip -q $(RAW_FILES) && cd ../../
@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@
out/%.apk: out/apk/%.unsigned.apk out/%.apk: out/apk/%.unsigned.apk
@echo "[apksigner] $(notdir $@)" @echo "[apksigner] $(notdir $@)"
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $< @$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $<
@@ -860,11 +761,6 @@ out/%.zopfli.apk: out/%.apk
release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk
.PHONY: release-apk .PHONY: release-apk
apkgo: out/TildeFriends-arm-debug.apk
@adb install -r $<
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
.PHONY: apkgo
releaseapkgo: out/TildeFriends-arm-release.apk releaseapkgo: out/TildeFriends-arm-release.apk
@adb install -r $< @adb install -r $<
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity @adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
@@ -945,7 +841,7 @@ fetchdeps:
ANDROID_DEPS := deps/openssl/android/arm64-v8a/usr/local/lib/libssl.a ANDROID_DEPS := deps/openssl/android/arm64-v8a/usr/local/lib/libssl.a
$(ANDROID_DEPS): $(ANDROID_DEPS):
+@ANDROID_NDK_ROOT=$(ANDROID_NDK) tools/ssl-android +@tools/ssl-android
$(filter $(BUILD_DIR)/android%,$(APP_OBJS)): | $(ANDROID_DEPS) $(filter $(BUILD_DIR)/android%,$(APP_OBJS)): | $(ANDROID_DEPS)
ifeq ($(HAVE_WIN),1) ifeq ($(HAVE_WIN),1)
@@ -966,7 +862,7 @@ clean:
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)
.PHONY: clean .PHONY: clean
dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk dist: release-apk iosrelease-ipa $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) default.nix
@echo [archive] dist/tildefriends-$(VERSION_NUMBER).tar.xz @echo [archive] dist/tildefriends-$(VERSION_NUMBER).tar.xz
@rm -rf out/tildefriends-$(VERSION_NUMBER) @rm -rf out/tildefriends-$(VERSION_NUMBER)
@mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER) @mkdir -p dist/ out/tildefriends-$(VERSION_NUMBER)
@@ -997,8 +893,6 @@ dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefrien
@cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa @cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
@test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe" @test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe"
@test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe @test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe
@cp out/TildeFriends.aab dist/TildeFriends-$(VERSION_NUMBER).aab
@cp out/TildeFriends-release.fdroid.apk dist/TildeFriends-$(VERSION_NUMBER).fdroid.apk
.PHONY: dist .PHONY: dist
dist-test: dist dist-test: dist
@@ -1008,7 +902,7 @@ dist-test: dist
@rm -rf tildefriends-$(VERSION_NUMBER) @rm -rf tildefriends-$(VERSION_NUMBER)
.PHONY: dist-test .PHONY: dist-test
format: format: prettier
@clang-format -i $(wildcard src/*.c src/*.h src/*.m) @clang-format -i $(wildcard src/*.c src/*.h src/*.m)
.PHONY: format .PHONY: format
@@ -1019,6 +913,3 @@ prettier:
docs: docs:
@doxygen @doxygen
.PHONY: docs .PHONY: docs
fdroid: out/apk/TildeFriends-release.fdroid.unsigned.apk
.PHONE: fdroid

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🐌", "emoji": "🐌",
"previous": "&2xK//SIpjFb0+uT5I7MSAGJ3d1FKuI/rlzhcCQd3NME=.sha256" "previous": "&zRv7YNZBT/NoliiTS7Jn/Q+3przdFZljUl8yPBIpSSE=.sha256"
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -76,9 +76,15 @@ class TfComposeElement extends LitElement {
let preview = this.renderRoot.getElementById('preview'); let preview = this.renderRoot.getElementById('preview');
preview.innerHTML = this.process_text(edit.innerText); preview.innerHTML = this.process_text(edit.innerText);
let content_warning = this.renderRoot.getElementById('content_warning'); let content_warning = this.renderRoot.getElementById('content_warning');
let content_warning_preview = this.renderRoot.getElementById(
'content_warning_preview'
);
if (content_warning && content_warning_preview) {
content_warning_preview.innerText = content_warning.value;
}
let draft = this.get_draft(); let draft = this.get_draft();
draft.text = edit.innerText; draft.text = edit.innerText;
draft.content_warning = content_warning?.value; draft.content_warning = content_warning?.innerText;
setTimeout(() => this.notify(draft), 0); setTimeout(() => this.notify(draft), 0);
} }
@@ -215,8 +221,12 @@ class TfComposeElement extends LitElement {
console.log('encrypted as', message); console.log('encrypted as', message);
} }
try { try {
await tfrpc.rpc.appendMessage(this.whoami, message); await tfrpc.rpc.appendMessage(this.whoami, message).then(function () {
self.notify(undefined); edit.innerText = '';
self.input();
self.notify(undefined);
self.requestUpdate();
});
} catch (error) { } catch (error) {
alert(error.message); alert(error.message);
} }
@@ -243,9 +253,9 @@ class TfComposeElement extends LitElement {
try { try {
let rows = await tfrpc.rpc.query( let rows = await tfrpc.rpc.query(
` `
SELECT json(messages.content) AS content FROM messages_fts(?) SELECT json(messages.content) FROM messages_fts(?)
JOIN messages ON messages.rowid = messages_fts.rowid JOIN messages ON messages.rowid = messages_fts.rowid
WHERE json(messages.content) LIKE ? WHERE messages.content LIKE ?
ORDER BY timestamp DESC LIMIT 10 ORDER BY timestamp DESC LIMIT 10
`, `,
['"' + text.replace('"', '""') + '"', `%![%${text}%](%)%`] ['"' + text.replace('"', '""') + '"', `%![%${text}%](%)%`]
@@ -281,7 +291,6 @@ class TfComposeElement extends LitElement {
); );
} }
let tribute = new Tribute({ let tribute = new Tribute({
iframe: this.shadowRoot,
collection: [ collection: [
{ {
values: values, values: values,
@@ -316,7 +325,6 @@ class TfComposeElement extends LitElement {
let encrypt = this.renderRoot.getElementById('encrypt_to'); let encrypt = this.renderRoot.getElementById('encrypt_to');
if (encrypt) { if (encrypt) {
let tribute = new Tribute({ let tribute = new Tribute({
iframe: this.shadowRoot,
values: Object.entries(this.users).map((x) => ({ values: Object.entries(this.users).map((x) => ({
key: x[1].name, key: x[1].name,
value: x[0], value: x[0],
@@ -449,7 +457,7 @@ class TfComposeElement extends LitElement {
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input> <input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
<label for="cw">CW</label> <label for="cw">CW</label>
</p> </p>
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input> <input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${this.input} @change=${this.change} value=${draft.content_warning}></input>
</div> </div>
`; `;
} else { } else {
@@ -538,7 +546,7 @@ class TfComposeElement extends LitElement {
id="edit" id="edit"
@input=${this.input} @input=${this.input}
@paste=${this.paste} @paste=${this.paste}
contenteditable="plaintext-only" contenteditable
.innerText=${live(draft.text ?? '')} .innerText=${live(draft.text ?? '')}
></span> ></span>
</div> </div>

View File

@@ -482,7 +482,16 @@ class TributeRange {
} }
getDocument() { getDocument() {
return document; let iframe;
if (this.tribute.current.collection) {
iframe = this.tribute.current.collection.iframe;
}
if (!iframe) {
return document
}
return iframe.contentWindow.document
} }
positionMenuAtCaret(scrollTo) { positionMenuAtCaret(scrollTo) {
@@ -644,8 +653,8 @@ class TributeRange {
} }
getWindowSelection() { getWindowSelection() {
if (this.tribute.collection[0].iframe?.getSelection) { if (this.tribute.collection.iframe) {
return this.tribute.collection[0].iframe.getSelection() return this.tribute.collection.iframe.contentWindow.getSelection()
} }
return window.getSelection() return window.getSelection()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -83,10 +83,10 @@ App.prototype.send = function (message) {
* @param {*} response * @param {*} response
* @param {*} client * @param {*} client
*/ */
async function socket(request, response, client) { function socket(request, response, client) {
let process; let process;
let options = {}; let options = {};
let credentials = await httpd.auth_query(request.headers); let credentials = httpd.auth_query(request.headers);
response.onClose = async function () { response.onClose = async function () {
if (process && process.task) { if (process && process.task) {
@@ -211,6 +211,10 @@ async function socket(request, response, client) {
if (process && process.timeout > 0) { if (process && process.timeout > 0) {
setTimeout(ping, process.timeout); setTimeout(ping, process.timeout);
} }
} else if (message.action == 'enableStats') {
if (process) {
core.enableStats(process, message.enabled);
}
} else if (message.action == 'resetPermission') { } else if (message.action == 'resetPermission') {
if (process) { if (process) {
process.resetPermission(message.permission); process.resetPermission(message.permission);
@@ -218,7 +222,7 @@ async function socket(request, response, client) {
} else if (message.action == 'setActiveIdentity') { } else if (message.action == 'setActiveIdentity') {
process.setActiveIdentity(message.identity); process.setActiveIdentity(message.identity);
} else if (message.action == 'createIdentity') { } else if (message.action == 'createIdentity') {
await process.createIdentity(); process.createIdentity();
} else if (message.message == 'tfrpc') { } else if (message.message == 'tfrpc') {
if (message.id && g_calls[message.id]) { if (message.id && g_calls[message.id]) {
if (message.error !== undefined) { if (message.error !== undefined) {

View File

@@ -3,7 +3,7 @@
<head> <head>
<title>Tilde Friends Sign-in</title> <title>Tilde Friends Sign-in</title>
<link type="text/css" rel="stylesheet" href="/static/style.css" /> <link type="text/css" rel="stylesheet" href="/static/style.css" />
<link type="image/svg+xml" rel="icon" href="/static/tildefriends.svg" /> <link type="image/png" rel="shortcut icon" href="/static/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
</head> </head>
<body> <body>

View File

@@ -118,6 +118,28 @@ class TfNavigationElement extends LitElement {
return this.spark_lines[key]; return this.spark_lines[key];
} }
/**
* TODOC
* @returns
*/
render_login() {
if (this?.credentials?.session?.name) {
return html`<a
class="w3-bar-item w3-right"
id="login"
href="/login/logout?return=${url() + hash()}"
>logout ${this.credentials.session.name}</a
>`;
} else {
return html`<a
class="w3-bar-item w3-right"
id="login"
href="/login?return=${url() + hash()}"
>login</a
>`;
}
}
set_active_identity(id) { set_active_identity(id) {
send({action: 'setActiveIdentity', identity: id}); send({action: 'setActiveIdentity', identity: id});
this.renderRoot.getElementById('id_dropdown').classList.remove('w3-show'); this.renderRoot.getElementById('id_dropdown').classList.remove('w3-show');
@@ -137,105 +159,70 @@ class TfNavigationElement extends LitElement {
window.location.href = '/~core/ssb/#' + this.identity; window.location.href = '/~core/ssb/#' + this.identity;
} }
logout() {
window.location.href = `/login/logout?return=${encodeURIComponent(url() + hash())}`;
}
render_identity() { render_identity() {
let self = this; let self = this;
if (this.identities?.length) {
if (this?.credentials?.session?.name) { return html`
if (this.identities?.length) { <link type="text/css" rel="stylesheet" href="/static/w3.css" />
return html` <div class="w3-dropdown-click w3-right" style="max-width: 100%">
<link type="text/css" rel="stylesheet" href="/static/w3.css" /> <button
<div class="w3-dropdown-click w3-right" style="max-width: 100%"> class="w3-button w3-rest w3-cyan"
style="text-overflow: ellipsis; overflow: hidden; white-space: nowrap; max-width: 100%"
@click=${self.toggle_id_dropdown}
>
${self.names[this.identity]}${self.names[this.identity] ===
this.identity
? ''
: html` - ${this.identity}`}
</button>
<div
id="id_dropdown"
class="w3-dropdown-content w3-bar-block w3-card-4"
style="max-width: 100%"
>
<button <button
class="w3-button w3-rest w3-cyan" class="w3-bar-item w3-button w3-border"
style="text-overflow: ellipsis; overflow: hidden; white-space: nowrap; max-width: 100%" @click=${() => (window.location.href = '/~core/identity')}
id="identity"
@click=${self.toggle_id_dropdown}
> >
${self.names[this.identity]} Manage Identities...
</button> </button>
<div <button
id="id_dropdown" class="w3-bar-item w3-button w3-border"
class="w3-dropdown-content w3-bar-block w3-card-4" @click=${self.edit_profile}
style="max-width: 100%; right: 0"
> >
<button Edit Profile...
class="w3-bar-item w3-button w3-border" </button>
@click=${() => (window.location.href = '/~core/identity')} ${this.identities.map(
> (x) => html`
Manage Identities... <button
</button> class="w3-bar-item w3-button ${x === self.identity
<button ? 'w3-cyan'
class="w3-bar-item w3-button w3-border" : ''}"
@click=${self.edit_profile} @click=${() => self.set_active_identity(x)}
> style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
Edit Profile... >
</button> ${self.names[x]}${self.names[x] === x ? '' : html` - ${x}`}
${this.identities.map( </button>
(x) => html` `
<button )}
class="w3-bar-item w3-button ${x === self.identity
? 'w3-cyan'
: ''}"
@click=${() => self.set_active_identity(x)}
style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap"
>
${self.names[x]}${self.names[x] === x ? '' : html` - ${x}`}
</button>
`
)}
<button
class="w3-bar-item w3-button w3-border"
id="logout"
@click=${self.logout}
>
Logout ${this.credentials.session.name}
</button>
</div>
</div> </div>
`; </div>
} else if ( `;
this.credentials?.session?.name && } else if (
this.credentials.session.name !== 'guest' this.credentials?.session?.name &&
) { this.credentials.session.name !== 'guest'
return html` ) {
<link type="text/css" rel="stylesheet" href="/static/w3.css" /> return html`
<button <link type="text/css" rel="stylesheet" href="/static/w3.css" />
class="w3-bar-item w3-button w3-right w3-cyan" <button
id="logout" id="create_identity"
@click=${self.logout} @click=${this.create_identity}
> class="w3-button w3-mobile w3-blue w3-right"
Logout ${this.credentials.session.name} >
</button> Create an Identity
<button </button>
id="create_identity" `;
@click=${this.create_identity}
class="w3-button w3-mobile w3-red w3-right"
>
Create an Identity
</button>
`;
} else {
return html`
<button
class="w3-bar-item w3-button w3-right w3-cyan"
id="logout"
@click=${self.logout}
>
Logout ${this.credentials.session.name}
</button>
`;
}
} else {
return html`<a
class="w3-bar-item w3-cyan w3-right"
id="login"
href="/login?return=${url() + hash()}"
>login</a
>`;
} }
} }
@@ -375,7 +362,7 @@ class TfNavigationElement extends LitElement {
${Object.keys(this.spark_lines) ${Object.keys(this.spark_lines)
.sort() .sort()
.map((x) => this.spark_lines[x])} .map((x) => this.spark_lines[x])}
${this.render_identity()} ${this.render_login()} ${this.render_identity()}
</div> </div>
${this.status?.is_error ${this.status?.is_error
? html` ? html`
@@ -1274,6 +1261,7 @@ function _receive_websocket_message(message) {
document.getElementById('viewPane').style.display = message.edit_only document.getElementById('viewPane').style.display = message.edit_only
? 'none' ? 'none'
: 'flex'; : 'flex';
send({action: 'enableStats', enabled: true});
} else if (message && message.action == 'ping') { } else if (message && message.action == 'ping') {
send({action: 'pong'}); send({action: 'pong'});
} else if (message && message.action == 'stats') { } else if (message && message.action == 'stats') {

View File

@@ -8,6 +8,10 @@ let gStatsTimer = false;
const k_content_security_policy = const k_content_security_policy =
'sandbox allow-downloads allow-top-navigation-by-user-activation'; 'sandbox allow-downloads allow-top-navigation-by-user-activation';
let k_static_files = [
{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
];
const k_global_settings = { const k_global_settings = {
index: { index: {
type: 'string', type: 'string',
@@ -202,7 +206,7 @@ function getUser(caller, process) {
* @param {*} process * @param {*} process
* @returns * @returns
*/ */
async function getApps(user, process) { function getApps(user, process) {
if ( if (
process.credentials && process.credentials &&
process.credentials.session && process.credentials.session &&
@@ -217,12 +221,10 @@ async function getApps(user, process) {
if (user) { if (user) {
let db = new Database(user); let db = new Database(user);
try { try {
let names = JSON.parse(await db.get('apps')); let names = JSON.parse(db.get('apps'));
let result = {}; return Object.fromEntries(
for (let name of names) { names.map((name) => [name, db.get('path:' + name)])
result[name] = await db.get('path:' + name); );
}
return result;
} catch {} } catch {}
} }
return {}; return {};
@@ -286,6 +288,7 @@ async function getProcessBlob(blobId, key, options) {
process.lastActive = Date.now(); process.lastActive = Date.now();
process.lastPing = null; process.lastPing = null;
process.timeout = options.timeout; process.timeout = options.timeout;
process.stats = false;
process.ready = new Promise(function (resolve, reject) { process.ready = new Promise(function (resolve, reject) {
resolveReady = resolve; resolveReady = resolve;
rejectReady = reject; rejectReady = reject;
@@ -317,9 +320,9 @@ async function getProcessBlob(blobId, key, options) {
} }
}, },
user: getUser(process, process), user: getUser(process, process),
users: async function () { users: function () {
try { try {
return JSON.parse(await new Database('auth').get('users')); return JSON.parse(new Database('auth').get('users'));
} catch { } catch {
return []; return [];
} }
@@ -467,7 +470,7 @@ async function getProcessBlob(blobId, key, options) {
process.credentials.session.name && process.credentials.session.name &&
process.credentials.session.name !== 'guest' process.credentials.session.name !== 'guest'
) { ) {
let id = await ssb.createIdentity(process.credentials.session.name); let id = ssb.createIdentity(process.credentials.session.name);
await process.sendIdentities(); await process.sendIdentities();
broadcastAppEventToUser( broadcastAppEventToUser(
process?.credentials?.session?.name, process?.credentials?.session?.name,
@@ -506,20 +509,25 @@ async function getProcessBlob(blobId, key, options) {
setGlobalSettings(gGlobalSettings); setGlobalSettings(gGlobalSettings);
print('Done.'); print('Done.');
}; };
imports.core.deleteUser = async function (user) { imports.core.deleteUser = function (user) {
await imports.core.permissionTest('delete_user'); return Promise.resolve(
let db = new Database('auth'); imports.core.permissionTest('delete_user')
db.remove('user:' + user); ).then(function () {
let users = new Set(); let db = new Database('auth');
let users_original = await db.get('users');
try { db.remove('user:' + user);
users = new Set(JSON.parse(users_original));
} catch {} let users = new Set();
users.delete(user); let users_original = db.get('users');
users = JSON.stringify([...users].sort()); try {
if (users !== users_original) { users = new Set(JSON.parse(users_original));
await db.set('users', users); } catch {}
} users.delete(user);
users = JSON.stringify([...users].sort());
if (users !== users_original) {
db.set('users', users);
}
});
}; };
} }
if (options.api) { if (options.api) {
@@ -744,7 +752,7 @@ async function getProcessBlob(blobId, key, options) {
}; };
process.task.setImports(imports); process.task.setImports(imports);
process.task.activate(); process.task.activate();
let source = await ssb.blobGet(blobId); let source = await getBlobOrContent(blobId);
let appSourceName = blobId; let appSourceName = blobId;
let appSource = utf8Decode(source); let appSource = utf8Decode(source);
try { try {
@@ -752,7 +760,7 @@ async function getProcessBlob(blobId, key, options) {
if (appObject.type == 'tildefriends-app') { if (appObject.type == 'tildefriends-app') {
appSourceName = options?.script ?? 'app.js'; appSourceName = options?.script ?? 'app.js';
let id = appObject.files[appSourceName]; let id = appObject.files[appSourceName];
let blob = await ssb.blobGet(id); let blob = await getBlobOrContent(id);
appSource = utf8Decode(blob); appSource = utf8Decode(blob);
await process.task.loadFile([ await process.task.loadFile([
'/tfrpc.js', '/tfrpc.js',
@@ -762,7 +770,7 @@ async function getProcessBlob(blobId, key, options) {
Object.keys(appObject.files).map(async function (f) { Object.keys(appObject.files).map(async function (f) {
await process.task.loadFile([ await process.task.loadFile([
f, f,
await ssb.blobGet(appObject.files[f]), await getBlobOrContent(appObject.files[f]),
]); ]);
}) })
); );
@@ -777,10 +785,6 @@ async function getProcessBlob(blobId, key, options) {
} }
await process.task.execute({name: appSourceName, source: appSource}); await process.task.execute({name: appSourceName, source: appSource});
resolveReady(process); resolveReady(process);
if (!gStatsTimer) {
gStatsTimer = true;
sendStats();
}
} catch (error) { } catch (error) {
if (process.app) { if (process.app) {
if (process?.task?.onError) { if (process?.task?.onError) {
@@ -802,15 +806,33 @@ async function getProcessBlob(blobId, key, options) {
* @param {*} settings * @param {*} settings
* @returns * @returns
*/ */
async function setGlobalSettings(settings) { function setGlobalSettings(settings) {
gGlobalSettings = settings; gGlobalSettings = settings;
try { try {
return await new Database('core').set('settings', JSON.stringify(settings)); return new Database('core').set('settings', JSON.stringify(settings));
} catch (error) { } catch (error) {
print('Error storing settings:', error); print('Error storing settings:', error);
} }
} }
/**
* TODOC
* @param {*} data
* @param {*} bytes
* @returns
*/
function startsWithBytes(data, bytes) {
if (data.byteLength >= bytes.length) {
let dataBytes = new Uint8Array(data.slice(0, bytes.length));
for (let i = 0; i < bytes.length; i++) {
if (dataBytes[i] !== bytes[i] && bytes[i] !== null) {
return;
}
}
return true;
}
}
/** /**
* TODOC * TODOC
* @param {*} response * @param {*} response
@@ -850,6 +872,21 @@ function sendData(response, data, type, headers, status_code) {
} }
} }
/**
* TODOC
* @param {*} id
* @returns
*/
async function getBlobOrContent(id) {
if (!id) {
return;
} else if (id.startsWith('&')) {
return ssb.blobGet(id);
} else if (id.startsWith('%')) {
return ssb.messageContentGet(id);
}
}
let g_handler_index = 0; let g_handler_index = 0;
/** /**
@@ -892,7 +929,7 @@ async function useAppHandler(
}, },
respond: do_resolve, respond: do_resolve,
}, },
credentials: await httpd.auth_query(headers), credentials: httpd.auth_query(headers),
packageOwner: packageOwner, packageOwner: packageOwner,
packageName: packageName, packageName: packageName,
} }
@@ -917,6 +954,34 @@ async function useAppHandler(
* @returns * @returns
*/ */
async function blobHandler(request, response, blobId, uri) { async function blobHandler(request, response, blobId, uri) {
// TODO(tasiaiso): break this down ?
for (let i in k_static_files) {
if (uri === k_static_files[i].uri && k_static_files[i].path) {
let stat = await File.stat('core/' + k_static_files[i].path);
let id = `${stat.mtime}_${stat.size}`;
if (request.headers['if-none-match'] === '"' + id + '"') {
response.writeHead(304, {'Content-Length': '0'});
response.end();
} else {
let data = await File.readFile('core/' + k_static_files[i].path);
response.writeHead(
200,
Object.assign(
{
'Content-Type': k_static_files[i].type,
'Content-Length': data.byteLength,
etag: '"' + id + '"',
},
k_static_files[i].headers || {}
)
);
response.end(data);
}
return;
}
}
if (!uri) { if (!uri) {
response.writeHead(303, { response.writeHead(303, {
Location: Location:
@@ -949,7 +1014,7 @@ async function blobHandler(request, response, blobId, uri) {
response.writeHead(304, headers); response.writeHead(304, headers);
response.end(); response.end();
} else { } else {
data = await ssb.blobGet(id); data = await getBlobOrContent(id);
if (match[3]) { if (match[3]) {
let appObject = JSON.parse(data); let appObject = JSON.parse(data);
data = appObject.files[match[3]]; data = appObject.files[match[3]];
@@ -981,7 +1046,7 @@ async function blobHandler(request, response, blobId, uri) {
response.writeHead(304, headers); response.writeHead(304, headers);
response.end(); response.end();
} else { } else {
data = await ssb.blobGet(blobId); data = await getBlobOrContent(blobId);
sendData( sendData(
response, response,
data, data,
@@ -995,7 +1060,7 @@ async function blobHandler(request, response, blobId, uri) {
if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) { if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) {
let user = match[1]; let user = match[1];
let appName = match[2]; let appName = match[2];
let credentials = await httpd.auth_query(request.headers); let credentials = httpd.auth_query(request.headers);
if ( if (
credentials && credentials &&
credentials.session && credentials.session &&
@@ -1005,7 +1070,7 @@ async function blobHandler(request, response, blobId, uri) {
let database = new Database(user); let database = new Database(user);
let app_object = JSON.parse(utf8Decode(request.body)); let app_object = JSON.parse(utf8Decode(request.body));
let previous_id = await database.get('path:' + appName); let previous_id = database.get('path:' + appName);
if (previous_id) { if (previous_id) {
try { try {
let previous_object = JSON.parse( let previous_object = JSON.parse(
@@ -1026,7 +1091,7 @@ async function blobHandler(request, response, blobId, uri) {
let newBlobId = await ssb.blobStore(JSON.stringify(app_object)); let newBlobId = await ssb.blobStore(JSON.stringify(app_object));
let apps = new Set(); let apps = new Set();
let apps_original = await database.get('apps'); let apps_original = database.get('apps');
try { try {
apps = new Set(JSON.parse(apps_original)); apps = new Set(JSON.parse(apps_original));
} catch {} } catch {}
@@ -1035,9 +1100,9 @@ async function blobHandler(request, response, blobId, uri) {
} }
apps = JSON.stringify([...apps].sort()); apps = JSON.stringify([...apps].sort());
if (apps != apps_original) { if (apps != apps_original) {
await database.set('apps', apps); database.set('apps', apps);
} }
await database.set('path:' + appName, newBlobId); database.set('path:' + appName, newBlobId);
response.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'}); response.writeHead(200, {'Content-Type': 'text/plain; charset=utf-8'});
response.end('/' + newBlobId); response.end('/' + newBlobId);
} else { } else {
@@ -1058,7 +1123,7 @@ async function blobHandler(request, response, blobId, uri) {
if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) { if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) {
let user = match[1]; let user = match[1];
let appName = match[2]; let appName = match[2];
let credentials = await httpd.auth_query(request.headers); let credentials = httpd.auth_query(request.headers);
if ( if (
credentials && credentials &&
credentials.session && credentials.session &&
@@ -1068,10 +1133,10 @@ async function blobHandler(request, response, blobId, uri) {
let database = new Database(user); let database = new Database(user);
let apps = new Set(); let apps = new Set();
try { try {
apps = new Set(JSON.parse(await database.get('apps'))); apps = new Set(JSON.parse(database.get('apps')));
} catch {} } catch {}
if (apps.delete(appName)) { if (apps.delete(appName)) {
await database.set('apps', JSON.stringify([...apps].sort())); database.set('apps', JSON.stringify([...apps].sort()));
} }
database.remove('path:' + appName); database.remove('path:' + appName);
} else { } else {
@@ -1097,9 +1162,9 @@ async function blobHandler(request, response, blobId, uri) {
app_id = await db.get('path:' + match[2]); app_id = await db.get('path:' + match[2]);
} }
let app_object = JSON.parse(utf8Decode(await ssb.blobGet(app_id))); let app_object = JSON.parse(utf8Decode(await getBlobOrContent(app_id)));
id = app_object?.files[uri.substring(1)]; id = app_object.files[uri.substring(1)];
if (!id && app_object?.files['handler.js']) { if (!id && app_object.files['handler.js']) {
let answer; let answer;
try { try {
answer = await useAppHandler( answer = await useAppHandler(
@@ -1153,7 +1218,7 @@ async function blobHandler(request, response, blobId, uri) {
'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Origin': '*',
'Content-Security-Policy': k_content_security_policy, 'Content-Security-Policy': k_content_security_policy,
}; };
data = await ssb.blobGet(id); data = await getBlobOrContent(id);
let type = let type =
httpd.mime_type_from_extension(uri) || httpd.mime_type_from_extension(uri) ||
httpd.mime_type_from_magic_bytes(data); httpd.mime_type_from_magic_bytes(data);
@@ -1183,7 +1248,7 @@ ssb.addEventListener('connections', function () {
async function loadSettings() { async function loadSettings() {
let data = {}; let data = {};
try { try {
let settings = await new Database('core').get('settings'); let settings = new Database('core').get('settings');
if (settings) { if (settings) {
data = JSON.parse(settings); data = JSON.parse(settings);
} }
@@ -1203,7 +1268,7 @@ async function loadSettings() {
*/ */
function sendStats() { function sendStats() {
let apps = Object.values(gProcesses) let apps = Object.values(gProcesses)
.filter((process) => process.app) .filter((process) => process.app && process.stats)
.map((process) => process.app); .map((process) => process.app);
if (apps.length) { if (apps.length) {
let stats = getStats(); let stats = getStats();
@@ -1216,6 +1281,19 @@ function sendStats() {
} }
} }
/**
* TODOC
* @param {*} process
* @param {*} enabled
*/
function enableStats(process, enabled) {
process.stats = enabled;
if (!gStatsTimer) {
gStatsTimer = true;
sendStats();
}
}
/** /**
* TODOC * TODOC
*/ */
@@ -1332,6 +1410,7 @@ function storePermission(user, packageOwner, packageName, permission, allow) {
export { export {
gGlobalSettings as globalSettings, gGlobalSettings as globalSettings,
setGlobalSettings, setGlobalSettings,
enableStats,
invoke, invoke,
getSessionProcessBlob, getSessionProcessBlob,
}; };

BIN
core/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 B

View File

@@ -4,7 +4,7 @@
<title>Tilde Friends</title> <title>Tilde Friends</title>
<link type="text/css" rel="stylesheet" href="/static/style.css" /> <link type="text/css" rel="stylesheet" href="/static/style.css" />
<link type="text/css" rel="stylesheet" href="/static/w3.css" /> <link type="text/css" rel="stylesheet" href="/static/w3.css" />
<link type="image/svg+xml" rel="icon" href="/static/tildefriends.svg" /> <link type="image/png" rel="shortcut icon" href="/static/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script> <script>
function set_access_key_title(event) { function set_access_key_title(event) {
@@ -25,17 +25,6 @@
max-height: 100%; max-height: 100%;
" "
> >
<noscript>
<div class="w3-container">
<div class="w3-panel w3-red w3-padding w3-card-4">
<h1>TildeFriends requires JavaScript.</h1>
<p>
It looks like JavaScript is disabled or unsupported. This isn't
going to work.
</p>
</div>
</div>
</noscript>
<tf-navigation></tf-navigation> <tf-navigation></tf-navigation>
<div id="content" class="hbox" style="flex: 1 0; overflow: auto"> <div id="content" class="hbox" style="flex: 1 0; overflow: auto">
<div <div

View File

@@ -1,88 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="65"
height="65"
viewBox="0 0 61 65"
fill="none"
version="1.1"
id="svg910"
sodipodi:docname="tildefriends.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs914" />
<sodipodi:namedview
id="namedview912"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="18.369231"
inkscape:cx="32.472781"
inkscape:cy="32.5"
inkscape:window-width="2256"
inkscape:window-height="1447"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg910" />
<path
style="fill:#0af;stroke-width:.712717;fill-opacity:1"
d="M6 0h49a8 8 45 0 1 8 8v49a8 8 135 0 1-8 8H6a8 8 45 0 1-8-8V8a8 8 135 0 1 8-8Z"
id="path886" />
<g
aria-label="~"
id="text890"
style="font-size:40px;line-height:1.25;fill:#000000">
<path
d="m 1.6762187,36.689095 v -4.003907 q 2.0703125,-2.34375 5.4296875,-2.34375 1.171875,0 2.4609375,0.351563 1.2890623,0.332031 3.6718753,1.347656 1.347656,0.566406 2.011718,0.742188 0.683594,0.175781 1.367188,0.175781 1.269531,0 2.617187,-0.761719 1.367188,-0.761719 2.421875,-1.914062 v 4.140625 q -1.25,1.171875 -2.539062,1.699218 -1.269531,0.527344 -2.871094,0.527344 -1.171875,0 -2.246094,-0.273437 -1.054687,-0.273438 -3.378906,-1.308594 -2.3046873,-1.035156 -3.847656,-1.035156 -1.25,0 -2.3632813,0.546875 -1.09375,0.527343 -2.734375,2.109375 z"
style="font-family:Arial;-inkscape-font-specification:'Arial, Normal'"
id="path1704" />
</g>
<g
transform="translate(16.213 5.975) scale(.72923)"
id="g896">
<circle
cx="36"
cy="36"
r="23"
fill="#fcea2b"
id="circle892" />
<path
fill="#3f3f3f"
d="M45.331 38.564c3.963 0 7.178-2.862 7.178-6.389 0-1.765.448-3.53-.852-4.685-1.299-1.156-4.345-1.704-6.326-1.704-2.357 0-5.143.143-6.451 1.704-.894 1.065-.727 3.253-.727 4.685 0 3.527 3.213 6.389 7.178 6.389zM25.738 38.564c3.963 0 7.179-2.862 7.179-6.389 0-1.765.447-3.53-.852-4.685-1.3-1.156-4.345-1.704-6.327-1.704-2.356 0-5.142.143-6.451 1.704-.893 1.065-.727 3.253-.727 4.685 0 3.527 3.213 6.389 7.178 6.389z"
id="path894" />
</g>
<g
stroke="#000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-miterlimit="10"
stroke-width="2"
transform="translate(16.213 5.975) scale(.72923)"
id="g908">
<circle
cx="35.887"
cy="36.056"
r="23"
id="circle898" />
<path
d="M45.702 44.862c-6.574 3.525-14.045 3.658-19.63 0M18.883 30.464s-.953 8.55 6.86 7.918c2.62-.212 7.817-.65 7.867-8.342.005-.698-.007-1.6-.81-2.63-1.065-1.367-3.572-1.971-9.945-1.422 0 0-3.446-.1-3.972 4.476z"
id="path900" />
<path
d="m18.953 29.931-.433-3.372 3.833-.527M52.741 30.464s.953 8.55-6.86 7.918c-2.62-.212-7.817-.65-7.868-8.342-.004-.698.008-1.6.811-2.63 1.065-1.367 3.572-1.971 9.945-1.422 0 0 3.446-.1 3.972 4.476z"
id="path902" />
<path
d="M31.505 26.416s4.124 2.534 8.657 0M33.536 31.318s2.202-3.751 4.536 0M52.664 29.933l.433-3.371-3.833-.528"
id="path904" />
<path
d="M33.955 30.027s1.795-3.75 3.699 0"
id="path906" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -21,26 +21,24 @@
}: }:
pkgs.stdenv.mkDerivation rec { pkgs.stdenv.mkDerivation rec {
pname = "tildefriends"; pname = "tildefriends";
version = "0.0.21"; version = "0.0.19";
src = pkgs.fetchFromGitea { src = pkgs.fetchFromGitea {
domain = "dev.tildefriends.net"; domain = "dev.tildefriends.net";
owner = "cory"; owner = "cory";
repo = "tildefriends"; repo = "tildefriends";
rev = "v${version}"; rev = "v${version}";
hash = "sha256-cBj9Hz0qT0Tqm7ivM8HPG9TNwC9iv0lTcE8XCNba8F4="; hash = "sha256-ttqL2wz06Jvn2f6kKIAGpF0nSSle+g4nSlj4jL0D+Fk=";
fetchSubmodules = true; fetchSubmodules = true;
}; };
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
glibc
gnumake gnumake
openssl openssl
which which
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [
glibc
openssl openssl
which which
]; ];

File diff suppressed because one or more lines are too long

213
deps/codemirror_src/package-lock.json generated vendored
View File

@@ -19,9 +19,9 @@
} }
}, },
"node_modules/@codemirror/autocomplete": { "node_modules/@codemirror/autocomplete": {
"version": "6.17.0", "version": "6.16.0",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.17.0.tgz", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz",
"integrity": "sha512-fdfj6e6ZxZf8yrkMHUSJJir7OJkHkZKaOZGzLWIYp2PZ3jd+d+UjG8zVPqJF6d3bKxkhvXTPan/UZ1t7Bqm0gA==", "integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==",
"dependencies": { "dependencies": {
"@codemirror/language": "^6.0.0", "@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0", "@codemirror/state": "^6.0.0",
@@ -36,13 +36,13 @@
} }
}, },
"node_modules/@codemirror/commands": { "node_modules/@codemirror/commands": {
"version": "6.6.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz", "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.5.0.tgz",
"integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==", "integrity": "sha512-rK+sj4fCAN/QfcY9BEzYMgp4wwL/q5aj/VfNSoH1RWPF9XS/dUwBkvlL3hpWgEjOqlpdN1uLC9UkjJ4tmyjJYg==",
"dependencies": { "dependencies": {
"@codemirror/language": "^6.0.0", "@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.4.0", "@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.27.0", "@codemirror/view": "^6.0.0",
"@lezer/common": "^1.1.0" "@lezer/common": "^1.1.0"
} }
}, },
@@ -98,9 +98,9 @@
} }
}, },
"node_modules/@codemirror/language": { "node_modules/@codemirror/language": {
"version": "6.10.2", "version": "6.10.1",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz", "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz",
"integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==", "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==",
"dependencies": { "dependencies": {
"@codemirror/state": "^6.0.0", "@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0", "@codemirror/view": "^6.23.0",
@@ -111,9 +111,9 @@
} }
}, },
"node_modules/@codemirror/lint": { "node_modules/@codemirror/lint": {
"version": "6.8.1", "version": "6.8.0",
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.1.tgz", "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.0.tgz",
"integrity": "sha512-IZ0Y7S4/bpaunwggW2jYqwLuHj0QtESf5xcROewY6+lDNwZ/NzvR4t+vpYgg9m7V8UXLPYqG+lu3DF470E5Oxg==", "integrity": "sha512-lsFofvaw0lnPRJlQylNsC4IRt/1lI4OD/yYslrSGVndOJfStc58v+8p9dgGiD90ktOfL7OhBWns1ZETYgz0EJA==",
"dependencies": { "dependencies": {
"@codemirror/state": "^6.0.0", "@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0", "@codemirror/view": "^6.0.0",
@@ -147,9 +147,9 @@
} }
}, },
"node_modules/@codemirror/view": { "node_modules/@codemirror/view": {
"version": "6.29.1", "version": "6.26.3",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.29.1.tgz", "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.3.tgz",
"integrity": "sha512-7r+DlO/QFwPqKp73uq5mmrS4TuLPUVotbNOKYzN3OLP5ScrOVXcm4g13/48b6ZXGhdmzMinzFYqH0vo+qihIkQ==", "integrity": "sha512-gmqxkPALZjkgSxIeeweY/wGQXBfwTUaLs8h7OKtSwfbj9Ct3L11lD+u1sS7XHppxFQoMDiMDp07P9f3I2jWOHw==",
"dependencies": { "dependencies": {
"@codemirror/state": "^6.4.0", "@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0", "style-mod": "^4.1.0",
@@ -199,9 +199,9 @@
} }
}, },
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0", "version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true "dev": true
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
@@ -238,9 +238,9 @@
} }
}, },
"node_modules/@lezer/html": { "node_modules/@lezer/html": {
"version": "1.3.10", "version": "1.3.9",
"resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz", "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.9.tgz",
"integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==", "integrity": "sha512-MXxeCMPyrcemSLGaTQEZx0dBUH0i+RPl8RN5GwMAzo53nTsd/Unc/t5ZxACeQoyPUM5/GkPLRUs2WliOImzkRA==",
"dependencies": { "dependencies": {
"@lezer/common": "^1.2.0", "@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0", "@lezer/highlight": "^1.0.0",
@@ -248,9 +248,9 @@
} }
}, },
"node_modules/@lezer/javascript": { "node_modules/@lezer/javascript": {
"version": "1.4.17", "version": "1.4.16",
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.17.tgz", "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.16.tgz",
"integrity": "sha512-bYW4ctpyGK+JMumDApeUzuIezX01H76R1foD6LcRX224FWfyYit/HYxiPGDjXXe/wQWASjCvVGoukTH68+0HIA==", "integrity": "sha512-84UXR3N7s11MPQHWgMnjb9571fr19MmXnr5zTv2XX0gHXXUvW3uPJ8GCjKrfTXmSdfktjRK0ayKklw+A13rk4g==",
"dependencies": { "dependencies": {
"@lezer/common": "^1.2.0", "@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.1.3", "@lezer/highlight": "^1.1.3",
@@ -268,9 +268,9 @@
} }
}, },
"node_modules/@lezer/lr": { "node_modules/@lezer/lr": {
"version": "1.4.2", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz",
"integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==",
"dependencies": { "dependencies": {
"@lezer/common": "^1.0.0" "@lezer/common": "^1.0.0"
} }
@@ -343,9 +343,9 @@
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz",
"integrity": "sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==", "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -355,9 +355,9 @@
] ]
}, },
"node_modules/@rollup/rollup-android-arm64": { "node_modules/@rollup/rollup-android-arm64": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz",
"integrity": "sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==", "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -367,9 +367,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-arm64": { "node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz",
"integrity": "sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==", "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -379,9 +379,9 @@
] ]
}, },
"node_modules/@rollup/rollup-darwin-x64": { "node_modules/@rollup/rollup-darwin-x64": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz",
"integrity": "sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==", "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -391,9 +391,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-gnueabihf": { "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz",
"integrity": "sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==", "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -403,9 +403,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm-musleabihf": { "node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz",
"integrity": "sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==", "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -415,9 +415,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-gnu": { "node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz",
"integrity": "sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==", "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -427,9 +427,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-arm64-musl": { "node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz",
"integrity": "sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==", "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -439,9 +439,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": { "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz",
"integrity": "sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==", "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@@ -451,9 +451,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-riscv64-gnu": { "node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz",
"integrity": "sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==", "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -463,9 +463,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-s390x-gnu": { "node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz",
"integrity": "sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==", "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@@ -475,9 +475,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-gnu": { "node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz",
"integrity": "sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==", "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -487,9 +487,9 @@
] ]
}, },
"node_modules/@rollup/rollup-linux-x64-musl": { "node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz",
"integrity": "sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==", "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -499,9 +499,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-arm64-msvc": { "node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz",
"integrity": "sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==", "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -511,9 +511,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-ia32-msvc": { "node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz",
"integrity": "sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==", "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -523,9 +523,9 @@
] ]
}, },
"node_modules/@rollup/rollup-win32-x64-msvc": { "node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz",
"integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==", "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -545,9 +545,9 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==" "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.12.1", "version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true, "dev": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
@@ -658,14 +658,11 @@
} }
}, },
"node_modules/is-core-module": { "node_modules/is-core-module": {
"version": "2.15.0", "version": "2.13.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz",
"integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==",
"dependencies": { "dependencies": {
"hasown": "^2.0.2" "hasown": "^2.0.0"
},
"engines": {
"node": ">= 0.4"
}, },
"funding": { "funding": {
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
@@ -718,9 +715,9 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.19.1", "version": "4.18.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz",
"integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==", "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==",
"dependencies": { "dependencies": {
"@types/estree": "1.0.5" "@types/estree": "1.0.5"
}, },
@@ -732,22 +729,22 @@
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.19.1", "@rollup/rollup-android-arm-eabi": "4.18.0",
"@rollup/rollup-android-arm64": "4.19.1", "@rollup/rollup-android-arm64": "4.18.0",
"@rollup/rollup-darwin-arm64": "4.19.1", "@rollup/rollup-darwin-arm64": "4.18.0",
"@rollup/rollup-darwin-x64": "4.19.1", "@rollup/rollup-darwin-x64": "4.18.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.19.1", "@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
"@rollup/rollup-linux-arm-musleabihf": "4.19.1", "@rollup/rollup-linux-arm-musleabihf": "4.18.0",
"@rollup/rollup-linux-arm64-gnu": "4.19.1", "@rollup/rollup-linux-arm64-gnu": "4.18.0",
"@rollup/rollup-linux-arm64-musl": "4.19.1", "@rollup/rollup-linux-arm64-musl": "4.18.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.19.1", "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0",
"@rollup/rollup-linux-riscv64-gnu": "4.19.1", "@rollup/rollup-linux-riscv64-gnu": "4.18.0",
"@rollup/rollup-linux-s390x-gnu": "4.19.1", "@rollup/rollup-linux-s390x-gnu": "4.18.0",
"@rollup/rollup-linux-x64-gnu": "4.19.1", "@rollup/rollup-linux-x64-gnu": "4.18.0",
"@rollup/rollup-linux-x64-musl": "4.19.1", "@rollup/rollup-linux-x64-musl": "4.18.0",
"@rollup/rollup-win32-arm64-msvc": "4.19.1", "@rollup/rollup-win32-arm64-msvc": "4.18.0",
"@rollup/rollup-win32-ia32-msvc": "4.19.1", "@rollup/rollup-win32-ia32-msvc": "4.18.0",
"@rollup/rollup-win32-x64-msvc": "4.19.1", "@rollup/rollup-win32-x64-msvc": "4.18.0",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@@ -822,9 +819,9 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.31.3", "version": "5.31.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.31.3.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz",
"integrity": "sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==", "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
deps/openssl_src vendored

Submodule deps/openssl_src deleted from db2ac4f6eb

118
flake.lock generated
View File

@@ -1,61 +1,61 @@
{ {
"nodes": { "nodes": {
"flake-utils": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1710146030, "lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1717281328, "lastModified": 1715395895,
"narHash": "sha256-evZPzpf59oNcDUXxh2GHcxHkTEG4fjae2ytWP85jXRo=", "narHash": "sha256-DreMqi6+qa21ffLQqhMQL2XRUkAGt3N7iVB5FhJKie4=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "b3b2b28c1daa04fe2ae47c21bb76fd226eac4ca1", "rev": "71bae31b7dbc335528ca7e96f479ec93462323ff",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-24.05", "ref": "nixos-23.11",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }
}, },
"root": { "root": {
"inputs": { "inputs": {
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }
}, },
"systems": { "systems": {
"locked": { "locked": {
"lastModified": 1681028828, "lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems", "owner": "nix-systems",
"repo": "default", "repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "nix-systems", "owner": "nix-systems",
"repo": "default", "repo": "default",
"type": "github" "type": "github"
} }
} }
}, },
"root": "root", "root": "root",
"version": 7 "version": 7
} }

View File

@@ -2,7 +2,7 @@
description = "Tilde Friends is a platform for making, running, and sharing web applications."; description = "Tilde Friends is a platform for making, running, and sharing web applications.";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
}; };
@@ -31,8 +31,6 @@
openssl openssl
llvmPackages_17.clang-unwrapped llvmPackages_17.clang-unwrapped
unzip unzip
doxygen
graphviz
]; ];
}; };
}); });

View File

@@ -1,7 +0,0 @@
* Reworked Android to launch its sandbox processes the prescribed way so that
a Google Play-shippable android .aab is possible.
* Reworked the build to generate what F-Droid wants, too.
* Updated libbacktrace.
* Updated CodeMirror.
* Updated Android NDK.
* Enabled LTO for smaller code sizes.

View File

@@ -1,3 +0,0 @@
Tilde Friends is both a peer-to-peer social network client, participating in
Secure Scuttlebutt, as well as a platform for writing and running web
applications.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

View File

@@ -1 +0,0 @@
A tool for making and sharing

View File

@@ -1 +0,0 @@
Tilde Friends

21
package-lock.json generated
View File

@@ -6,12 +6,29 @@
"": { "": {
"name": "tildefriends", "name": "tildefriends",
"license": "MIT", "license": "MIT",
"dependencies": { "devDependencies": {
"prettier": "^3.2.5" "husky": "9.0.11",
"prettier": "3.2.5"
}
},
"node_modules/husky": {
"version": "9.0.11",
"resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz",
"integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==",
"dev": true,
"bin": {
"husky": "bin.mjs"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "3.2.5", "version": "3.2.5",
"dev": true,
"license": "MIT", "license": "MIT",
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"

View File

@@ -1,11 +1,13 @@
{ {
"name": "tildefriends", "name": "tildefriends",
"scripts": { "scripts": {
"prettier": "prettier . --check --cache --write" "prettier": "prettier . --check --cache --write",
"prepare": "husky"
}, },
"author": "Cory McWilliams", "author": "Cory McWilliams",
"license": "MIT", "license": "MIT",
"dependencies": { "devDependencies": {
"prettier": "^3.2.5" "prettier": "3.2.5",
"husky": "9.0.11"
} }
} }

View File

@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends" package="com.unprompted.tildefriends"
android:versionCode="25" android:versionCode="20"
android:versionName="0.0.21.1"> android:versionName="0.0.20-wip">
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:label="Tilde Friends" android:label="Tilde Friends"
android:usesCleartextTraffic="true"> android:usesCleartextTraffic="true"
android:debuggable="true"
android:extractNativeLibs="true">
<meta-data android:name="android.max_aspect" android:value="2.1"/> <meta-data android:name="android.max_aspect" android:value="2.1"/>
<activity <activity
android:name=".TildeFriendsActivity" android:name=".TildeFriendsActivity"
@@ -18,10 +21,5 @@
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<service
android:name=".TildeFriendsSandboxService"
android:exported="false"
android:isolatedProcess="true"
android:process=":sandbox"/>
</application> </application>
</manifest> </manifest>

View File

@@ -1,5 +0,0 @@
{
"optimizations" : {
"uncompress_native_libraries" : {}
}
}

View File

@@ -1,6 +0,0 @@
<project>
<target name="clean"></target>
<target name="release">
<echo>Creating ../../../out/apk/TildeFriends-release.fdroid.unsigned.apk for release.</echo>
</target>
</project>

View File

@@ -3,18 +3,15 @@ package com.unprompted.tildefriends;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.app.DownloadManager; import android.app.DownloadManager;
import android.content.ComponentName;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment; import android.os.Environment;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.StrictMode; import android.os.StrictMode;
import android.os.SystemClock;
import android.os.strictmode.Violation;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent; import android.view.KeyEvent;
@@ -32,13 +29,19 @@ import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest; import android.webkit.WebResourceRequest;
import android.webkit.WebView; import android.webkit.WebView;
import android.webkit.WebViewClient; import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import java.io.BufferedInputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.lang.Process;
import java.lang.Thread;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds; import java.nio.file.StandardWatchEventKinds;
@@ -48,30 +51,17 @@ import java.nio.file.WatchService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public class TildeFriendsActivity extends Activity { public class TildeFriendsActivity extends Activity {
static TildeFriendsActivity s_activity;
TildeFriendsWebView web_view; TildeFriendsWebView web_view;
String base_url; String base_url;
String port_file_path; Process process;
Thread thread; Thread thread;
Thread server_thread;
ServiceConnection service_connection;
private ValueCallback<Uri[]> upload_message; private ValueCallback<Uri[]> upload_message;
private final static int FILECHOOSER_RESULT = 1; private final static int FILECHOOSER_RESULT = 1;
private float touch_down_y; private float touch_down_y;
static {
Log.w("tildefriends", "Calling system.loadLibrary().");
System.loadLibrary("tildefriends");
Log.w("tildefriends", "system.loadLibrary() completed.");
}
public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path);
public static native int tf_sandbox_main(int pipe_fd);
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
s_activity = this;
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedClosableObjects() .detectLeakedClosableObjects()
.penaltyLog() .penaltyLog()
@@ -86,7 +76,7 @@ public class TildeFriendsActivity extends Activity {
Log.w("tildefriends", String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString())); Log.w("tildefriends", String.format("getPackageResourcePath() is %s", getPackageResourcePath().toString()));
Log.w("tildefriends", String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir)); Log.w("tildefriends", String.format("nativeLibraryDir is %s", getApplicationInfo().nativeLibraryDir));
port_file_path = getFilesDir().toString() + "/port.txt"; String port_file_path = getFilesDir().toString() + "/port.txt";
new File(port_file_path).delete(); new File(port_file_path).delete();
base_url = "http://127.0.0.1:12345/"; base_url = "http://127.0.0.1:12345/";
@@ -144,15 +134,17 @@ public class TildeFriendsActivity extends Activity {
thread.start(); thread.start();
set_status("Starting server..."); set_status("Starting server...");
server_thread = new Thread(new Runnable() { String exe = getApplicationInfo().nativeLibraryDir + "/tildefriends.so";
@Override ProcessBuilder builder = new ProcessBuilder(exe, "run", "-z", getPackageResourcePath().toString(), "-a", "out_http_port_file=" + port_file_path, "-p", "0");
public void run() { Log.w("tildefriends", "files = " + getFilesDir().toString());
Log.w("tildefriends", "Calling tf_server_main."); Log.w("tildefriends", "exe = " + exe);
int result = tf_server_main(getFilesDir().toString(), getPackageResourcePath().toString(), port_file_path); builder.directory(getFilesDir());
Log.w("tildefriends", "tf_server_main returned " + result + "."); builder.inheritIO();
} try {
}); process = builder.start();
server_thread.start(); } catch (java.io.IOException e) {
Log.w("tildefriends", "IOException starting process: " + e.toString());
}
web_view.getSettings().setJavaScriptEnabled(true); web_view.getSettings().setJavaScriptEnabled(true);
web_view.getSettings().setDatabaseEnabled(true); web_view.getSettings().setDatabaseEnabled(true);
@@ -274,8 +266,13 @@ public class TildeFriendsActivity extends Activity {
@Override @Override
protected void onDestroy() protected void onDestroy()
{ {
if (process != null) {
Log.w("tildefriends", "Killing process.");
process.destroyForcibly();
Log.w("tildefriends", "Process killed.");
process = null;
}
super.onDestroy(); super.onDestroy();
s_activity = null;
} }
@Override @Override
@@ -377,49 +374,4 @@ public class TildeFriendsActivity extends Activity {
web_view.setVisibility(View.VISIBLE); web_view.setVisibility(View.VISIBLE);
text_view.setVisibility(View.GONE); text_view.setVisibility(View.GONE);
} }
public static void start_sandbox(int pipe_fd) {
Log.w("tildefriends", "starting service with fd: " + pipe_fd);
Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class);
s_activity.service_connection = new ServiceConnection() {
@Override
public void onBindingDied(ComponentName name) {
Log.w("tildefriends", "onBindingDied");
}
@Override
public void onNullBinding(ComponentName name) {
Log.w("tildefriends", "onNullBinding");
}
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.w("tildefriends", "onServiceConnected");
Parcel data = Parcel.obtain();
ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(pipe_fd);
data.writeParcelable(pfd, 0);
try {
binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY);
} catch (RemoteException e) {
Log.w("tildefriends", "RemoteException");
} finally {
data.recycle();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.w("tildefriends", "onServiceDisconnected");
}
};
s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE);
}
public static void stop_sandbox() {
Log.w("tildefriends", "stop_sandbox");
if (s_activity.service_connection != null) {
s_activity.unbindService(s_activity.service_connection);
s_activity.service_connection = null;
}
}
} }

View File

@@ -1,59 +0,0 @@
package com.unprompted.tildefriends;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.util.Log;
public class TildeFriendsSandboxService extends Service {
public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION;
Thread thread;
public int onStartCommand(Intent intent, int flags, int start_id) {
Log.w("tildefriends", "TildeFriendsSandboxService: onStartCommand");
return super.onStartCommand(intent, flags, start_id);
}
public void onDestroy() {
Log.w("tildefriends", "TildeFriendsSandboxService: onDestroy");
super.onDestroy();
}
private void start_thread(int pipe_fd) {
thread = new Thread(new Runnable() {
@Override
public void run() {
Log.w("tildefriends", "Calling tf_sandbox_main.");
int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd);
Log.w("tildefriends", "tf_sandbox_main returned " + result + ".");
}
});
thread.start();
}
@Override
public IBinder onBind(Intent intent) {
return new Binder() {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
if (code == START_CALL) {
ParcelFileDescriptor pfd = data.readParcelable(ParcelFileDescriptor.class.getClassLoader(), ParcelFileDescriptor.class);
if (pfd != null) {
Log.w("tildefriends", "fd is " + pfd.getFd());
start_thread(pfd.detachFd());
try {
pfd.close();
} catch (java.io.IOException e) {
}
}
return true;
}
return false;
}
};
}
}

View File

@@ -2,6 +2,7 @@ package com.unprompted.tildefriends;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
public class TildeFriendsWebView extends android.webkit.WebView { public class TildeFriendsWebView extends android.webkit.WebView {
boolean overscrolledY = false; boolean overscrolledY = false;

View File

@@ -1,55 +1,78 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="65dp" android:width="72dp"
android:height="65dp" android:height="72dp"
android:viewportWidth="61" android:viewportWidth="72"
android:viewportHeight="65"> android:viewportHeight="72">
<path <path
android:pathData="M6,0h49a8,8 45,0 1,8 8v49a8,8 135,0 1,-8 8H6a8,8 45,0 1,-8 -8V8a8,8 135,0 1,8 -8Z" android:pathData="M36,36m-23,0a23,23 0,1 1,46 0a23,23 0,1 1,-46 0"
android:strokeWidth=".712717" android:fillColor="#FCEA2B"/>
android:fillColor="#0af"
android:fillAlpha="1"/>
<path <path
android:pathData="m1.6762,36.6891v-4.0039q2.0703,-2.3438 5.4297,-2.3438 1.1719,0 2.4609,0.3516 1.2891,0.332 3.6719,1.3477 1.3477,0.5664 2.0117,0.7422 0.6836,0.1758 1.3672,0.1758 1.2695,0 2.6172,-0.7617 1.3672,-0.7617 2.4219,-1.9141v4.1406q-1.25,1.1719 -2.5391,1.6992 -1.2695,0.5273 -2.8711,0.5273 -1.1719,0 -2.2461,-0.2734 -1.0547,-0.2734 -3.3789,-1.3086 -2.3047,-1.0352 -3.8477,-1.0352 -1.25,0 -2.3633,0.5469 -1.0938,0.5273 -2.7344,2.1094z" android:pathData="M45.331,38.564c3.963,0 7.178,-2.862 7.178,-6.389c0,-1.765 0.447,-3.529 -0.852,-4.685s-4.345,-1.704 -6.326,-1.704c-2.357,0 -5.143,0.143 -6.451,1.704c-0.893,1.065 -0.727,3.253 -0.727,4.685C38.153,35.702 41.366,38.564 45.331,38.564z"
android:fillColor="#000000"/> android:fillColor="#3F3F3F"/>
<path <path
android:pathData="M42.4653,32.2273m-16.7723,0a16.7723,16.7723 0,1 1,33.5446 0a16.7723,16.7723 0,1 1,-33.5446 0" android:pathData="M25.738,38.564c3.963,0 7.178,-2.862 7.178,-6.389c0,-1.765 0.447,-3.529 -0.852,-4.685s-4.345,-1.704 -6.326,-1.704c-2.357,0 -5.143,0.143 -6.451,1.704c-0.893,1.065 -0.727,3.253 -0.727,4.685C18.56,35.702 21.773,38.564 25.738,38.564z"
android:fillColor="#fcea2b"/> android:fillColor="#3F3F3F"/>
<path <path
android:pathData="M49.2697,34.097c2.8899,0 5.2344,-2.0871 5.2344,-4.6591 0,-1.2871 0.3267,-2.5742 -0.6213,-3.4164 -0.9473,-0.843 -3.1685,-1.2426 -4.6131,-1.2426 -1.7188,0 -3.7504,0.1043 -4.7043,1.2426 -0.6519,0.7766 -0.5302,2.3722 -0.5302,3.4164 0,2.572 2.343,4.6591 5.2344,4.6591zM34.9819,34.097c2.8899,0 5.2351,-2.0871 5.2351,-4.6591 0,-1.2871 0.326,-2.5742 -0.6213,-3.4164 -0.948,-0.843 -3.1685,-1.2426 -4.6138,-1.2426 -1.7181,0 -3.7497,0.1043 -4.7043,1.2426 -0.6512,0.7766 -0.5302,2.3722 -0.5302,3.4164 0,2.572 2.343,4.6591 5.2344,4.6591z" android:pathData="M35.887,36.056m-23,0a23,23 0,1 1,46 0a23,23 0,1 1,-46 0"
android:fillColor="#3f3f3f"/>
<path
android:pathData="M42.3829,32.2681m-16.7723,0a16.7723,16.7723 0,1 1,33.5446 0a16.7723,16.7723 0,1 1,-33.5446 0"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000" android:strokeColor="#000000"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
<path <path
android:pathData="M49.5403,38.6897c-4.794,2.5705 -10.242,2.6675 -14.3148,0M29.983,28.1903s-0.695,6.2349 5.0025,5.774c1.9106,-0.1546 5.7004,-0.474 5.7369,-6.0832 0.0036,-0.509 -0.0051,-1.1668 -0.5907,-1.9179 -0.7766,-0.9969 -2.6048,-1.4373 -7.2522,-1.037 0,0 -2.5129,-0.0729 -2.8965,3.264z" android:pathData="M45.702,44.862c-6.574,3.525 -14.045,3.658 -19.629,0"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000" android:strokeColor="#000000"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
<path <path
android:pathData="m30.0341,27.8016 l-0.3158,-2.459 2.7951,-0.3843M54.6733,28.1903s0.695,6.2349 -5.0025,5.774c-1.9106,-0.1546 -5.7004,-0.474 -5.7376,-6.0832 -0.0029,-0.509 0.0058,-1.1668 0.5914,-1.9179 0.7766,-0.9969 2.6048,-1.4373 7.2522,-1.037 0,0 2.5129,-0.0729 2.8965,3.264z" android:pathData="M18.883,30.464c0,0 -0.953,8.551 6.861,7.918c2.62,-0.212 7.816,-0.651 7.867,-8.343c0.005,-0.698 -0.008,-1.599 -0.811,-2.63c-1.065,-1.367 -3.572,-1.971 -9.945,-1.422C22.855,25.988 19.409,25.889 18.883,30.464z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000" android:strokeColor="#000000"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
<path <path
android:pathData="M39.1874,25.2383s3.0073,1.8479 6.3129,0M40.6685,28.813s1.6058,-2.7353 3.3078,0M54.6172,27.803l0.3158,-2.4582 -2.7951,-0.385" android:pathData="M18.953,29.931l-0.433,-3.371l3.833,-0.528"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000" android:strokeColor="#000000"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
<path <path
android:pathData="M40.974,27.8716s1.309,-2.7346 2.6974,0" android:pathData="M52.741,30.464c0,0 0.953,8.551 -6.861,7.918c-2.62,-0.212 -7.816,-0.651 -7.867,-8.343c-0.005,-0.698 0.008,-1.599 0.811,-2.63c1.065,-1.367 3.572,-1.971 9.945,-1.422C48.769,25.988 52.215,25.889 52.741,30.464z"
android:strokeLineJoin="round" android:strokeLineJoin="round"
android:strokeWidth="2" android:strokeWidth="2"
android:fillColor="#00000000" android:fillColor="#00000000"
android:strokeColor="#000" android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M31.505,26.416c0,0 4.124,2.534 8.657,0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M33.536,31.318c0,0 2.202,-3.751 4.536,0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M52.664,29.933l0.433,-3.371l-3.833,-0.528"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/>
<path
android:pathData="M33.955,30.027c0,0 1.795,-3.751 3.699,0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="#000000"
android:strokeLineCap="round"/> android:strokeLineCap="round"/>
</vector> </vector>

View File

@@ -4,7 +4,6 @@
#include "mem.h" #include "mem.h"
#include "ssb.h" #include "ssb.h"
#include "task.h" #include "task.h"
#include "util.js.h"
#include "sqlite3.h" #include "sqlite3.h"
@@ -51,7 +50,7 @@ void tf_database_register(JSContext* context)
JS_SetPropertyStr(context, global, "Database", constructor); JS_SetPropertyStr(context, global, "Database", constructor);
JSValue databases = JS_NewObject(context); JSValue databases = JS_NewObject(context);
JS_SetPropertyStr(context, global, "databases", databases); JS_SetPropertyStr(context, global, "databases", databases);
JS_SetPropertyStr(context, databases, "list", JS_NewCFunctionData(context, _databases_list, 1, 0, 0, NULL)); JS_SetPropertyStr(context, databases, "list", JS_NewCFunctionData(context, _databases_list, 0, 0, 0, NULL));
JS_FreeValue(context, global); JS_FreeValue(context, global);
} }
@@ -92,455 +91,159 @@ static void _database_finalizer(JSRuntime* runtime, JSValue value)
--_database_count; --_database_count;
} }
typedef struct _database_get_t
{
const char* id;
const char* key;
size_t key_length;
char* out_value;
size_t out_length;
JSValue promise[2];
} database_get_t;
static void _database_get_work(tf_ssb_t* ssb, void* user_data)
{
database_get_t* work = user_data;
sqlite3_stmt* statement;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, work->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, work->key, work->key_length, NULL) == SQLITE_OK &&
sqlite3_step(statement) == SQLITE_ROW)
{
size_t length = sqlite3_column_bytes(statement, 0);
char* data = tf_malloc(length + 1);
memcpy(data, sqlite3_column_text(statement, 0), length);
data[length] = '\0';
work->out_value = data;
work->out_length = length;
}
sqlite3_finalize(statement);
}
tf_ssb_release_db_reader(ssb, db);
}
static void _database_get_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
database_get_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue result = JS_UNDEFINED;
if (work->out_value)
{
result = JS_NewStringLen(context, work->out_value, work->out_length);
}
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, result);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
tf_free(work->out_value);
tf_free(work);
}
static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
JSValue result = JS_UNDEFINED; JSValue entry = JS_UNDEFINED;
database_t* database = JS_GetOpaque(this_val, _database_class_id); database_t* database = JS_GetOpaque(this_val, _database_class_id);
if (database) if (database)
{ {
tf_ssb_t* ssb = tf_task_get_ssb(database->task); tf_ssb_t* ssb = tf_task_get_ssb(database->task);
sqlite3_stmt* statement;
size_t length; sqlite3* db = tf_ssb_acquire_db_reader(ssb);
const char* key = JS_ToCStringLen(context, &length, argv[0]); if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
database_get_t* work = tf_malloc(sizeof(database_get_t) + strlen(database->id) + 1 + length + 1);
*work = (database_get_t) {
.id = (const char*)(work + 1),
.key = (const char*)(work + 1) + strlen(database->id) + 1,
.key_length = length,
};
memcpy((char*)work->id, database->id, strlen(database->id) + 1);
memcpy((char*)work->key, key, length + 1);
JS_FreeCString(context, key);
tf_ssb_run_work(ssb, _database_get_work, _database_get_after_work, work);
result = JS_NewPromiseCapability(context, work->promise);
}
return result;
}
typedef struct _database_set_t
{
const char* id;
const char* key;
size_t key_length;
const char* value;
size_t value_length;
bool result;
JSValue promise[2];
} database_set_t;
static void _database_set_work(tf_ssb_t* ssb, void* user_data)
{
database_set_t* work = user_data;
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES (?1, ?2, ?3)", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, work->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, work->key, work->key_length, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 3, work->value, work->value_length, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_DONE)
{ {
work->result = true; size_t length;
const char* keyString = JS_ToCStringLen(context, &length, argv[0]);
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, keyString, length, NULL) == SQLITE_OK &&
sqlite3_step(statement) == SQLITE_ROW)
{
entry = JS_NewStringLen(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0));
}
JS_FreeCString(context, keyString);
sqlite3_finalize(statement);
} }
sqlite3_finalize(statement); tf_ssb_release_db_reader(ssb, db);
} }
tf_ssb_release_db_writer(ssb, db); return entry;
}
static void _database_set_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
database_set_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue result = work->result ? JS_TRUE : JS_UNDEFINED;
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, result);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
tf_free(work);
} }
static JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
JSValue result = JS_UNDEFINED;
database_t* database = JS_GetOpaque(this_val, _database_class_id); database_t* database = JS_GetOpaque(this_val, _database_class_id);
if (database) if (database)
{ {
sqlite3_stmt* statement;
tf_ssb_t* ssb = tf_task_get_ssb(database->task); tf_ssb_t* ssb = tf_task_get_ssb(database->task);
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
size_t key_length = 0; if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES (?1, ?2, ?3)", -1, &statement, NULL) == SQLITE_OK)
const char* key = JS_ToCStringLen(context, &key_length, argv[0]);
size_t value_length = 0;
const char* value = JS_ToCStringLen(context, &value_length, argv[1]);
database_set_t* work = tf_malloc(sizeof(database_set_t) + strlen(database->id) + 1 + key_length + 1 + value_length + 1);
*work = (database_set_t) {
.id = (const char*)(work + 1),
.key = (const char*)(work + 1) + strlen(database->id) + 1,
.value = (const char*)(work + 1) + strlen(database->id) + 1 + key_length + 1,
.key_length = key_length,
.value_length = value_length,
};
memcpy((char*)work->id, database->id, strlen(database->id) + 1);
memcpy((char*)work->key, key, key_length + 1);
memcpy((char*)work->value, value, value_length + 1);
result = JS_NewPromiseCapability(context, work->promise);
tf_ssb_run_work(ssb, _database_set_work, _database_set_after_work, work);
JS_FreeCString(context, key);
JS_FreeCString(context, value);
}
return result;
}
typedef struct _database_exchange_t
{
const char* id;
const char* key;
size_t key_length;
const char* expected;
size_t expected_length;
const char* value;
size_t value_length;
bool result;
JSValue promise[2];
} database_exchange_t;
static void _database_exchange_work(tf_ssb_t* ssb, void* user_data)
{
database_exchange_t* work = user_data;
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
sqlite3_stmt* statement;
if (!work->expected)
{
if (sqlite3_prepare(db, "INSERT INTO properties (id, key, value) VALUES (?1, ?2, ?3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
{ {
if (sqlite3_bind_text(statement, 1, work->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, work->key, work->key_length, NULL) == SQLITE_OK && size_t keyLength;
sqlite3_bind_text(statement, 3, work->value, work->value_length, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_DONE) const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
size_t valueLength;
const char* valueString = JS_ToCStringLen(context, &valueLength, argv[1]);
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, keyString, keyLength, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 3, valueString, valueLength, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_OK)
{ {
work->result = sqlite3_changes(db) != 0;
} }
JS_FreeCString(context, keyString);
JS_FreeCString(context, valueString);
sqlite3_finalize(statement); sqlite3_finalize(statement);
} }
tf_ssb_release_db_writer(ssb, db);
} }
else if (sqlite3_prepare(db, "UPDATE properties SET value = ?1 WHERE id = ?2 AND key = ?3 AND value = ?4", -1, &statement, NULL) == SQLITE_OK) return JS_UNDEFINED;
{
if (sqlite3_bind_text(statement, 1, work->value, work->value_length, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, work->id, -1, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 3, work->key, work->key_length, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 4, work->expected, work->expected_length, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_DONE)
{
work->result = sqlite3_changes(db) != 0;
}
sqlite3_finalize(statement);
}
tf_ssb_release_db_writer(ssb, db);
}
static void _database_exchange_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
database_exchange_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue result = work->result ? JS_TRUE : JS_UNDEFINED;
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, result);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
JS_FreeCString(context, work->key);
JS_FreeCString(context, work->expected);
JS_FreeCString(context, work->value);
tf_free((char*)work->id);
tf_free(work);
} }
static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
JSValue result = JS_UNDEFINED; JSValue exchanged = JS_UNDEFINED;
database_t* database = JS_GetOpaque(this_val, _database_class_id); database_t* database = JS_GetOpaque(this_val, _database_class_id);
if (database) if (database)
{ {
sqlite3_stmt* statement;
tf_ssb_t* ssb = tf_task_get_ssb(database->task); tf_ssb_t* ssb = tf_task_get_ssb(database->task);
database_exchange_t* work = tf_malloc(sizeof(database_exchange_t)); sqlite3* db = tf_ssb_acquire_db_writer(ssb);
*work = (database_exchange_t) { if (JS_IsNull(argv[1]) || JS_IsUndefined(argv[1]))
.id = tf_strdup(database->id),
};
work->key = JS_ToCStringLen(context, &work->key_length, argv[0]);
work->expected = (JS_IsNull(argv[1]) || JS_IsUndefined(argv[1])) ? NULL : JS_ToCStringLen(context, &work->expected_length, argv[1]);
work->value = JS_ToCStringLen(context, &work->value_length, argv[2]);
result = JS_NewPromiseCapability(context, work->promise);
tf_ssb_run_work(ssb, _database_exchange_work, _database_exchange_after_work, work);
}
return result;
}
typedef struct _database_remove_t
{
const char* id;
size_t key_length;
JSValue promise[2];
char key[];
} database_remove_t;
static void _database_remove_work(tf_ssb_t* ssb, void* user_data)
{
database_remove_t* work = user_data;
sqlite3_stmt* statement;
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
if (sqlite3_prepare(db, "DELETE FROM properties WHERE id = ?1 AND key = ?2", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, work->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, work->key, work->key_length, NULL) == SQLITE_OK &&
sqlite3_step(statement) == SQLITE_OK)
{ {
if (sqlite3_prepare(db, "INSERT INTO properties (id, key, value) VALUES (?1, ?2, ?3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
{
size_t key_length;
size_t set_length;
const char* key = JS_ToCStringLen(context, &key_length, argv[0]);
const char* set = JS_ToCStringLen(context, &set_length, argv[2]);
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, key, key_length, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 3, set, set_length, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_DONE)
{
exchanged = sqlite3_changes(db) != 0 ? JS_TRUE : JS_FALSE;
}
JS_FreeCString(context, key);
JS_FreeCString(context, set);
sqlite3_finalize(statement);
}
} }
sqlite3_finalize(statement); else if (sqlite3_prepare(db, "UPDATE properties SET value = ?1 WHERE id = ?2 AND key = ?3 AND value = ?4", -1, &statement, NULL) == SQLITE_OK)
{
size_t key_length;
size_t expected_length;
size_t set_length;
const char* key = JS_ToCStringLen(context, &key_length, argv[0]);
const char* expected = JS_ToCStringLen(context, &expected_length, argv[1]);
const char* set = JS_ToCStringLen(context, &set_length, argv[2]);
if (sqlite3_bind_text(statement, 1, set, set_length, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, database->id, -1, NULL) == SQLITE_OK &&
sqlite3_bind_text(statement, 3, key, key_length, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 4, expected, expected_length, NULL) == SQLITE_OK &&
sqlite3_step(statement) == SQLITE_DONE)
{
exchanged = sqlite3_changes(db) != 0 ? JS_TRUE : JS_FALSE;
}
JS_FreeCString(context, key);
JS_FreeCString(context, expected);
JS_FreeCString(context, set);
sqlite3_finalize(statement);
}
tf_ssb_release_db_writer(ssb, db);
} }
tf_ssb_release_db_writer(ssb, db); return exchanged;
}
static void _database_remove_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
database_remove_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue result = JS_UNDEFINED;
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, result);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
tf_free((char*)work->id);
tf_free(work);
} }
static JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
JSValue result = JS_UNDEFINED;
database_t* database = JS_GetOpaque(this_val, _database_class_id); database_t* database = JS_GetOpaque(this_val, _database_class_id);
if (database) if (database)
{ {
size_t key_length = 0; sqlite3_stmt* statement;
const char* key = JS_ToCStringLen(context, &key_length, argv[0]); tf_ssb_t* ssb = tf_task_get_ssb(database->task);
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
database_remove_t* work = tf_malloc(sizeof(database_remove_t) + key_length + 1); if (sqlite3_prepare(db, "DELETE FROM properties WHERE id = ?1 AND key = ?2", -1, &statement, NULL) == SQLITE_OK)
*work = (database_remove_t) {
.id = tf_strdup(database->id),
.key_length = key_length,
};
memcpy(work->key, key, key_length + 1);
JS_FreeCString(context, key);
result = JS_NewPromiseCapability(context, work->promise);
tf_ssb_run_work(tf_task_get_ssb(database->task), _database_remove_work, _database_remove_after_work, work);
}
return result;
}
typedef struct _database_get_all_t
{
const char* id;
const char* key;
size_t key_length;
char** out_values;
size_t* out_lengths;
int out_values_length;
JSValue promise[2];
} database_get_all_t;
static void _database_get_all_work(tf_ssb_t* ssb, void* user_data)
{
database_get_all_t* work = user_data;
sqlite3_stmt* statement;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
if (sqlite3_prepare(db, "SELECT key FROM properties WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, work->id, -1, NULL) == SQLITE_OK)
{ {
while (sqlite3_step(statement) == SQLITE_ROW) size_t keyLength;
const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, keyString, keyLength, NULL) == SQLITE_OK &&
sqlite3_step(statement) == SQLITE_OK)
{ {
work->out_values = tf_resize_vec(work->out_values, sizeof(char*) * (work->out_values_length + 1));
work->out_lengths = tf_resize_vec(work->out_lengths, sizeof(size_t) * (work->out_values_length + 1));
size_t length = sqlite3_column_bytes(statement, 0);
char* data = tf_malloc(length + 1);
memcpy(data, sqlite3_column_text(statement, 0), length);
data[length] = '\0';
work->out_values[work->out_values_length] = data;
work->out_lengths[work->out_values_length] = length;
work->out_values_length++;
} }
JS_FreeCString(context, keyString);
sqlite3_finalize(statement);
} }
sqlite3_finalize(statement); tf_ssb_release_db_writer(ssb, db);
} }
tf_ssb_release_db_reader(ssb, db); return JS_UNDEFINED;
}
static void _database_get_all_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
database_get_all_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue result = JS_NewArray(context);
;
for (int i = 0; i < work->out_values_length; i++)
{
JS_SetPropertyUint32(context, result, i, JS_NewStringLen(context, work->out_values[i], work->out_lengths[i]));
tf_free((void*)work->out_values[i]);
}
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, result);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
tf_free(work->out_values);
tf_free(work->out_lengths);
tf_free(work);
} }
static JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
JSValue result = JS_UNDEFINED; JSValue array = JS_UNDEFINED;
database_t* database = JS_GetOpaque(this_val, _database_class_id); database_t* database = JS_GetOpaque(this_val, _database_class_id);
if (database) if (database)
{ {
sqlite3_stmt* statement;
tf_ssb_t* ssb = tf_task_get_ssb(database->task); tf_ssb_t* ssb = tf_task_get_ssb(database->task);
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
size_t length; if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = ?1", -1, &statement, NULL) == SQLITE_OK)
const char* key = JS_ToCStringLen(context, &length, argv[0]);
database_get_all_t* work = tf_malloc(sizeof(database_get_all_t) + strlen(database->id) + 1 + length + 1);
*work = (database_get_all_t) {
.id = (const char*)(work + 1),
.key = (const char*)(work + 1) + strlen(database->id) + 1,
.key_length = length,
};
memcpy((char*)work->id, database->id, strlen(database->id) + 1);
memcpy((char*)work->key, key, length + 1);
JS_FreeCString(context, key);
tf_ssb_run_work(ssb, _database_get_all_work, _database_get_all_after_work, work);
result = JS_NewPromiseCapability(context, work->promise);
}
return result;
}
typedef struct _key_value_t
{
char* key;
size_t key_length;
char* value;
size_t value_length;
} key_value_t;
typedef struct _database_get_like_t
{
const char* id;
const char* pattern;
key_value_t* results;
int results_length;
JSValue promise[2];
} database_get_like_t;
static void _database_get_like_work(tf_ssb_t* ssb, void* user_data)
{
database_get_like_t* work = user_data;
sqlite3_stmt* statement;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, work->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, work->pattern, -1, NULL) == SQLITE_OK)
{ {
while (sqlite3_step(statement) == SQLITE_ROW) if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK)
{ {
work->results = tf_resize_vec(work->results, sizeof(key_value_t) * (work->results_length + 1)); array = JS_NewArray(context);
key_value_t* out = &work->results[work->results_length]; uint32_t index = 0;
*out = (key_value_t) { while (sqlite3_step(statement) == SQLITE_ROW)
.key_length = sqlite3_column_bytes(statement, 0), {
.value_length = sqlite3_column_bytes(statement, 1), JS_SetPropertyUint32(context, array, index++, JS_NewStringLen(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0)));
}; }
out->key = tf_malloc(out->key_length + 1);
memcpy(out->key, sqlite3_column_text(statement, 0), out->key_length + 1);
out->value = tf_malloc(out->value_length + 1);
memcpy(out->value, sqlite3_column_text(statement, 1), out->value_length + 1);
work->results_length++;
} }
sqlite3_finalize(statement);
} }
sqlite3_finalize(statement); tf_ssb_release_db_reader(ssb, db);
} }
tf_ssb_release_db_reader(ssb, db); return array;
}
static void _database_get_like_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
database_get_like_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue result = JS_NewObject(context);
for (int i = 0; i < work->results_length; i++)
{
const key_value_t* row = &work->results[i];
JS_SetPropertyStr(context, result, row->key, JS_NewStringLen(context, row->value, row->value_length));
tf_free(row->key);
tf_free(row->value);
}
JS_FreeCString(context, work->pattern);
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, result);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
tf_free((void*)work->id);
tf_free(work->results);
tf_free(work);
} }
static JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
@@ -549,77 +252,51 @@ static JSValue _database_get_like(JSContext* context, JSValueConst this_val, int
database_t* database = JS_GetOpaque(this_val, _database_class_id); database_t* database = JS_GetOpaque(this_val, _database_class_id);
if (database) if (database)
{ {
sqlite3_stmt* statement;
tf_ssb_t* ssb = tf_task_get_ssb(database->task); tf_ssb_t* ssb = tf_task_get_ssb(database->task);
database_get_like_t* work = tf_malloc(sizeof(database_get_like_t)); sqlite3* db = tf_ssb_acquire_db_reader(ssb);
*work = (database_get_like_t) { if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK)
.id = tf_strdup(database->id), {
.pattern = JS_ToCString(context, argv[0]), const char* pattern = JS_ToCString(context, argv[0]);
}; if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, pattern, -1, NULL) == SQLITE_OK)
result = JS_NewPromiseCapability(context, work->promise); {
tf_ssb_run_work(ssb, _database_get_like_work, _database_get_like_after_work, work); result = JS_NewObject(context);
while (sqlite3_step(statement) == SQLITE_ROW)
{
JS_SetPropertyStr(context, result, (const char*)sqlite3_column_text(statement, 0),
JS_NewStringLen(context, (const char*)sqlite3_column_text(statement, 1), sqlite3_column_bytes(statement, 1)));
}
}
JS_FreeCString(context, pattern);
sqlite3_finalize(statement);
}
tf_ssb_release_db_reader(ssb, db);
} }
return result; return result;
} }
typedef struct _databases_list_t
{
const char* pattern;
char** names;
int names_length;
JSValue promise[2];
} databases_list_t;
static void _databases_list_work(tf_ssb_t* ssb, void* user_data)
{
databases_list_t* work = user_data;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT DISTINCT id FROM properties WHERE id LIKE ?", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, work->pattern, -1, NULL) == SQLITE_OK)
{
while (sqlite3_step(statement) == SQLITE_ROW)
{
work->names = tf_resize_vec(work->names, sizeof(char*) * (work->names_length + 1));
work->names[work->names_length] = tf_strdup((const char*)sqlite3_column_text(statement, 0));
work->names_length++;
}
}
sqlite3_finalize(statement);
}
tf_ssb_release_db_reader(ssb, db);
}
static void _databases_list_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
databases_list_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue result = JS_NewArray(context);
for (int i = 0; i < work->names_length; i++)
{
JS_SetPropertyUint32(context, result, i, JS_NewString(context, work->names[i]));
tf_free(work->names[i]);
}
JS_FreeCString(context, work->pattern);
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, result);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
tf_free(work->names);
tf_free(work);
}
static JSValue _databases_list(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data) static JSValue _databases_list(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
{ {
tf_task_t* task = tf_task_get(context); tf_task_t* task = tf_task_get(context);
tf_ssb_t* ssb = tf_task_get_ssb(task); tf_ssb_t* ssb = tf_task_get_ssb(task);
databases_list_t* work = tf_malloc(sizeof(databases_list_t)); sqlite3* db = tf_ssb_acquire_db_reader(ssb);
*work = (databases_list_t) { JSValue array = JS_UNDEFINED;
.pattern = JS_ToCString(context, argv[0]), sqlite3_stmt* statement;
}; if (sqlite3_prepare(db, "SELECT DISTINCT id FROM properties WHERE id LIKE ?", -1, &statement, NULL) == SQLITE_OK)
JSValue result = JS_NewPromiseCapability(context, work->promise); {
tf_ssb_run_work(ssb, _databases_list_work, _databases_list_after_work, work); const char* pattern = JS_ToCString(context, argv[0]);
return result; if (sqlite3_bind_text(statement, 1, pattern, -1, NULL) == SQLITE_OK)
{
array = JS_NewArray(context);
uint32_t index = 0;
while (sqlite3_step(statement) == SQLITE_ROW)
{
JS_SetPropertyUint32(context, array, index++, JS_NewStringLen(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0)));
}
}
JS_FreeCString(context, pattern);
sqlite3_finalize(statement);
}
tf_ssb_release_db_reader(ssb, db);
return array;
} }

View File

@@ -1019,7 +1019,7 @@ void tf_http_request_unref(tf_http_request_t* request)
tf_free(request); tf_free(request);
} }
if (connection && --connection->ref_count == 0) if (--connection->ref_count == 0)
{ {
if (connection->http->is_shutting_down) if (connection->http->is_shutting_down)
{ {

View File

@@ -37,7 +37,7 @@
const int64_t k_refresh_interval = 1ULL * 7 * 24 * 60 * 60 * 1000; const int64_t k_refresh_interval = 1ULL * 7 * 24 * 60 * 60 * 1000;
static JSValue _authenticate_jwt(tf_ssb_t* ssb, JSContext* context, const char* jwt); static JSValue _authenticate_jwt(JSContext* context, const char* jwt);
static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
static const char* _make_session_jwt(tf_ssb_t* ssb, const char* name); static const char* _make_session_jwt(tf_ssb_t* ssb, const char* name);
static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie); static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie);
@@ -330,7 +330,7 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va
tf_ssb_t* ssb = tf_task_get_ssb(tf_task_get(context)); tf_ssb_t* ssb = tf_task_get_ssb(tf_task_get(context));
const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session"); const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session");
JSValue jwt = _authenticate_jwt(ssb, context, session); JSValue jwt = _authenticate_jwt(context, session);
tf_free((void*)session); tf_free((void*)session);
JSValue name = !JS_IsUndefined(jwt) ? JS_GetPropertyStr(context, jwt, "name") : JS_UNDEFINED; JSValue name = !JS_IsUndefined(jwt) ? JS_GetPropertyStr(context, jwt, "name") : JS_UNDEFINED;
const char* name_string = !JS_IsUndefined(name) ? JS_ToCString(context, name) : NULL; const char* name_string = !JS_IsUndefined(name) ? JS_ToCString(context, name) : NULL;
@@ -446,55 +446,6 @@ static JSValue _httpd_set_http_redirect(JSContext* context, JSValueConst this_va
return JS_UNDEFINED; return JS_UNDEFINED;
} }
typedef struct _auth_query_work_t
{
const char* settings;
JSValue entry;
JSValue result;
JSValue promise[2];
} auth_query_work_t;
static void _httpd_auth_query_work(tf_ssb_t* ssb, void* user_data)
{
auth_query_work_t* work = user_data;
work->settings = tf_ssb_db_get_property(ssb, "core", "settings");
}
static void _httpd_auth_query_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
auth_query_work_t* work = user_data;
JSContext* context = tf_ssb_get_context(ssb);
JSValue name = JS_GetPropertyStr(context, work->entry, "name");
const char* name_string = JS_ToCString(context, name);
JSValue settings_value = work->settings ? JS_ParseJSON(context, work->settings, strlen(work->settings), NULL) : JS_UNDEFINED;
JSValue out_permissions = JS_NewObject(context);
JS_SetPropertyStr(context, work->result, "permissions", out_permissions);
JSValue permissions = !JS_IsUndefined(settings_value) ? JS_GetPropertyStr(context, settings_value, "permissions") : JS_UNDEFINED;
JSValue user_permissions = !JS_IsUndefined(permissions) ? JS_GetPropertyStr(context, permissions, name_string) : JS_UNDEFINED;
int length = !JS_IsUndefined(user_permissions) ? tf_util_get_length(context, user_permissions) : 0;
for (int i = 0; i < length; i++)
{
JSValue permission = JS_GetPropertyUint32(context, user_permissions, i);
const char* permission_string = JS_ToCString(context, permission);
JS_SetPropertyStr(context, out_permissions, permission_string, JS_TRUE);
JS_FreeCString(context, permission_string);
JS_FreeValue(context, permission);
}
JSValue error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &work->result);
JS_FreeValue(context, work->result);
tf_util_report_error(context, error);
JS_FreeValue(context, error);
JS_FreeValue(context, work->promise[0]);
JS_FreeValue(context, work->promise[1]);
JS_FreeValue(context, user_permissions);
JS_FreeValue(context, permissions);
JS_FreeValue(context, settings_value);
tf_free((void*)work->settings);
JS_FreeCString(context, name_string);
JS_FreeValue(context, name);
tf_free(work);
}
static JSValue _httpd_auth_query(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _httpd_auth_query(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
tf_task_t* task = tf_task_get(context); tf_task_t* task = tf_task_get(context);
@@ -508,7 +459,7 @@ static JSValue _httpd_auth_query(JSContext* context, JSValueConst this_val, int
JSValue cookie = JS_GetPropertyStr(context, headers, "cookie"); JSValue cookie = JS_GetPropertyStr(context, headers, "cookie");
const char* cookie_string = JS_ToCString(context, cookie); const char* cookie_string = JS_ToCString(context, cookie);
const char* session = tf_http_get_cookie(cookie_string, "session"); const char* session = tf_http_get_cookie(cookie_string, "session");
JSValue entry = _authenticate_jwt(ssb, context, session); JSValue entry = _authenticate_jwt(context, session);
tf_free((void*)session); tf_free((void*)session);
JS_FreeCString(context, cookie_string); JS_FreeCString(context, cookie_string);
JS_FreeValue(context, cookie); JS_FreeValue(context, cookie);
@@ -516,16 +467,33 @@ static JSValue _httpd_auth_query(JSContext* context, JSValueConst this_val, int
JSValue result = JS_UNDEFINED; JSValue result = JS_UNDEFINED;
if (!JS_IsUndefined(entry)) if (!JS_IsUndefined(entry))
{ {
JSValue value = JS_NewObject(context); result = JS_NewObject(context);
JS_SetPropertyStr(context, value, "session", entry); JS_SetPropertyStr(context, result, "session", entry);
JSValue out_permissions = JS_NewObject(context);
JS_SetPropertyStr(context, result, "permissions", out_permissions);
auth_query_work_t* work = tf_malloc(sizeof(auth_query_work_t)); JSValue name = JS_GetPropertyStr(context, entry, "name");
*work = (auth_query_work_t) { const char* name_string = JS_ToCString(context, name);
.entry = entry,
.result = value, const char* settings = tf_ssb_db_get_property(ssb, "core", "settings");
}; JSValue settings_value = settings ? JS_ParseJSON(context, settings, strlen(settings), NULL) : JS_UNDEFINED;
result = JS_NewPromiseCapability(context, work->promise); JSValue permissions = !JS_IsUndefined(settings_value) ? JS_GetPropertyStr(context, settings_value, "permissions") : JS_UNDEFINED;
tf_ssb_run_work(ssb, _httpd_auth_query_work, _httpd_auth_query_after_work, work); JSValue user_permissions = !JS_IsUndefined(permissions) ? JS_GetPropertyStr(context, permissions, name_string) : JS_UNDEFINED;
int length = !JS_IsUndefined(user_permissions) ? tf_util_get_length(context, user_permissions) : 0;
for (int i = 0; i < length; i++)
{
JSValue permission = JS_GetPropertyUint32(context, user_permissions, i);
const char* permission_string = JS_ToCString(context, permission);
JS_SetPropertyStr(context, out_permissions, permission_string, JS_TRUE);
JS_FreeCString(context, permission_string);
JS_FreeValue(context, permission);
}
JS_FreeValue(context, user_permissions);
JS_FreeValue(context, permissions);
JS_FreeValue(context, settings_value);
tf_free((void*)settings);
JS_FreeCString(context, name_string);
JS_FreeValue(context, name);
} }
return result; return result;
} }
@@ -884,7 +852,7 @@ static void _httpd_endpoint_static(tf_http_request_t* request)
const char* k_static_files[] = { const char* k_static_files[] = {
"index.html", "index.html",
"client.js", "client.js",
"tildefriends.svg", "favicon.png",
"jszip.min.js", "jszip.min.js",
"style.css", "style.css",
"tfrpc.js", "tfrpc.js",
@@ -905,23 +873,14 @@ static void _httpd_endpoint_static(tf_http_request_t* request)
const char* file_path = NULL; const char* file_path = NULL;
for (int i = 0; i < tf_countof(k_map) && !after; i++) for (int i = 0; i < tf_countof(k_map) && !after; i++)
{ {
const char* next_after = _after(request->path, k_map[i][0]); after = _after(request->path, k_map[i][0]);
if (next_after) file_path = k_map[i][1];
{ is_core = is_core || (after && i == 0);
after = next_after;
file_path = k_map[i][1];
is_core = after && i == 0;
}
} }
if ((!after || !*after) && request->path[strlen(request->path) - 1] == '/') if (strcmp(request->path, "/speedscope/") == 0)
{ {
after = "index.html"; after = "index.html";
if (!file_path)
{
file_path = "core/";
is_core = true;
}
} }
if (!after || strstr(after, "..")) if (!after || strstr(after, ".."))
@@ -1103,17 +1062,13 @@ const char* _form_data_get(const char** form_data, const char* key)
typedef struct _login_request_t typedef struct _login_request_t
{ {
tf_http_request_t* request; tf_http_request_t* request;
const char* session_cookie;
JSValue jwt;
const char* name; const char* name;
const char* error; const char* error;
const char* settings;
const char* code_of_conduct; const char* code_of_conduct;
bool have_administrator; bool have_administrator;
bool session_is_new; bool session_is_new;
char location_header[1024];
const char* set_cookie_header;
int pending;
} login_request_t; } login_request_t;
static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie) static const char* _make_set_session_cookie_header(tf_http_request_t* request, const char* session_cookie)
@@ -1128,29 +1083,18 @@ static const char* _make_set_session_cookie_header(tf_http_request_t* request, c
return cookie; return cookie;
} }
static void _login_release(login_request_t* login)
{
int ref_count = --login->pending;
if (ref_count == 0)
{
tf_free((void*)login->name);
tf_free((void*)login->code_of_conduct);
tf_free((void*)login->set_cookie_header);
tf_free(login);
}
}
static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char* path, int result, const void* data, void* user_data) static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char* path, int result, const void* data, void* user_data)
{ {
login_request_t* login = user_data; login_request_t* login = user_data;
tf_http_request_t* request = login->request; tf_http_request_t* request = login->request;
if (result >= 0) if (result >= 0)
{ {
const char* cookie = _make_set_session_cookie_header(request, login->session_cookie);
const char* headers[] = { const char* headers[] = {
"Content-Type", "Content-Type",
"text/html; charset=utf-8", "text/html; charset=utf-8",
"Set-Cookie", "Set-Cookie",
login->set_cookie_header ? login->set_cookie_header : "", cookie ? cookie : "",
}; };
const char* replace_me = "$AUTH_DATA"; const char* replace_me = "$AUTH_DATA";
const char* auth = strstr(data, replace_me); const char* auth = strstr(data, replace_me);
@@ -1184,6 +1128,7 @@ static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char
{ {
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, data, result); tf_http_respond(request, 200, headers, tf_countof(headers) / 2, data, result);
} }
tf_free((void*)cookie);
} }
else else
{ {
@@ -1191,7 +1136,10 @@ static void _httpd_endpoint_login_file_read_callback(tf_task_t* task, const char
tf_http_respond(request, 404, NULL, 0, k_payload, strlen(k_payload)); tf_http_respond(request, 404, NULL, 0, k_payload, strlen(k_payload));
} }
tf_http_request_unref(request); tf_http_request_unref(request);
_login_release(login); tf_free((void*)login->name);
tf_free((void*)login->code_of_conduct);
tf_free((void*)login->session_cookie);
tf_free(login);
} }
static bool _string_property_equals(JSContext* context, JSValue object, const char* name, const char* value) static bool _string_property_equals(JSContext* context, JSValue object, const char* name, const char* value)
@@ -1204,7 +1152,12 @@ static bool _string_property_equals(JSContext* context, JSValue object, const ch
return equals; return equals;
} }
static JSValue _authenticate_jwt(tf_ssb_t* ssb, JSContext* context, const char* jwt) static void _public_key_visit(const char* identity, void* user_data)
{
snprintf(user_data, k_id_base64_len, "%s", identity);
}
static JSValue _authenticate_jwt(JSContext* context, const char* jwt)
{ {
if (!jwt) if (!jwt)
{ {
@@ -1245,8 +1198,10 @@ static JSValue _authenticate_jwt(tf_ssb_t* ssb, JSContext* context, const char*
return JS_UNDEFINED; return JS_UNDEFINED;
} }
tf_task_t* task = tf_task_get(context);
tf_ssb_t* ssb = tf_task_get_ssb(task);
char public_key_b64[k_id_base64_len] = { 0 }; char public_key_b64[k_id_base64_len] = { 0 };
tf_ssb_whoami(ssb, public_key_b64, sizeof(public_key_b64)); tf_ssb_db_identity_visit(ssb, ":admin", _public_key_visit, public_key_b64);
const char* payload = jwt + dot[0] + 1; const char* payload = jwt + dot[0] + 1;
size_t payload_length = dot[1] - dot[0] - 1; size_t payload_length = dot[1] - dot[0] - 1;
@@ -1305,6 +1260,25 @@ static bool _is_name_valid(const char* name)
return true; return true;
} }
static void _visit_auth_identity(const char* identity, void* user_data)
{
if (!*(char*)user_data)
{
snprintf((char*)user_data, k_id_base64_len, "%s", identity);
}
}
static bool _get_auth_private_key(tf_ssb_t* ssb, uint8_t* out_private_key)
{
char id[k_id_base64_len] = { 0 };
tf_ssb_db_identity_visit(ssb, ":admin", _visit_auth_identity, id);
if (*id)
{
return tf_ssb_db_identity_get_private_key(ssb, ":admin", id, out_private_key, crypto_sign_SECRETKEYBYTES);
}
return false;
}
static const char* _make_session_jwt(tf_ssb_t* ssb, const char* name) static const char* _make_session_jwt(tf_ssb_t* ssb, const char* name)
{ {
if (!name || !*name) if (!name || !*name)
@@ -1335,16 +1309,17 @@ static const char* _make_session_jwt(tf_ssb_t* ssb, const char* name)
char signature_base64[256] = { 0 }; char signature_base64[256] = { 0 };
uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 }; uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 };
tf_ssb_get_private_key(ssb, private_key, sizeof(private_key)); if (_get_auth_private_key(ssb, private_key))
if (crypto_sign_detached(signature, &signature_length, (const uint8_t*)payload_base64, strlen(payload_base64), private_key) == 0)
{ {
sodium_bin2base64(signature_base64, sizeof(signature_base64), signature, sizeof(signature), sodium_base64_VARIANT_URLSAFE_NO_PADDING); if (crypto_sign_detached(signature, &signature_length, (const uint8_t*)payload_base64, strlen(payload_base64), private_key) == 0)
size_t size = strlen(header_base64) + 1 + strlen(payload_base64) + 1 + strlen(signature_base64) + 1; {
result = tf_malloc(size); sodium_bin2base64(signature_base64, sizeof(signature_base64), signature, sizeof(signature), sodium_base64_VARIANT_URLSAFE_NO_PADDING);
snprintf(result, size, "%s.%s.%s", header_base64, payload_base64, signature_base64); size_t size = strlen(header_base64) + 1 + strlen(payload_base64) + 1 + strlen(signature_base64) + 1;
result = tf_malloc(size);
snprintf(result, size, "%s.%s.%s", header_base64, payload_base64, signature_base64);
}
sodium_memzero(private_key, sizeof(private_key));
} }
sodium_memzero(private_key, sizeof(private_key));
JS_FreeCString(context, payload_string); JS_FreeCString(context, payload_string);
JS_FreeValue(context, payload_json); JS_FreeValue(context, payload_json);
@@ -1360,10 +1335,26 @@ static bool _verify_password(const char* password, const char* hash)
return out_hash && strcmp(hash, out_hash) == 0; return out_hash && strcmp(hash, out_hash) == 0;
} }
static bool _make_administrator_if_first(tf_ssb_t* ssb, JSContext* context, const char* account_name_copy, bool may_become_first_admin) static const char* _get_code_of_conduct(tf_ssb_t* ssb)
{ {
JSContext* context = tf_ssb_get_context(ssb);
const char* settings = tf_ssb_db_get_property(ssb, "core", "settings"); const char* settings = tf_ssb_db_get_property(ssb, "core", "settings");
JSValue settings_value = settings && *settings ? JS_ParseJSON(context, settings, strlen(settings), NULL) : JS_UNDEFINED; JSValue settings_value = settings ? JS_ParseJSON(context, settings, strlen(settings), NULL) : JS_UNDEFINED;
JSValue code_of_conduct_value = JS_GetPropertyStr(context, settings_value, "code_of_conduct");
const char* code_of_conduct = JS_ToCString(context, code_of_conduct_value);
const char* result = tf_strdup(code_of_conduct);
JS_FreeCString(context, code_of_conduct);
JS_FreeValue(context, code_of_conduct_value);
JS_FreeValue(context, settings_value);
tf_free((void*)settings);
return result;
}
static bool _make_administrator_if_first(tf_ssb_t* ssb, const char* account_name_copy, bool may_become_first_admin)
{
JSContext* context = tf_ssb_get_context(ssb);
const char* settings = tf_ssb_db_get_property(ssb, "core", "settings");
JSValue settings_value = settings ? JS_ParseJSON(context, settings, strlen(settings), NULL) : JS_UNDEFINED;
if (JS_IsUndefined(settings_value)) if (JS_IsUndefined(settings_value))
{ {
settings_value = JS_NewObject(context); settings_value = JS_NewObject(context);
@@ -1432,32 +1423,30 @@ static bool _make_administrator_if_first(tf_ssb_t* ssb, JSContext* context, cons
return have_administrator; return have_administrator;
} }
static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data) static void _httpd_endpoint_login(tf_http_request_t* request)
{ {
login_request_t* login = user_data; tf_task_t* task = request->user_data;
tf_http_request_t* request = login->request; JSContext* context = tf_task_get_context(task);
tf_ssb_t* ssb = tf_task_get_ssb(task);
JSMallocFunctions funcs = { 0 };
tf_get_js_malloc_functions(&funcs);
JSRuntime* runtime = JS_NewRuntime2(&funcs, NULL);
JSContext* context = JS_NewContext(runtime);
const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session"); const char* session = tf_http_get_cookie(tf_http_request_get_header(request, "cookie"), "session");
const char** form_data = _form_data_decode(request->query, request->query ? strlen(request->query) : 0); const char** form_data = _form_data_decode(request->query, request->query ? strlen(request->query) : 0);
const char* account_name_copy = NULL; const char* account_name_copy = NULL;
JSValue jwt = _authenticate_jwt(ssb, context, session); JSValue jwt = _authenticate_jwt(context, session);
if (_session_is_authenticated_as_user(context, jwt)) if (_session_is_authenticated_as_user(context, jwt))
{ {
const char* return_url = _form_data_get(form_data, "return"); const char* return_url = _form_data_get(form_data, "return");
if (return_url) char url[1024];
if (!return_url)
{ {
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url); snprintf(url, sizeof(url), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
} return_url = url;
else
{
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
} }
const char* headers[] = {
"Location",
return_url,
};
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
goto done; goto done;
} }
@@ -1531,51 +1520,47 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
tf_free(post_form_data); tf_free(post_form_data);
} }
bool have_administrator = _make_administrator_if_first(ssb, context, account_name_copy, may_become_first_admin); bool have_administrator = _make_administrator_if_first(ssb, account_name_copy, may_become_first_admin);
if (session_is_new && _form_data_get(form_data, "return") && !login_error) if (session_is_new && _form_data_get(form_data, "return") && !login_error)
{ {
const char* return_url = _form_data_get(form_data, "return"); const char* return_url = _form_data_get(form_data, "return");
if (return_url) char url[1024];
if (!return_url)
{ {
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url); snprintf(url, sizeof(url), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host"));
return_url = url;
} }
else const char* cookie = _make_set_session_cookie_header(request, send_session);
{ const char* headers[] = {
snprintf(login->location_header, sizeof(login->location_header), "%s%s/", request->is_tls ? "https://" : "http://", tf_http_request_get_header(request, "host")); "Location",
} return_url,
login->set_cookie_header = _make_set_session_cookie_header(request, send_session); "Set-Cookie",
cookie ? cookie : "",
};
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
tf_free((void*)cookie);
tf_free((void*)send_session); tf_free((void*)send_session);
} }
else else
{ {
login->name = account_name_copy;
login->error = login_error;
login->set_cookie_header = _make_set_session_cookie_header(request, send_session);
tf_free((void*)send_session);
login->session_is_new = session_is_new;
login->have_administrator = have_administrator;
login->settings = tf_ssb_db_get_property(ssb, "core", "settings");
if (login->settings)
{
JSValue settings_value = JS_ParseJSON(context, login->settings, strlen(login->settings), NULL);
JSValue code_of_conduct_value = JS_GetPropertyStr(context, settings_value, "code_of_conduct");
const char* code_of_conduct = JS_ToCString(context, code_of_conduct_value);
const char* result = tf_strdup(code_of_conduct);
JS_FreeCString(context, code_of_conduct);
JS_FreeValue(context, code_of_conduct_value);
JS_FreeValue(context, settings_value);
tf_free((void*)login->settings);
login->settings = NULL;
login->code_of_conduct = result;
}
login->pending++;
tf_http_request_ref(request); tf_http_request_ref(request);
tf_file_read(login->request->user_data, "core/auth.html", _httpd_endpoint_login_file_read_callback, login);
login_request_t* login = tf_malloc(sizeof(login_request_t));
const char* code_of_conduct = _get_code_of_conduct(ssb);
*login = (login_request_t) {
.request = request,
.name = account_name_copy,
.jwt = jwt,
.error = login_error,
.session_cookie = send_session,
.session_is_new = session_is_new,
.code_of_conduct = code_of_conduct,
.have_administrator = have_administrator,
};
tf_file_read(request->user_data, "core/auth.html", _httpd_endpoint_login_file_read_callback, login);
jwt = JS_UNDEFINED;
account_name_copy = NULL; account_name_copy = NULL;
} }
@@ -1584,44 +1569,6 @@ done:
tf_free(form_data); tf_free(form_data);
tf_free((void*)account_name_copy); tf_free((void*)account_name_copy);
JS_FreeValue(context, jwt); JS_FreeValue(context, jwt);
JS_FreeContext(context);
JS_FreeRuntime(runtime);
}
static void _httpd_endpoint_login_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
login_request_t* login = user_data;
tf_http_request_t* request = login->request;
if (login->pending == 1)
{
if (*login->location_header)
{
const char* headers[] = {
"Location",
login->location_header,
"Set-Cookie",
login->set_cookie_header ? login->set_cookie_header : "",
};
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
}
}
tf_http_request_unref(request);
_login_release(login);
}
static void _httpd_endpoint_login(tf_http_request_t* request)
{
tf_task_t* task = request->user_data;
tf_http_request_ref(request);
tf_ssb_t* ssb = tf_task_get_ssb(task);
login_request_t* login = tf_malloc(sizeof(login_request_t));
*login = (login_request_t) {
.request = request,
};
login->pending++;
tf_ssb_run_work(ssb, _httpd_endpoint_login_work, _httpd_endpoint_login_after_work, login);
} }
static void _httpd_endpoint_logout(tf_http_request_t* request) static void _httpd_endpoint_logout(tf_http_request_t* request)
@@ -1677,7 +1624,6 @@ void tf_httpd_register(JSContext* context)
tf_http_add_handler(http, "/speedscope/*", _httpd_endpoint_static, NULL, task); tf_http_add_handler(http, "/speedscope/*", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/static/*", _httpd_endpoint_static, NULL, task); tf_http_add_handler(http, "/static/*", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task); tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/~*/*/", _httpd_endpoint_static, NULL, task);
tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL); tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL);
tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task); tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task);

View File

@@ -34,10 +34,6 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#if defined(__ANDROID__)
#include "jni.h"
#endif
#if !defined(_countof) #if !defined(_countof)
#define _countof(a) ((int)(sizeof((a)) / sizeof(*(a)))) #define _countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
#endif #endif
@@ -385,7 +381,6 @@ static int _tf_run_task(const tf_run_args_t* args, int index)
tf_ssb_import(tf_task_get_ssb(task), "core", "apps"); tf_ssb_import(tf_task_get_ssb(task), "core", "apps");
} }
} }
tf_ssb_set_main_thread(tf_task_get_ssb(task), true);
if (tf_task_execute(task, args->script)) if (tf_task_execute(task, args->script))
{ {
tf_task_run(task); tf_task_run(task);
@@ -607,16 +602,14 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
static int _tf_command_sandbox(const char* file, int argc, char* argv[]) static int _tf_command_sandbox(const char* file, int argc, char* argv[])
{ {
bool show_usage = false; bool show_usage = false;
int fd = STDIN_FILENO;
while (!show_usage) while (!show_usage)
{ {
static const struct option k_options[] = { static const struct option k_options[] = {
{ "fd", required_argument, NULL, 'f' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ 0 }, { 0 },
}; };
int c = getopt_long(argc, argv, "f:h", k_options, NULL); int c = getopt_long(argc, argv, "h", k_options, NULL);
if (c == -1) if (c == -1)
{ {
break; break;
@@ -628,9 +621,6 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
default: default:
show_usage = true; show_usage = true;
break; break;
case 'f':
fd = atoi(optarg);
break;
} }
} }
@@ -639,7 +629,6 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
tf_printf("\nUsage: %s sandbox [options]\n\n", file); tf_printf("\nUsage: %s sandbox [options]\n\n", file);
tf_printf("options:\n"); tf_printf("options:\n");
tf_printf(" -h, --help Show this usage information.\n"); tf_printf(" -h, --help Show this usage information.\n");
tf_printf(" -f, --fd File descriptor with which to communicate with parent process.\n");
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@@ -647,7 +636,7 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
prctl(PR_SET_PDEATHSIG, SIGHUP); prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif #endif
tf_task_t* task = tf_task_create(); tf_task_t* task = tf_task_create();
tf_task_configure_from_fd(task, fd); tf_task_configure_from_fd(task, STDIN_FILENO);
_shed_privileges(); _shed_privileges();
/* The caller will trigger tf_task_activate with a message. */ /* The caller will trigger tf_task_activate with a message. */
tf_task_run(task); tf_task_run(task);
@@ -695,6 +684,10 @@ static void _startup(int argc, char* argv[])
} }
} }
#if defined(__ANDROID__)
setenv("UV_USE_IO_URING", "0", 1);
#endif
tf_mem_startup(tracking); tf_mem_startup(tracking);
g_backtrace_state = backtrace_create_state(argv[0], 0, _backtrace_error, NULL); g_backtrace_state = backtrace_create_state(argv[0], 0, _backtrace_error, NULL);
@@ -722,7 +715,7 @@ static void _startup(int argc, char* argv[])
{ {
if ( if (
#if !defined(_WIN32) #if !defined(_WIN32)
signal(SIGSYS, _error_handler) == SIG_ERR || signal(SIGABRT, _error_handler) == SIG_ERR || signal(SIGSYS, _error_handler) == SIG_ERR ||
#endif #endif
signal(SIGSEGV, _error_handler) == SIG_ERR) signal(SIGSEGV, _error_handler) == SIG_ERR)
{ {
@@ -732,137 +725,23 @@ static void _startup(int argc, char* argv[])
} }
#if defined(__ANDROID__) #if defined(__ANDROID__)
static JNIEnv* s_jni_env;
static void _tf_service_start(int pipe_fd)
{
tf_printf("_tf_service_start\n");
jclass c = (*s_jni_env)->FindClass(s_jni_env, "com/unprompted/tildefriends/TildeFriendsActivity");
jmethodID start_sandbox = (*s_jni_env)->GetStaticMethodID(s_jni_env, c, "start_sandbox", "(I)V");
(*s_jni_env)->CallStaticVoidMethod(s_jni_env, c, start_sandbox, pipe_fd);
}
static void _tf_service_stop()
{
tf_printf("_tf_service_stop\n");
jclass c = (*s_jni_env)->FindClass(s_jni_env, "com/unprompted/tildefriends/TildeFriendsActivity");
jmethodID stop_sandbox = (*s_jni_env)->GetStaticMethodID(s_jni_env, c, "stop_sandbox", "()V");
(*s_jni_env)->CallStaticVoidMethod(s_jni_env, c, stop_sandbox);
}
static jint _tf_server_main(JNIEnv* env, jobject this_object, jstring files_dir, jstring apk_path, jstring out_port_file_path)
{
s_jni_env = env;
tf_printf("This is tf_server_main main.\n");
_startup(0, (char*[]) { NULL });
tf_printf("That was startup.\n");
const char* files = (*env)->GetStringUTFChars(env, files_dir, NULL);
const char* apk = (*env)->GetStringUTFChars(env, apk_path, NULL);
const char* out_port_file = (*env)->GetStringUTFChars(env, out_port_file_path, NULL);
tf_printf("FILES = %s\n", files);
tf_printf("APK = %s\n", apk);
tf_printf("OUT_PORT = %s\n", out_port_file);
int result = uv_chdir(files);
if (result)
{
tf_printf("uv_chdir: %s\n", uv_strerror(result));
}
size_t port_file_arg_length = strlen(out_port_file) + strlen("out_http_port_file=") + 1;
char* port_file_arg = alloca(port_file_arg_length);
snprintf(port_file_arg, port_file_arg_length, "out_http_port_file=%s", out_port_file);
const char* args[] = {
"run",
"-z",
apk,
"-a",
port_file_arg,
"-p",
"0",
};
tf_task_set_android_service_callbacks(_tf_service_start, _tf_service_stop);
result = _tf_command_run(apk, _countof(args), (char**)args);
tf_task_set_android_service_callbacks(NULL, NULL);
(*env)->ReleaseStringUTFChars(env, files_dir, files);
(*env)->ReleaseStringUTFChars(env, apk_path, apk);
(*env)->ReleaseStringUTFChars(env, out_port_file_path, out_port_file);
tf_mem_shutdown();
tf_printf("tf_server_main finished with %d.", result);
s_jni_env = NULL;
return result;
}
static jint _tf_sandbox_main(JNIEnv* env, jobject this_object, int pipe_fd)
{
s_jni_env = env;
tf_printf("This is tf_sandbox_main main (fd=%d).\n", pipe_fd);
_startup(0, (char*[]) { NULL });
tf_printf("That was startup.\n");
char fd[32] = { 0 };
snprintf(fd, sizeof(fd), "%d", pipe_fd);
const char* args[] = {
"sandbox",
"-f",
fd,
};
int result = _tf_command_sandbox(NULL, _countof(args), (char**)args);
tf_mem_shutdown();
tf_printf("tf_sandbox_main finished with %d.", result);
s_jni_env = NULL;
return result;
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
tf_printf("JNI_Onload called.\n");
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK)
{
tf_printf("Failed to get JNI environment.\n");
return JNI_ERR;
}
tf_printf("Finding class.\n");
jclass c = (*env)->FindClass(env, "com/unprompted/tildefriends/TildeFriendsActivity");
if (!c)
{
tf_printf("Failed to find TildeFriendsActivity class.\n");
return JNI_ERR;
}
tf_printf("Registering method.\n");
static const JNINativeMethod methods[] = {
{ "tf_server_main", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", _tf_server_main },
{ "tf_sandbox_main", "(I)I", _tf_sandbox_main },
};
int result = (*env)->RegisterNatives(env, c, methods, (int)_countof(methods));
if (result != JNI_OK)
{
return result;
}
tf_printf("Done.\n");
return JNI_VERSION_1_6;
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
tf_printf("Welcome to Tilde Friends. This is not the way to run on Android.\n"); _startup(argc, argv);
return EXIT_FAILURE; int result = -1;
if (argc > 1)
{
if (strcmp(argv[1], "run") == 0)
{
result = _tf_command_run(argv[0], argc - 1, argv + 1);
}
else if (strcmp(argv[1], "sandbox") == 0)
{
result = _tf_command_sandbox(argv[0], argc - 1, argv + 1);
}
}
tf_mem_shutdown();
return result;
} }
#elif TARGET_OS_IPHONE #elif TARGET_OS_IPHONE
void tf_run_thread_start(const char* zip_path) void tf_run_thread_start(const char* zip_path)

View File

@@ -1674,6 +1674,10 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
tf_trace_end(connection->ssb->trace); tf_trace_end(connection->ssb->trace);
} }
} }
else
{
tf_printf("No request callback for %p %d\n", connection, -request_number);
}
} }
if (close_connection) if (close_connection)
@@ -1780,28 +1784,18 @@ static bool _tf_ssb_connection_box_stream_recv(tf_ssb_connection_t* connection)
return true; return true;
} }
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int64_t previous_sequence) JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message)
{ {
char actual_previous_id[crypto_hash_sha256_BYTES * 2]; char previous_id[crypto_hash_sha256_BYTES * 2];
int64_t actual_previous_sequence = 0; int64_t previous_sequence = 0;
bool have_previous = false; bool have_previous = tf_ssb_db_get_latest_message_by_author(ssb, author, &previous_sequence, previous_id, sizeof(previous_id));
if (previous_id)
{
have_previous = *previous_id && previous_sequence > 0;
snprintf(actual_previous_id, sizeof(actual_previous_id), "%s", previous_id);
actual_previous_sequence = previous_sequence;
}
else
{
have_previous = tf_ssb_db_get_latest_message_by_author(ssb, author, &actual_previous_sequence, actual_previous_id, sizeof(actual_previous_id));
}
JSContext* context = ssb->context; JSContext* context = ssb->context;
JSValue root = JS_NewObject(context); JSValue root = JS_NewObject(context);
JS_SetPropertyStr(context, root, "previous", have_previous ? JS_NewString(context, actual_previous_id) : JS_NULL); JS_SetPropertyStr(context, root, "previous", have_previous ? JS_NewString(context, previous_id) : JS_NULL);
JS_SetPropertyStr(context, root, "author", JS_NewString(context, author)); JS_SetPropertyStr(context, root, "author", JS_NewString(context, author));
JS_SetPropertyStr(context, root, "sequence", JS_NewInt64(context, actual_previous_sequence + 1)); JS_SetPropertyStr(context, root, "sequence", JS_NewInt64(context, previous_sequence + 1));
int64_t now = (int64_t)time(NULL); int64_t now = (int64_t)time(NULL);
JS_SetPropertyStr(context, root, "timestamp", JS_NewInt64(context, now * 1000LL)); JS_SetPropertyStr(context, root, "timestamp", JS_NewInt64(context, now * 1000LL));
@@ -2266,7 +2260,6 @@ static void _tf_ssb_assert_not_main_thread(tf_ssb_t* ssb)
const char* bt = tf_util_backtrace_string(); const char* bt = tf_util_backtrace_string();
tf_printf("Acquiring DB from the main thread:\n%s\n", bt); tf_printf("Acquiring DB from the main thread:\n%s\n", bt);
tf_free((void*)bt); tf_free((void*)bt);
abort();
} }
} }
@@ -3756,12 +3749,7 @@ void tf_ssb_ref(tf_ssb_t* ssb)
void tf_ssb_unref(tf_ssb_t* ssb) void tf_ssb_unref(tf_ssb_t* ssb)
{ {
int new_count = --ssb->ref_count; ssb->ref_count--;
if (new_count < 0)
{
tf_printf("tf_ssb_unref past 0: %d\n", new_count);
abort();
}
} }
void tf_ssb_set_main_thread(tf_ssb_t* ssb, bool main_thread) void tf_ssb_set_main_thread(tf_ssb_t* ssb, bool main_thread)

View File

@@ -281,7 +281,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
tf_ssb_release_db_writer(ssb, db); tf_ssb_release_db_writer(ssb, db);
} }
static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous, bool* out_id_mismatch) static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous)
{ {
bool exists = false; bool exists = false;
if (sequence == 1) if (sequence == 1)
@@ -291,13 +291,12 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
else else
{ {
sqlite3_stmt* statement; sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT COUNT(*), id != ?3 AS is_mismatch FROM messages WHERE author = ?1 AND sequence = ?2", -1, &statement, NULL) == SQLITE_OK) if (sqlite3_prepare(db, "SELECT COUNT(*) FROM messages WHERE author = ?1 AND sequence = ?2 AND id = ?3", -1, &statement, NULL) == SQLITE_OK)
{ {
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, sequence - 1) == SQLITE_OK && if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, sequence - 1) == SQLITE_OK &&
sqlite3_bind_text(statement, 3, previous, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW) sqlite3_bind_text(statement, 3, previous, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
{ {
exists = sqlite3_column_int(statement, 0) != 0; exists = sqlite3_column_int(statement, 0) != 0;
*out_id_mismatch = sqlite3_column_int(statement, 1) != 0;
} }
sqlite3_finalize(statement); sqlite3_finalize(statement);
} }
@@ -310,9 +309,8 @@ static int64_t _tf_ssb_db_store_message_raw(tf_ssb_t* ssb, const char* id, const
{ {
sqlite3* db = tf_ssb_acquire_db_writer(ssb); sqlite3* db = tf_ssb_acquire_db_writer(ssb);
int64_t last_row_id = -1; int64_t last_row_id = -1;
bool id_mismatch = false;
if (_tf_ssb_db_previous_message_exists(db, author, sequence, previous, &id_mismatch)) if (_tf_ssb_db_previous_message_exists(db, author, sequence, previous))
{ {
const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, flags) VALUES (?, ?, ?, ?, ?, jsonb(?), " const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, flags) VALUES (?, ?, ?, ?, ?, jsonb(?), "
"?, ?, ?) ON CONFLICT DO NOTHING"; "?, ?, ?) ON CONFLICT DO NOTHING";
@@ -347,14 +345,8 @@ static int64_t _tf_ssb_db_store_message_raw(tf_ssb_t* ssb, const char* id, const
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db)); tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
} }
} }
else if (id_mismatch) else
{ {
/*
** Only warn if we find a previous message with the wrong ID.
** If a feed is forked, we would otherwise warn on every
** message when trying to receive what we don't have, and
** that's not helping anybody.
*/
tf_printf("%p: Previous message doesn't exist for author=%s sequence=%" PRId64 " previous=%s.\n", db, author, sequence, previous); tf_printf("%p: Previous message doesn't exist for author=%s sequence=%" PRId64 " previous=%s.\n", db, author, sequence, previous);
} }
tf_ssb_release_db_writer(ssb, db); tf_ssb_release_db_writer(ssb, db);
@@ -643,44 +635,6 @@ bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_
return result; return result;
} }
typedef struct _blob_get_async_t
{
tf_ssb_t* ssb;
char id[k_blob_id_len];
tf_ssb_db_blob_get_callback_t* callback;
void* user_data;
bool out_found;
uint8_t* out_data;
size_t out_size;
} blob_get_async_t;
static void _tf_ssb_db_blob_get_async_work(tf_ssb_t* ssb, void* user_data)
{
blob_get_async_t* async = user_data;
async->out_found = tf_ssb_db_blob_get(ssb, async->id, &async->out_data, &async->out_size);
}
static void _tf_ssb_db_blob_get_async_after_work(tf_ssb_t* ssb, int status, void* user_data)
{
blob_get_async_t* async = user_data;
async->callback(async->out_found, async->out_data, async->out_size, async->user_data);
tf_free(async->out_data);
tf_free(async);
}
void tf_ssb_db_blob_get_async(tf_ssb_t* ssb, const char* id, tf_ssb_db_blob_get_callback_t* callback, void* user_data)
{
blob_get_async_t* async = tf_malloc(sizeof(blob_get_async_t));
*async = (blob_get_async_t) {
.ssb = ssb,
.callback = callback,
.user_data = user_data,
};
snprintf(async->id, sizeof(async->id), "%s", id);
tf_ssb_run_work(ssb, _tf_ssb_db_blob_get_async_work, _tf_ssb_db_blob_get_async_after_work, async);
}
typedef struct _blob_store_work_t typedef struct _blob_store_work_t
{ {
const uint8_t* blob; const uint8_t* blob;
@@ -694,10 +648,7 @@ typedef struct _blob_store_work_t
static void _tf_ssb_db_blob_store_work(tf_ssb_t* ssb, void* user_data) static void _tf_ssb_db_blob_store_work(tf_ssb_t* ssb, void* user_data)
{ {
blob_store_work_t* blob_work = user_data; blob_store_work_t* blob_work = user_data;
if (!tf_ssb_is_shutting_down(ssb)) tf_ssb_db_blob_store(ssb, blob_work->blob, blob_work->size, blob_work->id, sizeof(blob_work->id), &blob_work->is_new);
{
tf_ssb_db_blob_store(ssb, blob_work->blob, blob_work->size, blob_work->id, sizeof(blob_work->id), &blob_work->is_new);
}
} }
static void _tf_ssb_db_blob_store_after_work(tf_ssb_t* ssb, int status, void* user_data) static void _tf_ssb_db_blob_store_after_work(tf_ssb_t* ssb, int status, void* user_data)
@@ -1095,8 +1046,6 @@ bool tf_ssb_db_identity_create(tf_ssb_t* ssb, const char* user, uint8_t* out_pub
if (out_private_key) if (out_private_key)
{ {
tf_ssb_id_str_to_bin(out_private_key, private); tf_ssb_id_str_to_bin(out_private_key, private);
/* HACK: tf_ssb_id_str_to_bin only produces 32 bytes even though the full private key is 32 + 32. */
tf_ssb_id_str_to_bin(out_private_key + crypto_sign_PUBLICKEYBYTES, public);
} }
return true; return true;
} }

View File

@@ -55,24 +55,6 @@ bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id);
*/ */
bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size); bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size);
/**
** A function called when a blob is retrieved from the database.
** @param found Whether the blob was found.
** @param data The blob data if found.
** @param size The size of the blob data if found, in bytes.
** @param user_data The user data.
*/
typedef void(tf_ssb_db_blob_get_callback_t)(bool found, const uint8_t* data, size_t size, void* user_data);
/**
** Retrieve a blob from the database asynchronously.
** @param ssb The SSB instance.
** @param id The blob identifier.
** @param callback Callback called with the result.
** @param user_data The user data.
*/
void tf_ssb_db_blob_get_async(tf_ssb_t* ssb, const char* id, tf_ssb_db_blob_get_callback_t* callback, void* user_data);
/** /**
** A function called when a message is stored in the database. ** A function called when a message is stored in the database.
** @param id The message identifier. ** @param id The message identifier.

View File

@@ -278,11 +278,9 @@ void tf_ssb_run(tf_ssb_t* ssb);
** @param author The author's public key. ** @param author The author's public key.
** @param private_key The author's private key. ** @param private_key The author's private key.
** @param message The message to sign. ** @param message The message to sign.
** @param previous_id The ID of the previous message in the feed. Optional.
** @param previous_sequence The sequence number of the previous message in the feed. Optional.
** @return The signed message. ** @return The signed message.
*/ */
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int64_t previous_sequence); JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message);
/** /**
** Get the server's identity. ** Get the server's identity.

File diff suppressed because it is too large Load Diff

View File

@@ -67,40 +67,6 @@ static void _tf_ssb_rpc_blobs_get_callback(
{ {
} }
typedef struct _blobs_get_work_t
{
int64_t request_number;
char id[k_id_base64_len];
bool found;
uint8_t* blob;
size_t size;
} blobs_get_work_t;
static void _tf_ssb_rpc_blobs_get_work(tf_ssb_connection_t* connection, void* user_data)
{
blobs_get_work_t* work = user_data;
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
work->found = tf_ssb_db_blob_get(ssb, work->id, &work->blob, &work->size);
}
static void _tf_ssb_rpc_blobs_get_after_work(tf_ssb_connection_t* connection, int status, void* user_data)
{
blobs_get_work_t* work = user_data;
if (work->found)
{
const size_t k_send_max = 8192;
for (size_t offset = 0; offset < work->size; offset += k_send_max)
{
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_binary | k_ssb_rpc_flag_stream, -work->request_number, NULL, work->blob + offset,
offset + k_send_max <= work->size ? k_send_max : (work->size - offset), NULL, NULL, NULL);
}
tf_free(work->blob);
}
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error | k_ssb_rpc_flag_stream, -work->request_number, NULL,
(const uint8_t*)(work->found ? "true" : "false"), strlen(work->found ? "true" : "false"), NULL, NULL, NULL);
tf_free(work);
}
static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{ {
if (flags & k_ssb_rpc_flag_end_error) if (flags & k_ssb_rpc_flag_end_error)
@@ -109,10 +75,11 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
} }
tf_ssb_connection_add_request(connection, -request_number, "blobs.get", _tf_ssb_rpc_blobs_get_callback, NULL, NULL, NULL); tf_ssb_connection_add_request(connection, -request_number, "blobs.get", _tf_ssb_rpc_blobs_get_callback, NULL, NULL, NULL);
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
JSContext* context = tf_ssb_connection_get_context(connection); JSContext* context = tf_ssb_connection_get_context(connection);
JSValue ids = JS_GetPropertyStr(context, args, "args"); JSValue ids = JS_GetPropertyStr(context, args, "args");
int length = tf_util_get_length(context, ids); int length = tf_util_get_length(context, ids);
bool success = false;
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
JSValue arg = JS_GetPropertyUint32(context, ids, i); JSValue arg = JS_GetPropertyUint32(context, ids, i);
@@ -127,18 +94,25 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
id = JS_ToCString(context, key); id = JS_ToCString(context, key);
JS_FreeValue(context, key); JS_FreeValue(context, key);
} }
uint8_t* blob = NULL;
blobs_get_work_t* work = tf_malloc(sizeof(blobs_get_work_t)); size_t size = 0;
*work = (blobs_get_work_t) { const size_t k_send_max = 8192;
.request_number = request_number, if (tf_ssb_db_blob_get(ssb, id, &blob, &size))
}; {
snprintf(work->id, sizeof(work->id), "%s", id); for (size_t offset = 0; offset < size; offset += k_send_max)
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_blobs_get_work, _tf_ssb_rpc_blobs_get_after_work, work); {
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_binary | k_ssb_rpc_flag_stream, -request_number, NULL, blob + offset,
offset + k_send_max <= size ? k_send_max : (size - offset), NULL, NULL, NULL);
}
success = true;
tf_free(blob);
}
JS_FreeCString(context, id); JS_FreeCString(context, id);
JS_FreeValue(context, arg); JS_FreeValue(context, arg);
} }
JS_FreeValue(context, ids); JS_FreeValue(context, ids);
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_end_error | k_ssb_rpc_flag_stream, -request_number, NULL,
(const uint8_t*)(success ? "true" : "false"), strlen(success ? "true" : "false"), NULL, NULL, NULL);
} }
static void _tf_ssb_rpc_blobs_has(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) static void _tf_ssb_rpc_blobs_has(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
@@ -506,45 +480,6 @@ static void _tf_ssb_rpc_connection_blobs_get(tf_ssb_connection_t* connection, co
JS_FreeValue(context, message); JS_FreeValue(context, message);
} }
typedef struct _blob_create_wants_work_t
{
tf_ssb_connection_t* connection;
char blob_id[k_blob_id_len];
bool out_result;
int64_t size;
size_t out_size;
} blob_create_wants_work_t;
static void _tf_ssb_rpc_connection_blobs_create_wants_work(tf_ssb_connection_t* connection, void* user_data)
{
blob_create_wants_work_t* work = user_data;
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(work->connection);
work->out_result = tf_ssb_db_blob_get(ssb, work->blob_id, NULL, &work->out_size);
}
static void _tf_ssb_rpc_connection_blobs_create_wants_after_work(tf_ssb_connection_t* connection, int result, void* user_data)
{
blob_create_wants_work_t* work = user_data;
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(work->connection);
tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
JSContext* context = tf_ssb_get_context(ssb);
if (work->out_result)
{
JSValue message = JS_NewObject(context);
JS_SetPropertyStr(context, message, work->blob_id, JS_NewInt64(context, work->out_size));
tf_ssb_connection_rpc_send_json(work->connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
JS_FreeValue(context, message);
}
else if (work->size == -1LL)
{
JSValue message = JS_NewObject(context);
JS_SetPropertyStr(context, message, work->blob_id, JS_NewInt64(context, -2));
tf_ssb_connection_rpc_send_json(work->connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
JS_FreeValue(context, message);
}
tf_free(work);
}
static void _tf_ssb_rpc_connection_blobs_createWants_callback( static void _tf_ssb_rpc_connection_blobs_createWants_callback(
tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{ {
@@ -554,6 +489,7 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
return; return;
} }
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
JSContext* context = tf_ssb_connection_get_context(connection); JSContext* context = tf_ssb_connection_get_context(connection);
JSValue name = JS_GetPropertyStr(context, args, "name"); JSValue name = JS_GetPropertyStr(context, args, "name");
@@ -588,13 +524,21 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
} }
if (size < 0) if (size < 0)
{ {
blob_create_wants_work_t* work = tf_malloc(sizeof(blob_create_wants_work_t)); size_t blob_size = 0;
*work = (blob_create_wants_work_t) { if (tf_ssb_db_blob_get(ssb, blob_id, NULL, &blob_size))
.connection = connection, {
.size = size, JSValue message = JS_NewObject(context);
}; JS_SetPropertyStr(context, message, blob_id, JS_NewInt64(context, blob_size));
snprintf(work->blob_id, sizeof(work->blob_id), "%s", blob_id); tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_connection_blobs_create_wants_work, _tf_ssb_rpc_connection_blobs_create_wants_after_work, work); JS_FreeValue(context, message);
}
else if (size == -1LL)
{
JSValue message = JS_NewObject(context);
JS_SetPropertyStr(context, message, blob_id, JS_NewInt64(context, -2));
tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_stream, -blob_wants->request_number, NULL, message, NULL, NULL, NULL);
JS_FreeValue(context, message);
}
} }
else else
{ {
@@ -832,10 +776,6 @@ static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t*
{ {
tf_ssb_connection_run_work(connection, _tf_ssb_connection_send_history_stream_work, _tf_ssb_connection_send_history_stream_after_work, user_data); tf_ssb_connection_run_work(connection, _tf_ssb_connection_send_history_stream_work, _tf_ssb_connection_send_history_stream_after_work, user_data);
} }
else
{
_tf_ssb_connection_send_history_stream_after_work(connection, -1, user_data);
}
} }
static void _tf_ssb_connection_send_history_stream(tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys, bool live) static void _tf_ssb_connection_send_history_stream(tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys, bool live)
@@ -1285,6 +1225,7 @@ static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
static void _tf_ssb_rpc_delete_blobs_after_work(tf_ssb_t* ssb, int status, void* user_data) static void _tf_ssb_rpc_delete_blobs_after_work(tf_ssb_t* ssb, int status, void* user_data)
{ {
tf_ssb_unref(ssb);
} }
static void _tf_ssb_rpc_start_delete_callback(tf_ssb_t* ssb, void* user_data) static void _tf_ssb_rpc_start_delete_callback(tf_ssb_t* ssb, void* user_data)

View File

@@ -16,15 +16,6 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#if !defined(_WIN32)
#include <sys/wait.h>
#endif
#if defined(_WIN32)
#define WIFEXITED(x) 1
#define WEXITSTATUS(x) (x)
#endif
void tf_ssb_test_id_conversion(const tf_test_options_t* options) void tf_ssb_test_id_conversion(const tf_test_options_t* options)
{ {
tf_printf("Testing id conversion.\n"); tf_printf("Testing id conversion.\n");
@@ -201,7 +192,7 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post")); JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post"));
JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Hello, world!")); JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Hello, world!"));
bool stored = false; bool stored = false;
JSValue signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj, NULL, 0); JSValue signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj);
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
JS_FreeValue(context0, signed_message); JS_FreeValue(context0, signed_message);
_wait_stored(ssb0, &stored); _wait_stored(ssb0, &stored);
@@ -211,7 +202,7 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post")); JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post"));
JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "First post.")); JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "First post."));
stored = false; stored = false;
signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj, NULL, 0); signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj);
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
JS_FreeValue(context0, signed_message); JS_FreeValue(context0, signed_message);
_wait_stored(ssb0, &stored); _wait_stored(ssb0, &stored);
@@ -226,7 +217,7 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
JS_SetPropertyUint32(context0, mentions, 0, mention); JS_SetPropertyUint32(context0, mentions, 0, mention);
JS_SetPropertyStr(context0, obj, "mentions", mentions); JS_SetPropertyStr(context0, obj, "mentions", mentions);
stored = false; stored = false;
signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj, NULL, 0); signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj);
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
JS_FreeValue(context0, signed_message); JS_FreeValue(context0, signed_message);
_wait_stored(ssb0, &stored); _wait_stored(ssb0, &stored);
@@ -285,7 +276,7 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post")); JS_SetPropertyStr(context0, obj, "type", JS_NewString(context0, "post"));
JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Message to self.")); JS_SetPropertyStr(context0, obj, "text", JS_NewString(context0, "Message to self."));
stored = false; stored = false;
signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj, NULL, 0); signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj);
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
JS_FreeValue(context0, signed_message); JS_FreeValue(context0, signed_message);
_wait_stored(ssb0, &stored); _wait_stored(ssb0, &stored);
@@ -558,7 +549,7 @@ void tf_ssb_test_following(const tf_test_options_t* options)
JS_SetPropertyStr(context, message, "contact", JS_NewString(context, contact)); \ JS_SetPropertyStr(context, message, "contact", JS_NewString(context, contact)); \
JS_SetPropertyStr(context, message, "following", follow ? JS_TRUE : JS_FALSE); \ JS_SetPropertyStr(context, message, "following", follow ? JS_TRUE : JS_FALSE); \
JS_SetPropertyStr(context, message, "blocking", block ? JS_TRUE : JS_FALSE); \ JS_SetPropertyStr(context, message, "blocking", block ? JS_TRUE : JS_FALSE); \
signed_message = tf_ssb_sign_message(ssb0, id, priv, message, NULL, 0); \ signed_message = tf_ssb_sign_message(ssb0, id, priv, message); \
stored = false; \ stored = false; \
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); \ tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); \
_wait_stored(ssb0, &stored); \ _wait_stored(ssb0, &stored); \
@@ -617,7 +608,7 @@ void tf_ssb_test_bench(const tf_test_options_t* options)
for (int i = 0; i < k_messages; i++) for (int i = 0; i < k_messages; i++)
{ {
bool stored = false; bool stored = false;
JSValue signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj, NULL, 0); JSValue signed_message = tf_ssb_sign_message(ssb0, id0, priv0, obj);
tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored); tf_ssb_verify_strip_and_store_message(ssb0, signed_message, _message_stored, &stored);
JS_FreeValue(tf_ssb_get_context(ssb0), signed_message); JS_FreeValue(tf_ssb_get_context(ssb0), signed_message);
_wait_stored(ssb0, &stored); _wait_stored(ssb0, &stored);
@@ -828,45 +819,3 @@ void tf_ssb_test_go_ssb_room(const tf_test_options_t* options)
uv_loop_close(&loop); uv_loop_close(&loop);
} }
#if !TARGET_OS_IPHONE
static void _write_file(const char* path, const char* contents)
{
FILE* file = fopen(path, "w");
if (!file)
{
printf("Unable to write %s: %s.\n", path, strerror(errno));
fflush(stdout);
abort();
}
fputs(contents, file);
fclose(file);
}
#define TEST_ARGS " --ssb-port=0 --http-port=0 --https-port=0"
void tf_ssb_test_encrypt(const tf_test_options_t* options)
{
_write_file("out/test.js",
"async function main() {\n"
" let a = await ssb.createIdentity('test');\n"
" let b = await ssb.createIdentity('test');\n"
" let c = await ssb.privateMessageEncrypt('test', a, [a, b], \"{'foo': 1}\");\n"
" if (!c.endsWith('.box')) {\n"
" exit(1);\n"
" }\n"
" print(await ssb.privateMessageDecrypt('test', a, c));\n"
"}\n"
"main().catch(() => exit(2));\n");
unlink("out/testdb.sqlite");
char command[256];
snprintf(command, sizeof(command), "%s run --db-path=out/testdb.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command);
int result = system(command);
(void)result;
assert(WIFEXITED(result));
printf("returned %d\n", WEXITSTATUS(result));
assert(WEXITSTATUS(result) == 0);
}
#endif

View File

@@ -47,10 +47,4 @@ void tf_ssb_test_bench(const tf_test_options_t* options);
*/ */
void tf_ssb_test_go_ssb_room(const tf_test_options_t* options); void tf_ssb_test_go_ssb_room(const tf_test_options_t* options);
/**
** Test encrypting a private message.
** @param options The test options.
*/
void tf_ssb_test_encrypt(const tf_test_options_t* options);
/** @} */ /** @} */

View File

@@ -50,9 +50,6 @@
static JSClassID _import_class_id; static JSClassID _import_class_id;
static int _count; static int _count;
static tf_android_start_service_t* s_android_start_service;
static tf_android_stop_service_t* s_android_stop_service;
extern struct backtrace_state* g_backtrace_state; extern struct backtrace_state* g_backtrace_state;
typedef struct _export_record_t export_record_t; typedef struct _export_record_t export_record_t;
@@ -2163,19 +2160,3 @@ static JSValue _tf_task_pokeSandbox(JSContext* context, JSValueConst this_val, i
#endif #endif
return JS_NewInt32(context, WEXITSTATUS(result)); return JS_NewInt32(context, WEXITSTATUS(result));
} }
void tf_task_set_android_service_callbacks(tf_android_start_service_t* start_service, tf_android_stop_service_t* stop_service)
{
s_android_start_service = start_service;
s_android_stop_service = stop_service;
}
tf_android_start_service_t* tf_task_get_android_start_service()
{
return s_android_start_service;
}
tf_android_stop_service_t* tf_task_get_android_stop_service()
{
return s_android_stop_service;
}

View File

@@ -333,35 +333,4 @@ char* tf_task_get_debug(tf_task_t* task);
*/ */
char* tf_task_get_hitches(tf_task_t* task); char* tf_task_get_hitches(tf_task_t* task);
/**
** A callback used to start an Android service.
** @param pipe_fd A file descriptor with which to communicate with the invoking
** task.
*/
typedef void(tf_android_start_service_t)(int pipe_fd);
/**
** A callback used to stop an Android service.
*/
typedef void(tf_android_stop_service_t)();
/**
** Set Android service callbacks.
** @param start_service Start service callback.
** @param stop_service Stop service callback.
*/
void tf_task_set_android_service_callbacks(tf_android_start_service_t* start_service, tf_android_stop_service_t* stop_service);
/**
** Get the callback registered for starting an Android service.
** @return the callback.
*/
tf_android_start_service_t* tf_task_get_android_start_service();
/**
** Get the callback registered for stopping an Android service.
** @return the callback.
*/
tf_android_stop_service_t* tf_task_get_android_stop_service();
/** @} */ /** @} */

View File

@@ -122,11 +122,10 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
char arg1[] = "sandbox"; char arg1[] = "sandbox";
char* command_argv[] = { _executable, arg1, 0 }; char* command_argv[] = { _executable, arg1, 0 };
tf_android_start_service_t* start_service = tf_task_get_android_start_service();
JSValue result = JS_NULL; JSValue result = JS_NULL;
if (tf_task_get_one_proc(parent) || start_service) if (tf_task_get_one_proc(parent))
{ {
uv_os_sock_t fds[2] = { 0 }; uv_os_sock_t fds[2];
int pipe_result = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); int pipe_result = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
if (pipe_result) if (pipe_result)
{ {
@@ -134,7 +133,7 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
} }
uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream); uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream);
*pipe = (uv_pipe_t) { 0 }; memset(pipe, 0, sizeof(*pipe));
pipe_result = uv_pipe_init(tf_task_get_loop(parent), pipe, 1); pipe_result = uv_pipe_init(tf_task_get_loop(parent), pipe, 1);
if (pipe_result != 0) if (pipe_result != 0)
{ {
@@ -146,16 +145,8 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
tf_printf("uv_pipe_open failed: %s\n", uv_strerror(pipe_result)); tf_printf("uv_pipe_open failed: %s\n", uv_strerror(pipe_result));
} }
if (start_service) uv_thread_t* thread = tf_malloc(sizeof(uv_thread_t));
{ uv_thread_create(thread, _tf_taskstub_run_sandbox_thread, (void*)(intptr_t)fds[1]);
start_service(fds[1]);
}
else
{
/* XXX: This is a leak. */
uv_thread_t* thread = tf_malloc(sizeof(uv_thread_t));
uv_thread_create(thread, _tf_taskstub_run_sandbox_thread, (void*)(intptr_t)fds[1]);
}
tf_packetstream_set_on_receive(stub->_stream, tf_task_on_receive_packet, stub); tf_packetstream_set_on_receive(stub->_stream, tf_task_on_receive_packet, stub);
tf_packetstream_start(stub->_stream); tf_packetstream_start(stub->_stream);
@@ -452,15 +443,7 @@ JSValue tf_taskstub_kill(tf_taskstub_t* stub)
JSValue result = JS_UNDEFINED; JSValue result = JS_UNDEFINED;
if (!tf_task_get_one_proc(stub->_owner)) if (!tf_task_get_one_proc(stub->_owner))
{ {
tf_android_stop_service_t* stop_service = tf_task_get_android_stop_service(); uv_process_kill(&stub->_process, SIGKILL);
if (stop_service)
{
stop_service();
}
else
{
uv_process_kill(&stub->_process, SIGKILL);
}
} }
else else
{ {

View File

@@ -266,47 +266,32 @@ static void _test_promise_remote_reject(const tf_test_options_t* options)
static void _test_database(const tf_test_options_t* options) static void _test_database(const tf_test_options_t* options)
{ {
_write_file("out/test.js", _write_file("out/test.js",
"async function main() {\n" "var db = new Database('testdb');\n"
" var db = new Database('testdb');\n" "if (db.get('a')) {\n"
" if (await db.get('a')) {\n" " exit(1);\n"
" exit(1);\n" "}\n"
" }\n" "db.set('a', 1);\n"
" await db.set('a', 1);\n" "if (db.get('a') != 1) {\n"
" if (await db.get('a') != 1) {\n" " exit(2);\n"
" exit(2);\n" "}\n"
" }\n" "db.set('b', 2);\n"
" await db.exchange('b', null, 1);\n" "db.set('c', 3);\n"
" await db.exchange('b', 1, 2);\n"
" if (await db.get('b') != 2) {\n"
" exit(5);\n"
" }\n"
" await db.set('c', 3);\n"
" await db.set('d', 3);\n"
" await db.remove('d', 3);\n"
" if (JSON.stringify(await db.getLike('b%')) != '{\"b\":\"2\"}') {\n"
" exit(6);\n"
" }\n"
"\n" "\n"
" var expected = ['a', 'b', 'c'];\n" "var expected = ['a', 'b', 'c'];\n"
" var have = await db.getAll();\n" "var have = db.getAll();\n"
" for (var i = 0; i < have.length; i++) {\n" "for (var i = 0; i < have.length; i++) {\n"
" var item = have[i];\n" " var item = have[i];\n"
" if (expected.indexOf(item) == -1) {\n" " if (expected.indexOf(item) == -1) {\n"
" print('Did not find ' + item + ' in db.');\n" " print('Did not find ' + item + ' in db.');\n"
" exit(3);\n" " exit(3);\n"
" } else {\n" " } else {\n"
" expected.splice(expected.indexOf(item), 1);\n" " expected.splice(expected.indexOf(item), 1);\n"
" }\n"
" }\n"
" if (expected.length) {\n"
" print('Expected but did not find: ' + JSON.stringify(expected));\n"
" exit(4);\n"
" }\n"
" if (JSON.stringify(await databases.list('%')) != '[\"testdb\"]') {\n"
" exit(7);\n"
" }\n" " }\n"
"}\n" "}\n"
"main();"); "if (expected.length) {\n"
" print('Expected but did not find: ' + JSON.stringify(expected));\n"
" exit(4);\n"
"}\n");
char command[256]; char command[256];
unlink("out/test_db0.sqlite"); unlink("out/test_db0.sqlite");
@@ -914,7 +899,6 @@ void tf_tests(const tf_test_options_t* options)
_tf_test_run(options, "bench", tf_ssb_test_bench, false); _tf_test_run(options, "bench", tf_ssb_test_bench, false);
_tf_test_run(options, "auto", _test_auto, false); _tf_test_run(options, "auto", _test_auto, false);
_tf_test_run(options, "go-ssb-room", tf_ssb_test_go_ssb_room, true); _tf_test_run(options, "go-ssb-room", tf_ssb_test_go_ssb_room, true);
_tf_test_run(options, "encrypt", tf_ssb_test_encrypt, false);
tf_printf("Tests completed.\n"); tf_printf("Tests completed.\n");
#endif #endif
} }

View File

@@ -435,16 +435,6 @@ const char* tf_util_backtrace_string()
return tf_util_backtrace_to_string(buffer, count); return tf_util_backtrace_to_string(buffer, count);
} }
void tf_util_print_backtrace()
{
const char* bt = tf_util_backtrace_string();
if (bt)
{
tf_printf("%s\n", bt);
}
tf_free((void*)bt);
}
#if defined(__ANDROID__) #if defined(__ANDROID__)
typedef struct _android_backtrace_t typedef struct _android_backtrace_t
{ {

View File

@@ -126,11 +126,6 @@ const char* tf_util_backtrace_to_string(void* const* buffer, int count);
*/ */
const char* tf_util_backtrace_string(); const char* tf_util_backtrace_string();
/**
** Print a stack backtrace of the calling thread.
*/
void tf_util_print_backtrace();
/** /**
** Convert a function pointer to its name, if possible. ** Convert a function pointer to its name, if possible.
** @return The function name or null. ** @return The function name or null.

View File

@@ -1,2 +1,2 @@
#define VERSION_NUMBER "0.0.21.1" #define VERSION_NUMBER "0.0.20-wip"
#define VERSION_NAME "What are we even doing here?" #define VERSION_NAME "One word all lowercase four words all uppercase."

View File

@@ -42,19 +42,20 @@ try:
driver.switch_to.frame(driver.find_element(By.ID, 'document')) driver.switch_to.frame(driver.find_element(By.ID, 'document'))
wait.until(expected_conditions.presence_of_element_located((By.LINK_TEXT, 'identity'))).click() wait.until(expected_conditions.presence_of_element_located((By.LINK_TEXT, 'identity'))).click()
driver.switch_to.default_content()
wait.until(expected_conditions.presence_of_element_located((By.ID, 'content')))
# StaleElementReferenceException # StaleElementReferenceException
while True: while True:
try: try:
driver.switch_to.default_content()
wait.until(expected_conditions.presence_of_element_located((By.ID, 'content')))
driver.switch_to.frame(wait.until(expected_conditions.presence_of_element_located((By.ID, 'document')))) driver.switch_to.frame(wait.until(expected_conditions.presence_of_element_located((By.ID, 'document'))))
wait.until(expected_conditions.presence_of_element_located((By.ID, 'create_id'))).click()
driver.switch_to.alert.accept()
break break
except: except:
pass pass
wait.until(expected_conditions.presence_of_element_located((By.ID, 'create_id'))).click()
driver.switch_to.alert.accept()
# StaleElementReferenceException # StaleElementReferenceException
while True: while True:
try: try:
@@ -125,8 +126,7 @@ try:
driver.switch_to.default_content() driver.switch_to.default_content()
driver.find_element(By.ID, 'allow').click() driver.find_element(By.ID, 'allow').click()
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'identity').click() driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'logout testuser').click()
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'logout').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'login_label').click() driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'login_label').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'name').send_keys('testuser') driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'name').send_keys('testuser')
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'password').send_keys('test_password') driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'password').send_keys('test_password')
@@ -134,8 +134,13 @@ try:
wait.until(expected_conditions.presence_of_element_located((By.ID, 'content'))) wait.until(expected_conditions.presence_of_element_located((By.ID, 'content')))
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'identity').click() # NoSuchElementException
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'logout').click() while True:
try:
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'logout testuser').click()
break
except:
pass
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'guest_label').click() driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'guest_label').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'guestButton').click() driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'guestButton').click()
@@ -144,7 +149,7 @@ try:
wait.until(expected_conditions.presence_of_element_located((By.TAG_NAME, 'tf-app'))).shadow_root wait.until(expected_conditions.presence_of_element_located((By.TAG_NAME, 'tf-app'))).shadow_root
driver.switch_to.default_content() driver.switch_to.default_content()
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'logout').click() driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'logout guest').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'login_label').click() driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'login_label').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'name').send_keys('testuser') driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'name').send_keys('testuser')
@@ -185,8 +190,7 @@ try:
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'confirm').send_keys('new_password') driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'confirm').send_keys('new_password')
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'loginButton').click() driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'loginButton').click()
wait.until(expected_conditions.presence_of_element_located((By.ID, 'document'))) wait.until(expected_conditions.presence_of_element_located((By.ID, 'document')))
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'identity').click() driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.LINK_TEXT, 'logout testuser').click()
driver.find_element(By.TAG_NAME, 'tf-navigation').shadow_root.find_element(By.ID, 'logout').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'login_label').click() driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'login_label').click()
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'name').send_keys('testuser') driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'name').send_keys('testuser')
driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'password').send_keys('test_password') driver.find_element(By.TAG_NAME, 'tf-auth').shadow_root.find_element(By.ID, 'password').send_keys('test_password')

View File

@@ -3,7 +3,7 @@
if [ -z $ANDROID_NDK_ROOT ]; then if [ -z $ANDROID_NDK_ROOT ]; then
ANDROID_NDK_ROOT=~/Android/Sdk/ndk/26.1.10909125 ANDROID_NDK_ROOT=~/Android/Sdk/ndk/26.1.10909125
fi fi
OPENSSL_VERSION=3.3.1 OPENSSL_VERSION=3.3.0
API_LEVEL=24 API_LEVEL=24
@@ -11,13 +11,23 @@ BUILD_DIR=out/openssl_android_build
BUILD_TARGETS="x86_64 x86 arm64-v8a armeabi-v7a" BUILD_TARGETS="x86_64 x86 arm64-v8a armeabi-v7a"
WORK_DIR=out/openssl-${OPENSSL_VERSION}-android rm -rf out/openssl-${OPENSSL_VERSION}
rm -rf $WORK_DIR
cp -arf deps/openssl_src/ $WORK_DIR
if [ ! -d out/openssl-${OPENSSL_VERSION} ]
then
if [ ! -f out/openssl-${OPENSSL_VERSION}.tar.gz ]
then
wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz -O out/openssl-${OPENSSL_VERSION}.tar.gz | exit 128
fi
tar -C out/ -xzf out/openssl-${OPENSSL_VERSION}.tar.gz || exit 128
fi
WORK_DIR=out/openssl-${OPENSSL_VERSION}
##### export ndk directory. Required by openssl-build-scripts #####
export ANDROID_NDK_ROOT export ANDROID_NDK_ROOT
echo ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT echo ANDROID_NDK_ROOT=$ANDROID_NDK_ROOT
##### build-function #####
build_the_thing() { build_the_thing() {
TOOLCHAIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64 TOOLCHAIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64
export PATH=$TOOLCHAIN/$TRIBLE/bin:$TOOLCHAIN/bin:$PATH export PATH=$TOOLCHAIN/$TRIBLE/bin:$TOOLCHAIN/bin:$PATH
@@ -27,7 +37,6 @@ build_the_thing() {
no-asm no-asm
no-async no-async
no-autoerrinit no-autoerrinit
no-autoload-config
no-cmp no-cmp
no-cms no-cms
no-comp no-comp
@@ -37,8 +46,6 @@ build_the_thing() {
no-dsa no-dsa
no-dso no-dso
no-dtls no-dtls
no-dtls1
no-dtls1-method
no-dynamic-engine no-dynamic-engine
no-ec2m no-ec2m
no-egd no-egd
@@ -59,32 +66,28 @@ build_the_thing() {
no-shared no-shared
no-sock no-sock
no-srp no-srp
no-ssl
no-ssl3 no-ssl3
no-ssl-trace no-ssl-trace
no-stdio no-stdio
no-tests no-tests
no-thread-pool no-thread-pool
no-threads no-threads
no-tls1
no-tls1-method
no-trace no-trace
no-ui-console no-ui-console
no-uplink no-uplink
no-whirlpool
no-weak-ssl-ciphers no-weak-ssl-ciphers
no-zlib no-zlib
-Os -Os
-DOPENSSL_SMALL_FOOTPRINT -DOPENSSL_SMALL_FOOTPRINT"
-flto"
pwd pwd
echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \ echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS -fuse-ld=$TOOLCHAIN/bin/ld" && \
./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS && \ ./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS -fuse-ld=$TOOLCHAIN/bin/ld no-tests && \
make -s clean && \ make clean && \
make -s build_generated && \ make build_generated && \
make -s libcrypto.a libssl.a || exit 128 make libcrypto.a libssl.a || exit 128
} }
##### set variables according to build-tagret #####
for build_target in $BUILD_TARGETS for build_target in $BUILD_TARGETS
do do
echo "Building $build_target" echo "Building $build_target"
@@ -125,6 +128,7 @@ do
rm -rf $DESTDIR rm -rf $DESTDIR
build_the_thing build_the_thing
popd popd
#### copy libraries and includes to output-directory #####
echo WORK_DIR=$WORK_DIR echo WORK_DIR=$WORK_DIR
rm -rf deps/openssl/android/$build_target/ rm -rf deps/openssl/android/$build_target/
mkdir -p deps/openssl/android/$build_target/usr/local/include/ mkdir -p deps/openssl/android/$build_target/usr/local/include/

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
OPENSSL_VERSION=3.3.1 OPENSSL_VERSION=3.3.0
API_LEVEL=28 API_LEVEL=28
@@ -8,10 +8,21 @@ BUILD_DIR=out/openssl_ios_build
BUILD_TARGETS="ios64-xcrun iossimulator-xcrun" BUILD_TARGETS="ios64-xcrun iossimulator-xcrun"
WORK_DIR=out/openssl-${OPENSSL_VERSION}-ios rm -rfv out/openssl-${OPENSSL_VERSION}
rm -rf $WORK_DIR
cp -af deps/openssl_src/ $WORK_DIR
if [ ! -d out/openssl-${OPENSSL_VERSION} ]
then
if [ ! -f out/openssl-${OPENSSL_VERSION}.tar.gz ]
then
curl https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz -o out/openssl-${OPENSSL_VERSION}.tar.gz || exit 128
fi
tar -C out/ -xzf out/openssl-${OPENSSL_VERSION}.tar.gz || exit 128
fi
WORK_DIR=out/openssl-${OPENSSL_VERSION}
##### export ndk directory. Required by openssl-build-scripts #####
##### build-function #####
build_the_thing() { build_the_thing() {
export PATH=$TOOLCHAIN/$TRIBLE/bin:$TOOLCHAIN/bin:$PATH export PATH=$TOOLCHAIN/$TRIBLE/bin:$TOOLCHAIN/bin:$PATH
echo $PATH echo $PATH
@@ -22,6 +33,7 @@ build_the_thing() {
make libcrypto.a libssl.a || exit 128 make libcrypto.a libssl.a || exit 128
} }
##### set variables according to build-tagret #####
for build_target in $BUILD_TARGETS for build_target in $BUILD_TARGETS
do do
echo "Building $build_target" echo "Building $build_target"
@@ -48,6 +60,7 @@ do
rm -rf $DESTDIR rm -rf $DESTDIR
build_the_thing build_the_thing
popd popd
#### copy libraries and includes to output-directory #####
echo WORK_DIR=$WORK_DIR echo WORK_DIR=$WORK_DIR
rm -rf deps/openssl/ios/$build_target/ rm -rf deps/openssl/ios/$build_target/
mkdir -p deps/openssl/ios/$build_target/usr/local/include/ mkdir -p deps/openssl/ios/$build_target/usr/local/include/

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
OPENSSL_VERSION=3.3.1 OPENSSL_VERSION=3.3.0
API_LEVEL=24 API_LEVEL=24
@@ -8,61 +8,21 @@ BUILD_DIR=out/openssl_mingw64_build
BUILD_TARGETS="mingw64" BUILD_TARGETS="mingw64"
WORK_DIR=out/openssl-${OPENSSL_VERSION}-mingw64 rm -rfv out/openssl-${OPENSSL_VERSION}
rm -rf $WORK_DIR
cp -arf deps/openssl_src/ $WORK_DIR
if [ ! -d out/openssl-${OPENSSL_VERSION} ]
then
if [ ! -f out/openssl-${OPENSSL_VERSION}.tar.gz ]
then
wget https://www.openssl.org/source/openssl-${OPENSSL_VERSION}.tar.gz -O out/openssl-${OPENSSL_VERSION}.tar.gz || exit 128
fi
tar -C out/ -xzf out/openssl-${OPENSSL_VERSION}.tar.gz || exit 128
fi
WORK_DIR=out/openssl-${OPENSSL_VERSION}
##### build-function #####
build_the_thing() { build_the_thing() {
export GLOBAL_OPTIONS=" export GLOBAL_OPTIONS="no-trace no-asm no-threads no-md2 no-md4 no-dso no-async no-multiblock no-dgram no-filenames no-shared no-ssl3 no-engine no-dynamic-engine no-zlib no-comp no-psk no-idea no-srp no-weak-ssl-ciphers no-dtls no-egd no-tests -Os"
no-apps
no-asm
no-async
no-autoerrinit
no-cmp
no-cms
no-comp
no-deprecated
no-dgram
no-docs
no-dsa
no-dso
no-dtls
no-dynamic-engine
no-ec2m
no-egd
no-engine
no-err
no-filenames
no-gost
no-http
no-idea
no-legacy
no-md2
no-md4
no-module
no-multiblock
no-nextprotoneg
no-ocsp
no-psk
no-shared
no-sock
no-srp
no-ssl-trace
no-ssl3
no-stdio
no-tests
no-thread-pool
no-threads
no-trace
no-ui-console
no-uplink
no-weak-ssl-ciphers
no-zlib
-Os
-ffunction-sections
-fdata-sections
-flto
-DOPENSSL_SMALL_FOOTPRINT"
echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \ echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \
./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS && \ ./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS && \
make clean && \ make clean && \
@@ -70,6 +30,7 @@ build_the_thing() {
make libcrypto.a libssl.a || exit 128 make libcrypto.a libssl.a || exit 128
} }
##### set variables according to build-tagret #####
for build_target in $BUILD_TARGETS for build_target in $BUILD_TARGETS
do do
echo "Building $build_target" echo "Building $build_target"
@@ -86,6 +47,7 @@ do
rm -rf $DESTDIR rm -rf $DESTDIR
build_the_thing build_the_thing
popd popd
#### copy libraries and includes to output-directory #####
echo WORK_DIR=$WORK_DIR echo WORK_DIR=$WORK_DIR
rm -rf deps/openssl/$build_target/ rm -rf deps/openssl/$build_target/
mkdir -p deps/openssl/$build_target/usr/local/include/ mkdir -p deps/openssl/$build_target/usr/local/include/

View File

@@ -1,4 +1,4 @@
VERSION=3.1.4 VERSION=3.1.3
wget https://cdn.jsdelivr.net/gh/lit/dist@$VERSION/all/lit-all.min.js -O deps/lit/lit-all.min.js wget https://cdn.jsdelivr.net/gh/lit/dist@$VERSION/all/lit-all.min.js -O deps/lit/lit-all.min.js
wget https://cdn.jsdelivr.net/gh/lit/dist@$VERSION/all/lit-all.min.js.map -O deps/lit/lit-all.min.js.map wget https://cdn.jsdelivr.net/gh/lit/dist@$VERSION/all/lit-all.min.js.map -O deps/lit/lit-all.min.js.map
cp -fv deps/lit/* apps/blog/ cp -fv deps/lit/* apps/blog/