Compare commits

...

54 Commits

Author SHA1 Message Date
671e3e19ff ssb: Try to stop dates from wrapping into a vertical line of single characters.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m9s
2024-12-31 08:39:02 -05:00
0c394c2e61 ssb: Trying to keep things CSS-ing off the screen.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 23m51s
2024-12-30 07:13:49 -05:00
4ecbb5234c ssb: Tweaking profile card CSS.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 23m31s
2024-12-29 15:51:51 -05:00
98f1700049 ssb: Fiddling with message card and compose CSS some more.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-29 15:42:12 -05:00
2f0b4a0187 ssb: Choose an unread notification that is a bit mire color-agnostic.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 23m39s
2024-12-29 15:15:28 -05:00
f66c6ed0c3 ssb: Fiddle with the placement of the hamburger menu, and fix the tf-compose placeholder text. 2024-12-29 15:11:14 -05:00
5d9785ac2d ssb: Why did this vertical alignment change? I will never know, but I can poke it back.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-29 14:59:12 -05:00
bb97a8cccc ssb: Show connections in the sidebar. Fiddle with tf-user CSS to make it fit.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-29 14:54:29 -05:00
571cf5b5b8 ssb: Color scheme is determined by day/hour/second. Couldn't help it.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 23m19s
2024-12-29 13:55:48 -05:00
1974ed1c03 ssb: We don't have to wait for channel status to finish load.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-29 13:41:57 -05:00
98275f7c87 cleanup: Remove a debug print.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-29 13:34:51 -05:00
eca8726909 ssb: Load and show new messages as they arrive.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-29 13:32:37 -05:00
baf125c450 ssb: Trying to get the sidebar to behave better. Fighting CSS.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 24m25s
2024-12-29 12:44:42 -05:00
efcc710d91 ssb: Let's assume we read our own messages.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 23m21s
2024-12-28 21:31:24 -05:00
5980ee4c86 cleanup: Unneeded #include.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-28 21:13:53 -05:00
db9b7a22c2 core: Report the c-ares version. 2024-12-28 20:38:07 -05:00
5e24d4f322 build: Support Xcode 16.2 on Linux, including cross-compiling OpenSSL.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m11s
2024-12-27 21:32:33 -05:00
2dd32cdce2 ssb: Tweak idle scheduling even more, still. Fixes -t=bench.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m36s
2024-12-27 15:51:33 -05:00
9cddd93dad ssb: Don't schedule duplicate history stream requests for the same account. Changes how we schedule idle work. Let's see if this is better.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m35s
2024-12-27 15:02:52 -05:00
4127898655 ssb: Avoid more work on shutdown. 2024-12-27 14:27:52 -05:00
45d48483d0 ssb: Avoid scheduling idle work while shutting down, so that we shut down sooner. 2024-12-27 14:03:14 -05:00
852c25296a ssb: Better errors for failing to decrypt private messages.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m32s
2024-12-27 13:38:09 -05:00
aea631138e ssb: Fix private messages starting with unread status when there are none. 2024-12-27 13:25:40 -05:00
683fdbb02a ssb: Fix channel status not updating reliably.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 23m2s
2024-12-27 13:23:29 -05:00
c3bbab35e2 ssb: Fix global settings defaults.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-27 13:16:11 -05:00
ba8941046e ssb: Consolidate some redundant connection code.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m32s
2024-12-27 12:27:54 -05:00
d202f4e00d auth: Provide some feedback about valid account names. #92
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 23m8s
2024-12-27 11:39:38 -05:00
42da5d8d32 ssb: Experimenting with generating the w3 css theme colors.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m38s
2024-12-26 22:24:52 -05:00
5af3533598 tests: Clean up some warnings by avoiding in-memory databases. I never got that working well, and it's not representative of actual operation.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m42s
2024-12-26 20:17:17 -05:00
7843168fad ssb: Hook up some more disconnect messaging. 2024-12-26 20:12:04 -05:00
8f51eb63b0 ssb: More experimenting with the unread status strategy.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m35s
2024-12-26 19:36:04 -05:00
855f5f7af4 ssb: Before destroying a connection, show a message on why it is going away in the UI.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 20m6s
2024-12-24 17:23:22 -05:00
c85dd2655c ssb: Strip out the old disconnection debug information.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 20m15s
2024-12-24 16:44:52 -05:00
fb0e4060cd ssb: Instrument some more callbacks for hitches.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-24 16:33:08 -05:00
707b4990a6 build: Not all the toolchains support -Oz. Oh well.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 20m3s
2024-12-24 15:01:09 -05:00
9c8b922069 build: Use all the tricks to make release smaller on all the platforms.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 2m15s
2024-12-24 14:47:33 -05:00
d4b421421d ssb: prettier. 2024-12-24 14:22:24 -05:00
58e9646fa6 ssb: Populate reply information in posts.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m33s
2024-12-24 13:09:27 -05:00
500f172561 ssb: Double down on a loading indicator.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m29s
2024-12-24 12:45:25 -05:00
68f6c90ea4 ssb: Get saving the about cache off of the main load path.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m31s
2024-12-24 12:05:31 -05:00
41e91f2922 ssb: Consolidate global settings helpers.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m28s
2024-12-24 11:16:52 -05:00
999117cfeb clean: Remove a file I sloppily added. 2024-12-24 10:45:47 -05:00
6185df512f build: Add help for armdebug/release, and fix some make help alignment. 2024-12-24 10:39:11 -05:00
0cbf66c007 build: Let's try to artifact the x86_64 + ARM linux executables.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 21m30s
2024-12-24 10:31:09 -05:00
cd378b721d build: Support and test cross-compiling for linux-aarch64.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 22m13s
2024-12-24 10:01:14 -05:00
547d38d1ef ssb: Put the database in (ie, ~/.local/share/tildefriends/db.sqlite) by default. Unless it already exists in the working directory, so that nobody worries they've lost it. #91
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 17m48s
2024-12-23 16:32:30 -05:00
dca56af5b9 build: Let's go static openssl on macos, too.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 18m0s
2024-12-23 14:41:31 -05:00
224442772e build: Let's try a thing. Use our own static openssl libraries built in-tree.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 17m49s
2024-12-23 14:22:27 -05:00
003951fdf7 ssb: Load more context for mentions.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m39s
2024-12-23 13:32:36 -05:00
d51b3da1b4 ssb: Fix channel cycling key shortcut.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-23 13:18:30 -05:00
69f4af84db ssb: Fix weird sidebar sizing.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-12-23 13:07:33 -05:00
771759b252 ssb: Show drafts in the sidebar.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m22s
2024-12-23 12:25:52 -05:00
20c7a71db6 ssb: Add a checkbox to reply in a new thread. #47 Also, it's crab time. I'm sorry it took me so long.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m53s
2024-12-23 12:06:32 -05:00
8475ee0985 build: Let's start work on 0.0.27. 2024-12-23 11:23:51 -05:00
32 changed files with 1271 additions and 826 deletions

View File

@ -24,15 +24,14 @@ jobs:
uses: android-actions/setup-android@v3 uses: android-actions/setup-android@v3
with: with:
packages: 'tools platform-tools build-tools;34.0.0 platforms;android-34 ndk;26.3.11579264' packages: 'tools platform-tools build-tools;34.0.0 platforms;android-34 ndk;26.3.11579264'
- run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64 libgpgme11 - run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64 libgpgme11 gcc-aarch64-linux-gnu
- run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all docs - run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all docs
- run: docker build . - run: docker build .
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
with: with:
path: out/TildeFriends-release.fdroid.apk path: |
- uses: actions/upload-artifact@v3 out/TildeFriends-release.fdroid.apk
with: out/winrelease/tildefriends.exe
path: out/winrelease/tildefriends.exe out/tildefriends-x86_64.AppImage
- uses: actions/upload-artifact@v3 out/release/tildefriends.standalone
with: out/armrelease/tildefriends.standalone
path: out/tildefriends-x86_64.AppImage

View File

@ -16,8 +16,8 @@ MAKEFLAGS += --no-builtin-rules
## LD := Linker. ## LD := Linker.
## ANDROID_SDK := Path to the Android SDK. ## ANDROID_SDK := Path to the Android SDK.
VERSION_CODE := 31 VERSION_CODE := 32
VERSION_NUMBER := 0.0.26 VERSION_NUMBER := 0.0.27-wip
VERSION_NAME := This program kills fascists. VERSION_NAME := This program kills fascists.
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3470200.zip SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3470200.zip
@ -34,6 +34,7 @@ ANDROID_SDK ?= ~/Android/Sdk
BUNDLETOOL = out/bundletool.jar BUNDLETOOL = out/bundletool.jar
HAVE_WIN := 0 HAVE_WIN := 0
HAVE_CROSS_AARCH64 := 0
export SOURCE_DATE_EPOCH=1 export SOURCE_DATE_EPOCH=1
export TZ=UTC export TZ=UTC
@ -45,6 +46,9 @@ BUILD_TYPES := debug release
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1,0) HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1,0)
HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1,0) HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1,0)
HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1,0) HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1,0)
ifneq ($(UNAME_M),aarch64)
HAVE_CROSS_AARCH64 = $(if $(shell which aarch64-linux-gnu-gcc),1,0)
endif
else ifeq ($(UNAME_S),Haiku) else ifeq ($(UNAME_S),Haiku)
BUILD_TYPES := debug release BUILD_TYPES := debug release
CFLAGS += -Dstatic_assert=_Static_assert CFLAGS += -Dstatic_assert=_Static_assert
@ -125,6 +129,13 @@ ifeq ($(HAVE_WIN),1)
BUILD_TYPES += windebug winrelease BUILD_TYPES += windebug winrelease
endif endif
AARCH64_TARGETS := \
out/armdebug/tildefriends \
out/armrelease/tildefriends
ifeq ($(HAVE_CROSS_AARCH64),1)
BUILD_TYPES += armdebug armrelease
endif
LINUX_TARGETS := \ LINUX_TARGETS := \
out/debug/tildefriends \ out/debug/tildefriends \
out/release/tildefriends out/release/tildefriends
@ -149,6 +160,9 @@ all: $(IOS_APPS) \
out/tildefriends-iossimdebug.app/tildefriends \ out/tildefriends-iossimdebug.app/tildefriends \
out/tildefriends-iossimrelease.app/tildefriends out/tildefriends-iossimrelease.app/tildefriends
endif endif
ifeq ($(HAVE_CROSS_AARCH64),1)
all: out/armrelease/tildefriends.standalone
endif
DEBUG_TARGETS := \ DEBUG_TARGETS := \
out/debug/tildefriends \ out/debug/tildefriends \
@ -159,7 +173,8 @@ DEBUG_TARGETS := \
out/androiddebug/tildefriends \ out/androiddebug/tildefriends \
out/androiddebug-armv7a/tildefriends \ out/androiddebug-armv7a/tildefriends \
out/androiddebug-x86_64/tildefriends \ out/androiddebug-x86_64/tildefriends \
out/androiddebug-x86/tildefriends out/androiddebug-x86/tildefriends \
out/armdebug/tildefriends
RELEASE_TARGETS := \ RELEASE_TARGETS := \
out/release/tildefriends \ out/release/tildefriends \
out/winrelease/tildefriends.exe \ out/winrelease/tildefriends.exe \
@ -169,7 +184,8 @@ RELEASE_TARGETS := \
out/androidrelease/tildefriends \ out/androidrelease/tildefriends \
out/androidrelease-armv7a/tildefriends \ out/androidrelease-armv7a/tildefriends \
out/androidrelease-x86_64/tildefriends \ out/androidrelease-x86_64/tildefriends \
out/androidrelease-x86/tildefriends out/androidrelease-x86/tildefriends \
out/armrelease/tildefriends
ALL_TARGETS = $(DEBUG_TARGETS) $(RELEASE_TARGETS) ALL_TARGETS = $(DEBUG_TARGETS) $(RELEASE_TARGETS)
ANDROID_RELEASE_TARGETS := $(filter-out $(DEBUG_TARGETS),$(ANDROID_TARGETS)) 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))
@ -192,11 +208,14 @@ $(ANDROID_TARGETS): CFLAGS += \
-Wno-unknown-warning-option -Wno-unknown-warning-option
$(ANDROID_TARGETS): LDFLAGS += --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC $(ANDROID_TARGETS): LDFLAGS += --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC
$(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og $(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og
$(DEBUG_TARGETS): LDFLAGS += -Og
$(RELEASE_TARGETS): CFLAGS += \ $(RELEASE_TARGETS): CFLAGS += \
-DNDEBUG \ -DNDEBUG \
-flto -flto
$(NONANDROID_RELEASE_TARGETS): CFLAGS += -O3
$(ANDROID_RELEASE_TARGETS): CFLAGS += -Oz $(ANDROID_RELEASE_TARGETS): CFLAGS += -Oz
$(ANDROID_RELEASE_TARGETS): LDFLAGS += -Oz
$(NONANDROID_RELEASE_TARGETS): CFLAGS += -Os
$(NONANDROID_RELEASE_TARGETS): LDFLAGS += -Os
$(WINDOWS_TARGETS): CC = x86_64-w64-mingw32-gcc-win32 $(WINDOWS_TARGETS): CC = x86_64-w64-mingw32-gcc-win32
$(WINDOWS_TARGETS): AS = $(CC) $(WINDOWS_TARGETS): AS = $(CC)
$(WINDOWS_TARGETS): CFLAGS += \ $(WINDOWS_TARGETS): CFLAGS += \
@ -208,6 +227,10 @@ $(WINDOWS_TARGETS): LDFLAGS += \
-static \ -static \
-lm \ -lm \
-Ldeps/openssl/mingw64/usr/local/lib -Ldeps/openssl/mingw64/usr/local/lib
$(AARCH64_TARGETS): CC = aarch64-linux-gnu-gcc
$(AARCH64_TARGETS): AS = $(CC)
$(AARCH64_TARGETS): CFLAGS += -Ideps/openssl/Linux/aarch64/usr/local/include
$(AARCH64_TARGETS): LDFLAGS += -Ldeps/openssl/Linux/aarch64/usr/local/lib
ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_S),Darwin)
$(MACOS_TARGETS): CC = xcrun clang $(MACOS_TARGETS): CC = xcrun clang
$(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path) $(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
@ -215,7 +238,8 @@ $(IOS_TARGETS): CC = xcrun --sdk iphoneos clang -isysroot $(IOS_SYSROOT) -arch a
$(IOSSIM_TARGETS): IOSSIM_SYSROOT := $(shell xcrun --sdk iphonesimulator --show-sdk-path) $(IOSSIM_TARGETS): IOSSIM_SYSROOT := $(shell xcrun --sdk iphonesimulator --show-sdk-path)
$(IOSSIM_TARGETS): CC = xcrun --sdk iphonesimulator clang -isysroot $(IOSSIM_SYSROOT) -arch x86_64 $(IOSSIM_TARGETS): CC = xcrun --sdk iphonesimulator clang -isysroot $(IOSSIM_SYSROOT) -arch x86_64
else ifeq ($(UNAME_S),Linux) else ifeq ($(UNAME_S),Linux)
$(IOS_TARGETS): IOS_SYSROOT := deps/iPhoneOS17.0.sdk $(IOS_TARGETS): CFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk -arch arm64
$(IOS_TARGETS): LDFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk
$(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang $(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang
endif endif
$(ANDROID_X86_64_TARGETS): ANDROID_NDK_TARGET_TRIPLE := x86_64-linux-android $(ANDROID_X86_64_TARGETS): ANDROID_NDK_TARGET_TRIPLE := x86_64-linux-android
@ -238,14 +262,23 @@ $(ANDROID_X86_64_TARGETS): CFLAGS += -Ideps/openssl/android/x86_64/usr/local/inc
$(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 $(DEADSTRIP_TARGETS): LDFLAGS += -Wl,--gc-sections
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=9.0 -Ideps/openssl/ios/ios64-xcrun/usr/local/include $(IOS_TARGETS): CFLAGS += -miphoneos-version-min=9.0
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=9.0 -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib $(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=9.0
ifeq ($(UNAME_S),Darwin)
$(IOS_TARGETS): CFLAGS += -Ideps/openssl/ios/ios64-xcrun/usr/local/include
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib
else
$(IOS_TARGETS): CFLAGS += -Ideps/openssl/$(UNAME_S)/ios64-cross/usr/local/include
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/$(UNAME_S)/ios64-cross/usr/local/lib
endif
$(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include $(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include
$(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib $(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib
$(LINUX_TARGETS) $(MACOS_TARGETS): CFLAGS += -Ideps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/include
$(LINUX_TARGETS) $(MACOS_TARGETS): LDFLAGS += -Ldeps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib
ifeq ($(UNAME_M),x86_64) ifeq ($(UNAME_M),x86_64)
ifeq ($(UNAME_S),Linux) ifeq ($(UNAME_S),Linux)
all: appimage all: appimage out/release/tildefriends.standalone
endif endif
ifneq ($(UNAME_S),Haiku) ifneq ($(UNAME_S),Haiku)
out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common
@ -260,7 +293,7 @@ endif
get_objs = \ get_objs = \
$(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \ $(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \
$(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \ $(foreach build_type,debug release armdebug armrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
$(foreach build_type,windebug winrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_win))))) \ $(foreach build_type,windebug winrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_win))))) \
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androiddebug-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \ $(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androiddebug-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \ $(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
@ -725,7 +758,7 @@ $(MINIUNZIP_OBJS): CFLAGS += \
LDFLAGS += \ LDFLAGS += \
-pthread \ -pthread \
-lm -lm
$(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \ $(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS): LDFLAGS += \
-lssl \ -lssl \
-lcrypto -lcrypto
ifneq ($(UNAME_S),Haiku) ifneq ($(UNAME_S),Haiku)
@ -765,6 +798,8 @@ $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
## ##
debug: ## Build a debug executable for the current platform. debug: ## Build a debug executable for the current platform.
release: ## Build a release executable for the current platform. release: ## Build a release executable for the current platform.
armdebug: ## Cross-compile aarch64 debug on Linux.
armrelease: ## Cross-compile aarch64 release on Linux.
all: $(BUILD_TYPES) ## Build all targets that appear possible to build on this machine. all: $(BUILD_TYPES) ## Build all targets that appear possible to build on this machine.
unix: debug release ## Build all UNIX targets. unix: debug release ## Build all UNIX targets.
win: windebug winrelease ## Build all Windows targets. win: windebug winrelease ## Build all Windows targets.
@ -1106,6 +1141,42 @@ $(ANDROID_DEPS):
+@ANDROID_NDK_ROOT=$(ANDROID_NDK) tools/ssl-android +@ANDROID_NDK_ROOT=$(ANDROID_NDK) tools/ssl-android
$(filter $(BUILD_DIR)/android%,$(APP_OBJS)): | $(ANDROID_DEPS) $(filter $(BUILD_DIR)/android%,$(APP_OBJS)): | $(ANDROID_DEPS)
ifeq ($(UNAME_S),Linux)
LOCAL_DEPS := deps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@OPTIONS=-flto tools/ssl-local
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
ifeq ($(HAVE_CROSS_AARCH64),1)
LOCAL_DEPS := deps/openssl/$(UNAME_S)/aarch64/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@OPTIONS="--cross-compile-prefix=aarch64-linux-gnu- -flto" BUILD_TARGET=aarch64 tools/ssl-local
$(filter $(BUILD_DIR)/armdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/armrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
ifeq ($(HAVE_LINUX_IOS),1)
LOCAL_DEPS := deps/openssl/$(UNAME_S)/ios64-cross/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@PATH=deps/ios_toolchain/target/bin:$$PATH \
BUILD_TARGET=ios64-cross \
SSL_TARGET=ios64-cross \
CROSS_COMPILE=../../deps/ios_toolchain/target/bin/arm-apple-darwin11- \
CROSS_TOP=../../deps/ios_toolchain/target \
CROSS_SDK=iPhoneOS18.2.sdk \
CC=clang \
OPTIONS=-miphoneos-version-min=9.0 \
tools/ssl-local
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
endif
ifeq ($(UNAME_S),Darwin)
LOCAL_DEPS := deps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
$(LOCAL_DEPS):
+@OPTIONS=-flto tools/ssl-local
$(filter $(BUILD_DIR)/macosdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/macosrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
endif
ifeq ($(HAVE_WIN),1) ifeq ($(HAVE_WIN),1)
WINDOWS_DEPS := deps/openssl/mingw64/usr/local/lib/libssl.a WINDOWS_DEPS := deps/openssl/mingw64/usr/local/lib/libssl.a
$(WINDOWS_DEPS): $(WINDOWS_DEPS):
@ -1198,7 +1269,7 @@ tarball: ## Build an all-inclusive source tarball (.tar.xz).
.PHONY: tarball .PHONY: tarball
dist: ## Build versions of all distributables for release. dist: ## Build versions of all distributables for release.
dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk appimage tarball dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk appimage tarball out/release/tildefriends.standalone $(if $(HAVE_CROSS_AARCH64), out/armrelease/tildefriends.standalone)
@mkdir -p dist/ @mkdir -p dist/
@echo "[cp] tildefriends-$(VERSION_NUMBER).tar.xz" @echo "[cp] tildefriends-$(VERSION_NUMBER).tar.xz"
@cp out/tildefriends-$(VERSION_NUMBER).tar.xz dist/tildefriends-$(VERSION_NUMBER).tar.xz @cp out/tildefriends-$(VERSION_NUMBER).tar.xz dist/tildefriends-$(VERSION_NUMBER).tar.xz
@ -1216,6 +1287,10 @@ dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefrien
@cp out/TildeFriends-release.fdroid.apk dist/TildeFriends-$(VERSION_NUMBER).fdroid.apk @cp out/TildeFriends-release.fdroid.apk dist/TildeFriends-$(VERSION_NUMBER).fdroid.apk
@echo "[cp] TildeFriends-x86_64-$(VERSION_NUMBER).AppImage" @echo "[cp] TildeFriends-x86_64-$(VERSION_NUMBER).AppImage"
@cp out/tildefriends-x86_64.AppImage dist/TildeFriends-x86_64-$(VERSION_NUMBER).AppImage @cp out/tildefriends-x86_64.AppImage dist/TildeFriends-x86_64-$(VERSION_NUMBER).AppImage
@echo "[cp] tildefriends-linux-$(UNAME_M)-$(VERSION_NUMBER)"
@cp out/release/tildefriends.standalone dist/tildefriends-linux-$(UNAME_M)-$(VERSION_NUMBER)
@test $(HAVE_CROSS_AARCH64) && echo "[cp] tildefriends-linux-aarch64-$(VERSION_NUMBER)"
@test $(HAVE_CROSS_AARCH64) && cp out/armrelease/tildefriends.standalone dist/tildefriends-linux-aarch64-$(VERSION_NUMBER)
.PHONY: dist .PHONY: dist
dist-test: dist ## Exercise some built distributable files, making sure they work as intended. dist-test: dist ## Exercise some built distributable files, making sure they work as intended.
@ -1258,7 +1333,7 @@ help: ## Display this help message.
/^##/ { sub(/^## ?/, ""); print $$0 } \ /^##/ { sub(/^## ?/, ""); print $$0 } \
/^[[:alnum:]-]+:.*##/ { \ /^[[:alnum:]-]+:.*##/ { \
sub(/:.*##\s?/, ":"); \ sub(/:.*##\s?/, ":"); \
printf " %s%-20s%s %s%s%s\n", G, $$1, R, O, $$2, R \ printf " %s%-21s%s %s%s%s\n", G, $$1, R, O, $$2, R \
} \ } \
' < $(filter-out %.d,$(MAKEFILE_LIST)) ' < $(filter-out %.d,$(MAKEFILE_LIST))
@echo "" # Blank line. @echo "" # Blank line.

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🐌", "emoji": "🦀",
"previous": "&q/1uGp0jMvsYGW7Gj8E33kf6UFo/uNYDXg3zo1sVKQg=.sha256" "previous": "&bBoMW+AjErDfa483Mg3+h1L25xfDDeVSpcfD9WAwL3U=.sha256"
} }

View File

@ -7,7 +7,6 @@ class TfElement extends LitElement {
return { return {
whoami: {type: String}, whoami: {type: String},
hash: {type: String}, hash: {type: String},
unread: {type: Array},
tab: {type: String}, tab: {type: String},
broadcasts: {type: Array}, broadcasts: {type: Array},
connections: {type: Array}, connections: {type: Array},
@ -28,7 +27,6 @@ class TfElement extends LitElement {
super(); super();
let self = this; let self = this;
this.hash = '#'; this.hash = '#';
this.unread = [];
this.tab = 'news'; this.tab = 'news';
this.broadcasts = []; this.broadcasts = [];
this.connections = []; this.connections = [];
@ -112,26 +110,24 @@ class TfElement extends LitElement {
keydown(event) { keydown(event) {
if (event.altKey && event.key == 'ArrowUp') { if (event.altKey && event.key == 'ArrowUp') {
this.next_channel(1); this.next_channel(-1);
event.preventDefault(); event.preventDefault();
} else if (event.altKey && event.key == 'ArrowDown') { } else if (event.altKey && event.key == 'ArrowDown') {
this.next_channel(-1); this.next_channel(1);
event.preventDefault(); event.preventDefault();
} }
} }
next_channel(delta) { next_channel(delta) {
let channel_names = ['', '@'].concat(this.channels); let channel_names = ['', '@', '🔐', ...this.channels.map((x) => '#' + x)];
let index = channel_names.indexOf(this.hash.substring(1)); let index = channel_names.indexOf(this.hash.substring(1));
if (index != -1) { index = index != -1 ? index + delta : 0;
index += delta; tfrpc.rpc.setHash(
this.set_hash( '#' +
'#' + encodeURIComponent(
encodeURIComponent( channel_names[(index + channel_names.length) % channel_names.length]
channel_names[(index + channel_names.length) % channel_names.length] )
) );
);
}
} }
set_hash(hash) { set_hash(hash) {
@ -150,6 +146,7 @@ class TfElement extends LitElement {
async fetch_about(ids, users) { async fetch_about(ids, users) {
const k_cache_version = 1; const k_cache_version = 1;
let cache = await tfrpc.rpc.databaseGet('about'); let cache = await tfrpc.rpc.databaseGet('about');
let original_cache = cache;
cache = cache ? JSON.parse(cache) : {}; cache = cache ? JSON.parse(cache) : {};
if (cache.version !== k_cache_version) { if (cache.version !== k_cache_version) {
cache = { cache = {
@ -215,7 +212,13 @@ class TfElement extends LitElement {
} }
} }
cache.last_row_id = max_row_id; cache.last_row_id = max_row_id;
await tfrpc.rpc.databaseSet('about', JSON.stringify(cache)); let new_cache = JSON.stringify(cache);
if (new_cache !== original_cache) {
let start_time = new Date();
tfrpc.rpc.databaseSet('about', new_cache).then(function () {
console.log('saving about took', (new Date() - start_time) / 1000);
});
}
users = users || {}; users = users || {};
for (let id of Object.keys(cache.about)) { for (let id of Object.keys(cache.about)) {
users[id] = Object.assign(users[id] || {}, cache.about[id]); users[id] = Object.assign(users[id] || {}, cache.about[id]);
@ -234,17 +237,13 @@ class TfElement extends LitElement {
[JSON.stringify(this.following), id] [JSON.stringify(this.following), id]
); );
for (let message of messages) { for (let message of messages) {
if (message.author == this.whoami) { if (
let content = JSON.parse(message.content); message.author == this.whoami &&
if (content?.type == 'channel') { JSON.parse(message.content)?.type == 'channel'
this.load_channels(); ) {
} this.load_channels();
} }
} }
if (messages && messages.length) {
this.unread = [...this.unread, ...messages];
this.unread = this.unread.slice(this.unread.length - 1024);
}
this.schedule_load_channels_latest(); this.schedule_load_channels_latest();
} }
@ -308,20 +307,29 @@ class TfElement extends LitElement {
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
JOIN json_each(?2) AS following ON messages.author = following.value JOIN json_each(?2) AS following ON messages.author = following.value
WHERE messages.content ->> 'type' = 'post' AND messages.content ->> 'root' IS NULL WHERE
messages.content ->> 'type' = 'post' AND
messages.content ->> 'root' IS NULL AND
messages.author != ?4
GROUP by channel GROUP by channel
UNION UNION
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
JOIN json_each(?2) AS following ON messages.author = following.value JOIN json_each(?2) AS following ON messages.author = following.value
WHERE
messages.content ->> 'type' = 'post' AND
messages.content ->> 'root' IS NULL AND
messages.author != ?4
UNION UNION
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3) SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
JOIN messages ON messages.rowid = messages_fts.rowid JOIN messages ON messages.rowid = messages_fts.rowid
JOIN json_each(?2) AS following ON messages.author = following.value JOIN json_each(?2) AS following ON messages.author = following.value
WHERE messages.author != ?4
`, `,
[ [
JSON.stringify(this.channels), JSON.stringify(this.channels),
JSON.stringify(following), JSON.stringify(following),
'"' + this.whoami.replace('"', '""') + '"', '"' + this.whoami.replace('"', '""') + '"',
this.whoami,
] ]
); );
this.channels_latest = Object.fromEntries( this.channels_latest = Object.fromEntries(
@ -349,14 +357,16 @@ class TfElement extends LitElement {
schedule_load_channels_latest() { schedule_load_channels_latest() {
if (!this.loading_channels_latest) { if (!this.loading_channels_latest) {
this.shadowRoot.getElementById('tf-tab-news')?.load_latest();
this.load_channels_latest(this.following); this.load_channels_latest(this.following);
} else if (!this.loading_channels_latest_scheduled) { } else if (!this.loading_channels_latest_scheduled) {
this.loading_channels_latest_scheduled++; this.loading_channels_latest_scheduled++;
setTimeout(this._schedule_load_channels_latest_timer, 5000); setTimeout(this._schedule_load_channels_latest_timer.bind(this), 5000);
} }
} }
async load() { async load() {
let start_time = new Date();
let whoami = this.whoami; let whoami = this.whoami;
let following = await tfrpc.rpc.following([whoami], 2); let following = await tfrpc.rpc.following([whoami], 2);
let users = {}; let users = {};
@ -370,11 +380,10 @@ class TfElement extends LitElement {
}; };
by_count.push({count: v.of, id: id}); by_count.push({count: v.of, id: id});
} }
let channels_latest = this.load_channels_latest(Object.keys(following)); this.load_channels_latest(Object.keys(following));
this.channels_unread = JSON.parse( this.channels_unread = JSON.parse(
(await tfrpc.rpc.databaseGet('unread')) ?? '{}' (await tfrpc.rpc.databaseGet('unread')) ?? '{}'
); );
let start_time = new Date();
users = await this.fetch_about(Object.keys(following).sort(), users); users = await this.fetch_about(Object.keys(following).sort(), users);
console.log( console.log(
'about took', 'about took',
@ -383,11 +392,9 @@ class TfElement extends LitElement {
Object.keys(users).length, Object.keys(users).length,
'users' 'users'
); );
start_time = new Date();
await channels_latest;
this.following = Object.keys(following); this.following = Object.keys(following);
this.users = users; this.users = users;
console.log(`load finished ${whoami} => ${this.whoami}`); console.log(`load finished ${whoami} => ${this.whoami} in ${(new Date() - start_time) / 1000}`);
this.whoami = whoami; this.whoami = whoami;
this.loaded = whoami; this.loaded = whoami;
} }
@ -435,13 +442,12 @@ class TfElement extends LitElement {
whoami=${this.whoami} whoami=${this.whoami}
.users=${this.users} .users=${this.users}
hash=${this.hash} hash=${this.hash}
.unread=${this.unread}
@refresh=${() => (this.unread = [])}
?loading=${this.loading} ?loading=${this.loading}
.channels=${this.channels} .channels=${this.channels}
.channels_latest=${this.channels_latest} .channels_latest=${this.channels_latest}
.channels_unread=${this.channels_unread} .channels_unread=${this.channels_unread}
@channelsetunread=${this.channel_set_unread} @channelsetunread=${this.channel_set_unread}
.connections=${this.connections}
></tf-tab-news> ></tf-tab-news>
`; `;
} else if (this.tab === 'connections') { } else if (this.tab === 'connections') {
@ -512,7 +518,7 @@ class TfElement extends LitElement {
let tabs = html` let tabs = html`
<div <div
class="w3-bar w3-theme-l1" class="w3-bar w3-theme-l1"
style="position: sticky; top: 0; z-index: 10" style="position: static; top: 0; z-index: 10"
> >
<button <button
class="w3-bar-item w3-button w3-circle w3-ripple" class="w3-bar-item w3-button w3-circle w3-ripple"
@ -538,22 +544,24 @@ class TfElement extends LitElement {
)} )}
</div> </div>
`; `;
let contents = !this.loaded let contents =
? this.loading !this.loaded || this.loading
? html`<div ? html`<div
class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge" class="w3-display-middle w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge w3-xlarge"
> >
Loading... <span class="w3-spin" style="display: inline-block">🦀</span>
</div> Loading...
${this.render_tab()}` </div>`
: html`<div>Select or create an identity.</div>` : this.render_tab();
: this.render_tab();
return html` return html`
<div <div
style="width: 100vw; min-height: 100vh; height: 100%" style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
class="w3-theme-dark" class="w3-theme-dark"
> >
${tabs} ${contents} <div style="flex: 0 0">${tabs}</div>
<div style="flex: 1 1; overflow: scroll; contain: layout">
${contents}
</div>
</div> </div>
`; `;
} }

View File

@ -15,6 +15,7 @@ class TfComposeElement extends LitElement {
drafts: {type: Object}, drafts: {type: Object},
author: {type: String}, author: {type: String},
channel: {type: String}, channel: {type: String},
new_thread: {type: Boolean},
}; };
} }
@ -28,6 +29,7 @@ class TfComposeElement extends LitElement {
this.apps = undefined; this.apps = undefined;
this.drafts = {}; this.drafts = {};
this.author = undefined; this.author = undefined;
this.new_thread = false;
} }
process_text(text) { process_text(text) {
@ -200,9 +202,23 @@ class TfComposeElement extends LitElement {
channel: this.channel, channel: this.channel,
}; };
if (this.root || this.branch) { if (this.root || this.branch) {
message.root = this.root; message.root = this.new_thread ? (this.branch ?? this.root) : this.root;
message.branch = this.branch; message.branch = this.branch;
} }
let reply = Object.fromEntries(
(
await tfrpc.rpc.query(
`
SELECT messages.id, messages.author FROM messages
JOIN json_each(?) AS refs ON messages.id = refs.value
`,
[JSON.stringify([this.root, this.branch])]
)
).map((row) => [row.id, row.author])
);
if (Object.keys(reply).length) {
message.reply = reply;
}
if (Object.values(draft.mentions || {}).length) { if (Object.values(draft.mentions || {}).length) {
message.mentions = Object.values(draft.mentions); message.mentions = Object.values(draft.mentions);
} }
@ -469,6 +485,20 @@ class TfComposeElement extends LitElement {
} }
} }
render_new_thread() {
let self = this;
if (
this.root !== undefined &&
this.branch !== undefined &&
this.root != this.branch
) {
return html`
<input type="checkbox" class="w3-check w3-theme-d1" id="new_thread" @change=${() => (self.new_thread = !self.new_thread)} ?checked=${self.new_thread}></input>
<label for="new_thread">New Thread</label>
`;
}
}
get_draft() { get_draft() {
return this.drafts[this.branch || ''] || {}; return this.drafts[this.branch || ''] || {};
} }
@ -533,14 +563,24 @@ class TfComposeElement extends LitElement {
🔐 🔐
</button>`; </button>`;
let result = html` let result = html`
<style>
.w3-input:empty::before {
content: attr(placeholder);
}
.w3-input:empty:focus::before {
content: '';
}
</style>
<div <div
class="w3-card-4 w3-theme-d4 w3-padding-small" class="w3-card-4 w3-theme-d4 w3-padding w3-margin-top w3-margin-bottom"
style="box-sizing: border-box" style="box-sizing: border-box"
> >
${this.channel !== undefined <header class="w3-container">
? html`<p>To #${this.channel}:</p>` ${this.channel !== undefined
: undefined} ? html`<p>To #${this.channel}:</p>`
${this.render_encrypt()} : undefined}
${this.render_encrypt()}
</header>
<div class="w3-container w3-padding-small"> <div class="w3-container w3-padding-small">
<div class="w3-half"> <div class="w3-half">
<span <span
@ -554,25 +594,28 @@ class TfComposeElement extends LitElement {
.innerText=${live(draft.text ?? '')} .innerText=${live(draft.text ?? '')}
></span> ></span>
</div> </div>
<div class="w3-half w3-padding"> <div class="w3-half">
${content_warning} ${content_warning}
<div id="preview"></div> <p id="preview"></p>
</div> </div>
</div> </div>
${Object.values(draft.mentions || {}).map((x) => ${Object.values(draft.mentions || {}).map((x) =>
self.render_mention(x) self.render_mention(x)
)} )}
${this.render_attach_app()} ${this.render_content_warning()} <footer class="w3-container">
<button class="w3-button w3-theme-d1" id="submit" @click=${this.submit}> ${this.render_attach_app()} ${this.render_content_warning()}
Submit ${this.render_new_thread()}
</button> <button class="w3-button w3-theme-d1" id="submit" @click=${this.submit}>
<button class="w3-button w3-theme-d1" @click=${this.attach}> Submit
Attach </button>
</button> <button class="w3-button w3-theme-d1" @click=${this.attach}>
${this.render_attach_app_button()} ${encrypt} Attach
<button class="w3-button w3-theme-d1" @click=${this.discard}> </button>
Discard ${this.render_attach_app_button()} ${encrypt}
</button> <button class="w3-button w3-theme-d1" @click=${this.discard}>
Discard
</button>
</footer>
</div> </div>
`; `;
return result; return result;

View File

@ -320,6 +320,8 @@ ${JSON.stringify(mention, null, 2)}</pre
></tf-message>` ></tf-message>`
)}`; )}`;
} }
} else {
return undefined;
} }
} }
@ -424,10 +426,10 @@ ${JSON.stringify(mention, null, 2)}</pre
return html` return html`
<div <div
class="w3-card-4 ${class_background} w3-border-theme" class="w3-card-4 ${class_background} w3-border-theme"
style="margin-top: 8px; padding: 16px; display: inline-block; overflow-wrap: anywhere" style="margin-top: 8px; padding: 16px; display: inline-block; overflow: scroll; overflow-wrap: anywhere; display: block; max-width: 100%"
> >
<tf-user id=${self.message.author} .users=${self.users}></tf-user> <tf-user id=${self.message.author} .users=${self.users}></tf-user>
<span style="padding-right: 8px" <span style="padding-right: 8px; text-wrap: nowrap"
><a tfarget="_top" href=${'#' + encodeURIComponent(self.message.id)} ><a tfarget="_top" href=${'#' + encodeURIComponent(self.message.id)}
>%</a >%</a
> >
@ -454,7 +456,7 @@ ${JSON.stringify(mention, null, 2)}</pre
if (this.message?.type === 'contact_group') { if (this.message?.type === 'contact_group') {
return html` <div return html` <div
class="w3-card-4 ${class_background} w3-border-theme" class="w3-card-4 ${class_background} w3-border-theme"
style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere" style="margin-top: 8px; padding: 16px; overflow: scroll; overflow-wrap: anywhere; display: block; max-width: 100%"
> >
${this.message.messages.map( ${this.message.messages.map(
(x) => (x) =>
@ -472,7 +474,7 @@ ${JSON.stringify(mention, null, 2)}</pre
} else if (this.message.placeholder) { } else if (this.message.placeholder) {
return html` <div return html` <div
class="w3-card-4 ${class_background} w3-border-theme" class="w3-card-4 ${class_background} w3-border-theme"
style="margin-top: 8px; padding: 16px; overflow-wrap: anywhere" style="margin-top: 8px; padding: 16px; overflow: scroll; overflow-wrap: anywhere; display: block; max-width: 100%"
> >
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)} <a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
>${this.message.id}</a >${this.message.id}</a
@ -622,13 +624,13 @@ ${JSON.stringify(content, null, 2)}</pre
</style> </style>
<div <div
class="w3-card-4 ${class_background} w3-border-theme" class="w3-card-4 ${class_background} w3-border-theme"
style="margin-top: 8px; padding: 16px" style="margin-top: 8px; padding: 16px; overflow: scroll; overflow-wrap: anywhere; display: block; max-width: 100%"
> >
<div style="display: flex; flex-direction: row"> <div style="display: flex; flex-direction: row">
<tf-user id=${this.message.author} .users=${this.users}></tf-user> <tf-user id=${this.message.author} .users=${this.users}></tf-user>
${is_encrypted} ${is_encrypted}
<span style="flex: 1"></span> <span style="flex: 1"></span>
<span style="padding-right: 8px" <span style="padding-right: 8px; text-wrap: nowrap"
><a ><a
target="_top" target="_top"
href=${'#' + encodeURIComponent(self.message.id)} href=${'#' + encodeURIComponent(self.message.id)}
@ -639,7 +641,7 @@ ${JSON.stringify(content, null, 2)}</pre
<span>${raw_button}</span> <span>${raw_button}</span>
</div> </div>
${payload} ${this.render_votes()} ${payload} ${this.render_votes()}
<p> <footer class="w3-container">
${reply} ${reply}
<button class="w3-button w3-theme-d1" @click=${this.react}> <button class="w3-button w3-theme-d1" @click=${this.react}>
React React
@ -654,8 +656,8 @@ ${JSON.stringify(content, null, 2)}</pre
</button> </button>
` `
: undefined} : undefined}
</p> ${this.render_children()}
${this.render_children()} </footer>
</div> </div>
`; `;
} else if (content.type === 'issue') { } else if (content.type === 'issue') {
@ -679,13 +681,13 @@ ${JSON.stringify(content, null, 2)}</pre
</style> </style>
<div <div
class="w3-card-4 ${class_background} w3-border-theme" class="w3-card-4 ${class_background} w3-border-theme"
style="margin-top: 8px; padding: 16px" style="margin-top: 8px; padding: 16px; overflow: scroll; overflow-wrap: anywhere; display: block; max-width: 100%"
> >
<div style="display: flex; flex-direction: row"> <div style="display: flex; flex-direction: row">
<tf-user id=${this.message.author} .users=${this.users}></tf-user> <tf-user id=${this.message.author} .users=${this.users}></tf-user>
${is_encrypted} ${is_encrypted}
<span style="flex: 1"></span> <span style="flex: 1"></span>
<span style="padding-right: 8px" <span style="padding-right: 8px; text-wrap: nowrap"
><a ><a
target="_top" target="_top"
href=${'#' + encodeURIComponent(self.message.id)} href=${'#' + encodeURIComponent(self.message.id)}
@ -696,12 +698,12 @@ ${JSON.stringify(content, null, 2)}</pre
<span>${raw_button}</span> <span>${raw_button}</span>
</div> </div>
${content.text} ${this.render_votes()} ${content.text} ${this.render_votes()}
<p> <footer class="w3-container">
<button class="w3-button w3-theme-d1" @click=${this.react}> <button class="w3-button w3-theme-d1" @click=${this.react}>
React React
</button> </button>
</p> ${this.render_children()}
${this.render_children()} </footer>
</div> </div>
`; `;
} else if (content.type === 'blog') { } else if (content.type === 'blog') {
@ -774,12 +776,12 @@ ${JSON.stringify(content, null, 2)}</pre
</style> </style>
<div <div
class="w3-card-4 ${class_background} w3-border-theme" class="w3-card-4 ${class_background} w3-border-theme"
style="margin-top: 8px; padding: 16px" style="margin-top: 8px; padding: 16px; overflow: scroll; overflow-wrap: anywhere; display: block; max-width: 100%"
> >
<div style="display: flex; flex-direction: row"> <div style="display: flex; flex-direction: row">
<tf-user id=${this.message.author} .users=${this.users}></tf-user> <tf-user id=${this.message.author} .users=${this.users}></tf-user>
<span style="flex: 1"></span> <span style="flex: 1"></span>
<span style="padding-right: 8px" <span style="padding-right: 8px; text-wrap: nowrap"
><a ><a
target="_top" target="_top"
href=${'#' + encodeURIComponent(self.message.id)} href=${'#' + encodeURIComponent(self.message.id)}
@ -792,13 +794,14 @@ ${JSON.stringify(content, null, 2)}</pre
<div>${body}</div> <div>${body}</div>
${this.render_mentions()} ${this.render_mentions()}
<div> ${this.render_votes()}
<footer class="w3-content">
${reply} ${reply}
<button class="w3-button w3-theme-d1" @click=${this.react}> <button class="w3-button w3-theme-d1" @click=${this.react}>
React React
</button> </button>
</div> ${this.render_children()}
${this.render_votes()} ${this.render_children()} </footer>
</div> </div>
`; `;
} else if (content.type === 'pub') { } else if (content.type === 'pub') {

View File

@ -210,7 +210,7 @@ class TfNewsElement extends LitElement {
channel=${this.channel} channel=${this.channel}
channel_unread=${this.channel_unread} channel_unread=${this.channel_unread}
></tf-message> ></tf-message>
${x.rowid == unread_rowid && x != final_messages[0] ${x.rowid == unread_rowid
? html`<div style="display: flex; flex-direction: row"> ? html`<div style="display: flex; flex-direction: row">
<div <div
style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px" style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px"

View File

@ -297,28 +297,34 @@ class TfProfileElement extends LitElement {
typeof profile.image == 'string' ? profile.image : profile.image?.link; typeof profile.image == 'string' ? profile.image : profile.image?.link;
image = this.editing?.image ?? image; image = this.editing?.image ?? image;
let description = this.editing?.description ?? profile.description; let description = this.editing?.description ?? profile.description;
return html`<div class="w3-container" style="box-sizing: border-box; border: 2px solid black; background-color: rgba(255, 255, 255, 0.2)"> return html`<div class="w3-card-4 w3-container w3-theme-d3" style="box-sizing: border-box">
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)}) <header class="w3-container">
<input type="text" class="w3-input w3-border w3-theme-d1" readonly value=${this.id}></input> <p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)})</p>
<button class="w3-button w3-theme-d1 w3-ripple" @click=${this.copy_id}>Copy</button> </header>
<div style="display: flex; flex-direction: row; gap: 1em"> <div class="w3-container">
${edit_profile} <input type="text" class="w3-input w3-border w3-theme-d1" readonly value=${this.id}></input>
<div style="flex: 1 0 50%"> <button class="w3-button w3-theme-d1 w3-ripple" @click=${this.copy_id}>Copy</button>
<div><img src=${'/' + image + '/view'} style="width: 256px; height: auto"></img></div> <div style="display: flex; flex-direction: row; gap: 1em">
<div>${unsafeHTML(tfutils.markdown(description))}</div> ${edit_profile}
<div style="flex: 1 0 50%">
<div><img src=${'/' + image + '/view'} style="width: 256px; height: auto"></img></div>
<div>${unsafeHTML(tfutils.markdown(description))}</div>
</div>
</div>
<div>
Following ${profile.following} identities.
Followed by ${profile.followed} identities.
Blocking ${profile.blocking} identities.
Blocked by ${profile.blocked} identities.
</div> </div>
</div> </div>
<div> <footer class="w3-container">
Following ${profile.following} identities. <p>
Followed by ${profile.followed} identities. ${edit}
Blocking ${profile.blocking} identities. ${follow}
Blocked by ${profile.blocked} identities. ${block}
</div> </p>
<p> </footer>
${edit}
${follow}
${block}
</p>
</div>`; </div>`;
} }
} }

View File

@ -1,4 +1,4 @@
import {css} from './lit-all.min.js'; import {css, unsafeCSS} from './lit-all.min.js';
const tf = css` const tf = css`
img { img {
@ -285,30 +285,165 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important} .w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
`; `;
// prettier-ignore function rgb_to_hsl(r, g, b) {
const w3_2016_snorkel_blue = css` let min,
.w3-theme-l5 {color:#000 !important; background-color:#e9f5ff !important} max,
.w3-theme-l4 {color:#000 !important; background-color:#b5dffd !important} i,
.w3-theme-l3 {color:#000 !important; background-color:#6bc0fc !important} l,
.w3-theme-l2 {color:#fff !important; background-color:#21a0fa !important} s,
.w3-theme-l1 {color:#fff !important; background-color:#0479cc !important} maxcolor,
.w3-theme-d1 {color:#fff !important; background-color:#024575 !important} h,
.w3-theme-d2 {color:#fff !important; background-color:#023e68 !important} rgb = [];
.w3-theme-d3 {color:#fff !important; background-color:#02365b !important} rgb[0] = r / 255;
.w3-theme-d4 {color:#fff !important; background-color:#022e4e !important} rgb[1] = g / 255;
.w3-theme-d5 {color:#fff !important; background-color:#012641 !important} rgb[2] = b / 255;
min = rgb[0];
max = rgb[0];
maxcolor = 0;
for (i = 0; i < rgb.length - 1; i++) {
if (rgb[i + 1] <= min) {
min = rgb[i + 1];
}
if (rgb[i + 1] >= max) {
max = rgb[i + 1];
maxcolor = i + 1;
}
}
if (maxcolor == 0) {
h = (rgb[1] - rgb[2]) / (max - min);
}
if (maxcolor == 1) {
h = 2 + (rgb[2] - rgb[0]) / (max - min);
}
if (maxcolor == 2) {
h = 4 + (rgb[0] - rgb[1]) / (max - min);
}
if (isNaN(h)) {
h = 0;
}
h = h * 60;
if (h < 0) {
h = h + 360;
}
l = (min + max) / 2;
if (min == max) {
s = 0;
} else {
if (l < 0.5) {
s = (max - min) / (max + min);
} else {
s = (max - min) / (2 - max - min);
}
}
s = s;
return [h, s, l];
}
.w3-theme-light {color:#000 !important; background-color:#e9f5ff !important} function hex_to_rgb(hex) {
.w3-theme-dark {color:#fff !important; background-color:#012641 !important} if (hex.charAt(0) == '#') {
.w3-theme-action {color:#fff !important; background-color:#012641 !important} hex = hex.substring(1);
}
return [
parseInt(hex.substring(0, 2), 16),
parseInt(hex.substring(2, 4), 16),
parseInt(hex.substring(4, 6), 16),
];
}
.w3-theme {color:#fff !important; background-color:#034f84 !important} function hsl_to_rgb(hue, sat, light) {
.w3-text-theme {color:#034f84 !important} let t2;
.w3-border-theme {border-color:#034f84 !important} hue /= 60;
if (light <= 0.5) {
t2 = light * (sat + 1);
} else {
t2 = light + sat - light * sat;
}
let t1 = light * 2 - t2;
return [
hue_to_rgb(t1, t2, hue + 2) * 255,
hue_to_rgb(t1, t2, hue) * 255,
hue_to_rgb(t1, t2, hue - 2) * 255,
];
}
function hue_to_rgb(t1, t2, hue) {
if (hue < 0) {
hue += 6;
}
if (hue >= 6) {
hue -= 6;
}
if (hue < 1) {
return (t2 - t1) * hue + t1;
} else if (hue < 3) {
return t2;
} else if (hue < 4) {
return (t2 - t1) * (4 - hue) + t1;
} else {
return t1;
}
}
.w3-hover-theme:hover {color:#fff !important; background-color:#034f84 !important} function rgb_to_hex(rgb) {
.w3-hover-text-theme:hover {color:#034f84 !important} const hex_pair = (x) => Math.floor(x).toString(16).padStart(2, '0');
.w3-hover-border-theme:hover {border-color:#034f84 !important} return `#${hex_pair(rgb[0])}${hex_pair(rgb[1])}${hex_pair(rgb[2])}`;
`; }
export let styles = [tf, w3, w3_2016_snorkel_blue]; function is_dark(hex, value) {
let [r, g, b] = hex_to_rgb(hex);
return (r * 299 + g * 587 + b * 114) / 1000 < value;
}
function generated() {
let now = new Date();
let k_color = rgb_to_hex([
now.getDay() * 255 / 6,
now.getHours() * 255 / 23,
now.getSeconds() * 255 / 59,
]);
//let k_color = '#034f84';
//let k_color = rgb_to_hex([Math.random() * 256, Math.random() * 256, Math.random() * 256]);
let [r, g, b] = hex_to_rgb(k_color);
let [h, s, l] = rgb_to_hsl(r, g, b);
let theme1 = {
l5: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 4.7)),
l4: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 4)),
l3: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 3)),
l2: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 2)),
l1: rgb_to_hex(hsl_to_rgb(h, s, l + ((1.0 - l) / 5) * 1)),
d0: rgb_to_hex(hsl_to_rgb(h, s, l)),
d1: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 0.5)),
d2: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 1)),
d3: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 1.5)),
d4: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 2)),
d5: rgb_to_hex(hsl_to_rgb(h, s, l - (l / 5) * 2.5)),
};
for (let [k, v] of Object.entries(theme1)) {
theme1['t' + k] = is_dark(v, 165) ? '#fff' : '#000';
}
let result = `
.w3-theme-l5 {color: ${theme1.tl5} !important; background-color: ${theme1.l5} !important}
.w3-theme-l4 {color: ${theme1.tl4} !important; background-color: ${theme1.l4} !important}
.w3-theme-l3 {color: ${theme1.tl3} !important; background-color: ${theme1.l3} !important}
.w3-theme-l2 {color: ${theme1.tl2} !important; background-color: ${theme1.l2} !important}
.w3-theme-l1 {color: ${theme1.tl1} !important; background-color: ${theme1.l1} !important}
.w3-theme-d1 {color: ${theme1.td1} !important; background-color: ${theme1.d1} !important}
.w3-theme-d2 {color: ${theme1.td2} !important; background-color: ${theme1.d2} !important}
.w3-theme-d3 {color: ${theme1.td3} !important; background-color: ${theme1.d3} !important}
.w3-theme-d4 {color: ${theme1.td4} !important; background-color: ${theme1.d4} !important}
.w3-theme-d5 {color: ${theme1.td5} !important; background-color: ${theme1.d5} !important}
.w3-theme-light {color: ${theme1.tl5} !important; background-color: ${theme1.l5} !important}
.w3-theme-dark {color: ${theme1.td5} !important; background-color: ${theme1.d5} !important}
.w3-theme-action {color: ${theme1.td5} !important; background-color: ${theme1.d5} !important}
.w3-theme {color: ${theme1.td0} !important; background-color: ${theme1.d0} !important}
.w3-text-theme {color: ${theme1.d0} !important}
.w3-border-theme {border-color: ${theme1.d0} !important}
.w3-hover-theme:hover {color: ${theme1.td0} !important; background-color: ${theme1.d0} !important}
.w3-hover-text-theme:hover {color: ${theme1.d0} !important}
.w3-hover-border-theme:hover {border-color: ${theme1.d0} !important}
`;
return unsafeCSS(result);
}
export let styles = [tf, w3, generated()];

View File

@ -175,6 +175,9 @@ class TfTabConnectionsElement extends LitElement {
.map((x) => html`<li>${this.render_connection(x)}</li>`)} .map((x) => html`<li>${this.render_connection(x)}</li>`)}
${this.render_room_peers(connection.id)} ${this.render_room_peers(connection.id)}
</ul> </ul>
<div ?hidden=${!connection.destroy_reason} class="w3-panel w3-red">
<p>${connection.destroy_reason}</p>
</div>
`; `;
} }

View File

@ -51,15 +51,21 @@ class TfTabNewsFeedElement extends LitElement {
if (this.hash == '#@') { if (this.hash == '#@') {
result = await tfrpc.rpc.query( result = await tfrpc.rpc.query(
` `
WITH mentions AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
FROM messages_fts(?1)
JOIN messages ON messages.rowid = messages_fts.rowid
JOIN json_each(?2) AS following ON messages.author = following.value
WHERE
messages.author != ?1 AND
messages.timestamp >= ?3 AND
messages.timestamp < ?4
ORDER BY timestamp DESC limit 20)
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
FROM messages_fts(?1) FROM mentions
JOIN messages ON messages.rowid = messages_fts.rowid JOIN messages_refs ON mentions.id = messages_refs.ref
JOIN json_each(?2) AS following ON messages.author = following.value JOIN messages ON messages_refs.message = messages.id
WHERE UNION
messages.author != ?1 AND SELECT * FROM mentions
messages.timestamp >= ?3 AND
messages.timestamp < ?4
ORDER BY timestamp DESC limit 20
`, `,
[ [
'"' + this.whoami.replace('"', '""') + '"', '"' + this.whoami.replace('"', '""') + '"',
@ -288,7 +294,12 @@ class TfTabNewsFeedElement extends LitElement {
this.loading--; this.loading--;
} }
this.messages = Object.values( this.messages = Object.values(
Object.fromEntries([...this.messages, ...messages].map((x) => [x.id, x])) Object.fromEntries(
[...this.messages, ...messages]
.sort((x, y) => x.timestamp - y.timestamp)
.slice(-1024)
.map((x) => [x.id, x])
)
); );
console.log('done loading latest messages.'); console.log('done loading latest messages.');
} }

View File

@ -8,7 +8,6 @@ class TfTabNewsElement extends LitElement {
whoami: {type: String}, whoami: {type: String},
users: {type: Object}, users: {type: Object},
hash: {type: String}, hash: {type: String},
unread: {type: Array},
following: {type: Array}, following: {type: Array},
drafts: {type: Object}, drafts: {type: Object},
expanded: {type: Object}, expanded: {type: Object},
@ -16,6 +15,7 @@ class TfTabNewsElement extends LitElement {
channels: {type: Array}, channels: {type: Array},
channels_unread: {type: Object}, channels_unread: {type: Object},
channels_latest: {type: Object}, channels_latest: {type: Object},
connections: {type: Array},
}; };
} }
@ -27,7 +27,6 @@ class TfTabNewsElement extends LitElement {
this.whoami = null; this.whoami = null;
this.users = {}; this.users = {};
this.hash = '#'; this.hash = '#';
this.unread = [];
this.following = []; this.following = [];
this.cache = {}; this.cache = {};
this.drafts = {}; this.drafts = {};
@ -35,6 +34,7 @@ class TfTabNewsElement extends LitElement {
this.channels_unread = {}; this.channels_unread = {};
this.channels_latest = {}; this.channels_latest = {};
this.channels = []; this.channels = [];
this.connections = [];
tfrpc.rpc.localStorageGet('drafts').then(function (d) { tfrpc.rpc.localStorageGet('drafts').then(function (d) {
self.drafts = JSON.parse(d || '{}'); self.drafts = JSON.parse(d || '{}');
}); });
@ -50,36 +50,13 @@ class TfTabNewsElement extends LitElement {
document.body.removeEventListener('keypress', this.on_keypress.bind(this)); document.body.removeEventListener('keypress', this.on_keypress.bind(this));
} }
show_more() { load_latest() {
let unread = this.unread;
let news = this.shadowRoot?.getElementById('news'); let news = this.shadowRoot?.getElementById('news');
if (news) { if (news) {
news.load_latest(); news.load_latest();
this.dispatchEvent(new CustomEvent('refresh'));
} }
} }
new_messages_text() {
if (!this.unread?.length) {
return 'No new messages.';
}
let counts = {};
for (let message of this.unread) {
let type = 'private';
try {
type = JSON.parse(message.content).type || type;
} catch {}
counts[type] = (counts[type] || 0) + 1;
}
return (
'↻ Show New: ' +
Object.keys(counts)
.sort()
.map((x) => counts[x].toString() + ' ' + x + 's')
.join(', ')
);
}
draft(event) { draft(event) {
let id = event.detail.id || ''; let id = event.detail.id || '';
let previous = this.drafts[id]; let previous = this.drafts[id];
@ -112,10 +89,11 @@ class TfTabNewsElement extends LitElement {
unread_status(channel) { unread_status(channel) {
if ( if (
this.channels_latest[channel] && this.channels_latest[channel] &&
this.channels_latest[channel] > 0 &&
(this.channels_unread[channel] === undefined || (this.channels_unread[channel] === undefined ||
this.channels_unread[channel] <= this.channels_latest[channel]) this.channels_unread[channel] <= this.channels_latest[channel])
) { ) {
return '🔵'; return '✉️ ';
} }
} }
@ -150,33 +128,11 @@ class TfTabNewsElement extends LitElement {
return this.hash.startsWith('##') ? this.hash.substring(2) : undefined; return this.hash.startsWith('##') ? this.hash.substring(2) : undefined;
} }
render() { render_sidebar() {
let profile =
this.hash.startsWith('#@') && this.hash != '#@'
? html`<tf-profile
class="tf-profile"
id=${this.hash.substring(1)}
whoami=${this.whoami}
.users=${this.users}
></tf-profile>`
: undefined;
let edit_profile;
if (
!this.loading &&
this.users[this.whoami]?.name === undefined &&
this.hash.substring(1) != this.whoami
) {
edit_profile = html` <div
class="w3-panel w3-padding w3-round w3-card-4 w3-theme-l3"
>
Follow your identity link ☝️ above to edit your profile and set your
name.
</div>`;
}
return html` return html`
<div <div
class="w3-sidebar w3-bar-block w3-theme-d1 w3-collapse w3-animate-left" class="w3-sidebar w3-bar-block w3-theme-d1 w3-collapse w3-animate-left"
style="width: 2in; left: 0; z-index: 5" style="width: 2in; left: 0; z-index: 5; box-sizing: border-box; top: 0"
id="sidebar" id="sidebar"
> >
<div <div
@ -202,89 +158,137 @@ class TfTabNewsElement extends LitElement {
href="#" href="#"
class="w3-bar-item w3-button" class="w3-bar-item w3-button"
style=${this.hash == '#' ? 'font-weight: bold' : undefined} style=${this.hash == '#' ? 'font-weight: bold' : undefined}
>general ${this.unread_status('')}</a >${this.unread_status('')}general</a
> >
<a <a
href="#@" href="#@"
class="w3-bar-item w3-button" class="w3-bar-item w3-button"
style=${this.hash == '#@' ? 'font-weight: bold' : undefined} style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
>@mentions ${this.unread_status('@')}</a >${this.unread_status('@')}@mentions</a
> >
<a <a
href="#🔐" href="#🔐"
class="w3-bar-item w3-button" class="w3-bar-item w3-button"
style=${this.hash == '#🔐' ? 'font-weight: bold' : undefined} style=${this.hash == '#🔐' ? 'font-weight: bold' : undefined}
>🔐private ${this.unread_status('🔐')}</a >${this.unread_status('🔐')}🔐private</a
> >
${Object.keys(this.drafts)
.sort()
.map(
(x) => html`
<a
href=${'#' + encodeURIComponent(x)}
class="w3-bar-item w3-button"
style="text-wrap: nowrap; text-overflow: ellipsis"
>📝 ${this.drafts[x]?.text ?? x}</a
>
`
)}
${this.channels.map( ${this.channels.map(
(x) => html` (x) => html`
<a <a
href=${'#' + encodeURIComponent('#' + x)} href=${'#' + encodeURIComponent('#' + x)}
class="w3-bar-item w3-button" class="w3-bar-item w3-button"
style=${this.hash == '##' + x ? 'font-weight: bold' : undefined} style=${this.hash == '##' + x ? 'font-weight: bold' : undefined}
>#${x} ${this.unread_status(x)}</a >${this.unread_status(x)}#${x}</a
> >
` `
)} )}
<div class="w3-bar-item w3-theme-d2">Connections</div>
${this.connections.map((x) => (html`
<tf-user class="w3-bar-item" style="max-width: 100%" id=${x.id} .users=${this.users}></tf-user>
`))}
</div> </div>
<div <div
class="w3-overlay" class="w3-overlay"
id="sidebar_overlay" id="sidebar_overlay"
@click=${this.hide_sidebar} @click=${this.hide_sidebar}
></div> ></div>
<div style="margin-left: 2in; padding: 8px" id="main" class="w3-main"> `;
<div }
id="show_sidebar"
class="w3-left w3-button w3-hide-large" render() {
@click=${this.show_sidebar} let profile =
> this.hash.startsWith('#@') && this.hash != '#@'
&#9776; ? html`<tf-profile
</div> class="tf-profile"
<p> id=${this.hash.substring(1)}
<button class="w3-button w3-theme-d1" @click=${this.show_more}>
${this.new_messages_text()}
</button>
${this.hash.startsWith('##')
? html`
<button
class="w3-button w3-theme-d1"
@click=${this.channel_toggle_subscribed}
>
${this.channels.indexOf(this.hash.substring(2)) != -1
? 'Unsubscribe from #'
: 'Subscribe to #'}${this.hash.substring(2)}
</button>
`
: undefined}
</p>
<div class="w3-bar">
Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
${edit_profile}
</div>
<div>
<tf-compose
id="tf-compose"
whoami=${this.whoami} whoami=${this.whoami}
.users=${this.users} .users=${this.users}
></tf-profile>`
: undefined;
let edit_profile;
if (
!this.loading &&
this.users[this.whoami]?.name === undefined &&
this.hash.substring(1) != this.whoami
) {
edit_profile = html` <div
class="w3-panel w3-padding w3-round w3-card-4 w3-theme-l3"
>
Follow your identity link ☝️ above to edit your profile and set your
name.
</div>`;
}
return html`
${this.render_sidebar()}
<div
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: scroll"
id="main"
class="w3-main"
>
<div style="padding: 8px">
<p>
${this.hash.startsWith('##')
? html`
<button
class="w3-button w3-theme-d1"
@click=${this.channel_toggle_subscribed}
>
${this.channels.indexOf(this.hash.substring(2)) != -1
? 'Unsubscribe from #'
: 'Subscribe to #'}${this.hash.substring(2)}
</button>
`
: undefined}
</p>
<div>
<div
id="show_sidebar"
class="w3-button w3-hide-large"
@click=${this.show_sidebar}
>
&#9776;
</div>
Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
${edit_profile}
</div>
<div>
<tf-compose
id="tf-compose"
whoami=${this.whoami}
.users=${this.users}
.drafts=${this.drafts}
@tf-draft=${this.draft}
.channel=${this.channel()}
></tf-compose>
</div>
${profile}
<tf-tab-news-feed
id="news"
whoami=${this.whoami}
.users=${this.users}
.following=${this.following}
hash=${this.hash}
.drafts=${this.drafts} .drafts=${this.drafts}
.expanded=${this.expanded}
@tf-draft=${this.draft} @tf-draft=${this.draft}
.channel=${this.channel()} @tf-expand=${this.on_expand}
></tf-compose> .channels_unread=${this.channels_unread}
.channels_latest=${this.channels_latest}
></tf-tab-news-feed>
</div> </div>
${profile}
<tf-tab-news-feed
id="news"
whoami=${this.whoami}
.users=${this.users}
.following=${this.following}
hash=${this.hash}
.drafts=${this.drafts}
.expanded=${this.expanded}
@tf-draft=${this.draft}
@tf-expand=${this.on_expand}
.channels_unread=${this.channels_unread}
.channels_latest=${this.channels_latest}
></tf-tab-news-feed>
</div> </div>
`; `;
} }

View File

@ -25,10 +25,7 @@ class TfUserElement extends LitElement {
>?</span >?</span
>`; >`;
let name = this.users?.[this.id]?.name; let name = this.users?.[this.id]?.name;
name = name = html`<a target="_top" href=${'#' + this.id}>${name !== undefined ? name : this.id}</a>`
name !== undefined
? html`<a target="_top" href=${'#' + this.id}>${name}</a>`
: html`<a target="_top" href=${'#' + this.id}>${this.id}</a>`;
if (this.users[this.id]) { if (this.users[this.id]) {
let image_link = this.users[this.id].image; let image_link = this.users[this.id].image;
@ -42,7 +39,7 @@ class TfUserElement extends LitElement {
/>`; />`;
} }
} }
return html` <div style="display: inline-block; font-weight: bold"> return html` <div style="display: inline-block; vertical-align: middle; font-weight: bold; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis">
${image} ${name} ${image} ${name}
</div>`; </div>`;
} }

View File

@ -21,14 +21,14 @@
}: }:
pkgs.stdenv.mkDerivation rec { pkgs.stdenv.mkDerivation rec {
pname = "tildefriends"; pname = "tildefriends";
version = "0.0.25"; version = "0.0.26";
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-Rfk+CUhi+Ss0z70CCgmtVM/w4nCL1GX/MsD4sPYIa5s="; hash = "sha256-XJ7M++risfsRn9GkS1zjTQpqqV5S09uyimeVzU9hGGg=";
fetchSubmodules = true; fetchSubmodules = true;
}; };

View File

@ -1,8 +1,8 @@
<?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="31" android:versionCode="32"
android:versionName="0.0.26"> android:versionName="0.0.27-wip">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application

View File

@ -738,25 +738,6 @@ static void _httpd_endpoint_mem(tf_http_request_t* request)
tf_free(response); tf_free(response);
} }
static void _httpd_endpoint_disconnections(tf_http_request_t* request)
{
if (_httpd_redirect(request))
{
return;
}
tf_task_t* task = request->user_data;
char* response = tf_task_get_disconnections(task);
const char* headers[] = {
"Content-Type",
"application/json; charset=utf-8",
"Access-Control-Allow-Origin",
"*",
};
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, response, response ? strlen(response) : 0);
tf_free(response);
}
static void _httpd_endpoint_hitches(tf_http_request_t* request) static void _httpd_endpoint_hitches(tf_http_request_t* request)
{ {
if (_httpd_redirect(request)) if (_httpd_redirect(request))
@ -2099,19 +2080,26 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
if (form_register && strcmp(form_register, "1") == 0) if (form_register && strcmp(form_register, "1") == 0)
{ {
bool registered = false; bool registered = false;
if (!have_account && _is_name_valid(account_name) && password && confirm && strcmp(password, confirm) == 0) if (!_is_name_valid(account_name))
{ {
sqlite3* db = tf_ssb_acquire_db_writer(ssb); login_error = "Invalid username. Usernames must contain only letters from the English alphabet and digits and must start with a letter.";
registered = tf_ssb_db_register_account(tf_ssb_get_loop(ssb), db, context, account_name, password); }
tf_ssb_release_db_writer(ssb, db); else
if (registered) {
if (!have_account && _is_name_valid(account_name) && password && confirm && strcmp(password, confirm) == 0)
{ {
tf_free((void*)send_session); sqlite3* db = tf_ssb_acquire_db_writer(ssb);
send_session = _make_session_jwt(context, ssb, account_name); registered = tf_ssb_db_register_account(tf_ssb_get_loop(ssb), db, context, account_name, password);
may_become_first_admin = true; tf_ssb_release_db_writer(ssb, db);
if (registered)
{
tf_free((void*)send_session);
send_session = _make_session_jwt(context, ssb, account_name);
may_become_first_admin = true;
}
} }
} }
if (!registered) if (!registered && !login_error)
{ {
login_error = "Error registering account."; login_error = "Error registering account.";
} }
@ -2317,7 +2305,6 @@ void tf_httpd_register(JSContext* context)
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);
tf_http_add_handler(http, "/disconnections", _httpd_endpoint_disconnections, NULL, task);
tf_http_add_handler(http, "/hitches", _httpd_endpoint_hitches, NULL, task); tf_http_add_handler(http, "/hitches", _httpd_endpoint_hitches, NULL, task);
tf_http_add_handler(http, "/mem", _httpd_endpoint_mem, NULL, task); tf_http_add_handler(http, "/mem", _httpd_endpoint_mem, NULL, task);
tf_http_add_handler(http, "/trace", _httpd_endpoint_trace, NULL, task); tf_http_add_handler(http, "/trace", _httpd_endpoint_trace, NULL, task);

View File

@ -25,6 +25,7 @@
#if defined(__linux__) #if defined(__linux__)
#include <sys/prctl.h> #include <sys/prctl.h>
#include <sys/stat.h>
#endif #endif
#if defined(__APPLE__) #if defined(__APPLE__)
@ -41,7 +42,102 @@
struct backtrace_state* g_backtrace_state; struct backtrace_state* g_backtrace_state;
const char* k_db_path_default = "db.sqlite"; #if !defined(TARGET_OS_IPHONE)
static const char* _get_db_path()
{
const char* k_db_path_default = "db.sqlite";
#if defined(__linux__)
if (stat(k_db_path_default, &(struct stat) { 0 }) == 0)
{
return tf_strdup(k_db_path_default);
}
else
{
char buffer[32];
char* data_home = NULL;
size_t size = sizeof(buffer);
int r = uv_os_getenv("XDG_DATA_HOME", buffer, &size);
if (r == 0 || r == UV_ENOBUFS)
{
size++;
data_home = alloca(size);
if (uv_os_getenv("XDG_DATA_HOME", data_home, &size) != 0)
{
data_home = NULL;
}
}
if (!data_home)
{
size = sizeof(buffer);
r = uv_os_getenv("HOME", buffer, &size);
if (r == 0 || r == UV_ENOBUFS)
{
size++;
char* home = alloca(size);
r = uv_os_getenv("HOME", home, &size);
if (r == 0)
{
size = snprintf(NULL, 0, "%s/.local/share", home) + 1;
data_home = alloca(size);
snprintf(data_home, size, "%s/.local/share", home);
}
}
}
if (data_home)
{
size = snprintf(NULL, 0, "%s/tildefriends/db.sqlite", data_home) + 1;
char* path = alloca(size);
snprintf(path, size, "%s/tildefriends/db.sqlite", data_home);
return tf_strdup(path);
}
}
#endif
return tf_strdup(k_db_path_default);
}
static void _create_directories_for_file(const char* path, int mode)
{
if (stat(path, &(struct stat) { 0 }) == 0)
{
/* It already exists. OK. */
return;
}
size_t length = strlen(path) + 1;
char* copy = alloca(length);
memcpy(copy, path, length);
#if defined(_WIN32)
for (char* c = copy; *c; c++)
{
if (*c == '\\')
{
*c = '/';
}
}
#endif
char* slash = copy;
while (slash)
{
slash = strchr(slash + 1, '/');
if (slash)
{
*slash = '\0';
#if defined(_WIN32)
if (mkdir(copy) == 0)
#else
if (mkdir(copy, mode) == 0)
#endif
{
tf_printf("Created directory %s.\n", copy);
}
*slash = '/';
}
}
}
#endif
#if !TARGET_OS_IPHONE && !defined(__ANDROID__) #if !TARGET_OS_IPHONE && !defined(__ANDROID__)
static int _tf_command_export(const char* file, int argc, char* argv[]); static int _tf_command_export(const char* file, int argc, char* argv[]);
@ -131,7 +227,8 @@ static int _tf_command_test(const char* file, int argc, char* argv[])
static int _tf_command_import(const char* file, int argc, char* argv[]) static int _tf_command_import(const char* file, int argc, char* argv[])
{ {
const char* user = "import"; const char* user = "import";
const char* db_path = k_db_path_default; const char* default_db_path = _get_db_path();
const char* db_path = default_db_path;
bool show_usage = false; bool show_usage = false;
while (!show_usage) while (!show_usage)
@ -169,11 +266,13 @@ static int _tf_command_import(const char* file, int argc, char* argv[])
tf_printf("\n%s import [options] [paths...]\n\n", file); tf_printf("\n%s import [options] [paths...]\n\n", file);
tf_printf("options:\n"); tf_printf("options:\n");
tf_printf(" -u, --user user User into whose account apps will be imported (default: \"import\").\n"); tf_printf(" -u, --user user User into whose account apps will be imported (default: \"import\").\n");
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
tf_printf(" -h, --help Show this usage information.\n"); tf_printf(" -h, --help Show this usage information.\n");
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
_create_directories_for_file(db_path, 0700);
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
if (optind < argc) if (optind < argc)
{ {
@ -189,13 +288,15 @@ static int _tf_command_import(const char* file, int argc, char* argv[])
tf_ssb_import(ssb, user, "apps"); tf_ssb_import(ssb, user, "apps");
} }
tf_ssb_destroy(ssb); tf_ssb_destroy(ssb);
tf_free((void*)default_db_path);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
static int _tf_command_export(const char* file, int argc, char* argv[]) static int _tf_command_export(const char* file, int argc, char* argv[])
{ {
const char* user = "core"; const char* user = "core";
const char* db_path = k_db_path_default; const char* default_db_path = _get_db_path();
const char* db_path = default_db_path;
bool show_usage = false; bool show_usage = false;
while (!show_usage) while (!show_usage)
@ -233,10 +334,11 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
tf_printf("\n%s export [options] [paths...]\n\n", file); tf_printf("\n%s export [options] [paths...]\n\n", file);
tf_printf("options:\n"); tf_printf("options:\n");
tf_printf(" -u, --user user User from whose account apps will be exported (default: \"core\").\n"); tf_printf(" -u, --user user User from whose account apps will be exported (default: \"core\").\n");
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
tf_printf(" -h, --help Show this usage information.\n"); tf_printf(" -h, --help Show this usage information.\n");
tf_printf("\n"); tf_printf("\n");
tf_printf("paths Paths of apps to export (example: /~core/ssb /~user/app).\n"); tf_printf("paths Paths of apps to export (example: /~core/ssb /~user/app).\n");
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -271,6 +373,7 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
} }
} }
tf_ssb_destroy(ssb); tf_ssb_destroy(ssb);
tf_free((void*)default_db_path);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@ -298,7 +401,8 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
{ {
const char* user = NULL; const char* user = NULL;
const char* identity = NULL; const char* identity = NULL;
const char* db_path = k_db_path_default; const char* default_db_path = _get_db_path();
const char* db_path = default_db_path;
const char* content = NULL; const char* content = NULL;
bool show_usage = false; bool show_usage = false;
@ -346,14 +450,16 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
tf_printf("options:\n"); tf_printf("options:\n");
tf_printf(" -u, --user user User owning identity with which to publish.\n"); tf_printf(" -u, --user user User owning identity with which to publish.\n");
tf_printf(" -i, --id identity Identity with which to publish message.\n"); tf_printf(" -i, --id identity Identity with which to publish message.\n");
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
tf_printf(" -c, --content json JSON content of message to publish.\n"); tf_printf(" -c, --content json JSON content of message to publish.\n");
tf_printf(" -h, --help Show this usage information.\n"); tf_printf(" -h, --help Show this usage information.\n");
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
int result = EXIT_FAILURE; int result = EXIT_FAILURE;
tf_printf("Posting %s as account %s belonging to %s...\n", content, identity, user); tf_printf("Posting %s as account %s belonging to %s...\n", content, identity, user);
_create_directories_for_file(db_path, 0700);
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
uint8_t private_key[512] = { 0 }; uint8_t private_key[512] = { 0 };
if (tf_ssb_db_identity_get_private_key(ssb, user, identity, private_key, sizeof(private_key))) if (tf_ssb_db_identity_get_private_key(ssb, user, identity, private_key, sizeof(private_key)))
@ -386,12 +492,14 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
tf_printf("Did not find private key for identity %s belonging to %s.\n", identity, user); tf_printf("Did not find private key for identity %s belonging to %s.\n", identity, user);
} }
tf_ssb_destroy(ssb); tf_ssb_destroy(ssb);
tf_free((void*)default_db_path);
return result; return result;
} }
static int _tf_command_store_blob(const char* file, int argc, char* argv[]) static int _tf_command_store_blob(const char* file, int argc, char* argv[])
{ {
const char* db_path = k_db_path_default; const char* default_db_path = _get_db_path();
const char* db_path = default_db_path;
const char* file_path = NULL; const char* file_path = NULL;
bool show_usage = false; bool show_usage = false;
@ -429,9 +537,10 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
{ {
tf_printf("\n%s store_blob [options]\n\n", file); tf_printf("\n%s store_blob [options]\n\n", file);
tf_printf("options:\n"); tf_printf("options:\n");
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
tf_printf(" -f, --file file_path Path to file to add to the blob store.\n"); tf_printf(" -f, --file file_path Path to file to add to the blob store.\n");
tf_printf(" -h, --help Show this usage information.\n"); tf_printf(" -h, --help Show this usage information.\n");
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -441,6 +550,7 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
if (!blob_file) if (!blob_file)
{ {
tf_printf("Failed to open %s: %s.\n", file_path, strerror(errno)); tf_printf("Failed to open %s: %s.\n", file_path, strerror(errno));
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -464,11 +574,13 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
tf_printf("Failed to read %s: %s.\n", file_path, strerror(errno)); tf_printf("Failed to read %s: %s.\n", file_path, strerror(errno));
fclose(blob_file); fclose(blob_file);
tf_free(data); tf_free(data);
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
fclose(blob_file); fclose(blob_file);
char id[256]; char id[256];
_create_directories_for_file(db_path, 0700);
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
if (tf_ssb_db_blob_store(ssb, (const uint8_t*)data, size, id, sizeof(id), NULL)) if (tf_ssb_db_blob_store(ssb, (const uint8_t*)data, size, id, sizeof(id), NULL))
{ {
@ -476,13 +588,15 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
} }
tf_ssb_destroy(ssb); tf_ssb_destroy(ssb);
tf_free(data); tf_free(data);
tf_free((void*)default_db_path);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
static int _tf_command_verify(const char* file, int argc, char* argv[]) static int _tf_command_verify(const char* file, int argc, char* argv[])
{ {
const char* identity = NULL; const char* identity = NULL;
const char* db_path = k_db_path_default; const char* default_db_path = _get_db_path();
const char* db_path = default_db_path;
bool show_usage = false; bool show_usage = false;
while (!show_usage) while (!show_usage)
@ -520,8 +634,9 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
tf_printf("\n%s import [options] [paths...]\n\n", file); tf_printf("\n%s import [options] [paths...]\n\n", file);
tf_printf("options:\n"); tf_printf("options:\n");
tf_printf(" -i, --identity identity Identity to verify.\n"); tf_printf(" -i, --identity identity Identity to verify.\n");
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", k_db_path_default); tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
tf_printf(" -h, --help Show this usage information.\n"); tf_printf(" -h, --help Show this usage information.\n");
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -529,6 +644,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL); tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
bool verified = tf_ssb_db_verify(ssb, identity); bool verified = tf_ssb_db_verify(ssb, identity);
tf_ssb_destroy(ssb); tf_ssb_destroy(ssb);
tf_free((void*)default_db_path);
return verified ? EXIT_SUCCESS : EXIT_FAILURE; return verified ? EXIT_SUCCESS : EXIT_FAILURE;
} }
#endif #endif
@ -673,13 +789,14 @@ static void _shed_privileges()
static int _tf_command_run(const char* file, int argc, char* argv[]) static int _tf_command_run(const char* file, int argc, char* argv[])
{ {
const char* default_db_path = _get_db_path();
tf_run_args_t args = { tf_run_args_t args = {
.count = 1, .count = 1,
.script = "core/core.js", .script = "core/core.js",
.http_port = 12345, .http_port = 12345,
.https_port = 12346, .https_port = 12346,
.ssb_port = 8008, .ssb_port = 8008,
.db_path = k_db_path_default, .db_path = default_db_path,
}; };
bool show_usage = false; bool show_usage = false;
@ -764,7 +881,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
tf_printf(" -b, --ssb-port port Port on which to run SSB (default: 8008, 0 disables).\n"); tf_printf(" -b, --ssb-port port Port on which to run SSB (default: 8008, 0 disables).\n");
tf_printf(" -p, --http-port port Port on which to run Tilde Friends web server (default: 12345).\n"); tf_printf(" -p, --http-port port Port on which to run Tilde Friends web server (default: 12345).\n");
tf_printf(" -q, --https-port port Port on which to run secure Tilde Friends web server (default: 12346).\n"); tf_printf(" -q, --https-port port Port on which to run secure Tilde Friends web server (default: 12346).\n");
tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", k_db_path_default); tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", default_db_path);
tf_printf(" -k, --ssb-network-key key SSB network key to use.\n"); tf_printf(" -k, --ssb-network-key key SSB network key to use.\n");
tf_printf(" -n, --count count Number of instances to run.\n"); tf_printf(" -n, --count count Number of instances to run.\n");
tf_printf(" -a, --args args Arguments of the format key=value,foo=bar,verbose=true.\n"); tf_printf(" -a, --args args Arguments of the format key=value,foo=bar,verbose=true.\n");
@ -772,6 +889,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
tf_printf(" -z, --zip path Zip archive from which to load files.\n"); tf_printf(" -z, --zip path Zip archive from which to load files.\n");
tf_printf(" -v, --verbose Log raw messages.\n"); tf_printf(" -v, --verbose Log raw messages.\n");
tf_printf(" -h, --help Show this usage information.\n"); tf_printf(" -h, --help Show this usage information.\n");
tf_free((void*)default_db_path);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -780,6 +898,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
setpgid(0, 0); setpgid(0, 0);
#endif #endif
_create_directories_for_file(args.db_path, 0700);
if (args.count == 1) if (args.count == 1)
{ {
result = _tf_run_task(&args, 0); result = _tf_run_task(&args, 0);
@ -807,6 +926,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
tf_free(data); tf_free(data);
tf_free(threads); tf_free(threads);
} }
tf_free((void*)default_db_path);
return result; return result;
} }
@ -1088,7 +1208,7 @@ void tf_run_thread_start(const char* zip_path)
.http_port = 12345, .http_port = 12345,
.https_port = 12346, .https_port = 12346,
.ssb_port = 8008, .ssb_port = 8008,
.db_path = k_db_path_default, .db_path = "db.sqlite",
.one_proc = true, .one_proc = true,
.zip = zip_path, .zip = zip_path,
}; };

554
src/ssb.c

File diff suppressed because it is too large Load Diff

View File

@ -1681,34 +1681,13 @@ bool tf_ssb_db_set_account_password(uv_loop_t* loop, sqlite3* db, JSContext* con
return result; return result;
} }
static bool _tf_ssb_db_get_global_setting_bool(sqlite3* db, const char* name, bool default_value)
{
bool result = default_value;
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
{
result = sqlite3_column_int(statement, 0) != 0;
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
}
return result;
}
bool tf_ssb_db_register_account(uv_loop_t* loop, sqlite3* db, JSContext* context, const char* name, const char* password) bool tf_ssb_db_register_account(uv_loop_t* loop, sqlite3* db, JSContext* context, const char* name, const char* password)
{ {
bool result = false; bool result = false;
JSValue users_array = JS_UNDEFINED; JSValue users_array = JS_UNDEFINED;
bool registration_allowed = _tf_ssb_db_get_global_setting_bool(db, "account_registration", true); bool registration_allowed = true;
tf_ssb_db_get_global_setting_bool(db, "account_registration", &registration_allowed);
if (registration_allowed) if (registration_allowed)
{ {
sqlite3_stmt* statement = NULL; sqlite3_stmt* statement = NULL;
@ -2030,3 +2009,72 @@ bool tf_ssb_db_user_has_permission(tf_ssb_t* ssb, sqlite3* db, const char* id, c
} }
return has_permission; return has_permission;
} }
bool tf_ssb_db_get_global_setting_bool(sqlite3* db, const char* name, bool* out_value)
{
bool result = false;
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
{
*out_value = sqlite3_column_int(statement, 0) != 0;
result = true;
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
}
return result;
}
bool tf_ssb_db_get_global_setting_int64(sqlite3* db, const char* name, int64_t* out_value)
{
bool result = false;
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
{
*out_value = sqlite3_column_int64(statement, 0);
result = true;
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
}
return result;
}
bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* out_value, size_t size)
{
bool result = false;
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
{
snprintf(out_value, size, "%s", sqlite3_column_text(statement, 0));
result = true;
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
}
return result;
}

View File

@ -455,6 +455,34 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id);
*/ */
bool tf_ssb_db_user_has_permission(tf_ssb_t* ssb, sqlite3* db, const char* id, const char* permission); bool tf_ssb_db_user_has_permission(tf_ssb_t* ssb, sqlite3* db, const char* id, const char* permission);
/**
** Get a boolean global setting value.
** @param db The database.
** @param name The setting name.
** @param out_value Populated with the value.
** @return true if the setting was found.
*/
bool tf_ssb_db_get_global_setting_bool(sqlite3* db, const char* name, bool* out_value);
/**
** Get an int64_t global setting value.
** @param db The database.
** @param name The setting name.
** @param out_value Populated with the value.
** @return true if the setting was found.
*/
bool tf_ssb_db_get_global_setting_int64(sqlite3* db, const char* name, int64_t* out_value);
/**
** Get a string global setting value.
** @param db The database.
** @param name The setting name.
** @param out_value Populated with the value.
** @param size The size of the out_value buffer.
** @return true if the setting was found.
*/
bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* out_value, size_t size);
/** /**
** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use. ** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use.
** @param user_data User data registered with the authorizer. ** @param user_data User data registered with the authorizer.

View File

@ -514,8 +514,9 @@ JSContext* tf_ssb_connection_get_context(tf_ssb_connection_t* connection);
/** /**
** Close a connection. ** Close a connection.
** @param connection The connection. ** @param connection The connection.
** @param reason Human-readable reason for closing the connection.
*/ */
void tf_ssb_connection_close(tf_ssb_connection_t* connection); void tf_ssb_connection_close(tf_ssb_connection_t* connection, const char* reason);
/** /**
** Check whether a connection is connected. ** Check whether a connection is connected.
@ -798,17 +799,19 @@ JSValue tf_ssb_connection_requests_to_object(tf_ssb_connection_t* connection);
/** /**
** A function scheduled to be run later. ** A function scheduled to be run later.
** @param connection The owning connection. ** @param connection The owning connection.
** @param skip Whether the work ought to be skipped, because it is being replaced.
** @param user_data User data registered with the callback. ** @param user_data User data registered with the callback.
*/ */
typedef void(tf_ssb_scheduled_callback_t)(tf_ssb_connection_t* connection, void* user_data); typedef void(tf_ssb_scheduled_callback_t)(tf_ssb_connection_t* connection, bool skip, void* user_data);
/** /**
** Schedule work to be run when the connection is next idle. ** Schedule work to be run when the connection is next idle.
** @param connection The owning connection. ** @param connection The owning connection.
** @param key A key identifying the work. If work by the same key already exists, the new request will be discarded.
** @param callback The callback to call. ** @param callback The callback to call.
** @param user_data User data to pass to the callback. ** @param user_data User data to pass to the callback.
*/ */
void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, tf_ssb_scheduled_callback_t* callback, void* user_data); void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, const char* key, tf_ssb_scheduled_callback_t* callback, void* user_data);
/** /**
** Schedule work to run on a worker thread. ** Schedule work to run on a worker thread.
@ -947,14 +950,6 @@ void tf_ssb_get_stats(tf_ssb_t* ssb, tf_ssb_stats_t* out_stats);
*/ */
tf_ssb_blob_wants_t* tf_ssb_connection_get_blob_wants_state(tf_ssb_connection_t* connection); tf_ssb_blob_wants_t* tf_ssb_connection_get_blob_wants_state(tf_ssb_connection_t* connection);
/**
** Get a report of information about recent disconnections.
** @param ssb The SSB instance.
** @param context A JS context.
** @return Information about disconnections.
*/
JSValue tf_ssb_get_disconnection_debug(tf_ssb_t* ssb, JSContext* context);
/** /**
** Record whether the calling thread is busy. ** Record whether the calling thread is busy.
** @param ssb The SSB instance. ** @param ssb The SSB instance.
@ -1101,6 +1096,13 @@ void tf_ssb_connection_adjust_read_backpressure(tf_ssb_connection_t* connection,
*/ */
void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta); void tf_ssb_connection_adjust_write_count(tf_ssb_connection_t* connection, int delta);
/**
** Get the reason why a connection is going away.
** @param connection The connection.
** @return The reason or NULL.
*/
const char* tf_ssb_connection_get_destroy_reason(tf_ssb_connection_t* connection);
/** /**
** Initiate a tunnel connection. ** Initiate a tunnel connection.
** @param ssb The SSB instance. ** @param ssb The SSB instance.

View File

@ -1120,6 +1120,11 @@ static JSValue _tf_ssb_connections(JSContext* context, JSValueConst this_val, in
int flags = tf_ssb_connection_get_flags(connection); int flags = tf_ssb_connection_get_flags(connection);
JS_SetPropertyStr(context, flags_object, "one_shot", JS_NewBool(context, (flags & k_tf_ssb_connect_flag_one_shot) != 0)); JS_SetPropertyStr(context, flags_object, "one_shot", JS_NewBool(context, (flags & k_tf_ssb_connect_flag_one_shot) != 0));
JS_SetPropertyStr(context, object, "flags", flags_object); JS_SetPropertyStr(context, object, "flags", flags_object);
const char* destroy_reason = tf_ssb_connection_get_destroy_reason(connection);
if (destroy_reason)
{
JS_SetPropertyStr(context, object, "destroy_reason", JS_NewString(context, destroy_reason));
}
JS_SetPropertyUint32(context, result, i, object); JS_SetPropertyUint32(context, result, i, object);
} }
} }
@ -1193,7 +1198,7 @@ static JSValue _tf_ssb_closeConnection(JSContext* context, JSValueConst this_val
tf_ssb_connection_t* connection = tf_ssb_connection_get(ssb, id); tf_ssb_connection_t* connection = tf_ssb_connection_get(ssb, id);
if (connection) if (connection)
{ {
tf_ssb_connection_close(connection); tf_ssb_connection_close(connection, "Close requested by user.");
} }
JS_FreeCString(context, id); JS_FreeCString(context, id);
return connection ? JS_TRUE : JS_FALSE; return connection ? JS_TRUE : JS_FALSE;
@ -2234,53 +2239,60 @@ static void _tf_ssb_private_message_decrypt_work(tf_ssb_t* ssb, void* user_data)
if (found) if (found)
{ {
uint8_t* decoded = tf_malloc(work->message_size); if (work->message_size >= strlen(".box") && memcmp(work->message + work->message_size - strlen(".box"), ".box", strlen(".box")) == 0)
int decoded_length = tf_base64_decode(work->message, work->message_size - strlen(".box"), decoded, work->message_size);
uint8_t* nonce = decoded;
uint8_t* public_key = decoded + crypto_box_NONCEBYTES;
if (public_key + crypto_secretbox_KEYBYTES < decoded + decoded_length)
{ {
uint8_t shared_secret[crypto_secretbox_KEYBYTES] = { 0 }; uint8_t* decoded = tf_malloc(work->message_size);
if (crypto_scalarmult(shared_secret, private_key, public_key) == 0) int decoded_length = tf_base64_decode(work->message, work->message_size - strlen(".box"), decoded, work->message_size);
uint8_t* nonce = decoded;
uint8_t* public_key = decoded + crypto_box_NONCEBYTES;
if (public_key + crypto_secretbox_KEYBYTES < decoded + decoded_length)
{ {
enum uint8_t shared_secret[crypto_secretbox_KEYBYTES] = { 0 };
if (crypto_scalarmult(shared_secret, private_key, public_key) == 0)
{ {
k_recipient_header_bytes = crypto_secretbox_MACBYTES + sizeof(uint8_t) + crypto_secretbox_KEYBYTES enum
};
for (uint8_t* p = decoded + crypto_box_NONCEBYTES + crypto_secretbox_KEYBYTES; p <= decoded + decoded_length - k_recipient_header_bytes;
p += k_recipient_header_bytes)
{
uint8_t out[k_recipient_header_bytes] = { 0 };
int opened = crypto_secretbox_open_easy(out, p, k_recipient_header_bytes, nonce, shared_secret);
if (opened != -1)
{ {
int recipients = (int)out[0]; k_recipient_header_bytes = crypto_secretbox_MACBYTES + sizeof(uint8_t) + crypto_secretbox_KEYBYTES
uint8_t* body = decoded + crypto_box_NONCEBYTES + crypto_secretbox_KEYBYTES + k_recipient_header_bytes * recipients; };
size_t body_size = decoded + decoded_length - body; for (uint8_t* p = decoded + crypto_box_NONCEBYTES + crypto_secretbox_KEYBYTES; p <= decoded + decoded_length - k_recipient_header_bytes;
uint8_t* decrypted = tf_malloc(body_size); p += k_recipient_header_bytes)
uint8_t* key = out + 1; {
if (crypto_secretbox_open_easy(decrypted, body, body_size, nonce, key) != -1) uint8_t out[k_recipient_header_bytes] = { 0 };
int opened = crypto_secretbox_open_easy(out, p, k_recipient_header_bytes, nonce, shared_secret);
if (opened != -1)
{ {
work->decrypted = (const char*)decrypted; int recipients = (int)out[0];
work->decrypted_size = body_size - crypto_secretbox_MACBYTES; uint8_t* body = decoded + crypto_box_NONCEBYTES + crypto_secretbox_KEYBYTES + k_recipient_header_bytes * recipients;
} size_t body_size = decoded + decoded_length - body;
else uint8_t* decrypted = tf_malloc(body_size);
{ uint8_t* key = out + 1;
work->error = "Received key to open secret box containing message body, but it did not work."; if (crypto_secretbox_open_easy(decrypted, body, body_size, nonce, key) != -1)
{
work->decrypted = (const char*)decrypted;
work->decrypted_size = body_size - crypto_secretbox_MACBYTES;
}
else
{
work->error = "Received key to open secret box containing message body, but it did not work.";
}
} }
} }
} }
else
{
work->error = "crypto_scalarmult failed.";
}
} }
else else
{ {
work->error = "crypto_scalarmult failed."; work->error = "Encrypted message was not long enough to contain its one-time public key.";
} }
tf_free(decoded);
} }
else else
{ {
work->error = "Encrypted message was not long enough to contains its one-time public key."; work->error = "Message does not end in \".box\".";
} }
tf_free(decoded);
} }
else else
{ {

View File

@ -20,30 +20,6 @@ static void _tf_ssb_rpc_send_peers_exchange(tf_ssb_connection_t* connection);
static void _tf_ssb_rpc_start_delete_blobs(tf_ssb_t* ssb, int delay_ms); static void _tf_ssb_rpc_start_delete_blobs(tf_ssb_t* ssb, int delay_ms);
static void _tf_ssb_rpc_start_delete_feeds(tf_ssb_t* ssb, int delay_ms); static void _tf_ssb_rpc_start_delete_feeds(tf_ssb_t* ssb, int delay_ms);
static int64_t _get_global_setting_int64(tf_ssb_t* ssb, const char* name, int64_t default_value)
{
int64_t result = default_value;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.' || ?) FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK)
{
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
{
result = sqlite3_column_int64(statement, 0);
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
}
tf_ssb_release_db_reader(ssb, db);
return result;
}
static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool default_value) static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool default_value)
{ {
bool result = default_value; bool result = default_value;
@ -243,7 +219,10 @@ static void _tf_ssb_request_blob_wants_work(tf_ssb_connection_t* connection, voi
blob_wants_work_t* work = user_data; blob_wants_work_t* work = user_data;
tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection); tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
int64_t age = _get_global_setting_int64(ssb, "blob_fetch_age_seconds", -1); int64_t age = -1;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
tf_ssb_db_get_global_setting_int64(db, "blob_fetch_age_seconds", &age);
tf_ssb_release_db_reader(ssb, db);
int64_t timestamp = -1; int64_t timestamp = -1;
if (age == 0) if (age == 0)
{ {
@ -256,7 +235,7 @@ static void _tf_ssb_request_blob_wants_work(tf_ssb_connection_t* connection, voi
timestamp = now - age * 1000ULL; timestamp = now - age * 1000ULL;
} }
sqlite3* db = tf_ssb_acquire_db_reader(ssb); db = tf_ssb_acquire_db_reader(ssb);
sqlite3_stmt* statement; sqlite3_stmt* statement;
if (sqlite3_prepare(db, "SELECT id FROM blob_wants_view WHERE id > ? AND timestamp > ? ORDER BY id LIMIT ?", -1, &statement, NULL) == SQLITE_OK) if (sqlite3_prepare(db, "SELECT id FROM blob_wants_view WHERE id > ? AND timestamp > ? ORDER BY id LIMIT ?", -1, &statement, NULL) == SQLITE_OK)
{ {
@ -336,7 +315,9 @@ static void _tf_ssb_rpc_tunnel_callback(tf_ssb_connection_t* connection, uint8_t
if (flags & k_ssb_rpc_flag_end_error) if (flags & k_ssb_rpc_flag_end_error)
{ {
tf_ssb_connection_remove_request(connection, request_number); tf_ssb_connection_remove_request(connection, request_number);
tf_ssb_connection_close(tun->connection); char buffer[1024];
snprintf(buffer, sizeof(buffer), "error from tunnel: %.*s", (int)size, message);
tf_ssb_connection_close(tun->connection, buffer);
} }
else else
{ {
@ -874,11 +855,21 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
request->out_finished = request->out_max_sequence_seen != request->sequence + k_max - 1; request->out_finished = request->out_max_sequence_seen != request->sequence + k_max - 1;
} }
static void _tf_ssb_connection_send_history_stream_destroy(tf_ssb_connection_send_history_stream_t* request)
{
for (int i = 0; i < request->out_messages_count; i++)
{
tf_free(request->out_messages[i]);
}
tf_free(request->out_messages);
tf_free(request);
}
static void _tf_ssb_connection_send_history_stream_after_work(tf_ssb_connection_t* connection, int result, void* user_data) static void _tf_ssb_connection_send_history_stream_after_work(tf_ssb_connection_t* connection, int result, void* user_data)
{ {
tf_ssb_connection_send_history_stream_t* request = user_data; tf_ssb_connection_send_history_stream_t* request = user_data;
tf_ssb_connection_adjust_write_count(connection, -1); tf_ssb_connection_adjust_write_count(connection, -1);
if (tf_ssb_connection_is_connected(connection)) if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)))
{ {
for (int i = 0; i < request->out_messages_count; i++) for (int i = 0; i < request->out_messages_count; i++)
{ {
@ -898,40 +889,40 @@ static void _tf_ssb_connection_send_history_stream_after_work(tf_ssb_connection_
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json, request->request_number, NULL, (const uint8_t*)"false", strlen("false"), NULL, NULL, NULL); tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json, request->request_number, NULL, (const uint8_t*)"false", strlen("false"), NULL, NULL, NULL);
} }
} }
for (int i = 0; i < request->out_messages_count; i++) _tf_ssb_connection_send_history_stream_destroy(request);
{
tf_free(request->out_messages[i]);
}
tf_free(request->out_messages);
tf_free(request);
} }
static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t* connection, void* user_data) static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t* connection, bool skip, void* user_data)
{ {
tf_ssb_connection_adjust_write_count(connection, 1); tf_ssb_connection_adjust_write_count(connection, 1);
if (tf_ssb_connection_is_connected(connection)) if (!skip && tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)))
{ {
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 else
{ {
_tf_ssb_connection_send_history_stream_after_work(connection, -1, user_data); _tf_ssb_connection_send_history_stream_destroy(user_data);
} }
} }
static void _tf_ssb_connection_send_history_stream( 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, bool end_request) tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys, bool live, bool end_request)
{ {
tf_ssb_connection_send_history_stream_t* async = tf_malloc(sizeof(tf_ssb_connection_send_history_stream_t)); if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)))
*async = (tf_ssb_connection_send_history_stream_t) { {
.request_number = request_number, tf_ssb_connection_send_history_stream_t* async = tf_malloc(sizeof(tf_ssb_connection_send_history_stream_t));
.sequence = sequence, *async = (tf_ssb_connection_send_history_stream_t) {
.keys = keys, .request_number = request_number,
.live = live, .sequence = sequence,
.end_request = end_request, .keys = keys,
}; .live = live,
snprintf(async->author, sizeof(async->author), "%s", author); .end_request = end_request,
tf_ssb_connection_schedule_idle(connection, _tf_ssb_connection_send_history_stream_callback, async); };
snprintf(async->author, sizeof(async->author), "%s", author);
char key[128];
snprintf(key, sizeof(key), "%s:%" PRId64, author, sequence);
tf_ssb_connection_schedule_idle(connection, key, _tf_ssb_connection_send_history_stream_callback, async);
}
} }
static void _tf_ssb_rpc_createHistoryStream( static void _tf_ssb_rpc_createHistoryStream(
@ -1003,7 +994,10 @@ static void _tf_ssb_rpc_ebt_replicate_send_clock_work(tf_ssb_connection_t* conne
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
JSValue full_clock = JS_NewObject(context); JSValue full_clock = JS_NewObject(context);
int64_t depth = _get_global_setting_int64(ssb, "replication_hops", 2); int64_t depth = 2;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
tf_ssb_db_get_global_setting_int64(db, "replication_hops", &depth);
tf_ssb_release_db_reader(ssb, db);
/* Ask for every identity we know is being followed from local accounts. */ /* Ask for every identity we know is being followed from local accounts. */
const char** visible = tf_ssb_db_get_all_visible_identities(ssb, depth); const char** visible = tf_ssb_db_get_all_visible_identities(ssb, depth);
@ -1179,11 +1173,14 @@ typedef struct _resend_clock_t
int32_t request_number; int32_t request_number;
} resend_clock_t; } resend_clock_t;
static void _tf_ssb_rpc_ebt_replicate_resend_clock(tf_ssb_connection_t* connection, void* user_data) static void _tf_ssb_rpc_ebt_replicate_resend_clock(tf_ssb_connection_t* connection, bool skip, void* user_data)
{ {
resend_clock_t* resend = user_data; resend_clock_t* resend = user_data;
_tf_ssb_rpc_ebt_replicate_send_clock(resend->connection, resend->request_number, JS_UNDEFINED); if (!skip)
tf_ssb_connection_set_sent_clock(resend->connection, true); {
_tf_ssb_rpc_ebt_replicate_send_clock(resend->connection, resend->request_number, JS_UNDEFINED);
tf_ssb_connection_set_sent_clock(resend->connection, true);
}
tf_free(user_data); tf_free(user_data);
} }
@ -1213,7 +1210,7 @@ static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t f
tf_ssb_connection_adjust_read_backpressure(connection, 1); tf_ssb_connection_adjust_read_backpressure(connection, 1);
tf_ssb_verify_strip_and_store_message(ssb, args, _tf_ssb_rpc_ebt_replicate_store_callback, connection); tf_ssb_verify_strip_and_store_message(ssb, args, _tf_ssb_rpc_ebt_replicate_store_callback, connection);
if (tf_ssb_connection_get_sent_clock(connection)) if (tf_ssb_connection_get_sent_clock(connection) && !tf_ssb_is_shutting_down(ssb))
{ {
tf_ssb_connection_set_sent_clock(connection, false); tf_ssb_connection_set_sent_clock(connection, false);
resend_clock_t* resend = tf_malloc(sizeof(resend_clock_t)); resend_clock_t* resend = tf_malloc(sizeof(resend_clock_t));
@ -1221,7 +1218,7 @@ static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t f
.connection = connection, .connection = connection,
.request_number = request_number, .request_number = request_number,
}; };
tf_ssb_connection_schedule_idle(connection, _tf_ssb_rpc_ebt_replicate_resend_clock, resend); tf_ssb_connection_schedule_idle(connection, "ebt.clock", _tf_ssb_rpc_ebt_replicate_resend_clock, resend);
} }
} }
else else
@ -1400,14 +1397,17 @@ typedef struct _delete_t
static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data) static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
{ {
delete_t* delete = user_data; delete_t* delete = user_data;
int64_t age = _get_global_setting_int64(ssb, "blob_expire_age_seconds", -1); int64_t age = -1;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
tf_ssb_db_get_global_setting_int64(db, "blob_expire_age_seconds", &age);
tf_ssb_release_db_reader(ssb, db);
if (age <= 0) if (age <= 0)
{ {
_tf_ssb_rpc_checkpoint(ssb); _tf_ssb_rpc_checkpoint(ssb);
return; return;
} }
int64_t start_ns = uv_hrtime(); int64_t start_ns = uv_hrtime();
sqlite3* db = tf_ssb_acquire_db_writer(ssb); db = tf_ssb_acquire_db_writer(ssb);
sqlite3_stmt* statement; sqlite3_stmt* statement;
int64_t now = (int64_t)time(NULL) * 1000ULL; int64_t now = (int64_t)time(NULL) * 1000ULL;
int64_t timestamp = now - age * 1000ULL; int64_t timestamp = now - age * 1000ULL;
@ -1474,7 +1474,10 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
return; return;
} }
int64_t start_ns = uv_hrtime(); int64_t start_ns = uv_hrtime();
int replication_hops = (int)_get_global_setting_int64(ssb, "replication_hops", 2); int64_t replication_hops = 2;
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
tf_ssb_db_get_global_setting_int64(db, "replication_hops", &replication_hops);
tf_ssb_release_db_reader(ssb, db);
const char** identities = tf_ssb_db_get_all_visible_identities(ssb, replication_hops); const char** identities = tf_ssb_db_get_all_visible_identities(ssb, replication_hops);
JSMallocFunctions funcs = { 0 }; JSMallocFunctions funcs = { 0 };
@ -1493,7 +1496,7 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
JS_FreeValue(context, json); JS_FreeValue(context, json);
JS_FreeValue(context, array); JS_FreeValue(context, array);
sqlite3* db = tf_ssb_acquire_db_writer(ssb); db = tf_ssb_acquire_db_writer(ssb);
sqlite3_stmt* statement; sqlite3_stmt* statement;
if (sqlite3_prepare(db, if (sqlite3_prepare(db,
"DELETE FROM messages WHERE author IN (" "DELETE FROM messages WHERE author IN ("

View File

@ -536,7 +536,7 @@ void tf_ssb_test_rooms(const tf_test_options_t* options)
uv_run(&loop, UV_RUN_NOWAIT); uv_run(&loop, UV_RUN_NOWAIT);
tf_ssb_connection_close(tun0); tf_ssb_connection_close(tun0, "done");
uv_run(&loop, UV_RUN_NOWAIT); uv_run(&loop, UV_RUN_NOWAIT);

View File

@ -17,6 +17,7 @@
#include "util.js.h" #include "util.js.h"
#include "version.h" #include "version.h"
#include "ares.h"
#include "backtrace.h" #include "backtrace.h"
#include "quickjs.h" #include "quickjs.h"
#include "sqlite3.h" #include "sqlite3.h"
@ -701,6 +702,7 @@ static JSValue _tf_task_version(JSContext* context, JSValueConst this_val, int a
JS_SetPropertyStr(context, version, "openssl", JS_NewString(context, OpenSSL_version(OPENSSL_VERSION))); JS_SetPropertyStr(context, version, "openssl", JS_NewString(context, OpenSSL_version(OPENSSL_VERSION)));
#endif #endif
const char* sodium_version_string(); const char* sodium_version_string();
JS_SetPropertyStr(context, version, "c-ares", JS_NewString(context, ares_version(NULL)));
JS_SetPropertyStr(context, version, "libsodium", JS_NewString(context, sodium_version_string())); JS_SetPropertyStr(context, version, "libsodium", JS_NewString(context, sodium_version_string()));
JS_SetPropertyStr(context, version, "zlib", JS_NewString(context, zlibVersion())); JS_SetPropertyStr(context, version, "zlib", JS_NewString(context, zlibVersion()));
tf_trace_end(task->_trace); tf_trace_end(task->_trace);
@ -932,21 +934,6 @@ char* tf_task_get_hitches(tf_task_t* task)
return result; return result;
} }
char* tf_task_get_disconnections(tf_task_t* task)
{
JSContext* context = task->_context;
tf_trace_begin(task->_trace, __func__);
JSValue object = tf_ssb_get_disconnection_debug(task->_ssb, context);
JSValue json = JS_JSONStringify(context, object, JS_NULL, JS_NewInt32(context, 2));
const char* string = JS_ToCString(context, json);
char* result = tf_strdup(string);
JS_FreeCString(context, string);
JS_FreeValue(context, json);
JS_FreeValue(context, object);
tf_trace_end(task->_trace);
return result;
}
static JSValue _tf_task_getFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) static JSValue _tf_task_getFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{ {
tf_task_t* task = JS_GetContextOpaque(context); tf_task_t* task = JS_GetContextOpaque(context);

View File

@ -310,14 +310,6 @@ void tf_task_remove_child(tf_task_t* task, tf_taskstub_t* child);
*/ */
bool tf_task_send_error_to_parent(tf_task_t* task, JSValue error); bool tf_task_send_error_to_parent(tf_task_t* task, JSValue error);
/**
** Get a report of recent disconnections.
** @param task The task.
** @return A JSON representation of recent disconnections that must be freed
** with tf_free().
*/
char* tf_task_get_disconnections(tf_task_t* task);
/** /**
** Get a report of miscellaneous debug information. ** Get a report of miscellaneous debug information.
** @param task The task. ** @param task The task.

View File

@ -52,7 +52,8 @@ static void _test_nop(const tf_test_options_t* options)
_write_file("out/test.js", "print('hi');"); _write_file("out/test.js", "print('hi');");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
(void)result; (void)result;
@ -65,7 +66,8 @@ static void _test_exception(const tf_test_options_t* options)
_write_file("out/test.js", "throw new Error('oops');"); _write_file("out/test.js", "throw new Error('oops');");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("result = %d\n", result); tf_printf("result = %d\n", result);
@ -98,7 +100,8 @@ static void _test_sandbox(const tf_test_options_t* options)
"exit(r);\n"); "exit(r);\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
(void)result; (void)result;
@ -130,7 +133,8 @@ static void _test_child(const tf_test_options_t* options)
"exit(0);\n"); "exit(0);\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
(void)result; (void)result;
@ -168,7 +172,8 @@ static void _test_promise(const tf_test_options_t* options)
"}\n"); "}\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
(void)result; (void)result;
@ -210,7 +215,8 @@ static void _test_promise_remote_throw(const tf_test_options_t* options)
"}\n"); "}\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
(void)result; (void)result;
@ -254,7 +260,8 @@ static void _test_promise_remote_reject(const tf_test_options_t* options)
"}\n"); "}\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
(void)result; (void)result;
@ -331,7 +338,8 @@ static void _test_this(const tf_test_options_t* options)
"exit(0);\n"); "exit(0);\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
@ -361,7 +369,8 @@ static void _test_await(const tf_test_options_t* options)
"\n"); "\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
@ -391,14 +400,16 @@ static void _test_import(const tf_test_options_t* options)
"}\n"); "}\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
assert(WIFEXITED(result)); assert(WIFEXITED(result));
assert(WEXITSTATUS(result) == 0); assert(WEXITSTATUS(result) == 0);
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/bad.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/bad.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
result = system(command); result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
@ -416,7 +427,8 @@ static void _test_exit(const tf_test_options_t* options)
_write_file("out/blah.js", "\n"); _write_file("out/blah.js", "\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
@ -434,7 +446,8 @@ static void _test_icu(const tf_test_options_t* options)
"print(parseInt('3').toLocaleString());\n"); "print(parseInt('3').toLocaleString());\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
@ -482,7 +495,8 @@ static void _test_uint8array(const tf_test_options_t* options)
"}\n"); "}\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
@ -523,7 +537,8 @@ static void _test_float(const tf_test_options_t* options)
"print(\"child ready\");\n"); "print(\"child ready\");\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
(void)result; (void)result;
@ -610,7 +625,8 @@ static void _test_socket(const tf_test_options_t* options)
"});\n"); "});\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));
@ -659,7 +675,8 @@ static void _test_b64(const tf_test_options_t* options)
"}\n"); "}\n");
char command[256]; char command[256];
snprintf(command, sizeof(command), "%s run --db-path=:memory: -s out/test.js" TEST_ARGS, options->exe_path); unlink("out/test_db0.sqlite");
snprintf(command, sizeof(command), "%s run --db-path=out/test_db0.sqlite -s out/test.js" TEST_ARGS, options->exe_path);
tf_printf("%s\n", command); tf_printf("%s\n", command);
int result = system(command); int result = system(command);
tf_printf("returned %d\n", WEXITSTATUS(result)); tf_printf("returned %d\n", WEXITSTATUS(result));

View File

@ -1,2 +1,2 @@
#define VERSION_NUMBER "0.0.26" #define VERSION_NUMBER "0.0.27-wip"
#define VERSION_NAME "This program kills fascists." #define VERSION_NAME "This program kills fascists."

3
test.c Normal file
View File

@ -0,0 +1,3 @@
int main() {
return 0;
}

View File

@ -73,8 +73,10 @@ build_the_thing() {
no-whirlpool no-whirlpool
no-weak-ssl-ciphers no-weak-ssl-ciphers
no-zlib no-zlib
-Os -Oz
-DOPENSSL_SMALL_FOOTPRINT -DOPENSSL_SMALL_FOOTPRINT
-ffunction-sections
-fdata-sections
-flto" -flto"
pwd pwd
echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \ echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \

View File

@ -29,14 +29,14 @@ do
case $build_target in case $build_target in
ios64-xcrun) ios64-xcrun)
TRIBLE="arm64-darwin-ios" TRIBLE="arm64-darwin-ios"
OPTIONS="--static -static -ffunction-sections -fdata-sections -fPIC -Wno-macro-redefined -miphoneos-version-min=9.0" OPTIONS="--static -static -Os -ffunction-sections -fdata-sections -fPIC -Wno-macro-redefined -miphoneos-version-min=9.0"
DESTDIR="/tmp/$BUILD_DIR/arm64-ios" DESTDIR="/tmp/$BUILD_DIR/arm64-ios"
SSL_TARGET="ios64-xcrun" SSL_TARGET="ios64-xcrun"
CC=clang CC=clang
;; ;;
iossimulator-xcrun) iossimulator-xcrun)
TRIBLE="x86_64-darwin-ios" TRIBLE="x86_64-darwin-ios"
OPTIONS="--static -static -ffunction-sections -fdata-sections -fPIC -Wno-macro-redefined" OPTIONS="--static -static -Os -ffunction-sections -fdata-sections -fPIC -Wno-macro-redefined"
DESTDIR="/tmp/$BUILD_DIR/x86_64-iossim" DESTDIR="/tmp/$BUILD_DIR/x86_64-iossim"
SSL_TARGET="iossimulator-xcrun" SSL_TARGET="iossimulator-xcrun"
CC=clang CC=clang

92
tools/ssl-local Executable file
View File

@ -0,0 +1,92 @@
#!/bin/bash
BUILD_PLATFORM=$(uname -s)
if [[ -z $BUILD_TARGET ]]; then
BUILD_TARGET=$(uname -m)
WORK_DIR=out/openssl-local
else
WORK_DIR=out/openssl-$BUILD_TARGET
if [[ -z $SSL_TARGET ]]; then
SSL_TARGET=linux-$BUILD_TARGET
fi
fi
rm -rf $WORK_DIR
cp -aRf deps/openssl_src/ $WORK_DIR
echo "Building"
pwd
pushd $WORK_DIR || exit 128
rm -rf $DESTDIR
echo $PATH
export GLOBAL_OPTIONS="
no-apps
no-asm
no-async
no-autoerrinit
no-autoload-config
no-cmp
no-cms
no-comp
no-deprecated
no-dgram
no-docs
no-dsa
no-dso
no-dtls
no-dtls1
no-dtls1-method
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
no-ssl3
no-ssl-trace
no-stdio
no-tests
no-thread-pool
no-threads
no-tls1
no-tls1-method
no-trace
no-ui-console
no-uplink
no-whirlpool
no-weak-ssl-ciphers
no-zlib
-Os
-DOPENSSL_SMALL_FOOTPRINT
-ffunction-sections
-fdata-sections"
pwd
echo "./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS" && \
./Configure $SSL_TARGET $OPTIONS $GLOBAL_OPTIONS && \
make -s clean && \
make -s build_generated && \
make -s libcrypto.a libssl.a || exit 128
popd
echo WORK_DIR=$WORK_DIR
rm -rf deps/openssl/$BUILD_PLATFORM/$BUILD_TARGET/
mkdir -p deps/openssl/$BUILD_PLATFORM/$BUILD_TARGET/usr/local/include/
mkdir -p deps/openssl/$BUILD_PLATFORM/$BUILD_TARGET/usr/local/lib/
cp -R $WORK_DIR/include/* deps/openssl/$BUILD_PLATFORM/$BUILD_TARGET/usr/local/include/
cp $WORK_DIR/*.a deps/openssl/$BUILD_PLATFORM/$BUILD_TARGET/usr/local/lib/
echo Success