Compare commits
110 Commits
392d31cc53
...
main
Author | SHA1 | Date | |
---|---|---|---|
951155f1b6 | |||
1b678175ef | |||
8eb1f40eec | |||
235887b3bf | |||
0b3d66dd48 | |||
beb9ef3754 | |||
9f6a480736 | |||
b3bac2927d | |||
ef389f2ba2 | |||
ef21dc6ae8 | |||
6e55b6b49e | |||
db115ef1bd | |||
678838dbd5 | |||
586f87625d | |||
1542370f9b | |||
1f7d5968c7 | |||
39e51f7790 | |||
052663efbe | |||
8f84ff2611 | |||
37e1c5d97b | |||
cef526bcf3 | |||
6af36cafa9 | |||
fca859d93d | |||
2178300d8d | |||
636bdcce6b | |||
94b7703ca9 | |||
a391dd1316 | |||
b6ba5211b7 | |||
8e8e130045 | |||
1f40bc1a0f | |||
5437212222 | |||
a8ab845cd2 | |||
8cee6dc98b | |||
70c2b73414 | |||
98013c4422 | |||
e9e22b762d | |||
620db19936 | |||
94a79dd62c | |||
b56c3efde0 | |||
066827f8f1 | |||
c3b65d9cd8 | |||
a15b916b06 | |||
31d0a5c233 | |||
140179e80a | |||
53cba2d7e4 | |||
e54312d3b8 | |||
cadc27b7b5 | |||
388b829ec1 | |||
67861f0f33 | |||
a1f1eb34d5 | |||
2a6789063e | |||
cbf1273a55 | |||
8143a23ced | |||
3c17810747 | |||
bea7a2e9ed | |||
2f0a2ac6b0 | |||
18908b6b56 | |||
b135a210cc | |||
3a2a829940 | |||
e56dd2dd2d | |||
3f41a48bc7 | |||
65ed53281a | |||
1121557a2e | |||
d4a7b86ee7 | |||
626c18b04e | |||
bfa97ed7c7 | |||
deae4d5367 | |||
899605c860 | |||
dc9a279991 | |||
2a53892581 | |||
6bef0eb764 | |||
462b40640c | |||
72e1b2025c | |||
fc7c4b1257 | |||
6c22c59056 | |||
94c2b1184f | |||
45231d703d | |||
7882fcbe8f | |||
3bbc8c4d35 | |||
8ae10dc80b | |||
9b11c2c629 | |||
e2a231fb4a | |||
8a9502d1f2 | |||
534438df63 | |||
45a4feec96 | |||
aa7a32395e | |||
ab9f57f044 | |||
4040d6aa08 | |||
1c96f5c35e | |||
4d3e42812d | |||
f7b3711d4f | |||
2408e076ff | |||
6f71ffb477 | |||
214433f36a | |||
309b22732e | |||
6fe7687b2a | |||
a8cbf757ff | |||
4a4bedfe2b | |||
051291f725 | |||
d2b338095f | |||
899827a8f2 | |||
5fcbe3d6a9 | |||
a0a40e6cb2 | |||
bb1190e3f8 | |||
0a3baed1da | |||
4931c489ed | |||
996f9abaa2 | |||
08c097e176 | |||
daa861a98b | |||
a25d08fd76 |
@ -48,7 +48,7 @@ jobs:
|
|||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: |
|
run: |
|
||||||
mkdir -p out/html/ ~/.ssh/
|
mkdir -p out/html/ ~/.ssh/
|
||||||
make docs
|
make -j`nproc` docs
|
||||||
echo 'pildefriends ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKD3Kde5vDO0TrMBDK0IGGeNGe/XinWAZkSQ/rXxwUjt' >> ~/.ssh/known_hosts
|
echo 'pildefriends ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKD3Kde5vDO0TrMBDK0IGGeNGe/XinWAZkSQ/rXxwUjt' >> ~/.ssh/known_hosts
|
||||||
rsync -avP --delete -e "ssh -i /opt/keys/ssh.ed25519" out/html/ tfdocs@pildefriends:docs/html/
|
rsync -avP --delete -e "ssh -i /opt/keys/ssh.ed25519" out/html/ tfdocs@pildefriends:docs/html/
|
||||||
- name: Setup JDK
|
- name: Setup JDK
|
||||||
@ -59,11 +59,11 @@ jobs:
|
|||||||
- name: Setup Android SDK
|
- name: Setup Android SDK
|
||||||
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;35.0.0 platforms;android-35 ndk;27.2.12479018'
|
||||||
- name: Docker build
|
- name: Docker build
|
||||||
run: DOCKER_BUILDKIT=1 docker build .
|
run: DOCKER_BUILDKIT=1 docker build .
|
||||||
- name: Build
|
- name: Build
|
||||||
run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all dist docs
|
run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all dist
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
2
Doxyfile
@ -1051,7 +1051,7 @@ EXAMPLE_RECURSIVE = NO
|
|||||||
# that contain images that are to be included in the documentation (see the
|
# that contain images that are to be included in the documentation (see the
|
||||||
# \image command).
|
# \image command).
|
||||||
|
|
||||||
IMAGE_PATH =
|
IMAGE_PATH = docs/images/
|
||||||
|
|
||||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||||
|
33
GNUmakefile
@ -16,14 +16,14 @@ MAKEFLAGS += --no-builtin-rules
|
|||||||
## LD := Linker.
|
## LD := Linker.
|
||||||
## ANDROID_SDK := Path to the Android SDK.
|
## ANDROID_SDK := Path to the Android SDK.
|
||||||
|
|
||||||
VERSION_CODE := 37
|
VERSION_CODE := 39
|
||||||
VERSION_CODE_IOS := 13
|
VERSION_CODE_IOS := 15
|
||||||
VERSION_NUMBER := 0.0.31-wip
|
VERSION_NUMBER := 0.0.32.1
|
||||||
VERSION_NAME := This program kills fascists.
|
VERSION_NAME := This program kills fascists.
|
||||||
|
|
||||||
IPHONEOS_VERSION_MIN=14.0
|
IPHONEOS_VERSION_MIN=14.0
|
||||||
|
|
||||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3490200.zip
|
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500200.zip
|
||||||
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
||||||
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||||
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
||||||
@ -106,10 +106,10 @@ LDFLAGS += \
|
|||||||
-Wno-aggressive-loop-optimizations
|
-Wno-aggressive-loop-optimizations
|
||||||
|
|
||||||
ANDROID_MIN_SDK_VERSION := 24
|
ANDROID_MIN_SDK_VERSION := 24
|
||||||
ANDROID_TARGET_SDK_VERSION := 34
|
ANDROID_TARGET_SDK_VERSION := 35
|
||||||
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0
|
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/35.0.0
|
||||||
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-$(ANDROID_TARGET_SDK_VERSION)
|
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-$(ANDROID_TARGET_SDK_VERSION)
|
||||||
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.3.11579264
|
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/27.2.12479018
|
||||||
|
|
||||||
ANDROID_ARMV7A_TARGETS := \
|
ANDROID_ARMV7A_TARGETS := \
|
||||||
out/androiddebug-armv7a/tildefriends \
|
out/androiddebug-armv7a/tildefriends \
|
||||||
@ -650,6 +650,7 @@ SODIUM_SOURCES := \
|
|||||||
deps/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
|
deps/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
|
||||||
deps/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c \
|
deps/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c \
|
||||||
deps/libsodium/src/libsodium/crypto_core/softaes/softaes.c \
|
deps/libsodium/src/libsodium/crypto_core/softaes/softaes.c \
|
||||||
|
deps/libsodium/src/libsodium/crypto_generichash/crypto_generichash.c \
|
||||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
|
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
|
||||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c \
|
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c \
|
||||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c \
|
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c \
|
||||||
@ -1054,7 +1055,7 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
|
|||||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86_64/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym
|
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86_64/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym
|
||||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym
|
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym
|
||||||
@cd out/aab/staging; zip -u ../../../$@ BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym; cd ../../../
|
@cd out/aab/staging; zip -u ../../../$@ BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym; cd ../../../
|
||||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign -ks .keys/android.jks --ks-key-alias androidKey -ks-pass pass:android --min-sdk-version=$(ANDROID_MIN_SDK_VERSION) $@
|
@$(ANDROID_BUILD_TOOLS)/apksigner sign -ks .keys/android.jks --ks-key-alias androidKey -ks-pass pass:android --min-sdk-version=$(ANDROID_MIN_SDK_VERSION) --alignment-preserved $@
|
||||||
|
|
||||||
aab: out/TildeFriends.aab ## Build an Android App Bundle.
|
aab: out/TildeFriends.aab ## Build an Android App Bundle.
|
||||||
.PHONY: aab
|
.PHONY: aab
|
||||||
@ -1116,12 +1117,12 @@ out/apk/TildeFriends-%.fdroid.unsigned.apk:
|
|||||||
|
|
||||||
out/%.apk: out/apk/%.unsigned.apk
|
out/%.apk: out/apk/%.unsigned.apk
|
||||||
@echo "[apksigner] $(notdir $@)"
|
@echo "[apksigner] $(notdir $@)"
|
||||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $<
|
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ --alignment-preserved $<
|
||||||
|
|
||||||
out/%.zopfli.apk: out/%.apk
|
out/%.zopfli.apk: out/%.apk
|
||||||
@echo "[zopfli] $(notdir $@)"
|
@echo "[zopfli] $(notdir $@)"
|
||||||
$(ANDROID_BUILD_TOOLS)/zipalign -f -z 4 $< $@.zopfli
|
$(ANDROID_BUILD_TOOLS)/zipalign -f -z 4 $< $@.zopfli
|
||||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $@.zopfli
|
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ --alignment-preserved $@.zopfli
|
||||||
|
|
||||||
release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk ## Build an Android release APK.
|
release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk ## Build an Android release APK.
|
||||||
.PHONY: release-apk
|
.PHONY: release-apk
|
||||||
@ -1483,6 +1484,18 @@ help: ## Display this help message.
|
|||||||
.PHONY: help
|
.PHONY: help
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
docs: debug
|
||||||
docs: ## Build HTML docs.
|
docs: ## Build HTML docs.
|
||||||
|
@echo '# CLI Usage\n' > docs/usage.md
|
||||||
|
@echo "## tildefriends -h" >> docs/usage.md
|
||||||
|
@echo '\n```' >> docs/usage.md
|
||||||
|
@out/debug/tildefriends -h >> docs/usage.md
|
||||||
|
@echo '```' >> docs/usage.md
|
||||||
|
@for command in $$(out/debug/tildefriends -h | grep -Po '[A-Za-z_]*(?= - )'); do
|
||||||
|
@ echo "\n## tildefriends $$command -h" >> docs/usage.md
|
||||||
|
@ echo '\n```' >> docs/usage.md
|
||||||
|
@ out/debug/tildefriends $$command -h >> docs/usage.md
|
||||||
|
@ echo '```' >> docs/usage.md
|
||||||
|
@done
|
||||||
@doxygen
|
@doxygen
|
||||||
.PHONY: docs
|
.PHONY: docs
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "💡",
|
"emoji": "💡",
|
||||||
"previous": "&ckE7T/dt9f1xV8jNSgXVcXYkAzMhU9689MRQbhOi9Wo=.sha256"
|
"previous": "&eN6DNPpQUNhGvxneLuLPgsOXR6qyFZ7u+MAz0b4fa7k=.sha256"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,10 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tfrpc.register(async function complete() {
|
tfrpc.register(async function complete() {
|
||||||
if ((await core.globalSettingsGet('index')) == '/~core/intro/') {
|
if (
|
||||||
|
core.user?.credentials?.permissions?.administration &&
|
||||||
|
(await core.globalSettingsGet('index')) == '/~core/intro/'
|
||||||
|
) {
|
||||||
return await core.globalSettingsSet('index', '/~core/ssb/');
|
return await core.globalSettingsSet('index', '/~core/ssb/');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
<style>
|
<style>
|
||||||
.slide {
|
.slide {
|
||||||
display: none;
|
display: none;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
.dot {
|
.dot {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
@ -27,28 +29,38 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
"
|
"
|
||||||
class="w3-flex w3-dark-gray w3-center"
|
class="w3-flex w3-dark-gray w3-center"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style="flex: 1 1 auto; overflow: auto; contain: content; padding: 64px"
|
style="
|
||||||
|
flex: 1 1 auto;
|
||||||
|
overflow: auto;
|
||||||
|
contain: content;
|
||||||
|
padding-top: 16px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div
|
<div class="slide">
|
||||||
class="slide w3-content w3-xlarge w3-blue w3-card-4 w3-panel w3-padding-32 w3-round-xlarge"
|
<div
|
||||||
>
|
class="w3-content w3-xlarge w3-card-4 w3-blue w3-panel w3-padding-32 w3-round-xlarge"
|
||||||
<div>
|
style="margin: 32px"
|
||||||
<div>Welcome to</div>
|
>
|
||||||
<div>~😎 Tilde Friends.</div>
|
<div>
|
||||||
|
<div>Welcome to</div>
|
||||||
|
<div>~😎 Tilde Friends.</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<button class="w3-button w3-yellow proceed">Next</button>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
|
||||||
<button class="w3-button w3-yellow proceed">Next</button>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="slide w3-card-4 w3-gray" style="width: 90%">
|
<div class="slide w3-card-4 w3-gray" style="width: 90%">
|
||||||
<header class="w3-container w3-blue w3-xlarge">
|
<header class="w3-container w3-blue w3-xlarge">
|
||||||
<h1>This brief tutorial will introduce:</h1>
|
<h1>This brief tutorial will introduce:</h1>
|
||||||
</header>
|
</header>
|
||||||
<ul class="w3-xlarge w3-left-align">
|
<ul class="w3-large w3-left-align">
|
||||||
<li><b>Secure Scuttlebutt</b>, a decentralized social network.</li>
|
<li><b>Secure Scuttlebutt</b>, a decentralized social network.</li>
|
||||||
<li>
|
<li>
|
||||||
<b>Tilde Friends</b>, the application platform that you are using
|
<b>Tilde Friends</b>, the application platform that you are using
|
||||||
@ -65,27 +77,33 @@
|
|||||||
<div class="slide w3-gray" style="width: 90%">
|
<div class="slide w3-gray" style="width: 90%">
|
||||||
<div class="w3-card-4 w3-xlarge">
|
<div class="w3-card-4 w3-xlarge">
|
||||||
<header class="w3-container w3-blue">
|
<header class="w3-container w3-blue">
|
||||||
<h1>Secure Scuttlebutt in a nutshell:</h1>
|
<h1>💻Secure Scuttlebutt in a Nutshell🦀</h1>
|
||||||
</header>
|
</header>
|
||||||
<div class="w3-container w3-xlarge w3-left-align">
|
<div class="w3-container w3-large w3-left-align">
|
||||||
|
<p>
|
||||||
|
Secure Scuttlebutt is a social network whose technical operation
|
||||||
|
attempts to mirror human social interaction.
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
You create your own account and post to your own feed. This is
|
You can create your own account and post to your own feed on
|
||||||
all <b>local</b> and happens <b>offline</b>. You are fully in
|
your own device. This is all <b>local</b> with no external
|
||||||
charge, and you can do most things with no reception whatsoever.
|
communication. This puts you fully in control of your own words
|
||||||
|
and actions.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
To interact with others, <b>connect over the network</b>, either
|
Before you can interact with others, you need to
|
||||||
directly to your friends (ie, on coffee shop Wi-Fi), to
|
<b>connect over the network</b>, either directly to your friends
|
||||||
<i>rooms</i> (search the web for <i>#ssbroom</i>), or to
|
(i.e., peer-to-peer between your phones on coffee shop Wi-Fi) or
|
||||||
<i>pubs</i>.
|
to 🚪<i>rooms</i> and 🍻<i>pubs</i> (hint: search the web for
|
||||||
|
<i>#ssbroom</i>).
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
To start seeing content, you need to make some connections and
|
Who you choose to <b>follow</b> determines what you see, with
|
||||||
then <b>follow accounts</b>. By default you see your direct
|
most people choosing to see messages from friends and friends of
|
||||||
friends and friends of those friends. If you encounter content
|
those friends. If you encounter content you'd rather not see,
|
||||||
you'd rather not see, <b>block</b> the offending account to
|
<b>block</b> the offending account to improve the experience for
|
||||||
improve the experience for you and your followers.
|
you and your followers.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Your feed is an <b>immutable</b> log of your activity. Post with
|
Your feed is an <b>immutable</b> log of your activity. Post with
|
||||||
@ -110,20 +128,28 @@
|
|||||||
<header class="w3-container w3-blue w3-center">
|
<header class="w3-container w3-blue w3-center">
|
||||||
<h1>~😎 Let's Talk Tilde Friends ~😎</h1>
|
<h1>~😎 Let's Talk Tilde Friends ~😎</h1>
|
||||||
</header>
|
</header>
|
||||||
<div class="w3-container w3-xlarge w3-left-align">
|
<div class="w3-container w3-large w3-left-align">
|
||||||
Tilde Friends is an application platform that is an application of
|
<p>
|
||||||
its own.
|
Tilde Friends is an application platform that is an application of
|
||||||
|
its own.
|
||||||
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
This intro is a Tilde Friends app. You can click <b>edit</b> at
|
This intro is a Tilde Friends app. You can click <b>edit</b> at
|
||||||
the top to look under the hood and make changes if so inclined.
|
the top to look under the hood and make changes.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
But it's also set up so that you can't just break an instance of
|
It is already possible to make and share new applications using
|
||||||
an app that everybody is using. There are <b>protections</b> in
|
only Tilde Friends and Secure Scuttlebutt without having to set
|
||||||
place like an operating system. The intent is also for it to be
|
up development environments, configure web servers, register
|
||||||
<b>safe</b>
|
domain names, or pay for hosting services.
|
||||||
to run strange apps without worrying about adverse effects.
|
</li>
|
||||||
|
<li>
|
||||||
|
But it's also set up so that you can't just break an app that
|
||||||
|
everybody is using or do malicious things with personal content.
|
||||||
|
There are <b>protections</b> in place like an operating system.
|
||||||
|
The intent is also for it to be <b>safe</b> to run strange apps
|
||||||
|
without worrying about adverse effects.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
But this is all a big 🚧work in progress🚧 and
|
But this is all a big 🚧work in progress🚧 and
|
||||||
@ -132,9 +158,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<footer class="w3-center w3-xlarge w3-padding">
|
<footer class="w3-center w3-xlarge w3-padding">
|
||||||
<a class="w3-button w3-yellow" target="_top" href="/~core/ssb/"
|
<button class="w3-button w3-yellow proceed">Okay</button>
|
||||||
>Okay</a
|
|
||||||
>
|
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -143,19 +167,23 @@
|
|||||||
<header class="w3-container w3-blue w3-center">
|
<header class="w3-container w3-blue w3-center">
|
||||||
<h1>🦀Let's Get this Tilde Friends Party Started🎉</h1>
|
<h1>🦀Let's Get this Tilde Friends Party Started🎉</h1>
|
||||||
</header>
|
</header>
|
||||||
<div class="w3-container w3-xlarge w3-left-align">
|
<div class="w3-container w3-large w3-left-align">
|
||||||
|
<p>The button below will take you to the Secure Scuttlebutt app.</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
|
||||||
The button below will take you to the Secure Scuttlebutt app.
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
Remember:
|
Remember:
|
||||||
<ol>
|
<ol>
|
||||||
<li>You are in charge. This is all on your device.</li>
|
<li>You are in charge. This is all on your device.</li>
|
||||||
<li>Make network connections to replicate with others.</li>
|
|
||||||
<li>Follow more accounts to see more content.</li>
|
|
||||||
<li>
|
<li>
|
||||||
Post respectfully, and block those you'd rather not see.
|
Make network connections to exchange messages with others.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Follow more accounts to see more content, and block those
|
||||||
|
posting content you'd rather not see.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Be respectful, and consider the consequences of what you
|
||||||
|
post.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
This is all under active development. Exercise patience, and
|
This is all under active development. Exercise patience, and
|
||||||
@ -177,7 +205,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="w3-text-white w3-xlarge w3-center w3-flex"
|
class="w3-text-white w3-xlarge w3-center w3-flex"
|
||||||
style="width: 100%; flex: 0 1; flex-direction: row; align-items: center"
|
style="
|
||||||
|
width: 100%;
|
||||||
|
flex: 0 1;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<div class="w3-jumbo" id="left" style="flex: 1 0; cursor: pointer">
|
<div class="w3-jumbo" id="left" style="flex: 1 0; cursor: pointer">
|
||||||
❮
|
❮
|
||||||
@ -230,7 +264,7 @@
|
|||||||
document.getElementById('right').onclick = () => show(1);
|
document.getElementById('right').onclick = () => show(1);
|
||||||
document.getElementById('complete').onclick = function () {
|
document.getElementById('complete').onclick = function () {
|
||||||
console.log('completing');
|
console.log('completing');
|
||||||
tfrpc.rpc.complete().then(function () {
|
tfrpc.rpc.complete().finally(function () {
|
||||||
console.log('completed');
|
console.log('completed');
|
||||||
let a = document.createElement('a');
|
let a = document.createElement('a');
|
||||||
a.href = '/~core/ssb/';
|
a.href = '/~core/ssb/';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🚪",
|
"emoji": "🚪",
|
||||||
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
|
"previous": "&DJwkqNfYWtW9yBtJQMseEXm7l04Enpi+yAxZulLq9Vk=.sha256"
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ async function main() {
|
|||||||
print(core.url);
|
print(core.url);
|
||||||
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
|
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
|
||||||
let port = await ssb.port();
|
let port = await ssb.port();
|
||||||
let id = (await ssb.getServerIdentity()).substring(1);
|
let id = (await ssb.getServerIdentity()).substring(1).split('.')[0];
|
||||||
let room = `net:${host}:${port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
let room = `net:${host}:${port}~shs:${id}:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||||
await app.setDocument(`
|
await app.setDocument(`
|
||||||
<body style="color: #fff">
|
<body style="color: #fff">
|
||||||
<h1>Server</h1>
|
<h1>Server</h1>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&nd1gmPKrvLdkxW9D4Zct/0hD+iwLWjkF1gxaebFQ5I8=.sha256"
|
"previous": "&Ym1vefMN4CV4UIgLuV+zu52qj58WwIScctt4v5YIHmQ=.sha256"
|
||||||
}
|
}
|
||||||
|
@ -106,6 +106,15 @@ tfrpc.register(async function sync() {
|
|||||||
tfrpc.register(async function url() {
|
tfrpc.register(async function url() {
|
||||||
return core.url;
|
return core.url;
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function globalSettingsGet(key) {
|
||||||
|
return core.globalSettingsGet(key);
|
||||||
|
});
|
||||||
|
tfrpc.register(async function globalSettingsSet(key, value) {
|
||||||
|
return core.globalSettingsSet(key, value);
|
||||||
|
});
|
||||||
|
tfrpc.register(function isAdministrator() {
|
||||||
|
return core.user?.credentials?.permissions?.administration;
|
||||||
|
});
|
||||||
|
|
||||||
core.register('onBroadcastsChanged', async function () {
|
core.register('onBroadcastsChanged', async function () {
|
||||||
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
||||||
|
@ -11,6 +11,7 @@ class TfElement extends LitElement {
|
|||||||
broadcasts: {type: Array},
|
broadcasts: {type: Array},
|
||||||
connections: {type: Array},
|
connections: {type: Array},
|
||||||
loading: {type: Boolean},
|
loading: {type: Boolean},
|
||||||
|
loading_about: {type: Number},
|
||||||
loaded: {type: Boolean},
|
loaded: {type: Boolean},
|
||||||
following: {type: Array},
|
following: {type: Array},
|
||||||
users: {type: Object},
|
users: {type: Object},
|
||||||
@ -22,6 +23,8 @@ class TfElement extends LitElement {
|
|||||||
url: {type: String},
|
url: {type: String},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
is_administrator: {type: Boolean},
|
||||||
|
stay_connected: {type: Boolean},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,6 +40,7 @@ class TfElement extends LitElement {
|
|||||||
this.following = [];
|
this.following = [];
|
||||||
this.users = {};
|
this.users = {};
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
|
this.loading_about = 0;
|
||||||
this.channels = [];
|
this.channels = [];
|
||||||
this.channels_unread = {};
|
this.channels_unread = {};
|
||||||
this.channels_latest = {};
|
this.channels_latest = {};
|
||||||
@ -71,6 +75,10 @@ class TfElement extends LitElement {
|
|||||||
async initial_load() {
|
async initial_load() {
|
||||||
let whoami = await tfrpc.rpc.getActiveIdentity();
|
let whoami = await tfrpc.rpc.getActiveIdentity();
|
||||||
let ids = (await tfrpc.rpc.getIdentities()) || [];
|
let ids = (await tfrpc.rpc.getIdentities()) || [];
|
||||||
|
this.is_administrator = await tfrpc.rpc.isAdministrator();
|
||||||
|
this.stay_connected =
|
||||||
|
this.is_administrator &&
|
||||||
|
(await tfrpc.rpc.globalSettingsGet('stay_connected'));
|
||||||
this.url = await tfrpc.rpc.url();
|
this.url = await tfrpc.rpc.url();
|
||||||
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
||||||
this.guest = !this.whoami?.length;
|
this.guest = !this.whoami?.length;
|
||||||
@ -126,7 +134,13 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
next_channel(delta) {
|
next_channel(delta) {
|
||||||
let channel_names = ['', '@', '🔐', ...this.channels.map((x) => '#' + x)];
|
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));
|
||||||
index = index != -1 ? index + delta : 0;
|
index = index != -1 ? index + delta : 0;
|
||||||
tfrpc.rpc.setHash(
|
tfrpc.rpc.setHash(
|
||||||
@ -151,6 +165,7 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fetch_about(following, users) {
|
async fetch_about(following, users) {
|
||||||
|
this.loading_about++;
|
||||||
let ids = Object.keys(following).sort();
|
let ids = Object.keys(following).sort();
|
||||||
const k_cache_version = 3;
|
const k_cache_version = 3;
|
||||||
let cache = await tfrpc.rpc.databaseGet('about');
|
let cache = await tfrpc.rpc.databaseGet('about');
|
||||||
@ -230,6 +245,8 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.loading_about--;
|
||||||
|
|
||||||
let new_cache = JSON.stringify(cache);
|
let new_cache = JSON.stringify(cache);
|
||||||
if (new_cache != original_cache) {
|
if (new_cache != original_cache) {
|
||||||
let start_time = new Date();
|
let start_time = new Date();
|
||||||
@ -297,11 +314,7 @@ class TfElement extends LitElement {
|
|||||||
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
||||||
}
|
}
|
||||||
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
|
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
|
||||||
ranges.push([
|
ranges.push([Math.max(i - k_chunk_size, 0), i, false]);
|
||||||
Math.max(i - k_chunk_size, 0),
|
|
||||||
Math.min(cache.range[0], i + k_chunk_size),
|
|
||||||
false,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < latest; i += k_chunk_size) {
|
for (let i = 0; i < latest; i += k_chunk_size) {
|
||||||
@ -317,7 +330,7 @@ class TfElement extends LitElement {
|
|||||||
messages.rowid > ?1 AND
|
messages.rowid > ?1 AND
|
||||||
messages.rowid <= ?2 AND
|
messages.rowid <= ?2 AND
|
||||||
json(messages.content) LIKE '"%'
|
json(messages.content) LIKE '"%'
|
||||||
ORDER BY sequence DESC
|
ORDER BY messages.rowid DESC
|
||||||
`,
|
`,
|
||||||
[range[0], range[1]]
|
[range[0], range[1]]
|
||||||
);
|
);
|
||||||
@ -343,42 +356,83 @@ class TfElement extends LitElement {
|
|||||||
return [cache.latest, cache.messages];
|
return [cache.latest, cache.messages];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async query_timed(sql, args) {
|
||||||
|
let start = new Date();
|
||||||
|
let result = await tfrpc.rpc.query(sql, args);
|
||||||
|
let end = new Date();
|
||||||
|
console.log((end - start) / 1000, sql.replaceAll(/\s+/g, ' ').trim());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async load_channels_latest(following) {
|
async load_channels_latest(following) {
|
||||||
let start_time = new Date();
|
let start_time = new Date();
|
||||||
let latest_private = this.get_latest_private(following);
|
let latest_private = this.get_latest_private(following);
|
||||||
let channels = await tfrpc.rpc.query(
|
const k_args = [
|
||||||
`
|
JSON.stringify(this.channels),
|
||||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
JSON.stringify(following),
|
||||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
'"' + this.whoami.replace('"', '""') + '"',
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
this.whoami,
|
||||||
WHERE
|
];
|
||||||
messages.content ->> 'type' = 'post' AND
|
let channels = (
|
||||||
messages.content ->> 'root' IS NULL AND
|
await Promise.all([
|
||||||
messages.author != ?4
|
this.query_timed(
|
||||||
GROUP by channel
|
`
|
||||||
UNION
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
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
|
WHERE
|
||||||
messages.content ->> 'type' = 'post' AND
|
messages.content ->> 'type' = 'post' AND
|
||||||
messages.content ->> 'root' IS NULL AND
|
messages.content ->> 'root' IS NULL AND
|
||||||
messages.author != ?4
|
messages.author != ?4
|
||||||
UNION
|
GROUP by channel
|
||||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
`,
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
k_args
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
),
|
||||||
WHERE messages.author != ?4
|
this.query_timed(
|
||||||
`,
|
`
|
||||||
[
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
JSON.stringify(this.channels),
|
JOIN messages_refs ON messages.id = messages_refs.message
|
||||||
JSON.stringify(following),
|
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||||
'"' + this.whoami.replace('"', '""') + '"',
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
this.whoami,
|
WHERE
|
||||||
]
|
messages.content ->> 'type' = 'post' AND
|
||||||
);
|
messages.content ->> 'root' IS NULL AND
|
||||||
this.channels_latest = Object.fromEntries(
|
messages.author != ?4
|
||||||
channels.map((x) => [x.channel, x.rowid])
|
GROUP by channel
|
||||||
);
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
|
this.query_timed(
|
||||||
|
`
|
||||||
|
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
|
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
|
||||||
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
|
this.query_timed(
|
||||||
|
`
|
||||||
|
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||||
|
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||||
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
|
WHERE messages.author != ?4
|
||||||
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
|
])
|
||||||
|
).flat();
|
||||||
|
let latest = {};
|
||||||
|
for (let row of channels) {
|
||||||
|
if (!latest[row.channel]) {
|
||||||
|
latest[row.channel] = row.rowid;
|
||||||
|
} else {
|
||||||
|
latest[row.channel] = Math.max(row.rowid, latest[row.channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.channels_latest = latest;
|
||||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
console.log('channels took', (new Date() - start_time) / 1000.0);
|
||||||
let self = this;
|
let self = this;
|
||||||
start_time = new Date();
|
start_time = new Date();
|
||||||
@ -416,13 +470,10 @@ class TfElement extends LitElement {
|
|||||||
[JSON.stringify(Object.keys(users))]
|
[JSON.stringify(Object.keys(users))]
|
||||||
);
|
);
|
||||||
for (let row of info) {
|
for (let row of info) {
|
||||||
users[row.author] = Object.assign(
|
users[row.author] = Object.assign(users[row.author], {
|
||||||
{
|
seq: row.max_sequence,
|
||||||
seq: row.max_sequence,
|
ts: row.max_ts,
|
||||||
ts: row.max_ts,
|
});
|
||||||
},
|
|
||||||
users[row.author]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return users;
|
return users;
|
||||||
}
|
}
|
||||||
@ -545,14 +596,18 @@ class TfElement extends LitElement {
|
|||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
.users=${this.users}
|
.users=${this.users}
|
||||||
hash=${this.hash}
|
hash=${this.hash}
|
||||||
?loading=${this.loading}
|
?loading=${this.loading || this.loading_about != 0}
|
||||||
.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}
|
||||||
|
@refresh=${this.refresh}
|
||||||
|
@toggle_stay_connected=${this.toggle_stay_connected}
|
||||||
.connections=${this.connections}
|
.connections=${this.connections}
|
||||||
.private_messages=${this.private_messages}
|
.private_messages=${this.private_messages}
|
||||||
.recent_reactions=${this.recent_reactions}
|
.recent_reactions=${this.recent_reactions}
|
||||||
|
?is_administrator=${this.is_administrator}
|
||||||
|
?stay_connected=${this.stay_connected}
|
||||||
></tf-tab-news>
|
></tf-tab-news>
|
||||||
`;
|
`;
|
||||||
} else if (this.tab === 'connections') {
|
} else if (this.tab === 'connections') {
|
||||||
@ -603,6 +658,18 @@ class TfElement extends LitElement {
|
|||||||
tfrpc.rpc.sync();
|
tfrpc.rpc.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async toggle_stay_connected() {
|
||||||
|
let stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
|
||||||
|
let new_stay_connected = !this.stay_connected;
|
||||||
|
try {
|
||||||
|
if (new_stay_connected != stay_connected) {
|
||||||
|
await tfrpc.rpc.globalSettingsSet('stay_connected', new_stay_connected);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
@ -625,14 +692,26 @@ class TfElement extends LitElement {
|
|||||||
class="w3-bar w3-theme-l1"
|
class="w3-bar w3-theme-l1"
|
||||||
style="position: static; top: 0; z-index: 10"
|
style="position: static; top: 0; z-index: 10"
|
||||||
>
|
>
|
||||||
<button
|
${this.is_administrator
|
||||||
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
? html`
|
||||||
(this.connections?.some((x) => x.flags.one_shot) ? ' w3-spin' : '')}
|
<button
|
||||||
style="width: 1.5em; height: 1.5em; padding: 8px"
|
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
||||||
@click=${this.refresh}
|
(this.connections?.some((x) => x.flags.one_shot)
|
||||||
>
|
? ' w3-spin'
|
||||||
↻
|
: '')}
|
||||||
</button>
|
style="width: 1.5em; height: 1.5em; padding: 8px"
|
||||||
|
@click=${this.refresh}
|
||||||
|
>
|
||||||
|
↻
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="w3-bar-item w3-button w3-ripple"
|
||||||
|
@click=${this.toggle_stay_connected}
|
||||||
|
>
|
||||||
|
${this.stay_connected ? '🔗' : '⛓️💥'}
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
${Object.entries(k_tabs).map(
|
${Object.entries(k_tabs).map(
|
||||||
([k, v]) => html`
|
([k, v]) => html`
|
||||||
<button
|
<button
|
||||||
|
@ -446,12 +446,15 @@ class TfComposeElement extends LitElement {
|
|||||||
self.apps = await tfrpc.rpc.apps();
|
self.apps = await tfrpc.rpc.apps();
|
||||||
}
|
}
|
||||||
if (!this.apps) {
|
if (!this.apps) {
|
||||||
return html`<button class="w3-button w3-theme-d1" @click=${attach_app}>
|
return html`<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${attach_app}
|
||||||
|
>
|
||||||
Attach App
|
Attach App
|
||||||
</button>`;
|
</button>`;
|
||||||
} else {
|
} else {
|
||||||
return html`<button
|
return html`<button
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
@click=${() => (this.apps = null)}
|
@click=${() => (this.apps = null)}
|
||||||
>
|
>
|
||||||
Discard App
|
Discard App
|
||||||
@ -472,18 +475,9 @@ class TfComposeElement extends LitElement {
|
|||||||
if (draft.content_warning !== undefined) {
|
if (draft.content_warning !== undefined) {
|
||||||
return html`
|
return html`
|
||||||
<div class="w3-container w3-padding">
|
<div class="w3-container w3-padding">
|
||||||
<p>
|
|
||||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
|
|
||||||
<label for="cw">CW</label>
|
|
||||||
</p>
|
|
||||||
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input>
|
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else {
|
|
||||||
return html`
|
|
||||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning('')}></input>
|
|
||||||
<label for="cw">CW</label>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,6 +540,31 @@ class TfComposeElement extends LitElement {
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle_menu(event) {
|
||||||
|
event.srcElement.parentNode
|
||||||
|
.querySelector('.w3-dropdown-content')
|
||||||
|
.classList.toggle('w3-show');
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._click_callback = this.document_click.bind(this);
|
||||||
|
document.body.addEventListener('mouseup', this._click_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
document.body.removeEventListener('mouseup', this._click_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
document_click(event) {
|
||||||
|
let content = this.renderRoot.querySelector('.w3-dropdown-content');
|
||||||
|
let target = event.target;
|
||||||
|
if (content && !content.contains(target)) {
|
||||||
|
content.classList.remove('w3-show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
let draft = self.get_draft();
|
let draft = self.get_draft();
|
||||||
@ -559,10 +578,10 @@ class TfComposeElement extends LitElement {
|
|||||||
draft.encrypt_to !== undefined
|
draft.encrypt_to !== undefined
|
||||||
? undefined
|
? undefined
|
||||||
: html`<button
|
: html`<button
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
@click=${() => this.set_encrypt([])}
|
@click=${() => this.set_encrypt([])}
|
||||||
>
|
>
|
||||||
🔐
|
🔐 Encrypt
|
||||||
</button>`;
|
</button>`;
|
||||||
let result = html`
|
let result = html`
|
||||||
<style>
|
<style>
|
||||||
@ -583,7 +602,7 @@ class TfComposeElement extends LitElement {
|
|||||||
: undefined}
|
: undefined}
|
||||||
${this.render_encrypt()}
|
${this.render_encrypt()}
|
||||||
</header>
|
</header>
|
||||||
<div class="w3-container w3-padding-small">
|
<div class="w3-container" style="padding: 0 0 16px 0">
|
||||||
<div class="w3-half">
|
<div class="w3-half">
|
||||||
<span
|
<span
|
||||||
class="w3-input w3-theme-d1 w3-border"
|
class="w3-input w3-theme-d1 w3-border"
|
||||||
@ -604,7 +623,7 @@ class TfComposeElement extends LitElement {
|
|||||||
${Object.values(draft.mentions || {}).map((x) =>
|
${Object.values(draft.mentions || {}).map((x) =>
|
||||||
self.render_mention(x)
|
self.render_mention(x)
|
||||||
)}
|
)}
|
||||||
<footer class="w3-container">
|
<footer>
|
||||||
${this.render_attach_app()} ${this.render_content_warning()}
|
${this.render_attach_app()} ${this.render_content_warning()}
|
||||||
${this.render_new_thread()}
|
${this.render_new_thread()}
|
||||||
<button
|
<button
|
||||||
@ -614,13 +633,43 @@ class TfComposeElement extends LitElement {
|
|||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.attach}>
|
<div class="w3-dropdown-click">
|
||||||
Attach
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
</button>
|
⚙️
|
||||||
${this.render_attach_app_button()} ${encrypt}
|
</button>
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.discard}>
|
<div class="w3-dropdown-content w3-bar-block">
|
||||||
Discard
|
${this.get_draft().content_warning === undefined
|
||||||
</button>
|
? html`
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${() => self.set_content_warning('')}
|
||||||
|
>
|
||||||
|
Add Content Warning
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${() => self.set_content_warning(undefined)}
|
||||||
|
>
|
||||||
|
Remove Content Warning
|
||||||
|
</button>
|
||||||
|
`}
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${this.attach}
|
||||||
|
>
|
||||||
|
Attach
|
||||||
|
</button>
|
||||||
|
${this.render_attach_app_button()} ${encrypt}
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${this.discard}
|
||||||
|
>
|
||||||
|
Discard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import {LitElement, html, repeat, render, unsafeHTML} from './lit-all.min.js';
|
import {
|
||||||
|
LitElement,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
repeat,
|
||||||
|
render,
|
||||||
|
unsafeHTML,
|
||||||
|
} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import * as tfutils from './tf-utils.js';
|
import * as tfutils from './tf-utils.js';
|
||||||
import * as emojis from './emojis.js';
|
import * as emojis from './emojis.js';
|
||||||
@ -86,12 +93,18 @@ class TfMessageElement extends LitElement {
|
|||||||
|
|
||||||
render_votes() {
|
render_votes() {
|
||||||
function normalize_expression(expression) {
|
function normalize_expression(expression) {
|
||||||
if (expression === 'Like' || expression === 'like' || !expression) {
|
if (
|
||||||
return '👍';
|
expression === 'Unlike' ||
|
||||||
} else if (expression === 'Unlike' || expression === 'unlike') {
|
expression === 'unlike' ||
|
||||||
|
expression == 'undig'
|
||||||
|
) {
|
||||||
return '👎';
|
return '👎';
|
||||||
} else if (expression === 'heart') {
|
} else if (expression === 'heart') {
|
||||||
return '❤️';
|
return '❤️';
|
||||||
|
} else if (
|
||||||
|
(expression ?? '').split('').every((x) => x.charCodeAt(0) < 256)
|
||||||
|
) {
|
||||||
|
return '👍';
|
||||||
} else {
|
} else {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
@ -297,31 +310,35 @@ class TfMessageElement extends LitElement {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expanded_key() {
|
||||||
|
return this.message?.id || this.messages?.map((x) => x.id).join(':');
|
||||||
|
}
|
||||||
|
|
||||||
set_expanded(expanded, tag) {
|
set_expanded(expanded, tag) {
|
||||||
|
let key = this.expanded_key();
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('tf-expand', {
|
new CustomEvent('tf-expand', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true,
|
||||||
detail: {id: (this.message.id || '') + (tag || ''), expanded: expanded},
|
detail: {id: key + (tag || ''), expanded: expanded},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle_expanded(tag) {
|
toggle_expanded(tag) {
|
||||||
this.set_expanded(
|
let key = this.expanded_key();
|
||||||
!this.expanded[(this.message.id || '') + (tag || '')],
|
this.set_expanded(!this.expanded[key + (tag || '')], tag);
|
||||||
tag
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is_expanded(tag) {
|
is_expanded(tag) {
|
||||||
return this.expanded[(this.message.id || '') + (tag || '')];
|
let key = this.expanded_key();
|
||||||
|
return this.expanded[key + (tag || '')];
|
||||||
}
|
}
|
||||||
|
|
||||||
render_children() {
|
render_children() {
|
||||||
let self = this;
|
let self = this;
|
||||||
if (this.message.child_messages?.length) {
|
if (this.message.child_messages?.length) {
|
||||||
if (!this.expanded[this.message.id]) {
|
if (!this.expanded[this.expanded_key()]) {
|
||||||
return html`
|
return html`
|
||||||
<button
|
<button
|
||||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
@ -397,7 +414,7 @@ class TfMessageElement extends LitElement {
|
|||||||
class_background() {
|
class_background() {
|
||||||
return this.message?.decrypted
|
return this.message?.decrypted
|
||||||
? 'w3-pale-red'
|
? 'w3-pale-red'
|
||||||
: this.message?.rowid >= this.channel_unread
|
: this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||||
? 'w3-theme-d2'
|
? 'w3-theme-d2'
|
||||||
: 'w3-theme-d4';
|
: 'w3-theme-d4';
|
||||||
}
|
}
|
||||||
@ -489,7 +506,10 @@ class TfMessageElement extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<header class="w3-bar">
|
<header class="w3-bar">
|
||||||
<span class="w3-bar-item">
|
<span class="w3-bar-item">
|
||||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
${this.render_unread_icon()}<tf-user
|
||||||
|
id=${this.message.author}
|
||||||
|
.users=${this.users}
|
||||||
|
></tf-user>
|
||||||
</span>
|
</span>
|
||||||
${is_encrypted} ${this.render_menu()}
|
${is_encrypted} ${this.render_menu()}
|
||||||
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
|
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
|
||||||
@ -574,6 +594,56 @@ class TfMessageElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content_group_by_author() {
|
||||||
|
let sorted = this.message.messages
|
||||||
|
.map((x) => [
|
||||||
|
x.author,
|
||||||
|
x.content.blocking !== undefined
|
||||||
|
? x.content.blocking
|
||||||
|
? 'is blocking'
|
||||||
|
: 'is no longer blocking'
|
||||||
|
: x.content.following !== undefined
|
||||||
|
? x.content.following
|
||||||
|
? 'is following'
|
||||||
|
: 'is no longer following'
|
||||||
|
: '',
|
||||||
|
x.content.contact,
|
||||||
|
x,
|
||||||
|
])
|
||||||
|
.sort();
|
||||||
|
let result = [];
|
||||||
|
let last;
|
||||||
|
let group;
|
||||||
|
for (let row of sorted) {
|
||||||
|
if (last && last[0] == row[0] && last[1] == row[1]) {
|
||||||
|
group.push(row[2]);
|
||||||
|
} else {
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], users: group});
|
||||||
|
}
|
||||||
|
last = row;
|
||||||
|
group = [row[2]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], users: group});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
allow_unread() {
|
||||||
|
return (
|
||||||
|
this.channel == '@' ||
|
||||||
|
(!this.channel.startsWith('@') && !this.channel.startsWith('%'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render_unread_icon() {
|
||||||
|
return this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||||
|
? html`✉️`
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let content = this.message?.content;
|
let content = this.message?.content;
|
||||||
if (this.message?.decrypted?.type == 'post') {
|
if (this.message?.decrypted?.type == 'post') {
|
||||||
@ -582,29 +652,94 @@ class TfMessageElement extends LitElement {
|
|||||||
let class_background = this.class_background();
|
let class_background = this.class_background();
|
||||||
let self = this;
|
let self = this;
|
||||||
if (this.message?.type === 'contact_group') {
|
if (this.message?.type === 'contact_group') {
|
||||||
return this.render_frame(
|
if (this.expanded[this.expanded_key()]) {
|
||||||
html` ${this.message.messages.map(
|
return this.render_frame(html`
|
||||||
(x) =>
|
<div class="w3-padding">
|
||||||
html`<tf-message
|
${this.message.messages.map(
|
||||||
.message=${x}
|
(x) =>
|
||||||
whoami=${this.whoami}
|
html`<tf-message
|
||||||
.users=${this.users}
|
.message=${x}
|
||||||
.drafts=${this.drafts}
|
whoami=${this.whoami}
|
||||||
.expanded=${this.expanded}
|
.users=${this.users}
|
||||||
channel=${this.channel}
|
.drafts=${this.drafts}
|
||||||
channel_unread=${this.channel_unread}
|
.expanded=${this.expanded}
|
||||||
></tf-message>`
|
channel=${this.channel}
|
||||||
)}`
|
channel_unread=${this.channel_unread}
|
||||||
);
|
></tf-message>`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(false)}
|
||||||
|
>
|
||||||
|
Collapse
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
return this.render_frame(html`
|
||||||
|
<div class="w3-padding">
|
||||||
|
${this.content_group_by_author().map(
|
||||||
|
(x) => html`
|
||||||
|
<div>
|
||||||
|
<tf-user id=${x.author} .users=${this.users}></tf-user>
|
||||||
|
${x.action}
|
||||||
|
${x.users.map(
|
||||||
|
(y) => html`
|
||||||
|
<tf-user
|
||||||
|
id=${y}
|
||||||
|
.users=${this.users}
|
||||||
|
icon_only="true"
|
||||||
|
></tf-user>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(true)}
|
||||||
|
>
|
||||||
|
Expand
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
}
|
||||||
} else if (this.message.placeholder) {
|
} else if (this.message.placeholder) {
|
||||||
return this.render_frame(
|
return this.render_frame(
|
||||||
html`<div class="w3-padding">
|
html`<div>
|
||||||
<p>
|
<div class="w3-bar">
|
||||||
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
|
<a
|
||||||
>${this.message.id}</a
|
class="w3-bar-item w3-panel w3-round-xlarge w3-theme-d1 w3-margin w3-button"
|
||||||
|
target="_top"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
>
|
>
|
||||||
(placeholder)
|
This message is not currently available.
|
||||||
</p>
|
</a>
|
||||||
|
<div class="w3-bar-item w3-right">
|
||||||
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
|
%
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||||
|
style="right: 48px"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
target="_top"
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
|
>View Message</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-border-bottom"
|
||||||
|
@click=${this.copy_id}
|
||||||
|
>
|
||||||
|
Copy ID
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>${this.render_votes()}</div>
|
<div>${this.render_votes()}</div>
|
||||||
${(this.message.child_messages || []).map(
|
${(this.message.child_messages || []).map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
@ -631,7 +766,7 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
if (content.image !== undefined) {
|
if (content.image !== undefined) {
|
||||||
image = html`
|
image = html`
|
||||||
<div><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
<div @click=${this.body_click}><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
if (content.description !== undefined) {
|
if (content.description !== undefined) {
|
||||||
@ -654,25 +789,60 @@ class TfMessageElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
} else if (content.type == 'contact') {
|
} else if (content.type == 'contact') {
|
||||||
return html`
|
return this.render_frame(html`
|
||||||
<div class="w3-padding">
|
<div class="w3-bar">
|
||||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
<div class="w3-bar-item">
|
||||||
is
|
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||||
${content.blocking === true
|
is
|
||||||
? 'blocking'
|
${content.blocking === true
|
||||||
: content.blocking === false
|
? 'blocking'
|
||||||
? 'no longer blocking'
|
: content.blocking === false
|
||||||
: content.following === true
|
? 'no longer blocking'
|
||||||
? 'following'
|
: content.following === true
|
||||||
: content.following === false
|
? 'following'
|
||||||
? 'no longer following'
|
: content.following === false
|
||||||
: '?'}
|
? 'no longer following'
|
||||||
<tf-user
|
: '?'}
|
||||||
id=${this.message.content.contact}
|
<tf-user
|
||||||
.users=${this.users}
|
id=${this.message.content.contact}
|
||||||
></tf-user>
|
.users=${this.users}
|
||||||
|
></tf-user>
|
||||||
|
</div>
|
||||||
|
<div class="w3-bar-item w3-right">
|
||||||
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
|
%
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||||
|
style="right: 48px"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
target="_top"
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
|
>View Message</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-border-bottom"
|
||||||
|
@click=${this.copy_id}
|
||||||
|
>
|
||||||
|
Copy ID
|
||||||
|
</button>
|
||||||
|
${this.drafts[this.message?.id] === undefined
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
@click=${this.show_reply}
|
||||||
|
>
|
||||||
|
↩️ Reply
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${this.render_votes()} ${this.render_actions()}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`);
|
||||||
} else if (content.type == 'post') {
|
} else if (content.type == 'post') {
|
||||||
let self = this;
|
let self = this;
|
||||||
let body;
|
let body;
|
||||||
|
@ -14,6 +14,7 @@ class TfNewsElement extends LitElement {
|
|||||||
channel: {type: String},
|
channel: {type: String},
|
||||||
channel_unread: {type: Number},
|
channel_unread: {type: Number},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
hash: {type: String},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +167,10 @@ class TfNewsElement extends LitElement {
|
|||||||
if (message?.content?.type === 'contact') {
|
if (message?.content?.type === 'contact') {
|
||||||
group.push(message);
|
group.push(message);
|
||||||
} else {
|
} else {
|
||||||
if (group.length > 0) {
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: 'contact_group',
|
||||||
@ -177,7 +181,10 @@ class TfNewsElement extends LitElement {
|
|||||||
result.push(message);
|
result.push(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (group.length > 0) {
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: 'contact_group',
|
||||||
@ -187,15 +194,21 @@ class TfNewsElement extends LitElement {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unread_allowed() {
|
||||||
|
return !this.hash?.startsWith('#%') && !this.hash?.startsWith('#@');
|
||||||
|
}
|
||||||
|
|
||||||
load_and_render(messages) {
|
load_and_render(messages) {
|
||||||
let messages_by_id = this.process_messages(messages);
|
let messages_by_id = this.process_messages(messages);
|
||||||
let final_messages = this.group_following(
|
let final_messages = this.group_following(
|
||||||
this.finalize_messages(messages_by_id)
|
this.finalize_messages(messages_by_id)
|
||||||
);
|
);
|
||||||
let unread_rowid = -1;
|
let unread_rowid = -1;
|
||||||
for (let message of final_messages) {
|
if (this.unread_allowed()) {
|
||||||
if (message.rowid >= this.channel_unread) {
|
for (let message of final_messages) {
|
||||||
unread_rowid = message.rowid;
|
if (message.rowid >= this.channel_unread) {
|
||||||
|
unread_rowid = message.rowid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
@ -220,7 +233,19 @@ class TfNewsElement extends LitElement {
|
|||||||
<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"
|
||||||
></div>
|
></div>
|
||||||
<div style="color: #f00; padding: 8px">unread</div>
|
<button
|
||||||
|
style="color: #f00; padding: 8px"
|
||||||
|
class="w3-button"
|
||||||
|
@click=${() =>
|
||||||
|
this.dispatchEvent(
|
||||||
|
new Event('mark_all_read', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
unread
|
||||||
|
</button>
|
||||||
<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"
|
||||||
></div>
|
></div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
import {LitElement, html, until, unsafeHTML} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import * as tfutils from './tf-utils.js';
|
import * as tfutils from './tf-utils.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles} from './tf-styles.js';
|
||||||
@ -166,6 +166,74 @@ class TfProfileElement extends LitElement {
|
|||||||
navigator.clipboard.writeText(this.id);
|
navigator.clipboard.writeText(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_image(link) {
|
||||||
|
let div = document.createElement('div');
|
||||||
|
div.style.left = 0;
|
||||||
|
div.style.top = 0;
|
||||||
|
div.style.width = '100%';
|
||||||
|
div.style.height = '100%';
|
||||||
|
div.style.position = 'fixed';
|
||||||
|
div.style.background = '#000';
|
||||||
|
div.style.zIndex = 100;
|
||||||
|
div.style.display = 'grid';
|
||||||
|
let img = document.createElement('img');
|
||||||
|
img.src = link;
|
||||||
|
img.style.maxWidth = '100%';
|
||||||
|
img.style.maxHeight = '100%';
|
||||||
|
img.style.display = 'block';
|
||||||
|
img.style.margin = 'auto';
|
||||||
|
img.style.objectFit = 'contain';
|
||||||
|
img.style.width = '100%';
|
||||||
|
div.appendChild(img);
|
||||||
|
function image_close(event) {
|
||||||
|
document.body.removeChild(div);
|
||||||
|
window.removeEventListener('keydown', image_close);
|
||||||
|
}
|
||||||
|
div.onclick = image_close;
|
||||||
|
window.addEventListener('keydown', image_close);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
body_click(event) {
|
||||||
|
if (event.srcElement.tagName == 'IMG') {
|
||||||
|
this.show_image(event.srcElement.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle_account_list(event) {
|
||||||
|
let content = event.srcElement.nextElementSibling;
|
||||||
|
if (content.classList.toggle('w3-hide')) {
|
||||||
|
event.srcElement.innerText = 'Show Followed Accounts';
|
||||||
|
} else {
|
||||||
|
event.srcElement.innerText = 'Hide Followed Accounts';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async load_follows() {
|
||||||
|
let accounts = await tfrpc.rpc.following([this.id], 1);
|
||||||
|
return html`
|
||||||
|
<div class="w3-container">
|
||||||
|
<button
|
||||||
|
class="w3-button w3-block w3-theme-d1"
|
||||||
|
@click=${this.toggle_account_list}
|
||||||
|
>
|
||||||
|
Show Followed Accounts
|
||||||
|
</button>
|
||||||
|
<div class="w3-hide w3-card">
|
||||||
|
<ul class="w3-ul w3-theme-d4 w3-border-theme">
|
||||||
|
${Object.keys(accounts).map(
|
||||||
|
(x) => html`
|
||||||
|
<li class="w3-border-theme">
|
||||||
|
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||||
|
</li>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.load();
|
this.load();
|
||||||
let self = this;
|
let self = this;
|
||||||
@ -254,7 +322,7 @@ class TfProfileElement extends LitElement {
|
|||||||
<header class="w3-container">
|
<header class="w3-container">
|
||||||
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
||||||
</header>
|
</header>
|
||||||
<div class="w3-container">
|
<div class="w3-container" @click=${this.body_click}>
|
||||||
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
||||||
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
||||||
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
||||||
@ -280,6 +348,7 @@ class TfProfileElement extends LitElement {
|
|||||||
Blocked by ${profile.blocked} identities.
|
Blocked by ${profile.blocked} identities.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${until(this.load_follows(), html`<p>Loading accounts followed...</p>`)}
|
||||||
<footer class="w3-container">
|
<footer class="w3-container">
|
||||||
<p>
|
<p>
|
||||||
${edit}
|
${edit}
|
||||||
|
@ -277,7 +277,7 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
class="w3-button w3-block w3-theme-d1"
|
class="w3-button w3-block w3-theme-d1"
|
||||||
@click=${() => self.toggle_accordian('broadcasts')}
|
@click=${() => self.toggle_accordian('broadcasts')}
|
||||||
>
|
>
|
||||||
Broadcasts (${this.valid_broadcasts().length})
|
Discovery (${this.valid_broadcasts().length})
|
||||||
</h2>
|
</h2>
|
||||||
<ul class="w3-ul w3-border w3-hide" id="broadcasts">
|
<ul class="w3-ul w3-border w3-hide" id="broadcasts">
|
||||||
${this.valid_broadcasts().map((x) => self.render_broadcast(x))}
|
${this.valid_broadcasts().map((x) => self.render_broadcast(x))}
|
||||||
@ -308,6 +308,12 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
<div class="w3-bar-item">
|
<div class="w3-bar-item">
|
||||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||||
<div><small>${x.address}:${x.port}</small></div>
|
<div><small>${x.address}:${x.port}</small></div>
|
||||||
|
<div>
|
||||||
|
<small
|
||||||
|
>Last connection:
|
||||||
|
${new Date(x.last_success * 1000)}</small
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this.render_message(x)}
|
${this.render_message(x)}
|
||||||
|
@ -174,13 +174,13 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
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
|
FROM messages
|
||||||
JOIN json_each(?) AS following ON messages.author = following.value
|
JOIN json_each(?) AS following ON messages.author = following.value
|
||||||
WHERE messages.content ->> 'channel' = ?4
|
WHERE messages.content ->> 'channel' = ?4 AND messages.content ->> 'type' != 'vote'
|
||||||
UNION
|
UNION
|
||||||
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(?5)
|
FROM messages_refs
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
JOIN messages ON messages.id = messages_refs.message
|
||||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||||
JOIN json_tree(messages.content, '$.mentions') AS mention ON mention.value = '#' || ?4
|
WHERE messages_refs.ref = '#' || ?4 AND messages.content ->> 'type' != 'vote'
|
||||||
)
|
)
|
||||||
SELECT TRUE AS is_primary, all_news.* FROM all_news
|
SELECT TRUE AS is_primary, all_news.* FROM all_news
|
||||||
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
||||||
@ -191,7 +191,6 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
start_time,
|
start_time,
|
||||||
end_time,
|
end_time,
|
||||||
this.hash.substring(2),
|
this.hash.substring(2),
|
||||||
'"#' + this.hash.substring(2).replace('"', '""') + '"',
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
let t1 = new Date();
|
let t1 = new Date();
|
||||||
@ -209,27 +208,39 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
WHERE
|
WHERE
|
||||||
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
|
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
|
||||||
json(messages.content) LIKE '"%'
|
json(messages.content) LIKE '"%'
|
||||||
ORDER BY messages.sequence DESC LIMIT 20
|
ORDER BY messages.rowid DESC LIMIT 20
|
||||||
`,
|
`,
|
||||||
[JSON.stringify(this.private_messages), start_time, end_time]
|
[JSON.stringify(this.private_messages), start_time, end_time]
|
||||||
);
|
);
|
||||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||||
|
} else if (this.hash == '#👍') {
|
||||||
|
result = await tfrpc.rpc.query(
|
||||||
|
`
|
||||||
|
WITH votes 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
|
||||||
|
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||||
|
WHERE
|
||||||
|
messages.content ->> 'type' = 'vote' AND
|
||||||
|
(?2 IS NULL OR messages.timestamp >= ?2) AND messages.timestamp < ?3
|
||||||
|
ORDER BY timestamp DESC limit 20)
|
||||||
|
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
|
FROM votes
|
||||||
|
JOIN messages ON messages.id = votes.content ->> '$.vote.link'
|
||||||
|
UNION
|
||||||
|
SELECT TRUE AS is_primary, * FROM votes
|
||||||
|
`,
|
||||||
|
[JSON.stringify(this.following), start_time, end_time]
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let t0 = new Date();
|
let t0 = new Date();
|
||||||
let initial_messages = await tfrpc.rpc.query(
|
let initial_messages = await tfrpc.rpc.query(
|
||||||
`
|
`
|
||||||
WITH
|
SELECT TRUE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
all_news AS (
|
FROM messages
|
||||||
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
JOIN json_each(?) AS following ON messages.author = following.value
|
||||||
FROM messages
|
WHERE messages.timestamp < ?3 AND (?2 IS NULL OR messages.timestamp >= ?2) AND
|
||||||
JOIN json_each(?) AS following ON messages.author = following.value
|
messages.content ->> 'type' != 'vote'
|
||||||
),
|
ORDER BY timestamp DESC LIMIT 20
|
||||||
news AS (
|
|
||||||
SELECT * FROM all_news
|
|
||||||
WHERE all_news.timestamp < ?3 AND (?2 IS NULL OR all_news.timestamp >= ?2)
|
|
||||||
ORDER BY timestamp DESC LIMIT 20
|
|
||||||
)
|
|
||||||
SELECT TRUE AS is_primary, news.* FROM news
|
|
||||||
`,
|
`,
|
||||||
[JSON.stringify(this.following), start_time, end_time]
|
[JSON.stringify(this.following), start_time, end_time]
|
||||||
);
|
);
|
||||||
@ -258,6 +269,13 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unread_allowed() {
|
||||||
|
return (
|
||||||
|
this.hash == '#@' ||
|
||||||
|
(!this.hash.startsWith('#%') && !this.hash.startsWith('#@'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async load_more() {
|
async load_more() {
|
||||||
this.loading++;
|
this.loading++;
|
||||||
this.loading_canceled = false;
|
this.loading_canceled = false;
|
||||||
@ -407,9 +425,16 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
if (!this.hash.startsWith('#%')) {
|
if (!this.hash.startsWith('#%')) {
|
||||||
more = html`
|
more = html`
|
||||||
<p>
|
<p>
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
${this.unread_allowed()
|
||||||
Mark All Read
|
? html`
|
||||||
</button>
|
<button
|
||||||
|
class="w3-button w3-theme-d1"
|
||||||
|
@click=${this.mark_all_read}
|
||||||
|
>
|
||||||
|
Mark All Read
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
<button
|
<button
|
||||||
?disabled=${this.loading}
|
?disabled=${this.loading}
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-theme-d1"
|
||||||
@ -441,9 +466,14 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return cache(html`
|
return cache(html`
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
${this.unread_allowed()
|
||||||
Mark All Read
|
? html`<button
|
||||||
</button>
|
class="w3-button w3-theme-d1"
|
||||||
|
@click=${this.mark_all_read}
|
||||||
|
>
|
||||||
|
Mark All Read
|
||||||
|
</button>`
|
||||||
|
: undefined}
|
||||||
<tf-news
|
<tf-news
|
||||||
id="news"
|
id="news"
|
||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
@ -452,9 +482,11 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
.following=${this.following}
|
.following=${this.following}
|
||||||
.drafts=${this.drafts}
|
.drafts=${this.drafts}
|
||||||
.expanded=${this.expanded}
|
.expanded=${this.expanded}
|
||||||
|
hash=${this.hash}
|
||||||
channel=${this.channel()}
|
channel=${this.channel()}
|
||||||
channel_unread=${this.channels_unread?.[this.channel()]}
|
channel_unread=${this.channels_unread?.[this.channel()]}
|
||||||
.recent_reactions=${this.recent_reactions}
|
.recent_reactions=${this.recent_reactions}
|
||||||
|
@mark_all_read=${this.mark_all_read}
|
||||||
></tf-news>
|
></tf-news>
|
||||||
${more}
|
${more}
|
||||||
`);
|
`);
|
||||||
|
@ -25,6 +25,9 @@ class TfTabNewsElement extends LitElement {
|
|||||||
connections: {type: Array},
|
connections: {type: Array},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
peer_exchange: {type: Boolean},
|
||||||
|
is_administrator: {type: Boolean},
|
||||||
|
stay_connected: {type: Boolean},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +51,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
|
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
|
||||||
self.drafts = JSON.parse(d || '{}');
|
self.drafts = JSON.parse(d || '{}');
|
||||||
});
|
});
|
||||||
|
this.check_peer_exchange();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@ -60,6 +64,14 @@ class TfTabNewsElement extends LitElement {
|
|||||||
document.body.removeEventListener('keypress', this.on_keypress.bind(this));
|
document.body.removeEventListener('keypress', this.on_keypress.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async check_peer_exchange() {
|
||||||
|
if (await tfrpc.rpc.isAdministrator()) {
|
||||||
|
this.peer_exchange = await tfrpc.rpc.globalSettingsGet('peer_exchange');
|
||||||
|
} else {
|
||||||
|
this.peer_exchange = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
load_latest() {
|
load_latest() {
|
||||||
let news = this.shadowRoot?.getElementById('news');
|
let news = this.shadowRoot?.getElementById('news');
|
||||||
if (news) {
|
if (news) {
|
||||||
@ -164,6 +176,11 @@ class TfTabNewsElement extends LitElement {
|
|||||||
.map((x) => x[0]);
|
.map((x) => x[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async enable_peer_exchange() {
|
||||||
|
await tfrpc.rpc.globalSettingsSet('peer_exchange', true);
|
||||||
|
await this.check_peer_exchange();
|
||||||
|
}
|
||||||
|
|
||||||
render_sidebar() {
|
render_sidebar() {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@ -177,6 +194,32 @@ class TfTabNewsElement extends LitElement {
|
|||||||
>
|
>
|
||||||
×
|
×
|
||||||
</div>
|
</div>
|
||||||
|
${this.is_administrator
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
class="w3-bar-item w3-button"
|
||||||
|
@click=${() =>
|
||||||
|
this.dispatchEvent(
|
||||||
|
new Event('refresh', {bubbles: true, composed: true})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span style="width: 1.5em; height: 1.5em; padding: 8px">↻</span>
|
||||||
|
Sync now
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="w3-bar-item w3-button w3-ripple"
|
||||||
|
@click=${() =>
|
||||||
|
this.dispatchEvent(
|
||||||
|
new Event('toggle_stay_connected', {
|
||||||
|
bubbles: true,
|
||||||
|
composed: true,
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
${this.stay_connected ? '🔗 Online mode' : '⛓️💥 Passive mode'}
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
${this.hash.startsWith('##') &&
|
${this.hash.startsWith('##') &&
|
||||||
this.channels.indexOf(this.hash.substring(2)) == -1
|
this.channels.indexOf(this.hash.substring(2)) == -1
|
||||||
? html`
|
? html`
|
||||||
@ -202,6 +245,12 @@ class TfTabNewsElement extends LitElement {
|
|||||||
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
|
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
|
||||||
>${this.unread_status('@')}@mentions</a
|
>${this.unread_status('@')}@mentions</a
|
||||||
>
|
>
|
||||||
|
<a
|
||||||
|
href="#👍"
|
||||||
|
class="w3-bar-item w3-button"
|
||||||
|
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||||
|
>${this.unread_status('👍')}👍votes</a
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
href="#🔐"
|
href="#🔐"
|
||||||
class="w3-bar-item w3-button"
|
class="w3-bar-item w3-button"
|
||||||
@ -234,13 +283,42 @@ class TfTabNewsElement extends LitElement {
|
|||||||
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
|
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
|
||||||
<h4 style="margin: 0">Connections</h4>
|
<h4 style="margin: 0">Connections</h4>
|
||||||
</a>
|
</a>
|
||||||
|
${this.connections?.filter((x) => x.id)?.length == 0
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
class=${'w3-bar-item w3-button' +
|
||||||
|
(this.connections?.some((x) => x.flags.one_shot)
|
||||||
|
? ' w3-spin'
|
||||||
|
: '')}
|
||||||
|
@click=${() =>
|
||||||
|
this.dispatchEvent(
|
||||||
|
new Event('refresh', {bubbles: true, composed: true})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
↻ Sync now
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class=${'w3-bar-item w3-button' +
|
||||||
|
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
||||||
|
@click=${this.enable_peer_exchange}
|
||||||
|
>
|
||||||
|
Enable peer exchange
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
${this.connections
|
${this.connections
|
||||||
.filter((x) => x.id && !x.destroy_reason)
|
.filter((x) => x.id)
|
||||||
.map(
|
.map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
<tf-user
|
<tf-user
|
||||||
class="w3-bar-item"
|
class="w3-bar-item"
|
||||||
style="max-width: 100%"
|
style=${x.destroy_reason
|
||||||
|
? 'border-left: 4px solid red; border-right: 4px solid red'
|
||||||
|
: x.connected
|
||||||
|
? x.flags?.one_shot
|
||||||
|
? 'border-left: 4px solid blue; border-right: 4px solid blue'
|
||||||
|
: 'border-left: 4px solid green; border-right: 4px solid green'
|
||||||
|
: ''}
|
||||||
id=${x.id}
|
id=${x.id}
|
||||||
fallback_name=${x.host}
|
fallback_name=${x.host}
|
||||||
.users=${this.users}
|
.users=${this.users}
|
||||||
|
@ -7,6 +7,7 @@ class TfUserElement extends LitElement {
|
|||||||
return {
|
return {
|
||||||
id: {type: String},
|
id: {type: String},
|
||||||
fallback_name: {type: String},
|
fallback_name: {type: String},
|
||||||
|
icon_only: {type: Boolean},
|
||||||
users: {type: Object},
|
users: {type: Object},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -17,6 +18,7 @@ class TfUserElement extends LitElement {
|
|||||||
super();
|
super();
|
||||||
this.id = null;
|
this.id = null;
|
||||||
this.fallback_name = null;
|
this.fallback_name = null;
|
||||||
|
this.icon_only = false;
|
||||||
this.users = {};
|
this.users = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,9 +34,10 @@ class TfUserElement extends LitElement {
|
|||||||
>😎</span
|
>😎</span
|
||||||
>`;
|
>`;
|
||||||
let name = this.users?.[this.id]?.name;
|
let name = this.users?.[this.id]?.name;
|
||||||
name = html`<a target="_top" href=${'#' + this.id}
|
let name_string = name ?? this.fallback_name ?? this.id;
|
||||||
>${name ?? this.fallback_name ?? this.id}</a
|
name = this.icon_only
|
||||||
>`;
|
? undefined
|
||||||
|
: html`<a target="_top" href=${'#' + this.id}>${name_string}</a>`;
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
let image_link = user.image;
|
let image_link = user.image;
|
||||||
@ -48,6 +51,7 @@ class TfUserElement extends LitElement {
|
|||||||
class=${'w3-theme-l4 ' + shape}
|
class=${'w3-theme-l4 ' + shape}
|
||||||
style="width: 2em; height: 2em; vertical-align: middle; object-fit: cover"
|
style="width: 2em; height: 2em; vertical-align: middle; object-fit: cover"
|
||||||
src="/${image_link}/view"
|
src="/${image_link}/view"
|
||||||
|
title=${name_string + ' (' + this.id + ')'}
|
||||||
/>`;
|
/>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,9 +50,9 @@ function image(node, entering) {
|
|||||||
'</div>'
|
'</div>'
|
||||||
);
|
);
|
||||||
if (this.options.safe && potentiallyUnsafe(node.destination)) {
|
if (this.options.safe && potentiallyUnsafe(node.destination)) {
|
||||||
this.lit('<img src="" alt="');
|
this.lit('<img src="" title="');
|
||||||
} else {
|
} else {
|
||||||
this.lit('<img src="' + this.esc(node.destination) + '" alt="');
|
this.lit('<img src="' + this.esc(node.destination) + '" title="');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.disableTags += 1;
|
this.disableTags += 1;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "👋",
|
"emoji": "👋",
|
||||||
"previous": "&1o8MrBHfH42NnO+ruajwCmW/DUCb+IT1qtnAZI/agyo=.sha256"
|
"previous": "&3puDxDNnf6C+YXpFysYLgxFMAy54/AO9V7Xpja6qO/k=.sha256"
|
||||||
}
|
}
|
||||||
|
1521
apps/welcome/hermietildefriends.svg
Normal file
After Width: | Height: | Size: 86 KiB |
@ -28,23 +28,12 @@
|
|||||||
<b>😎 Tilde Friends</b>
|
<b>😎 Tilde Friends</b>
|
||||||
</h1>
|
</h1>
|
||||||
<h1 class="w3-xxlarge w3-text-green">
|
<h1 class="w3-xxlarge w3-text-green">
|
||||||
<b
|
<b>a Secure Scuttlebutt decentralized social network client</b>
|
||||||
>the Secure Scuttlebutt decentralized social network client that's
|
|
||||||
<i>fancy🎩</i></b
|
|
||||||
>
|
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
In addition to participating in Secure Scuttlebutt, Tilde Friends is
|
In addition to participating in Secure Scuttlebutt, Tilde Friends is
|
||||||
a platform for building, running, and sharing applications.
|
a platform for building, running, and sharing applications.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
Available for lots of devices:
|
|
||||||
<i class="fa-brands fa-linux w3-xlarge"></i>
|
|
||||||
<i class="fa-brands fa-android w3-xlarge"></i>
|
|
||||||
<i class="fa-brands fa-apple w3-xlarge"></i>
|
|
||||||
<i class="fa fa-mobile-screen w3-xlarge"></i>
|
|
||||||
<i class="fa-brands fa-windows w3-xlarge"></i>
|
|
||||||
</p>
|
|
||||||
<a
|
<a
|
||||||
class="w3-button w3-blue w3-padding-large"
|
class="w3-button w3-blue w3-padding-large"
|
||||||
href="https://www.tildefriends.net/~core/ssb/"
|
href="https://www.tildefriends.net/~core/ssb/"
|
||||||
@ -52,7 +41,7 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="w3-button w3-black w3-padding-large"
|
class="w3-button w3-black w3-padding-large"
|
||||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||||
><i class="fa fa-download"></i> Download</a
|
><i class="fa fa-download"></i> Download</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
@ -70,35 +59,6 @@
|
|||||||
href="https://www.tildefriends.net/~cory/tildeblog/"
|
href="https://www.tildefriends.net/~cory/tildeblog/"
|
||||||
><i class="fa fa-solid fa-square-rss"></i> Blog</a
|
><i class="fa fa-solid fa-square-rss"></i> Blog</a
|
||||||
>
|
>
|
||||||
<p>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
|
||||||
><img src="f-droid.svg" style="height: 2em; margin: 0" /> Get it
|
|
||||||
on F-Droid</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
|
||||||
>
|
|
||||||
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
|
||||||
Get Linux 64-bit AppImage
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
|
||||||
>
|
|
||||||
<img src="googleplay.svg" style="height: 2em; margin: 0" />
|
|
||||||
Get it on Google Play (Open Testing)
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://testflight.apple.com/join/tXxgtSpE"
|
|
||||||
>
|
|
||||||
<img src="ios.svg" style="height: 2em; margin: 0" />
|
|
||||||
Get it on iOS (TestFlight)
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-col l4 m6">
|
<div class="w3-col l4 m6">
|
||||||
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
||||||
@ -116,15 +76,119 @@
|
|||||||
<h2>First-time user checklist:</h2>
|
<h2>First-time user checklist:</h2>
|
||||||
<ol type="1" style="text-align: left">
|
<ol type="1" style="text-align: left">
|
||||||
<li>
|
<li>
|
||||||
<a href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
<a
|
||||||
|
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||||
>Download</a
|
>Download</a
|
||||||
>
|
>
|
||||||
Tilde Friends or use
|
Tilde Friends or use
|
||||||
<a href="https://www.tildefriends.net/"
|
<a href="https://www.tildefriends.net/"
|
||||||
>https://www.tildefriends.net/</a
|
>https://www.tildefriends.net/</a
|
||||||
>.
|
>.
|
||||||
|
<div class="w3-cell-row">
|
||||||
|
<div class="w3-container w3-cell w3-mobile">
|
||||||
|
<h3>Mobile</h3>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
|
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||||
|
><img src="f-droid.svg" style="height: 2em; margin: 0" />
|
||||||
|
Get it on F-Droid</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
|
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="googleplay.svg"
|
||||||
|
style="height: 2em; margin: 0"
|
||||||
|
/>
|
||||||
|
Get it on Google Play (Open Testing)
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
|
href="https://testflight.apple.com/join/tXxgtSpE"
|
||||||
|
>
|
||||||
|
<img src="ios.svg" style="height: 2em; margin: 0" />
|
||||||
|
Get it on iOS (TestFlight)
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>Just launch the app.</p>
|
||||||
|
</div>
|
||||||
|
<div class="w3-container w3-cell w3-mobile">
|
||||||
|
<h3>Web</h3>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-blue w3-padding-large"
|
||||||
|
href="https://www.tildefriends.net/~core/ssb/"
|
||||||
|
>🦀 Try It</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="/login?return=/~core/intro"
|
||||||
|
>Register an account with tildefriends.net</a
|
||||||
|
>
|
||||||
|
to take it for a spin right away.
|
||||||
|
</p>
|
||||||
|
<h3>PeachCloud</h3>
|
||||||
|
<p>
|
||||||
|
Tilde Friends is also a part of 🍑☁️<a
|
||||||
|
href="https://peach-docs.commoninternet.net/"
|
||||||
|
>PeachCloud</a
|
||||||
|
>, which is available on
|
||||||
|
<a href="https://apps.yunohost.org/app/peachpub"
|
||||||
|
>YunoHost</a
|
||||||
|
>
|
||||||
|
for accessible self-hosting.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="w3-container w3-cell w3-mobile">
|
||||||
|
<h3>Desktop</h3>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-black w3-padding-large"
|
||||||
|
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||||
|
><i class="fa fa-download"></i> Download</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray"
|
||||||
|
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
||||||
|
>
|
||||||
|
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
||||||
|
Get Linux 64-bit AppImage
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Tilde Friends is distributed as a single executable file (or
|
||||||
|
source that you can
|
||||||
|
<a href="http://dev.tildefriends.net">build yourself</a>)
|
||||||
|
and stores all of its data in a single
|
||||||
|
file(<code>db.sqlite</code>). You can generally download the
|
||||||
|
latest executable from
|
||||||
|
<a
|
||||||
|
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||||
|
>releases</a
|
||||||
|
>
|
||||||
|
for your platform, mark it as executable (<code
|
||||||
|
>chmod +x tildefriends*</code
|
||||||
|
>
|
||||||
|
on macOS and Linux), and run. Run with <code>-h</code> to
|
||||||
|
learn more.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Tilde Friends will run in the console and provide a web
|
||||||
|
interface at
|
||||||
|
<a href="http://localhost:12345/">http://localhost:12345/</a
|
||||||
|
>. You will have to register a username and password to sign
|
||||||
|
into your instance.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
After a <a href="/~core/intro">brief introduction</a>, Tilde
|
||||||
|
Friends will take you to the Secure Scuttlebutt social network
|
||||||
|
app.
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>Create an account to identify yourself with that instance.</li>
|
|
||||||
<li>
|
<li>
|
||||||
Describe yourself in your profile in the <b>ssb</b> app. Give
|
Describe yourself in your profile in the <b>ssb</b> app. Give
|
||||||
yourself a name and an avatar if you like.
|
yourself a name and an avatar if you like.
|
||||||
@ -158,11 +222,11 @@
|
|||||||
<!-- SSB Section -->
|
<!-- SSB Section -->
|
||||||
<div class="w3-light-grey">
|
<div class="w3-light-grey">
|
||||||
<div class="w3-row-padding w3-padding-64">
|
<div class="w3-row-padding w3-padding-64">
|
||||||
<div class="w3-col l4 m6 s4">
|
<div class="w3-col l4 m6 s4 w3-center">
|
||||||
<a href="https://scuttlebutt.nz/"
|
<a href="https://scuttlebutt.nz/"
|
||||||
><img
|
><img
|
||||||
class="w3-image w3-round-large"
|
class="w3-image"
|
||||||
src="ssb.png"
|
src="hermietildefriends.svg"
|
||||||
alt="Secure Scuttlebutt"
|
alt="Secure Scuttlebutt"
|
||||||
/></a>
|
/></a>
|
||||||
</div>
|
</div>
|
||||||
|
Before Width: | Height: | Size: 50 KiB |
@ -8,6 +8,7 @@ let gFiles = {};
|
|||||||
let gApp = {files: {}, emoji: '📦'};
|
let gApp = {files: {}, emoji: '📦'};
|
||||||
let gEditor;
|
let gEditor;
|
||||||
let gOriginalInput;
|
let gOriginalInput;
|
||||||
|
let gUnloading;
|
||||||
|
|
||||||
let kErrorColor = '#dc322f';
|
let kErrorColor = '#dc322f';
|
||||||
let kDisconnectColor = '#f00';
|
let kDisconnectColor = '#f00';
|
||||||
@ -1560,27 +1561,31 @@ function connectSocket(path) {
|
|||||||
_receive_websocket_message(JSON.parse(event.data));
|
_receive_websocket_message(JSON.parse(event.data));
|
||||||
};
|
};
|
||||||
gSocket.onclose = function (event) {
|
gSocket.onclose = function (event) {
|
||||||
const k_codes = {
|
if (gUnloading) {
|
||||||
1000: 'Normal closure',
|
setStatusMessage('⚪ Closing...', kStatusColor);
|
||||||
1001: 'Going away',
|
} else {
|
||||||
1002: 'Protocol error',
|
const k_codes = {
|
||||||
1003: 'Unsupported data',
|
1000: 'Normal closure',
|
||||||
1005: 'No status received',
|
1001: 'Going away',
|
||||||
1006: 'Abnormal closure',
|
1002: 'Protocol error',
|
||||||
1007: 'Invalid frame payload data',
|
1003: 'Unsupported data',
|
||||||
1008: 'Policy violation',
|
1005: 'No status received',
|
||||||
1009: 'Message too big',
|
1006: 'Abnormal closure',
|
||||||
1010: 'Missing extension',
|
1007: 'Invalid frame payload data',
|
||||||
1011: 'Internal error',
|
1008: 'Policy violation',
|
||||||
1012: 'Service restart',
|
1009: 'Message too big',
|
||||||
1013: 'Try again later',
|
1010: 'Missing extension',
|
||||||
1014: 'Bad gateway',
|
1011: 'Internal error',
|
||||||
1015: 'TLS handshake',
|
1012: 'Service restart',
|
||||||
};
|
1013: 'Try again later',
|
||||||
setStatusMessage(
|
1014: 'Bad gateway',
|
||||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
1015: 'TLS handshake',
|
||||||
kDisconnectColor
|
};
|
||||||
);
|
setStatusMessage(
|
||||||
|
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||||
|
kDisconnectColor
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1854,6 +1859,9 @@ window.addEventListener('load', function () {
|
|||||||
window.addEventListener('blur', blur);
|
window.addEventListener('blur', blur);
|
||||||
window.addEventListener('message', message, false);
|
window.addEventListener('message', message, false);
|
||||||
window.addEventListener('online', connectSocket);
|
window.addEventListener('online', connectSocket);
|
||||||
|
window.addEventListener('beforeunload', function () {
|
||||||
|
gUnloading = true;
|
||||||
|
});
|
||||||
document.getElementById('name').value = window.location.pathname;
|
document.getElementById('name').value = window.location.pathname;
|
||||||
document
|
document
|
||||||
.getElementById('closeEditor')
|
.getElementById('closeEditor')
|
||||||
|
@ -25,14 +25,14 @@
|
|||||||
}:
|
}:
|
||||||
pkgs.stdenv.mkDerivation rec {
|
pkgs.stdenv.mkDerivation rec {
|
||||||
pname = "tildefriends";
|
pname = "tildefriends";
|
||||||
version = "0.0.30";
|
version = "0.0.32";
|
||||||
|
|
||||||
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-t5yvouzSL2j/ge1VHLqzIZ+Avqj4iEDt7L+yrHoTZAQ=";
|
hash = "sha256-Dk0NOEQIg2LeENySK0+MgpZEtfsClGq6dZL+eOOpE0U=";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
2
deps/codemirror/cm6.js
vendored
223
deps/codemirror_src/package-lock.json
generated
vendored
@ -83,18 +83,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/lang-json": {
|
"node_modules/@codemirror/lang-json": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
|
||||||
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@lezer/json": "^1.0.0"
|
"@lezer/json": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/language": {
|
"node_modules/@codemirror/language": {
|
||||||
"version": "6.11.0",
|
"version": "6.11.2",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.2.tgz",
|
||||||
"integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==",
|
"integrity": "sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
"@codemirror/view": "^6.23.0",
|
"@codemirror/view": "^6.23.0",
|
||||||
@ -133,9 +133,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/theme-one-dark": {
|
"node_modules/@codemirror/theme-one-dark": {
|
||||||
"version": "6.1.2",
|
"version": "6.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
|
||||||
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
|
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
@ -144,11 +144,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.36.8",
|
"version": "6.38.0",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.0.tgz",
|
||||||
"integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==",
|
"integrity": "sha512-yvSchUwHOdupXkd7xJ0ob36jdsSR/I+/C+VbY0ffBiL5NiSTEBDfB1ZGWbbIlDd5xgdUkody+lukAdOxYrOBeg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
|
"crelt": "^1.0.6",
|
||||||
"style-mod": "^4.1.0",
|
"style-mod": "^4.1.0",
|
||||||
"w3c-keyname": "^2.2.4"
|
"w3c-keyname": "^2.2.4"
|
||||||
}
|
}
|
||||||
@ -323,9 +324,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.1.4",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
||||||
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
|
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0",
|
"@types/estree": "^1.0.0",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
@ -344,9 +345,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.1.tgz",
|
||||||
"integrity": "sha512-KxN+zCjOYHGwCl4UCtSfZ6jrq/qi88JDUtiEFk8LELEHq2Egfc/FgW+jItZiOLRuQfb/3xJSgFuNPC9jzggX+A==",
|
"integrity": "sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -356,9 +357,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.1.tgz",
|
||||||
"integrity": "sha512-yDvqx3lWlcugozax3DItKJI5j05B0d4Kvnjx+5mwiUpWramVvmAByYigMplaoAQ3pvdprGCTCE03eduqE/8mPQ==",
|
"integrity": "sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -368,9 +369,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.1.tgz",
|
||||||
"integrity": "sha512-2KOU574vD3gzcPSjxO0eyR5iWlnxxtmW1F5CkNOHmMlueKNCQkxR6+ekgWyVnz6zaZihpUNkGxjsYrkTJKhkaw==",
|
"integrity": "sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -380,9 +381,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.1.tgz",
|
||||||
"integrity": "sha512-gE5ACNSxHcEZyP2BA9TuTakfZvULEW4YAOtxl/A/YDbIir/wPKukde0BNPlnBiP88ecaN4BJI2TtAd+HKuZPQQ==",
|
"integrity": "sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -392,9 +393,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.1.tgz",
|
||||||
"integrity": "sha512-GSxU6r5HnWij7FoSo7cZg3l5GPg4HFLkzsFFh0N/b16q5buW1NAWuCJ+HMtIdUEi6XF0qH+hN0TEd78laRp7Dg==",
|
"integrity": "sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -404,9 +405,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.1.tgz",
|
||||||
"integrity": "sha512-KGiGKGDg8qLRyOWmk6IeiHJzsN/OYxO6nSbT0Vj4MwjS2XQy/5emsmtoqLAabqrohbgLWJ5GV3s/ljdrIr8Qjg==",
|
"integrity": "sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -416,9 +417,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.1.tgz",
|
||||||
"integrity": "sha512-46OzWeqEVQyX3N2/QdiU/CMXYDH/lSHpgfBkuhl3igpZiaB3ZIfSjKuOnybFVBQzjsLwkus2mjaESy8H41SzvA==",
|
"integrity": "sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -428,9 +429,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.1.tgz",
|
||||||
"integrity": "sha512-lfgW3KtQP4YauqdPpcUZHPcqQXmTmH4nYU0cplNeW583CMkAGjtImw4PKli09NFi2iQgChk4e9erkwlfYem6Lg==",
|
"integrity": "sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@ -440,9 +441,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.1.tgz",
|
||||||
"integrity": "sha512-nn8mEyzMbdEJzT7cwxgObuwviMx6kPRxzYiOl6o/o+ChQq23gfdlZcUNnt89lPhhz3BYsZ72rp0rxNqBSfqlqw==",
|
"integrity": "sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -452,9 +453,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.1.tgz",
|
||||||
"integrity": "sha512-l+QK99je2zUKGd31Gh+45c4pGDAqZSuWQiuRFCdHYC2CSiO47qUWsCcenrI6p22hvHZrDje9QjwSMAFL3iwXwQ==",
|
"integrity": "sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -464,9 +465,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.1.tgz",
|
||||||
"integrity": "sha512-WbnJaxPv1gPIm6S8O/Wg+wfE/OzGSXlBMbOe4ie+zMyykMOeqmgD1BhPxZQuDqwUN+0T/xOFtL2RUWBspnZj3w==",
|
"integrity": "sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@ -476,9 +477,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.1.tgz",
|
||||||
"integrity": "sha512-eRDWR5t67/b2g8Q/S8XPi0YdbKcCs4WQ8vklNnUYLaSWF+Cbv2axZsp4jni6/j7eKvMLYCYdcsv8dcU+a6QNFg==",
|
"integrity": "sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@ -488,9 +489,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.1.tgz",
|
||||||
"integrity": "sha512-TWrZb6GF5jsEKG7T1IHwlLMDRy2f3DPqYldmIhnA2DVqvvhY2Ai184vZGgahRrg8k9UBWoSlHv+suRfTN7Ua4A==",
|
"integrity": "sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -500,9 +501,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.1.tgz",
|
||||||
"integrity": "sha512-ieQljaZKuJpmWvd8gW87ZmSFwid6AxMDk5bhONJ57U8zT77zpZ/TPKkU9HpnnFrM4zsgr4kiGuzbIbZTGi7u9A==",
|
"integrity": "sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@ -512,9 +513,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.1.tgz",
|
||||||
"integrity": "sha512-/L3pW48SxrWAlVsKCN0dGLB2bi8Nv8pr5S5ocSM+S0XCn5RCVCXqi8GVtHFsOBBCSeR+u9brV2zno5+mg3S4Aw==",
|
"integrity": "sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@ -524,9 +525,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.1.tgz",
|
||||||
"integrity": "sha512-XMLeKjyH8NsEDCRptf6LO8lJk23o9wvB+dJwcXMaH6ZQbbkHu2dbGIUindbMtRN6ux1xKi16iXWu6q9mu7gDhQ==",
|
"integrity": "sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -536,9 +537,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.1.tgz",
|
||||||
"integrity": "sha512-m/P7LycHZTvSQeXhFmgmdqEiTqSV80zn6xHaQ1JSqwCtD1YGtwEK515Qmy9DcB2HK4dOUVypQxvhVSy06cJPEg==",
|
"integrity": "sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -548,9 +549,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.1.tgz",
|
||||||
"integrity": "sha512-4yodtcOrFHpbomJGVEqZ8fzD4kfBeCbpsUy5Pqk4RluXOdsWdjLnjhiKy2w3qzcASWd04fp52Xz7JKarVJ5BTg==",
|
"integrity": "sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -560,9 +561,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.1.tgz",
|
||||||
"integrity": "sha512-tmazCrAsKzdkXssEc65zIE1oC6xPHwfy9d5Ta25SRCDOZS+I6RypVVShWALNuU9bxIfGA0aqrmzlzoM5wO5SPQ==",
|
"integrity": "sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -572,9 +573,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.1.tgz",
|
||||||
"integrity": "sha512-h1J+Yzjo/X+0EAvR2kIXJDuTuyT7drc+t2ALY0nIcGPbTatNOf0VWdhEA2Z4AAjv6X1NJV7SYo5oCTYRJhSlVA==",
|
"integrity": "sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -584,9 +585,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/resolve": {
|
"node_modules/@types/resolve": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.2",
|
||||||
@ -594,9 +595,9 @@
|
|||||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.14.1",
|
"version": "8.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@ -612,9 +613,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/codemirror": {
|
"node_modules/codemirror": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/commands": "^6.0.0",
|
"@codemirror/commands": "^6.0.0",
|
||||||
@ -745,11 +746,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.41.0",
|
"version": "4.44.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.1.tgz",
|
||||||
"integrity": "sha512-HqMFpUbWlf/tvcxBFNKnJyzc7Lk+XO3FGc3pbNBLqEbOz0gPLRgcrlS3UF4MfUrVlstOaP/q0kM6GVvi+LrLRg==",
|
"integrity": "sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.7"
|
"@types/estree": "1.0.8"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@ -759,26 +760,26 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.41.0",
|
"@rollup/rollup-android-arm-eabi": "4.44.1",
|
||||||
"@rollup/rollup-android-arm64": "4.41.0",
|
"@rollup/rollup-android-arm64": "4.44.1",
|
||||||
"@rollup/rollup-darwin-arm64": "4.41.0",
|
"@rollup/rollup-darwin-arm64": "4.44.1",
|
||||||
"@rollup/rollup-darwin-x64": "4.41.0",
|
"@rollup/rollup-darwin-x64": "4.44.1",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.41.0",
|
"@rollup/rollup-freebsd-arm64": "4.44.1",
|
||||||
"@rollup/rollup-freebsd-x64": "4.41.0",
|
"@rollup/rollup-freebsd-x64": "4.44.1",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.41.0",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.44.1",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.41.0",
|
"@rollup/rollup-linux-arm-musleabihf": "4.44.1",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.41.0",
|
"@rollup/rollup-linux-arm64-gnu": "4.44.1",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.41.0",
|
"@rollup/rollup-linux-arm64-musl": "4.44.1",
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.41.0",
|
"@rollup/rollup-linux-loongarch64-gnu": "4.44.1",
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.0",
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.1",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.41.0",
|
"@rollup/rollup-linux-riscv64-gnu": "4.44.1",
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.41.0",
|
"@rollup/rollup-linux-riscv64-musl": "4.44.1",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.41.0",
|
"@rollup/rollup-linux-s390x-gnu": "4.44.1",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.41.0",
|
"@rollup/rollup-linux-x64-gnu": "4.44.1",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.41.0",
|
"@rollup/rollup-linux-x64-musl": "4.44.1",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.41.0",
|
"@rollup/rollup-win32-arm64-msvc": "4.44.1",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.41.0",
|
"@rollup/rollup-win32-ia32-msvc": "4.44.1",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.41.0",
|
"@rollup/rollup-win32-x64-msvc": "4.44.1",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -853,9 +854,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.39.2",
|
"version": "5.43.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.39.2.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
||||||
"integrity": "sha512-yEPUmWve+VA78bI71BW70Dh0TuV4HHd+I5SHOAfS1+QBOmvmCiiffgjR8ryyEd3KIfvPGFqoADt8LdQ6XpXIvg==",
|
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
2
deps/openssl_src
vendored
966
deps/sqlite/shell.c
vendored
4467
deps/sqlite/sqlite3.c
vendored
232
deps/sqlite/sqlite3.h
vendored
@ -133,7 +133,7 @@ extern "C" {
|
|||||||
**
|
**
|
||||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||||
** SQLite source code has been stored in the
|
** SQLite source code has been stored in the
|
||||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
** <a href="http://fossil-scm.org/">Fossil configuration management
|
||||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||||
** a string which identifies a particular check-in of SQLite
|
** a string which identifies a particular check-in of SQLite
|
||||||
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
||||||
@ -146,9 +146,9 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.49.2"
|
#define SQLITE_VERSION "3.50.2"
|
||||||
#define SQLITE_VERSION_NUMBER 3049002
|
#define SQLITE_VERSION_NUMBER 3050002
|
||||||
#define SQLITE_SOURCE_ID "2025-05-07 10:39:52 17144570b0d96ae63cd6f3edca39e27ebd74925252bbaf6723bcb2f6b4861fb1"
|
#define SQLITE_SOURCE_ID "2025-06-28 14:00:48 2af157d77fb1304a74176eaee7fbc7c7e932d946bf25325e9c26c91db19e3079"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
@ -1163,6 +1163,12 @@ struct sqlite3_io_methods {
|
|||||||
** the value that M is to be set to. Before returning, the 32-bit signed
|
** the value that M is to be set to. Before returning, the 32-bit signed
|
||||||
** integer is overwritten with the previous value of M.
|
** integer is overwritten with the previous value of M.
|
||||||
**
|
**
|
||||||
|
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
|
||||||
|
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
|
||||||
|
** VFS to block when taking a SHARED lock to connect to a wal mode database.
|
||||||
|
** This is used to implement the functionality associated with
|
||||||
|
** SQLITE_SETLK_BLOCK_ON_CONNECT.
|
||||||
|
**
|
||||||
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
||||||
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
||||||
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
||||||
@ -1259,6 +1265,7 @@ struct sqlite3_io_methods {
|
|||||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||||
#define SQLITE_FCNTL_RESET_CACHE 42
|
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||||
#define SQLITE_FCNTL_NULL_IO 43
|
#define SQLITE_FCNTL_NULL_IO 43
|
||||||
|
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
||||||
|
|
||||||
/* deprecated names */
|
/* deprecated names */
|
||||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||||
@ -1989,13 +1996,16 @@ struct sqlite3_mem_methods {
|
|||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
||||||
** the default size of lookaside memory on each [database connection].
|
** the default size of [lookaside memory] on each [database connection].
|
||||||
** The first argument is the
|
** The first argument is the
|
||||||
** size of each lookaside buffer slot and the second is the number of
|
** size of each lookaside buffer slot ("sz") and the second is the number of
|
||||||
** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
|
** slots allocated to each database connection ("cnt").)^
|
||||||
** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
|
** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
|
||||||
** option to [sqlite3_db_config()] can be used to change the lookaside
|
** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
|
||||||
** configuration on individual connections.)^ </dd>
|
** be used to change the lookaside configuration on individual connections.)^
|
||||||
|
** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
|
||||||
|
** default lookaside configuration at compile-time.
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
||||||
@ -2232,31 +2242,50 @@ struct sqlite3_mem_methods {
|
|||||||
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
||||||
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
||||||
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
||||||
** configuration of the lookaside memory allocator within a database
|
** configuration of the [lookaside memory allocator] within a database
|
||||||
** connection.
|
** connection.
|
||||||
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
||||||
** in the [DBCONFIG arguments|usual format].
|
** in the [DBCONFIG arguments|usual format].
|
||||||
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
||||||
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
||||||
** should have a total of five parameters.
|
** should have a total of five parameters.
|
||||||
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
|
** <ol>
|
||||||
|
** <li><p>The first argument ("buf") is a
|
||||||
** pointer to a memory buffer to use for lookaside memory.
|
** pointer to a memory buffer to use for lookaside memory.
|
||||||
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
|
** The first argument may be NULL in which case SQLite will allocate the
|
||||||
** may be NULL in which case SQLite will allocate the
|
** lookaside buffer itself using [sqlite3_malloc()].
|
||||||
** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
|
** <li><P>The second argument ("sz") is the
|
||||||
** size of each lookaside buffer slot. ^The third argument is the number of
|
** size of each lookaside buffer slot. Lookaside is disabled if "sz"
|
||||||
** slots. The size of the buffer in the first argument must be greater than
|
** is less than 8. The "sz" argument should be a multiple of 8 less than
|
||||||
** or equal to the product of the second and third arguments. The buffer
|
** 65536. If "sz" does not meet this constraint, it is reduced in size until
|
||||||
** must be aligned to an 8-byte boundary. ^If the second argument to
|
** it does.
|
||||||
** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
|
** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
|
||||||
** rounded down to the next smaller multiple of 8. ^(The lookaside memory
|
** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
|
||||||
|
** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
|
||||||
|
** parameter is usually chosen so that the product of "sz" and "cnt" is less
|
||||||
|
** than 1,000,000.
|
||||||
|
** </ol>
|
||||||
|
** <p>If the "buf" argument is not NULL, then it must
|
||||||
|
** point to a memory buffer with a size that is greater than
|
||||||
|
** or equal to the product of "sz" and "cnt".
|
||||||
|
** The buffer must be aligned to an 8-byte boundary.
|
||||||
|
** The lookaside memory
|
||||||
** configuration for a database connection can only be changed when that
|
** configuration for a database connection can only be changed when that
|
||||||
** connection is not currently using lookaside memory, or in other words
|
** connection is not currently using lookaside memory, or in other words
|
||||||
** when the "current value" returned by
|
** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
|
||||||
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
|
||||||
** Any attempt to change the lookaside memory configuration when lookaside
|
** Any attempt to change the lookaside memory configuration when lookaside
|
||||||
** memory is in use leaves the configuration unchanged and returns
|
** memory is in use leaves the configuration unchanged and returns
|
||||||
** [SQLITE_BUSY].)^</dd>
|
** [SQLITE_BUSY].
|
||||||
|
** If the "buf" argument is NULL and an attempt
|
||||||
|
** to allocate memory based on "sz" and "cnt" fails, then
|
||||||
|
** lookaside is silently disabled.
|
||||||
|
** <p>
|
||||||
|
** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
|
||||||
|
** default lookaside configuration at initialization. The
|
||||||
|
** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
|
||||||
|
** configuration at compile-time. Typical values for lookaside are 1200 for
|
||||||
|
** "sz" and 40 to 100 for "cnt".
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
||||||
@ -2993,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
|
|||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Set the Setlk Timeout
|
||||||
|
** METHOD: sqlite3
|
||||||
|
**
|
||||||
|
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
|
||||||
|
** the VFS supports blocking locks, it sets the timeout in ms used by
|
||||||
|
** eligible locks taken on wal mode databases by the specified database
|
||||||
|
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
|
||||||
|
** not support blocking locks, this function is a no-op.
|
||||||
|
**
|
||||||
|
** Passing 0 to this function disables blocking locks altogether. Passing
|
||||||
|
** -1 to this function requests that the VFS blocks for a long time -
|
||||||
|
** indefinitely if possible. The results of passing any other negative value
|
||||||
|
** are undefined.
|
||||||
|
**
|
||||||
|
** Internally, each SQLite database handle store two timeout values - the
|
||||||
|
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||||
|
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||||
|
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||||
|
** values, this function sets only the setlk-timeout value. Therefore,
|
||||||
|
** to configure separate busy-timeout and setlk-timeout values for a single
|
||||||
|
** database handle, call sqlite3_busy_timeout() followed by this function.
|
||||||
|
**
|
||||||
|
** Whenever the number of connections to a wal mode database falls from
|
||||||
|
** 1 to 0, the last connection takes an exclusive lock on the database,
|
||||||
|
** then checkpoints and deletes the wal file. While it is doing this, any
|
||||||
|
** new connection that tries to read from the database fails with an
|
||||||
|
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
|
||||||
|
** passed to this API, the new connection blocks until the exclusive lock
|
||||||
|
** has been released.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Flags for sqlite3_setlk_timeout()
|
||||||
|
*/
|
||||||
|
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Convenience Routines For Running Queries
|
** CAPI3REF: Convenience Routines For Running Queries
|
||||||
** METHOD: sqlite3
|
** METHOD: sqlite3
|
||||||
@ -4012,7 +4079,7 @@ SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
|
|||||||
**
|
**
|
||||||
** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of
|
** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of
|
||||||
** database filename D with corresponding journal file J and WAL file W and
|
** database filename D with corresponding journal file J and WAL file W and
|
||||||
** with N URI parameters key/values pairs in the array P. The result from
|
** an array P of N URI Key/Value pairs. The result from
|
||||||
** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that
|
** sqlite3_create_filename(D,J,W,N,P) is a pointer to a database filename that
|
||||||
** is safe to pass to routines like:
|
** is safe to pass to routines like:
|
||||||
** <ul>
|
** <ul>
|
||||||
@ -4693,7 +4760,7 @@ typedef struct sqlite3_context sqlite3_context;
|
|||||||
** METHOD: sqlite3_stmt
|
** METHOD: sqlite3_stmt
|
||||||
**
|
**
|
||||||
** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants,
|
** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants,
|
||||||
** literals may be replaced by a [parameter] that matches one of following
|
** literals may be replaced by a [parameter] that matches one of the following
|
||||||
** templates:
|
** templates:
|
||||||
**
|
**
|
||||||
** <ul>
|
** <ul>
|
||||||
@ -4738,7 +4805,7 @@ typedef struct sqlite3_context sqlite3_context;
|
|||||||
**
|
**
|
||||||
** [[byte-order determination rules]] ^The byte-order of
|
** [[byte-order determination rules]] ^The byte-order of
|
||||||
** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
|
** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
|
||||||
** found in first character, which is removed, or in the absence of a BOM
|
** found in the first character, which is removed, or in the absence of a BOM
|
||||||
** the byte order is the native byte order of the host
|
** the byte order is the native byte order of the host
|
||||||
** machine for sqlite3_bind_text16() or the byte order specified in
|
** machine for sqlite3_bind_text16() or the byte order specified in
|
||||||
** the 6th parameter for sqlite3_bind_text64().)^
|
** the 6th parameter for sqlite3_bind_text64().)^
|
||||||
@ -4758,7 +4825,7 @@ typedef struct sqlite3_context sqlite3_context;
|
|||||||
** or sqlite3_bind_text16() or sqlite3_bind_text64() then
|
** or sqlite3_bind_text16() or sqlite3_bind_text64() then
|
||||||
** that parameter must be the byte offset
|
** that parameter must be the byte offset
|
||||||
** where the NUL terminator would occur assuming the string were NUL
|
** where the NUL terminator would occur assuming the string were NUL
|
||||||
** terminated. If any NUL characters occurs at byte offsets less than
|
** terminated. If any NUL characters occur at byte offsets less than
|
||||||
** the value of the fourth parameter then the resulting string value will
|
** the value of the fourth parameter then the resulting string value will
|
||||||
** contain embedded NULs. The result of expressions involving strings
|
** contain embedded NULs. The result of expressions involving strings
|
||||||
** with embedded NULs is undefined.
|
** with embedded NULs is undefined.
|
||||||
@ -4970,7 +5037,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);
|
|||||||
** METHOD: sqlite3_stmt
|
** METHOD: sqlite3_stmt
|
||||||
**
|
**
|
||||||
** ^These routines provide a means to determine the database, table, and
|
** ^These routines provide a means to determine the database, table, and
|
||||||
** table column that is the origin of a particular result column in
|
** table column that is the origin of a particular result column in a
|
||||||
** [SELECT] statement.
|
** [SELECT] statement.
|
||||||
** ^The name of the database or table or column can be returned as
|
** ^The name of the database or table or column can be returned as
|
||||||
** either a UTF-8 or UTF-16 string. ^The _database_ routines return
|
** either a UTF-8 or UTF-16 string. ^The _database_ routines return
|
||||||
@ -5108,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
|||||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||||
** sqlite3_step(). Failure to reset the prepared statement using
|
** sqlite3_step(). Failure to reset the prepared statement using
|
||||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
||||||
** sqlite3_step() began
|
** sqlite3_step() began
|
||||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||||
@ -5539,8 +5606,8 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
|
|||||||
**
|
**
|
||||||
** For best security, the [SQLITE_DIRECTONLY] flag is recommended for
|
** For best security, the [SQLITE_DIRECTONLY] flag is recommended for
|
||||||
** all application-defined SQL functions that do not need to be
|
** all application-defined SQL functions that do not need to be
|
||||||
** used inside of triggers, view, CHECK constraints, or other elements of
|
** used inside of triggers, views, CHECK constraints, or other elements of
|
||||||
** the database schema. This flags is especially recommended for SQL
|
** the database schema. This flag is especially recommended for SQL
|
||||||
** functions that have side effects or reveal internal application state.
|
** functions that have side effects or reveal internal application state.
|
||||||
** Without this flag, an attacker might be able to modify the schema of
|
** Without this flag, an attacker might be able to modify the schema of
|
||||||
** a database file to include invocations of the function with parameters
|
** a database file to include invocations of the function with parameters
|
||||||
@ -5571,7 +5638,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
|
|||||||
** [user-defined window functions|available here].
|
** [user-defined window functions|available here].
|
||||||
**
|
**
|
||||||
** ^(If the final parameter to sqlite3_create_function_v2() or
|
** ^(If the final parameter to sqlite3_create_function_v2() or
|
||||||
** sqlite3_create_window_function() is not NULL, then it is destructor for
|
** sqlite3_create_window_function() is not NULL, then it is the destructor for
|
||||||
** the application data pointer. The destructor is invoked when the function
|
** the application data pointer. The destructor is invoked when the function
|
||||||
** is deleted, either by being overloaded or when the database connection
|
** is deleted, either by being overloaded or when the database connection
|
||||||
** closes.)^ ^The destructor is also invoked if the call to
|
** closes.)^ ^The destructor is also invoked if the call to
|
||||||
@ -5971,7 +6038,7 @@ SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
|
|||||||
** METHOD: sqlite3_value
|
** METHOD: sqlite3_value
|
||||||
**
|
**
|
||||||
** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value]
|
** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value]
|
||||||
** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
|
** object V and returns a pointer to that copy. ^The [sqlite3_value] returned
|
||||||
** is a [protected sqlite3_value] object even if the input is not.
|
** is a [protected sqlite3_value] object even if the input is not.
|
||||||
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
|
** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
|
||||||
** memory allocation fails. ^If V is a [pointer value], then the result
|
** memory allocation fails. ^If V is a [pointer value], then the result
|
||||||
@ -6009,7 +6076,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value*);
|
|||||||
** allocation error occurs.
|
** allocation error occurs.
|
||||||
**
|
**
|
||||||
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
|
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
|
||||||
** determined by the N parameter on first successful call. Changing the
|
** determined by the N parameter on the first successful call. Changing the
|
||||||
** value of N in any subsequent call to sqlite3_aggregate_context() within
|
** value of N in any subsequent call to sqlite3_aggregate_context() within
|
||||||
** the same aggregate function instance will not resize the memory
|
** the same aggregate function instance will not resize the memory
|
||||||
** allocation.)^ Within the xFinal callback, it is customary to set
|
** allocation.)^ Within the xFinal callback, it is customary to set
|
||||||
@ -6171,7 +6238,7 @@ SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(voi
|
|||||||
**
|
**
|
||||||
** Security Warning: These interfaces should not be exposed in scripting
|
** Security Warning: These interfaces should not be exposed in scripting
|
||||||
** languages or in other circumstances where it might be possible for an
|
** languages or in other circumstances where it might be possible for an
|
||||||
** an attacker to invoke them. Any agent that can invoke these interfaces
|
** attacker to invoke them. Any agent that can invoke these interfaces
|
||||||
** can probably also take control of the process.
|
** can probably also take control of the process.
|
||||||
**
|
**
|
||||||
** Database connection client data is only available for SQLite
|
** Database connection client data is only available for SQLite
|
||||||
@ -6285,7 +6352,7 @@ typedef void (*sqlite3_destructor_type)(void*);
|
|||||||
** pointed to by the 2nd parameter are taken as the application-defined
|
** pointed to by the 2nd parameter are taken as the application-defined
|
||||||
** function result. If the 3rd parameter is non-negative, then it
|
** function result. If the 3rd parameter is non-negative, then it
|
||||||
** must be the byte offset into the string where the NUL terminator would
|
** must be the byte offset into the string where the NUL terminator would
|
||||||
** appear if the string where NUL terminated. If any NUL characters occur
|
** appear if the string were NUL terminated. If any NUL characters occur
|
||||||
** in the string at a byte offset that is less than the value of the 3rd
|
** in the string at a byte offset that is less than the value of the 3rd
|
||||||
** parameter, then the resulting string will contain embedded NULs and the
|
** parameter, then the resulting string will contain embedded NULs and the
|
||||||
** result of expressions operating on strings with embedded NULs is undefined.
|
** result of expressions operating on strings with embedded NULs is undefined.
|
||||||
@ -6343,7 +6410,7 @@ typedef void (*sqlite3_destructor_type)(void*);
|
|||||||
** string and preferably a string literal. The sqlite3_result_pointer()
|
** string and preferably a string literal. The sqlite3_result_pointer()
|
||||||
** routine is part of the [pointer passing interface] added for SQLite 3.20.0.
|
** routine is part of the [pointer passing interface] added for SQLite 3.20.0.
|
||||||
**
|
**
|
||||||
** If these routines are called from within the different thread
|
** If these routines are called from within a different thread
|
||||||
** than the one containing the application-defined function that received
|
** than the one containing the application-defined function that received
|
||||||
** the [sqlite3_context] pointer, the results are undefined.
|
** the [sqlite3_context] pointer, the results are undefined.
|
||||||
*/
|
*/
|
||||||
@ -6749,7 +6816,7 @@ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
|
|||||||
** METHOD: sqlite3
|
** METHOD: sqlite3
|
||||||
**
|
**
|
||||||
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
|
** ^The sqlite3_db_name(D,N) interface returns a pointer to the schema name
|
||||||
** for the N-th database on database connection D, or a NULL pointer of N is
|
** for the N-th database on database connection D, or a NULL pointer if N is
|
||||||
** out of range. An N value of 0 means the main database file. An N of 1 is
|
** out of range. An N value of 0 means the main database file. An N of 1 is
|
||||||
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
|
** the "temp" schema. Larger values of N correspond to various ATTACH-ed
|
||||||
** databases.
|
** databases.
|
||||||
@ -6844,7 +6911,7 @@ SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
|
|||||||
** <dd>The SQLITE_TXN_READ state means that the database is currently
|
** <dd>The SQLITE_TXN_READ state means that the database is currently
|
||||||
** in a read transaction. Content has been read from the database file
|
** in a read transaction. Content has been read from the database file
|
||||||
** but nothing in the database file has changed. The transaction state
|
** but nothing in the database file has changed. The transaction state
|
||||||
** will advanced to SQLITE_TXN_WRITE if any changes occur and there are
|
** will be advanced to SQLITE_TXN_WRITE if any changes occur and there are
|
||||||
** no other conflicting concurrent write transactions. The transaction
|
** no other conflicting concurrent write transactions. The transaction
|
||||||
** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or
|
** state will revert to SQLITE_TXN_NONE following a [ROLLBACK] or
|
||||||
** [COMMIT].</dd>
|
** [COMMIT].</dd>
|
||||||
@ -6853,7 +6920,7 @@ SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
|
|||||||
** <dd>The SQLITE_TXN_WRITE state means that the database is currently
|
** <dd>The SQLITE_TXN_WRITE state means that the database is currently
|
||||||
** in a write transaction. Content has been written to the database file
|
** in a write transaction. Content has been written to the database file
|
||||||
** but has not yet committed. The transaction state will change to
|
** but has not yet committed. The transaction state will change to
|
||||||
** to SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].</dd>
|
** SQLITE_TXN_NONE at the next [ROLLBACK] or [COMMIT].</dd>
|
||||||
*/
|
*/
|
||||||
#define SQLITE_TXN_NONE 0
|
#define SQLITE_TXN_NONE 0
|
||||||
#define SQLITE_TXN_READ 1
|
#define SQLITE_TXN_READ 1
|
||||||
@ -7004,6 +7071,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
|
|||||||
**
|
**
|
||||||
** ^The second argument is a pointer to the function to invoke when a
|
** ^The second argument is a pointer to the function to invoke when a
|
||||||
** row is updated, inserted or deleted in a rowid table.
|
** row is updated, inserted or deleted in a rowid table.
|
||||||
|
** ^The update hook is disabled by invoking sqlite3_update_hook()
|
||||||
|
** with a NULL pointer as the second parameter.
|
||||||
** ^The first argument to the callback is a copy of the third argument
|
** ^The first argument to the callback is a copy of the third argument
|
||||||
** to sqlite3_update_hook().
|
** to sqlite3_update_hook().
|
||||||
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
||||||
@ -7132,7 +7201,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
|
|||||||
** CAPI3REF: Impose A Limit On Heap Size
|
** CAPI3REF: Impose A Limit On Heap Size
|
||||||
**
|
**
|
||||||
** These interfaces impose limits on the amount of heap memory that will be
|
** These interfaces impose limits on the amount of heap memory that will be
|
||||||
** by all database connections within a single process.
|
** used by all database connections within a single process.
|
||||||
**
|
**
|
||||||
** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the
|
** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the
|
||||||
** soft limit on the amount of heap memory that may be allocated by SQLite.
|
** soft limit on the amount of heap memory that may be allocated by SQLite.
|
||||||
@ -7190,7 +7259,7 @@ SQLITE_API int sqlite3_db_release_memory(sqlite3*);
|
|||||||
** </ul>)^
|
** </ul>)^
|
||||||
**
|
**
|
||||||
** The circumstances under which SQLite will enforce the heap limits may
|
** The circumstances under which SQLite will enforce the heap limits may
|
||||||
** changes in future releases of SQLite.
|
** change in future releases of SQLite.
|
||||||
*/
|
*/
|
||||||
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);
|
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 N);
|
||||||
SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N);
|
SQLITE_API sqlite3_int64 sqlite3_hard_heap_limit64(sqlite3_int64 N);
|
||||||
@ -7305,8 +7374,8 @@ SQLITE_API int sqlite3_table_column_metadata(
|
|||||||
** ^The entry point is zProc.
|
** ^The entry point is zProc.
|
||||||
** ^(zProc may be 0, in which case SQLite will try to come up with an
|
** ^(zProc may be 0, in which case SQLite will try to come up with an
|
||||||
** entry point name on its own. It first tries "sqlite3_extension_init".
|
** entry point name on its own. It first tries "sqlite3_extension_init".
|
||||||
** If that does not work, it constructs a name "sqlite3_X_init" where the
|
** If that does not work, it constructs a name "sqlite3_X_init" where
|
||||||
** X is consists of the lower-case equivalent of all ASCII alphabetic
|
** X consists of the lower-case equivalent of all ASCII alphabetic
|
||||||
** characters in the filename from the last "/" to the first following
|
** characters in the filename from the last "/" to the first following
|
||||||
** "." and omitting any initial "lib".)^
|
** "." and omitting any initial "lib".)^
|
||||||
** ^The sqlite3_load_extension() interface returns
|
** ^The sqlite3_load_extension() interface returns
|
||||||
@ -7377,7 +7446,7 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
|
|||||||
** ^(Even though the function prototype shows that xEntryPoint() takes
|
** ^(Even though the function prototype shows that xEntryPoint() takes
|
||||||
** no arguments and returns void, SQLite invokes xEntryPoint() with three
|
** no arguments and returns void, SQLite invokes xEntryPoint() with three
|
||||||
** arguments and expects an integer result as if the signature of the
|
** arguments and expects an integer result as if the signature of the
|
||||||
** entry point where as follows:
|
** entry point were as follows:
|
||||||
**
|
**
|
||||||
** <blockquote><pre>
|
** <blockquote><pre>
|
||||||
** int xEntryPoint(
|
** int xEntryPoint(
|
||||||
@ -7541,7 +7610,7 @@ struct sqlite3_module {
|
|||||||
** virtual table and might not be checked again by the byte code.)^ ^(The
|
** virtual table and might not be checked again by the byte code.)^ ^(The
|
||||||
** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
|
** aConstraintUsage[].omit flag is an optimization hint. When the omit flag
|
||||||
** is left in its default setting of false, the constraint will always be
|
** is left in its default setting of false, the constraint will always be
|
||||||
** checked separately in byte code. If the omit flag is change to true, then
|
** checked separately in byte code. If the omit flag is changed to true, then
|
||||||
** the constraint may or may not be checked in byte code. In other words,
|
** the constraint may or may not be checked in byte code. In other words,
|
||||||
** when the omit flag is true there is no guarantee that the constraint will
|
** when the omit flag is true there is no guarantee that the constraint will
|
||||||
** not be checked again using byte code.)^
|
** not be checked again using byte code.)^
|
||||||
@ -7567,7 +7636,7 @@ struct sqlite3_module {
|
|||||||
** The xBestIndex method may optionally populate the idxFlags field with a
|
** The xBestIndex method may optionally populate the idxFlags field with a
|
||||||
** mask of SQLITE_INDEX_SCAN_* flags. One such flag is
|
** mask of SQLITE_INDEX_SCAN_* flags. One such flag is
|
||||||
** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN]
|
** [SQLITE_INDEX_SCAN_HEX], which if set causes the [EXPLAIN QUERY PLAN]
|
||||||
** output to show the idxNum has hex instead of as decimal. Another flag is
|
** output to show the idxNum as hex instead of as decimal. Another flag is
|
||||||
** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will
|
** SQLITE_INDEX_SCAN_UNIQUE, which if set indicates that the query plan will
|
||||||
** return at most one row.
|
** return at most one row.
|
||||||
**
|
**
|
||||||
@ -7708,7 +7777,7 @@ struct sqlite3_index_info {
|
|||||||
** the implementation of the [virtual table module]. ^The fourth
|
** the implementation of the [virtual table module]. ^The fourth
|
||||||
** parameter is an arbitrary client data pointer that is passed through
|
** parameter is an arbitrary client data pointer that is passed through
|
||||||
** into the [xCreate] and [xConnect] methods of the virtual table module
|
** into the [xCreate] and [xConnect] methods of the virtual table module
|
||||||
** when a new virtual table is be being created or reinitialized.
|
** when a new virtual table is being created or reinitialized.
|
||||||
**
|
**
|
||||||
** ^The sqlite3_create_module_v2() interface has a fifth parameter which
|
** ^The sqlite3_create_module_v2() interface has a fifth parameter which
|
||||||
** is a pointer to a destructor for the pClientData. ^SQLite will
|
** is a pointer to a destructor for the pClientData. ^SQLite will
|
||||||
@ -7873,7 +7942,7 @@ typedef struct sqlite3_blob sqlite3_blob;
|
|||||||
** in *ppBlob. Otherwise an [error code] is returned and, unless the error
|
** in *ppBlob. Otherwise an [error code] is returned and, unless the error
|
||||||
** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
|
** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
|
||||||
** the API is not misused, it is always safe to call [sqlite3_blob_close()]
|
** the API is not misused, it is always safe to call [sqlite3_blob_close()]
|
||||||
** on *ppBlob after this function it returns.
|
** on *ppBlob after this function returns.
|
||||||
**
|
**
|
||||||
** This function fails with SQLITE_ERROR if any of the following are true:
|
** This function fails with SQLITE_ERROR if any of the following are true:
|
||||||
** <ul>
|
** <ul>
|
||||||
@ -7993,7 +8062,7 @@ SQLITE_API int sqlite3_blob_close(sqlite3_blob *);
|
|||||||
**
|
**
|
||||||
** ^Returns the size in bytes of the BLOB accessible via the
|
** ^Returns the size in bytes of the BLOB accessible via the
|
||||||
** successfully opened [BLOB handle] in its only argument. ^The
|
** successfully opened [BLOB handle] in its only argument. ^The
|
||||||
** incremental blob I/O routines can only read or overwriting existing
|
** incremental blob I/O routines can only read or overwrite existing
|
||||||
** blob content; they cannot change the size of a blob.
|
** blob content; they cannot change the size of a blob.
|
||||||
**
|
**
|
||||||
** This routine only works on a [BLOB handle] which has been created
|
** This routine only works on a [BLOB handle] which has been created
|
||||||
@ -8143,7 +8212,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
|
|||||||
** ^The sqlite3_mutex_alloc() routine allocates a new
|
** ^The sqlite3_mutex_alloc() routine allocates a new
|
||||||
** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc()
|
** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc()
|
||||||
** routine returns NULL if it is unable to allocate the requested
|
** routine returns NULL if it is unable to allocate the requested
|
||||||
** mutex. The argument to sqlite3_mutex_alloc() must one of these
|
** mutex. The argument to sqlite3_mutex_alloc() must be one of these
|
||||||
** integer constants:
|
** integer constants:
|
||||||
**
|
**
|
||||||
** <ul>
|
** <ul>
|
||||||
@ -8376,7 +8445,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
|
|||||||
** CAPI3REF: Retrieve the mutex for a database connection
|
** CAPI3REF: Retrieve the mutex for a database connection
|
||||||
** METHOD: sqlite3
|
** METHOD: sqlite3
|
||||||
**
|
**
|
||||||
** ^This interface returns a pointer the [sqlite3_mutex] object that
|
** ^This interface returns a pointer to the [sqlite3_mutex] object that
|
||||||
** serializes access to the [database connection] given in the argument
|
** serializes access to the [database connection] given in the argument
|
||||||
** when the [threading mode] is Serialized.
|
** when the [threading mode] is Serialized.
|
||||||
** ^If the [threading mode] is Single-thread or Multi-thread then this
|
** ^If the [threading mode] is Single-thread or Multi-thread then this
|
||||||
@ -8499,7 +8568,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
|
|||||||
** CAPI3REF: SQL Keyword Checking
|
** CAPI3REF: SQL Keyword Checking
|
||||||
**
|
**
|
||||||
** These routines provide access to the set of SQL language keywords
|
** These routines provide access to the set of SQL language keywords
|
||||||
** recognized by SQLite. Applications can uses these routines to determine
|
** recognized by SQLite. Applications can use these routines to determine
|
||||||
** whether or not a specific identifier needs to be escaped (for example,
|
** whether or not a specific identifier needs to be escaped (for example,
|
||||||
** by enclosing in double-quotes) so as not to confuse the parser.
|
** by enclosing in double-quotes) so as not to confuse the parser.
|
||||||
**
|
**
|
||||||
@ -8667,7 +8736,7 @@ SQLITE_API void sqlite3_str_reset(sqlite3_str*);
|
|||||||
** content of the dynamic string under construction in X. The value
|
** content of the dynamic string under construction in X. The value
|
||||||
** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X
|
** returned by [sqlite3_str_value(X)] is managed by the sqlite3_str object X
|
||||||
** and might be freed or altered by any subsequent method on the same
|
** and might be freed or altered by any subsequent method on the same
|
||||||
** [sqlite3_str] object. Applications must not used the pointer returned
|
** [sqlite3_str] object. Applications must not use the pointer returned by
|
||||||
** [sqlite3_str_value(X)] after any subsequent method call on the same
|
** [sqlite3_str_value(X)] after any subsequent method call on the same
|
||||||
** object. ^Applications may change the content of the string returned
|
** object. ^Applications may change the content of the string returned
|
||||||
** by [sqlite3_str_value(X)] as long as they do not write into any bytes
|
** by [sqlite3_str_value(X)] as long as they do not write into any bytes
|
||||||
@ -8753,7 +8822,7 @@ SQLITE_API int sqlite3_status64(
|
|||||||
** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]
|
** allocation which could not be satisfied by the [SQLITE_CONFIG_PAGECACHE]
|
||||||
** buffer and where forced to overflow to [sqlite3_malloc()]. The
|
** buffer and where forced to overflow to [sqlite3_malloc()]. The
|
||||||
** returned value includes allocations that overflowed because they
|
** returned value includes allocations that overflowed because they
|
||||||
** where too large (they were larger than the "sz" parameter to
|
** were too large (they were larger than the "sz" parameter to
|
||||||
** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because
|
** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because
|
||||||
** no space was left in the page cache.</dd>)^
|
** no space was left in the page cache.</dd>)^
|
||||||
**
|
**
|
||||||
@ -8837,28 +8906,29 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
|
|||||||
** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
|
** [[SQLITE_DBSTATUS_LOOKASIDE_HIT]] ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_HIT</dt>
|
||||||
** <dd>This parameter returns the number of malloc attempts that were
|
** <dd>This parameter returns the number of malloc attempts that were
|
||||||
** satisfied using lookaside memory. Only the high-water value is meaningful;
|
** satisfied using lookaside memory. Only the high-water value is meaningful;
|
||||||
** the current value is always zero.)^
|
** the current value is always zero.</dd>)^
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]]
|
** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE]]
|
||||||
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
|
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE</dt>
|
||||||
** <dd>This parameter returns the number malloc attempts that might have
|
** <dd>This parameter returns the number of malloc attempts that might have
|
||||||
** been satisfied using lookaside memory but failed due to the amount of
|
** been satisfied using lookaside memory but failed due to the amount of
|
||||||
** memory requested being larger than the lookaside slot size.
|
** memory requested being larger than the lookaside slot size.
|
||||||
** Only the high-water value is meaningful;
|
** Only the high-water value is meaningful;
|
||||||
** the current value is always zero.)^
|
** the current value is always zero.</dd>)^
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]]
|
** [[SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL]]
|
||||||
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
|
** ^(<dt>SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL</dt>
|
||||||
** <dd>This parameter returns the number malloc attempts that might have
|
** <dd>This parameter returns the number of malloc attempts that might have
|
||||||
** been satisfied using lookaside memory but failed due to all lookaside
|
** been satisfied using lookaside memory but failed due to all lookaside
|
||||||
** memory already being in use.
|
** memory already being in use.
|
||||||
** Only the high-water value is meaningful;
|
** Only the high-water value is meaningful;
|
||||||
** the current value is always zero.)^
|
** the current value is always zero.</dd>)^
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
|
** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
|
||||||
** <dd>This parameter returns the approximate number of bytes of heap
|
** <dd>This parameter returns the approximate number of bytes of heap
|
||||||
** memory used by all pager caches associated with the database connection.)^
|
** memory used by all pager caches associated with the database connection.)^
|
||||||
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
|
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
|
** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
|
||||||
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt>
|
** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt>
|
||||||
@ -8867,10 +8937,10 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
|
|||||||
** memory used by that pager cache is divided evenly between the attached
|
** memory used by that pager cache is divided evenly between the attached
|
||||||
** connections.)^ In other words, if none of the pager caches associated
|
** connections.)^ In other words, if none of the pager caches associated
|
||||||
** with the database connection are shared, this request returns the same
|
** with the database connection are shared, this request returns the same
|
||||||
** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are
|
** value as DBSTATUS_CACHE_USED. Or, if one or more of the pager caches are
|
||||||
** shared, the value returned by this call will be smaller than that returned
|
** shared, the value returned by this call will be smaller than that returned
|
||||||
** by DBSTATUS_CACHE_USED. ^The highwater mark associated with
|
** by DBSTATUS_CACHE_USED. ^The highwater mark associated with
|
||||||
** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0.
|
** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0.</dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
|
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
|
||||||
** <dd>This parameter returns the approximate number of bytes of heap
|
** <dd>This parameter returns the approximate number of bytes of heap
|
||||||
@ -8880,6 +8950,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
|
|||||||
** schema memory is shared with other database connections due to
|
** schema memory is shared with other database connections due to
|
||||||
** [shared cache mode] being enabled.
|
** [shared cache mode] being enabled.
|
||||||
** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
|
** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
|
** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
|
||||||
** <dd>This parameter returns the approximate number of bytes of heap
|
** <dd>This parameter returns the approximate number of bytes of heap
|
||||||
@ -8916,7 +8987,7 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
|
|||||||
** been written to disk in the middle of a transaction due to the page
|
** been written to disk in the middle of a transaction due to the page
|
||||||
** cache overflowing. Transactions are more efficient if they are written
|
** cache overflowing. Transactions are more efficient if they are written
|
||||||
** to disk all at once. When pages spill mid-transaction, that introduces
|
** to disk all at once. When pages spill mid-transaction, that introduces
|
||||||
** additional overhead. This parameter can be used help identify
|
** additional overhead. This parameter can be used to help identify
|
||||||
** inefficiencies that can be resolved by increasing the cache size.
|
** inefficiencies that can be resolved by increasing the cache size.
|
||||||
** </dd>
|
** </dd>
|
||||||
**
|
**
|
||||||
@ -9396,7 +9467,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
|||||||
** external process or via a database connection other than the one being
|
** external process or via a database connection other than the one being
|
||||||
** used by the backup operation, then the backup will be automatically
|
** used by the backup operation, then the backup will be automatically
|
||||||
** restarted by the next call to sqlite3_backup_step(). ^If the source
|
** restarted by the next call to sqlite3_backup_step(). ^If the source
|
||||||
** database is modified by the using the same database connection as is used
|
** database is modified by using the same database connection as is used
|
||||||
** by the backup operation, then the backup database is automatically
|
** by the backup operation, then the backup database is automatically
|
||||||
** updated at the same time.
|
** updated at the same time.
|
||||||
**
|
**
|
||||||
@ -9413,7 +9484,7 @@ typedef struct sqlite3_backup sqlite3_backup;
|
|||||||
** and may not be used following a call to sqlite3_backup_finish().
|
** and may not be used following a call to sqlite3_backup_finish().
|
||||||
**
|
**
|
||||||
** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no
|
** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no
|
||||||
** sqlite3_backup_step() errors occurred, regardless or whether or not
|
** sqlite3_backup_step() errors occurred, regardless of whether or not
|
||||||
** sqlite3_backup_step() completed.
|
** sqlite3_backup_step() completed.
|
||||||
** ^If an out-of-memory condition or IO error occurred during any prior
|
** ^If an out-of-memory condition or IO error occurred during any prior
|
||||||
** sqlite3_backup_step() call on the same [sqlite3_backup] object, then
|
** sqlite3_backup_step() call on the same [sqlite3_backup] object, then
|
||||||
@ -10483,7 +10554,7 @@ SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
|
|||||||
** METHOD: sqlite3
|
** METHOD: sqlite3
|
||||||
**
|
**
|
||||||
** ^If a write-transaction is open on [database connection] D when the
|
** ^If a write-transaction is open on [database connection] D when the
|
||||||
** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
|
** [sqlite3_db_cacheflush(D)] interface is invoked, any dirty
|
||||||
** pages in the pager-cache that are not currently in use are written out
|
** pages in the pager-cache that are not currently in use are written out
|
||||||
** to disk. A dirty page may be in use if a database cursor created by an
|
** to disk. A dirty page may be in use if a database cursor created by an
|
||||||
** active SQL statement is reading from it, or if it is page 1 of a database
|
** active SQL statement is reading from it, or if it is page 1 of a database
|
||||||
@ -11486,9 +11557,10 @@ SQLITE_API void sqlite3session_table_filter(
|
|||||||
** is inserted while a session object is enabled, then later deleted while
|
** is inserted while a session object is enabled, then later deleted while
|
||||||
** the same session object is disabled, no INSERT record will appear in the
|
** the same session object is disabled, no INSERT record will appear in the
|
||||||
** changeset, even though the delete took place while the session was disabled.
|
** changeset, even though the delete took place while the session was disabled.
|
||||||
** Or, if one field of a row is updated while a session is disabled, and
|
** Or, if one field of a row is updated while a session is enabled, and
|
||||||
** another field of the same row is updated while the session is enabled, the
|
** then another field of the same row is updated while the session is disabled,
|
||||||
** resulting changeset will contain an UPDATE change that updates both fields.
|
** the resulting changeset will contain an UPDATE change that updates both
|
||||||
|
** fields.
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3session_changeset(
|
SQLITE_API int sqlite3session_changeset(
|
||||||
sqlite3_session *pSession, /* Session object */
|
sqlite3_session *pSession, /* Session object */
|
||||||
@ -11560,8 +11632,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
|
|||||||
** database zFrom the contents of the two compatible tables would be
|
** database zFrom the contents of the two compatible tables would be
|
||||||
** identical.
|
** identical.
|
||||||
**
|
**
|
||||||
** It an error if database zFrom does not exist or does not contain the
|
** Unless the call to this function is a no-op as described above, it is an
|
||||||
** required compatible table.
|
** error if database zFrom does not exist or does not contain the required
|
||||||
|
** compatible table.
|
||||||
**
|
**
|
||||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||||
@ -11696,7 +11769,7 @@ SQLITE_API int sqlite3changeset_start_v2(
|
|||||||
** The following flags may passed via the 4th parameter to
|
** The following flags may passed via the 4th parameter to
|
||||||
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
||||||
**
|
**
|
||||||
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
|
||||||
** Invert the changeset while iterating through it. This is equivalent to
|
** Invert the changeset while iterating through it. This is equivalent to
|
||||||
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
||||||
** It is an error to specify this flag with a patchset.
|
** It is an error to specify this flag with a patchset.
|
||||||
@ -12011,19 +12084,6 @@ SQLITE_API int sqlite3changeset_concat(
|
|||||||
void **ppOut /* OUT: Buffer containing output changeset */
|
void **ppOut /* OUT: Buffer containing output changeset */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
|
|
||||||
*/
|
|
||||||
SQLITE_API int sqlite3changeset_upgrade(
|
|
||||||
sqlite3 *db,
|
|
||||||
const char *zDb,
|
|
||||||
int nIn, const void *pIn, /* Input changeset */
|
|
||||||
int *pnOut, void **ppOut /* OUT: Inverse of input */
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Changegroup Handle
|
** CAPI3REF: Changegroup Handle
|
||||||
**
|
**
|
||||||
|
4
deps/sqlite/sqlite3ext.h
vendored
@ -366,6 +366,8 @@ struct sqlite3_api_routines {
|
|||||||
/* Version 3.44.0 and later */
|
/* Version 3.44.0 and later */
|
||||||
void *(*get_clientdata)(sqlite3*,const char*);
|
void *(*get_clientdata)(sqlite3*,const char*);
|
||||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||||
|
/* Version 3.50.0 and later */
|
||||||
|
int (*setlk_timeout)(sqlite3*,int,int);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -699,6 +701,8 @@ typedef int (*sqlite3_loadext_entry)(
|
|||||||
/* Version 3.44.0 and later */
|
/* Version 3.44.0 and later */
|
||||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||||
|
/* Version 3.50.0 and later */
|
||||||
|
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||||
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
40
docs/connecting_manyverse.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Connecting with Manyverse
|
||||||
|
|
||||||
|
Communication with [Manyverse](https://www.manyver.se/) should Just Work (tm).
|
||||||
|
|
||||||
|
This document is intended as a cheat sheet for the instances where it doesn't.
|
||||||
|
If your experience differs, please share so we can make things better.
|
||||||
|
|
||||||
|
## Connecting Manyverse to the tildefriends.net room
|
||||||
|
|
||||||
|
Open the `Connections` tab. This is from the desktop app, but mobile is similar.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Open the `Connections Panel`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Use the `Add Connection` button at the bottom right to open the dialog to enter
|
||||||
|
a connections string to add a new connection.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Copy the tildefriends.net room code from https://www.tildefriends.net/~cory/room/.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Paste.
|
||||||
|
|
||||||
|
On mobile especially, make sure the full text is pasted without modification.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Click `Done`, and you should be connected successfully. tildefriends.net is
|
||||||
|
all things: a room, a pub, and a client, so you should be able to start replicating
|
||||||
|
immediately as well as find other similarly connected people with whom to establish
|
||||||
|
further connections.
|
||||||
|
|
||||||
|
When logged into tildefriends.net, active connections it sees can be found on
|
||||||
|
the `Connections` tab: https://www.tildefriends.net/~core/ssb/#connections,
|
||||||
|
which may indicate errors if you find yourself disconnecting.
|
BIN
docs/images/manyverse_code.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
docs/images/manyverse_connections_panel.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
docs/images/manyverse_connections_tab.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
docs/images/manyverse_paste_invite_code.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
docs/images/tildefriends_room_app.png
Normal file
After Width: | Height: | Size: 136 KiB |
@ -14,7 +14,7 @@
|
|||||||
- upload to Apple with dist-ios on macos
|
- upload to Apple with dist-ios on macos
|
||||||
- nix
|
- nix
|
||||||
- june and december: update release version
|
- june and december: update release version
|
||||||
- run `nix flake update`
|
- run `nix --extra-experimental-features nix-command --extra-experimental-features flakes flake update`
|
||||||
- comment out the hash in default.nix
|
- comment out the hash in default.nix
|
||||||
- update the version
|
- update the version
|
||||||
- run `nix-build`
|
- run `nix-build`
|
||||||
|
289
docs/usage.md
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
# CLI Usage
|
||||||
|
|
||||||
|
## tildefriends -h
|
||||||
|
|
||||||
|
```
|
||||||
|
Usage: out/debug/tildefriends command [command-options]
|
||||||
|
commands:
|
||||||
|
run - Run tildefriends (default).
|
||||||
|
sandbox - Run a sandboxed tildefriends sandbox process (used internally).
|
||||||
|
import - Import apps from file to the database.
|
||||||
|
export - Export apps from the database to file.
|
||||||
|
publish - Append a message to a feed.
|
||||||
|
private - Append a private post message to a feed.
|
||||||
|
create_invite - Create an invite.
|
||||||
|
get_sequence - Get the last sequence number for a feed.
|
||||||
|
get_identity - Get the server account identity.
|
||||||
|
get_profile - Get profile information for the given identity.
|
||||||
|
get_contacts - Get information about followed, blocked, and friend identities.
|
||||||
|
has_blob - Check whether a blob is in the blob store.
|
||||||
|
get_blob - Read a file from the blob store.
|
||||||
|
store_blob - Write a file to the blob store.
|
||||||
|
verify - Verify a feed.
|
||||||
|
test - Test SSB.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends run -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends run [options]
|
||||||
|
|
||||||
|
Run tildefriends (default).
|
||||||
|
|
||||||
|
options:
|
||||||
|
-s, --script script Script to run (default: core/core.js).
|
||||||
|
-d, --db-path path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-k, --ssb-network-key key SSB network key to use.
|
||||||
|
-n, --count count Number of instances to run.
|
||||||
|
-a, --args args Arguments of the format key=value,foo=bar,verbose=true (note: these are persisted to the database).
|
||||||
|
code_of_conduct (default: ""): Code of conduct presented at sign-in.
|
||||||
|
ssb_port (default: 8008): Port on which to listen for SSB secure handshake connections.
|
||||||
|
http_local_only (default: false): Whether to bind http(s) to the loopback address. Otherwise any.
|
||||||
|
http_port (default: 12345): Port on which to listen for HTTP connections.
|
||||||
|
https_port (default: 0): Port on which to listen for secure HTTP connections.
|
||||||
|
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
||||||
|
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
||||||
|
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
||||||
|
fetch_hosts (default: ""): Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.
|
||||||
|
http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com")
|
||||||
|
index (default: "/~core/intro/"): Default path.
|
||||||
|
index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/"
|
||||||
|
peer_exchange (default: false): Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.
|
||||||
|
replicator (default: true): Enable message and blob replication.
|
||||||
|
room (default: true): Enable peers to tunnel through this instance as a room.
|
||||||
|
room_name (default: "tilde friends tunnel"): Name of the room.
|
||||||
|
seeds_host (default: "seeds.tildefriends.net"): Hostname for seed connections.
|
||||||
|
account_registration (default: true): Allow registration of new accounts.
|
||||||
|
replication_hops (default: 2): Number of hops to replicate (1 = direct follows, 2 = follows of follows, etc.).
|
||||||
|
delete_stale_feeds (default: false): Periodically delete feeds that aren't visible from local accounts or related follows.
|
||||||
|
talk_to_strangers (default: true): Whether connections are accepted from accounts that aren't in the replication range or otherwise already known.
|
||||||
|
autologin (default: false): Whether mobile autologin is supported.
|
||||||
|
broadcast (default: true): Send network discovery broadcasts.
|
||||||
|
discovery (default: true): Receive network discovery broadcasts.
|
||||||
|
stay_connected (default: false): Whether to attempt to keep several peer connections open.
|
||||||
|
-o, --one-proc Run everything in one process (unsafely!).
|
||||||
|
-z, --zip path Zip archive from which to load files.
|
||||||
|
-v, --verbose Log raw messages.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends sandbox -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends sandbox [options]
|
||||||
|
|
||||||
|
Run a sandboxed tildefriends sandbox process (used internally).
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
-f, --fd File descriptor with which to communicate with parent process.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends import -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends import [options] [paths...]
|
||||||
|
|
||||||
|
Import apps from file to the database.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-u, --user user User into whose account apps will be imported (default: "import").
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends export -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends export [options] [paths...]
|
||||||
|
|
||||||
|
Export apps from the database to file.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-u, --user user User from whose account apps will be exported (default: "core").
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
|
||||||
|
paths Paths of apps to export (example: /~core/ssb /~user/app).
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends publish -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends publish [options]
|
||||||
|
|
||||||
|
Append a message to a feed.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-u, --user user User owning identity with which to publish.
|
||||||
|
-i, --id identity Identity with which to publish message.
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-c, --content json JSON content of message to publish.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends private -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends private [options]
|
||||||
|
|
||||||
|
Append a private post message to a feed.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-u, --user user User owning identity with which to publish (optional).
|
||||||
|
-i, --id identity Identity with which to publish message.
|
||||||
|
-r, --recipients recipients Recipient identities.
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-t, --text text Private post text.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends create_invite -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends create_invite [options]
|
||||||
|
|
||||||
|
Create an invite.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-i, --identity identity Account from which to get latest sequence number.
|
||||||
|
-a, --address address Address to which the recipient will connect.
|
||||||
|
-p, --port port Port to which the recipient will connect.
|
||||||
|
-u, --use_count count Number of times this invite may be used (default: 1).
|
||||||
|
-e, --expires seconds How long this invite is valid in seconds (-1 for indefinitely, default: 1 hour).
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends get_sequence -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends get_sequence [options]
|
||||||
|
|
||||||
|
Get the last sequence number for a feed.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-i, --identity identity Account from which to get latest sequence number.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends get_identity -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends get_identity [options]
|
||||||
|
|
||||||
|
Get the server account identity.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends get_profile -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends get_profile [options]
|
||||||
|
|
||||||
|
Get profile information for the given identity.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-i, --identity identity Account for which to get profile information.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends get_contacts -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends get_contacts [options]
|
||||||
|
|
||||||
|
Get information about followed, blocked, and friend identities.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-i, --identity identity Account from which to get contact information.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends has_blob -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends has_blob [options]
|
||||||
|
|
||||||
|
Check whether a blob is in the blob store.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-b, --blob_id blob_id ID of blob to query.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends get_blob -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends get_blob [options]
|
||||||
|
|
||||||
|
Read a file from the blob store.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-b, --blob blob_id Blob identifier to retrieve.
|
||||||
|
-o, --output file_path Location to write the retrieved blob.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends store_blob -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends store_blob [options]
|
||||||
|
|
||||||
|
Write a file to the blob store.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-f, --file file_path Path to file to add to the blob store.
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends verify -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends verify [options]
|
||||||
|
|
||||||
|
Verify a feed.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-i, --identity identity Identity to verify.
|
||||||
|
-s, --sequence sequence Sequence number to debug.
|
||||||
|
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
||||||
|
|
||||||
|
## tildefriends test -h
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: out/debug/tildefriends test [options]
|
||||||
|
|
||||||
|
Test SSB.
|
||||||
|
|
||||||
|
options:
|
||||||
|
-t, --tests tests Comma-separated list of tests to run. (default: all)
|
||||||
|
-h, --help Show this usage information.
|
||||||
|
```
|
8
flake.lock
generated
@ -20,16 +20,16 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1745279238,
|
"lastModified": 1750622754,
|
||||||
"narHash": "sha256-AQ7M9wTa/Pa/kK5pcGTgX/DGqMHyzsyINfN7ktsI7Fo=",
|
"narHash": "sha256-kMhs+YzV4vPGfuTpD3mwzibWUE6jotw5Al2wczI0Pv8=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9684b53175fc6c09581e94cc85f05ab77464c7e3",
|
"rev": "c7ab75210cb8cb16ddd8f290755d9558edde7ee1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"ref": "nixos-24.11",
|
"ref": "nixos-25.05",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
description = "Tilde Friends is a platform for making, running, and sharing web applications.";
|
description = "Tilde Friends is a platform for making, running, and sharing web applications.";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
13
metadata/en-US/changelogs/37.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
* Faster loads.
|
||||||
|
* Minor UI tweaks.
|
||||||
|
* Added an intro app as part of the initial flow for first-time users.
|
||||||
|
* Fixed more shutdown issues.
|
||||||
|
* Fixed a longstanding potential database issue.
|
||||||
|
* Added a blob export command.
|
||||||
|
* Refresh blob wants for blobs that are requested over the web.
|
||||||
|
* Updates:
|
||||||
|
* CodeMirror
|
||||||
|
* QuickJS 2025-04-26
|
||||||
|
* libuv 1.51.0
|
||||||
|
* sqlite 3.49.2
|
||||||
|
* w3.css 5.02
|
14
metadata/en-US/changelogs/38.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
* Improve load times.
|
||||||
|
* Fix the messages_refs table, and make it usable for hashtags.
|
||||||
|
* Fix a circumstance where we would fail to call promise callbacks.
|
||||||
|
* Fix the private messages tab.
|
||||||
|
* Fix the room app.
|
||||||
|
* Expose followed accounts in a user's profile.
|
||||||
|
* Show connection status in the sidebar.
|
||||||
|
* Simplify placeholder messages.
|
||||||
|
* Only show "Mark as Read" when relevant.
|
||||||
|
* Treat profile images more like post images.
|
||||||
|
* Limit the WAL file size.
|
||||||
|
* Updates:
|
||||||
|
* CodeMirror
|
||||||
|
* sqlite 3.50.1
|
2
metadata/en-US/changelogs/39.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
* Updating Android SDK+target versions.
|
||||||
|
* Minor UI improvements.
|
Before Width: | Height: | Size: 275 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 108 KiB |
6
package-lock.json
generated
@ -11,9 +11,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.5.3",
|
"version": "3.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
||||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
||||||
"bin": {
|
"bin": {
|
||||||
"prettier": "bin/prettier.cjs"
|
"prettier": "bin/prettier.cjs"
|
||||||
},
|
},
|
||||||
|
@ -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="37"
|
android:versionCode="39"
|
||||||
android:versionName="0.0.31-wip">
|
android:versionName="0.0.32.1">
|
||||||
<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
|
||||||
|
@ -813,6 +813,11 @@ void tf_http_destroy(tf_http_t* http)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!http->is_shutting_down)
|
||||||
|
{
|
||||||
|
tf_printf("tf_http_destroy\n");
|
||||||
|
}
|
||||||
|
|
||||||
http->is_shutting_down = true;
|
http->is_shutting_down = true;
|
||||||
http->is_in_destroy = true;
|
http->is_in_destroy = true;
|
||||||
|
|
||||||
|
@ -75,7 +75,6 @@ static int _object_to_headers(JSContext* context, JSValue object, const char** h
|
|||||||
JS_GetOwnPropertyNames(context, &ptab, &plen, object, JS_GPN_STRING_MASK);
|
JS_GetOwnPropertyNames(context, &ptab, &plen, object, JS_GPN_STRING_MASK);
|
||||||
for (; count < (int)plen && count < headers_length / 2; ++count)
|
for (; count < (int)plen && count < headers_length / 2; ++count)
|
||||||
{
|
{
|
||||||
JSValue key = JS_AtomToString(context, ptab[count].atom);
|
|
||||||
JSPropertyDescriptor desc;
|
JSPropertyDescriptor desc;
|
||||||
JSValue key_value = JS_NULL;
|
JSValue key_value = JS_NULL;
|
||||||
if (JS_GetOwnProperty(context, &desc, object, ptab[count].atom) == 1)
|
if (JS_GetOwnProperty(context, &desc, object, ptab[count].atom) == 1)
|
||||||
@ -84,9 +83,8 @@ static int _object_to_headers(JSContext* context, JSValue object, const char** h
|
|||||||
JS_FreeValue(context, desc.setter);
|
JS_FreeValue(context, desc.setter);
|
||||||
JS_FreeValue(context, desc.getter);
|
JS_FreeValue(context, desc.getter);
|
||||||
}
|
}
|
||||||
headers[count * 2 + 0] = JS_ToCString(context, key);
|
headers[count * 2 + 0] = JS_AtomToCString(context, ptab[count].atom);
|
||||||
headers[count * 2 + 1] = JS_ToCString(context, key_value);
|
headers[count * 2 + 1] = JS_ToCString(context, key_value);
|
||||||
JS_FreeValue(context, key);
|
|
||||||
JS_FreeValue(context, key_value);
|
JS_FreeValue(context, key_value);
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
for (uint32_t i = 0; i < plen; ++i)
|
||||||
@ -640,25 +638,6 @@ static void _httpd_endpoint_mem(tf_http_request_t* request)
|
|||||||
tf_free(response);
|
tf_free(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_hitches(tf_http_request_t* request)
|
|
||||||
{
|
|
||||||
if (_httpd_redirect(request))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tf_task_t* task = request->user_data;
|
|
||||||
char* response = tf_task_get_hitches(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 const char* _after(const char* text, const char* prefix)
|
static const char* _after(const char* text, const char* prefix)
|
||||||
{
|
{
|
||||||
if (!text || !prefix)
|
if (!text || !prefix)
|
||||||
@ -950,7 +929,7 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
char* app_path = tf_malloc(path_length);
|
char* app_path = tf_malloc(path_length);
|
||||||
snprintf(app_path, path_length, "path:%s", data->user_app->app);
|
snprintf(app_path, path_length, "path:%s", data->user_app->app);
|
||||||
const char* value = tf_ssb_db_get_property(ssb, data->user_app->user, app_path);
|
const char* value = tf_ssb_db_get_property(ssb, data->user_app->user, app_path);
|
||||||
snprintf(data->app_blob_id, sizeof(data->app_blob_id), "%s", value);
|
tf_string_set(data->app_blob_id, sizeof(data->app_blob_id), value);
|
||||||
tf_free(app_path);
|
tf_free(app_path);
|
||||||
tf_free((void*)value);
|
tf_free((void*)value);
|
||||||
data->file = last_slash + 1;
|
data->file = last_slash + 1;
|
||||||
@ -1116,6 +1095,7 @@ typedef struct _view_t
|
|||||||
void* data;
|
void* data;
|
||||||
size_t size;
|
size_t size;
|
||||||
char etag[256];
|
char etag[256];
|
||||||
|
char notify_want_blob_id[k_blob_id_len];
|
||||||
bool not_modified;
|
bool not_modified;
|
||||||
} view_t;
|
} view_t;
|
||||||
|
|
||||||
@ -1148,7 +1128,7 @@ static void _httpd_endpoint_view_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
char* app_path = tf_malloc(app_path_length);
|
char* app_path = tf_malloc(app_path_length);
|
||||||
snprintf(app_path, app_path_length, "path:%s", user_app->app);
|
snprintf(app_path, app_path_length, "path:%s", user_app->app);
|
||||||
const char* value = tf_ssb_db_get_property(ssb, user_app->user, app_path);
|
const char* value = tf_ssb_db_get_property(ssb, user_app->user, app_path);
|
||||||
snprintf(blob_id, sizeof(blob_id), "%s", value);
|
tf_string_set(blob_id, sizeof(blob_id), value);
|
||||||
tf_free(app_path);
|
tf_free(app_path);
|
||||||
tf_free((void*)value);
|
tf_free((void*)value);
|
||||||
}
|
}
|
||||||
@ -1175,6 +1155,7 @@ static void _httpd_endpoint_view_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||||
tf_ssb_db_add_blob_wants(db, blob_id);
|
tf_ssb_db_add_blob_wants(db, blob_id);
|
||||||
tf_ssb_release_db_writer(ssb, db);
|
tf_ssb_release_db_writer(ssb, db);
|
||||||
|
tf_string_set(view->notify_want_blob_id, sizeof(view->notify_want_blob_id), blob_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1218,6 +1199,12 @@ static void _httpd_endpoint_view_after_work(tf_ssb_t* ssb, int status, void* use
|
|||||||
const char* k_payload = tf_http_status_text(404);
|
const char* k_payload = tf_http_status_text(404);
|
||||||
tf_http_respond(view->request, 404, NULL, 0, k_payload, strlen(k_payload));
|
tf_http_respond(view->request, 404, NULL, 0, k_payload, strlen(k_payload));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (*view->notify_want_blob_id)
|
||||||
|
{
|
||||||
|
tf_ssb_notify_blob_want_added(ssb, view->notify_want_blob_id);
|
||||||
|
}
|
||||||
|
|
||||||
tf_free(view->form_data);
|
tf_free(view->form_data);
|
||||||
tf_http_request_unref(view->request);
|
tf_http_request_unref(view->request);
|
||||||
tf_free(view);
|
tf_free(view);
|
||||||
@ -1319,7 +1306,7 @@ static void _httpd_endpoint_save_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
tf_ssb_db_set_property(ssb, user_app->user, app_path, blob_id))
|
tf_ssb_db_set_property(ssb, user_app->user, app_path, blob_id))
|
||||||
{
|
{
|
||||||
tf_ssb_db_add_value_to_array_property(ssb, user_app->user, "apps", user_app->app);
|
tf_ssb_db_add_value_to_array_property(ssb, user_app->user, "apps", user_app->app);
|
||||||
snprintf(save->blob_id, sizeof(save->blob_id), "%s", blob_id);
|
tf_string_set(save->blob_id, sizeof(save->blob_id), blob_id);
|
||||||
save->response = 200;
|
save->response = 200;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1352,7 +1339,7 @@ static void _httpd_endpoint_save_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
char blob_id[k_blob_id_len] = { 0 };
|
char blob_id[k_blob_id_len] = { 0 };
|
||||||
if (tf_ssb_db_blob_store(ssb, request->body, request->content_length, blob_id, sizeof(blob_id), NULL))
|
if (tf_ssb_db_blob_store(ssb, request->body, request->content_length, blob_id, sizeof(blob_id), NULL))
|
||||||
{
|
{
|
||||||
snprintf(save->blob_id, sizeof(save->blob_id), "%s", blob_id);
|
tf_string_set(save->blob_id, sizeof(save->blob_id), blob_id);
|
||||||
save->response = 200;
|
save->response = 200;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1972,7 +1959,7 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
const char* return_url = _form_data_get(form_data, "return");
|
const char* return_url = _form_data_get(form_data, "return");
|
||||||
if (return_url)
|
if (return_url)
|
||||||
{
|
{
|
||||||
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url);
|
tf_string_set(login->location_header, sizeof(login->location_header), return_url);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2077,7 +2064,7 @@ static void _httpd_endpoint_login_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
const char* return_url = _form_data_get(form_data, "return");
|
const char* return_url = _form_data_get(form_data, "return");
|
||||||
if (return_url)
|
if (return_url)
|
||||||
{
|
{
|
||||||
snprintf(login->location_header, sizeof(login->location_header), "%s", return_url);
|
tf_string_set(login->location_header, sizeof(login->location_header), return_url);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -2429,7 +2416,6 @@ tf_http_t* tf_httpd_create(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, "/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);
|
||||||
tf_http_add_handler(http, "/ebt", _httpd_endpoint_ebt, NULL, task);
|
tf_http_add_handler(http, "/ebt", _httpd_endpoint_ebt, NULL, task);
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
** @{
|
** @{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "quickjs.h"
|
/** A JS context. */
|
||||||
|
typedef struct JSContext JSContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** An HTTP server instance.
|
** An HTTP server instance.
|
||||||
|
@ -13,13 +13,13 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.0.31</string>
|
<string>0.0.32.1</string>
|
||||||
<key>CFBundleSupportedPlatforms</key>
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
<array>
|
<array>
|
||||||
<string>iPhoneOS</string>
|
<string>iPhoneOS</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>13</string>
|
<string>15</string>
|
||||||
<key>DTPlatformName</key>
|
<key>DTPlatformName</key>
|
||||||
<string>iphoneos</string>
|
<string>iphoneos</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
190
src/main.c
@ -148,6 +148,7 @@ static int _tf_command_private(const char* file, int argc, char* argv[]);
|
|||||||
static int _tf_command_run(const char* file, int argc, char* argv[]);
|
static int _tf_command_run(const char* file, int argc, char* argv[]);
|
||||||
static int _tf_command_sandbox(const char* file, int argc, char* argv[]);
|
static int _tf_command_sandbox(const char* file, int argc, char* argv[]);
|
||||||
static int _tf_command_has_blob(const char* file, int argc, char* argv[]);
|
static int _tf_command_has_blob(const char* file, int argc, char* argv[]);
|
||||||
|
static int _tf_command_get_blob(const char* file, int argc, char* argv[]);
|
||||||
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[]);
|
||||||
static int _tf_command_create_invite(const char* file, int argc, char* argv[]);
|
static int _tf_command_create_invite(const char* file, int argc, char* argv[]);
|
||||||
static int _tf_command_get_sequence(const char* file, int argc, char* argv[]);
|
static int _tf_command_get_sequence(const char* file, int argc, char* argv[]);
|
||||||
@ -168,8 +169,8 @@ typedef struct _command_t
|
|||||||
const command_t k_commands[] = {
|
const command_t k_commands[] = {
|
||||||
{ "run", _tf_command_run, "Run tildefriends (default)." },
|
{ "run", _tf_command_run, "Run tildefriends (default)." },
|
||||||
{ "sandbox", _tf_command_sandbox, "Run a sandboxed tildefriends sandbox process (used internally)." },
|
{ "sandbox", _tf_command_sandbox, "Run a sandboxed tildefriends sandbox process (used internally)." },
|
||||||
{ "import", _tf_command_import, "Import apps to SSB." },
|
{ "import", _tf_command_import, "Import apps from file to the database." },
|
||||||
{ "export", _tf_command_export, "Export apps from SSB." },
|
{ "export", _tf_command_export, "Export apps from the database to file." },
|
||||||
{ "publish", _tf_command_publish, "Append a message to a feed." },
|
{ "publish", _tf_command_publish, "Append a message to a feed." },
|
||||||
{ "private", _tf_command_private, "Append a private post message to a feed." },
|
{ "private", _tf_command_private, "Append a private post message to a feed." },
|
||||||
{ "create_invite", _tf_command_create_invite, "Create an invite." },
|
{ "create_invite", _tf_command_create_invite, "Create an invite." },
|
||||||
@ -178,11 +179,24 @@ const command_t k_commands[] = {
|
|||||||
{ "get_profile", _tf_command_get_profile, "Get profile information for the given identity." },
|
{ "get_profile", _tf_command_get_profile, "Get profile information for the given identity." },
|
||||||
{ "get_contacts", _tf_command_get_contacts, "Get information about followed, blocked, and friend identities." },
|
{ "get_contacts", _tf_command_get_contacts, "Get information about followed, blocked, and friend identities." },
|
||||||
{ "has_blob", _tf_command_has_blob, "Check whether a blob is in the blob store." },
|
{ "has_blob", _tf_command_has_blob, "Check whether a blob is in the blob store." },
|
||||||
|
{ "get_blob", _tf_command_get_blob, "Read a file from the blob store." },
|
||||||
{ "store_blob", _tf_command_store_blob, "Write a file to the blob store." },
|
{ "store_blob", _tf_command_store_blob, "Write a file to the blob store." },
|
||||||
{ "verify", _tf_command_verify, "Verify a feed." },
|
{ "verify", _tf_command_verify, "Verify a feed." },
|
||||||
{ "test", _tf_command_test, "Test SSB." },
|
{ "test", _tf_command_test, "Test SSB." },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char* _description(const char* name)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tf_countof(k_commands); i++)
|
||||||
|
{
|
||||||
|
if (strcmp(name, k_commands[i].name) == 0)
|
||||||
|
{
|
||||||
|
return k_commands[i].description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int _tf_command_test(const char* file, int argc, char* argv[])
|
static int _tf_command_test(const char* file, int argc, char* argv[])
|
||||||
{
|
{
|
||||||
#if !defined(__ANDROID__)
|
#if !defined(__ANDROID__)
|
||||||
@ -231,8 +245,9 @@ static int _tf_command_test(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s test [options]\n\n", file);
|
tf_printf("\nUsage: %s test [options]\n\n", file);
|
||||||
tf_printf("options\n");
|
tf_printf("%s\n\n", _description("test"));
|
||||||
|
tf_printf("options:\n");
|
||||||
tf_printf(" -t, --tests tests Comma-separated list of tests to run. (default: all)\n");
|
tf_printf(" -t, --tests tests Comma-separated list of tests to run. (default: all)\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);
|
tf_free((void*)default_db_path);
|
||||||
@ -286,7 +301,8 @@ static int _tf_command_import(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s import [options] [paths...]\n\n", file);
|
tf_printf("\nUsage: %s import [options] [paths...]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("import"));
|
||||||
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", default_db_path);
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
@ -355,7 +371,8 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s export [options] [paths...]\n\n", file);
|
tf_printf("\nUsage: %s export [options] [paths...]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("export"));
|
||||||
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", default_db_path);
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
@ -471,7 +488,8 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !identity || !content)
|
if (show_usage || !identity || !content)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s publish [options]\n\n", file);
|
tf_printf("\nUsage: %s publish [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("publish"));
|
||||||
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");
|
||||||
@ -499,7 +517,7 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
|
|||||||
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)))
|
||||||
{
|
{
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
int64_t sequence = 0;
|
int32_t sequence = 0;
|
||||||
char previous[k_id_base64_len] = { 0 };
|
char previous[k_id_base64_len] = { 0 };
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, previous, sizeof(previous));
|
tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, previous, sizeof(previous));
|
||||||
JSValue content_value = JS_ParseJSON(context, content, strlen(content), NULL);
|
JSValue content_value = JS_ParseJSON(context, content, strlen(content), NULL);
|
||||||
@ -590,7 +608,8 @@ static int _tf_command_private(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !identity || !recipients || !text)
|
if (show_usage || !identity || !recipients || !text)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s private [options]\n\n", file);
|
tf_printf("\nUsage: %s private [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("private"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -u, --user user User owning identity with which to publish (optional).\n");
|
tf_printf(" -u, --user user User owning identity with which to publish (optional).\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");
|
||||||
@ -651,7 +670,7 @@ static int _tf_command_private(const char* file, int argc, char* argv[])
|
|||||||
char* encrypted = tf_ssb_private_message_encrypt(private_key, recipient_list, recipient_count, message_str, strlen(message_str));
|
char* encrypted = tf_ssb_private_message_encrypt(private_key, recipient_list, recipient_count, message_str, strlen(message_str));
|
||||||
if (encrypted)
|
if (encrypted)
|
||||||
{
|
{
|
||||||
int64_t sequence = 0;
|
int32_t sequence = 0;
|
||||||
char previous[k_id_base64_len] = { 0 };
|
char previous[k_id_base64_len] = { 0 };
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, previous, sizeof(previous));
|
tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, previous, sizeof(previous));
|
||||||
|
|
||||||
@ -721,7 +740,8 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !file_path)
|
if (show_usage || !file_path)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s store_blob [options]\n\n", file);
|
tf_printf("\nUsage: %s store_blob [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("store_blob"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
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");
|
||||||
@ -779,6 +799,114 @@ static int _tf_command_store_blob(const char* file, int argc, char* argv[])
|
|||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _tf_command_get_blob(const char* file, int argc, char* argv[])
|
||||||
|
{
|
||||||
|
const char* default_db_path = _get_db_path();
|
||||||
|
const char* db_path = default_db_path;
|
||||||
|
const char* output = NULL;
|
||||||
|
const char* blob_id = NULL;
|
||||||
|
bool show_usage = false;
|
||||||
|
|
||||||
|
while (!show_usage)
|
||||||
|
{
|
||||||
|
static const struct option k_options[] = {
|
||||||
|
{ "db-path", required_argument, NULL, 'd' },
|
||||||
|
{ "blob", required_argument, NULL, 'b' },
|
||||||
|
{ "output", required_argument, NULL, 'o' },
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ 0 },
|
||||||
|
};
|
||||||
|
int c = getopt_long(argc, argv, "d:b:o:h", k_options, NULL);
|
||||||
|
if (c == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case '?':
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
show_usage = true;
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
db_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
blob_id = optarg;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
output = optarg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_usage || !blob_id)
|
||||||
|
{
|
||||||
|
tf_printf("\nUsage: %s get_blob [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("get_blob"));
|
||||||
|
tf_printf("options:\n");
|
||||||
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
|
tf_printf(" -b, --blob blob_id Blob identifier to retrieve.\n");
|
||||||
|
tf_printf(" -o, --output file_path Location to write the retrieved blob.\n");
|
||||||
|
tf_printf(" -h, --help Show this usage information.\n");
|
||||||
|
tf_free((void*)default_db_path);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* blob = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
_create_directories_for_file(db_path, 0700);
|
||||||
|
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
|
||||||
|
tf_ssb_set_quiet(ssb, true);
|
||||||
|
bool fetched = tf_ssb_db_blob_get(ssb, blob_id, &blob, &size);
|
||||||
|
tf_ssb_destroy(ssb);
|
||||||
|
|
||||||
|
if (!fetched)
|
||||||
|
{
|
||||||
|
tf_printf("Failed to fetch blob: %s.\n", blob_id);
|
||||||
|
tf_free((void*)default_db_path);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output)
|
||||||
|
{
|
||||||
|
FILE* blob_file = fopen(output, "wb");
|
||||||
|
if (!blob_file)
|
||||||
|
{
|
||||||
|
tf_printf("Failed to open %s: %s.\n", output, strerror(errno));
|
||||||
|
tf_free((void*)default_db_path);
|
||||||
|
tf_free(blob);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(blob, 1, size, blob_file) != size)
|
||||||
|
{
|
||||||
|
tf_printf("Failed to write %s: %s\n", output, strerror(errno));
|
||||||
|
tf_free(blob);
|
||||||
|
tf_free((void*)default_db_path);
|
||||||
|
fclose(blob_file);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(blob_file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (fwrite(blob, 1, size, stdout) != size)
|
||||||
|
{
|
||||||
|
tf_free(blob);
|
||||||
|
tf_free((void*)default_db_path);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tf_free(blob);
|
||||||
|
tf_free((void*)default_db_path);
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
static int _tf_command_has_blob(const char* file, int argc, char* argv[])
|
static int _tf_command_has_blob(const char* file, int argc, char* argv[])
|
||||||
{
|
{
|
||||||
const char* default_db_path = _get_db_path();
|
const char* default_db_path = _get_db_path();
|
||||||
@ -818,7 +946,8 @@ static int _tf_command_has_blob(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !blob_id)
|
if (show_usage || !blob_id)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s has_blob [options]\n\n", file);
|
tf_printf("\nUsage: %s has_blob [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("has_blob"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
tf_printf(" -b, --blob_id blob_id ID of blob to query.\n");
|
tf_printf(" -b, --blob_id blob_id ID of blob to query.\n");
|
||||||
@ -896,7 +1025,8 @@ static int _tf_command_create_invite(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !identity || !use_count || !expires || !host || !port)
|
if (show_usage || !identity || !use_count || !expires || !host || !port)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s get_sequence [options]\n\n", file);
|
tf_printf("\nUsage: %s create_invite [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("create_invite"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
tf_printf(" -i, --identity identity Account from which to get latest sequence number.\n");
|
tf_printf(" -i, --identity identity Account from which to get latest sequence number.\n");
|
||||||
@ -964,7 +1094,8 @@ static int _tf_command_get_sequence(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !identity)
|
if (show_usage || !identity)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s get_sequence [options]\n\n", file);
|
tf_printf("\nUsage: %s get_sequence [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("get_sequence"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
tf_printf(" -i, --identity identity Account from which to get latest sequence number.\n");
|
tf_printf(" -i, --identity identity Account from which to get latest sequence number.\n");
|
||||||
@ -975,9 +1106,9 @@ static int _tf_command_get_sequence(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);
|
||||||
tf_ssb_set_quiet(ssb, true);
|
tf_ssb_set_quiet(ssb, true);
|
||||||
int64_t sequence = -1;
|
int32_t sequence = -1;
|
||||||
int result = tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, NULL, 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
int result = tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, NULL, 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
tf_printf("%" PRId64 "\n", sequence);
|
tf_printf("%d\n", sequence);
|
||||||
tf_ssb_destroy(ssb);
|
tf_ssb_destroy(ssb);
|
||||||
tf_free((void*)default_db_path);
|
tf_free((void*)default_db_path);
|
||||||
return result;
|
return result;
|
||||||
@ -1017,7 +1148,8 @@ static int _tf_command_get_identity(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s get_identity [options]\n\n", file);
|
tf_printf("\nUsage: %s get_identity [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("get_identity"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
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");
|
||||||
@ -1074,7 +1206,8 @@ static int _tf_command_get_profile(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !identity)
|
if (show_usage || !identity)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s get_profile [options]\n\n", file);
|
tf_printf("\nUsage: %s get_profile [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("get_profile"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
tf_printf(" -i, --identity identity Account for which to get profile information.\n");
|
tf_printf(" -i, --identity identity Account for which to get profile information.\n");
|
||||||
@ -1133,7 +1266,8 @@ static int _tf_command_get_contacts(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage || !identity)
|
if (show_usage || !identity)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s get_contacts [options]\n\n", file);
|
tf_printf("\nUsage: %s get_contacts [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("get_contacts"));
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
tf_printf(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
|
||||||
tf_printf(" -i, --identity identity Account from which to get contact information.\n");
|
tf_printf(" -i, --identity identity Account from which to get contact information.\n");
|
||||||
@ -1195,7 +1329,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
|
|||||||
const char* identity = NULL;
|
const char* identity = NULL;
|
||||||
const char* default_db_path = _get_db_path();
|
const char* default_db_path = _get_db_path();
|
||||||
const char* db_path = default_db_path;
|
const char* db_path = default_db_path;
|
||||||
int64_t sequence = 0;
|
int32_t sequence = 0;
|
||||||
bool show_usage = false;
|
bool show_usage = false;
|
||||||
|
|
||||||
while (!show_usage)
|
while (!show_usage)
|
||||||
@ -1224,7 +1358,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
|
|||||||
identity = optarg;
|
identity = optarg;
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
sequence = atoll(optarg);
|
sequence = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
db_path = optarg;
|
db_path = optarg;
|
||||||
@ -1234,7 +1368,8 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s import [options] [paths...]\n\n", file);
|
tf_printf("\nUsage: %s verify [options]\n\n", file);
|
||||||
|
tf_printf("%s\n\n", _description("verify"));
|
||||||
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(" -s, --sequence sequence Sequence number to debug.\n");
|
tf_printf(" -s, --sequence sequence Sequence number to debug.\n");
|
||||||
@ -1562,8 +1697,11 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
|||||||
|
|
||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\n%s run [options]\n\n", file);
|
tf_printf("\nUsage: %s run [options]\n\n", file);
|
||||||
tf_printf("options\n");
|
#if !defined(__ANDROID__)
|
||||||
|
tf_printf("%s\n\n", _description("run"));
|
||||||
|
#endif
|
||||||
|
tf_printf("options:\n");
|
||||||
tf_printf(" -s, --script script Script to run (default: core/core.js).\n");
|
tf_printf(" -s, --script script Script to run (default: core/core.js).\n");
|
||||||
tf_printf(" -d, --db-path path SQLite database path (default: %s).\n", default_db_path);
|
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");
|
||||||
@ -1648,6 +1786,9 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
|||||||
if (show_usage)
|
if (show_usage)
|
||||||
{
|
{
|
||||||
tf_printf("\nUsage: %s sandbox [options]\n\n", file);
|
tf_printf("\nUsage: %s sandbox [options]\n\n", file);
|
||||||
|
#if !defined(__ANDROID__)
|
||||||
|
tf_printf("%s\n\n", _description("sandbox"));
|
||||||
|
#endif
|
||||||
tf_printf("options:\n");
|
tf_printf("options:\n");
|
||||||
tf_printf(" -h, --help Show this usage information.\n");
|
tf_printf(" -h, --help Show this usage information.\n");
|
||||||
tf_printf(" -f, --fd File descriptor with which to communicate with parent process.\n");
|
tf_printf(" -f, --fd File descriptor with which to communicate with parent process.\n");
|
||||||
@ -1917,6 +2058,7 @@ void tf_run_thread_start(const char* zip_path)
|
|||||||
#else
|
#else
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
_startup(argc, argv);
|
_startup(argc, argv);
|
||||||
ares_library_init(0);
|
ares_library_init(0);
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
#include "quickjs.h"
|
#include "quickjs.h"
|
||||||
|
#include "sodium/crypto_generichash.h"
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
|
|
||||||
@ -154,11 +155,11 @@ static void _tf_mem_summarize(void* ptr, size_t size, int frames_count, void* co
|
|||||||
{
|
{
|
||||||
summary_t* summary = user_data;
|
summary_t* summary = user_data;
|
||||||
tf_mem_allocation_t allocation = {
|
tf_mem_allocation_t allocation = {
|
||||||
.stack_hash = tf_util_fnv32a(frames, sizeof(void*) * frames_count, 0),
|
|
||||||
.count = 1,
|
.count = 1,
|
||||||
.size = size,
|
.size = size,
|
||||||
.frames_count = frames_count,
|
.frames_count = frames_count,
|
||||||
};
|
};
|
||||||
|
crypto_generichash((void*)&allocation.stack_hash, sizeof(allocation.stack_hash), (const void*)frames, sizeof(void*) * frames_count, NULL, 0);
|
||||||
memcpy(allocation.frames, frames, sizeof(void*) * frames_count);
|
memcpy(allocation.frames, frames, sizeof(void*) * frames_count);
|
||||||
|
|
||||||
int index = tf_util_insert_index(&allocation, summary->allocations, summary->count, sizeof(tf_mem_allocation_t), _tf_mem_hash_stack_compare);
|
int index = tf_util_insert_index(&allocation, summary->allocations, summary->count, sizeof(tf_mem_allocation_t), _tf_mem_hash_stack_compare);
|
||||||
|
@ -489,7 +489,7 @@ static JSValue _socket_connect(JSContext* context, JSValueConst this_val, int ar
|
|||||||
const char* node = JS_ToCString(context, argv[0]);
|
const char* node = JS_ToCString(context, argv[0]);
|
||||||
const char* port = JS_ToCString(context, argv[1]);
|
const char* port = JS_ToCString(context, argv[1]);
|
||||||
|
|
||||||
snprintf(socket->_peerName, sizeof(socket->_peerName), "%s", node);
|
tf_string_set(socket->_peerName, sizeof(socket->_peerName), node);
|
||||||
|
|
||||||
socket_resolve_data_t* data = tf_malloc(sizeof(socket_resolve_data_t));
|
socket_resolve_data_t* data = tf_malloc(sizeof(socket_resolve_data_t));
|
||||||
memset(data, 0, sizeof(*data));
|
memset(data, 0, sizeof(*data));
|
||||||
@ -1062,9 +1062,7 @@ static void _socket_onShutdown(uv_shutdown_t* request, int status)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char error[256];
|
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "uv_shutdown: %s", uv_strerror(status)));
|
||||||
snprintf(error, sizeof(error), "uv_shutdown: %s", uv_strerror(status));
|
|
||||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error));
|
|
||||||
}
|
}
|
||||||
tf_free(request);
|
tf_free(request);
|
||||||
}
|
}
|
||||||
|
172
src/ssb.c
@ -34,9 +34,6 @@
|
|||||||
#define CYAN "\e[1;36m"
|
#define CYAN "\e[1;36m"
|
||||||
#define RESET "\e[0m"
|
#define RESET "\e[0m"
|
||||||
|
|
||||||
#define PRE_CALLBACK(ssb, cb) uint64_t pre_callback_hrtime_ns = _tf_ssb_callback_pre(ssb)
|
|
||||||
#define POST_CALLBACK(ssb, cb) _tf_ssb_callback_post(ssb, cb, pre_callback_hrtime_ns)
|
|
||||||
|
|
||||||
const int k_read_back_pressure_threshold = 256;
|
const int k_read_back_pressure_threshold = 256;
|
||||||
|
|
||||||
static_assert(k_id_base64_len == sodium_base64_ENCODED_LEN(9 + crypto_box_PUBLICKEYBYTES, sodium_base64_VARIANT_ORIGINAL), "k_id_base64_len");
|
static_assert(k_id_base64_len == sodium_base64_ENCODED_LEN(9 + crypto_box_PUBLICKEYBYTES, sodium_base64_VARIANT_ORIGINAL), "k_id_base64_len");
|
||||||
@ -168,6 +165,12 @@ typedef struct _tf_ssb_timer_t
|
|||||||
void* user_data;
|
void* user_data;
|
||||||
} tf_ssb_timer_t;
|
} tf_ssb_timer_t;
|
||||||
|
|
||||||
|
typedef struct _tf_ssb_broadcast_result_t
|
||||||
|
{
|
||||||
|
struct sockaddr_storage addr;
|
||||||
|
int result;
|
||||||
|
} tf_ssb_broadcast_result_t;
|
||||||
|
|
||||||
typedef struct _tf_ssb_t
|
typedef struct _tf_ssb_t
|
||||||
{
|
{
|
||||||
bool own_context;
|
bool own_context;
|
||||||
@ -183,6 +186,7 @@ typedef struct _tf_ssb_t
|
|||||||
sqlite3* db_writer;
|
sqlite3* db_writer;
|
||||||
sqlite3** db_readers;
|
sqlite3** db_readers;
|
||||||
int db_readers_count;
|
int db_readers_count;
|
||||||
|
int db_ref_count;
|
||||||
|
|
||||||
uv_loop_t own_loop;
|
uv_loop_t own_loop;
|
||||||
uv_loop_t* loop;
|
uv_loop_t* loop;
|
||||||
@ -194,6 +198,9 @@ typedef struct _tf_ssb_t
|
|||||||
uv_timer_t request_activity_timer;
|
uv_timer_t request_activity_timer;
|
||||||
uv_tcp_t server;
|
uv_tcp_t server;
|
||||||
|
|
||||||
|
tf_ssb_broadcast_result_t* broadcast_results;
|
||||||
|
int broadcast_results_count;
|
||||||
|
|
||||||
uint8_t network_key[32];
|
uint8_t network_key[32];
|
||||||
|
|
||||||
uint8_t pub[crypto_sign_PUBLICKEYBYTES];
|
uint8_t pub[crypto_sign_PUBLICKEYBYTES];
|
||||||
@ -237,9 +244,6 @@ typedef struct _tf_ssb_t
|
|||||||
int32_t thread_busy_count;
|
int32_t thread_busy_count;
|
||||||
int32_t thread_busy_max;
|
int32_t thread_busy_max;
|
||||||
|
|
||||||
void (*hitch_callback)(const char* name, uint64_t duration, void* user_data);
|
|
||||||
void* hitch_user_data;
|
|
||||||
|
|
||||||
tf_ssb_store_queue_t store_queue;
|
tf_ssb_store_queue_t store_queue;
|
||||||
int ref_count;
|
int ref_count;
|
||||||
|
|
||||||
@ -368,8 +372,6 @@ static int s_connection_index;
|
|||||||
static int s_tunnel_index;
|
static int s_tunnel_index;
|
||||||
|
|
||||||
static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast, int expires_seconds);
|
static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast, int expires_seconds);
|
||||||
static uint64_t _tf_ssb_callback_pre(tf_ssb_t* ssb);
|
|
||||||
static void _tf_ssb_callback_post(tf_ssb_t* ssb, void* callback, uint64_t pre);
|
|
||||||
static void _tf_ssb_connection_client_send_hello(tf_ssb_connection_t* connection);
|
static void _tf_ssb_connection_client_send_hello(tf_ssb_connection_t* connection);
|
||||||
static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason);
|
static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason);
|
||||||
static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value);
|
static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value);
|
||||||
@ -645,9 +647,7 @@ static void _tf_ssb_connection_dispatch_scheduled(tf_ssb_connection_t* connectio
|
|||||||
connection->scheduled_count--;
|
connection->scheduled_count--;
|
||||||
|
|
||||||
tf_trace_begin(connection->ssb->trace, "scheduled callback");
|
tf_trace_begin(connection->ssb->trace, "scheduled callback");
|
||||||
PRE_CALLBACK(connection->ssb, scheduled.callback);
|
|
||||||
scheduled.callback(connection, false, scheduled.user_data);
|
scheduled.callback(connection, false, scheduled.user_data);
|
||||||
POST_CALLBACK(connection->ssb, scheduled.callback);
|
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -666,9 +666,7 @@ void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, const char
|
|||||||
{
|
{
|
||||||
/* Skip the new request. */
|
/* Skip the new request. */
|
||||||
tf_trace_begin(connection->ssb->trace, "scheduled callback (skip)");
|
tf_trace_begin(connection->ssb->trace, "scheduled callback (skip)");
|
||||||
PRE_CALLBACK(connection->ssb, callback);
|
|
||||||
callback(connection, true, user_data);
|
callback(connection, true, user_data);
|
||||||
POST_CALLBACK(connection->ssb, callback);
|
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -679,7 +677,7 @@ void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, const char
|
|||||||
.callback = callback,
|
.callback = callback,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
};
|
};
|
||||||
snprintf(connection->scheduled[index].key, sizeof(connection->scheduled[index].key), "%s", key);
|
tf_string_set(connection->scheduled[index].key, sizeof(connection->scheduled[index].key), key);
|
||||||
connection->scheduled_count++;
|
connection->scheduled_count++;
|
||||||
|
|
||||||
uv_async_send(&connection->scheduled_async);
|
uv_async_send(&connection->scheduled_async);
|
||||||
@ -800,7 +798,7 @@ void tf_ssb_connection_add_request(tf_ssb_connection_t* connection, int32_t requ
|
|||||||
.dependent_connection = dependent_connection,
|
.dependent_connection = dependent_connection,
|
||||||
.last_active = now_ms,
|
.last_active = now_ms,
|
||||||
};
|
};
|
||||||
snprintf(request.name, sizeof(request.name), "%s", name);
|
tf_string_set(request.name, sizeof(request.name), name);
|
||||||
int index = tf_util_insert_index(&request_number, connection->requests, connection->requests_count, sizeof(tf_ssb_request_t), _request_compare);
|
int index = tf_util_insert_index(&request_number, connection->requests, connection->requests_count, sizeof(tf_ssb_request_t), _request_compare);
|
||||||
connection->requests = tf_resize_vec(connection->requests, sizeof(tf_ssb_request_t) * (connection->requests_count + 1));
|
connection->requests = tf_resize_vec(connection->requests, sizeof(tf_ssb_request_t) * (connection->requests_count + 1));
|
||||||
if (connection->requests_count - index)
|
if (connection->requests_count - index)
|
||||||
@ -853,7 +851,7 @@ void tf_ssb_connection_add_new_message_request(tf_ssb_connection_t* connection,
|
|||||||
.request_number = request_number,
|
.request_number = request_number,
|
||||||
.keys = keys,
|
.keys = keys,
|
||||||
};
|
};
|
||||||
snprintf(connection->message_requests[index].author, sizeof(connection->message_requests[index].author), "%s", author);
|
tf_string_set(connection->message_requests[index].author, sizeof(connection->message_requests[index].author), author);
|
||||||
connection->message_requests_count++;
|
connection->message_requests_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1249,22 +1247,6 @@ bool tf_ssb_id_str_to_bin(uint8_t* bin, const char* str)
|
|||||||
return author_id && type ? tf_base64_decode(author_id, type - author_id, bin, crypto_box_PUBLICKEYBYTES) != 0 : false;
|
return author_id && type ? tf_base64_decode(author_id, type - author_id, bin, crypto_box_PUBLICKEYBYTES) != 0 : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t _tf_ssb_callback_pre(tf_ssb_t* ssb)
|
|
||||||
{
|
|
||||||
return uv_hrtime();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_callback_post(tf_ssb_t* ssb, void* callback, uint64_t pre)
|
|
||||||
{
|
|
||||||
if (ssb->hitch_callback)
|
|
||||||
{
|
|
||||||
uint64_t post = uv_hrtime();
|
|
||||||
const char* name = tf_util_function_to_string(callback);
|
|
||||||
ssb->hitch_callback(name, post - pre, ssb->hitch_user_data);
|
|
||||||
tf_free((void*)name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection)
|
static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection)
|
||||||
{
|
{
|
||||||
tf_ssb_connections_changed_callback_node_t* next = NULL;
|
tf_ssb_connections_changed_callback_node_t* next = NULL;
|
||||||
@ -1272,9 +1254,7 @@ static void _tf_ssb_notify_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t ch
|
|||||||
{
|
{
|
||||||
next = node->next;
|
next = node->next;
|
||||||
tf_trace_begin(ssb->trace, "connections_changed");
|
tf_trace_begin(ssb->trace, "connections_changed");
|
||||||
PRE_CALLBACK(ssb, node->callback);
|
|
||||||
node->callback(ssb, change, connection, node->user_data);
|
node->callback(ssb, change, connection, node->user_data);
|
||||||
POST_CALLBACK(ssb, node->callback);
|
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1383,9 +1363,7 @@ static void _tf_ssb_connection_verify_identity(tf_ssb_connection_t* connection,
|
|||||||
connection->state = k_tf_ssb_state_verified;
|
connection->state = k_tf_ssb_state_verified;
|
||||||
if (connection->connect_callback)
|
if (connection->connect_callback)
|
||||||
{
|
{
|
||||||
PRE_CALLBACK(connection->ssb, connection->connect_callback);
|
|
||||||
connection->connect_callback(connection, NULL, connection->connect_callback_user_data);
|
connection->connect_callback(connection, NULL, connection->connect_callback_user_data);
|
||||||
POST_CALLBACK(connection->ssb, connection->connect_callback);
|
|
||||||
connection->connect_callback = NULL;
|
connection->connect_callback = NULL;
|
||||||
connection->connect_callback_user_data = NULL;
|
connection->connect_callback_user_data = NULL;
|
||||||
}
|
}
|
||||||
@ -1734,7 +1712,7 @@ static void _tf_ssb_name_to_string(JSContext* context, JSValue object, char* buf
|
|||||||
else if (JS_IsString(name))
|
else if (JS_IsString(name))
|
||||||
{
|
{
|
||||||
const char* part_str = JS_ToCString(context, name);
|
const char* part_str = JS_ToCString(context, name);
|
||||||
snprintf(buffer, size, "%s", part_str);
|
tf_string_set(buffer, size, part_str);
|
||||||
JS_FreeCString(context, part_str);
|
JS_FreeCString(context, part_str);
|
||||||
}
|
}
|
||||||
JS_FreeValue(context, name);
|
JS_FreeValue(context, name);
|
||||||
@ -1777,9 +1755,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
|||||||
char buffer[64];
|
char buffer[64];
|
||||||
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
|
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
|
||||||
tf_trace_begin(connection->ssb->trace, buffer);
|
tf_trace_begin(connection->ssb->trace, buffer);
|
||||||
PRE_CALLBACK(connection->ssb, callback);
|
|
||||||
callback(connection, flags, request_number, val, message, size, user_data);
|
callback(connection, flags, request_number, val, message, size, user_data);
|
||||||
POST_CALLBACK(connection->ssb, callback);
|
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
if (!(flags & k_ssb_rpc_flag_stream))
|
if (!(flags & k_ssb_rpc_flag_stream))
|
||||||
{
|
{
|
||||||
@ -1798,9 +1774,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
|||||||
{
|
{
|
||||||
tf_ssb_connection_add_request(connection, -request_number, name, NULL, NULL, NULL, NULL);
|
tf_ssb_connection_add_request(connection, -request_number, name, NULL, NULL, NULL, NULL);
|
||||||
tf_trace_begin(connection->ssb->trace, it->name);
|
tf_trace_begin(connection->ssb->trace, it->name);
|
||||||
PRE_CALLBACK(connection->ssb, it->callback);
|
|
||||||
it->callback(connection, flags, request_number, val, message, size, it->user_data);
|
it->callback(connection, flags, request_number, val, message, size, it->user_data);
|
||||||
POST_CALLBACK(connection->ssb, it->callback);
|
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
@ -1840,9 +1814,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
|
|||||||
char buffer[64];
|
char buffer[64];
|
||||||
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
|
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
|
||||||
tf_trace_begin(connection->ssb->trace, buffer);
|
tf_trace_begin(connection->ssb->trace, buffer);
|
||||||
PRE_CALLBACK(connection->ssb, callback);
|
|
||||||
callback(connection, flags, request_number, JS_UNDEFINED, message, size, user_data);
|
callback(connection, flags, request_number, JS_UNDEFINED, message, size, user_data);
|
||||||
POST_CALLBACK(connection->ssb, callback);
|
|
||||||
tf_trace_end(connection->ssb->trace);
|
tf_trace_end(connection->ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1876,7 +1848,7 @@ static void _tf_ssb_connection_rpc_recv_push(tf_ssb_connection_t* connection, co
|
|||||||
|
|
||||||
while (connection->rpc_recv_size >= 9)
|
while (connection->rpc_recv_size >= 9)
|
||||||
{
|
{
|
||||||
uint8_t flags = *connection->rpc_recv_buffer;
|
uint8_t flags = (*connection->rpc_recv_buffer) & 0xf;
|
||||||
uint32_t body_len;
|
uint32_t body_len;
|
||||||
int32_t request_number;
|
int32_t request_number;
|
||||||
memcpy(&body_len, connection->rpc_recv_buffer + 1, sizeof(body_len));
|
memcpy(&body_len, connection->rpc_recv_buffer + 1, sizeof(body_len));
|
||||||
@ -1954,15 +1926,15 @@ static bool _tf_ssb_connection_box_stream_recv(tf_ssb_connection_t* connection)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int64_t previous_sequence)
|
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int32_t previous_sequence)
|
||||||
{
|
{
|
||||||
char actual_previous_id[crypto_hash_sha256_BYTES * 2];
|
char actual_previous_id[crypto_hash_sha256_BYTES * 2];
|
||||||
int64_t actual_previous_sequence = 0;
|
int32_t actual_previous_sequence = 0;
|
||||||
bool have_previous = false;
|
bool have_previous = false;
|
||||||
if (previous_id)
|
if (previous_id)
|
||||||
{
|
{
|
||||||
have_previous = *previous_id && previous_sequence > 0;
|
have_previous = *previous_id && previous_sequence > 0;
|
||||||
snprintf(actual_previous_id, sizeof(actual_previous_id), "%s", previous_id);
|
tf_string_set(actual_previous_id, sizeof(actual_previous_id), previous_id);
|
||||||
actual_previous_sequence = previous_sequence;
|
actual_previous_sequence = previous_sequence;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1975,7 +1947,7 @@ JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* pr
|
|||||||
|
|
||||||
JS_SetPropertyStr(context, root, "previous", have_previous ? JS_NewString(context, actual_previous_id) : JS_NULL);
|
JS_SetPropertyStr(context, root, "previous", have_previous ? JS_NewString(context, actual_previous_id) : JS_NULL);
|
||||||
JS_SetPropertyStr(context, root, "author", JS_NewString(context, author));
|
JS_SetPropertyStr(context, root, "author", JS_NewString(context, author));
|
||||||
JS_SetPropertyStr(context, root, "sequence", JS_NewInt64(context, actual_previous_sequence + 1));
|
JS_SetPropertyStr(context, root, "sequence", JS_NewInt32(context, actual_previous_sequence + 1));
|
||||||
|
|
||||||
int64_t now = (int64_t)time(NULL);
|
int64_t now = (int64_t)time(NULL);
|
||||||
JS_SetPropertyStr(context, root, "timestamp", JS_NewInt64(context, now * 1000LL));
|
JS_SetPropertyStr(context, root, "timestamp", JS_NewInt64(context, now * 1000LL));
|
||||||
@ -2029,9 +2001,7 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch
|
|||||||
}
|
}
|
||||||
if (connection->connect_callback)
|
if (connection->connect_callback)
|
||||||
{
|
{
|
||||||
PRE_CALLBACK(connection->ssb, connection->connect_callback);
|
|
||||||
connection->connect_callback(NULL, reason, connection->connect_callback_user_data);
|
connection->connect_callback(NULL, reason, connection->connect_callback_user_data);
|
||||||
POST_CALLBACK(connection->ssb, connection->connect_callback);
|
|
||||||
connection->connect_callback = NULL;
|
connection->connect_callback = NULL;
|
||||||
connection->connect_callback_user_data = NULL;
|
connection->connect_callback_user_data = NULL;
|
||||||
}
|
}
|
||||||
@ -2519,6 +2489,7 @@ sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb)
|
|||||||
tf_ssb_db_init_reader(db);
|
tf_ssb_db_init_reader(db);
|
||||||
}
|
}
|
||||||
tf_trace_sqlite(ssb->trace, db);
|
tf_trace_sqlite(ssb->trace, db);
|
||||||
|
ssb->db_ref_count++;
|
||||||
uv_mutex_unlock(&ssb->db_readers_lock);
|
uv_mutex_unlock(&ssb->db_readers_lock);
|
||||||
sqlite3_set_authorizer(db, NULL, NULL);
|
sqlite3_set_authorizer(db, NULL, NULL);
|
||||||
return db;
|
return db;
|
||||||
@ -2535,10 +2506,17 @@ void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db)
|
|||||||
{
|
{
|
||||||
sqlite3_db_release_memory(db);
|
sqlite3_db_release_memory(db);
|
||||||
uv_mutex_lock(&ssb->db_readers_lock);
|
uv_mutex_lock(&ssb->db_readers_lock);
|
||||||
|
ssb->db_ref_count--;
|
||||||
|
bool destroy = ssb->shutting_down_deferred && ssb->db_ref_count == 0;
|
||||||
ssb->db_readers = tf_resize_vec(ssb->db_readers, sizeof(sqlite3*) * (ssb->db_readers_count + 1));
|
ssb->db_readers = tf_resize_vec(ssb->db_readers, sizeof(sqlite3*) * (ssb->db_readers_count + 1));
|
||||||
ssb->db_readers[ssb->db_readers_count++] = db;
|
ssb->db_readers[ssb->db_readers_count++] = db;
|
||||||
uv_mutex_unlock(&ssb->db_readers_lock);
|
uv_mutex_unlock(&ssb->db_readers_lock);
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
|
|
||||||
|
if (destroy)
|
||||||
|
{
|
||||||
|
tf_ssb_destroy(ssb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb)
|
sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb)
|
||||||
@ -2832,15 +2810,6 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
|||||||
JS_FreeRuntime(ssb->runtime);
|
JS_FreeRuntime(ssb->runtime);
|
||||||
ssb->own_context = false;
|
ssb->own_context = false;
|
||||||
}
|
}
|
||||||
if (ssb->db_writer)
|
|
||||||
{
|
|
||||||
int r = sqlite3_close(ssb->db_writer);
|
|
||||||
if (r != SQLITE_OK)
|
|
||||||
{
|
|
||||||
tf_printf("sqlite3_close: %s\n", sqlite3_errstr(r));
|
|
||||||
}
|
|
||||||
ssb->db_writer = NULL;
|
|
||||||
}
|
|
||||||
while (ssb->broadcasts)
|
while (ssb->broadcasts)
|
||||||
{
|
{
|
||||||
tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
|
tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
|
||||||
@ -2856,6 +2825,15 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
|||||||
tf_printf("sqlite3_close: %s\n", sqlite3_errstr(r));
|
tf_printf("sqlite3_close: %s\n", sqlite3_errstr(r));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ssb->db_writer)
|
||||||
|
{
|
||||||
|
int r = sqlite3_close(ssb->db_writer);
|
||||||
|
if (r != SQLITE_OK)
|
||||||
|
{
|
||||||
|
tf_printf("sqlite3_close: %s\n", sqlite3_errstr(r));
|
||||||
|
}
|
||||||
|
ssb->db_writer = NULL;
|
||||||
|
}
|
||||||
ssb->db_readers_count = 0;
|
ssb->db_readers_count = 0;
|
||||||
if (ssb->db_readers)
|
if (ssb->db_readers)
|
||||||
{
|
{
|
||||||
@ -2872,9 +2850,15 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
|||||||
tf_free(ssb->room_name);
|
tf_free(ssb->room_name);
|
||||||
ssb->room_name = NULL;
|
ssb->room_name = NULL;
|
||||||
}
|
}
|
||||||
|
if (ssb->broadcast_results_count)
|
||||||
|
{
|
||||||
|
tf_free(ssb->broadcast_results);
|
||||||
|
ssb->broadcast_results = NULL;
|
||||||
|
ssb->broadcast_results_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
ssb->shutting_down_deferred = true;
|
ssb->shutting_down_deferred = true;
|
||||||
if (ssb->connection_ref_count == 0)
|
if (ssb->connection_ref_count == 0 && ssb->db_ref_count == 0)
|
||||||
{
|
{
|
||||||
uv_mutex_destroy(&ssb->db_readers_lock);
|
uv_mutex_destroy(&ssb->db_readers_lock);
|
||||||
uv_mutex_destroy(&ssb->db_writer_lock);
|
uv_mutex_destroy(&ssb->db_writer_lock);
|
||||||
@ -2996,7 +2980,7 @@ static tf_ssb_connection_t* _tf_ssb_connection_create(
|
|||||||
tf_ssb_connection_t* connection = _tf_ssb_connection_create_internal(ssb, "cli", s_connection_index++);
|
tf_ssb_connection_t* connection = _tf_ssb_connection_create_internal(ssb, "cli", s_connection_index++);
|
||||||
connection->connect.data = connection;
|
connection->connect.data = connection;
|
||||||
|
|
||||||
snprintf(connection->host, sizeof(connection->host), "%s", host);
|
tf_string_set(connection->host, sizeof(connection->host), host);
|
||||||
connection->port = ntohs(addr->sin_port);
|
connection->port = ntohs(addr->sin_port);
|
||||||
connection->connect_callback = callback;
|
connection->connect_callback = callback;
|
||||||
connection->connect_callback_user_data = user_data;
|
connection->connect_callback_user_data = user_data;
|
||||||
@ -3171,7 +3155,7 @@ void tf_ssb_connect(tf_ssb_t* ssb, const char* host, int port, const uint8_t* ke
|
|||||||
{
|
{
|
||||||
tf_ssb_connections_store(ssb->connections_tracker, host, port, id);
|
tf_ssb_connections_store(ssb->connections_tracker, host, port, id);
|
||||||
}
|
}
|
||||||
snprintf(connect->host, sizeof(connect->host), "%s", host);
|
tf_string_set(connect->host, sizeof(connect->host), host);
|
||||||
memcpy(connect->key, key, k_id_bin_len);
|
memcpy(connect->key, key, k_id_bin_len);
|
||||||
tf_ssb_ref(ssb);
|
tf_ssb_ref(ssb);
|
||||||
int r = uv_getaddrinfo(ssb->loop, &connect->req, _tf_on_connect_getaddrinfo, host, NULL, &(struct addrinfo) { .ai_family = AF_INET });
|
int r = uv_getaddrinfo(ssb->loop, &connect->req, _tf_on_connect_getaddrinfo, host, NULL, &(struct addrinfo) { .ai_family = AF_INET });
|
||||||
@ -3220,7 +3204,7 @@ static void _tf_ssb_connect_with_invite(
|
|||||||
{
|
{
|
||||||
tf_ssb_connections_store(ssb->connections_tracker, host, port, id);
|
tf_ssb_connections_store(ssb->connections_tracker, host, port, id);
|
||||||
}
|
}
|
||||||
snprintf(connect->host, sizeof(connect->host), "%s", host);
|
tf_string_set(connect->host, sizeof(connect->host), host);
|
||||||
memcpy(connect->key, key, k_id_bin_len);
|
memcpy(connect->key, key, k_id_bin_len);
|
||||||
memcpy(connect->invite, invite, sizeof(connect->invite));
|
memcpy(connect->invite, invite, sizeof(connect->invite));
|
||||||
tf_ssb_ref(ssb);
|
tf_ssb_ref(ssb);
|
||||||
@ -3288,6 +3272,39 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status)
|
|||||||
_tf_ssb_connection_read_start(connection);
|
_tf_ssb_connection_read_start(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_update_broadcast_result(tf_ssb_t* ssb, struct sockaddr* address, const char* address_str, int result)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ssb->broadcast_results_count; i++)
|
||||||
|
{
|
||||||
|
if (ssb->broadcast_results[i].addr.ss_family == address->sa_family && address->sa_family == AF_INET &&
|
||||||
|
memcmp(&ssb->broadcast_results[i].addr, address, sizeof(struct sockaddr_in)) == 0)
|
||||||
|
{
|
||||||
|
if (result != ssb->broadcast_results[i].result)
|
||||||
|
{
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
char broadcast_str[256] = { 0 };
|
||||||
|
uv_ip4_name((struct sockaddr_in*)address, broadcast_str, sizeof(broadcast_str));
|
||||||
|
tf_printf("Unable to send broadcast for %s via %s (%d): %s.\n", address_str, broadcast_str, result, uv_strerror(result));
|
||||||
|
}
|
||||||
|
ssb->broadcast_results[i].result = result;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address->sa_family == AF_INET)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage storage = { 0 };
|
||||||
|
memcpy(&storage, address, sizeof(struct sockaddr_in));
|
||||||
|
ssb->broadcast_results = tf_resize_vec(ssb->broadcast_results, sizeof(tf_ssb_broadcast_result_t) * (ssb->broadcast_results_count + 1));
|
||||||
|
ssb->broadcast_results[ssb->broadcast_results_count++] = (tf_ssb_broadcast_result_t) {
|
||||||
|
.result = result,
|
||||||
|
.addr = storage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, struct sockaddr_in* netmask)
|
static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, struct sockaddr_in* netmask)
|
||||||
{
|
{
|
||||||
struct sockaddr server_addr;
|
struct sockaddr server_addr;
|
||||||
@ -3321,12 +3338,7 @@ static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, s
|
|||||||
broadcast_addr.sin_port = htons(8008);
|
broadcast_addr.sin_port = htons(8008);
|
||||||
broadcast_addr.sin_addr.s_addr = (address->sin_addr.s_addr & netmask->sin_addr.s_addr) | (INADDR_BROADCAST & ~netmask->sin_addr.s_addr);
|
broadcast_addr.sin_addr.s_addr = (address->sin_addr.s_addr & netmask->sin_addr.s_addr) | (INADDR_BROADCAST & ~netmask->sin_addr.s_addr);
|
||||||
r = uv_udp_try_send(&ssb->broadcast_sender, &buf, 1, (struct sockaddr*)&broadcast_addr);
|
r = uv_udp_try_send(&ssb->broadcast_sender, &buf, 1, (struct sockaddr*)&broadcast_addr);
|
||||||
if (r < 0)
|
_tf_ssb_update_broadcast_result(ssb, (struct sockaddr*)&broadcast_addr, address_str, r);
|
||||||
{
|
|
||||||
char broadcast_str[256] = { 0 };
|
|
||||||
uv_ip4_name(&broadcast_addr, broadcast_str, sizeof(broadcast_str));
|
|
||||||
tf_printf("failed to send broadcast for %s via %s (%d): %s\n", address_str, broadcast_str, r, uv_strerror(r));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct _seeds_t
|
typedef struct _seeds_t
|
||||||
@ -3551,9 +3563,7 @@ static void _tf_ssb_notify_broadcasts_changed(tf_ssb_t* ssb)
|
|||||||
if (node->callback)
|
if (node->callback)
|
||||||
{
|
{
|
||||||
tf_trace_begin(ssb->trace, "broadcasts changed");
|
tf_trace_begin(ssb->trace, "broadcasts changed");
|
||||||
PRE_CALLBACK(ssb, node->callback);
|
|
||||||
node->callback(ssb, node->user_data);
|
node->callback(ssb, node->user_data);
|
||||||
POST_CALLBACK(ssb, node->callback);
|
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3657,9 +3667,7 @@ void tf_ssb_visit_broadcasts(tf_ssb_t* ssb,
|
|||||||
if (node->mtime - now < 60)
|
if (node->mtime - now < 60)
|
||||||
{
|
{
|
||||||
tf_trace_begin(ssb->trace, "broadcast");
|
tf_trace_begin(ssb->trace, "broadcast");
|
||||||
PRE_CALLBACK(ssb, callback);
|
|
||||||
callback(node->host, &node->addr, node->origin, node->tunnel_connection, node->pub, user_data);
|
callback(node->host, &node->addr, node->origin, node->tunnel_connection, node->pub, user_data);
|
||||||
POST_CALLBACK(ssb, callback);
|
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3958,7 +3966,7 @@ void tf_ssb_notify_blob_stored(tf_ssb_t* ssb, const char* id)
|
|||||||
ssb->blobs_stored++;
|
ssb->blobs_stored++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, JSValue message_keys)
|
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_keys)
|
||||||
{
|
{
|
||||||
tf_ssb_message_added_callback_node_t* next = NULL;
|
tf_ssb_message_added_callback_node_t* next = NULL;
|
||||||
ssb->messages_stored++;
|
ssb->messages_stored++;
|
||||||
@ -3966,9 +3974,7 @@ void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequ
|
|||||||
{
|
{
|
||||||
next = node->next;
|
next = node->next;
|
||||||
tf_trace_begin(ssb->trace, "message added callback");
|
tf_trace_begin(ssb->trace, "message added callback");
|
||||||
PRE_CALLBACK(ssb, node->callback);
|
|
||||||
node->callback(ssb, author, sequence, id, node->user_data);
|
node->callback(ssb, author, sequence, id, node->user_data);
|
||||||
POST_CALLBACK(ssb, node->callback);
|
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4044,9 +4050,7 @@ void tf_ssb_notify_blob_want_added(tf_ssb_t* ssb, const char* id)
|
|||||||
{
|
{
|
||||||
next = node->next;
|
next = node->next;
|
||||||
tf_trace_begin(ssb->trace, "blob want added callback");
|
tf_trace_begin(ssb->trace, "blob want added callback");
|
||||||
PRE_CALLBACK(ssb, node->callback);
|
|
||||||
node->callback(ssb, id, node->user_data);
|
node->callback(ssb, id, node->user_data);
|
||||||
POST_CALLBACK(ssb, node->callback);
|
|
||||||
tf_trace_end(ssb->trace);
|
tf_trace_end(ssb->trace);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4231,12 +4235,6 @@ float tf_ssb_get_average_thread_percent(tf_ssb_t* ssb)
|
|||||||
return 100.0f * ssb->thread_busy_count / ssb->thread_busy_max;
|
return 100.0f * ssb->thread_busy_count / ssb->thread_busy_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data)
|
|
||||||
{
|
|
||||||
ssb->hitch_callback = callback;
|
|
||||||
ssb->hitch_user_data = user_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb)
|
tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb)
|
||||||
{
|
{
|
||||||
return &ssb->store_queue;
|
return &ssb->store_queue;
|
||||||
@ -4292,9 +4290,7 @@ static void _tf_ssb_connection_after_work_callback(uv_work_t* work, int status)
|
|||||||
if (data->after_work_callback)
|
if (data->after_work_callback)
|
||||||
{
|
{
|
||||||
tf_trace_begin(data->connection->ssb->trace, data->after_name);
|
tf_trace_begin(data->connection->ssb->trace, data->after_name);
|
||||||
PRE_CALLBACK(data->connection->ssb, data->after_work_callback);
|
|
||||||
data->after_work_callback(data->connection, status, data->user_data);
|
data->after_work_callback(data->connection, status, data->user_data);
|
||||||
POST_CALLBACK(data->connection->ssb, data->after_work_callback);
|
|
||||||
tf_trace_end(data->connection->ssb->trace);
|
tf_trace_end(data->connection->ssb->trace);
|
||||||
}
|
}
|
||||||
data->connection->ref_count--;
|
data->connection->ref_count--;
|
||||||
@ -4361,9 +4357,7 @@ static void _tf_ssb_after_work_callback(uv_work_t* work, int status)
|
|||||||
if (data->after_work_callback)
|
if (data->after_work_callback)
|
||||||
{
|
{
|
||||||
tf_trace_begin(data->ssb->trace, data->after_name);
|
tf_trace_begin(data->ssb->trace, data->after_name);
|
||||||
PRE_CALLBACK(data->ssb, data->after_work_callback);
|
|
||||||
data->after_work_callback(data->ssb, status, data->user_data);
|
data->after_work_callback(data->ssb, status, data->user_data);
|
||||||
POST_CALLBACK(data->ssb, data->after_work_callback);
|
|
||||||
tf_trace_end(data->ssb->trace);
|
tf_trace_end(data->ssb->trace);
|
||||||
}
|
}
|
||||||
tf_ssb_unref(data->ssb);
|
tf_ssb_unref(data->ssb);
|
||||||
@ -4485,7 +4479,7 @@ static void _tf_ssb_update_settings_after_work(tf_ssb_t* ssb, int result, void*
|
|||||||
uv_timer_start(&ssb->broadcast_timer, _tf_ssb_broadcast_timer, 2000, 2000);
|
uv_timer_start(&ssb->broadcast_timer, _tf_ssb_broadcast_timer, 2000, 2000);
|
||||||
}
|
}
|
||||||
ssb->discovery = update->discovery;
|
ssb->discovery = update->discovery;
|
||||||
snprintf(ssb->seeds_host, sizeof(ssb->seeds_host), "%s", update->seeds_host);
|
tf_string_set(ssb->seeds_host, sizeof(ssb->seeds_host), update->seeds_host);
|
||||||
_tf_ssb_start_update_settings(ssb);
|
_tf_ssb_start_update_settings(ssb);
|
||||||
tf_free(update);
|
tf_free(update);
|
||||||
}
|
}
|
||||||
@ -4526,9 +4520,7 @@ void tf_ssb_set_quiet(tf_ssb_t* ssb, bool quiet)
|
|||||||
static void _tf_ssb_scheduled_timer(uv_timer_t* handle)
|
static void _tf_ssb_scheduled_timer(uv_timer_t* handle)
|
||||||
{
|
{
|
||||||
tf_ssb_timer_t* timer = handle->data;
|
tf_ssb_timer_t* timer = handle->data;
|
||||||
PRE_CALLBACK(timer->ssb, timer->callback);
|
|
||||||
timer->callback(timer->ssb, timer->user_data);
|
timer->callback(timer->ssb, timer->user_data);
|
||||||
POST_CALLBACK(timer->ssb, timer->callback);
|
|
||||||
uv_close((uv_handle_t*)handle, _tf_ssb_on_timer_close);
|
uv_close((uv_handle_t*)handle, _tf_ssb_on_timer_close);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
|
#include "ssb.db.h"
|
||||||
#include "ssb.h"
|
#include "ssb.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
@ -51,19 +52,23 @@ static void _tf_ssb_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_change_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connections, char* host, size_t host_size, int* port, char* key, size_t key_size)
|
static bool _tf_ssb_connections_get_next_connection(
|
||||||
|
tf_ssb_connections_t* connections, char* host, size_t host_size, int* port, char* key, size_t key_size, bool* out_stay_connected)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(connections->ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(connections->ssb);
|
||||||
|
|
||||||
|
tf_ssb_db_get_global_setting_bool(db, "stay_connected", out_stay_connected);
|
||||||
|
|
||||||
if (sqlite3_prepare_v2(db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > ?1) ORDER BY last_attempt LIMIT 1",
|
if (sqlite3_prepare_v2(db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > ?1) ORDER BY last_attempt LIMIT 1",
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_int(statement, 1, 60000) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
if (sqlite3_bind_int(statement, 1, *out_stay_connected ? 15 : 60000) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
snprintf(host, host_size, "%s", sqlite3_column_text(statement, 0));
|
tf_string_set(host, host_size, (const char*)sqlite3_column_text(statement, 0));
|
||||||
*port = sqlite3_column_int(statement, 1);
|
*port = sqlite3_column_int(statement, 1);
|
||||||
snprintf(key, key_size, "%s", sqlite3_column_text(statement, 2));
|
tf_string_set(key, key_size, (const char*)sqlite3_column_text(statement, 2));
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
@ -80,6 +85,8 @@ typedef struct _tf_ssb_connections_get_next_t
|
|||||||
{
|
{
|
||||||
tf_ssb_connections_t* connections;
|
tf_ssb_connections_t* connections;
|
||||||
bool ready;
|
bool ready;
|
||||||
|
bool stay_connected;
|
||||||
|
bool full;
|
||||||
char host[256];
|
char host[256];
|
||||||
int port;
|
int port;
|
||||||
char key[k_id_base64_len];
|
char key[k_id_base64_len];
|
||||||
@ -92,7 +99,7 @@ static void _tf_ssb_connections_get_next_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
next->ready = _tf_ssb_connections_get_next_connection(next->connections, next->host, sizeof(next->host), &next->port, next->key, sizeof(next->key));
|
next->ready = _tf_ssb_connections_get_next_connection(next->connections, next->host, sizeof(next->host), &next->port, next->key, sizeof(next->key), &next->stay_connected);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_connections_get_next_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
static void _tf_ssb_connections_get_next_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
@ -100,12 +107,20 @@ static void _tf_ssb_connections_get_next_after_work(tf_ssb_t* ssb, int status, v
|
|||||||
tf_ssb_connections_get_next_t* next = user_data;
|
tf_ssb_connections_get_next_t* next = user_data;
|
||||||
if (next->ready)
|
if (next->ready)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
** Might be a duplicate connection or otherwise discarded by
|
||||||
|
** tf_ssb_connect() before we otherwise set attempted, so do it
|
||||||
|
** here.
|
||||||
|
*/
|
||||||
|
tf_ssb_connections_set_attempted(next->connections, next->host, next->port, next->key);
|
||||||
|
|
||||||
uint8_t key_bin[k_id_bin_len];
|
uint8_t key_bin[k_id_bin_len];
|
||||||
if (tf_ssb_id_str_to_bin(key_bin, next->key))
|
if (tf_ssb_id_str_to_bin(key_bin, next->key))
|
||||||
{
|
{
|
||||||
tf_ssb_connect(ssb, next->host, next->port, key_bin, k_tf_ssb_connect_flag_do_not_store, NULL, NULL);
|
tf_ssb_connect(ssb, next->host, next->port, key_bin, k_tf_ssb_connect_flag_do_not_store, NULL, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
uv_timer_set_repeat(&next->connections->timer, next->stay_connected ? (next->full ? 2000 : 200) : (next->full ? 10000 : 2000));
|
||||||
tf_free(next);
|
tf_free(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +139,7 @@ static void _tf_ssb_connections_timer(uv_timer_t* timer)
|
|||||||
tf_ssb_connections_get_next_t* next = tf_malloc(sizeof(tf_ssb_connections_get_next_t));
|
tf_ssb_connections_get_next_t* next = tf_malloc(sizeof(tf_ssb_connections_get_next_t));
|
||||||
*next = (tf_ssb_connections_get_next_t) {
|
*next = (tf_ssb_connections_get_next_t) {
|
||||||
.connections = connections,
|
.connections = connections,
|
||||||
|
.full = count + 1 == tf_countof(active),
|
||||||
};
|
};
|
||||||
tf_ssb_run_work(connections->ssb, _tf_ssb_connections_get_next_work, _tf_ssb_connections_get_next_after_work, next);
|
tf_ssb_run_work(connections->ssb, _tf_ssb_connections_get_next_work, _tf_ssb_connections_get_next_after_work, next);
|
||||||
}
|
}
|
||||||
@ -246,8 +262,8 @@ void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* hos
|
|||||||
*update = (tf_ssb_connections_update_t) {
|
*update = (tf_ssb_connections_update_t) {
|
||||||
.port = port,
|
.port = port,
|
||||||
};
|
};
|
||||||
snprintf(update->host, sizeof(update->host), "%s", host);
|
tf_string_set(update->host, sizeof(update->host), host);
|
||||||
snprintf(update->key, sizeof(update->key), "%s", key);
|
tf_string_set(update->key, sizeof(update->key), key);
|
||||||
_tf_ssb_connections_queue_update(connections, update);
|
_tf_ssb_connections_queue_update(connections, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,8 +274,8 @@ void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const c
|
|||||||
.port = port,
|
.port = port,
|
||||||
.attempted = true,
|
.attempted = true,
|
||||||
};
|
};
|
||||||
snprintf(update->host, sizeof(update->host), "%s", host);
|
tf_string_set(update->host, sizeof(update->host), host);
|
||||||
snprintf(update->key, sizeof(update->key), "%s", key);
|
tf_string_set(update->key, sizeof(update->key), key);
|
||||||
_tf_ssb_connections_queue_update(connections, update);
|
_tf_ssb_connections_queue_update(connections, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,8 +286,8 @@ void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const c
|
|||||||
.port = port,
|
.port = port,
|
||||||
.succeeded = true,
|
.succeeded = true,
|
||||||
};
|
};
|
||||||
snprintf(update->host, sizeof(update->host), "%s", host);
|
tf_string_set(update->host, sizeof(update->host), host);
|
||||||
snprintf(update->key, sizeof(update->key), "%s", key);
|
tf_string_set(update->key, sizeof(update->key), key);
|
||||||
_tf_ssb_connections_queue_update(connections, update);
|
_tf_ssb_connections_queue_update(connections, update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
165
src/ssb.db.c
@ -31,6 +31,10 @@ static int _tf_ssb_db_try_exec(sqlite3* db, const char* statement)
|
|||||||
{
|
{
|
||||||
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
|
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
sqlite3_free(error);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +45,13 @@ static void _tf_ssb_db_exec(sqlite3* db, const char* statement)
|
|||||||
if (result != SQLITE_OK)
|
if (result != SQLITE_OK)
|
||||||
{
|
{
|
||||||
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
|
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
sqlite3_free(error);
|
||||||
|
}
|
||||||
|
if (result != SQLITE_OK)
|
||||||
|
{
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,6 +87,26 @@ static int _tf_ssb_db_busy_handler(void* user_data, int count)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _tf_ssb_db_wal_hook(void* user_data, sqlite3* db, const char* db_name, int log_pages)
|
||||||
|
{
|
||||||
|
/* Keeps the log below about 64MB with default 4096 byte pages. */
|
||||||
|
if (log_pages >= 16384)
|
||||||
|
{
|
||||||
|
int log = 0;
|
||||||
|
int checkpointed = 0;
|
||||||
|
uint64_t checkpoint_start_ns = uv_hrtime();
|
||||||
|
if (sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_TRUNCATE, &log, &checkpointed) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
tf_printf("Checkpointed %d pages in %d ms. Log is now %d frames.\n", log_pages, (int)((uv_hrtime() - checkpoint_start_ns) / 1000000LL), log);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("Checkpoint: %s.\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_ssb_db_init_internal(sqlite3* db)
|
static void _tf_ssb_db_init_internal(sqlite3* db)
|
||||||
{
|
{
|
||||||
sqlite3_extended_result_codes(db, 1);
|
sqlite3_extended_result_codes(db, 1);
|
||||||
@ -93,6 +124,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
{
|
{
|
||||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||||
_tf_ssb_db_init_internal(db);
|
_tf_ssb_db_init_internal(db);
|
||||||
|
sqlite3_wal_hook(db, _tf_ssb_db_wal_hook, NULL);
|
||||||
|
|
||||||
sqlite3_stmt* statement = NULL;
|
sqlite3_stmt* statement = NULL;
|
||||||
int auto_vacuum = 0;
|
int auto_vacuum = 0;
|
||||||
@ -200,6 +232,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_size_by_author_index ON messages (author, length(content))");
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_size_by_author_index ON messages (author, length(content))");
|
||||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_timestamp_index ON messages (timestamp)");
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_timestamp_index ON messages (timestamp)");
|
||||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_type_timestamp_index ON messages (content ->> 'type', timestamp)");
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_type_timestamp_index ON messages (content ->> 'type', timestamp)");
|
||||||
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_type_root_index ON messages (author, content ->> 'type', content ->> 'root')");
|
||||||
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_author_id_index");
|
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_author_id_index");
|
||||||
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_by_author_index");
|
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_by_author_index");
|
||||||
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_id_index");
|
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_id_index");
|
||||||
@ -277,6 +310,13 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES ('delete', old.rowid, "
|
"CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN INSERT INTO messages_fts(messages_fts, rowid, content) VALUES ('delete', old.rowid, "
|
||||||
"old.content); END");
|
"old.content); END");
|
||||||
|
|
||||||
|
if (_tf_ssb_db_has_rows(db, "SELECT * FROM sqlite_schema WHERE type = 'trigger' AND name = 'messages_ai_refs' AND NOT sql LIKE '%ltrim%'"))
|
||||||
|
{
|
||||||
|
tf_printf("Deleting incorrect messages_refs...\n");
|
||||||
|
_tf_ssb_db_exec(db, "DROP TABLE IF EXISTS messages_refs");
|
||||||
|
tf_printf("Done.\n");
|
||||||
|
}
|
||||||
|
|
||||||
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_refs')"))
|
if (!_tf_ssb_db_has_rows(db, "PRAGMA table_list('messages_refs')"))
|
||||||
{
|
{
|
||||||
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||||
@ -291,8 +331,9 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"INSERT INTO messages_refs(message, ref) "
|
"INSERT INTO messages_refs(message, ref) "
|
||||||
"SELECT messages.id, j.value FROM messages, json_tree(messages.content) AS j WHERE "
|
"SELECT messages.id, j.value FROM messages, json_tree(messages.content) AS j WHERE "
|
||||||
"j.value LIKE '&%.sha256' OR "
|
"j.value LIKE '&%.sha256' OR "
|
||||||
"j.value LIKE '%%%.sha256' OR "
|
"j.value LIKE '!%%.sha256' ESCAPE '!' OR "
|
||||||
"j.value LIKE '@%.ed25519' "
|
"j.value LIKE '@%.ed25519' OR "
|
||||||
|
"(j.value LIKE '#%' AND ltrim(substr(j.value, 2), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') = '') "
|
||||||
"ON CONFLICT DO NOTHING");
|
"ON CONFLICT DO NOTHING");
|
||||||
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||||
tf_printf("Done.\n");
|
tf_printf("Done.\n");
|
||||||
@ -304,8 +345,9 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
"INSERT INTO messages_refs(message, ref) "
|
"INSERT INTO messages_refs(message, ref) "
|
||||||
"SELECT DISTINCT new.id, j.value FROM json_tree(new.content) AS j WHERE "
|
"SELECT DISTINCT new.id, j.value FROM json_tree(new.content) AS j WHERE "
|
||||||
"j.value LIKE '&%.sha256' OR "
|
"j.value LIKE '&%.sha256' OR "
|
||||||
"j.value LIKE '%%%.sha256' OR "
|
"j.value LIKE '!%%.sha256' ESCAPE '!' OR "
|
||||||
"j.value LIKE '@%.ed25519' "
|
"j.value LIKE '@%.ed25519' OR "
|
||||||
|
"(j.value LIKE '#%' AND ltrim(substr(j.value, 2), 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_') = '') "
|
||||||
"ON CONFLICT DO NOTHING; END");
|
"ON CONFLICT DO NOTHING; END");
|
||||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad_refs");
|
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad_refs");
|
||||||
_tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ad_refs AFTER DELETE ON messages BEGIN DELETE FROM messages_refs WHERE messages_refs.message = old.id; END");
|
_tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ad_refs AFTER DELETE ON messages BEGIN DELETE FROM messages_refs WHERE messages_refs.message = old.id; END");
|
||||||
@ -454,7 +496,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
tf_ssb_release_db_writer(ssb, db);
|
tf_ssb_release_db_writer(ssb, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int64_t sequence, const char* previous, bool* out_id_mismatch)
|
static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author, int32_t sequence, const char* previous, bool* out_id_mismatch)
|
||||||
{
|
{
|
||||||
bool exists = false;
|
bool exists = false;
|
||||||
if (sequence == 1)
|
if (sequence == 1)
|
||||||
@ -466,7 +508,7 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
|
|||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
if (sqlite3_prepare_v2(db, "SELECT COUNT(*), id != ?3 AS is_mismatch FROM messages WHERE author = ?1 AND sequence = ?2", -1, &statement, NULL) == SQLITE_OK)
|
if (sqlite3_prepare_v2(db, "SELECT COUNT(*), id != ?3 AS is_mismatch FROM messages WHERE author = ?1 AND sequence = ?2", -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, sequence - 1) == SQLITE_OK &&
|
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 2, sequence - 1) == SQLITE_OK &&
|
||||||
sqlite3_bind_text(statement, 3, previous, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
sqlite3_bind_text(statement, 3, previous, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
exists = sqlite3_column_int(statement, 0) != 0;
|
exists = sqlite3_column_int(statement, 0) != 0;
|
||||||
@ -478,7 +520,7 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
|
|||||||
return exists;
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const char* previous, const char* author, int64_t sequence, double timestamp, const char* content,
|
static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const char* previous, const char* author, int32_t sequence, double timestamp, const char* content,
|
||||||
size_t content_len, const char* signature, int flags)
|
size_t content_len, const char* signature, int flags)
|
||||||
{
|
{
|
||||||
int64_t last_row_id = -1;
|
int64_t last_row_id = -1;
|
||||||
@ -493,7 +535,7 @@ static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const c
|
|||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||||
(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK &&
|
(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK &&
|
||||||
sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 4, sequence) == SQLITE_OK &&
|
sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 4, sequence) == SQLITE_OK &&
|
||||||
sqlite3_bind_double(statement, 5, timestamp) == SQLITE_OK && sqlite3_bind_text(statement, 6, content, content_len, NULL) == SQLITE_OK &&
|
sqlite3_bind_double(statement, 5, timestamp) == SQLITE_OK && sqlite3_bind_text(statement, 6, content, content_len, NULL) == SQLITE_OK &&
|
||||||
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK &&
|
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK &&
|
||||||
sqlite3_bind_int(statement, 9, flags) == SQLITE_OK)
|
sqlite3_bind_int(statement, 9, flags) == SQLITE_OK)
|
||||||
@ -527,7 +569,7 @@ static int64_t _tf_ssb_db_store_message_raw(sqlite3* db, const char* id, const c
|
|||||||
** message when trying to receive what we don't have, and
|
** message when trying to receive what we don't have, and
|
||||||
** that's not helping anybody.
|
** that's not helping anybody.
|
||||||
*/
|
*/
|
||||||
tf_printf("%p: Previous message doesn't exist for author=%s sequence=%" PRId64 " previous=%s.\n", db, author, sequence, previous);
|
tf_printf("%p: Previous message doesn't exist for author=%s sequence=%d previous=%s.\n", db, author, sequence, previous);
|
||||||
}
|
}
|
||||||
return last_row_id;
|
return last_row_id;
|
||||||
}
|
}
|
||||||
@ -541,7 +583,7 @@ static char* _tf_ssb_db_get_message_blob_wants(tf_ssb_t* ssb, int64_t rowid)
|
|||||||
|
|
||||||
if (sqlite3_prepare_v2(db,
|
if (sqlite3_prepare_v2(db,
|
||||||
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
|
"SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND "
|
||||||
"json.value LIKE '&%%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL",
|
"json.value LIKE '&%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL",
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
if (sqlite3_bind_int64(statement, 1, rowid) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
||||||
@ -584,7 +626,7 @@ typedef struct _message_store_t
|
|||||||
int flags;
|
int flags;
|
||||||
char previous[k_id_base64_len];
|
char previous[k_id_base64_len];
|
||||||
char author[k_id_base64_len];
|
char author[k_id_base64_len];
|
||||||
int64_t sequence;
|
int32_t sequence;
|
||||||
double timestamp;
|
double timestamp;
|
||||||
const char* content;
|
const char* content;
|
||||||
size_t length;
|
size_t length;
|
||||||
@ -720,9 +762,9 @@ void tf_ssb_db_store_message(
|
|||||||
const char* author = JS_ToCString(context, authorval);
|
const char* author = JS_ToCString(context, authorval);
|
||||||
JS_FreeValue(context, authorval);
|
JS_FreeValue(context, authorval);
|
||||||
|
|
||||||
int64_t sequence = -1;
|
int32_t sequence = -1;
|
||||||
JSValue sequenceval = JS_GetPropertyStr(context, val, "sequence");
|
JSValue sequenceval = JS_GetPropertyStr(context, val, "sequence");
|
||||||
JS_ToInt64(context, &sequence, sequenceval);
|
JS_ToInt32(context, &sequence, sequenceval);
|
||||||
JS_FreeValue(context, sequenceval);
|
JS_FreeValue(context, sequenceval);
|
||||||
|
|
||||||
double timestamp = -1.0;
|
double timestamp = -1.0;
|
||||||
@ -748,10 +790,10 @@ void tf_ssb_db_store_message(
|
|||||||
.callback = callback,
|
.callback = callback,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
};
|
};
|
||||||
snprintf(store->id, sizeof(store->id), "%s", id);
|
tf_string_set(store->id, sizeof(store->id), id);
|
||||||
snprintf(store->previous, sizeof(store->previous), "%s", previous ? previous : "");
|
tf_string_set(store->previous, sizeof(store->previous), previous ? previous : "");
|
||||||
snprintf(store->author, sizeof(store->author), "%s", author);
|
tf_string_set(store->author, sizeof(store->author), author);
|
||||||
snprintf(store->signature, sizeof(store->signature), "%s", signature);
|
tf_string_set(store->signature, sizeof(store->signature), signature);
|
||||||
JS_FreeCString(context, author);
|
JS_FreeCString(context, author);
|
||||||
JS_FreeCString(context, previous);
|
JS_FreeCString(context, previous);
|
||||||
|
|
||||||
@ -908,7 +950,7 @@ void tf_ssb_db_blob_get_async(tf_ssb_t* ssb, const char* id, tf_ssb_db_blob_get_
|
|||||||
.callback = callback,
|
.callback = callback,
|
||||||
.user_data = user_data,
|
.user_data = user_data,
|
||||||
};
|
};
|
||||||
snprintf(async->id, sizeof(async->id), "%s", id);
|
tf_string_set(async->id, sizeof(async->id), id);
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_db_blob_get_async_work, _tf_ssb_db_blob_get_async_after_work, async);
|
tf_ssb_run_work(ssb, _tf_ssb_db_blob_get_async_work, _tf_ssb_db_blob_get_async_after_work, async);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1005,7 +1047,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
|
|||||||
|
|
||||||
if (result && out_id)
|
if (result && out_id)
|
||||||
{
|
{
|
||||||
snprintf(out_id, out_id_size, "%s", id);
|
tf_string_set(out_id, out_id_size, id);
|
||||||
}
|
}
|
||||||
if (out_new)
|
if (out_new)
|
||||||
{
|
{
|
||||||
@ -1014,7 +1056,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int32_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||||
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags)
|
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@ -1023,11 +1065,11 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
|||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
if (sqlite3_prepare_v2(db, query, -1, &statement, NULL) == SQLITE_OK)
|
if (sqlite3_prepare_v2(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, sequence) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 2, sequence) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
if (out_message_id)
|
if (out_message_id)
|
||||||
{
|
{
|
||||||
snprintf(out_message_id, out_message_id_size, "%s", (const char*)sqlite3_column_text(statement, 0));
|
tf_string_set(out_message_id, out_message_id_size, (const char*)sqlite3_column_text(statement, 0));
|
||||||
}
|
}
|
||||||
if (out_previous)
|
if (out_previous)
|
||||||
{
|
{
|
||||||
@ -1040,7 +1082,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf(out_previous, out_previous_size, "%s", (const char*)sqlite3_column_text(statement, 1));
|
tf_string_set(out_previous, out_previous_size, (const char*)sqlite3_column_text(statement, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out_timestamp)
|
if (out_timestamp)
|
||||||
@ -1053,11 +1095,11 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
|||||||
}
|
}
|
||||||
if (out_hash)
|
if (out_hash)
|
||||||
{
|
{
|
||||||
snprintf(out_hash, out_hash_size, "%s", (const char*)sqlite3_column_text(statement, 4));
|
tf_string_set(out_hash, out_hash_size, (const char*)sqlite3_column_text(statement, 4));
|
||||||
}
|
}
|
||||||
if (out_signature)
|
if (out_signature)
|
||||||
{
|
{
|
||||||
snprintf(out_signature, out_signature_size, "%s", (const char*)sqlite3_column_text(statement, 5));
|
tf_string_set(out_signature, out_signature_size, (const char*)sqlite3_column_text(statement, 5));
|
||||||
}
|
}
|
||||||
if (out_flags)
|
if (out_flags)
|
||||||
{
|
{
|
||||||
@ -1075,7 +1117,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int64_t* out_sequence, char* out_message_id, size_t out_message_id_size)
|
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int32_t* out_sequence, char* out_message_id, size_t out_message_id_size)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
@ -1090,7 +1132,7 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
|||||||
{
|
{
|
||||||
if (out_sequence)
|
if (out_sequence)
|
||||||
{
|
{
|
||||||
*out_sequence = sqlite3_column_int64(statement, 1);
|
*out_sequence = sqlite3_column_int(statement, 1);
|
||||||
}
|
}
|
||||||
if (out_message_id)
|
if (out_message_id)
|
||||||
{
|
{
|
||||||
@ -1114,7 +1156,7 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
|||||||
{
|
{
|
||||||
if (out_sequence)
|
if (out_sequence)
|
||||||
{
|
{
|
||||||
*out_sequence = sqlite3_column_int64(statement, 0);
|
*out_sequence = sqlite3_column_int(statement, 0);
|
||||||
}
|
}
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@ -1292,19 +1334,19 @@ JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue bi
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSValue tf_ssb_format_message(
|
JSValue tf_ssb_format_message(
|
||||||
JSContext* context, const char* previous, const char* author, int64_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags)
|
JSContext* context, const char* previous, const char* author, int32_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags)
|
||||||
{
|
{
|
||||||
JSValue value = JS_NewObject(context);
|
JSValue value = JS_NewObject(context);
|
||||||
JS_SetPropertyStr(context, value, "previous", (previous && *previous) ? JS_NewString(context, previous) : JS_NULL);
|
JS_SetPropertyStr(context, value, "previous", (previous && *previous) ? JS_NewString(context, previous) : JS_NULL);
|
||||||
if (flags & k_tf_ssb_message_flag_sequence_before_author)
|
if (flags & k_tf_ssb_message_flag_sequence_before_author)
|
||||||
{
|
{
|
||||||
JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence));
|
JS_SetPropertyStr(context, value, "sequence", JS_NewInt32(context, sequence));
|
||||||
JS_SetPropertyStr(context, value, "author", JS_NewString(context, author));
|
JS_SetPropertyStr(context, value, "author", JS_NewString(context, author));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JS_SetPropertyStr(context, value, "author", JS_NewString(context, author));
|
JS_SetPropertyStr(context, value, "author", JS_NewString(context, author));
|
||||||
JS_SetPropertyStr(context, value, "sequence", JS_NewInt64(context, sequence));
|
JS_SetPropertyStr(context, value, "sequence", JS_NewInt32(context, sequence));
|
||||||
}
|
}
|
||||||
JS_SetPropertyStr(context, value, "timestamp", JS_NewFloat64(context, timestamp));
|
JS_SetPropertyStr(context, value, "timestamp", JS_NewFloat64(context, timestamp));
|
||||||
JS_SetPropertyStr(context, value, "hash", JS_NewString(context, hash));
|
JS_SetPropertyStr(context, value, "hash", JS_NewString(context, hash));
|
||||||
@ -1511,14 +1553,14 @@ typedef struct _following_t following_t;
|
|||||||
typedef struct _following_t
|
typedef struct _following_t
|
||||||
{
|
{
|
||||||
char id[k_id_base64_len];
|
char id[k_id_base64_len];
|
||||||
|
bool populated;
|
||||||
|
int depth;
|
||||||
following_t** following;
|
following_t** following;
|
||||||
following_t** blocking;
|
following_t** blocking;
|
||||||
int following_count;
|
int following_count;
|
||||||
int blocking_count;
|
int blocking_count;
|
||||||
int depth;
|
|
||||||
int ref_count;
|
int ref_count;
|
||||||
int block_ref_count;
|
int block_ref_count;
|
||||||
bool populated;
|
|
||||||
} following_t;
|
} following_t;
|
||||||
|
|
||||||
static int _following_compare(const void* a, const void* b)
|
static int _following_compare(const void* a, const void* b)
|
||||||
@ -1610,7 +1652,7 @@ static following_t* _make_following_node(const char* id, following_t*** followin
|
|||||||
(*following_count)++;
|
(*following_count)++;
|
||||||
memset(entry, 0, sizeof(*entry));
|
memset(entry, 0, sizeof(*entry));
|
||||||
entry->depth = INT_MAX;
|
entry->depth = INT_MAX;
|
||||||
snprintf(entry->id, sizeof(entry->id), "%s", id);
|
tf_string_set(entry->id, sizeof(entry->id), id);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@ -1704,7 +1746,7 @@ static sqlite3_stmt* _make_following_statement(sqlite3* db)
|
|||||||
"SELECT content ->> '$.contact' AS contact, content ->> '$.following', content ->> '$.blocking' "
|
"SELECT content ->> '$.contact' AS contact, content ->> '$.following', content ->> '$.blocking' "
|
||||||
"FROM messages "
|
"FROM messages "
|
||||||
"WHERE author = ? AND content ->> '$.type' = 'contact' AND contact IS NOT NULL "
|
"WHERE author = ? AND content ->> '$.type' = 'contact' AND contact IS NOT NULL "
|
||||||
"ORDER BY content ->> '$.contact', sequence",
|
"ORDER BY sequence",
|
||||||
-1, &statement, NULL) != SQLITE_OK)
|
-1, &statement, NULL) != SQLITE_OK)
|
||||||
{
|
{
|
||||||
tf_printf("prepare failed: %s", sqlite3_errmsg(db));
|
tf_printf("prepare failed: %s", sqlite3_errmsg(db));
|
||||||
@ -1737,22 +1779,23 @@ tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, in
|
|||||||
}
|
}
|
||||||
|
|
||||||
tf_ssb_following_t* result = tf_malloc(sizeof(tf_ssb_following_t) * (actual_following_count + 1));
|
tf_ssb_following_t* result = tf_malloc(sizeof(tf_ssb_following_t) * (actual_following_count + 1));
|
||||||
memset(result, 0, sizeof(tf_ssb_following_t) * (actual_following_count + 1));
|
|
||||||
|
|
||||||
int write_index = 0;
|
int write_index = 0;
|
||||||
for (int i = 0; i < following_count; i++)
|
for (int i = 0; i < following_count; i++)
|
||||||
{
|
{
|
||||||
if (following[i]->ref_count > 0 || include_blocks)
|
if (following[i]->ref_count > 0 || include_blocks)
|
||||||
{
|
{
|
||||||
snprintf(result[write_index].id, sizeof(result[write_index].id), "%s", following[i]->id);
|
result[write_index] = (tf_ssb_following_t) {
|
||||||
result[write_index].following_count = following[i]->following_count;
|
.following_count = following[i]->following_count,
|
||||||
result[write_index].blocking_count = following[i]->blocking_count;
|
.blocking_count = following[i]->blocking_count,
|
||||||
result[write_index].followed_by_count = following[i]->ref_count;
|
.followed_by_count = following[i]->ref_count,
|
||||||
result[write_index].blocked_by_count = following[i]->block_ref_count;
|
.blocked_by_count = following[i]->block_ref_count,
|
||||||
result[write_index].depth = following[i]->depth;
|
.depth = following[i]->depth,
|
||||||
|
};
|
||||||
|
tf_string_set(result[write_index].id, sizeof(result[write_index].id), following[i]->id);
|
||||||
write_index++;
|
write_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result[write_index] = (tf_ssb_following_t) { 0 };
|
||||||
|
|
||||||
for (int i = 0; i < following_count; i++)
|
for (int i = 0; i < following_count; i++)
|
||||||
{
|
{
|
||||||
@ -1799,7 +1842,7 @@ const char** tf_ssb_db_following_deep_ids(tf_ssb_t* ssb, const char** ids, int c
|
|||||||
if (following[i]->ref_count > 0)
|
if (following[i]->ref_count > 0)
|
||||||
{
|
{
|
||||||
result[write_index] = result_ids + k_id_base64_len * write_index;
|
result[write_index] = result_ids + k_id_base64_len * write_index;
|
||||||
snprintf(result[write_index], k_id_base64_len, "%s", following[i]->id);
|
tf_string_set(result[write_index], k_id_base64_len, following[i]->id);
|
||||||
write_index++;
|
write_index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1859,7 +1902,7 @@ JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys)
|
|||||||
{
|
{
|
||||||
JSValue message = JS_UNDEFINED;
|
JSValue message = JS_UNDEFINED;
|
||||||
JSValue formatted = tf_ssb_format_message(context, (const char*)sqlite3_column_text(statement, 0), (const char*)sqlite3_column_text(statement, 1),
|
JSValue formatted = tf_ssb_format_message(context, (const char*)sqlite3_column_text(statement, 0), (const char*)sqlite3_column_text(statement, 1),
|
||||||
sqlite3_column_int64(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
|
sqlite3_column_int(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
|
||||||
(const char*)sqlite3_column_text(statement, 6), (const char*)sqlite3_column_text(statement, 7), sqlite3_column_int(statement, 8));
|
(const char*)sqlite3_column_text(statement, 6), (const char*)sqlite3_column_text(statement, 7), sqlite3_column_int(statement, 8));
|
||||||
if (is_keys)
|
if (is_keys)
|
||||||
{
|
{
|
||||||
@ -1888,16 +1931,18 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
if (sqlite3_prepare_v2(db, "SELECT host, port, key FROM connections ORDER BY host, port, key", -1, &statement, NULL) == SQLITE_OK)
|
if (sqlite3_prepare_v2(db, "SELECT host, port, key, last_attempt, last_success FROM connections ORDER BY host, port, key", -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
result = tf_resize_vec(result, sizeof(tf_ssb_db_stored_connection_t) * (count + 1));
|
result = tf_resize_vec(result, sizeof(tf_ssb_db_stored_connection_t) * (count + 1));
|
||||||
result[count] = (tf_ssb_db_stored_connection_t) {
|
result[count] = (tf_ssb_db_stored_connection_t) {
|
||||||
.port = sqlite3_column_int(statement, 1),
|
.port = sqlite3_column_int(statement, 1),
|
||||||
|
.last_attempt = sqlite3_column_int64(statement, 3),
|
||||||
|
.last_success = sqlite3_column_int64(statement, 4),
|
||||||
};
|
};
|
||||||
snprintf(result[count].address, sizeof(result[count].address), "%s", (const char*)sqlite3_column_text(statement, 0));
|
tf_string_set(result[count].address, sizeof(result[count].address), (const char*)sqlite3_column_text(statement, 0));
|
||||||
snprintf(result[count].pubkey, sizeof(result[count].pubkey), "%s", (const char*)sqlite3_column_text(statement, 2));
|
tf_string_set(result[count].pubkey, sizeof(result[count].pubkey), (const char*)sqlite3_column_text(statement, 2));
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
@ -1935,7 +1980,7 @@ bool tf_ssb_db_get_account_password_hash(tf_ssb_t* ssb, const char* name, char*
|
|||||||
{
|
{
|
||||||
if (sqlite3_step(statement) == SQLITE_ROW)
|
if (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
snprintf(out_password, password_size, "%s", (const char*)sqlite3_column_text(statement, 0));
|
tf_string_set(out_password, password_size, (const char*)sqlite3_column_text(statement, 0));
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2133,7 +2178,7 @@ bool tf_ssb_db_identity_get_active(sqlite3* db, const char* user, const char* pa
|
|||||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, package_owner, -1, NULL) == SQLITE_OK &&
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, package_owner, -1, NULL) == SQLITE_OK &&
|
||||||
sqlite3_bind_text(statement, 3, package_name, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
sqlite3_bind_text(statement, 3, package_name, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
snprintf(out_identity, out_identity_size, "%s", (const char*)sqlite3_column_text(statement, 0));
|
tf_string_set(out_identity, out_identity_size, (const char*)sqlite3_column_text(statement, 0));
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
@ -2245,14 +2290,14 @@ static void _tf_ssb_db_set_flags(tf_ssb_t* ssb, const char* message_id, int flag
|
|||||||
tf_ssb_release_db_writer(ssb, db);
|
tf_ssb_release_db_writer(ssb, db);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, bool fix)
|
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int32_t debug_sequence, bool fix)
|
||||||
{
|
{
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
bool verified = true;
|
bool verified = true;
|
||||||
int64_t sequence = -1;
|
int32_t sequence = -1;
|
||||||
if (tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0))
|
if (tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0))
|
||||||
{
|
{
|
||||||
for (int64_t i = 1; i <= sequence; i++)
|
for (int32_t i = 1; i <= sequence; i++)
|
||||||
{
|
{
|
||||||
char message_id[k_id_base64_len];
|
char message_id[k_id_base64_len];
|
||||||
char previous[256];
|
char previous[256];
|
||||||
@ -2271,12 +2316,12 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, boo
|
|||||||
if (!tf_ssb_verify_and_strip_signature(context, message, i == debug_sequence ? k_tf_ssb_verify_flag_debug : 0, calculated_id, sizeof(calculated_id),
|
if (!tf_ssb_verify_and_strip_signature(context, message, i == debug_sequence ? k_tf_ssb_verify_flag_debug : 0, calculated_id, sizeof(calculated_id),
|
||||||
extracted_signature, sizeof(extracted_signature), &calculated_flags))
|
extracted_signature, sizeof(extracted_signature), &calculated_flags))
|
||||||
{
|
{
|
||||||
tf_printf("author=%s sequence=%" PRId64 " verify failed.\n", id, i);
|
tf_printf("author=%s sequence=%d verify failed.\n", id, i);
|
||||||
verified = false;
|
verified = false;
|
||||||
}
|
}
|
||||||
if (calculated_flags != flags)
|
if (calculated_flags != flags)
|
||||||
{
|
{
|
||||||
tf_printf("author=%s sequence=%" PRId64 " flag mismatch %d => %d.\n", id, i, flags, calculated_flags);
|
tf_printf("author=%s sequence=%d flag mismatch %d => %d.\n", id, i, flags, calculated_flags);
|
||||||
if (fix)
|
if (fix)
|
||||||
{
|
{
|
||||||
_tf_ssb_db_set_flags(ssb, message_id, calculated_flags);
|
_tf_ssb_db_set_flags(ssb, message_id, calculated_flags);
|
||||||
@ -2288,7 +2333,7 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, boo
|
|||||||
}
|
}
|
||||||
if (strcmp(message_id, calculated_id))
|
if (strcmp(message_id, calculated_id))
|
||||||
{
|
{
|
||||||
tf_printf("author=%s sequence=%" PRId64 " id mismatch %s => %s.\n", id, i, message_id, calculated_id);
|
tf_printf("author=%s sequence=%d id mismatch %s => %s.\n", id, i, message_id, calculated_id);
|
||||||
verified = false;
|
verified = false;
|
||||||
}
|
}
|
||||||
JS_FreeValue(context, message);
|
JS_FreeValue(context, message);
|
||||||
@ -2301,7 +2346,7 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, boo
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_printf("Unable to find message with sequence=%" PRId64 " for author=%s.", i, id);
|
tf_printf("Unable to find message with sequence=%d for author=%s.", i, id);
|
||||||
verified = false;
|
verified = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2403,7 +2448,7 @@ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* ou
|
|||||||
{
|
{
|
||||||
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
|
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
|
||||||
{
|
{
|
||||||
snprintf(out_value, size, "%s", sqlite3_column_text(statement, 0));
|
tf_string_set(out_value, size, (const char*)sqlite3_column_text(statement, 0));
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2415,7 +2460,7 @@ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* ou
|
|||||||
}
|
}
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
snprintf(out_value, size, "%s", tf_util_get_default_global_setting_string(name));
|
tf_string_set(out_value, size, tf_util_get_default_global_setting_string(name));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -2697,7 +2742,7 @@ tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* u
|
|||||||
tf_ssb_db_identity_get_active(db, user, package_owner, package_name, info->active_identity, sizeof(info->active_identity));
|
tf_ssb_db_identity_get_active(db, user, package_owner, package_name, info->active_identity, sizeof(info->active_identity));
|
||||||
if (!*info->active_identity && info->count)
|
if (!*info->active_identity && info->count)
|
||||||
{
|
{
|
||||||
snprintf(info->active_identity, sizeof(info->active_identity), "%s", info->identity[0]);
|
tf_string_set(info->active_identity, sizeof(info->active_identity), info->identity[0]);
|
||||||
}
|
}
|
||||||
tf_ssb_release_db_reader(ssb, db);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
|
||||||
|
12
src/ssb.db.h
@ -151,7 +151,7 @@ JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys)
|
|||||||
** @param[out] out_flags Populated with flags describing the format of the message.
|
** @param[out] out_flags Populated with flags describing the format of the message.
|
||||||
** @return True if the message was found and retrieved.
|
** @return True if the message was found and retrieved.
|
||||||
*/
|
*/
|
||||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int32_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||||
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags);
|
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -163,7 +163,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
|||||||
** @param out_message_id_size The size of the out_message_id buffer.
|
** @param out_message_id_size The size of the out_message_id buffer.
|
||||||
** @return True if the message was found and information was retrieved.
|
** @return True if the message was found and information was retrieved.
|
||||||
*/
|
*/
|
||||||
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int64_t* out_sequence, char* out_message_id, size_t out_message_id_size);
|
bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, int32_t* out_sequence, char* out_message_id, size_t out_message_id_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Call a function for each result row of an SQL query.
|
** Call a function for each result row of an SQL query.
|
||||||
@ -281,7 +281,7 @@ bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const c
|
|||||||
** @param flags tf_ssb_message_flags_t describing the message.
|
** @param flags tf_ssb_message_flags_t describing the message.
|
||||||
*/
|
*/
|
||||||
JSValue tf_ssb_format_message(
|
JSValue tf_ssb_format_message(
|
||||||
JSContext* context, const char* previous, const char* author, int64_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags);
|
JSContext* context, const char* previous, const char* author, int32_t sequence, double timestamp, const char* hash, const char* content, const char* signature, int flags);
|
||||||
|
|
||||||
/** Information about a single followed account. */
|
/** Information about a single followed account. */
|
||||||
typedef struct _tf_ssb_following_t
|
typedef struct _tf_ssb_following_t
|
||||||
@ -340,6 +340,10 @@ typedef struct _tf_ssb_db_stored_connection_t
|
|||||||
int port;
|
int port;
|
||||||
/** The identity. */
|
/** The identity. */
|
||||||
char pubkey[k_id_base64_len];
|
char pubkey[k_id_base64_len];
|
||||||
|
/** Time of last attempted connection. */
|
||||||
|
int64_t last_attempt;
|
||||||
|
/** Time of last successful connection. */
|
||||||
|
int64_t last_success;
|
||||||
} tf_ssb_db_stored_connection_t;
|
} tf_ssb_db_stored_connection_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -456,7 +460,7 @@ void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callb
|
|||||||
** @param fix Fix invalid messages when possible.
|
** @param fix Fix invalid messages when possible.
|
||||||
** @return true If the feed verified successfully.
|
** @return true If the feed verified successfully.
|
||||||
*/
|
*/
|
||||||
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, bool fix);
|
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int32_t debug_sequence, bool fix);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Check if a user has a specific permission.
|
** Check if a user has a specific permission.
|
||||||
|
@ -102,7 +102,7 @@ static ebt_entry_t* _ebt_get_entry(tf_ssb_ebt_t* ebt, const char* id)
|
|||||||
.in = -1,
|
.in = -1,
|
||||||
.out = -1,
|
.out = -1,
|
||||||
};
|
};
|
||||||
snprintf(ebt->entries[index].id, sizeof(ebt->entries[index].id), "%s", id);
|
tf_string_set(ebt->entries[index].id, sizeof(ebt->entries[index].id), id);
|
||||||
ebt->entries_count++;
|
ebt->entries_count++;
|
||||||
return &ebt->entries[index];
|
return &ebt->entries[index];
|
||||||
}
|
}
|
||||||
@ -127,10 +127,9 @@ void tf_ssb_ebt_receive_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue clo
|
|||||||
}
|
}
|
||||||
if (!JS_IsUndefined(in_clock))
|
if (!JS_IsUndefined(in_clock))
|
||||||
{
|
{
|
||||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
const char* author = JS_AtomToCString(context, ptab[i].atom);
|
||||||
const char* author = JS_ToCString(context, key);
|
int32_t sequence = -1;
|
||||||
int64_t sequence = -1;
|
JS_ToInt32(context, &sequence, in_clock);
|
||||||
JS_ToInt64(context, &sequence, in_clock);
|
|
||||||
|
|
||||||
ebt_entry_t* entry = _ebt_get_entry(ebt, author);
|
ebt_entry_t* entry = _ebt_get_entry(ebt, author);
|
||||||
if (entry)
|
if (entry)
|
||||||
@ -153,7 +152,6 @@ void tf_ssb_ebt_receive_clock(tf_ssb_ebt_t* ebt, JSContext* context, JSValue clo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, author);
|
JS_FreeCString(context, author);
|
||||||
JS_FreeValue(context, key);
|
|
||||||
}
|
}
|
||||||
JS_FreeValue(context, in_clock);
|
JS_FreeValue(context, in_clock);
|
||||||
}
|
}
|
||||||
@ -212,7 +210,7 @@ static void _ebt_add_to_clock(ebt_get_clock_t* work, const char* id, int64_t val
|
|||||||
memmove(work->clock->entries + index + 1, work->clock->entries + index, (count - index) * sizeof(tf_ssb_ebt_clock_entry_t));
|
memmove(work->clock->entries + index + 1, work->clock->entries + index, (count - index) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||||
}
|
}
|
||||||
work->clock->entries[index] = (tf_ssb_ebt_clock_entry_t) { .value = out_value };
|
work->clock->entries[index] = (tf_ssb_ebt_clock_entry_t) { .value = out_value };
|
||||||
snprintf(work->clock->entries[index].id, sizeof(work->clock->entries[index].id), "%s", id);
|
tf_string_set(work->clock->entries[index].id, sizeof(work->clock->entries[index].id), id);
|
||||||
work->clock->count = count + 1;
|
work->clock->count = count + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -237,12 +235,12 @@ static void _tf_ssb_ebt_get_send_clock_work(tf_ssb_connection_t* connection, voi
|
|||||||
const char** visible = tf_ssb_db_get_all_visible_identities(ssb, depth);
|
const char** visible = tf_ssb_db_get_all_visible_identities(ssb, depth);
|
||||||
if (visible)
|
if (visible)
|
||||||
{
|
{
|
||||||
int64_t* sequences = NULL;
|
int32_t* sequences = NULL;
|
||||||
for (int i = 0; visible[i]; i++)
|
for (int i = 0; visible[i]; i++)
|
||||||
{
|
{
|
||||||
int64_t sequence = 0;
|
int32_t sequence = 0;
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb, visible[i], &sequence, NULL, 0);
|
tf_ssb_db_get_latest_message_by_author(ssb, visible[i], &sequence, NULL, 0);
|
||||||
sequences = tf_resize_vec(sequences, (i + 1) * sizeof(int64_t));
|
sequences = tf_resize_vec(sequences, (i + 1) * sizeof(int32_t));
|
||||||
sequences[i] = sequence;
|
sequences[i] = sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +259,7 @@ static void _tf_ssb_ebt_get_send_clock_work(tf_ssb_connection_t* connection, voi
|
|||||||
char id[k_id_base64_len] = "";
|
char id[k_id_base64_len] = "";
|
||||||
if (tf_ssb_connection_get_id(connection, id, sizeof(id)))
|
if (tf_ssb_connection_get_id(connection, id, sizeof(id)))
|
||||||
{
|
{
|
||||||
int64_t sequence = 0;
|
int32_t sequence = 0;
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0);
|
tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0);
|
||||||
uv_mutex_lock(&work->ebt->mutex);
|
uv_mutex_lock(&work->ebt->mutex);
|
||||||
_ebt_add_to_clock(work, id, sequence, true, true);
|
_ebt_add_to_clock(work, id, sequence, true, true);
|
||||||
@ -279,7 +277,7 @@ static void _tf_ssb_ebt_get_send_clock_work(tf_ssb_connection_t* connection, voi
|
|||||||
{
|
{
|
||||||
requested = tf_resize_vec(requested, (requested_count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
requested = tf_resize_vec(requested, (requested_count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||||
requested[requested_count] = (tf_ssb_ebt_clock_entry_t) { .value = -1 };
|
requested[requested_count] = (tf_ssb_ebt_clock_entry_t) { .value = -1 };
|
||||||
snprintf(requested[requested_count].id, sizeof(requested[requested_count].id), "%s", entry->id);
|
tf_string_set(requested[requested_count].id, sizeof(requested[requested_count].id), entry->id);
|
||||||
requested_count++;
|
requested_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,7 +340,7 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt)
|
|||||||
{
|
{
|
||||||
clock = tf_resize_vec(clock, sizeof(tf_ssb_ebt_clock_t) + (count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
clock = tf_resize_vec(clock, sizeof(tf_ssb_ebt_clock_t) + (count + 1) * sizeof(tf_ssb_ebt_clock_entry_t));
|
||||||
clock->entries[count] = (tf_ssb_ebt_clock_entry_t) { .value = entry->in };
|
clock->entries[count] = (tf_ssb_ebt_clock_entry_t) { .value = entry->in };
|
||||||
snprintf(clock->entries[count].id, sizeof(clock->entries[count].id), "%s", entry->id);
|
tf_string_set(clock->entries[count].id, sizeof(clock->entries[count].id), entry->id);
|
||||||
clock->count = ++count;
|
clock->count = ++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +348,7 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt)
|
|||||||
return clock;
|
return clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence)
|
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence)
|
||||||
{
|
{
|
||||||
uv_mutex_lock(&ebt->mutex);
|
uv_mutex_lock(&ebt->mutex);
|
||||||
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
||||||
@ -365,7 +363,7 @@ void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t seq
|
|||||||
uv_mutex_unlock(&ebt->mutex);
|
uv_mutex_unlock(&ebt->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence)
|
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence)
|
||||||
{
|
{
|
||||||
uv_mutex_lock(&ebt->mutex);
|
uv_mutex_lock(&ebt->mutex);
|
||||||
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
ebt_entry_t* entry = _ebt_get_entry(ebt, id);
|
||||||
|
@ -19,7 +19,7 @@ typedef struct _tf_ssb_ebt_clock_entry_t
|
|||||||
/** The identity. */
|
/** The identity. */
|
||||||
char id[k_id_base64_len];
|
char id[k_id_base64_len];
|
||||||
/** The sequence number. */
|
/** The sequence number. */
|
||||||
int64_t value;
|
int32_t value;
|
||||||
} tf_ssb_ebt_clock_entry_t;
|
} tf_ssb_ebt_clock_entry_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,7 +76,7 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt);
|
|||||||
** @param id The identity to update.
|
** @param id The identity to update.
|
||||||
** @param sequence The maximum sequence number sent.
|
** @param sequence The maximum sequence number sent.
|
||||||
*/
|
*/
|
||||||
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence);
|
void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Update the clock state indicating the messages that have been received for an account.
|
** Update the clock state indicating the messages that have been received for an account.
|
||||||
@ -84,7 +84,7 @@ void tf_ssb_ebt_set_messages_sent(tf_ssb_ebt_t* ebt, const char* id, int64_t seq
|
|||||||
** @param id The identity to update.
|
** @param id The identity to update.
|
||||||
** @param sequence The maximum sequence number received.
|
** @param sequence The maximum sequence number received.
|
||||||
*/
|
*/
|
||||||
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int64_t sequence);
|
void tf_ssb_ebt_set_messages_received(tf_ssb_ebt_t* ebt, const char* id, int32_t sequence);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Destroy an EBT instance.
|
** Destroy an EBT instance.
|
||||||
|
@ -142,8 +142,7 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
|||||||
JSPropertyDescriptor desc;
|
JSPropertyDescriptor desc;
|
||||||
if (JS_GetOwnProperty(context, &desc, files, ptab[i].atom) == 1)
|
if (JS_GetOwnProperty(context, &desc, files, ptab[i].atom) == 1)
|
||||||
{
|
{
|
||||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
const char* file_name = JS_AtomToCString(context, ptab[i].atom);
|
||||||
const char* file_name = JS_ToCString(context, key);
|
|
||||||
const char* blob_id = JS_ToCString(context, desc.value);
|
const char* blob_id = JS_ToCString(context, desc.value);
|
||||||
|
|
||||||
uint8_t* file_blob = NULL;
|
uint8_t* file_blob = NULL;
|
||||||
@ -156,7 +155,6 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
|||||||
}
|
}
|
||||||
|
|
||||||
JS_FreeCString(context, file_name);
|
JS_FreeCString(context, file_name);
|
||||||
JS_FreeValue(context, key);
|
|
||||||
JS_FreeCString(context, blob_id);
|
JS_FreeCString(context, blob_id);
|
||||||
JS_FreeValue(context, desc.value);
|
JS_FreeValue(context, desc.value);
|
||||||
JS_FreeValue(context, desc.setter);
|
JS_FreeValue(context, desc.setter);
|
||||||
|
15
src/ssb.h
@ -323,7 +323,7 @@ void tf_ssb_run(tf_ssb_t* ssb);
|
|||||||
** @param previous_sequence The sequence number of the previous message in the feed. Optional.
|
** @param previous_sequence The sequence number of the previous message in the feed. Optional.
|
||||||
** @return The signed message.
|
** @return The signed message.
|
||||||
*/
|
*/
|
||||||
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int64_t previous_sequence);
|
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message, const char* previous_id, int32_t previous_sequence);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Get the server's identity.
|
** Get the server's identity.
|
||||||
@ -651,7 +651,7 @@ void tf_ssb_remove_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_
|
|||||||
** @param id The message identifier.
|
** @param id The message identifier.
|
||||||
** @param user_data The user data.
|
** @param user_data The user data.
|
||||||
*/
|
*/
|
||||||
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data);
|
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Register a callback called when a message is added to the database.
|
** Register a callback called when a message is added to the database.
|
||||||
@ -678,7 +678,7 @@ void tf_ssb_remove_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_ca
|
|||||||
** @param id The message identity added.
|
** @param id The message identity added.
|
||||||
** @param message_with_keys The message added in the format required if keys are requested.
|
** @param message_with_keys The message added in the format required if keys are requested.
|
||||||
*/
|
*/
|
||||||
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, JSValue message_with_keys);
|
void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, JSValue message_with_keys);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Record that a new blob was stored.
|
** Record that a new blob was stored.
|
||||||
@ -1006,15 +1006,6 @@ void tf_ssb_record_thread_busy(tf_ssb_t* ssb, bool busy);
|
|||||||
*/
|
*/
|
||||||
float tf_ssb_get_average_thread_percent(tf_ssb_t* ssb);
|
float tf_ssb_get_average_thread_percent(tf_ssb_t* ssb);
|
||||||
|
|
||||||
/**
|
|
||||||
** Register a callback to be called when the main thread blocks for an
|
|
||||||
** unreasonable amount of time.
|
|
||||||
** @param ssb The SSB instance.
|
|
||||||
** @param callback The callback to call.
|
|
||||||
** @param user_data User data to pass to the callback.
|
|
||||||
*/
|
|
||||||
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Get the queue of messages in the progress of being stored.
|
** Get the queue of messages in the progress of being stored.
|
||||||
** @param ssb The SSB instance.
|
** @param ssb The SSB instance.
|
||||||
|
28
src/ssb.js.c
@ -241,7 +241,7 @@ static JSValue _tf_ssb_deleteIdentity(JSContext* context, JSValueConst this_val,
|
|||||||
{
|
{
|
||||||
delete_identity_t* work = tf_malloc(sizeof(delete_identity_t) + user_length + 1);
|
delete_identity_t* work = tf_malloc(sizeof(delete_identity_t) + user_length + 1);
|
||||||
*work = (delete_identity_t) { 0 };
|
*work = (delete_identity_t) { 0 };
|
||||||
snprintf(work->id, sizeof(work->id), "%s", *id == '@' ? id + 1 : id);
|
tf_string_set(work->id, sizeof(work->id), *id == '@' ? id + 1 : id);
|
||||||
memcpy(work->user, user, user_length + 1);
|
memcpy(work->user, user, user_length + 1);
|
||||||
result = JS_NewPromiseCapability(context, work->promise);
|
result = JS_NewPromiseCapability(context, work->promise);
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_delete_identity_work, _tf_ssb_delete_identity_after_work, work);
|
tf_ssb_run_work(ssb, _tf_ssb_delete_identity_work, _tf_ssb_delete_identity_after_work, work);
|
||||||
@ -279,10 +279,14 @@ static void _tf_ssb_swap_with_server_identity_work(tf_ssb_t* ssb, void* user_dat
|
|||||||
sqlite3_bind_text(statement, 2, work->user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 3, work->id, -1, NULL) == SQLITE_OK &&
|
sqlite3_bind_text(statement, 2, work->user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 3, work->id, -1, NULL) == SQLITE_OK &&
|
||||||
sqlite3_step(statement) == SQLITE_DONE && sqlite3_changes(db) == 1)
|
sqlite3_step(statement) == SQLITE_DONE && sqlite3_changes(db) == 1)
|
||||||
{
|
{
|
||||||
error = NULL;
|
char* commit_error = NULL;
|
||||||
if (sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, &error) != SQLITE_OK)
|
if (sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, &commit_error) != SQLITE_OK)
|
||||||
{
|
{
|
||||||
work->error = error ? tf_strdup(error) : tf_strdup(sqlite3_errmsg(db));
|
work->error = commit_error ? tf_strdup(commit_error) : tf_strdup(sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
if (commit_error)
|
||||||
|
{
|
||||||
|
sqlite3_free(commit_error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -300,6 +304,10 @@ static void _tf_ssb_swap_with_server_identity_work(tf_ssb_t* ssb, void* user_dat
|
|||||||
{
|
{
|
||||||
work->error = error ? tf_strdup(error) : tf_strdup(sqlite3_errmsg(db));
|
work->error = error ? tf_strdup(error) : tf_strdup(sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
sqlite3_free(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -345,7 +353,7 @@ static JSValue _tf_ssb_swap_with_server_identity(JSContext* context, JSValueCons
|
|||||||
swap_with_server_identity_t* work = tf_malloc(sizeof(swap_with_server_identity_t) + user_length + 1);
|
swap_with_server_identity_t* work = tf_malloc(sizeof(swap_with_server_identity_t) + user_length + 1);
|
||||||
*work = (swap_with_server_identity_t) { 0 };
|
*work = (swap_with_server_identity_t) { 0 };
|
||||||
tf_ssb_whoami(ssb, work->server_id, sizeof(work->server_id));
|
tf_ssb_whoami(ssb, work->server_id, sizeof(work->server_id));
|
||||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
tf_string_set(work->id, sizeof(work->id), id);
|
||||||
memcpy(work->user, user, user_length + 1);
|
memcpy(work->user, user, user_length + 1);
|
||||||
result = JS_NewPromiseCapability(context, work->promise);
|
result = JS_NewPromiseCapability(context, work->promise);
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_swap_with_server_identity_work, _tf_ssb_swap_with_server_identity_after_work, work);
|
tf_ssb_run_work(ssb, _tf_ssb_swap_with_server_identity_work, _tf_ssb_swap_with_server_identity_after_work, work);
|
||||||
@ -481,7 +489,7 @@ static JSValue _tf_ssb_getPrivateKey(JSContext* context, JSValueConst this_val,
|
|||||||
get_private_key_t* work = tf_malloc(sizeof(get_private_key_t) + user_length + 1);
|
get_private_key_t* work = tf_malloc(sizeof(get_private_key_t) + user_length + 1);
|
||||||
*work = (get_private_key_t) { .context = context };
|
*work = (get_private_key_t) { .context = context };
|
||||||
memcpy(work->user, user, user_length + 1);
|
memcpy(work->user, user, user_length + 1);
|
||||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
tf_string_set(work->id, sizeof(work->id), id);
|
||||||
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
JSValue result = JS_NewPromiseCapability(context, work->promise);
|
||||||
tf_ssb_run_work(ssb, _tf_ssb_get_private_key_work, _tf_ssb_get_private_key_after_work, work);
|
tf_ssb_run_work(ssb, _tf_ssb_get_private_key_work, _tf_ssb_get_private_key_after_work, work);
|
||||||
|
|
||||||
@ -688,7 +696,7 @@ typedef struct _append_message_t
|
|||||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
||||||
bool got_private_key;
|
bool got_private_key;
|
||||||
char previous_id[512];
|
char previous_id[512];
|
||||||
int64_t previous_sequence;
|
int32_t previous_sequence;
|
||||||
JSContext* context;
|
JSContext* context;
|
||||||
JSValue promise[2];
|
JSValue promise[2];
|
||||||
JSValue message;
|
JSValue message;
|
||||||
@ -758,7 +766,7 @@ static JSValue _tf_ssb_appendMessageWithIdentity(JSContext* context, JSValueCons
|
|||||||
append_message_t* work = tf_malloc(sizeof(append_message_t) + user_length + 1);
|
append_message_t* work = tf_malloc(sizeof(append_message_t) + user_length + 1);
|
||||||
*work = (append_message_t) { .context = context, .message = JS_DupValue(context, argv[2]) };
|
*work = (append_message_t) { .context = context, .message = JS_DupValue(context, argv[2]) };
|
||||||
memcpy(work->user, user, user_length + 1);
|
memcpy(work->user, user, user_length + 1);
|
||||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
tf_string_set(work->id, sizeof(work->id), id);
|
||||||
|
|
||||||
JS_FreeCString(context, id);
|
JS_FreeCString(context, id);
|
||||||
JS_FreeCString(context, user);
|
JS_FreeCString(context, user);
|
||||||
@ -988,6 +996,8 @@ static void _tf_ssb_stored_connections_after_work(tf_ssb_t* ssb, int status, voi
|
|||||||
JS_SetPropertyStr(context, connection, "address", JS_NewString(context, work->connections[i].address));
|
JS_SetPropertyStr(context, connection, "address", JS_NewString(context, work->connections[i].address));
|
||||||
JS_SetPropertyStr(context, connection, "port", JS_NewInt32(context, work->connections[i].port));
|
JS_SetPropertyStr(context, connection, "port", JS_NewInt32(context, work->connections[i].port));
|
||||||
JS_SetPropertyStr(context, connection, "pubkey", JS_NewString(context, work->connections[i].pubkey));
|
JS_SetPropertyStr(context, connection, "pubkey", JS_NewString(context, work->connections[i].pubkey));
|
||||||
|
JS_SetPropertyStr(context, connection, "last_attempt", JS_NewInt64(context, work->connections[i].last_attempt));
|
||||||
|
JS_SetPropertyStr(context, connection, "last_success", JS_NewInt64(context, work->connections[i].last_success));
|
||||||
JS_SetPropertyUint32(context, result, i, connection);
|
JS_SetPropertyUint32(context, result, i, connection);
|
||||||
}
|
}
|
||||||
tf_free(work->connections);
|
tf_free(work->connections);
|
||||||
@ -1594,7 +1604,7 @@ static void _tf_ssb_cleanup_value(tf_ssb_t* ssb, void* user_data)
|
|||||||
JS_FreeValue(tf_ssb_get_context(ssb), callback);
|
JS_FreeValue(tf_ssb_get_context(ssb), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
static void _tf_ssb_on_message_added_callback(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
JSContext* context = tf_ssb_get_context(ssb);
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);
|
||||||
|
317
src/ssb.rpc.c
@ -16,7 +16,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
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, int32_t sequence, bool keys, bool live, bool end_request);
|
||||||
static void _tf_ssb_rpc_send_peers_exchange(tf_ssb_connection_t* connection);
|
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);
|
||||||
@ -134,7 +134,7 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
|
|||||||
*work = (blobs_get_work_t) {
|
*work = (blobs_get_work_t) {
|
||||||
.request_number = request_number,
|
.request_number = request_number,
|
||||||
};
|
};
|
||||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
tf_string_set(work->id, sizeof(work->id), id);
|
||||||
tf_ssb_connection_schedule_idle(connection, id, _tf_ssb_blobs_get_callback, work);
|
tf_ssb_connection_schedule_idle(connection, id, _tf_ssb_blobs_get_callback, work);
|
||||||
|
|
||||||
JS_FreeCString(context, id);
|
JS_FreeCString(context, id);
|
||||||
@ -184,7 +184,7 @@ static void _tf_ssb_rpc_blobs_has(tf_ssb_connection_t* connection, uint8_t flags
|
|||||||
*work = (blobs_has_work_t) {
|
*work = (blobs_has_work_t) {
|
||||||
.request_number = request_number,
|
.request_number = request_number,
|
||||||
};
|
};
|
||||||
snprintf(work->id, sizeof(work->id), "%s", id_str);
|
tf_string_set(work->id, sizeof(work->id), id_str);
|
||||||
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_blobs_has_work, _tf_ssb_rpc_blobs_has_after_work, work);
|
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_blobs_has_work, _tf_ssb_rpc_blobs_has_after_work, work);
|
||||||
|
|
||||||
JS_FreeCString(context, id_str);
|
JS_FreeCString(context, id_str);
|
||||||
@ -239,7 +239,7 @@ static void _tf_ssb_request_blob_wants_work(tf_ssb_connection_t* connection, voi
|
|||||||
{
|
{
|
||||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
snprintf(work->out_id[work->out_id_count], sizeof(work->out_id[work->out_id_count]), "%s", (const char*)sqlite3_column_text(statement, 0));
|
tf_string_set(work->out_id[work->out_id_count], sizeof(work->out_id[work->out_id_count]), (const char*)sqlite3_column_text(statement, 0));
|
||||||
work->out_id_count++;
|
work->out_id_count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ static void _tf_ssb_request_blob_wants_after_work(tf_ssb_connection_t* connectio
|
|||||||
}
|
}
|
||||||
if (work->out_id_count)
|
if (work->out_id_count)
|
||||||
{
|
{
|
||||||
snprintf(blob_wants->last_id, sizeof(blob_wants->last_id), "%s", work->out_id[work->out_id_count - 1]);
|
tf_string_set(blob_wants->last_id, sizeof(blob_wants->last_id), work->out_id[work->out_id_count - 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tf_free(work);
|
tf_free(work);
|
||||||
@ -332,8 +332,6 @@ static void _tf_ssb_rpc_tunnel_callback(tf_ssb_connection_t* connection, uint8_t
|
|||||||
|
|
||||||
JS_FreeValue(context, stack_val);
|
JS_FreeValue(context, stack_val);
|
||||||
JS_FreeValue(context, message_val);
|
JS_FreeValue(context, message_val);
|
||||||
|
|
||||||
tf_ssb_connection_close(tun->connection, buffer);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -349,11 +347,6 @@ static void _tf_ssb_rpc_tunnel_cleanup(tf_ssb_t* ssb, void* user_data)
|
|||||||
static void _tf_ssb_rpc_tunnel_connect(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
static void _tf_ssb_rpc_tunnel_connect(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||||
{
|
{
|
||||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||||
if (!tf_ssb_is_room(ssb))
|
|
||||||
{
|
|
||||||
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "tunnel.connect");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSContext* context = tf_ssb_connection_get_context(connection);
|
JSContext* context = tf_ssb_connection_get_context(connection);
|
||||||
JSValue arg_array = JS_GetPropertyStr(context, args, "args");
|
JSValue arg_array = JS_GetPropertyStr(context, args, "args");
|
||||||
@ -364,54 +357,62 @@ static void _tf_ssb_rpc_tunnel_connect(tf_ssb_connection_t* connection, uint8_t
|
|||||||
|
|
||||||
if (JS_IsUndefined(origin) && !JS_IsUndefined(portal) && !JS_IsUndefined(target))
|
if (JS_IsUndefined(origin) && !JS_IsUndefined(portal) && !JS_IsUndefined(target))
|
||||||
{
|
{
|
||||||
const char* target_str = JS_ToCString(context, target);
|
if (!tf_ssb_is_room(ssb))
|
||||||
|
|
||||||
tf_ssb_connection_t* target_connection = tf_ssb_connection_get(ssb, target_str);
|
|
||||||
if (target_connection)
|
|
||||||
{
|
{
|
||||||
int32_t tunnel_request_number = tf_ssb_connection_next_request_number(target_connection);
|
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "tunnel.connect");
|
||||||
const char* portal_str = JS_ToCString(context, portal);
|
|
||||||
|
|
||||||
JSValue message = JS_NewObject(context);
|
|
||||||
JSValue name = JS_NewArray(context);
|
|
||||||
JS_SetPropertyUint32(context, name, 0, JS_NewString(context, "tunnel"));
|
|
||||||
JS_SetPropertyUint32(context, name, 1, JS_NewString(context, "connect"));
|
|
||||||
JS_SetPropertyStr(context, message, "name", name);
|
|
||||||
JSValue arg_obj = JS_NewObject(context);
|
|
||||||
char origin_str[k_id_base64_len] = "";
|
|
||||||
tf_ssb_connection_get_id(connection, origin_str, sizeof(origin_str));
|
|
||||||
JS_SetPropertyStr(context, arg_obj, "origin", JS_NewString(context, origin_str));
|
|
||||||
JS_SetPropertyStr(context, arg_obj, "portal", JS_NewString(context, portal_str));
|
|
||||||
JS_SetPropertyStr(context, arg_obj, "target", JS_NewString(context, target_str));
|
|
||||||
JSValue arg_array = JS_NewArray(context);
|
|
||||||
JS_SetPropertyUint32(context, arg_array, 0, arg_obj);
|
|
||||||
JS_SetPropertyStr(context, message, "args", arg_array);
|
|
||||||
JS_SetPropertyStr(context, message, "type", JS_NewString(context, "duplex"));
|
|
||||||
|
|
||||||
tf_ssb_connection_rpc_send_json(
|
|
||||||
target_connection, k_ssb_rpc_flag_stream | k_ssb_rpc_flag_new_request, tunnel_request_number, "tunnel.connect", message, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
tunnel_t* data0 = tf_malloc(sizeof(tunnel_t));
|
|
||||||
*data0 = (tunnel_t) {
|
|
||||||
.connection = target_connection,
|
|
||||||
.request_number = tunnel_request_number,
|
|
||||||
};
|
|
||||||
tunnel_t* data1 = tf_malloc(sizeof(tunnel_t));
|
|
||||||
*data1 = (tunnel_t) {
|
|
||||||
.connection = connection,
|
|
||||||
.request_number = -request_number,
|
|
||||||
};
|
|
||||||
tf_ssb_connection_add_request(connection, -request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data0, target_connection);
|
|
||||||
tf_ssb_connection_add_request(target_connection, tunnel_request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data1, connection);
|
|
||||||
|
|
||||||
JS_FreeValue(context, message);
|
|
||||||
JS_FreeCString(context, portal_str);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, "Connection not found.");
|
const char* target_str = JS_ToCString(context, target);
|
||||||
|
|
||||||
|
tf_ssb_connection_t* target_connection = tf_ssb_connection_get(ssb, target_str);
|
||||||
|
if (target_connection)
|
||||||
|
{
|
||||||
|
int32_t tunnel_request_number = tf_ssb_connection_next_request_number(target_connection);
|
||||||
|
const char* portal_str = JS_ToCString(context, portal);
|
||||||
|
|
||||||
|
JSValue message = JS_NewObject(context);
|
||||||
|
JSValue name = JS_NewArray(context);
|
||||||
|
JS_SetPropertyUint32(context, name, 0, JS_NewString(context, "tunnel"));
|
||||||
|
JS_SetPropertyUint32(context, name, 1, JS_NewString(context, "connect"));
|
||||||
|
JS_SetPropertyStr(context, message, "name", name);
|
||||||
|
JSValue arg_obj = JS_NewObject(context);
|
||||||
|
char origin_str[k_id_base64_len] = "";
|
||||||
|
tf_ssb_connection_get_id(connection, origin_str, sizeof(origin_str));
|
||||||
|
JS_SetPropertyStr(context, arg_obj, "origin", JS_NewString(context, origin_str));
|
||||||
|
JS_SetPropertyStr(context, arg_obj, "portal", JS_NewString(context, portal_str));
|
||||||
|
JS_SetPropertyStr(context, arg_obj, "target", JS_NewString(context, target_str));
|
||||||
|
JSValue arg_array = JS_NewArray(context);
|
||||||
|
JS_SetPropertyUint32(context, arg_array, 0, arg_obj);
|
||||||
|
JS_SetPropertyStr(context, message, "args", arg_array);
|
||||||
|
JS_SetPropertyStr(context, message, "type", JS_NewString(context, "duplex"));
|
||||||
|
|
||||||
|
tf_ssb_connection_rpc_send_json(
|
||||||
|
target_connection, k_ssb_rpc_flag_stream | k_ssb_rpc_flag_new_request, tunnel_request_number, "tunnel.connect", message, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
tunnel_t* data0 = tf_malloc(sizeof(tunnel_t));
|
||||||
|
*data0 = (tunnel_t) {
|
||||||
|
.connection = target_connection,
|
||||||
|
.request_number = tunnel_request_number,
|
||||||
|
};
|
||||||
|
tunnel_t* data1 = tf_malloc(sizeof(tunnel_t));
|
||||||
|
*data1 = (tunnel_t) {
|
||||||
|
.connection = connection,
|
||||||
|
.request_number = -request_number,
|
||||||
|
};
|
||||||
|
tf_ssb_connection_add_request(connection, -request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data0, target_connection);
|
||||||
|
tf_ssb_connection_add_request(
|
||||||
|
target_connection, tunnel_request_number, "tunnel.connect", _tf_ssb_rpc_tunnel_callback, _tf_ssb_rpc_tunnel_cleanup, data1, connection);
|
||||||
|
|
||||||
|
JS_FreeValue(context, message);
|
||||||
|
JS_FreeCString(context, portal_str);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_ssb_connection_rpc_send_error(connection, flags, -request_number, "Connection not found.");
|
||||||
|
}
|
||||||
|
JS_FreeCString(context, target_str);
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, target_str);
|
|
||||||
}
|
}
|
||||||
else if (!JS_IsUndefined(origin) && !JS_IsUndefined(portal) && !JS_IsUndefined(target))
|
else if (!JS_IsUndefined(origin) && !JS_IsUndefined(portal) && !JS_IsUndefined(target))
|
||||||
{
|
{
|
||||||
@ -611,7 +612,7 @@ static void _tf_ssb_rpc_connection_blobs_get(tf_ssb_connection_t* connection, co
|
|||||||
{
|
{
|
||||||
blobs_get_t* get = tf_malloc(sizeof(blobs_get_t) + size);
|
blobs_get_t* get = tf_malloc(sizeof(blobs_get_t) + size);
|
||||||
*get = (blobs_get_t) { .ssb = tf_ssb_connection_get_ssb(connection), .connection = connection, .expected_size = size };
|
*get = (blobs_get_t) { .ssb = tf_ssb_connection_get_ssb(connection), .connection = connection, .expected_size = size };
|
||||||
snprintf(get->id, sizeof(get->id), "%s", blob_id);
|
tf_string_set(get->id, sizeof(get->id), blob_id);
|
||||||
memset(get->buffer, 0, size);
|
memset(get->buffer, 0, size);
|
||||||
|
|
||||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||||
@ -713,7 +714,7 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
|||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
for (uint32_t i = 0; i < plen; ++i)
|
||||||
{
|
{
|
||||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
const char* blob_id = JS_AtomToCString(context, ptab[i].atom);
|
||||||
JSPropertyDescriptor desc;
|
JSPropertyDescriptor desc;
|
||||||
JSValue key_value = JS_NULL;
|
JSValue key_value = JS_NULL;
|
||||||
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
|
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
|
||||||
@ -722,7 +723,6 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
|||||||
JS_FreeValue(context, desc.setter);
|
JS_FreeValue(context, desc.setter);
|
||||||
JS_FreeValue(context, desc.getter);
|
JS_FreeValue(context, desc.getter);
|
||||||
}
|
}
|
||||||
const char* blob_id = JS_ToCString(context, key);
|
|
||||||
int64_t size = 0;
|
int64_t size = 0;
|
||||||
JS_ToInt64(context, &size, key_value);
|
JS_ToInt64(context, &size, key_value);
|
||||||
if (--blob_wants->wants_sent == 0)
|
if (--blob_wants->wants_sent == 0)
|
||||||
@ -736,18 +736,17 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
|
|||||||
.connection = connection,
|
.connection = connection,
|
||||||
.size = size,
|
.size = size,
|
||||||
};
|
};
|
||||||
snprintf(work->blob_id, sizeof(work->blob_id), "%s", blob_id);
|
tf_string_set(work->blob_id, sizeof(work->blob_id), blob_id);
|
||||||
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_connection_blobs_create_wants_work, _tf_ssb_rpc_connection_blobs_create_wants_after_work, work);
|
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_connection_blobs_create_wants_work, _tf_ssb_rpc_connection_blobs_create_wants_after_work, work);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
blob_get_t* get = tf_malloc(sizeof(blob_get_t));
|
blob_get_t* get = tf_malloc(sizeof(blob_get_t));
|
||||||
*get = (blob_get_t) { .size = size };
|
*get = (blob_get_t) { .size = size };
|
||||||
snprintf(get->id, sizeof(get->id), "%s", blob_id);
|
tf_string_set(get->id, sizeof(get->id), blob_id);
|
||||||
tf_ssb_connection_schedule_idle(connection, blob_id, _tf_ssb_rpc_connection_blobs_get_idle, get);
|
tf_ssb_connection_schedule_idle(connection, blob_id, _tf_ssb_rpc_connection_blobs_get_idle, get);
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, blob_id);
|
JS_FreeCString(context, blob_id);
|
||||||
JS_FreeValue(context, key);
|
|
||||||
JS_FreeValue(context, key_value);
|
JS_FreeValue(context, key_value);
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
for (uint32_t i = 0; i < plen; ++i)
|
||||||
@ -862,13 +861,13 @@ typedef struct _tf_ssb_connection_send_history_stream_t
|
|||||||
{
|
{
|
||||||
int32_t request_number;
|
int32_t request_number;
|
||||||
char author[k_id_base64_len];
|
char author[k_id_base64_len];
|
||||||
int64_t sequence;
|
int32_t sequence;
|
||||||
bool keys;
|
bool keys;
|
||||||
bool live;
|
bool live;
|
||||||
bool end_request;
|
bool end_request;
|
||||||
|
|
||||||
bool out_finished;
|
bool out_finished;
|
||||||
int64_t out_max_sequence_seen;
|
int32_t out_max_sequence_seen;
|
||||||
char** out_messages;
|
char** out_messages;
|
||||||
int out_messages_count;
|
int out_messages_count;
|
||||||
} tf_ssb_connection_send_history_stream_t;
|
} tf_ssb_connection_send_history_stream_t;
|
||||||
@ -889,8 +888,8 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
|
|||||||
"sequence < ?3 ORDER BY sequence",
|
"sequence < ?3 ORDER BY sequence",
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, request->author, -1, NULL) == SQLITE_OK && sqlite3_bind_int64(statement, 2, request->sequence) == SQLITE_OK &&
|
if (sqlite3_bind_text(statement, 1, request->author, -1, NULL) == SQLITE_OK && sqlite3_bind_int(statement, 2, request->sequence) == SQLITE_OK &&
|
||||||
sqlite3_bind_int64(statement, 3, request->sequence + k_max) == SQLITE_OK)
|
sqlite3_bind_int(statement, 3, request->sequence + k_max) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
JSMallocFunctions funcs = { 0 };
|
JSMallocFunctions funcs = { 0 };
|
||||||
tf_get_js_malloc_functions(&funcs);
|
tf_get_js_malloc_functions(&funcs);
|
||||||
@ -901,7 +900,7 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
|
|||||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||||
{
|
{
|
||||||
JSValue message = JS_UNDEFINED;
|
JSValue message = JS_UNDEFINED;
|
||||||
request->out_max_sequence_seen = sqlite3_column_int64(statement, 3);
|
request->out_max_sequence_seen = sqlite3_column_int(statement, 3);
|
||||||
|
|
||||||
JSValue formatted = tf_ssb_format_message(context, (const char*)sqlite3_column_text(statement, 0), (const char*)sqlite3_column_text(statement, 1),
|
JSValue formatted = tf_ssb_format_message(context, (const char*)sqlite3_column_text(statement, 0), (const char*)sqlite3_column_text(statement, 1),
|
||||||
sqlite3_column_int64(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
|
sqlite3_column_int64(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
|
||||||
@ -996,7 +995,7 @@ static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t*
|
|||||||
}
|
}
|
||||||
|
|
||||||
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, int32_t sequence, bool keys, bool live, bool end_request)
|
||||||
{
|
{
|
||||||
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection))
|
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection))
|
||||||
{
|
{
|
||||||
@ -1008,7 +1007,7 @@ static void _tf_ssb_connection_send_history_stream(
|
|||||||
.live = live,
|
.live = live,
|
||||||
.end_request = end_request,
|
.end_request = end_request,
|
||||||
};
|
};
|
||||||
snprintf(async->author, sizeof(async->author), "%s", author);
|
tf_string_set(async->author, sizeof(async->author), author);
|
||||||
tf_ssb_connection_schedule_idle(connection, author, _tf_ssb_connection_send_history_stream_callback, async);
|
tf_ssb_connection_schedule_idle(connection, author, _tf_ssb_connection_send_history_stream_callback, async);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1035,8 +1034,8 @@ static void _tf_ssb_rpc_createHistoryStream(
|
|||||||
JSValue live = JS_GetPropertyStr(context, arg, "live");
|
JSValue live = JS_GetPropertyStr(context, arg, "live");
|
||||||
bool is_keys = JS_IsUndefined(keys) || JS_ToBool(context, keys) > 0;
|
bool is_keys = JS_IsUndefined(keys) || JS_ToBool(context, keys) > 0;
|
||||||
bool is_live = JS_ToBool(context, live) > 0 && (tf_ssb_connection_get_flags(connection) & k_tf_ssb_connect_flag_one_shot) == 0;
|
bool is_live = JS_ToBool(context, live) > 0 && (tf_ssb_connection_get_flags(connection) & k_tf_ssb_connect_flag_one_shot) == 0;
|
||||||
int64_t sequence = 0;
|
int32_t sequence = 0;
|
||||||
JS_ToInt64(context, &sequence, seq);
|
JS_ToInt32(context, &sequence, seq);
|
||||||
const char* author = JS_ToCString(context, id);
|
const char* author = JS_ToCString(context, id);
|
||||||
|
|
||||||
_tf_ssb_connection_send_history_stream(connection, -request_number, author, sequence, is_keys, is_live, true);
|
_tf_ssb_connection_send_history_stream(connection, -request_number, author, sequence, is_keys, is_live, true);
|
||||||
@ -1257,7 +1256,7 @@ typedef struct _invite_use_t
|
|||||||
;
|
;
|
||||||
uint8_t private_key[512];
|
uint8_t private_key[512];
|
||||||
char previous_id[64];
|
char previous_id[64];
|
||||||
int64_t previous_sequence;
|
int32_t previous_sequence;
|
||||||
|
|
||||||
char host[256];
|
char host[256];
|
||||||
int port;
|
int port;
|
||||||
@ -1337,7 +1336,7 @@ static void _tf_ssb_rpc_invite_use_callback(
|
|||||||
.ssb = ssb,
|
.ssb = ssb,
|
||||||
.port = tf_ssb_connection_get_port(connection),
|
.port = tf_ssb_connection_get_port(connection),
|
||||||
};
|
};
|
||||||
snprintf(work->host, sizeof(work->host), "%s", tf_ssb_connection_get_host(connection));
|
tf_string_set(work->host, sizeof(work->host), tf_ssb_connection_get_host(connection));
|
||||||
tf_ssb_whoami(ssb, work->author, sizeof(work->author));
|
tf_ssb_whoami(ssb, work->author, sizeof(work->author));
|
||||||
tf_ssb_get_private_key(ssb, work->private_key, sizeof(work->private_key));
|
tf_ssb_get_private_key(ssb, work->private_key, sizeof(work->private_key));
|
||||||
tf_ssb_connection_get_id(connection, work->pub, sizeof(work->pub));
|
tf_ssb_connection_get_id(connection, work->pub, sizeof(work->pub));
|
||||||
@ -1463,23 +1462,6 @@ static void _tf_ssb_rpc_broadcasts_changed_callback(tf_ssb_t* ssb, void* user_da
|
|||||||
tf_ssb_visit_broadcasts(ssb, _tf_ssb_rpc_broadcasts_changed_visit, ssb);
|
tf_ssb_visit_broadcasts(ssb, _tf_ssb_rpc_broadcasts_changed_visit, ssb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_checkpoint(tf_ssb_t* ssb)
|
|
||||||
{
|
|
||||||
int64_t checkpoint_start_ms = uv_hrtime();
|
|
||||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
|
||||||
int log = 0;
|
|
||||||
int checkpointed = 0;
|
|
||||||
if (sqlite3_wal_checkpoint_v2(db, NULL, SQLITE_CHECKPOINT_PASSIVE, &log, &checkpointed) == SQLITE_OK)
|
|
||||||
{
|
|
||||||
tf_printf("Checkpointed %d frames in %d ms. Log is now %d frames.\n", checkpointed, (int)((uv_hrtime() - checkpoint_start_ms) / 1000000LL), log);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tf_printf("Checkpoint: %s.\n", sqlite3_errmsg(db));
|
|
||||||
}
|
|
||||||
tf_ssb_release_db_writer(ssb, db);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct _delete_t
|
typedef struct _delete_t
|
||||||
{
|
{
|
||||||
int deleted;
|
int deleted;
|
||||||
@ -1495,58 +1477,91 @@ static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
tf_ssb_release_db_reader(ssb, db);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
if (age <= 0)
|
if (age <= 0)
|
||||||
{
|
{
|
||||||
_tf_ssb_rpc_checkpoint(ssb);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int64_t start_ns = uv_hrtime();
|
int64_t start_ns = uv_hrtime();
|
||||||
db = tf_ssb_acquire_db_writer(ssb);
|
|
||||||
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;
|
||||||
const int k_limit = 128;
|
sqlite3_stmt* statement;
|
||||||
int deleted = 0;
|
|
||||||
if (sqlite3_prepare_v2(db, "DELETE FROM blob_wants_cache WHERE source IS NULL and timestamp < ?1", -1, &statement, NULL) == SQLITE_OK)
|
char** ids = NULL;
|
||||||
{
|
int ids_count = 0;
|
||||||
if (sqlite3_bind_int64(statement, 1, timestamp) == SQLITE_OK)
|
|
||||||
{
|
db = tf_ssb_acquire_db_reader(ssb);
|
||||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
|
||||||
{
|
|
||||||
tf_printf("Deleting stale blob wants cache entries: %s.\n", sqlite3_errmsg(db));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sqlite3_finalize(statement);
|
|
||||||
}
|
|
||||||
if (sqlite3_prepare_v2(db,
|
if (sqlite3_prepare_v2(db,
|
||||||
"DELETE FROM blobs WHERE blobs.id IN ("
|
"SELECT blobs.id FROM blobs "
|
||||||
" SELECT blobs.id FROM blobs "
|
"JOIN messages_refs ON blobs.id = messages_refs.ref "
|
||||||
" JOIN messages_refs ON blobs.id = messages_refs.ref "
|
"JOIN messages ON messages.id = messages_refs.message "
|
||||||
" JOIN messages ON messages.id = messages_refs.message "
|
"WHERE blobs.created < ?1 / 1000 "
|
||||||
" WHERE blobs.created < ?1 / 1000 "
|
"GROUP BY blobs.id HAVING MAX(messages.timestamp) < ?1 LIMIT ?2",
|
||||||
" GROUP BY blobs.id HAVING MAX(messages.timestamp) < ?1 LIMIT ?2)",
|
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
-1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
|
const int k_limit = 128;
|
||||||
if (sqlite3_bind_int64(statement, 1, timestamp) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_limit) == SQLITE_OK)
|
if (sqlite3_bind_int64(statement, 1, timestamp) == SQLITE_OK && sqlite3_bind_int(statement, 2, k_limit) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
int r = sqlite3_step(statement);
|
int r = SQLITE_OK;
|
||||||
|
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
ids = tf_realloc(ids, sizeof(char*) * (ids_count + 1));
|
||||||
|
ids[ids_count++] = tf_strdup((const char*)sqlite3_column_text(statement, 0));
|
||||||
|
}
|
||||||
if (r != SQLITE_DONE)
|
if (r != SQLITE_DONE)
|
||||||
{
|
{
|
||||||
tf_printf("_tf_ssb_rpc_delete_blobs_work: %s\n", sqlite3_errmsg(db));
|
tf_printf("_tf_ssb_rpc_delete_blobs_work: %s\n", sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
tf_printf("_tf_ssb_rpc_delete_blobs_work: %d rows\n", sqlite3_changes(db));
|
|
||||||
}
|
|
||||||
deleted = sqlite3_changes(db);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
tf_ssb_release_db_writer(ssb, db);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
|
||||||
|
int deleted = 0;
|
||||||
|
if (ids_count)
|
||||||
|
{
|
||||||
|
db = tf_ssb_acquire_db_writer(ssb);
|
||||||
|
if (sqlite3_prepare_v2(db, "DELETE FROM blob_wants_cache WHERE source IS NULL and timestamp < ?1", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_int64(statement, 1, timestamp) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
tf_printf("Deleting stale blob wants cache entries: %s.\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
if (sqlite3_prepare_v2(db, "DELETE FROM blobs WHERE blobs.id = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ids_count; i++)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, ids[i], -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
int r = sqlite3_step(statement);
|
||||||
|
if (r != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
tf_printf("_tf_ssb_rpc_delete_blobs_work: %s\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deleted += sqlite3_changes(db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_reset(statement);
|
||||||
|
tf_free(ids[i]);
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
tf_ssb_release_db_writer(ssb, db);
|
||||||
|
tf_free(ids);
|
||||||
|
}
|
||||||
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
|
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
|
||||||
tf_printf("Deleted %d blobs in %d ms.\n", deleted, (int)delete->duration_ms);
|
tf_printf("Deleted %d blobs in %d ms.\n", deleted, (int)delete->duration_ms);
|
||||||
_tf_ssb_rpc_checkpoint(ssb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_delete_blobs_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
static void _tf_ssb_rpc_delete_blobs_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
@ -1602,28 +1617,57 @@ 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);
|
||||||
|
|
||||||
db = tf_ssb_acquire_db_writer(ssb);
|
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
if (sqlite3_prepare_v2(db,
|
|
||||||
"DELETE FROM messages WHERE id IN ("
|
char** ids = NULL;
|
||||||
" SELECT id FROM messages WHERE author NOT IN (SELECT value FROM json_each(?)) ORDER BY rowid DESC LIMIT 1024"
|
int ids_count = 0;
|
||||||
")",
|
|
||||||
-1, &statement, NULL) == SQLITE_OK)
|
db = tf_ssb_acquire_db_reader(ssb);
|
||||||
|
if (sqlite3_prepare_v2(db, "SELECT id FROM messages WHERE author NOT IN (SELECT value FROM json_each(?)) ORDER BY rowid DESC LIMIT 1024", -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_bind_text(statement, 1, arg, -1, NULL) == SQLITE_OK)
|
if (sqlite3_bind_text(statement, 1, arg, -1, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
int r = SQLITE_OK;
|
||||||
|
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
ids = tf_realloc(ids, sizeof(char*) * (ids_count + 1));
|
||||||
|
ids[ids_count++] = tf_strdup((const char*)sqlite3_column_text(statement, 0));
|
||||||
|
}
|
||||||
|
if (r != SQLITE_DONE)
|
||||||
{
|
{
|
||||||
tf_printf("deleting messages: %s\n", sqlite3_errmsg(db));
|
tf_printf("deleting messages: %s\n", sqlite3_errmsg(db));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
delete->deleted += sqlite3_changes(db);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
sqlite3_finalize(statement);
|
sqlite3_finalize(statement);
|
||||||
}
|
}
|
||||||
tf_ssb_release_db_writer(ssb, db);
|
tf_ssb_release_db_reader(ssb, db);
|
||||||
|
|
||||||
|
if (ids_count)
|
||||||
|
{
|
||||||
|
db = tf_ssb_acquire_db_writer(ssb);
|
||||||
|
if (sqlite3_prepare_v2(db, "DELETE FROM messages WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ids_count; i++)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, ids[i], -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||||
|
{
|
||||||
|
tf_printf("deleting messages: %s\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete->deleted++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_reset(statement);
|
||||||
|
tf_free(ids[i]);
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
tf_ssb_release_db_writer(ssb, db);
|
||||||
|
tf_free(ids);
|
||||||
|
}
|
||||||
|
|
||||||
JS_FreeCString(context, arg);
|
JS_FreeCString(context, arg);
|
||||||
|
|
||||||
@ -1632,7 +1676,6 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
|
|
||||||
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
|
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
|
||||||
tf_printf("Deleted %d message in %d ms.\n", delete->deleted, (int)delete->duration_ms);
|
tf_printf("Deleted %d message in %d ms.\n", delete->deleted, (int)delete->duration_ms);
|
||||||
_tf_ssb_rpc_checkpoint(ssb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_delete_feeds_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
static void _tf_ssb_rpc_delete_feeds_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
@ -1736,7 +1779,7 @@ static void _tf_ssb_rpc_peers_exchange_internal(
|
|||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
for (uint32_t i = 0; i < plen; ++i)
|
||||||
{
|
{
|
||||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
const char* connection = JS_AtomToCString(context, ptab[i].atom);
|
||||||
JSPropertyDescriptor desc;
|
JSPropertyDescriptor desc;
|
||||||
JSValue key_value = JS_NULL;
|
JSValue key_value = JS_NULL;
|
||||||
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
|
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
|
||||||
@ -1745,12 +1788,10 @@ static void _tf_ssb_rpc_peers_exchange_internal(
|
|||||||
JS_FreeValue(context, desc.setter);
|
JS_FreeValue(context, desc.setter);
|
||||||
JS_FreeValue(context, desc.getter);
|
JS_FreeValue(context, desc.getter);
|
||||||
}
|
}
|
||||||
const char* connection = JS_ToCString(context, key);
|
|
||||||
int64_t timestamp = 0;
|
int64_t timestamp = 0;
|
||||||
JS_ToInt64(context, ×tamp, key_value);
|
JS_ToInt64(context, ×tamp, key_value);
|
||||||
/* ADD BROADCAST connection: timestamp */
|
/* ADD BROADCAST connection: timestamp */
|
||||||
JS_FreeCString(context, connection);
|
JS_FreeCString(context, connection);
|
||||||
JS_FreeValue(context, key);
|
|
||||||
JS_FreeValue(context, key_value);
|
JS_FreeValue(context, key_value);
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
for (uint32_t i = 0; i < plen; ++i)
|
||||||
@ -1801,7 +1842,7 @@ typedef struct _invite_t
|
|||||||
int32_t request_number;
|
int32_t request_number;
|
||||||
bool accepted;
|
bool accepted;
|
||||||
char previous_id[256];
|
char previous_id[256];
|
||||||
int64_t previous_sequence;
|
int32_t previous_sequence;
|
||||||
char* message;
|
char* message;
|
||||||
} invite_t;
|
} invite_t;
|
||||||
|
|
||||||
@ -1895,7 +1936,7 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
|
|||||||
JSValue feed = JS_GetPropertyStr(context, object, "feed");
|
JSValue feed = JS_GetPropertyStr(context, object, "feed");
|
||||||
tf_ssb_connection_get_id(connection, work->invite_public_key, sizeof(work->invite_public_key));
|
tf_ssb_connection_get_id(connection, work->invite_public_key, sizeof(work->invite_public_key));
|
||||||
const char* id = JS_ToCString(context, feed);
|
const char* id = JS_ToCString(context, feed);
|
||||||
snprintf(work->id, sizeof(work->id), "%s", id);
|
tf_string_set(work->id, sizeof(work->id), id);
|
||||||
JS_FreeCString(context, id);
|
JS_FreeCString(context, id);
|
||||||
JS_FreeValue(context, feed);
|
JS_FreeValue(context, feed);
|
||||||
JS_FreeValue(context, object);
|
JS_FreeValue(context, object);
|
||||||
@ -1903,7 +1944,7 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
|
|||||||
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work);
|
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
tf_ssb_connection_t* connections[256];
|
tf_ssb_connection_t* connections[256];
|
||||||
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
|
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
|
||||||
|
@ -114,7 +114,7 @@ static int _ssb_test_count_messages(tf_ssb_t* ssb)
|
|||||||
return count.count;
|
return count.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _message_added(tf_ssb_t* ssb, const char* author, int64_t sequence, const char* id, void* user_data)
|
static void _message_added(tf_ssb_t* ssb, const char* author, int32_t sequence, const char* id, void* user_data)
|
||||||
{
|
{
|
||||||
++*(int*)user_data;
|
++*(int*)user_data;
|
||||||
}
|
}
|
||||||
@ -811,7 +811,7 @@ static void _break_in_a_bit(tf_ssb_t* ssb, tf_ssb_connection_t* connection, cons
|
|||||||
.data = data,
|
.data = data,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
snprintf(data->id, sizeof(data->id), "%s", id);
|
tf_string_set(data->id, sizeof(data->id), id);
|
||||||
uv_timer_init(tf_ssb_get_loop(ssb), &data->timer);
|
uv_timer_init(tf_ssb_get_loop(ssb), &data->timer);
|
||||||
uv_timer_start(&data->timer, _close_callback, 3000, 0);
|
uv_timer_start(&data->timer, _close_callback, 3000, 0);
|
||||||
}
|
}
|
||||||
@ -1046,11 +1046,11 @@ void tf_ssb_test_publish(const tf_test_options_t* options)
|
|||||||
static void _test_print_identity(const char* identity, void* user_data)
|
static void _test_print_identity(const char* identity, void* user_data)
|
||||||
{
|
{
|
||||||
tf_ssb_t* ssb = user_data;
|
tf_ssb_t* ssb = user_data;
|
||||||
int64_t sequence = -1;
|
int32_t sequence = -1;
|
||||||
char id[k_id_base64_len] = { 0 };
|
char id[k_id_base64_len] = { 0 };
|
||||||
snprintf(id, sizeof(id), "@%s", identity);
|
snprintf(id, sizeof(id), "@%s", identity);
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0);
|
tf_ssb_db_get_latest_message_by_author(ssb, id, &sequence, NULL, 0);
|
||||||
tf_printf("IDENTITY %s: %d\n", id, (int)sequence);
|
tf_printf("IDENTITY %s: %d\n", id, sequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_test_replicate(const tf_test_options_t* options)
|
void tf_ssb_test_replicate(const tf_test_options_t* options)
|
||||||
@ -1154,7 +1154,7 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
|
|||||||
JSValue obj = JS_NewObject(context1);
|
JSValue obj = JS_NewObject(context1);
|
||||||
JS_SetPropertyStr(context1, obj, "type", JS_NewString(context1, "contact"));
|
JS_SetPropertyStr(context1, obj, "type", JS_NewString(context1, "contact"));
|
||||||
char self[k_id_base64_len];
|
char self[k_id_base64_len];
|
||||||
snprintf(self, sizeof(self), "%s", id1);
|
tf_string_set(self, sizeof(self), id1);
|
||||||
char contact[k_id_base64_len];
|
char contact[k_id_base64_len];
|
||||||
snprintf(contact, sizeof(contact), "@%s", public[0]);
|
snprintf(contact, sizeof(contact), "@%s", public[0]);
|
||||||
JS_SetPropertyStr(context1, obj, "contact", JS_NewString(context1, contact));
|
JS_SetPropertyStr(context1, obj, "contact", JS_NewString(context1, contact));
|
||||||
@ -1377,6 +1377,8 @@ void tf_ssb_test_invite(const tf_test_options_t* options)
|
|||||||
while (count0 != 3 || count1 != 3)
|
while (count0 != 3 || count1 != 3)
|
||||||
{
|
{
|
||||||
uv_run(&loop, UV_RUN_ONCE);
|
uv_run(&loop, UV_RUN_ONCE);
|
||||||
|
|
||||||
|
tf_printf("count0=%d count1=%d\n", count0, count1);
|
||||||
}
|
}
|
||||||
tf_ssb_set_main_thread(ssb0, false);
|
tf_ssb_set_main_thread(ssb0, false);
|
||||||
tf_ssb_set_main_thread(ssb1, false);
|
tf_ssb_set_main_thread(ssb1, false);
|
||||||
@ -1434,9 +1436,9 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
|
|||||||
clock_gettime(CLOCK_REALTIME, &end_time);
|
clock_gettime(CLOCK_REALTIME, &end_time);
|
||||||
tf_printf("insert = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9);
|
tf_printf("insert = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9);
|
||||||
|
|
||||||
int64_t max_sequence = 0;
|
int32_t max_sequence = 0;
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
||||||
tf_printf("max_sequence=%" PRId64 "\n", max_sequence);
|
tf_printf("max_sequence=%d\n", max_sequence);
|
||||||
assert(max_sequence == 5);
|
assert(max_sequence == 5);
|
||||||
|
|
||||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb0);
|
sqlite3* db = tf_ssb_acquire_db_writer(ssb0);
|
||||||
@ -1445,7 +1447,7 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
|
|||||||
|
|
||||||
max_sequence = 0;
|
max_sequence = 0;
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
||||||
tf_printf("max_sequence=%" PRId64 "\n", max_sequence);
|
tf_printf("max_sequence=%d\n", max_sequence);
|
||||||
assert(max_sequence == 4);
|
assert(max_sequence == 4);
|
||||||
|
|
||||||
tf_ssb_acquire_db_writer(ssb0);
|
tf_ssb_acquire_db_writer(ssb0);
|
||||||
@ -1454,7 +1456,7 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
|
|||||||
|
|
||||||
max_sequence = 0;
|
max_sequence = 0;
|
||||||
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
tf_ssb_db_get_latest_message_by_author(ssb0, id0, &max_sequence, NULL, 0);
|
||||||
tf_printf("max_sequence=%" PRId64 "\n", max_sequence);
|
tf_printf("max_sequence=%d\n", max_sequence);
|
||||||
assert(max_sequence == 0);
|
assert(max_sequence == 0);
|
||||||
|
|
||||||
uv_run(&loop, UV_RUN_DEFAULT);
|
uv_run(&loop, UV_RUN_DEFAULT);
|
||||||
@ -1619,7 +1621,7 @@ void tf_ssb_test_following_perf(const tf_test_options_t* options)
|
|||||||
|
|
||||||
uint64_t start = uv_hrtime();
|
uint64_t start = uv_hrtime();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (int i = 0; i < 100; i++)
|
for (int i = 0; i < 1000; i++)
|
||||||
{
|
{
|
||||||
const char** ids = tf_ssb_db_get_all_visible_identities(ssb, 2);
|
const char** ids = tf_ssb_db_get_all_visible_identities(ssb, 2);
|
||||||
while (ids[count])
|
while (ids[count])
|
||||||
|
95
src/task.c
@ -22,6 +22,7 @@
|
|||||||
#include "ares.h"
|
#include "ares.h"
|
||||||
#include "backtrace.h"
|
#include "backtrace.h"
|
||||||
#include "quickjs.h"
|
#include "quickjs.h"
|
||||||
|
#include "sodium/crypto_generichash.h"
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
#include "unzip.h"
|
#include "unzip.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
@ -84,16 +85,10 @@ typedef struct _promise_stack_t
|
|||||||
uint32_t hash;
|
uint32_t hash;
|
||||||
int count;
|
int count;
|
||||||
const char* stack;
|
const char* stack;
|
||||||
void* cstack[32];
|
void* cstack[31];
|
||||||
int cstack_count;
|
int cstack_count;
|
||||||
} promise_stack_t;
|
} promise_stack_t;
|
||||||
|
|
||||||
typedef struct _hitch_t
|
|
||||||
{
|
|
||||||
char name[256];
|
|
||||||
uint64_t duration_ns;
|
|
||||||
} hitch_t;
|
|
||||||
|
|
||||||
typedef struct _timeout_t timeout_t;
|
typedef struct _timeout_t timeout_t;
|
||||||
|
|
||||||
typedef struct _timeout_t
|
typedef struct _timeout_t
|
||||||
@ -170,8 +165,6 @@ typedef struct _tf_task_t
|
|||||||
|
|
||||||
timeout_t* timeouts;
|
timeout_t* timeouts;
|
||||||
|
|
||||||
hitch_t hitches[32];
|
|
||||||
|
|
||||||
uint64_t last_gc_ns;
|
uint64_t last_gc_ns;
|
||||||
int64_t last_gc_duration_ns;
|
int64_t last_gc_duration_ns;
|
||||||
} tf_task_t;
|
} tf_task_t;
|
||||||
@ -558,6 +551,7 @@ static JSValue _task_invokeExport_internal(tf_taskstub_t* from, tf_task_t* to, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
result = JS_Call(to->_context, function, this_val, length - 1, argument_array);
|
result = JS_Call(to->_context, function, this_val, length - 1, argument_array);
|
||||||
|
tf_task_check_jobs(to);
|
||||||
tf_trace_end(to->_trace);
|
tf_trace_end(to->_trace);
|
||||||
|
|
||||||
JS_FreeValue(to->_context, this_val);
|
JS_FreeValue(to->_context, this_val);
|
||||||
@ -930,28 +924,6 @@ char* tf_task_get_debug(tf_task_t* task)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* tf_task_get_hitches(tf_task_t* task)
|
|
||||||
{
|
|
||||||
JSContext* context = task->_context;
|
|
||||||
tf_trace_begin(task->_trace, __func__);
|
|
||||||
JSValue object = JS_NewObject(context);
|
|
||||||
for (int i = 0; i < tf_countof(task->hitches); i++)
|
|
||||||
{
|
|
||||||
if (*task->hitches[i].name)
|
|
||||||
{
|
|
||||||
JS_SetPropertyStr(context, object, task->hitches[i].name, JS_NewFloat64(context, task->hitches[i].duration_ns / 1e9));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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);
|
||||||
@ -1196,7 +1168,7 @@ static JSValue _tf_task_executeSource(tf_task_t* task, const char* source, const
|
|||||||
JSValue result = JS_Eval(task->_context, source, strlen(source), name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_ASYNC);
|
JSValue result = JS_Eval(task->_context, source, strlen(source), name, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_ASYNC);
|
||||||
if (!*task->_scriptName)
|
if (!*task->_scriptName)
|
||||||
{
|
{
|
||||||
snprintf(task->_scriptName, sizeof(task->_scriptName), "%s", name);
|
tf_string_set(task->_scriptName, sizeof(task->_scriptName), name);
|
||||||
}
|
}
|
||||||
tf_trace_end(task->_trace);
|
tf_trace_end(task->_trace);
|
||||||
return result;
|
return result;
|
||||||
@ -1250,6 +1222,7 @@ static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack
|
|||||||
{
|
{
|
||||||
memmove(task->_promise_stacks + index + 1, task->_promise_stacks + index, sizeof(promise_stack_t) * (task->_promise_stack_count - index));
|
memmove(task->_promise_stacks + index + 1, task->_promise_stacks + index, sizeof(promise_stack_t) * (task->_promise_stack_count - index));
|
||||||
}
|
}
|
||||||
|
count = tf_min(count, tf_countof(task->_promise_stacks[index].cstack));
|
||||||
task->_promise_stacks[index] = (promise_stack_t) { .hash = hash, .stack = tf_strdup(stack), .count = 1, .cstack_count = count };
|
task->_promise_stacks[index] = (promise_stack_t) { .hash = hash, .stack = tf_strdup(stack), .count = 1, .cstack_count = count };
|
||||||
memcpy(task->_promise_stacks[index].cstack, buffer, sizeof(void*) * count);
|
memcpy(task->_promise_stacks[index].cstack, buffer, sizeof(void*) * count);
|
||||||
task->_promise_stack_count++;
|
task->_promise_stack_count++;
|
||||||
@ -1283,6 +1256,8 @@ static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
|
|||||||
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
||||||
{
|
{
|
||||||
uint32_t stack_hash = 0;
|
uint32_t stack_hash = 0;
|
||||||
|
crypto_generichash_state state;
|
||||||
|
crypto_generichash_init(&state, NULL, 0, sizeof(stack_hash));
|
||||||
if (task->_promise_stack_debug)
|
if (task->_promise_stack_debug)
|
||||||
{
|
{
|
||||||
JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
|
JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
|
||||||
@ -1290,30 +1265,31 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
|||||||
JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
|
JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
|
||||||
size_t length = 0;
|
size_t length = 0;
|
||||||
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
|
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
|
||||||
stack_hash = tf_util_fnv32a((const void*)stack, (int)length, 0);
|
crypto_generichash_update(&state, (const void*)stack, (int)length);
|
||||||
void* buffer[32];
|
void* buffer[31];
|
||||||
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
|
||||||
stack_hash = tf_util_fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
|
crypto_generichash_update(&state, (const void*)buffer, sizeof(void*) * count);
|
||||||
_add_promise_stack(task, stack_hash, stack, buffer, count);
|
_add_promise_stack(task, stack_hash, stack, buffer, count);
|
||||||
JS_FreeCString(task->_context, stack);
|
JS_FreeCString(task->_context, stack);
|
||||||
JS_FreeValue(task->_context, stack_value);
|
JS_FreeValue(task->_context, stack_value);
|
||||||
JS_FreeValue(task->_context, exception);
|
JS_FreeValue(task->_context, exception);
|
||||||
JS_FreeValue(task->_context, error);
|
JS_FreeValue(task->_context, error);
|
||||||
}
|
}
|
||||||
|
crypto_generichash_final(&state, (void*)&stack_hash, sizeof(stack_hash));
|
||||||
|
|
||||||
promiseid_t promiseId;
|
promiseid_t promise_id;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
promiseId = task->_nextPromise++;
|
promise_id = task->_nextPromise++;
|
||||||
} while (_tf_task_find_promise(task, promiseId) || !promiseId);
|
} while (_tf_task_find_promise(task, promise_id) || !promise_id);
|
||||||
|
|
||||||
promise_t promise = {
|
promise_t promise = {
|
||||||
.id = promiseId,
|
.id = promise_id,
|
||||||
.values = { JS_NULL, JS_NULL },
|
.values = { JS_NULL, JS_NULL },
|
||||||
.stack_hash = stack_hash,
|
.stack_hash = stack_hash,
|
||||||
};
|
};
|
||||||
JSValue result = JS_NewPromiseCapability(task->_context, promise.values);
|
JSValue result = JS_NewPromiseCapability(task->_context, promise.values);
|
||||||
int index = tf_util_insert_index((void*)(intptr_t)promiseId, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
int index = tf_util_insert_index((void*)(intptr_t)promise_id, task->_promises, task->_promise_count, sizeof(promise_t), _promise_compare);
|
||||||
task->_promises = tf_resize_vec(task->_promises, sizeof(promise_t) * (task->_promise_count + 1));
|
task->_promises = tf_resize_vec(task->_promises, sizeof(promise_t) * (task->_promise_count + 1));
|
||||||
if (task->_promise_count - index)
|
if (task->_promise_count - index)
|
||||||
{
|
{
|
||||||
@ -1321,7 +1297,12 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
|
|||||||
}
|
}
|
||||||
task->_promises[index] = promise;
|
task->_promises[index] = promise;
|
||||||
task->_promise_count++;
|
task->_promise_count++;
|
||||||
*out_promise = promiseId;
|
*out_promise = promise_id;
|
||||||
|
|
||||||
|
if (task->_shutting_down)
|
||||||
|
{
|
||||||
|
tf_task_reject_promise(task, promise_id, JS_ThrowInternalError(task->_context, "Shutting down"));
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1383,7 +1364,7 @@ static void _promise_release_for_task(tf_task_t* task, taskid_t task_id)
|
|||||||
const promise_t* promise = &task->_promises[i];
|
const promise_t* promise = &task->_promises[i];
|
||||||
if (promise->task == task_id)
|
if (promise->task == task_id)
|
||||||
{
|
{
|
||||||
tf_task_reject_promise(task, promise->id, JS_ThrowInternalError(task->_context, "Task is gone."));
|
tf_task_reject_promise(task, promise->id, JS_ThrowInternalError(task->_context, "Task is gone"));
|
||||||
more = true;
|
more = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1661,24 +1642,6 @@ static void _tf_task_trace_to_parent(tf_trace_t* trace, const char* buffer, size
|
|||||||
tf_packetstream_send(tf_taskstub_get_stream(task->_parent), kTaskTrace, buffer, size);
|
tf_packetstream_send(tf_taskstub_get_stream(task->_parent), kTaskTrace, buffer, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _tf_task_record_hitch(const char* name, uint64_t duration_ns, void* user_data)
|
|
||||||
{
|
|
||||||
tf_task_t* task = user_data;
|
|
||||||
for (int i = 0; i < tf_countof(task->hitches); i++)
|
|
||||||
{
|
|
||||||
if (duration_ns > task->hitches[i].duration_ns)
|
|
||||||
{
|
|
||||||
if (i + 1 < tf_countof(task->hitches))
|
|
||||||
{
|
|
||||||
memmove(task->hitches + i + 1, task->hitches + i, sizeof(hitch_t) * (tf_countof(task->hitches) - i - 1));
|
|
||||||
}
|
|
||||||
snprintf(task->hitches[i].name, sizeof(task->hitches[i].name), "%s", name);
|
|
||||||
task->hitches[i].duration_ns = duration_ns;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tf_task_activate(tf_task_t* task)
|
void tf_task_activate(tf_task_t* task)
|
||||||
{
|
{
|
||||||
assert(!task->_activated);
|
assert(!task->_activated);
|
||||||
@ -1712,7 +1675,6 @@ void tf_task_activate(tf_task_t* task)
|
|||||||
tf_ssb_set_trace(task->_ssb, task->_trace);
|
tf_ssb_set_trace(task->_ssb, task->_trace);
|
||||||
tf_ssb_register(context, task->_ssb);
|
tf_ssb_register(context, task->_ssb);
|
||||||
tf_api_register(context);
|
tf_api_register(context);
|
||||||
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
|
|
||||||
|
|
||||||
if (task->_args)
|
if (task->_args)
|
||||||
{
|
{
|
||||||
@ -1819,6 +1781,11 @@ JSValue tf_taskstub_kill(tf_taskstub_t* stub);
|
|||||||
|
|
||||||
void tf_task_destroy(tf_task_t* task)
|
void tf_task_destroy(tf_task_t* task)
|
||||||
{
|
{
|
||||||
|
if (!task->_shutting_down)
|
||||||
|
{
|
||||||
|
tf_printf("tf_task_destroy\n");
|
||||||
|
}
|
||||||
|
|
||||||
task->_shutting_down = true;
|
task->_shutting_down = true;
|
||||||
|
|
||||||
while (task->_children)
|
while (task->_children)
|
||||||
@ -2005,7 +1972,7 @@ void tf_task_set_ssb_network_key(tf_task_t* task, const char* network_key)
|
|||||||
|
|
||||||
void tf_task_set_db_path(tf_task_t* task, const char* db_path)
|
void tf_task_set_db_path(tf_task_t* task, const char* db_path)
|
||||||
{
|
{
|
||||||
snprintf(task->_db_path, sizeof(task->_db_path), "%s", db_path);
|
tf_string_set(task->_db_path, sizeof(task->_db_path), db_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
|
void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
|
||||||
@ -2015,7 +1982,7 @@ void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
|
|||||||
unzClose(task->_zip);
|
unzClose(task->_zip);
|
||||||
task->_zip = NULL;
|
task->_zip = NULL;
|
||||||
}
|
}
|
||||||
snprintf(task->_zip_path, sizeof(task->_zip_path), "%s", zip_path);
|
tf_string_set(task->_zip_path, sizeof(task->_zip_path), zip_path);
|
||||||
if (zip_path)
|
if (zip_path)
|
||||||
{
|
{
|
||||||
task->_zip = unzOpen(zip_path);
|
task->_zip = unzOpen(zip_path);
|
||||||
@ -2025,7 +1992,7 @@ void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
|
|||||||
|
|
||||||
void tf_task_set_root_path(tf_task_t* task, const char* path)
|
void tf_task_set_root_path(tf_task_t* task, const char* path)
|
||||||
{
|
{
|
||||||
snprintf(task->_root_path, sizeof(task->_root_path), "%s", path ? path : "");
|
tf_string_set(task->_root_path, sizeof(task->_root_path), path ? path : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* tf_task_get_zip_path(tf_task_t* task)
|
const char* tf_task_get_zip_path(tf_task_t* task)
|
||||||
|
@ -319,13 +319,6 @@ bool tf_task_send_error_to_parent(tf_task_t* task, JSValue error);
|
|||||||
*/
|
*/
|
||||||
char* tf_task_get_debug(tf_task_t* task);
|
char* tf_task_get_debug(tf_task_t* task);
|
||||||
|
|
||||||
/**
|
|
||||||
** Get a report of hitches that occurred.
|
|
||||||
** @param task The task.
|
|
||||||
** @return A JSON report of recent hitches that must be freed with tf_free().
|
|
||||||
*/
|
|
||||||
char* tf_task_get_hitches(tf_task_t* task);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** A callback used to start an Android service.
|
** A callback used to start an Android service.
|
||||||
** @param pipe_fd A file descriptor with which to communicate with the invoking
|
** @param pipe_fd A file descriptor with which to communicate with the invoking
|
||||||
|
@ -104,7 +104,7 @@ void tf_trace_destroy(tf_trace_t* trace)
|
|||||||
|
|
||||||
void tf_trace_set_process_name(tf_trace_t* trace, const char* name)
|
void tf_trace_set_process_name(tf_trace_t* trace, const char* name)
|
||||||
{
|
{
|
||||||
snprintf(trace->process_name, sizeof(trace->process_name), "%s", name);
|
tf_string_set(trace->process_name, sizeof(trace->process_name), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_trace_raw(tf_trace_t* trace, const char* buffer, size_t size)
|
void tf_trace_raw(tf_trace_t* trace, const char* buffer, size_t size)
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
#include "backtrace.h"
|
#include "backtrace.h"
|
||||||
#include "openssl/sha.h"
|
|
||||||
#include "picohttpparser.h"
|
#include "picohttpparser.h"
|
||||||
#include "sodium/utils.h"
|
#include "sodium/utils.h"
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
@ -400,6 +399,10 @@ static const setting_t k_settings[] = {
|
|||||||
{ .name = "autologin", .type = "boolean", .description = "Whether mobile autologin is supported.", .default_value = { .kind = k_kind_bool, .bool_value = TF_IS_MOBILE != 0 } },
|
{ .name = "autologin", .type = "boolean", .description = "Whether mobile autologin is supported.", .default_value = { .kind = k_kind_bool, .bool_value = TF_IS_MOBILE != 0 } },
|
||||||
{ .name = "broadcast", .type = "boolean", .description = "Send network discovery broadcasts.", .default_value = { .kind = k_kind_bool, .bool_value = true } },
|
{ .name = "broadcast", .type = "boolean", .description = "Send network discovery broadcasts.", .default_value = { .kind = k_kind_bool, .bool_value = true } },
|
||||||
{ .name = "discovery", .type = "boolean", .description = "Receive network discovery broadcasts.", .default_value = { .kind = k_kind_bool, .bool_value = true } },
|
{ .name = "discovery", .type = "boolean", .description = "Receive network discovery broadcasts.", .default_value = { .kind = k_kind_bool, .bool_value = true } },
|
||||||
|
{ .name = "stay_connected",
|
||||||
|
.type = "boolean",
|
||||||
|
.description = "Whether to attempt to keep several peer connections open.",
|
||||||
|
.default_value = { .kind = k_kind_bool, .bool_value = false } },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const setting_t* _util_get_setting(const char* name, tf_setting_kind_t kind)
|
static const setting_t* _util_get_setting(const char* name, tf_setting_kind_t kind)
|
||||||
@ -500,11 +503,12 @@ void tf_util_document_settings(const char* line_prefix)
|
|||||||
JSValue tf_util_new_uint8_array(JSContext* context, const uint8_t* data, size_t size)
|
JSValue tf_util_new_uint8_array(JSContext* context, const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
JSValue array_buffer = JS_NewArrayBufferCopy(context, data, size);
|
JSValue array_buffer = JS_NewArrayBufferCopy(context, data, size);
|
||||||
JSValue global = JS_GetGlobalObject(context);
|
JSValue args[] = {
|
||||||
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
|
array_buffer,
|
||||||
JSValue result = JS_CallConstructor(context, constructor, 1, &array_buffer);
|
JS_NewInt64(context, 0),
|
||||||
JS_FreeValue(context, constructor);
|
JS_NewInt64(context, size),
|
||||||
JS_FreeValue(context, global);
|
};
|
||||||
|
JSValue result = JS_NewTypedArray(context, tf_countof(args), args, JS_TYPED_ARRAY_UINT8C);
|
||||||
JS_FreeValue(context, array_buffer);
|
JS_FreeValue(context, array_buffer);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -612,7 +616,7 @@ static int _tf_util_backtrace_single_callback(void* data, uintptr_t pc, const ch
|
|||||||
{
|
{
|
||||||
char** stack = data;
|
char** stack = data;
|
||||||
char line[256];
|
char line[256];
|
||||||
int length = snprintf(line, sizeof(line), "%s", function);
|
int length = (int)tf_string_set(line, sizeof(line), function);
|
||||||
int current = *stack ? strlen(*stack) : 0;
|
int current = *stack ? strlen(*stack) : 0;
|
||||||
*stack = tf_resize_vec(*stack, current + length + 1);
|
*stack = tf_resize_vec(*stack, current + length + 1);
|
||||||
memcpy(*stack + current, line, length + 1);
|
memcpy(*stack + current, line, length + 1);
|
||||||
@ -690,13 +694,17 @@ bool tf_util_is_mobile()
|
|||||||
return TF_IS_MOBILE != 0;
|
return TF_IS_MOBILE != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start)
|
size_t tf_string_set(char* buffer, size_t size, const char* string)
|
||||||
{
|
{
|
||||||
uint32_t result = 0x811c9dc5;
|
size_t length = string ? strlen(string) : 0;
|
||||||
for (int i = 0; i < length; i++)
|
length = tf_min(length, size - 1);
|
||||||
|
if (size)
|
||||||
{
|
{
|
||||||
result ^= ((const uint8_t*)buffer)[i];
|
if (length)
|
||||||
result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
|
{
|
||||||
|
memcpy(buffer, string, length);
|
||||||
|
}
|
||||||
|
buffer[length] = 0;
|
||||||
}
|
}
|
||||||
return result;
|
return length;
|
||||||
}
|
}
|
||||||
|
@ -225,12 +225,12 @@ void tf_util_document_settings(const char* line_prefix);
|
|||||||
bool tf_util_is_mobile();
|
bool tf_util_is_mobile();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Compute a 32-bit hash of a buffer.
|
** Populate a string buffer, truncating if necessary.
|
||||||
** @param buffer The data.
|
** @param buffer The buffer.
|
||||||
** @param length The size of the buffer in bytes.
|
** @param size The size of the buffer.
|
||||||
** @param start The hash seed.
|
** @param string The value to set.
|
||||||
** @return The computed hash.
|
** @return The number of bytes set, not including the NULL terminator.
|
||||||
*/
|
*/
|
||||||
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start);
|
size_t tf_string_set(char* buffer, size_t size, const char* string);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
#define VERSION_NUMBER "0.0.31-wip"
|
#define VERSION_NUMBER "0.0.32.1"
|
||||||
#define VERSION_NAME "This program kills fascists."
|
#define VERSION_NAME "This program kills fascists."
|
||||||
|
@ -80,6 +80,15 @@ try:
|
|||||||
select(driver, ['tf-auth', 'shadow_root', '#loginButton'], ('click',))
|
select(driver, ['tf-auth', 'shadow_root', '#loginButton'], ('click',))
|
||||||
select(driver, ['#document', 'frame', '=identity'])
|
select(driver, ['#document', 'frame', '=identity'])
|
||||||
|
|
||||||
|
driver.get('http://localhost:8888/')
|
||||||
|
select(driver, ['#document', 'frame', '//button[text()="Next"]'], ('click',))
|
||||||
|
select(driver, ['#document', 'frame', '//button[text()="Onward"]'], ('click',))
|
||||||
|
select(driver, ['#document', 'frame', '//button[text()="Got It"]'], ('click',))
|
||||||
|
select(driver, ['#document', 'frame', '//button[text()="Okay"]'], ('click',))
|
||||||
|
select(driver, ['#document', 'frame', '//button[text()="Let\'s Go!"]'], ('click',))
|
||||||
|
select(driver, ['//button[text()="✅ Allow"]'], ('click',))
|
||||||
|
select(driver, ['#document', 'frame', 'tf-app', 'shadow_root', '#tf-tab-news', 'shadow_root', '#tf-compose', 'shadow_root', '#edit'], ('send_keys', 'We made it to the ssb app.'))
|
||||||
|
|
||||||
driver.get('http://localhost:8888/~core/admin/')
|
driver.get('http://localhost:8888/~core/admin/')
|
||||||
select(driver, ['#document', 'frame', '#gs_room_name'], ('send_keys', 'test room'))
|
select(driver, ['#document', 'frame', '#gs_room_name'], ('send_keys', 'test room'))
|
||||||
select(driver, ['#document', 'frame', '//*[@id="gs_room_name"]/following-sibling::button'], ('click',))
|
select(driver, ['#document', 'frame', '//*[@id="gs_room_name"]/following-sibling::button'], ('click',))
|
||||||
@ -115,7 +124,7 @@ try:
|
|||||||
select(driver, ['tf-navigation', 'shadow_root', '#close_error'], ('click',))
|
select(driver, ['tf-navigation', 'shadow_root', '#close_error'], ('click',))
|
||||||
select(driver, ['tf-navigation', 'shadow_root', '=edit'], ('click',))
|
select(driver, ['tf-navigation', 'shadow_root', '=edit'], ('click',))
|
||||||
select(driver, ['#editor', '.cm-content'], ('click',))
|
select(driver, ['#editor', '.cm-content'], ('click',))
|
||||||
select(driver, ['#editor', '.cm-content'], ('send_keys', 'app.setDocument(\n\t"<div id=\'test-div\'>Hello, world!</div>"\n);'))
|
select(driver, ['#editor', '.cm-content'], ('send_keys', 'app.setDocument(\n\t`<div id=\'test-div\' style=\'color: white; font-size: xx-large\'>\n\t\tHello, world!\n\t</div>`\n);'))
|
||||||
select(driver, ['#save'], ('click',))
|
select(driver, ['#save'], ('click',))
|
||||||
|
|
||||||
select(driver, ['#document', 'frame', '#test-div'])
|
select(driver, ['#document', 'frame', '#test-div'])
|
||||||
|