Compare commits
	
		
			17 Commits
		
	
	
		
			v0.0.26
			...
			58e9646fa6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 58e9646fa6 | |||
| 500f172561 | |||
| 68f6c90ea4 | |||
| 41e91f2922 | |||
| 999117cfeb | |||
| 6185df512f | |||
| 0cbf66c007 | |||
| cd378b721d | |||
| 547d38d1ef | |||
| dca56af5b9 | |||
| 224442772e | |||
| 003951fdf7 | |||
| d51b3da1b4 | |||
| 69f4af84db | |||
| 771759b252 | |||
| 20c7a71db6 | |||
| 8475ee0985 | 
@@ -24,15 +24,14 @@ jobs:
 | 
			
		||||
        uses: android-actions/setup-android@v3
 | 
			
		||||
        with:
 | 
			
		||||
          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: docker build .
 | 
			
		||||
      - uses: actions/upload-artifact@v3
 | 
			
		||||
        with:
 | 
			
		||||
          path: out/TildeFriends-release.fdroid.apk
 | 
			
		||||
      - uses: actions/upload-artifact@v3
 | 
			
		||||
        with:
 | 
			
		||||
          path: out/winrelease/tildefriends.exe
 | 
			
		||||
      - uses: actions/upload-artifact@v3
 | 
			
		||||
        with:
 | 
			
		||||
          path: out/tildefriends-x86_64.AppImage
 | 
			
		||||
          path: |
 | 
			
		||||
            out/TildeFriends-release.fdroid.apk
 | 
			
		||||
            out/winrelease/tildefriends.exe
 | 
			
		||||
            out/tildefriends-x86_64.AppImage
 | 
			
		||||
            out/release/tildefriends.standalone
 | 
			
		||||
            out/armrelease/tildefriends.standalone
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										67
									
								
								GNUmakefile
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								GNUmakefile
									
									
									
									
									
								
							@@ -16,8 +16,8 @@ MAKEFLAGS += --no-builtin-rules
 | 
			
		||||
## LD := Linker.
 | 
			
		||||
## ANDROID_SDK := Path to the Android SDK.
 | 
			
		||||
 | 
			
		||||
VERSION_CODE := 31
 | 
			
		||||
VERSION_NUMBER := 0.0.26
 | 
			
		||||
VERSION_CODE := 32
 | 
			
		||||
VERSION_NUMBER := 0.0.27-wip
 | 
			
		||||
VERSION_NAME := This program kills fascists.
 | 
			
		||||
 | 
			
		||||
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3470200.zip
 | 
			
		||||
@@ -34,6 +34,7 @@ ANDROID_SDK ?= ~/Android/Sdk
 | 
			
		||||
BUNDLETOOL = out/bundletool.jar
 | 
			
		||||
 | 
			
		||||
HAVE_WIN := 0
 | 
			
		||||
HAVE_CROSS_AARCH64 := 0
 | 
			
		||||
 | 
			
		||||
export SOURCE_DATE_EPOCH=1
 | 
			
		||||
export TZ=UTC
 | 
			
		||||
@@ -45,6 +46,9 @@ BUILD_TYPES := debug release
 | 
			
		||||
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_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)
 | 
			
		||||
BUILD_TYPES := debug release
 | 
			
		||||
CFLAGS += -Dstatic_assert=_Static_assert
 | 
			
		||||
@@ -125,6 +129,13 @@ ifeq ($(HAVE_WIN),1)
 | 
			
		||||
BUILD_TYPES += windebug winrelease
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
AARCH64_TARGETS := \
 | 
			
		||||
	out/armdebug/tildefriends \
 | 
			
		||||
	out/armrelease/tildefriends
 | 
			
		||||
ifeq ($(HAVE_CROSS_AARCH64),1)
 | 
			
		||||
BUILD_TYPES += armdebug armrelease
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
LINUX_TARGETS := \
 | 
			
		||||
	out/debug/tildefriends \
 | 
			
		||||
	out/release/tildefriends
 | 
			
		||||
@@ -149,6 +160,9 @@ all: $(IOS_APPS) \
 | 
			
		||||
	out/tildefriends-iossimdebug.app/tildefriends \
 | 
			
		||||
	out/tildefriends-iossimrelease.app/tildefriends
 | 
			
		||||
endif
 | 
			
		||||
ifeq ($(HAVE_CROSS_AARCH64),1)
 | 
			
		||||
all: out/armrelease/tildefriends.standalone
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
DEBUG_TARGETS := \
 | 
			
		||||
	out/debug/tildefriends \
 | 
			
		||||
@@ -159,7 +173,8 @@ DEBUG_TARGETS := \
 | 
			
		||||
	out/androiddebug/tildefriends \
 | 
			
		||||
	out/androiddebug-armv7a/tildefriends \
 | 
			
		||||
	out/androiddebug-x86_64/tildefriends \
 | 
			
		||||
	out/androiddebug-x86/tildefriends
 | 
			
		||||
	out/androiddebug-x86/tildefriends \
 | 
			
		||||
	out/armdebug/tildefriends
 | 
			
		||||
RELEASE_TARGETS := \
 | 
			
		||||
	out/release/tildefriends \
 | 
			
		||||
	out/winrelease/tildefriends.exe \
 | 
			
		||||
@@ -169,7 +184,8 @@ RELEASE_TARGETS := \
 | 
			
		||||
	out/androidrelease/tildefriends \
 | 
			
		||||
	out/androidrelease-armv7a/tildefriends \
 | 
			
		||||
	out/androidrelease-x86_64/tildefriends \
 | 
			
		||||
	out/androidrelease-x86/tildefriends
 | 
			
		||||
	out/androidrelease-x86/tildefriends \
 | 
			
		||||
	out/armrelease/tildefriends
 | 
			
		||||
ALL_TARGETS = $(DEBUG_TARGETS) $(RELEASE_TARGETS)
 | 
			
		||||
ANDROID_RELEASE_TARGETS := $(filter-out $(DEBUG_TARGETS),$(ANDROID_TARGETS))
 | 
			
		||||
NONANDROID_RELEASE_TARGETS := $(filter-out $(ANDROID_ARM64_TARGETS),$(RELEASE_TARGETS))
 | 
			
		||||
@@ -208,6 +224,10 @@ $(WINDOWS_TARGETS): LDFLAGS += \
 | 
			
		||||
	-static \
 | 
			
		||||
	-lm \
 | 
			
		||||
	-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)
 | 
			
		||||
$(MACOS_TARGETS): CC = xcrun clang
 | 
			
		||||
$(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
 | 
			
		||||
@@ -242,10 +262,12 @@ $(IOS_TARGETS): CFLAGS += -miphoneos-version-min=9.0 -Ideps/openssl/ios/ios64-xc
 | 
			
		||||
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=9.0 -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib
 | 
			
		||||
$(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include
 | 
			
		||||
$(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_S),Linux)
 | 
			
		||||
all: appimage
 | 
			
		||||
all: appimage out/release/tildefriends.standalone
 | 
			
		||||
endif
 | 
			
		||||
ifneq ($(UNAME_S),Haiku)
 | 
			
		||||
out/debug/tildefriends: CFLAGS += -fsanitize=address -fsanitize=undefined -fno-common
 | 
			
		||||
@@ -260,7 +282,7 @@ endif
 | 
			
		||||
 | 
			
		||||
get_objs = \
 | 
			
		||||
	$(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,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))))) \
 | 
			
		||||
@@ -725,7 +747,7 @@ $(MINIUNZIP_OBJS): CFLAGS += \
 | 
			
		||||
LDFLAGS += \
 | 
			
		||||
	-pthread \
 | 
			
		||||
	-lm
 | 
			
		||||
$(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
 | 
			
		||||
$(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS): LDFLAGS += \
 | 
			
		||||
	-lssl \
 | 
			
		||||
	-lcrypto
 | 
			
		||||
ifneq ($(UNAME_S),Haiku)
 | 
			
		||||
@@ -765,6 +787,8 @@ $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
 | 
			
		||||
##
 | 
			
		||||
debug: ## Build a debug 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.
 | 
			
		||||
unix: debug release ## Build all UNIX targets.
 | 
			
		||||
win: windebug winrelease ## Build all Windows targets.
 | 
			
		||||
@@ -1106,6 +1130,27 @@ $(ANDROID_DEPS):
 | 
			
		||||
	+@ANDROID_NDK_ROOT=$(ANDROID_NDK) tools/ssl-android
 | 
			
		||||
$(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):
 | 
			
		||||
	+@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- BUILD_TARGET=aarch64 tools/ssl-local
 | 
			
		||||
$(filter $(BUILD_DIR)/armdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/armrelease/%,$(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):
 | 
			
		||||
	+@tools/ssl-local
 | 
			
		||||
$(filter $(BUILD_DIR)/macosdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/macosrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
ifeq ($(HAVE_WIN),1)
 | 
			
		||||
WINDOWS_DEPS := deps/openssl/mingw64/usr/local/lib/libssl.a
 | 
			
		||||
$(WINDOWS_DEPS):
 | 
			
		||||
@@ -1198,7 +1243,7 @@ tarball: ## Build an all-inclusive source tarball (.tar.xz).
 | 
			
		||||
.PHONY: tarball
 | 
			
		||||
 | 
			
		||||
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/
 | 
			
		||||
	@echo "[cp] tildefriends-$(VERSION_NUMBER).tar.xz"
 | 
			
		||||
	@cp out/tildefriends-$(VERSION_NUMBER).tar.xz dist/tildefriends-$(VERSION_NUMBER).tar.xz
 | 
			
		||||
@@ -1216,6 +1261,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
 | 
			
		||||
	@echo "[cp] 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
 | 
			
		||||
 | 
			
		||||
dist-test: dist ## Exercise some built distributable files, making sure they work as intended.
 | 
			
		||||
@@ -1258,7 +1307,7 @@ help: ## Display this help message.
 | 
			
		||||
	/^##/ { sub(/^## ?/, ""); print $$0 } \
 | 
			
		||||
	/^[[:alnum:]-]+:.*##/ { \
 | 
			
		||||
		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))
 | 
			
		||||
	@echo "" # Blank line.
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
{
 | 
			
		||||
	"type": "tildefriends-app",
 | 
			
		||||
	"emoji": "🐌",
 | 
			
		||||
	"previous": "&q/1uGp0jMvsYGW7Gj8E33kf6UFo/uNYDXg3zo1sVKQg=.sha256"
 | 
			
		||||
	"emoji": "🦀",
 | 
			
		||||
	"previous": "&QuEaXfdWGAl42dkJBoHocZbtKRT3zT25BNgCo88CSfE=.sha256"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -112,26 +112,24 @@ class TfElement extends LitElement {
 | 
			
		||||
 | 
			
		||||
	keydown(event) {
 | 
			
		||||
		if (event.altKey && event.key == 'ArrowUp') {
 | 
			
		||||
			this.next_channel(1);
 | 
			
		||||
			this.next_channel(-1);
 | 
			
		||||
			event.preventDefault();
 | 
			
		||||
		} else if (event.altKey && event.key == 'ArrowDown') {
 | 
			
		||||
			this.next_channel(-1);
 | 
			
		||||
			this.next_channel(1);
 | 
			
		||||
			event.preventDefault();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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));
 | 
			
		||||
		if (index != -1) {
 | 
			
		||||
			index += delta;
 | 
			
		||||
			this.set_hash(
 | 
			
		||||
				'#' +
 | 
			
		||||
					encodeURIComponent(
 | 
			
		||||
						channel_names[(index + channel_names.length) % channel_names.length]
 | 
			
		||||
					)
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
		index = index != -1 ? index + delta : 0;
 | 
			
		||||
		tfrpc.rpc.setHash(
 | 
			
		||||
			'#' +
 | 
			
		||||
				encodeURIComponent(
 | 
			
		||||
					channel_names[(index + channel_names.length) % channel_names.length]
 | 
			
		||||
				)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	set_hash(hash) {
 | 
			
		||||
@@ -150,6 +148,7 @@ class TfElement extends LitElement {
 | 
			
		||||
	async fetch_about(ids, users) {
 | 
			
		||||
		const k_cache_version = 1;
 | 
			
		||||
		let cache = await tfrpc.rpc.databaseGet('about');
 | 
			
		||||
		let original_cache = cache;
 | 
			
		||||
		cache = cache ? JSON.parse(cache) : {};
 | 
			
		||||
		if (cache.version !== k_cache_version) {
 | 
			
		||||
			cache = {
 | 
			
		||||
@@ -215,7 +214,13 @@ class TfElement extends LitElement {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		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 || {};
 | 
			
		||||
		for (let id of Object.keys(cache.about)) {
 | 
			
		||||
			users[id] = Object.assign(users[id] || {}, cache.about[id]);
 | 
			
		||||
@@ -538,15 +543,12 @@ class TfElement extends LitElement {
 | 
			
		||||
				)}
 | 
			
		||||
			</div>
 | 
			
		||||
		`;
 | 
			
		||||
		let contents = !this.loaded
 | 
			
		||||
			? this.loading
 | 
			
		||||
				? html`<div
 | 
			
		||||
							class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge"
 | 
			
		||||
						>
 | 
			
		||||
							Loading...
 | 
			
		||||
						</div>
 | 
			
		||||
						${this.render_tab()}`
 | 
			
		||||
				: html`<div>Select or create an identity.</div>`
 | 
			
		||||
		let contents = !this.loaded || this.loading
 | 
			
		||||
			? html`<div
 | 
			
		||||
						class="w3-display-middle w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge w3-xlarge"
 | 
			
		||||
					>
 | 
			
		||||
						<span class="w3-spin" style="display: inline-block">🦀</span> Loading...
 | 
			
		||||
					</div>`
 | 
			
		||||
			: this.render_tab();
 | 
			
		||||
		return html`
 | 
			
		||||
			<div
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ class TfComposeElement extends LitElement {
 | 
			
		||||
			drafts: {type: Object},
 | 
			
		||||
			author: {type: String},
 | 
			
		||||
			channel: {type: String},
 | 
			
		||||
			new_thread: {type: Boolean},
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -28,6 +29,7 @@ class TfComposeElement extends LitElement {
 | 
			
		||||
		this.apps = undefined;
 | 
			
		||||
		this.drafts = {};
 | 
			
		||||
		this.author = undefined;
 | 
			
		||||
		this.new_thread = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	process_text(text) {
 | 
			
		||||
@@ -200,9 +202,21 @@ class TfComposeElement extends LitElement {
 | 
			
		||||
			channel: this.channel,
 | 
			
		||||
		};
 | 
			
		||||
		if (this.root || this.branch) {
 | 
			
		||||
			message.root = this.root;
 | 
			
		||||
			message.root = this.new_thread ? this.branch ?? this.root : this.root;
 | 
			
		||||
			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) {
 | 
			
		||||
			message.mentions = Object.values(draft.mentions);
 | 
			
		||||
		}
 | 
			
		||||
@@ -469,6 +483,16 @@ 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() {
 | 
			
		||||
		return this.drafts[this.branch || ''] || {};
 | 
			
		||||
	}
 | 
			
		||||
@@ -562,7 +586,9 @@ class TfComposeElement extends LitElement {
 | 
			
		||||
				${Object.values(draft.mentions || {}).map((x) =>
 | 
			
		||||
					self.render_mention(x)
 | 
			
		||||
				)}
 | 
			
		||||
				${this.render_attach_app()} ${this.render_content_warning()}
 | 
			
		||||
				${this.render_attach_app()}
 | 
			
		||||
				${this.render_content_warning()}
 | 
			
		||||
				${this.render_new_thread()}
 | 
			
		||||
				<button class="w3-button w3-theme-d1" id="submit" @click=${this.submit}>
 | 
			
		||||
					Submit
 | 
			
		||||
				</button>
 | 
			
		||||
 
 | 
			
		||||
@@ -51,15 +51,21 @@ class TfTabNewsFeedElement extends LitElement {
 | 
			
		||||
		if (this.hash == '#@') {
 | 
			
		||||
			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
 | 
			
		||||
					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
 | 
			
		||||
						FROM mentions
 | 
			
		||||
						JOIN messages_refs ON mentions.id = messages_refs.ref
 | 
			
		||||
						JOIN messages ON messages_refs.message = messages.id
 | 
			
		||||
					UNION
 | 
			
		||||
					SELECT * FROM mentions
 | 
			
		||||
				`,
 | 
			
		||||
				[
 | 
			
		||||
					'"' + this.whoami.replace('"', '""') + '"',
 | 
			
		||||
 
 | 
			
		||||
@@ -176,7 +176,7 @@ class TfTabNewsElement extends LitElement {
 | 
			
		||||
		return html`
 | 
			
		||||
			<div
 | 
			
		||||
				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"
 | 
			
		||||
				id="sidebar"
 | 
			
		||||
			>
 | 
			
		||||
				<div
 | 
			
		||||
@@ -216,6 +216,13 @@ class TfTabNewsElement extends LitElement {
 | 
			
		||||
					style=${this.hash == '#🔐' ? 'font-weight: bold' : undefined}
 | 
			
		||||
					>🔐private ${this.unread_status('🔐')}</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(
 | 
			
		||||
					(x) => html`
 | 
			
		||||
						<a
 | 
			
		||||
 
 | 
			
		||||
@@ -21,14 +21,14 @@
 | 
			
		||||
}:
 | 
			
		||||
pkgs.stdenv.mkDerivation rec {
 | 
			
		||||
  pname = "tildefriends";
 | 
			
		||||
  version = "0.0.25";
 | 
			
		||||
  version = "0.0.26";
 | 
			
		||||
 | 
			
		||||
  src = pkgs.fetchFromGitea {
 | 
			
		||||
    domain = "dev.tildefriends.net";
 | 
			
		||||
    owner = "cory";
 | 
			
		||||
    repo = "tildefriends";
 | 
			
		||||
    rev = "v${version}";
 | 
			
		||||
    hash = "sha256-Rfk+CUhi+Ss0z70CCgmtVM/w4nCL1GX/MsD4sPYIa5s=";
 | 
			
		||||
    hash = "sha256-XJ7M++risfsRn9GkS1zjTQpqqV5S09uyimeVzU9hGGg=";
 | 
			
		||||
    fetchSubmodules = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
	package="com.unprompted.tildefriends"
 | 
			
		||||
	android:versionCode="31"
 | 
			
		||||
	android:versionName="0.0.26">
 | 
			
		||||
	android:versionCode="32"
 | 
			
		||||
	android:versionName="0.0.27-wip">
 | 
			
		||||
	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 | 
			
		||||
	<uses-permission android:name="android.permission.INTERNET"/>
 | 
			
		||||
	<application
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										148
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										148
									
								
								src/main.c
									
									
									
									
									
								
							@@ -25,6 +25,7 @@
 | 
			
		||||
 | 
			
		||||
#if defined(__linux__)
 | 
			
		||||
#include <sys/prctl.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
@@ -41,7 +42,102 @@
 | 
			
		||||
 | 
			
		||||
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__)
 | 
			
		||||
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[])
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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("options:\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_free((void*)default_db_path);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_create_directories_for_file(db_path, 0700);
 | 
			
		||||
	tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
 | 
			
		||||
	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_destroy(ssb);
 | 
			
		||||
	tf_free((void*)default_db_path);
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _tf_command_export(const char* file, int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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("options:\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("\n");
 | 
			
		||||
		tf_printf("paths                      Paths of apps to export (example: /~core/ssb /~user/app).\n");
 | 
			
		||||
		tf_free((void*)default_db_path);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -271,6 +373,7 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	tf_ssb_destroy(ssb);
 | 
			
		||||
	tf_free((void*)default_db_path);
 | 
			
		||||
	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* 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;
 | 
			
		||||
	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("  -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("  -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("  -h, --help               Show this usage information.\n");
 | 
			
		||||
		tf_free((void*)default_db_path);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int result = EXIT_FAILURE;
 | 
			
		||||
	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);
 | 
			
		||||
	uint8_t private_key[512] = { 0 };
 | 
			
		||||
	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_ssb_destroy(ssb);
 | 
			
		||||
	tf_free((void*)default_db_path);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
	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("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("  -h, --help               Show this usage information.\n");
 | 
			
		||||
		tf_free((void*)default_db_path);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -441,6 +550,7 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
 | 
			
		||||
	if (!blob_file)
 | 
			
		||||
	{
 | 
			
		||||
		tf_printf("Failed to open %s: %s.\n", file_path, strerror(errno));
 | 
			
		||||
		tf_free((void*)default_db_path);
 | 
			
		||||
		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));
 | 
			
		||||
		fclose(blob_file);
 | 
			
		||||
		tf_free(data);
 | 
			
		||||
		tf_free((void*)default_db_path);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
	fclose(blob_file);
 | 
			
		||||
 | 
			
		||||
	char id[256];
 | 
			
		||||
	_create_directories_for_file(db_path, 0700);
 | 
			
		||||
	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))
 | 
			
		||||
	{
 | 
			
		||||
@@ -476,13 +588,15 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
 | 
			
		||||
	}
 | 
			
		||||
	tf_ssb_destroy(ssb);
 | 
			
		||||
	tf_free(data);
 | 
			
		||||
	tf_free((void*)default_db_path);
 | 
			
		||||
	return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int _tf_command_verify(const char* file, int argc, char* argv[])
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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("options:\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_free((void*)default_db_path);
 | 
			
		||||
		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);
 | 
			
		||||
	bool verified = tf_ssb_db_verify(ssb, identity);
 | 
			
		||||
	tf_ssb_destroy(ssb);
 | 
			
		||||
	tf_free((void*)default_db_path);
 | 
			
		||||
	return verified ? EXIT_SUCCESS : EXIT_FAILURE;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
@@ -673,13 +789,14 @@ static void _shed_privileges()
 | 
			
		||||
 | 
			
		||||
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 = {
 | 
			
		||||
		.count = 1,
 | 
			
		||||
		.script = "core/core.js",
 | 
			
		||||
		.http_port = 12345,
 | 
			
		||||
		.https_port = 12346,
 | 
			
		||||
		.ssb_port = 8008,
 | 
			
		||||
		.db_path = k_db_path_default,
 | 
			
		||||
		.db_path = default_db_path,
 | 
			
		||||
	};
 | 
			
		||||
	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("  -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("  -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("  -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");
 | 
			
		||||
@@ -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("  -v, --verbose              Log raw messages.\n");
 | 
			
		||||
		tf_printf("  -h, --help                 Show this usage information.\n");
 | 
			
		||||
		tf_free((void*)default_db_path);
 | 
			
		||||
		return EXIT_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -780,6 +898,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
 | 
			
		||||
	setpgid(0, 0);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	_create_directories_for_file(args.db_path, 0700);
 | 
			
		||||
	if (args.count == 1)
 | 
			
		||||
	{
 | 
			
		||||
		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(threads);
 | 
			
		||||
	}
 | 
			
		||||
	tf_free((void*)default_db_path);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1088,7 +1208,7 @@ void tf_run_thread_start(const char* zip_path)
 | 
			
		||||
		.http_port = 12345,
 | 
			
		||||
		.https_port = 12346,
 | 
			
		||||
		.ssb_port = 8008,
 | 
			
		||||
		.db_path = k_db_path_default,
 | 
			
		||||
		.db_path = "db.sqlite",
 | 
			
		||||
		.one_proc = true,
 | 
			
		||||
		.zip = zip_path,
 | 
			
		||||
	};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										64
									
								
								src/ssb.c
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								src/ssb.c
									
									
									
									
									
								
							@@ -4222,63 +4222,19 @@ typedef struct _update_settings_t
 | 
			
		||||
	char room_name[1024];
 | 
			
		||||
} update_settings_t;
 | 
			
		||||
 | 
			
		||||
static bool _get_global_setting_string(tf_ssb_t* ssb, const char* name, char* out_value, size_t size)
 | 
			
		||||
{
 | 
			
		||||
	bool result = false;
 | 
			
		||||
	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)
 | 
			
		||||
			{
 | 
			
		||||
				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));
 | 
			
		||||
	}
 | 
			
		||||
	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)
 | 
			
		||||
{
 | 
			
		||||
	bool 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)
 | 
			
		||||
			{
 | 
			
		||||
				result = sqlite3_column_int(statement, 0) != 0;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		sqlite3_finalize(statement);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
 | 
			
		||||
	}
 | 
			
		||||
	tf_ssb_release_db_reader(ssb, db);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_update_settings_work(tf_ssb_t* ssb, void* user_data)
 | 
			
		||||
{
 | 
			
		||||
	update_settings_t* update = user_data;
 | 
			
		||||
	update->is_room = _get_global_setting_bool(ssb, "room", true);
 | 
			
		||||
	update->is_replicator = _get_global_setting_bool(ssb, "replicator", true);
 | 
			
		||||
	update->is_peer_exchange = _get_global_setting_bool(ssb, "peer_exchange", true);
 | 
			
		||||
	_get_global_setting_string(ssb, "room_name", update->room_name, sizeof(update->room_name));
 | 
			
		||||
	_get_global_setting_string(ssb, "seeds_host", update->seeds_host, sizeof(update->seeds_host));
 | 
			
		||||
	sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
			
		||||
	update->is_room = true;
 | 
			
		||||
	update->is_replicator = true;
 | 
			
		||||
	update->is_peer_exchange = true;
 | 
			
		||||
	tf_ssb_db_get_global_setting_bool(db, "room", &update->is_room);
 | 
			
		||||
	tf_ssb_db_get_global_setting_bool(db, "replicator", &update->is_replicator);
 | 
			
		||||
	tf_ssb_db_get_global_setting_bool(db, "peer_exchange", &update->is_peer_exchange);
 | 
			
		||||
	tf_ssb_db_get_global_setting_string(db, "room_name", update->room_name, sizeof(update->room_name));
 | 
			
		||||
	tf_ssb_db_get_global_setting_string(db, "seeds_host", update->seeds_host, sizeof(update->seeds_host));
 | 
			
		||||
	tf_ssb_release_db_reader(ssb, db);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void _tf_ssb_update_settings_after_work(tf_ssb_t* ssb, int result, void* user_data)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								src/ssb.db.c
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								src/ssb.db.c
									
									
									
									
									
								
							@@ -1681,34 +1681,13 @@ bool tf_ssb_db_set_account_password(uv_loop_t* loop, sqlite3* db, JSContext* con
 | 
			
		||||
	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 result = false;
 | 
			
		||||
	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", ®istration_allowed);
 | 
			
		||||
	if (registration_allowed)
 | 
			
		||||
	{
 | 
			
		||||
		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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
			{
 | 
			
		||||
				*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)
 | 
			
		||||
			{
 | 
			
		||||
				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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								src/ssb.db.h
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								src/ssb.db.h
									
									
									
									
									
								
							@@ -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);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
** 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.
 | 
			
		||||
** @param user_data User data registered with the authorizer.
 | 
			
		||||
 
 | 
			
		||||
@@ -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_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)
 | 
			
		||||
{
 | 
			
		||||
	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;
 | 
			
		||||
	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);
 | 
			
		||||
	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;
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
			
		||||
	db = tf_ssb_acquire_db_reader(ssb);
 | 
			
		||||
	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)
 | 
			
		||||
	{
 | 
			
		||||
@@ -1003,7 +982,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);
 | 
			
		||||
	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. */
 | 
			
		||||
	const char** visible = tf_ssb_db_get_all_visible_identities(ssb, depth);
 | 
			
		||||
@@ -1400,14 +1382,17 @@ typedef struct _delete_t
 | 
			
		||||
static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* 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)
 | 
			
		||||
	{
 | 
			
		||||
		_tf_ssb_rpc_checkpoint(ssb);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	int64_t start_ns = uv_hrtime();
 | 
			
		||||
	sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
			
		||||
	db = tf_ssb_acquire_db_writer(ssb);
 | 
			
		||||
	sqlite3_stmt* statement;
 | 
			
		||||
	int64_t now = (int64_t)time(NULL) * 1000ULL;
 | 
			
		||||
	int64_t timestamp = now - age * 1000ULL;
 | 
			
		||||
@@ -1474,7 +1459,10 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
	JSMallocFunctions funcs = { 0 };
 | 
			
		||||
@@ -1493,7 +1481,7 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
 | 
			
		||||
	JS_FreeValue(context, json);
 | 
			
		||||
	JS_FreeValue(context, array);
 | 
			
		||||
 | 
			
		||||
	sqlite3* db = tf_ssb_acquire_db_writer(ssb);
 | 
			
		||||
	db = tf_ssb_acquire_db_writer(ssb);
 | 
			
		||||
	sqlite3_stmt* statement;
 | 
			
		||||
	if (sqlite3_prepare(db,
 | 
			
		||||
			"DELETE FROM messages WHERE author IN ("
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
#define VERSION_NUMBER "0.0.26"
 | 
			
		||||
#define VERSION_NUMBER "0.0.27-wip"
 | 
			
		||||
#define VERSION_NAME "This program kills fascists."
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										89
									
								
								tools/ssl-local
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										89
									
								
								tools/ssl-local
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,89 @@
 | 
			
		||||
#!/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
 | 
			
		||||
	SSL_TARGET=linux-$BUILD_TARGET
 | 
			
		||||
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
 | 
			
		||||
-flto"
 | 
			
		||||
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
 | 
			
		||||
		Reference in New Issue
	
	Block a user