Compare commits
32 Commits
v0.0.16
...
88d8e60511
Author | SHA1 | Date | |
---|---|---|---|
88d8e60511 | |||
439f07162e | |||
efe2b6cbd9 | |||
0aa1ed9464 | |||
cb94ed6a2a | |||
cf187ee46b | |||
3e71fc20fd | |||
f3601321f7 | |||
540059368c | |||
7ce89123f7 | |||
e3c7c86212 | |||
794804e27f | |||
6d89c1da6e | |||
d059554464 | |||
3a392d4a9f | |||
e3071b372a | |||
18bd279b0c | |||
5b93db7463 | |||
5b7e5eb91b | |||
78ca383e3c | |||
c1eed9ada3 | |||
8d6feb5394 | |||
42994f8977 | |||
f0a871e1f8 | |||
a710c30572 | |||
c991763b00 | |||
72dae14f87 | |||
5800340762 | |||
c5f5adcac6 | |||
591642efb3 | |||
6182ffa1d4 | |||
402a898d96 |
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,8 +1,10 @@
|
||||
**/node_modules
|
||||
.keys
|
||||
.zsign_cache/
|
||||
db.*
|
||||
deps/ios_toolchain/
|
||||
deps/openssl/
|
||||
dist/
|
||||
.keys
|
||||
**/node_modules
|
||||
out
|
||||
*.swo
|
||||
*.swp
|
||||
.zsign_cache/
|
||||
|
37
CONTRIBUTING.md
Normal file
37
CONTRIBUTING.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Contributing to Tilde Friends
|
||||
|
||||
Thank you for your interest in Tilde Friends.
|
||||
|
||||
Above all, Tilde Friends aims to be a fun, safe place to play. When that is at
|
||||
odds with the course of development, we will work through it with respectful
|
||||
communication.
|
||||
|
||||
## How can I contribute?
|
||||
|
||||
The nature of Tilde Friends makes for a wide range of ways to contribute
|
||||
|
||||
- Just use it. Really, just kicking the tires will probably shake out issues
|
||||
in useful ways at this point.
|
||||
- Report and comment on bugs: https://dev.tildefriends.net/issues.
|
||||
- Make apps. You don't need my permission to make and share apps with Tilde
|
||||
Friends. I hope that an ecosystem of good apps grows outside of this
|
||||
repository. If you want to recreate better versions of the stock apps, just
|
||||
do it. If you make a better ssb app or whatever and drop me a line however
|
||||
is most convenient for you, I will probably take a look and consider
|
||||
replacing the stock one with it.
|
||||
- Write about it. Docs in the git repository, blog posts, private messages to
|
||||
me with ideas...really there is no wrong answer. Just make some noise, and
|
||||
I'll do my best to incorporate or otherwise link your feedback and make the
|
||||
most of it.
|
||||
- Write C code in the git repository. I'm really striving for it to be the
|
||||
case that other people don't really need to meddle in there, but if you can
|
||||
help out, I will gladly review your pull requests via
|
||||
https://dev.tildefriends.net/pulls.
|
||||
|
||||
## Best practices
|
||||
|
||||
- The C code is formatted with clang-format. Run `make format`.
|
||||
- The rest is formatted with prettier. Run `npm run prettier`.
|
||||
- We strive to have code compile on all platforms with no warnings and run with
|
||||
no sanitizer issues.
|
||||
- There are tests. Run `out/debug/tildefriends test`.
|
27
GNUmakefile
27
GNUmakefile
@ -3,9 +3,9 @@
|
||||
MAKEFLAGS += --warn-undefined-variables
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
VERSION_CODE := 16
|
||||
VERSION_NUMBER := 0.0.16
|
||||
VERSION_NAME := Now with 38% more process.
|
||||
VERSION_CODE := 17
|
||||
VERSION_NUMBER := 0.0.17-wip
|
||||
VERSION_NAME := Please enjoy responsibly.
|
||||
|
||||
PROJECT = tildefriends
|
||||
BUILD_DIR ?= out
|
||||
@ -55,7 +55,7 @@ CFLAGS += \
|
||||
|
||||
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0
|
||||
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-34
|
||||
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.1.10909125
|
||||
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.2.11394342
|
||||
ANDROID_MIN_SDK_VERSION := 24
|
||||
ANDROID_TARGET_SDK_VERSION := 34
|
||||
|
||||
@ -726,7 +726,7 @@ out/apk/TildeFriends-arm-%.unsigned.apk:
|
||||
@cp out/apk/classes.dex out/apk-arm-$(BUILD_TYPE)/
|
||||
@cd out/apk-arm-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
|
||||
@zip -u $@.zip -q $(RAW_FILES)
|
||||
@rm -f $@ && $(ANDROID_BUILD_TOOLS)/zipalign -z 4 $@.zip $@
|
||||
@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@
|
||||
|
||||
out/apk/TildeFriends-x86-%.unsigned.apk:
|
||||
@mkdir -p $(dir $@) out/apk-x86-$(BUILD_TYPE)/lib/x86_64/ out/apk-x86-$(BUILD_TYPE)/lib/x86/
|
||||
@ -739,13 +739,18 @@ out/apk/TildeFriends-x86-%.unsigned.apk:
|
||||
@cp out/apk/classes.dex out/apk-x86-$(BUILD_TYPE)/
|
||||
@cd out/apk-x86-$(BUILD_TYPE) && zip -u ../../$@.zip -q -9 -r . && cd ../../
|
||||
@zip -u $@.zip -q $(RAW_FILES)
|
||||
@rm -f $@ && $(ANDROID_BUILD_TOOLS)/zipalign -z 4 $@.zip $@
|
||||
@$(ANDROID_BUILD_TOOLS)/zipalign -f 4 $@.zip $@
|
||||
|
||||
out/%.apk: out/apk/%.unsigned.apk
|
||||
@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 $@ $<
|
||||
|
||||
release-apk: out/TildeFriends-arm-release.apk out/TildeFriends-x86-release.apk
|
||||
out/%.zopfli.apk: out/%.apk
|
||||
@echo "[zopfli] $(notdir $@)"
|
||||
$(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
|
||||
|
||||
release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk
|
||||
.PHONY: release-apk
|
||||
|
||||
releaseapkgo: out/TildeFriends-arm-release.apk
|
||||
@ -859,9 +864,9 @@ dist: release-apk iosrelease-ipa
|
||||
--exclude=deps/zlib/doc \
|
||||
-caf dist/tildefriends-$(VERSION_NUMBER).tar.xz out/tildefriends-$(VERSION_NUMBER)
|
||||
@echo "[cp] TildeFriends-x86-$(VERSION_NUMBER).apk"
|
||||
@cp out/TildeFriends-x86-release.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk
|
||||
@cp out/TildeFriends-x86-release.zopfli.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk"
|
||||
@cp out/TildeFriends-arm-release.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk
|
||||
@cp out/TildeFriends-arm-release.zopfli.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
|
||||
@cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
|
||||
.PHONY: dist
|
||||
@ -877,6 +882,10 @@ format:
|
||||
@clang-format -i $(wildcard src/*.c src/*.h src/*.m)
|
||||
.PHONY: format
|
||||
|
||||
prettier:
|
||||
@npm run prettier
|
||||
.PHONY: prettier
|
||||
|
||||
docs:
|
||||
@doxygen
|
||||
.PHONY: docs
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💻",
|
||||
"previous": "&RdVEsVscZm3aWzcMrEZS8mskO5tUmvaEUihex2MMfZQ=.sha256"
|
||||
"previous": "&icsPplXHgmpkbNWyo/WTvRdT/A8BXxW4lJixOtP4ueQ=.sha256"
|
||||
}
|
||||
|
@ -28,10 +28,10 @@ async function fetch_shared_apps() {
|
||||
|
||||
await ssb.sqlAsync(
|
||||
`
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages_fts('"application/tildefriends"')
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
ORDER BY timestamp
|
||||
ORDER BY messages.timestamp
|
||||
`,
|
||||
[],
|
||||
function (row) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📝",
|
||||
"previous": "&2hdIDbBrAg63T2X1MzdGSF7yiqHvlnfF0PnInQLp0DA=.sha256"
|
||||
"previous": "&b//KqE4Vx6kOSBRODK1p/8wjOLKZJ+CBB5IkaBt5YsM=.sha256"
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "👟"
|
||||
"emoji": "👟",
|
||||
"previous": "&zhV2BKLLZ6aG3HsVyRTs9ESLxE2lb0e7TDE7PobnyNY=.sha256"
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🐌",
|
||||
"previous": "&DUxMMCJcuhm6S9jg/eKgEyWodkITu6Tg9g5I5wgLWFU=.sha256"
|
||||
"previous": "&Xs1X5TzLCk6KVr+5IDc80JAHYxJyoD10cXKBUYpFqWQ=.sha256"
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ class TfElement extends LitElement {
|
||||
let abouts = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT
|
||||
messages.*
|
||||
messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM
|
||||
messages,
|
||||
json_each(?1) AS following
|
||||
@ -118,7 +118,7 @@ class TfElement extends LitElement {
|
||||
json_extract(messages.content, '$.type') = 'about'
|
||||
UNION
|
||||
SELECT
|
||||
messages.*
|
||||
messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM
|
||||
messages,
|
||||
json_each(?2) AS following
|
||||
@ -158,7 +158,7 @@ class TfElement extends LitElement {
|
||||
async fetch_new_message(id) {
|
||||
let messages = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.*
|
||||
SELECT messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.id = ?
|
||||
@ -221,7 +221,7 @@ class TfElement extends LitElement {
|
||||
this.tags = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
recent AS (SELECT id, content FROM messages
|
||||
recent AS (SELECT id, json(content) AS content FROM messages
|
||||
WHERE messages.timestamp > ? AND json_extract(content, '$.type') = 'post'
|
||||
ORDER BY timestamp DESC LIMIT 1024),
|
||||
recent_channels AS (SELECT recent.id, '#' || json_extract(content, '$.channel') AS tag
|
||||
|
@ -262,7 +262,7 @@ class TfComposeElement extends LitElement {
|
||||
try {
|
||||
let rows = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.content FROM messages_fts(?)
|
||||
SELECT json(messages.content) FROM messages_fts(?)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
WHERE messages.content LIKE ?
|
||||
ORDER BY timestamp DESC LIMIT 10
|
||||
|
@ -494,7 +494,7 @@ ${JSON.stringify(mention, null, 2)}</pre
|
||||
<tf-compose
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
root=${this.message.content.root || this.message.id}
|
||||
root=${content.root || this.message.id}
|
||||
branch=${this.message.id}
|
||||
.drafts=${this.drafts}
|
||||
@tf-discard=${this.discard_reply}
|
||||
@ -681,7 +681,7 @@ ${JSON.stringify(content, null, 2)}</pre
|
||||
<tf-compose
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
root=${this.message.content.root || this.message.id}
|
||||
root=${content.root || this.message.id}
|
||||
branch=${this.message.id}
|
||||
.drafts=${this.drafts}
|
||||
@tf-discard=${this.discard_reply}
|
||||
|
@ -29,7 +29,7 @@ class TfTabMentionsElement extends LitElement {
|
||||
console.log('Loading...', this.whoami);
|
||||
let results = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages_fts(?)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
|
@ -33,12 +33,12 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
if (this.hash.startsWith('#@')) {
|
||||
let r = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH mine AS (SELECT messages.*
|
||||
WITH mine AS (SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
WHERE messages.author = ?
|
||||
ORDER BY sequence DESC
|
||||
LIMIT 20)
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM mine
|
||||
JOIN messages_refs ON mine.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
@ -51,11 +51,11 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
} else if (this.hash.startsWith('#%')) {
|
||||
return await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.*
|
||||
SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
WHERE id = ?1
|
||||
UNION
|
||||
SELECT messages.*
|
||||
SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages JOIN messages_refs
|
||||
ON messages.id = messages_refs.message
|
||||
WHERE messages_refs.ref = ?1
|
||||
@ -69,17 +69,17 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
promises.push(
|
||||
tfrpc.rpc.query(
|
||||
`
|
||||
WITH news AS (SELECT messages.*
|
||||
WITH news AS (SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.timestamp > ? AND messages.timestamp < ?
|
||||
ORDER BY messages.timestamp DESC)
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.message
|
||||
JOIN messages ON messages_refs.ref = messages.id
|
||||
@ -107,18 +107,18 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
this.start_time = last_start_time - 24 * 60 * 60 * 1000;
|
||||
let more = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH news AS (SELECT messages.*
|
||||
WITH news AS (SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.timestamp > ?
|
||||
AND messages.timestamp <= ?
|
||||
ORDER BY messages.timestamp DESC)
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.message
|
||||
JOIN messages ON messages_refs.ref = messages.id
|
||||
|
@ -35,7 +35,7 @@ class TfTabSearchElement extends LitElement {
|
||||
await tfrpc.rpc.setHash('#q=' + encodeURIComponent(query));
|
||||
let results = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT messages.*
|
||||
SELECT messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages_fts(?)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "📝",
|
||||
"previous": "&/wl8HE2jZShRXTYEVYRrK3pjHwi41Wbxl9HoSJaQP6Y=.sha256"
|
||||
"previous": "&DnfuAUGzzalSh9NgZXnzDc9Ru5aM0omfRJ4h27jYw4k=.sha256"
|
||||
}
|
||||
|
@ -578,6 +578,7 @@ async function getProcessBlob(blobId, key, options) {
|
||||
imports.ssb = Object.fromEntries(
|
||||
Object.keys(ssb).map((key) => [key, ssb[key].bind(ssb)])
|
||||
);
|
||||
imports.ssb.port = tildefriends.ssb_port;
|
||||
imports.ssb.createIdentity = function () {
|
||||
if (
|
||||
process.credentials &&
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.unprompted.tildefriends"
|
||||
android:versionCode="16"
|
||||
android:versionName="0.0.16">
|
||||
android:versionCode="17"
|
||||
android:versionName="0.0.17-wip">
|
||||
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="34"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application
|
||||
|
@ -7,8 +7,13 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/** A JS context. */
|
||||
typedef struct JSContext JSContext;
|
||||
|
||||
/**
|
||||
** Register the bcrypt script interface.
|
||||
** @param context The JS context.
|
||||
*/
|
||||
void tf_bcrypt_register(JSContext* context);
|
||||
|
||||
/** @} */
|
||||
|
16
src/bip39.h
16
src/bip39.h
@ -11,7 +11,23 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
** Convert a key from bytes to words.
|
||||
** @param bytes A raw binary representation of a key.
|
||||
** @param bytes_size The size of bytes.
|
||||
** @param[out] out_words A human-readable English word representation of a key.
|
||||
** @param words_size The size of the out_words buffer.
|
||||
** @return True if the key was successfully converted.
|
||||
*/
|
||||
bool tf_bip39_bytes_to_words(const uint8_t* bytes, size_t bytes_size, char* out_words, size_t words_size);
|
||||
|
||||
/**
|
||||
** Convert a key from words to bytes.
|
||||
** @param words A space-separated list of English words forming a key.
|
||||
** @param[out] out_bytes A buffer to receive the raw binary form of the key.
|
||||
** @param bytes_size The size of the out_bytes buffer.
|
||||
** @return True if the key was successfully converted.
|
||||
*/
|
||||
bool tf_bip39_words_to_bytes(const char* words, uint8_t* out_bytes, size_t bytes_size);
|
||||
|
||||
/** @} */
|
||||
|
@ -10,6 +10,8 @@ enum
|
||||
{
|
||||
k_bip39_words_count = 2048
|
||||
};
|
||||
|
||||
/** An array of words used for BIP39-encoding a key. */
|
||||
extern const char* k_bip39_words[k_bip39_words_count];
|
||||
|
||||
/** @} */
|
||||
|
@ -6,8 +6,13 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/** A JS context. */
|
||||
typedef struct JSContext JSContext;
|
||||
|
||||
/**
|
||||
** Register the database script interface.
|
||||
** @param context The JS context.
|
||||
*/
|
||||
void tf_database_register(JSContext* context);
|
||||
|
||||
/** @} */
|
||||
|
@ -234,6 +234,7 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
|
||||
},
|
||||
.size = k_file_read_max,
|
||||
};
|
||||
memset(req + 1, 0, k_file_read_max);
|
||||
int result = uv_fs_open(tf_task_get_loop(task), &req->fs, file_name, UV_FS_O_RDONLY, 0, _file_read_open_callback);
|
||||
if (result < 0)
|
||||
{
|
||||
|
@ -8,12 +8,33 @@
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
/** A JS context. */
|
||||
typedef struct JSContext JSContext;
|
||||
/** A task. */
|
||||
typedef struct _tf_task_t tf_task_t;
|
||||
|
||||
/**
|
||||
** Register the file script interface.
|
||||
** @param context The JS context.
|
||||
*/
|
||||
void tf_file_register(JSContext* context);
|
||||
|
||||
/**
|
||||
** Asynchronously stat() a file.
|
||||
** @param task The running task.
|
||||
** @param path The path to the file to stat().
|
||||
** @param callback A function that will be called with the stat result.
|
||||
** @param user_data User data that will be passed to the callback.
|
||||
*/
|
||||
void tf_file_stat(tf_task_t* task, const char* path, void (*callback)(tf_task_t* task, const char* path, int result, const uv_stat_t* stat, void* user_data), void* user_data);
|
||||
|
||||
/**
|
||||
** Asynchronously read a file's contents.
|
||||
** @param task The running task.
|
||||
** @param path The path to the file.
|
||||
** @param callback A callback that will be called with the file contents.
|
||||
** @param user_data User data that will be provided to the callback.
|
||||
*/
|
||||
void tf_file_read(tf_task_t* task, const char* path, void (*callback)(tf_task_t* task, const char* path, int result, const void* data, void* user_data), void* user_data);
|
||||
|
||||
/** @} */
|
||||
|
15
src/http.c
15
src/http.c
@ -619,15 +619,28 @@ int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls, tf_http_cle
|
||||
|
||||
if (r == 0)
|
||||
{
|
||||
#if defined(__HAIKU__)
|
||||
/*
|
||||
** Binding to IPv6 here fails with an odd error, and the socket
|
||||
** becomes unusable. Since we probably want localhost only
|
||||
** on this single-user OS, let's just assume IPv4.
|
||||
*/
|
||||
struct sockaddr_in addr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr = { .s_addr = INADDR_ANY },
|
||||
.sin_port = ntohs(port),
|
||||
};
|
||||
#else
|
||||
struct sockaddr_in6 addr = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = IN6ADDR_ANY_INIT,
|
||||
.sin6_port = ntohs(port),
|
||||
};
|
||||
#endif
|
||||
r = uv_tcp_bind(&listener->tcp, (struct sockaddr*)&addr, 0);
|
||||
if (r)
|
||||
{
|
||||
tf_printf("uv_tcp_bind: %s\n", uv_strerror(r));
|
||||
tf_printf("%s:%d: uv_tcp_bind: %s\n", __FILE__, __LINE__, uv_strerror(r));
|
||||
}
|
||||
}
|
||||
|
||||
|
154
src/http.h
154
src/http.h
@ -14,38 +14,88 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/** An HTTP connection. */
|
||||
typedef struct _tf_http_connection_t tf_http_connection_t;
|
||||
|
||||
/** An HTTP request. */
|
||||
typedef struct _tf_http_request_t tf_http_request_t;
|
||||
|
||||
/** An HTTP instance. */
|
||||
typedef struct _tf_http_t tf_http_t;
|
||||
|
||||
/** A TLS context. */
|
||||
typedef struct _tf_tls_context_t tf_tls_context_t;
|
||||
|
||||
/** A trace instance. */
|
||||
typedef struct _tf_trace_t tf_trace_t;
|
||||
|
||||
/** An event loop. */
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
|
||||
/**
|
||||
** A callback called when receiving a websocket message.
|
||||
** @param request The HTTP request.
|
||||
** @param op_code The type of websocket message.
|
||||
** @param data The payload.
|
||||
** @param size The size of the payload in bytes.
|
||||
*/
|
||||
typedef void(tf_http_message_callback)(tf_http_request_t* request, int op_code, const void* data, size_t size);
|
||||
|
||||
/**
|
||||
** A callback called when a request closes.
|
||||
** @param request The HTTP request.
|
||||
*/
|
||||
typedef void(tf_http_close_callback)(tf_http_request_t* request);
|
||||
|
||||
/**
|
||||
** A callback called when an HTTP request is received.
|
||||
** @param request The HTTP request.
|
||||
*/
|
||||
typedef void(tf_http_callback_t)(tf_http_request_t* request);
|
||||
|
||||
/**
|
||||
** A callback called when the HTTP instance is destroyed.
|
||||
** @param user_data User data provided with the callback.
|
||||
*/
|
||||
typedef void(tf_http_cleanup_t)(void* user_data);
|
||||
|
||||
/**
|
||||
** An HTTP request.
|
||||
*/
|
||||
typedef struct _tf_http_request_t
|
||||
{
|
||||
/** The HTTP instance this request belongs to. */
|
||||
tf_http_t* http;
|
||||
/** The HTTP connection associated with this request. */
|
||||
tf_http_connection_t* connection;
|
||||
/** True if this is an HTTPS session. */
|
||||
bool is_tls;
|
||||
/** The HTTP method of the request (GET/POST/...). */
|
||||
const char* method;
|
||||
/** The HTTP request path. */
|
||||
const char* path;
|
||||
/** The raw HTTP query string. */
|
||||
const char* query;
|
||||
/** The HTTP request body. */
|
||||
void* body;
|
||||
/** The length of the HTTP request body. */
|
||||
size_t content_length;
|
||||
/** Header storage. Can also be accessed with tf_http_request_get_header(). */
|
||||
struct phr_header* headers;
|
||||
/** The number of headers stored. */
|
||||
int headers_count;
|
||||
/** A callback to be called when receiving a websocket message. */
|
||||
tf_http_message_callback* on_message;
|
||||
/** A callback to be called when the connection is closed. */
|
||||
tf_http_close_callback* on_close;
|
||||
/** Extra storage for user data. */
|
||||
void* context;
|
||||
/** User data available to callbacks. */
|
||||
void* user_data;
|
||||
/** The number of times tf_http_request_ref() has been called without tf_http_request_unref(). */
|
||||
int ref_count;
|
||||
} tf_http_request_t;
|
||||
|
||||
typedef void(tf_http_callback_t)(tf_http_request_t* request);
|
||||
typedef void(tf_http_cleanup_t)(void* user_data);
|
||||
|
||||
/**
|
||||
** Create an HTTP server using the given libuv loop.
|
||||
** @param loop A libuv loop to use.
|
||||
@ -53,22 +103,120 @@ typedef void(tf_http_cleanup_t)(void* user_data);
|
||||
*/
|
||||
tf_http_t* tf_http_create(uv_loop_t* loop);
|
||||
|
||||
/**
|
||||
** Register a trace instance with the HTTP instance to record the begin and end
|
||||
** time of request handlers.
|
||||
** @param http The HTTP instance to trace.
|
||||
** @param trace The trace instance to use, or NULL to disable.
|
||||
*/
|
||||
void tf_http_set_trace(tf_http_t* http, tf_trace_t* trace);
|
||||
|
||||
/**
|
||||
** Begin listening for HTTP requests on a new port. May be called multiple
|
||||
** times to listen on multiple ports.
|
||||
** @param http The HTTP instance.
|
||||
** @param port The port on which to listen, or 0 to assign a free port.
|
||||
** @param tls An optional TLS context to use for HTTPS requests.
|
||||
** @param cleanup A function called when the HTTP instance is being cleaned up.
|
||||
** @param user_data User data passed to the cleanup callback.
|
||||
** @return The port number on which the HTTP instance is now listening.
|
||||
*/
|
||||
int tf_http_listen(tf_http_t* http, int port, tf_tls_context_t* tls, tf_http_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Add an HTTP request handler.
|
||||
** @param http The HTTP instance.
|
||||
** @param pattern The prefix that the path of incoming requests must match to use this handler.
|
||||
** @param callback The function to be called to handle the request.
|
||||
** @param cleanup A function to be called when the request is complete.
|
||||
** @param user_data User data to pass to the callbacks.
|
||||
*/
|
||||
void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_t* callback, tf_http_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Respond to an HTTP request.
|
||||
** @param request The request.
|
||||
** @param status The HTTP status with which to respond.
|
||||
** @param headers Headers to include in the response. Content-Length will be added internally.
|
||||
** @param headers_count The number of headers. The headers array must have
|
||||
** twice as many entries as this value, since it is both keys and values.
|
||||
** @param body The response body or NULL.
|
||||
** @param content_length The length of the response body.
|
||||
*/
|
||||
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length);
|
||||
|
||||
/**
|
||||
** Get the request body content.
|
||||
** @param request An incoming request.
|
||||
** @param out_data The request body content. Valid until the request is completed.
|
||||
** @return The size of the request body content.
|
||||
*/
|
||||
size_t tf_http_get_body(const tf_http_request_t* request, const void** out_data);
|
||||
|
||||
/**
|
||||
** Destroy an HTTP instance.
|
||||
** @param http The HTTP instance.
|
||||
*/
|
||||
void tf_http_destroy(tf_http_t* http);
|
||||
|
||||
/**
|
||||
** Set instance-wide HTTP user data and a callback to clean it up.
|
||||
** @param http The HTTP instance.
|
||||
** @param user_data The user data.
|
||||
** @param cleanup The cleanup callback.
|
||||
*/
|
||||
void tf_http_set_user_data(tf_http_t* http, void* user_data, tf_http_cleanup_t* cleanup);
|
||||
|
||||
/**
|
||||
** Get HTTP instance user data previous set with tf_http_set_user_data().
|
||||
** @param http The HTTP instance.
|
||||
** @return The user data.
|
||||
*/
|
||||
void* tf_http_get_user_data(tf_http_t* http);
|
||||
|
||||
/**
|
||||
** Increment a requests refcount to keep it around after its callback.
|
||||
** tf_http_respond() may be called at a later time, and tf_http_request_unref()
|
||||
** must eventually be called in order to not leak the request.
|
||||
** @param request The request to retain.
|
||||
*/
|
||||
void tf_http_request_ref(tf_http_request_t* request);
|
||||
|
||||
/**
|
||||
** Decrement a requests refcount. tf_http_request_ref() must have been previously called.
|
||||
** @param request The request.
|
||||
*/
|
||||
void tf_http_request_unref(tf_http_request_t* request);
|
||||
|
||||
/**
|
||||
** Get the value of a header from an HTTP request.
|
||||
** @param request The request.
|
||||
** @param name The header key. Matched case insensitively.
|
||||
** @return The value or NULL.
|
||||
*/
|
||||
const char* tf_http_request_get_header(tf_http_request_t* request, const char* name);
|
||||
|
||||
/**
|
||||
** Send a websocket message.
|
||||
** @param request The HTTP request which was previously updated to a websocket
|
||||
** session with tf_http_request_websocket_upgrade().
|
||||
** @param data The message data.
|
||||
** @param size The size of data.
|
||||
*/
|
||||
void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size);
|
||||
|
||||
/**
|
||||
** Upgrade an HTTP request to a websocket session.
|
||||
** @param request The HTTP request.
|
||||
*/
|
||||
void tf_http_request_websocket_upgrade(tf_http_request_t* request);
|
||||
|
||||
/**
|
||||
** Get standard HTTP status text for common HTTP statuses. 200 = "OK", 404 =
|
||||
** "File not found", etc.
|
||||
** @param status The HTTP status.
|
||||
** @return The status text or NULL.
|
||||
*/
|
||||
const char* tf_http_status_text(int status);
|
||||
|
||||
/** @} */
|
||||
|
@ -13,6 +13,12 @@
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
/**
|
||||
** Register the HTTP script interface. Also registers a number of built-in
|
||||
** request handlers. An ongoing project is to move the JS request handlers
|
||||
** into C, after which point this will only do the latter.
|
||||
** @param context The JS context.
|
||||
*/
|
||||
void tf_httpd_register(JSContext* context);
|
||||
|
||||
/** @} */
|
||||
|
@ -5,6 +5,11 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/**
|
||||
** Log a message using printf-style formatting. Tries to use appropriate
|
||||
** platform-specific functionality where necessary to make sure output goes
|
||||
** somewhere that it can be seen.
|
||||
*/
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
#define tf_printf(...) __android_log_print(ANDROID_LOG_INFO, "tildefriends", __VA_ARGS__)
|
||||
|
12
src/main.c
12
src/main.c
@ -166,7 +166,7 @@ static int _tf_command_import(const char* file, int argc, char* argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path);
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
|
||||
if (optind < argc)
|
||||
{
|
||||
for (int i = optind; i < argc; i++)
|
||||
@ -232,7 +232,7 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path);
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db_path, NULL);
|
||||
if (optind < argc)
|
||||
{
|
||||
for (int i = optind; i < argc; i++)
|
||||
@ -270,6 +270,7 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
|
||||
typedef struct tf_run_args_t
|
||||
{
|
||||
const char* script;
|
||||
const char* network_key;
|
||||
int ssb_port;
|
||||
int http_port;
|
||||
int https_port;
|
||||
@ -296,6 +297,7 @@ static int _tf_run_task(const tf_run_args_t* args, int index)
|
||||
tf_task_set_trusted(task, true);
|
||||
tf_printf("setting zip path to %s\n", args->zip);
|
||||
tf_task_set_zip_path(task, args->zip);
|
||||
tf_task_set_ssb_network_key(task, args->network_key);
|
||||
tf_task_set_ssb_port(task, args->ssb_port ? args->ssb_port + index : 0);
|
||||
tf_task_set_http_port(task, args->http_port ? args->http_port + index : 0);
|
||||
tf_task_set_https_port(task, args->https_port ? args->https_port + index : 0);
|
||||
@ -419,6 +421,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
static const struct option k_options[] = {
|
||||
{ "script", required_argument, NULL, 's' },
|
||||
{ "ssb-port", required_argument, NULL, 'b' },
|
||||
{ "ssb-network-key", required_argument, NULL, 'k' },
|
||||
{ "http-port", required_argument, NULL, 'p' },
|
||||
{ "https-port", required_argument, NULL, 'q' },
|
||||
{ "db-path", required_argument, NULL, 'd' },
|
||||
@ -429,7 +432,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
{ "verbose", no_argument, NULL, 'v' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
};
|
||||
int c = getopt_long(argc, argv, "s:b:p:q:d:n:a:oz:vh", k_options, NULL);
|
||||
int c = getopt_long(argc, argv, "s:b:k:p:q:d:n:a:oz:vh", k_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
@ -445,6 +448,9 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
case 's':
|
||||
args.script = optarg;
|
||||
break;
|
||||
case 'k':
|
||||
args.network_key = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
args.ssb_port = atoi(optarg);
|
||||
break;
|
||||
|
95
src/mem.h
95
src/mem.h
@ -12,43 +12,138 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/** JS malloc functions. */
|
||||
typedef struct JSMallocFunctions JSMallocFunctions;
|
||||
|
||||
/**
|
||||
** Do early setup for memory tracking.
|
||||
** @param tracking Whether tracking will be enabled, which adds a time and
|
||||
** memory cost of storing stack traces for every allocation.
|
||||
*/
|
||||
void tf_mem_startup(bool tracking);
|
||||
|
||||
/**
|
||||
** Clean up the memory system.
|
||||
*/
|
||||
void tf_mem_shutdown();
|
||||
|
||||
/**
|
||||
** Register a custom allocator with libuv.
|
||||
*/
|
||||
void tf_mem_replace_uv_allocator();
|
||||
|
||||
/**
|
||||
** Get the number of bytes currently allocated by libuv.
|
||||
** @return The allocated size in bytes.
|
||||
*/
|
||||
size_t tf_mem_get_uv_malloc_size();
|
||||
|
||||
/**
|
||||
** Register a custom allocator with OpenSSL.
|
||||
*/
|
||||
void tf_mem_replace_tls_allocator();
|
||||
|
||||
/**
|
||||
** Get the number of bytes currently allocated by OpenSSL.
|
||||
** @return The allocated size in bytes.
|
||||
*/
|
||||
size_t tf_mem_get_tls_malloc_size();
|
||||
|
||||
/**
|
||||
** Register a custom allocator with SQLite.
|
||||
*/
|
||||
void tf_mem_replace_sqlite_allocator();
|
||||
|
||||
/**
|
||||
** Get the number of bytes currently allocated by SQLite.
|
||||
** @return The allocated size in bytes.
|
||||
*/
|
||||
size_t tf_mem_get_sqlite_malloc_size();
|
||||
|
||||
/**
|
||||
** Get the number of bytes currently allocated by tf_malloc().
|
||||
** @return The allocated size in bytes.
|
||||
*/
|
||||
size_t tf_mem_get_tf_malloc_size();
|
||||
|
||||
/**
|
||||
** Allocate memory. Like malloc() but with more tracking.
|
||||
** @param size The number of bytes to allocate.
|
||||
** @return The allocated memory.
|
||||
*/
|
||||
void* tf_malloc(size_t size);
|
||||
|
||||
/**
|
||||
** Reallocate memory. Like realloc() but with more tracking.
|
||||
** @param ptr The previously allocated memory or NULL.
|
||||
** @param size The new desired size.
|
||||
** @return The new allocation.
|
||||
*/
|
||||
void* tf_realloc(void* ptr, size_t size);
|
||||
|
||||
/**
|
||||
** Free memory allocated by tf_malloc() or tf_realloc().
|
||||
** @param ptr The allocation.
|
||||
*/
|
||||
void tf_free(void* ptr);
|
||||
|
||||
/**
|
||||
** Duplicate a string.
|
||||
** @param string The string to copy.
|
||||
** @return The newly allocated string. Free with tf_free().
|
||||
*/
|
||||
char* tf_strdup(const char* string);
|
||||
|
||||
/**
|
||||
** Resize a vector. Like tf_realloc() but overallocatess and prefers not to
|
||||
** shrink in order to speed up repeated growth.
|
||||
** @param ptr The allocation to resize.
|
||||
** @param size The desired new size.
|
||||
** @return The new allocation.
|
||||
*/
|
||||
void* tf_resize_vec(void* ptr, size_t size);
|
||||
|
||||
/**
|
||||
** Populate a struct with custom JS allocation functions.
|
||||
** @param[out] out The struct to receive the functions.
|
||||
*/
|
||||
void tf_get_js_malloc_functions(JSMallocFunctions* out);
|
||||
|
||||
/**
|
||||
** Get the number of bytes currently allocated by JS allocators.
|
||||
** @return The allocated size in bytes.
|
||||
*/
|
||||
size_t tf_mem_get_js_malloc_size();
|
||||
|
||||
/**
|
||||
** Call a function for every live memory allocation.
|
||||
** @param callback The callback to call.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_mem_walk_allocations(void (*callback)(void* ptr, size_t size, int frames_count, void* const* frames, void* user_data), void* user_data);
|
||||
|
||||
/**
|
||||
** Information about a memory allocation.
|
||||
*/
|
||||
typedef struct _tf_mem_allocation_t
|
||||
{
|
||||
/** A hash of the callstack used for determining uniqueness. */
|
||||
uint32_t stack_hash;
|
||||
/** The number of instances of this allocation. */
|
||||
int count;
|
||||
/** The size of this allocation. */
|
||||
size_t size;
|
||||
/** The callstack from which this allocation was made. */
|
||||
void* frames[32];
|
||||
/** The number of frames in the callstack. */
|
||||
int frames_count;
|
||||
} tf_mem_allocation_t;
|
||||
|
||||
/**
|
||||
** Generate a list of live allocations.
|
||||
** @param[out] out_count The number of allocations returned.
|
||||
** @return An array of allocation information. Free with tf_free().
|
||||
*/
|
||||
tf_mem_allocation_t* tf_mem_summarize_allocations(int* out_count);
|
||||
|
||||
/** @} */
|
||||
|
@ -8,19 +8,67 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/** A handle to a UNIX-style pipe. */
|
||||
typedef struct uv_pipe_s uv_pipe_t;
|
||||
|
||||
/** A packet stream instance. */
|
||||
typedef struct _tf_packetstream_t tf_packetstream_t;
|
||||
|
||||
/**
|
||||
** A function called when a packet is received.
|
||||
** @param packet_type The type of the packet as specified by the sender.
|
||||
** @param begin The beginning of the data.
|
||||
** @param length The length of the data.
|
||||
** @param user_data User data.
|
||||
*/
|
||||
typedef void(tf_packetstream_onreceive_t)(int packet_type, const char* begin, size_t length, void* user_data);
|
||||
|
||||
/**
|
||||
** Create a packet stream.
|
||||
** @return The packet stream.
|
||||
*/
|
||||
tf_packetstream_t* tf_packetstream_create();
|
||||
|
||||
/**
|
||||
** Destroy a packet stream.
|
||||
** @param stream The packet stream.
|
||||
*/
|
||||
void tf_packetstream_destroy(tf_packetstream_t* stream);
|
||||
|
||||
/**
|
||||
** Start a packet stream receiving.
|
||||
** @param stream The packet stream.
|
||||
*/
|
||||
void tf_packetstream_start(tf_packetstream_t* stream);
|
||||
|
||||
/**
|
||||
** Send a discrete packet over a packet stream.
|
||||
** @param stream The packet stream.
|
||||
** @param packet_type The type of the packet.
|
||||
** @param begin The start of the packet data.
|
||||
** @param length The length of the packet data.
|
||||
*/
|
||||
void tf_packetstream_send(tf_packetstream_t* stream, int packet_type, const char* begin, size_t length);
|
||||
|
||||
/**
|
||||
** Register the callback to be called when a message is received.
|
||||
** @param stream The packet stream.
|
||||
** @param callback The callback function.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_packetstream_set_on_receive(tf_packetstream_t* stream, tf_packetstream_onreceive_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Close a packet stream.
|
||||
** @param stream The packet stream.
|
||||
*/
|
||||
void tf_packetstream_close(tf_packetstream_t* stream);
|
||||
|
||||
/**
|
||||
** Get the internal pipe object from a packet stream.
|
||||
** @param stream The packet stream.
|
||||
** @return The pipe.
|
||||
*/
|
||||
uv_pipe_t* tf_packetstream_get_pipe(tf_packetstream_t* stream);
|
||||
|
||||
/** @} */
|
||||
|
@ -9,10 +9,29 @@
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
/** A task. */
|
||||
typedef struct _tf_task_t tf_task_t;
|
||||
/** A handle to a remote task. */
|
||||
typedef struct _tf_taskstub_t tf_taskstub_t;
|
||||
|
||||
/**
|
||||
** Store JS data in a binary blob.
|
||||
** @param task The calling task.
|
||||
** @param to The handle to the task to which the data will be sent.
|
||||
** @param[out] out_buffer Populated with the stored data.
|
||||
** @param[out] out_size Populated with the size of out_data.
|
||||
** @param value The JS value to store.
|
||||
*/
|
||||
void tf_serialize_store(tf_task_t* task, tf_taskstub_t* to, void** out_buffer, size_t* out_size, JSValue value);
|
||||
|
||||
/**
|
||||
** Retrieve JS data from a binary blob.
|
||||
** @param task The calling task.
|
||||
** @param from The handle to the task from which the data was received.
|
||||
** @param buffer The data.
|
||||
** @param size The size of the data.
|
||||
** @return The received JS data.
|
||||
*/
|
||||
JSValue tf_serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size);
|
||||
|
||||
/** @} */
|
||||
|
@ -8,8 +8,23 @@
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
/**
|
||||
** Register the socket script interface.
|
||||
** @param context The JS context.
|
||||
** @return The Socket constructor.
|
||||
*/
|
||||
JSValue tf_socket_register(JSContext* context);
|
||||
|
||||
/**
|
||||
** Get the number of active socket objects.
|
||||
** @return The count.
|
||||
*/
|
||||
int tf_socket_get_count();
|
||||
|
||||
/**
|
||||
** Get the number of connected socket objects.
|
||||
** @return the count.
|
||||
*/
|
||||
int tf_socket_get_open_count();
|
||||
|
||||
/** @} */
|
||||
|
124
src/ssb.c
124
src/ssb.c
@ -42,8 +42,7 @@ static_assert(k_id_base64_len == sodium_base64_ENCODED_LEN(9 + crypto_box_PUBLIC
|
||||
static_assert(k_id_bin_len == crypto_box_PUBLICKEYBYTES, "k_id_bin_len");
|
||||
static_assert(k_blob_id_len == (sodium_base64_ENCODED_LEN(crypto_hash_sha256_BYTES, sodium_base64_VARIANT_ORIGINAL) + 8), "k_blob_id_len");
|
||||
|
||||
const uint8_t k_ssb_network[] = { 0xd4, 0xa1, 0xcb, 0x88, 0xa6, 0x6f, 0x02, 0xf8, 0xdb, 0x63, 0x5c, 0xe2, 0x64, 0x41, 0xcc, 0x5d, 0xac, 0x1b, 0x08, 0x42, 0x0c, 0xea, 0xac, 0x23,
|
||||
0x08, 0x39, 0xb7, 0x55, 0x84, 0x5a, 0x9f, 0xfb };
|
||||
const char* k_ssb_network_string = "d4a1cb88a66f02f8db635ce26441cc5dac1b08420ceaac230839b755845a9ffb";
|
||||
|
||||
const char* k_ssb_type_names[] = {
|
||||
"binary",
|
||||
@ -203,6 +202,8 @@ typedef struct _tf_ssb_t
|
||||
uv_timer_t trace_timer;
|
||||
uv_tcp_t server;
|
||||
|
||||
uint8_t network_key[32];
|
||||
|
||||
uint8_t pub[crypto_sign_PUBLICKEYBYTES];
|
||||
uint8_t priv[crypto_sign_SECRETKEYBYTES];
|
||||
|
||||
@ -489,7 +490,7 @@ static void _tf_ssb_write(tf_ssb_connection_t* connection, void* data, size_t si
|
||||
static void _tf_ssb_connection_send_identity(tf_ssb_connection_t* connection, uint8_t* hmac, uint8_t* pubkey)
|
||||
{
|
||||
memcpy(connection->serverepub, pubkey, sizeof(connection->serverepub));
|
||||
if (crypto_auth_hmacsha512256_verify(hmac, connection->serverepub, 32, k_ssb_network) != 0)
|
||||
if (crypto_auth_hmacsha512256_verify(hmac, connection->serverepub, 32, connection->ssb->network_key) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "invalid server hello");
|
||||
return;
|
||||
@ -519,10 +520,10 @@ static void _tf_ssb_connection_send_identity(tf_ssb_connection_t* connection, ui
|
||||
uint8_t hash[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(hash, shared_secret_ab, sizeof(shared_secret_ab));
|
||||
|
||||
uint8_t msg[sizeof(k_ssb_network) + sizeof(connection->serverpub) + crypto_hash_sha256_BYTES];
|
||||
memcpy(msg, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(msg + sizeof(k_ssb_network), connection->serverpub, sizeof(connection->serverpub));
|
||||
memcpy(msg + sizeof(k_ssb_network) + sizeof(connection->serverpub), hash, sizeof(hash));
|
||||
uint8_t msg[sizeof(connection->ssb->network_key) + sizeof(connection->serverpub) + crypto_hash_sha256_BYTES];
|
||||
memcpy(msg, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(msg + sizeof(connection->ssb->network_key), connection->serverpub, sizeof(connection->serverpub));
|
||||
memcpy(msg + sizeof(connection->ssb->network_key) + sizeof(connection->serverpub), hash, sizeof(hash));
|
||||
|
||||
unsigned long long siglen;
|
||||
if (crypto_sign_detached(connection->detached_signature_A, &siglen, msg, sizeof(msg), connection->ssb->priv) != 0)
|
||||
@ -536,10 +537,10 @@ static void _tf_ssb_connection_send_identity(tf_ssb_connection_t* connection, ui
|
||||
memcpy(tosend + sizeof(connection->detached_signature_A), connection->ssb->pub, sizeof(connection->ssb->pub));
|
||||
uint8_t nonce[crypto_secretbox_NONCEBYTES] = { 0 };
|
||||
|
||||
uint8_t tohash[sizeof(k_ssb_network) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB)];
|
||||
memcpy(tohash, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(tohash + sizeof(k_ssb_network), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(tohash + sizeof(k_ssb_network) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
uint8_t tohash[sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB)];
|
||||
memcpy(tohash, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(tohash + sizeof(connection->ssb->network_key), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(tohash + sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
uint8_t hash2[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(hash2, tohash, sizeof(tohash));
|
||||
|
||||
@ -1136,11 +1137,11 @@ static void _tf_ssb_connection_verify_identity(tf_ssb_connection_t* connection,
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t tohash[sizeof(k_ssb_network) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB) + sizeof(shared_secret_Ab)];
|
||||
memcpy(tohash, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(tohash + sizeof(k_ssb_network), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(tohash + sizeof(k_ssb_network) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
memcpy(tohash + sizeof(k_ssb_network) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB), shared_secret_Ab, sizeof(shared_secret_Ab));
|
||||
uint8_t tohash[sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB) + sizeof(shared_secret_Ab)];
|
||||
memcpy(tohash, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(tohash + sizeof(connection->ssb->network_key), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(tohash + sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
memcpy(tohash + sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB), shared_secret_Ab, sizeof(shared_secret_Ab));
|
||||
uint8_t hash2[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(hash2, tohash, sizeof(tohash));
|
||||
|
||||
@ -1164,11 +1165,11 @@ static void _tf_ssb_connection_verify_identity(tf_ssb_connection_t* connection,
|
||||
uint8_t hash3[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(hash3, shared_secret_ab, sizeof(shared_secret_ab));
|
||||
|
||||
uint8_t msg[sizeof(k_ssb_network) + sizeof(connection->detached_signature_A) + sizeof(connection->ssb->pub) + sizeof(hash3)];
|
||||
memcpy(msg, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(msg + sizeof(k_ssb_network), connection->detached_signature_A, sizeof(connection->detached_signature_A));
|
||||
memcpy(msg + sizeof(k_ssb_network) + sizeof(connection->detached_signature_A), connection->ssb->pub, sizeof(connection->ssb->pub));
|
||||
memcpy(msg + sizeof(k_ssb_network) + sizeof(connection->detached_signature_A) + sizeof(connection->ssb->pub), hash3, sizeof(hash3));
|
||||
uint8_t msg[sizeof(connection->ssb->network_key) + sizeof(connection->detached_signature_A) + sizeof(connection->ssb->pub) + sizeof(hash3)];
|
||||
memcpy(msg, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(msg + sizeof(connection->ssb->network_key), connection->detached_signature_A, sizeof(connection->detached_signature_A));
|
||||
memcpy(msg + sizeof(connection->ssb->network_key) + sizeof(connection->detached_signature_A), connection->ssb->pub, sizeof(connection->ssb->pub));
|
||||
memcpy(msg + sizeof(connection->ssb->network_key) + sizeof(connection->detached_signature_A) + sizeof(connection->ssb->pub), hash3, sizeof(hash3));
|
||||
if (crypto_sign_verify_detached(m, msg, sizeof(msg), connection->serverpub) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "unable to verify server identity");
|
||||
@ -1176,7 +1177,7 @@ static void _tf_ssb_connection_verify_identity(tf_ssb_connection_t* connection,
|
||||
}
|
||||
|
||||
uint8_t nonce2[crypto_auth_hmacsha512256_BYTES];
|
||||
if (crypto_auth_hmacsha512256(nonce2, connection->epub, sizeof(connection->epub), k_ssb_network) != 0)
|
||||
if (crypto_auth_hmacsha512256(nonce2, connection->epub, sizeof(connection->epub), connection->ssb->network_key) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "unable to compute client recv nonce");
|
||||
return;
|
||||
@ -1184,7 +1185,7 @@ static void _tf_ssb_connection_verify_identity(tf_ssb_connection_t* connection,
|
||||
memcpy(connection->nonce, nonce2, sizeof(connection->nonce));
|
||||
|
||||
uint8_t nonce3[crypto_auth_hmacsha512256_BYTES];
|
||||
if (crypto_auth_hmacsha512256(nonce3, connection->serverepub, sizeof(connection->serverepub), k_ssb_network) != 0)
|
||||
if (crypto_auth_hmacsha512256(nonce3, connection->serverepub, sizeof(connection->serverepub), connection->ssb->network_key) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "unable to compute client send nonce");
|
||||
return;
|
||||
@ -1290,11 +1291,11 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(sizeof(k_ssb_network) == crypto_auth_KEYBYTES, "network key size");
|
||||
uint8_t tohash[sizeof(k_ssb_network) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB)];
|
||||
memcpy(tohash, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(tohash + sizeof(k_ssb_network), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(tohash + sizeof(k_ssb_network) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
static_assert(sizeof(connection->ssb->network_key) == crypto_auth_KEYBYTES, "network key size");
|
||||
uint8_t tohash[sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB)];
|
||||
memcpy(tohash, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(tohash + sizeof(connection->ssb->network_key), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(tohash + sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
uint8_t hash2[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(hash2, tohash, sizeof(tohash));
|
||||
|
||||
@ -1335,10 +1336,10 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne
|
||||
uint8_t hash3[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(hash3, shared_secret_ab, sizeof(shared_secret_ab));
|
||||
|
||||
uint8_t msg[sizeof(k_ssb_network) + sizeof(connection->ssb->pub) + sizeof(hash3)];
|
||||
memcpy(msg, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(msg + sizeof(k_ssb_network), connection->ssb->pub, sizeof(connection->ssb->pub));
|
||||
memcpy(msg + sizeof(k_ssb_network) + sizeof(connection->ssb->pub), hash3, sizeof(hash3));
|
||||
uint8_t msg[sizeof(connection->ssb->network_key) + sizeof(connection->ssb->pub) + sizeof(hash3)];
|
||||
memcpy(msg, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(msg + sizeof(connection->ssb->network_key), connection->ssb->pub, sizeof(connection->ssb->pub));
|
||||
memcpy(msg + sizeof(connection->ssb->network_key) + sizeof(connection->ssb->pub), hash3, sizeof(hash3));
|
||||
if (crypto_sign_verify_detached(detached_signature_A, msg, sizeof(msg), connection->serverpub) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "unable to verify client identity");
|
||||
@ -1346,7 +1347,7 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne
|
||||
}
|
||||
|
||||
uint8_t nonce2[crypto_auth_hmacsha512256_BYTES];
|
||||
if (crypto_auth_hmacsha512256(nonce2, connection->epub, sizeof(connection->epub), k_ssb_network) != 0)
|
||||
if (crypto_auth_hmacsha512256(nonce2, connection->epub, sizeof(connection->epub), connection->ssb->network_key) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "unable to compute initial recv nonce as server");
|
||||
return;
|
||||
@ -1354,7 +1355,7 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne
|
||||
memcpy(connection->nonce, nonce2, sizeof(connection->nonce));
|
||||
|
||||
uint8_t nonce3[crypto_auth_hmacsha512256_BYTES];
|
||||
if (crypto_auth_hmacsha512256(nonce3, connection->serverepub, sizeof(connection->serverepub), k_ssb_network) != 0)
|
||||
if (crypto_auth_hmacsha512256(nonce3, connection->serverepub, sizeof(connection->serverepub), connection->ssb->network_key) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "unable to compute initial send nonce as server");
|
||||
return;
|
||||
@ -1362,11 +1363,11 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne
|
||||
memcpy(connection->send_nonce, nonce3, sizeof(connection->send_nonce));
|
||||
|
||||
int detached_signature_A_size = 64;
|
||||
uint8_t sign_b[sizeof(k_ssb_network) + detached_signature_A_size + sizeof(connection->serverpub) + sizeof(hash3)];
|
||||
memcpy(sign_b, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(sign_b + sizeof(k_ssb_network), detached_signature_A, detached_signature_A_size);
|
||||
memcpy(sign_b + sizeof(k_ssb_network) + detached_signature_A_size, connection->serverpub, sizeof(connection->serverpub));
|
||||
memcpy(sign_b + sizeof(k_ssb_network) + detached_signature_A_size + sizeof(connection->serverpub), hash3, sizeof(hash3));
|
||||
uint8_t sign_b[sizeof(connection->ssb->network_key) + detached_signature_A_size + sizeof(connection->serverpub) + sizeof(hash3)];
|
||||
memcpy(sign_b, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(sign_b + sizeof(connection->ssb->network_key), detached_signature_A, detached_signature_A_size);
|
||||
memcpy(sign_b + sizeof(connection->ssb->network_key) + detached_signature_A_size, connection->serverpub, sizeof(connection->serverpub));
|
||||
memcpy(sign_b + sizeof(connection->ssb->network_key) + detached_signature_A_size + sizeof(connection->serverpub), hash3, sizeof(hash3));
|
||||
|
||||
uint8_t detached_signature_B[crypto_sign_BYTES];
|
||||
unsigned long long siglen;
|
||||
@ -1390,11 +1391,11 @@ static void _tf_ssb_connection_verify_client_identity(tf_ssb_connection_t* conne
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t key_buf[sizeof(k_ssb_network) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB) + sizeof(shared_secret_Ab)];
|
||||
memcpy(key_buf, k_ssb_network, sizeof(k_ssb_network));
|
||||
memcpy(key_buf + sizeof(k_ssb_network), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(key_buf + sizeof(k_ssb_network) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
memcpy(key_buf + sizeof(k_ssb_network) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB), shared_secret_Ab, sizeof(shared_secret_Ab));
|
||||
uint8_t key_buf[sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB) + sizeof(shared_secret_Ab)];
|
||||
memcpy(key_buf, connection->ssb->network_key, sizeof(connection->ssb->network_key));
|
||||
memcpy(key_buf + sizeof(connection->ssb->network_key), shared_secret_ab, sizeof(shared_secret_ab));
|
||||
memcpy(key_buf + sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab), shared_secret_aB, sizeof(shared_secret_aB));
|
||||
memcpy(key_buf + sizeof(connection->ssb->network_key) + sizeof(shared_secret_ab) + sizeof(shared_secret_aB), shared_secret_Ab, sizeof(shared_secret_Ab));
|
||||
|
||||
uint8_t key_hash[crypto_hash_sha256_BYTES];
|
||||
crypto_hash_sha256(key_hash, key_buf, sizeof(key_buf));
|
||||
@ -1941,7 +1942,7 @@ static void _tf_ssb_connection_on_tcp_recv_internal(tf_ssb_connection_t* connect
|
||||
uint8_t* hmac = hello;
|
||||
memcpy(connection->serverepub, hello + crypto_box_PUBLICKEYBYTES, crypto_box_PUBLICKEYBYTES);
|
||||
static_assert(sizeof(connection->serverepub) == crypto_box_PUBLICKEYBYTES, "serverepub size");
|
||||
if (crypto_auth_hmacsha512256_verify(hmac, connection->serverepub, 32, k_ssb_network) != 0)
|
||||
if (crypto_auth_hmacsha512256_verify(hmac, connection->serverepub, 32, connection->ssb->network_key) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "crypto_auth_hmacsha512256_verify failed");
|
||||
}
|
||||
@ -1998,7 +1999,7 @@ static void _tf_ssb_connection_client_send_hello(tf_ssb_connection_t* connection
|
||||
}
|
||||
|
||||
uint8_t a[crypto_auth_hmacsha512256_BYTES];
|
||||
if (crypto_auth_hmacsha512256(a, connection->epub, sizeof(connection->epub), k_ssb_network) != 0)
|
||||
if (crypto_auth_hmacsha512256(a, connection->epub, sizeof(connection->epub), connection->ssb->network_key) != 0)
|
||||
{
|
||||
_tf_ssb_connection_close(connection, "failed to create hello message");
|
||||
return;
|
||||
@ -2106,11 +2107,17 @@ void tf_ssb_get_stats(tf_ssb_t* ssb, tf_ssb_stats_t* out_stats)
|
||||
ssb->rpc_out = 0;
|
||||
}
|
||||
|
||||
tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path)
|
||||
tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path, const char* network_key)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_malloc(sizeof(tf_ssb_t));
|
||||
memset(ssb, 0, sizeof(*ssb));
|
||||
|
||||
const char* actual_key = network_key ? network_key : k_ssb_network_string;
|
||||
if (sodium_hex2bin(ssb->network_key, sizeof(ssb->network_key), actual_key, strlen(actual_key), ": ", NULL, NULL))
|
||||
{
|
||||
tf_printf("Error parsing network key: %s.", actual_key);
|
||||
}
|
||||
|
||||
char buffer[8] = { 0 };
|
||||
size_t buffer_size = sizeof(buffer);
|
||||
ssb->store_debug_messages = uv_os_getenv("TF_DEBUG_CLOSE", buffer, &buffer_size) == 0 && strcmp(buffer, "1") == 0;
|
||||
@ -2817,38 +2824,45 @@ static void _tf_ssb_broadcast_timer(uv_timer_t* timer)
|
||||
}
|
||||
}
|
||||
|
||||
void tf_ssb_server_open(tf_ssb_t* ssb, int port)
|
||||
int tf_ssb_server_open(tf_ssb_t* ssb, int port)
|
||||
{
|
||||
if (ssb->server.data)
|
||||
{
|
||||
tf_printf("Already listening.\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssb->server.data = ssb;
|
||||
if (uv_tcp_init(ssb->loop, &ssb->server) != 0)
|
||||
{
|
||||
tf_printf("uv_tcp_init failed\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr = { 0 };
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
addr.sin_addr.s_addr = INADDR_ANY;
|
||||
if (uv_tcp_bind(&ssb->server, (struct sockaddr*)&addr, 0) != 0)
|
||||
int status = uv_tcp_bind(&ssb->server, (struct sockaddr*)&addr, 0);
|
||||
if (status != 0)
|
||||
{
|
||||
tf_printf("uv_tcp_bind failed\n");
|
||||
return;
|
||||
tf_printf("%s:%d: uv_tcp_bind failed: %s\n", __FILE__, __LINE__, uv_strerror(status));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int status = uv_listen((uv_stream_t*)&ssb->server, SOMAXCONN, _tf_ssb_on_connection);
|
||||
status = uv_listen((uv_stream_t*)&ssb->server, SOMAXCONN, _tf_ssb_on_connection);
|
||||
if (status != 0)
|
||||
{
|
||||
tf_printf("uv_listen failed: %s\n", uv_strerror(status));
|
||||
/* TODO: cleanup */
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sockaddr_storage name = { 0 };
|
||||
int size = (int)sizeof(name);
|
||||
status = uv_tcp_getsockname(&ssb->server, (struct sockaddr*)&name, &size);
|
||||
int assigned_port = ntohs(((struct sockaddr_in*)&name)->sin_port);
|
||||
return status == 0 ? assigned_port : 0;
|
||||
}
|
||||
|
||||
void tf_ssb_server_close(tf_ssb_t* ssb)
|
||||
|
@ -7,14 +7,50 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
|
||||
/** An SSB connections tracker instance. */
|
||||
typedef struct _tf_ssb_connections_t tf_ssb_connections_t;
|
||||
|
||||
/**
|
||||
** Create a connection tracker.
|
||||
** @param ssb The SSB instance.
|
||||
** @return The connection tracker instance.
|
||||
*/
|
||||
tf_ssb_connections_t* tf_ssb_connections_create(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Destroy a connection tracker.
|
||||
** @param connections The connection tracker to destroy.
|
||||
*/
|
||||
void tf_ssb_connections_destroy(tf_ssb_connections_t* connections);
|
||||
|
||||
/**
|
||||
** Store a connection in the connection tracker.
|
||||
** @param connections The connection tracker.
|
||||
** @param host The host name or address.
|
||||
** @param port The network port number.
|
||||
** @param key The identity on the other end of the connection.
|
||||
*/
|
||||
void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* host, int port, const char* key);
|
||||
|
||||
/**
|
||||
** Record that a connection was recently attempted.
|
||||
** @param connections The connection tracker.
|
||||
** @param host The host name or address.
|
||||
** @param port The network port number.
|
||||
** @param key The identity on the other end of the connection.
|
||||
*/
|
||||
void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const char* host, int port, const char* key);
|
||||
|
||||
/**
|
||||
** Record that a connection recently succeeded.
|
||||
** @param connections The connection tracker.
|
||||
** @param host The host name or address.
|
||||
** @param port The network port number.
|
||||
** @param key The identity on the other end of the connection.
|
||||
*/
|
||||
void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const char* host, int port, const char* key);
|
||||
|
||||
/** @} */
|
||||
|
34
src/ssb.db.c
34
src/ssb.db.c
@ -101,11 +101,28 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
" timestamp REAL,"
|
||||
" previous TEXT,"
|
||||
" hash TEXT,"
|
||||
" content TEXT,"
|
||||
" content BLOB,"
|
||||
" signature TEXT,"
|
||||
" sequence_before_author INTEGER,"
|
||||
" UNIQUE(author, sequence)"
|
||||
")");
|
||||
|
||||
if (_tf_ssb_db_has_rows(db, "SELECT name FROM pragma_table_info('messages') WHERE name = 'content' AND type == 'TEXT'"))
|
||||
{
|
||||
tf_printf("converting to JSONB\n");
|
||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ai_refs");
|
||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad_refs");
|
||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ai");
|
||||
_tf_ssb_db_exec(db, "DROP TRIGGER IF EXISTS messages_ad");
|
||||
_tf_ssb_db_exec(db, "DROP TABLE IF EXISTS messages_fts");
|
||||
_tf_ssb_db_exec(db, "BEGIN TRANSACTION");
|
||||
_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN contentb BLOB");
|
||||
_tf_ssb_db_exec(db, "UPDATE messages SET contentb = jsonb(content)");
|
||||
_tf_ssb_db_exec(db, "ALTER TABLE messages DROP COLUMN content");
|
||||
_tf_ssb_db_exec(db, "ALTER TABLE messages RENAME COLUMN contentb TO content");
|
||||
_tf_ssb_db_exec(db, "COMMIT TRANSACTION");
|
||||
}
|
||||
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_id_index ON messages (author, id)");
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_sequence_index ON messages (author, sequence)");
|
||||
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS messages_author_timestamp_index ON messages (author, timestamp)");
|
||||
@ -161,11 +178,12 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
if (populate_fts)
|
||||
{
|
||||
tf_printf("Populating full-text search...\n");
|
||||
_tf_ssb_db_exec(db, "INSERT INTO messages_fts (rowid, content) SELECT rowid, content FROM messages");
|
||||
_tf_ssb_db_exec(db, "INSERT INTO messages_fts (rowid, content) SELECT rowid, json(content) FROM messages");
|
||||
tf_printf("Done.\n");
|
||||
}
|
||||
|
||||
_tf_ssb_db_exec(db, "CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN INSERT INTO messages_fts(rowid, content) VALUES (new.rowid, new.content); END");
|
||||
_tf_ssb_db_exec(
|
||||
db, "CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN INSERT INTO messages_fts(rowid, content) VALUES (new.rowid, json(new.content)); END");
|
||||
_tf_ssb_db_exec(db,
|
||||
"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");
|
||||
@ -287,7 +305,7 @@ static int64_t _tf_ssb_db_store_message_raw(tf_ssb_t* ssb, const char* id, const
|
||||
|
||||
if (_tf_ssb_db_previous_message_exists(db, author, sequence, previous))
|
||||
{
|
||||
const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, sequence_before_author) VALUES (?, ?, ?, ?, ?, ?, "
|
||||
const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, sequence_before_author) VALUES (?, ?, ?, ?, ?, jsonb(?), "
|
||||
"?, ?, ?) ON CONFLICT DO NOTHING";
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
@ -557,7 +575,7 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
const char* query = "SELECT content FROM messages WHERE id = ?";
|
||||
const char* query = "SELECT json(content) FROM messages WHERE id = ?";
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK && sqlite3_step(statement) == SQLITE_ROW)
|
||||
@ -764,7 +782,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(
|
||||
{
|
||||
bool found = false;
|
||||
sqlite3_stmt* statement;
|
||||
const char* query = "SELECT id, timestamp, content FROM messages WHERE author = ?1 AND sequence = ?2";
|
||||
const char* query = "SELECT id, timestamp, json(content) FROM messages WHERE author = ?1 AND sequence = ?2";
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -1481,8 +1499,8 @@ JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys)
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(
|
||||
db, "SELECT previous, author, id, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, "SELECT previous, author, id, sequence, timestamp, hash, json(content), signature, sequence_before_author FROM messages WHERE id = ?", -1, &statement,
|
||||
NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
|
251
src/ssb.db.h
251
src/ssb.db.h
@ -13,65 +13,314 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
|
||||
/**
|
||||
** Initialize the database writer for an SSB instance.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_db_init(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Configure an opened SQLite database for reading.
|
||||
*/
|
||||
void tf_ssb_db_init_reader(sqlite3* db);
|
||||
|
||||
/**
|
||||
** Get message content by ID.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The message identifier.
|
||||
** @param[out] out_blob Populated with the message content.
|
||||
** @param[out] out_size POpulated with the size of the message content.
|
||||
** @return true If the message content was found and retrieved.
|
||||
*/
|
||||
bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size);
|
||||
|
||||
/**
|
||||
** Determine whether a blob is in the database by ID.
|
||||
** @param ssb The SSB instasnce.
|
||||
** @param id The blob identifier.
|
||||
** @return true If the blob is in the database.
|
||||
*/
|
||||
bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id);
|
||||
|
||||
/**
|
||||
** Retrieve a blob from the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The blob identifier.
|
||||
** @param[out] out_blob Populated with the blob data.
|
||||
** @param[out] out_size The size of the blob data.
|
||||
** @return true If the blob was found and retrieved.
|
||||
*/
|
||||
bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_t* out_size);
|
||||
|
||||
/**
|
||||
** A function called when a message is stored in the database.
|
||||
** @param id The message identifier.
|
||||
** @param stored True if the message wasn't already in the database.
|
||||
** @param user_data The user data.
|
||||
*/
|
||||
typedef void(tf_ssb_db_store_message_callback_t)(const char* id, bool stored, void* user_data);
|
||||
|
||||
/**
|
||||
** Store a message in the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param context The JS context.
|
||||
** @param id The message identifier.
|
||||
** @param val The message object.
|
||||
** @param signature The signature of the message.
|
||||
** @param sequence_before_author The order of the message fields.
|
||||
** @param callback A callback to call upon completion.
|
||||
** @param user_data User data for the callback.
|
||||
*/
|
||||
void tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id, JSValue val, const char* signature, bool sequence_before_author,
|
||||
tf_ssb_db_store_message_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** A function called when a block is stored in the database.
|
||||
** @param id The blob identifier.
|
||||
** @param is_new True if the blob wasn't already in the database.
|
||||
** @param user_data The user data.
|
||||
*/
|
||||
typedef void(tf_ssb_db_blob_store_callback_t)(const char* id, bool is_new, void* user_data);
|
||||
|
||||
/**
|
||||
** Store a blob in the database asynchronously.
|
||||
** @param ssb The SSB instance.
|
||||
** @param blob The blob data.
|
||||
** @param size The size of the blob data.
|
||||
** @param callback A callback to call upon completion.
|
||||
** @param user_data User data for the callback.
|
||||
*/
|
||||
void tf_ssb_db_blob_store_async(tf_ssb_t* ssb, const uint8_t* blob, size_t size, tf_ssb_db_blob_store_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Store a blob in the database and wait for the operation to complete.
|
||||
** @param ssb The SSB instance.
|
||||
** @param blob The blob data.
|
||||
** @param size The size of the blob.
|
||||
** @param[out] out_id Populated with the blob identifier.
|
||||
** @param out_id_size The size of the out_id buffer.
|
||||
** @param[out] out_new True if the blob wasn't already in the datbase.
|
||||
*/
|
||||
bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* out_id, size_t out_id_size, bool* out_new);
|
||||
|
||||
/**
|
||||
** Get a message by its identifier.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The message identifier.
|
||||
** @param is_keys Whether to produce {"key": id, "value": message, "timestamp": ts} or just the message.
|
||||
** @return The message.
|
||||
*/
|
||||
JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys);
|
||||
|
||||
/**
|
||||
** Get a message by its author and sequence number.
|
||||
** @param ssb The SSB instance.
|
||||
** @param author The author's identity.
|
||||
** @param sequence The message sequence number.
|
||||
** @param[out] out_message_id Populated with the message identifier.
|
||||
** @param out_message_id_size The size of the out_message_id buffer.
|
||||
** @param[out] out_timestamp Populated with the timestamp.
|
||||
** @param[out] out_content Populated with the message content. Free with tf_free().
|
||||
** @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, double* out_timestamp, char** out_content);
|
||||
|
||||
/**
|
||||
** Get information about the last message from an author.
|
||||
** @param ssb The SSB instance.
|
||||
** @param author The author's identity.
|
||||
** @param[out] out_sequence Populated with the message sequence number.
|
||||
** @param[out] out_message_id Populated with the message identifier.
|
||||
** @param out_message_id_size The size of the out_message_id buffer.
|
||||
** @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);
|
||||
|
||||
/**
|
||||
** Call a function for each result row of an SQL query.
|
||||
** @param ssb The SSB instance.
|
||||
** @param query The SQL query.
|
||||
** @param binds An array of values to bind to SQL parameters.
|
||||
** @param callback A callback to call for each result row.
|
||||
** @param user_data User data to pass to the callback.
|
||||
** @return A promise resolved when the query completes or rejected if it fails.
|
||||
*/
|
||||
JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds, void (*callback)(JSValue row, void* user_data), void* user_data);
|
||||
|
||||
typedef struct sqlite3 sqlite3;
|
||||
/**
|
||||
** Sanity check the feed for the given author.
|
||||
** @param db The SQLite database instance to use.
|
||||
** @param author The identity of the author to check.
|
||||
** @return True if the author's feed is fully valid.
|
||||
*/
|
||||
bool tf_ssb_db_check(sqlite3* db, const char* author);
|
||||
|
||||
/**
|
||||
** Get the number of SSB identities a Tilde Friends user has.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user The user's username.
|
||||
** @return The number of identities found.
|
||||
*/
|
||||
int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user);
|
||||
|
||||
/**
|
||||
** Create a new identity for a user.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user The user's username.
|
||||
** @param[out] out_public_key A buffer populated with the new public key.
|
||||
** @param[out] out_private_key A buffer populated with the new privatee key.
|
||||
** @return True if the identity was created.
|
||||
*/
|
||||
bool tf_ssb_db_identity_create(tf_ssb_t* ssb, const char* user, uint8_t* out_public_key, uint8_t* out_private_key);
|
||||
|
||||
/**
|
||||
** Delete an identity for a user from the database. This is an unrecoverable operation.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user The user's username.
|
||||
** @param public_key The identity to delete.
|
||||
** @return True if the identity was deleted.
|
||||
*/
|
||||
bool tf_ssb_db_identity_delete(tf_ssb_t* ssb, const char* user, const char* public_key);
|
||||
|
||||
/**
|
||||
** Add an identity for a user to the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user The user's username.
|
||||
** @param public_key The public key of the identity.
|
||||
** @param private_key The private key of the identity.
|
||||
*/
|
||||
bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_key, const char* private_key);
|
||||
|
||||
/**
|
||||
** Call a function for each identity owned by a user.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user The user's username.
|
||||
** @param callback The function to call for each identity.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(const char* identity, void* user_data), void* user_data);
|
||||
|
||||
/**
|
||||
** Call a function for all identities in the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback to call for each identity.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_db_identity_visit_all(tf_ssb_t* ssb, void (*callback)(const char* identity, void* user_data), void* user_data);
|
||||
|
||||
/**
|
||||
** Get the private key for an identity in the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user The owning user's username.
|
||||
** @param public_key The public key of the identity.
|
||||
** @param[out] out_private_key A buffer to receive the private key of the identity.
|
||||
** @param private_key_size The size of the out_private_key buffer.
|
||||
** @return True if the private key was found and retrieved.
|
||||
*/
|
||||
bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const char* public_key, uint8_t* out_private_key, size_t private_key_size);
|
||||
|
||||
/**
|
||||
** Format a message in the standard format for SSB signing.
|
||||
** @param context A JS context.
|
||||
** @param previous The previous message identifier.
|
||||
** @param author The author's public key.
|
||||
** @param sequence The message sequence number.
|
||||
** @param timestamp The message timestamp.
|
||||
** @param hash The hash type (probably "sha256").
|
||||
** @param content The message content.
|
||||
** @param signature The signature of the message.
|
||||
** @param sequence_before_author The order of the message fields (prefer false).
|
||||
*/
|
||||
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, bool sequence_before_author);
|
||||
|
||||
/** Information about a single followed account. */
|
||||
typedef struct _tf_ssb_following_t
|
||||
{
|
||||
/** The number of known users the account is following. */
|
||||
int following_count;
|
||||
/** The number of known users the account is blocking. */
|
||||
int blocking_count;
|
||||
/** The number of known users following the account. */
|
||||
int followed_by_count;
|
||||
/** The number of known users blocking the account. */
|
||||
int blocked_by_count;
|
||||
/** The account's identity. */
|
||||
char id[k_id_base64_len];
|
||||
} tf_ssb_following_t;
|
||||
|
||||
/**
|
||||
** Get all the identities visible from a set of identities given known follows and blocks.
|
||||
** @param ssb The SSB instance.
|
||||
** @param ids An array of identities.
|
||||
** @param count The number of identities.
|
||||
** @param depth The following depth to use (prefer 2).
|
||||
** @return An array of identities. Free with tf_free().
|
||||
*/
|
||||
const char** tf_ssb_db_following_deep_ids(tf_ssb_t* ssb, const char** ids, int count, int depth);
|
||||
|
||||
/**
|
||||
** Return information about identities visible from a set of identities given known follows and blocks.
|
||||
** @param ssb The SSB instance.
|
||||
** @param ids An array of identities.
|
||||
** @param count The number of identities.
|
||||
** @param depth The following depth to use (prefer 2).
|
||||
** @return An array of information about visible accounts. Fere with tf_free().
|
||||
*/
|
||||
tf_ssb_following_t* tf_ssb_db_following_deep(tf_ssb_t* ssb, const char** ids, int count, int depth);
|
||||
|
||||
/**
|
||||
** Get all visible identities from all local accounts.
|
||||
** @param ssb The SSB instance.
|
||||
** @param depth The following depth to consider (prefer 2).
|
||||
** @return The visible identities. Free with tf_free().
|
||||
*/
|
||||
const char** tf_ssb_db_get_all_visible_identities(tf_ssb_t* ssb, int depth);
|
||||
|
||||
/**
|
||||
** Information about a stored SHS connection.
|
||||
*/
|
||||
typedef struct _tf_ssb_db_stored_connection_t
|
||||
{
|
||||
/** The connection's address. */
|
||||
char address[256];
|
||||
/** The network port number of the connection. */
|
||||
int port;
|
||||
/** The identity. */
|
||||
char pubkey[k_id_base64_len];
|
||||
} tf_ssb_db_stored_connection_t;
|
||||
|
||||
/**
|
||||
** Get the list of stored connections from the SSB connection tracker.
|
||||
** @param ssb The SSB instance.
|
||||
** @param[out] out_count Populated with the number of returned connections.
|
||||
** @return Information about all the stored connections.
|
||||
*/
|
||||
tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, int* out_count);
|
||||
|
||||
/**
|
||||
** Remove a stored connection.
|
||||
** @param ssb The SSB instance.
|
||||
** @param address The connection address.
|
||||
** @param port The connection network port number.
|
||||
** @param pubkey The identity of the connection.
|
||||
*/
|
||||
void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int port, const char* pubkey);
|
||||
|
||||
/**
|
||||
** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use.
|
||||
** @param user_data User data registered with the authorizer.
|
||||
** @param action_code The type of action.
|
||||
** @param arg0 Depends on the action.
|
||||
** @param arg1 Depends on the action.
|
||||
** @param arg2 Depends on the action.
|
||||
** @param arg3 Depends on the action.
|
||||
** @return A value indicating whether the operation is allowed.
|
||||
*/
|
||||
int tf_ssb_sqlite_authorizer(void* user_data, int action_code, const char* arg0, const char* arg1, const char* arg2, const char* arg3);
|
||||
|
||||
/** @} */
|
||||
|
@ -8,8 +8,14 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
|
||||
/**
|
||||
** Export an app to disk.
|
||||
** @param ssb The SSB instance.
|
||||
** @param key The app path in the form "/~user/appname".
|
||||
*/
|
||||
void tf_ssb_export(tf_ssb_t* ssb, const char* key);
|
||||
|
||||
/** @} */
|
||||
|
735
src/ssb.h
735
src/ssb.h
@ -30,6 +30,9 @@ enum
|
||||
k_ssb_blob_bytes_max = 5 * 1024 * 1024,
|
||||
};
|
||||
|
||||
/**
|
||||
** The type of change to a set of connections.
|
||||
*/
|
||||
typedef enum _tf_ssb_change_t
|
||||
{
|
||||
k_tf_ssb_change_create,
|
||||
@ -37,12 +40,17 @@ typedef enum _tf_ssb_change_t
|
||||
k_tf_ssb_change_remove,
|
||||
} tf_ssb_change_t;
|
||||
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
typedef struct _tf_ssb_rpc_t tf_ssb_rpc_t;
|
||||
/** An SSB connection. */
|
||||
typedef struct _tf_ssb_connection_t tf_ssb_connection_t;
|
||||
/** A trace instance. */
|
||||
typedef struct _tf_trace_t tf_trace_t;
|
||||
/** An SQLite database handle. */
|
||||
typedef struct sqlite3 sqlite3;
|
||||
/** An event loop. */
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
/** A socket address. */
|
||||
struct sockaddr_in;
|
||||
|
||||
enum
|
||||
@ -52,15 +60,26 @@ enum
|
||||
k_blob_id_len = 53,
|
||||
};
|
||||
|
||||
/**
|
||||
** Statistics about an SSB instance.
|
||||
*/
|
||||
typedef struct _tf_ssb_stats_t
|
||||
{
|
||||
/** Number of active connections. */
|
||||
int connections;
|
||||
/** Number of active hosts discovered by network broadcast. */
|
||||
int broadcasts;
|
||||
/** Number of messages stored. */
|
||||
int messages_stored;
|
||||
/** Number of blobs stored. */
|
||||
int blobs_stored;
|
||||
/** Number of RPC messages received. */
|
||||
int rpc_in;
|
||||
/** Number of RPC messages sent. */
|
||||
int rpc_out;
|
||||
/** Number of active RPC requests. */
|
||||
int request_count;
|
||||
/** Number of callbacks registered. */
|
||||
struct
|
||||
{
|
||||
int rpc;
|
||||
@ -71,170 +90,874 @@ typedef struct _tf_ssb_stats_t
|
||||
} callbacks;
|
||||
} tf_ssb_stats_t;
|
||||
|
||||
/**
|
||||
** State about requesting blobs.
|
||||
*/
|
||||
typedef struct _tf_ssb_blob_wants_t
|
||||
{
|
||||
/** The request number of the blob.wants RPC call. */
|
||||
int32_t request_number;
|
||||
/** The number of blob wants we are waiting for a response to. */
|
||||
int wants_sent;
|
||||
/** The last blob ID considered. */
|
||||
char last_id[k_blob_id_len];
|
||||
} tf_ssb_blob_wants_t;
|
||||
|
||||
/**
|
||||
** A queue for storing messages.
|
||||
*/
|
||||
typedef struct _tf_ssb_store_queue_t
|
||||
{
|
||||
/** The first node in the queue. */
|
||||
void* head;
|
||||
/** The last node in the queue. */
|
||||
void* tail;
|
||||
/** Whether the queue is currently running. */
|
||||
bool running;
|
||||
} tf_ssb_store_queue_t;
|
||||
|
||||
tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path);
|
||||
/**
|
||||
** Create an SSB instance.
|
||||
** @param loop The event loop to use or NULL to create a new one.
|
||||
** @param context The JS context to use or NULL to create a new one.
|
||||
** @param db_path The path to the SQLite database to use.
|
||||
** @param network_key The SSB network key to use or NULL to use the standard key.
|
||||
*/
|
||||
tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path, const char* network_key);
|
||||
|
||||
/**
|
||||
** Destroy an SSB instance.
|
||||
** @param ssb The SSB instance to destroy.
|
||||
*/
|
||||
void tf_ssb_destroy(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Start optional periodic work.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_start_periodic(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Control logging verbosity.
|
||||
** @param ssb The SSB instance.
|
||||
** @param verbose True to log messages for every RPC message sent and received.
|
||||
*/
|
||||
void tf_ssb_set_verbose(tf_ssb_t* ssb, bool verbose);
|
||||
|
||||
/**
|
||||
** Acquire an SQLite database for unrestricted reading. Release qith tf_ssb_release_db_reader().
|
||||
** @param ssb The SSB instance.
|
||||
** @return A database with full read access to the database.
|
||||
*/
|
||||
sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Acquire an SQLite database for restricted reading. Release qith
|
||||
** tf_ssb_release_db_reader().
|
||||
** @param ssb The SSB instance.
|
||||
** @return A database with read access to a safe subset of the database.
|
||||
*/
|
||||
sqlite3* tf_ssb_acquire_db_reader_restricted(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Release a database acquired with tf_ssb_acquire_db_reader() or
|
||||
** tf_ssb_acquire_db_reader_restricted().
|
||||
** @param ssb The SSB instance.
|
||||
** @param db The database.
|
||||
*/
|
||||
void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db);
|
||||
|
||||
/**
|
||||
** Acquire an SQLite database with full write access to the database. Release
|
||||
** with tf_ssb_release_db_writer().
|
||||
** @param ssb The SSB instance.
|
||||
** @return The writable database.
|
||||
*/
|
||||
sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Release a database acquired with tf_ssb_acquire_db_writer().
|
||||
** @param ssb The SSB instance.
|
||||
** @param db The database.
|
||||
*/
|
||||
void tf_ssb_release_db_writer(tf_ssb_t* ssb, sqlite3* db);
|
||||
|
||||
/**
|
||||
** Get the SSB instance's event loop.
|
||||
** @param ssb The SSB instance.
|
||||
** @return The loop.
|
||||
*/
|
||||
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Generate a public/private key pair for the SSB instance.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_generate_keys(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Generate a public/private key pair and store in buffers.
|
||||
** @param[out] out_public Buffer to receive the public key.
|
||||
** @param public_size Size of the public key buffer.
|
||||
** @param[out] out_private Buffer to receive the private key.
|
||||
** @param private_size Size of the private key buffer.
|
||||
*/
|
||||
void tf_ssb_generate_keys_buffer(char* out_public, size_t public_size, char* out_private, size_t private_size);
|
||||
|
||||
/**
|
||||
** Get the private key of the SSB instance.
|
||||
** @param ssb The SSB instance.
|
||||
** @param[out] out_private Buffer to receive the private key.
|
||||
** @param private_size The size of the private key buffer.
|
||||
*/
|
||||
void tf_ssb_get_private_key(tf_ssb_t* ssb, uint8_t* out_private, size_t private_size);
|
||||
|
||||
/**
|
||||
** Set the trace instance to use for the SSB instance.
|
||||
** @param ssb The SSB instance.
|
||||
** @param trace The trace instance to use.
|
||||
*/
|
||||
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace);
|
||||
|
||||
/**
|
||||
** Get the SSB instance's trace instance.
|
||||
** @param ssb The SSB instance.
|
||||
** @return The trace instance.
|
||||
*/
|
||||
tf_trace_t* tf_ssb_get_trace(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Get the SSB istance's JS context.
|
||||
** @param ssb The SSB instance.
|
||||
** @return The JS context.
|
||||
*/
|
||||
JSContext* tf_ssb_get_context(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Begin listening for SSB discovery messages.
|
||||
** @param ssb The SSB instance.
|
||||
** @param linger True if listening for broadcasts should keep the event loop alive.
|
||||
*/
|
||||
void tf_ssb_broadcast_listener_start(tf_ssb_t* ssb, bool linger);
|
||||
|
||||
/**
|
||||
** Begin sending SSB discovevry messages.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_broadcast_sender_start(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Run the SSB instance until there is no work to do or stopped.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_run(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Sign an SSB message.
|
||||
** @param ssb The SSB instance.
|
||||
** @param author The author's public key.
|
||||
** @param private_key The author's private key.
|
||||
** @param message The message to sign.
|
||||
** @return The signed message.
|
||||
*/
|
||||
JSValue tf_ssb_sign_message(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message);
|
||||
|
||||
/**
|
||||
** Get the server's identity.
|
||||
** @param ssb The SSB instance.
|
||||
** @param[out] out_id A buffer populated with the identity.
|
||||
** @param out_id_size The size of the identity buffer.
|
||||
** @return True if the identity was successfully retrieved.
|
||||
*/
|
||||
bool tf_ssb_whoami(tf_ssb_t* ssb, char* out_id, size_t out_id_size);
|
||||
|
||||
/**
|
||||
** Call a callback for each active host discovered by network discovery broadcast.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback.
|
||||
** @param user_data User data for the callback.
|
||||
*/
|
||||
void tf_ssb_visit_broadcasts(
|
||||
tf_ssb_t* ssb, void (*callback)(const char* host, const struct sockaddr_in* addr, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data), void* user_data);
|
||||
|
||||
/**
|
||||
** Get the identities of all active connections.
|
||||
** @param ssb The SSB instance.
|
||||
** @return A NULL-terminated array of SSB identities. Free with tf_free().
|
||||
*/
|
||||
const char** tf_ssb_get_connection_ids(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Retrieve a list of active connections.
|
||||
** @param ssb The SSB instance.
|
||||
** @param[out] out_connections An array to be populated with the connections.
|
||||
** @param out_connections_count The size of the connections array.
|
||||
** @return The number of connections populated in out_connections.
|
||||
*/
|
||||
int tf_ssb_get_connections(tf_ssb_t* ssb, tf_ssb_connection_t** out_connections, int out_connections_count);
|
||||
|
||||
/**
|
||||
** Establish an SHS connection with a host.
|
||||
** @param ssb The SSB instance.
|
||||
** @param host The host name or address.
|
||||
** @param port The host's SHS port.
|
||||
** @param key The host's SSB identity.
|
||||
*/
|
||||
void tf_ssb_connect(tf_ssb_t* ssb, const char* host, int port, const uint8_t* key);
|
||||
|
||||
/**
|
||||
** Establish an SHS connection with a host by string address.
|
||||
** @param ssb The SSB instance.
|
||||
** @param address The address.
|
||||
*/
|
||||
void tf_ssb_connect_str(tf_ssb_t* ssb, const char* address);
|
||||
void tf_ssb_server_open(tf_ssb_t* ssb, int port);
|
||||
|
||||
/**
|
||||
** Begin listening for SHS connections on the given port.
|
||||
** @param ssb The SSB instance.
|
||||
** @param port The port number.
|
||||
** @return The assigned port on success or 0 on failure.
|
||||
*/
|
||||
int tf_ssb_server_open(tf_ssb_t* ssb, int port);
|
||||
|
||||
/**
|
||||
** Stop listening for SHS connections.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_server_close(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Close all active SHS connections.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_close_all(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Send a graceful close message to all active SHS connections.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_send_close(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Convert an SSB identity from string to binary.
|
||||
** @param[out] bin A buffer to receive the binary identity.
|
||||
** @param str The string identity.
|
||||
** @return True if the conversion was successful.
|
||||
*/
|
||||
bool tf_ssb_id_str_to_bin(uint8_t* bin, const char* str);
|
||||
|
||||
/**
|
||||
** Convert an SSB identity from binary to string.
|
||||
** @param[out] str A buffer to receive the identity string.
|
||||
** @param str_size The size of the string buffer.
|
||||
** @param bin The binary identity.
|
||||
** @return True if the conversion was successful.
|
||||
*/
|
||||
bool tf_ssb_id_bin_to_str(char* str, size_t str_size, const uint8_t* bin);
|
||||
|
||||
/**
|
||||
** Verify a message's signature and remove the signature if successful.
|
||||
** @param context A JS context.
|
||||
** @param val The message.
|
||||
** @param[out] out_id A buffer to receive the message's identity.
|
||||
** @param out_id_size The size of out_id.
|
||||
** @param[out] out_signature A buffer to receive the message's signature.
|
||||
** @param out_signature_size The size of out_signature.
|
||||
** @param[out] out_sequence_before_author A flag describing the order of the sequence and author fields.
|
||||
** @return True if the signature is valid and was successfully extracted.
|
||||
*/
|
||||
bool tf_ssb_verify_and_strip_signature(
|
||||
JSContext* context, JSValue val, char* out_id, size_t out_id_size, char* out_signature, size_t out_signature_size, bool* out_sequence_before_author);
|
||||
|
||||
/**
|
||||
** Determine the message identifier.
|
||||
** @param context A JS context.
|
||||
** @param message The message.
|
||||
** @param[out] out_id A buffer to receive the identifier.
|
||||
** @param out_id_size The size of out_id.
|
||||
*/
|
||||
void tf_ssb_calculate_message_id(JSContext* context, JSValue message, char* out_id, size_t out_id_size);
|
||||
|
||||
/**
|
||||
** A function called on completion of tf_ssb_verify_strip_and_store_message().
|
||||
** @param id The stored message identifier.
|
||||
** @param verified True if the message was verified successfully.
|
||||
** @param is_new True if the message was newly added to the database.
|
||||
** @param user_data The user data.
|
||||
*/
|
||||
typedef void(tf_ssb_verify_strip_store_callback_t)(const char* id, bool verified, bool is_new, void* user_data);
|
||||
|
||||
/**
|
||||
** Verify a message's signature, remove the signature, and store the message in the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param value The message.
|
||||
** @param callback A callback called when the operation completed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_verify_strip_and_store_message(tf_ssb_t* ssb, JSValue value, tf_ssb_verify_strip_store_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Check if a connection is an outgoing connection.
|
||||
** @param connection The connection.
|
||||
** @return True if the connection is outgoing.
|
||||
*/
|
||||
bool tf_ssb_connection_is_client(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get the hostname of the remote end of a connection.
|
||||
** @param connection The connection.
|
||||
** @return The hostname or address.
|
||||
*/
|
||||
const char* tf_ssb_connection_get_host(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get a connection's remote port number.
|
||||
** @param connection The connection.
|
||||
** @return The port number.
|
||||
*/
|
||||
int tf_ssb_connection_get_port(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** If a connection is a tunnel, get its parent connection.
|
||||
** @param connection The connection.
|
||||
** @return The parent connection if the connection is tunneled or NULL.
|
||||
*/
|
||||
tf_ssb_connection_t* tf_ssb_connection_get_tunnel(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get a connection's SSB instance.
|
||||
** @param connection The connection.
|
||||
** @return The SSB instance.
|
||||
*/
|
||||
tf_ssb_t* tf_ssb_connection_get_ssb(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get a connection's JS context.
|
||||
** @param connection The connection.
|
||||
** @return The JS context.
|
||||
*/
|
||||
JSContext* tf_ssb_connection_get_context(tf_ssb_connection_t* connection);
|
||||
sqlite3* tf_ssb_connection_get_db(tf_ssb_connection_t* connection);
|
||||
void tf_ssb_connection_close(tf_ssb_connection_t* connect);
|
||||
|
||||
/**
|
||||
** Close a connection.
|
||||
** @param connection The connection.
|
||||
*/
|
||||
void tf_ssb_connection_close(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Check whether a connection is connected.
|
||||
** @param connection The connection.
|
||||
** @return True if the connection is alive.
|
||||
*/
|
||||
bool tf_ssb_connection_is_connected(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get the next outgoing request number for a connection.
|
||||
** @param connection The connection.
|
||||
** @return The next request number.
|
||||
*/
|
||||
int32_t tf_ssb_connection_next_request_number(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get an active connection by its identity.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The SSB identity.
|
||||
** @return The connection if found or NULL.
|
||||
*/
|
||||
tf_ssb_connection_t* tf_ssb_connection_get(tf_ssb_t* ssb, const char* id);
|
||||
|
||||
/**
|
||||
** Get the SSB identity of a connection.
|
||||
** @param connection The connection.
|
||||
** @param[out] out_id A buffer to be populated with the identity.
|
||||
** @param out_id_size The size of out_id.
|
||||
** @return True if the identity was retrieved.
|
||||
*/
|
||||
bool tf_ssb_connection_get_id(tf_ssb_connection_t* connection, char* out_id, size_t out_id_size);
|
||||
|
||||
/**
|
||||
** Get the JS object representing a connection.
|
||||
** @param connection The connection.
|
||||
** @return The object.
|
||||
*/
|
||||
JSValue tf_ssb_connection_get_object(tf_ssb_connection_t* connection);
|
||||
|
||||
/* Callbacks. */
|
||||
/**
|
||||
** A callback called when a callback is cleaned up.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user_data User data.
|
||||
*/
|
||||
typedef void(tf_ssb_callback_cleanup_t)(tf_ssb_t* ssb, void* user_data);
|
||||
|
||||
/**
|
||||
** A callback called when the connection list changes.
|
||||
** @param ssb The SSB instance.
|
||||
** @param change The type of change.
|
||||
** @param connection The connection that changed.
|
||||
** @param user_data User data.
|
||||
*/
|
||||
typedef void(tf_ssb_connections_changed_callback_t)(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection, void* user_data);
|
||||
|
||||
/**
|
||||
** Register a callback when the connection list changes.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback to register.
|
||||
** @param cleanup The cleanup callback to register.
|
||||
** @param user_data User data to pass to the callbacks.
|
||||
*/
|
||||
void tf_ssb_add_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_connections_changed_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Remove a callback when the connection list changes.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback.
|
||||
** @param user_data The user data registered with the callback.
|
||||
*/
|
||||
void tf_ssb_remove_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_connections_changed_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** A callback called when a new broadcast is received or one expires.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user_data The user data.
|
||||
*/
|
||||
typedef void(tf_ssb_broadcasts_changed_callback_t)(tf_ssb_t* ssb, void* user_data);
|
||||
|
||||
/**
|
||||
** Register a callback when broadcasts change.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback function.
|
||||
** @param cleanup A function to call when the callback is removed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_add_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_changed_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Remove a callback registered for when broadcasts changed.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback function.
|
||||
** @param user_data The user data registered with the callback.
|
||||
*/
|
||||
void tf_ssb_remove_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_changed_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** A callback called when a message is added to the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The message identifier.
|
||||
** @param user_data The user data.
|
||||
*/
|
||||
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* id, void* user_data);
|
||||
|
||||
/**
|
||||
** Register a callback called when a message is added to the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback function.
|
||||
** @param cleanup A function to call when the callback is removed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_add_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Remove a callback registered for when a message is added to the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback function.
|
||||
** @param user_data User data registered with the callback.
|
||||
*/
|
||||
void tf_ssb_remove_message_added_callback(tf_ssb_t* ssb, tf_ssb_message_added_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Call all callbacks registered for when a message is added to the database.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The message identity added.
|
||||
** @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* id, JSValue message_with_keys);
|
||||
|
||||
/**
|
||||
** Record that a new blob was stored.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The identity of the newly stored blob.
|
||||
*/
|
||||
void tf_ssb_notify_blob_stored(tf_ssb_t* ssb, const char* id);
|
||||
|
||||
/**
|
||||
** A callback called when a blob is newly requested.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The blob identity.
|
||||
** @param user_data The user data.
|
||||
*/
|
||||
typedef void(tf_ssb_blob_want_added_callback_t)(tf_ssb_t* ssb, const char* id, void* user_data);
|
||||
|
||||
/**
|
||||
** Register a function to be called when a blob is newly requested.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback.
|
||||
** @param cleanup A function to call when the callback is removed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_add_blob_want_added_callback(tf_ssb_t* ssb, tf_ssb_blob_want_added_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Remove a callback registered for when a blob is newly requested.
|
||||
** @param ssb The SSB instance.
|
||||
** @param callback The callback to remove.
|
||||
** @param user_data The user data registered with the callback.
|
||||
*/
|
||||
void tf_ssb_remove_blob_want_added_callback(tf_ssb_t* ssb, tf_ssb_blob_want_added_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Call all callbacks registered for when a blob is newly requested.
|
||||
** @param ssb The SSB instance.
|
||||
** @param id The requested blob identity.
|
||||
*/
|
||||
void tf_ssb_notify_blob_want_added(tf_ssb_t* ssb, const char* id);
|
||||
|
||||
/**
|
||||
** A function called when a MUXRPC request is made.
|
||||
** @param connection The SSB connection.
|
||||
** @param flags The RPC flags.
|
||||
** @param request_number The request number.
|
||||
** @param args Request arguments.
|
||||
** @param message The raw message data.
|
||||
** @param size The size of the raw message data.
|
||||
** @param user_data User data registered with the callback.
|
||||
*/
|
||||
typedef void(tf_ssb_rpc_callback_t)(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data);
|
||||
|
||||
/**
|
||||
** Register a MUXRPC callback by name.
|
||||
** @param ssb The SSB instance.
|
||||
** @param name The NULL-terminated name.
|
||||
** @param callback The callback.
|
||||
** @param cleanup A function to be called when the callback is removed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_add_rpc_callback(tf_ssb_t* ssb, const char** name, tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Remove a MUXRPC callback.
|
||||
** @param ssb The SSB instance.
|
||||
** @param name The NULL-terminated name.
|
||||
** @param callback The callback to remove.
|
||||
** @param user_data The user data registered with the callback.
|
||||
*/
|
||||
void tf_ssb_remove_rpc_callback(tf_ssb_t* ssb, const char** name, tf_ssb_rpc_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Send a MUXRPC message.
|
||||
** @param connection The connection on which to send the message.
|
||||
** @param flags The message flags.
|
||||
** @param request_number The request number.
|
||||
** @param message The message payload.
|
||||
** @param size The size of the message.
|
||||
** @param callback A callback to call if a response is received.
|
||||
** @param cleanup A callback to call if the callback is removed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_connection_rpc_send(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, const uint8_t* message, size_t size, tf_ssb_rpc_callback_t* callback,
|
||||
tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Send a JSON MUXRPC message.
|
||||
** @param connection The connection on which to send the message.
|
||||
** @param flags The message flags.
|
||||
** @param request_number The request number.
|
||||
** @param message The JS message payload.
|
||||
** @param callback A callback to call if a response is received.
|
||||
** @param cleanup A callback to call if the callback is removed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_connection_rpc_send_json(
|
||||
tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue message, tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data);
|
||||
|
||||
/**
|
||||
** Send a MUXRPC error message.
|
||||
** @param connection The connection on which to send the message.
|
||||
** @param flags The message flags.
|
||||
** @param request_number The request number.
|
||||
** @param error The error string.
|
||||
*/
|
||||
void tf_ssb_connection_rpc_send_error(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, const char* error);
|
||||
|
||||
/**
|
||||
** Send a MUXRPC "method not allowed" error message.
|
||||
** @param connection The connection on which to send the message.
|
||||
** @param flags The message flags.
|
||||
** @param request_number The request number.
|
||||
** @param name The name of the not-allowed method.
|
||||
*/
|
||||
void tf_ssb_connection_rpc_send_error_method_not_allowed(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, const char* name);
|
||||
|
||||
/**
|
||||
** Register a callback to be called when a message is received for the given
|
||||
** request number.
|
||||
** @param connection The connection on which to register the callback.
|
||||
** @param request_number The request number.
|
||||
** @param callback The callback.
|
||||
** @param cleanup The function to call when the callback is removed.
|
||||
** @param user_data User data to pass to the callback.
|
||||
** @param dependent_connection A connection, which, if removed, invalidates this request.
|
||||
*/
|
||||
void tf_ssb_connection_add_request(tf_ssb_connection_t* connection, int32_t request_number, tf_ssb_rpc_callback_t* callback, tf_ssb_callback_cleanup_t* cleanup, void* user_data,
|
||||
tf_ssb_connection_t* dependent_connection);
|
||||
|
||||
/**
|
||||
** Remove a callback registered to be called when a message is received for the
|
||||
** given request number.
|
||||
** @param connection The connection.
|
||||
** @param request_number The request number.
|
||||
*/
|
||||
void tf_ssb_connection_remove_request(tf_ssb_connection_t* connection, int32_t request_number);
|
||||
|
||||
/**
|
||||
** A function scheduled to be run later.
|
||||
** @param connection The owning connection.
|
||||
** @param user_data User data registered with the callback.
|
||||
*/
|
||||
typedef void(tf_ssb_scheduled_callback_t)(tf_ssb_connection_t* connection, void* user_data);
|
||||
|
||||
/**
|
||||
** Schedule work to be run when the server is next idle.
|
||||
** @param connection The owning connection.
|
||||
** @param callback The callback to call.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, tf_ssb_scheduled_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Schedule work to run on a worker thread.
|
||||
** @param connection The owning connection.
|
||||
** @param work_callback The callback to run on a thread.
|
||||
** @param after_work_callback The callback to run on the main thread when the work is complete.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_connection_run_work(tf_ssb_connection_t* connection, void (*work_callback)(tf_ssb_connection_t* connection, void* user_data),
|
||||
void (*after_work_callback)(tf_ssb_connection_t* connection, int result, void* user_data), void* user_data);
|
||||
|
||||
/**
|
||||
** Register for new messages on a connection.
|
||||
** @param connection The SHS connection.
|
||||
** @param author The author for whom to request new messages.
|
||||
** @param request_number The MUXRPC request on which to send new messages.
|
||||
** @param keys Whether to send with keys.
|
||||
*/
|
||||
void tf_ssb_connection_add_new_message_request(tf_ssb_connection_t* connection, const char* author, int32_t request_number, bool keys);
|
||||
|
||||
/**
|
||||
** Remove a request for new messages on a connection.
|
||||
** @param connection the SHS connection.
|
||||
** @param author The author for whom to no longer request new messages.
|
||||
*/
|
||||
void tf_ssb_connection_remove_new_message_request(tf_ssb_connection_t* connection, const char* author);
|
||||
|
||||
/**
|
||||
** Get whether we are an attendant on a room connection.
|
||||
** @param connection The SHS connection.
|
||||
** @return True if this is an attendant connection.
|
||||
*/
|
||||
bool tf_ssb_connection_is_attendant(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get the request number used to notify of room attendant changes.
|
||||
** @param connection the SHS connection.
|
||||
** @return A request number.
|
||||
*/
|
||||
int32_t tf_ssb_connection_get_attendant_request_number(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Register for attendant change notifications on a connection.
|
||||
** @param connection The SHS connection.
|
||||
** @param attendant Whether this connection will be an attendant.
|
||||
** @param request_number The request number on which to send attendant changes.
|
||||
*/
|
||||
void tf_ssb_connection_set_attendant(tf_ssb_connection_t* connection, bool attendant, int request_number);
|
||||
|
||||
/**
|
||||
** Clear all attendants from a room.
|
||||
** @param connection The SHS connection.
|
||||
*/
|
||||
void tf_ssb_connection_clear_room_attendants(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Add a room attendant.
|
||||
** @param connection The SHS connection.
|
||||
** @param id The attendant identifier.
|
||||
*/
|
||||
void tf_ssb_connection_add_room_attendant(tf_ssb_connection_t* connection, const char* id);
|
||||
|
||||
/**
|
||||
** Remove a room attendant.
|
||||
** @param connection The SHS connection.
|
||||
** @param id The attendanr identifier.
|
||||
*/
|
||||
void tf_ssb_connection_remove_room_attendant(tf_ssb_connection_t* connection, const char* id);
|
||||
|
||||
/**
|
||||
** Create a tunnel.
|
||||
** @param ssb The SSB instance.
|
||||
** @param portal_id The identity of the tunnel intermediary.
|
||||
** @param request_number The tunnel request.
|
||||
** @param target_id The identity being tunneled to.
|
||||
** @return The new tunnel connection.
|
||||
*/
|
||||
tf_ssb_connection_t* tf_ssb_connection_tunnel_create(tf_ssb_t* ssb, const char* portal_id, int32_t request_number, const char* target_id);
|
||||
|
||||
/**
|
||||
** Get the request number on which to send EBT responses.
|
||||
** @param connection The SHS connection.
|
||||
** @return The request number.
|
||||
*/
|
||||
int32_t tf_ssb_connection_get_ebt_request_number(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Set the request number on which to send EBT responses.
|
||||
** @param connection The SHS connection.
|
||||
** @param request_number The request number.
|
||||
*/
|
||||
void tf_ssb_connection_set_ebt_request_number(tf_ssb_connection_t* connection, int32_t request_number);
|
||||
|
||||
/**
|
||||
** Get the EBT clock for a connection.
|
||||
** @param connection An SHS connection.
|
||||
** @return The EBT clock.
|
||||
*/
|
||||
JSValue tf_ssb_connection_get_ebt_send_clock(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Set the EBT clock for a connection.
|
||||
** @param connection An SHS connection.
|
||||
** @param send_clock The clock state.
|
||||
*/
|
||||
void tf_ssb_connection_set_ebt_send_clock(tf_ssb_connection_t* connection, JSValue send_clock);
|
||||
|
||||
/**
|
||||
** Get whether the EBT clock has been sent for a connection.
|
||||
** @param connection An SHS connection.
|
||||
** @return True if the clock has been sent.
|
||||
*/
|
||||
bool tf_ssb_connection_get_sent_clock(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Set the EBT clock sent state for a connection.
|
||||
** @param connection An SHS connection.
|
||||
** @param sent_clock Whether the clock has been sent.
|
||||
*/
|
||||
void tf_ssb_connection_set_sent_clock(tf_ssb_connection_t* connection, bool sent_clock);
|
||||
|
||||
/**
|
||||
** Get the JS class ID of the SSB connection class.
|
||||
** @return The class ID
|
||||
*/
|
||||
JSClassID tf_ssb_get_connection_class_id();
|
||||
|
||||
/**
|
||||
** Get general statistics about an SSB instance.
|
||||
** @param ssb The SSB instance.
|
||||
** @param[out] out_stats Populated with performance statistics.
|
||||
*/
|
||||
void tf_ssb_get_stats(tf_ssb_t* ssb, tf_ssb_stats_t* out_stats);
|
||||
|
||||
/**
|
||||
** Get information about requested blobs.
|
||||
** @param connection An SHS connection.
|
||||
** @return Blob wants information.
|
||||
*/
|
||||
tf_ssb_blob_wants_t* tf_ssb_connection_get_blob_wants_state(tf_ssb_connection_t* connection);
|
||||
|
||||
/**
|
||||
** Get a report of information about recent disconnections.
|
||||
** @param ssb The SSB instance.
|
||||
** @param context A JS context.
|
||||
** @return Information about disconnections.
|
||||
*/
|
||||
JSValue tf_ssb_get_disconnection_debug(tf_ssb_t* ssb, JSContext* context);
|
||||
|
||||
/**
|
||||
** Record whether the calling thread is busy.
|
||||
** @param ssb The SSB instance.
|
||||
** @param busy True if the calling thread is now busy.
|
||||
*/
|
||||
void tf_ssb_record_thread_busy(tf_ssb_t* ssb, bool busy);
|
||||
|
||||
/**
|
||||
** Get an estimate of utilization of all running threads.
|
||||
** @param ssb The SSB instance.
|
||||
** @return The utilization percent.
|
||||
*/
|
||||
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.
|
||||
** @param ssb The SSB instance.
|
||||
** @return The queue.
|
||||
*/
|
||||
tf_ssb_store_queue_t* tf_ssb_get_store_queue(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Increment the SSB instance's ref count. Prevents it from being destroyed
|
||||
** until it reaches zero.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_ref(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Decrement the SSB instance's ref count. May destroy the instance when the
|
||||
** count returns to zero.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_unref(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Record whether the calling thread is the main thread or not. Some
|
||||
** operations are disallowed on the main thread for performance.
|
||||
** @param ssb The SSB instance.
|
||||
** @param main_thread Whether the calling thread is the main thread.
|
||||
*/
|
||||
void tf_ssb_set_main_thread(tf_ssb_t* ssb, bool main_thread);
|
||||
|
||||
/**
|
||||
** Get whether the running server is operating a room.
|
||||
** @param ssb The SSB instance.
|
||||
** @return True if the server is a room.
|
||||
*/
|
||||
bool tf_ssb_is_room(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Set whether the running server is operating a room.
|
||||
** @param ssb The SSB instance.
|
||||
** @param is_room Whether to run a room.
|
||||
*/
|
||||
void tf_ssb_set_is_room(tf_ssb_t* ssb, bool is_room);
|
||||
|
||||
/**
|
||||
** Get the name of the room hosted by the running server.
|
||||
** @param ssb The SSB instance.
|
||||
** @return The room name or NULL.
|
||||
*/
|
||||
const char* tf_ssb_get_room_name(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Set the name of the room hosted by the running server.
|
||||
** @param ssb The SSB instance.
|
||||
** @param room_name The name of the room.
|
||||
*/
|
||||
void tf_ssb_set_room_name(tf_ssb_t* ssb, const char* room_name);
|
||||
|
||||
/**
|
||||
** Schedule work to be run after a time delay.
|
||||
** @param ssb The SSB instance.
|
||||
** @param delay_ms The duration to wait in milliseconds.
|
||||
** @param callback The callback to call to run the work.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_ssb_schedule_work(tf_ssb_t* ssb, int delay_ms, void (*callback)(tf_ssb_t* ssb, void* user_data), void* user_data);
|
||||
|
||||
/** @} */
|
||||
|
@ -128,7 +128,11 @@ static void _tf_ssb_import_recursive_add_files(tf_ssb_t* ssb, uv_loop_t* loop, J
|
||||
uv_dirent_t ent;
|
||||
while (uv_fs_scandir_next(&req, &ent) == 0)
|
||||
{
|
||||
if (ent.type == UV_DIRENT_FILE)
|
||||
if (ent.type == UV_DIRENT_FILE
|
||||
#if defined(__HAIKU__)
|
||||
|| ent.type == UV_DIRENT_UNKNOWN
|
||||
#endif
|
||||
)
|
||||
{
|
||||
size_t len = strlen(path) + strlen(ent.name) + 2;
|
||||
char* full_path = tf_malloc(len);
|
||||
|
@ -8,9 +8,24 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
|
||||
/**
|
||||
** Import apps in a directory to a user's account.
|
||||
** @param ssb The SSB instance.
|
||||
** @param user The username.
|
||||
** @param path The on-disk path of the apps.
|
||||
*/
|
||||
void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path);
|
||||
|
||||
/**
|
||||
** Import apps from a zip file to a user's account.
|
||||
** @param ssb The SSB instance.
|
||||
** @param zip_path The path to the zip file on disk.
|
||||
** @param user The user into whose account the apps will be imported.
|
||||
** @param path The path in the zip to the apps.
|
||||
*/
|
||||
void tf_ssb_import_from_zip(tf_ssb_t* ssb, const char* zip_path, const char* user, const char* path);
|
||||
|
||||
/** @} */
|
||||
|
@ -272,7 +272,7 @@ static JSValue _tf_ssb_getPrivateKey(JSContext* context, JSValueConst this_val,
|
||||
|
||||
static JSValue _tf_ssb_getServerIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue result = JS_NewArray(context);
|
||||
JSValue result = JS_UNDEFINED;
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb)
|
||||
{
|
||||
|
@ -6,9 +6,14 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/** A JS context. */
|
||||
typedef struct JSContext JSContext;
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
|
||||
/**
|
||||
** Register the SSB script interface.
|
||||
*/
|
||||
void tf_ssb_register(JSContext* context, tf_ssb_t* ssb);
|
||||
|
||||
/** @} */
|
||||
|
@ -30,7 +30,7 @@ static int64_t _get_global_setting_int64(tf_ssb_t* ssb, const char* name, int64_
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, name, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) == SQLITE_ROW)
|
||||
if (sqlite3_step(statement) == SQLITE_ROW && sqlite3_column_type(statement, 0) != SQLITE_NULL)
|
||||
{
|
||||
result = sqlite3_column_int64(statement, 0);
|
||||
}
|
||||
@ -678,7 +678,7 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
|
||||
sqlite3_stmt* statement;
|
||||
const int k_max = 32;
|
||||
if (sqlite3_prepare(db,
|
||||
"SELECT previous, author, id, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages WHERE author = ?1 AND sequence > ?2 AND "
|
||||
"SELECT previous, author, id, sequence, timestamp, hash, json(content), signature, sequence_before_author FROM messages WHERE author = ?1 AND sequence > ?2 AND "
|
||||
"sequence "
|
||||
"< ?3 ORDER BY sequence",
|
||||
-1, &statement, NULL) == SQLITE_OK)
|
||||
|
@ -7,9 +7,19 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
|
||||
/**
|
||||
** Register standard muxrpc callbacks.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_rpc_register(tf_ssb_t* ssb);
|
||||
|
||||
/**
|
||||
** Start periodic SSB maintenance tasks.
|
||||
** @param ssb The SSB instance.
|
||||
*/
|
||||
void tf_ssb_rpc_start_periodic(tf_ssb_t* ssb);
|
||||
|
||||
/** @} */
|
||||
|
@ -143,10 +143,10 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
|
||||
uv_loop_init(&loop);
|
||||
|
||||
unlink("out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
|
||||
tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
|
||||
unlink("out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
|
||||
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
|
||||
|
||||
uv_idle_t idle0 = { .data = ssb0 };
|
||||
@ -352,13 +352,13 @@ void tf_ssb_test_rooms(const tf_test_options_t* options)
|
||||
uv_loop_init(&loop);
|
||||
|
||||
unlink("out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
|
||||
tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
|
||||
unlink("out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
|
||||
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
|
||||
unlink("out/test_db2.sqlite");
|
||||
tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, "file:out/test_db2.sqlite");
|
||||
tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, "file:out/test_db2.sqlite", NULL);
|
||||
tf_ssb_register(tf_ssb_get_context(ssb2), ssb2);
|
||||
|
||||
uv_idle_t idle0 = { .data = ssb0 };
|
||||
@ -513,7 +513,7 @@ void tf_ssb_test_following(const tf_test_options_t* options)
|
||||
uv_loop_init(&loop);
|
||||
|
||||
unlink("out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
|
||||
tf_ssb_generate_keys(ssb0);
|
||||
|
||||
char id0[k_id_base64_len] = { "@" };
|
||||
@ -588,7 +588,7 @@ void tf_ssb_test_bench(const tf_test_options_t* options)
|
||||
tf_trace_t* trace = tf_trace_create();
|
||||
|
||||
unlink("out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
|
||||
tf_ssb_set_trace(ssb0, trace);
|
||||
tf_ssb_generate_keys(ssb0);
|
||||
|
||||
@ -618,7 +618,7 @@ void tf_ssb_test_bench(const tf_test_options_t* options)
|
||||
tf_printf("insert = %f seconds\n", (end_time.tv_sec - start_time.tv_sec) + (end_time.tv_nsec - start_time.tv_nsec) / 1e9);
|
||||
|
||||
unlink("out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
|
||||
tf_ssb_set_trace(ssb1, trace);
|
||||
tf_ssb_generate_keys(ssb1);
|
||||
uint8_t id0bin[k_id_bin_len];
|
||||
@ -793,12 +793,12 @@ void tf_ssb_test_go_ssb_room(const tf_test_options_t* options)
|
||||
tf_trace_t* trace = tf_trace_create();
|
||||
|
||||
unlink("out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
|
||||
tf_ssb_set_trace(ssb0, trace);
|
||||
tf_ssb_generate_keys(ssb0);
|
||||
|
||||
unlink("out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
|
||||
tf_ssb_set_trace(ssb1, trace);
|
||||
tf_ssb_generate_keys(ssb1);
|
||||
|
||||
|
@ -6,13 +6,45 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/**
|
||||
** Options to control how tests are run.
|
||||
*/
|
||||
typedef struct _tf_test_options_t tf_test_options_t;
|
||||
|
||||
/**
|
||||
** Test converting SSB identities.
|
||||
** @param options The test options.
|
||||
*/
|
||||
void tf_ssb_test_id_conversion(const tf_test_options_t* options);
|
||||
|
||||
/**
|
||||
** Test SSB connections and replication.
|
||||
** @param options The test options.
|
||||
*/
|
||||
void tf_ssb_test_ssb(const tf_test_options_t* options);
|
||||
|
||||
/**
|
||||
** Test SSB following calculations.
|
||||
** @param options The test options.
|
||||
*/
|
||||
void tf_ssb_test_following(const tf_test_options_t* options);
|
||||
|
||||
/**
|
||||
** Test SSB rooms.
|
||||
** @param options The test options.
|
||||
*/
|
||||
void tf_ssb_test_rooms(const tf_test_options_t* options);
|
||||
|
||||
/**
|
||||
** Benchmark SSB replication performacnce.
|
||||
** @param options The test options.
|
||||
*/
|
||||
void tf_ssb_test_bench(const tf_test_options_t* options);
|
||||
|
||||
/**
|
||||
** Test communicating with go-ssb-room.
|
||||
** @param options The test options.
|
||||
*/
|
||||
void tf_ssb_test_go_ssb_room(const tf_test_options_t* options);
|
||||
|
||||
/** @} */
|
||||
|
22
src/task.c
22
src/task.c
@ -150,6 +150,7 @@ typedef struct _tf_task_t
|
||||
int _import_count;
|
||||
JSValue _loadedFiles;
|
||||
|
||||
const char* _network_key;
|
||||
int _ssb_port;
|
||||
int _http_port;
|
||||
int _https_port;
|
||||
@ -949,9 +950,6 @@ char* tf_task_get_disconnections(tf_task_t* task)
|
||||
return result;
|
||||
}
|
||||
|
||||
char* tf_task_get_debug(tf_task_t* task);
|
||||
char* tf_task_get_hitches(tf_task_t* task);
|
||||
|
||||
static JSValue _tf_task_getFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_task_t* task = JS_GetContextOpaque(context);
|
||||
@ -1696,9 +1694,6 @@ void tf_task_activate(tf_task_t* task)
|
||||
JS_FreeAtom(context, atom);
|
||||
|
||||
JSValue tildefriends = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, tildefriends, "ssb_port", JS_NewInt32(context, task->_ssb_port));
|
||||
JS_SetPropertyStr(context, tildefriends, "http_port", JS_NewInt32(context, task->_http_port));
|
||||
JS_SetPropertyStr(context, tildefriends, "https_port", JS_NewInt32(context, task->_https_port));
|
||||
JSValue args = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, tildefriends, "args", args);
|
||||
if (task->_args)
|
||||
@ -1746,18 +1741,24 @@ void tf_task_activate(tf_task_t* task)
|
||||
tf_database_register(context);
|
||||
tf_httpd_register(context);
|
||||
|
||||
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path);
|
||||
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key);
|
||||
tf_ssb_set_trace(task->_ssb, task->_trace);
|
||||
tf_ssb_register(context, task->_ssb);
|
||||
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
|
||||
|
||||
int actual_ssb_port = task->_ssb_port;
|
||||
|
||||
if (task->_ssb_port)
|
||||
{
|
||||
tf_ssb_broadcast_listener_start(task->_ssb, false);
|
||||
tf_ssb_broadcast_sender_start(task->_ssb);
|
||||
tf_ssb_server_open(task->_ssb, task->_ssb_port);
|
||||
actual_ssb_port = tf_ssb_server_open(task->_ssb, task->_ssb_port);
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(context, tildefriends, "ssb_port", JS_NewInt32(context, actual_ssb_port));
|
||||
JS_SetPropertyStr(context, tildefriends, "http_port", JS_NewInt32(context, task->_http_port));
|
||||
JS_SetPropertyStr(context, tildefriends, "https_port", JS_NewInt32(context, task->_https_port));
|
||||
|
||||
JS_SetPropertyStr(context, global, "getStats", JS_NewCFunction(context, _tf_task_getStats, "getStats", 0));
|
||||
}
|
||||
else
|
||||
@ -2000,6 +2001,11 @@ tf_task_t* tf_task_get(JSContext* context)
|
||||
return JS_GetContextOpaque(context);
|
||||
}
|
||||
|
||||
void tf_task_set_ssb_network_key(tf_task_t* task, const char* network_key)
|
||||
{
|
||||
task->_network_key = network_key;
|
||||
}
|
||||
|
||||
void tf_task_set_ssb_port(tf_task_t* task, int port)
|
||||
{
|
||||
task->_ssb_port = port;
|
||||
|
253
src/task.h
253
src/task.h
@ -13,19 +13,30 @@
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
/** An event loop. */
|
||||
typedef struct uv_loop_s uv_loop_t;
|
||||
/** A timer. */
|
||||
typedef struct uv_timer_s uv_timer_t;
|
||||
|
||||
/** A task identifier. */
|
||||
typedef int taskid_t;
|
||||
/** A promise identifier. */
|
||||
typedef int promiseid_t;
|
||||
/** An exported function identifier. */
|
||||
typedef int exportid_t;
|
||||
/** A handle to a task. */
|
||||
typedef struct _tf_taskstub_t tf_taskstub_t;
|
||||
/** A task. */
|
||||
typedef struct _tf_task_t tf_task_t;
|
||||
/** A trace instance. */
|
||||
typedef struct _tf_trace_t tf_trace_t;
|
||||
/** An SSB instance. */
|
||||
typedef struct _tf_ssb_t tf_ssb_t;
|
||||
|
||||
/** The fixed ID of the parent task. */
|
||||
static const taskid_t k_task_parent_id = 0;
|
||||
|
||||
/** A message type that can be sent between tasks. */
|
||||
typedef enum _tf_task_message_t
|
||||
{
|
||||
kResolvePromise,
|
||||
@ -44,50 +55,282 @@ typedef enum _tf_task_message_t
|
||||
kPrint,
|
||||
} tf_task_message_t;
|
||||
|
||||
/**
|
||||
** Create a task.
|
||||
** @return A new task.
|
||||
*/
|
||||
tf_task_t* tf_task_create();
|
||||
|
||||
/**
|
||||
** Configure a task from a file descriptor. Typically a pipe to the parent
|
||||
** task's process.
|
||||
** @param task The task to configure.
|
||||
** @param fd The file descriptor.
|
||||
*/
|
||||
void tf_task_configure_from_fd(tf_task_t* task, int fd);
|
||||
|
||||
/**
|
||||
** Set the SSB network key.
|
||||
** @param task The task.
|
||||
** @param network_key The network key.
|
||||
*/
|
||||
void tf_task_set_ssb_network_key(tf_task_t* task, const char* network_key);
|
||||
|
||||
/**
|
||||
** Set the port number on which to run an SSB secure handshake server.
|
||||
** @param task The task.
|
||||
** @param port The port number or 0 to disable.
|
||||
*/
|
||||
void tf_task_set_ssb_port(tf_task_t* task, int port);
|
||||
|
||||
/**
|
||||
** Set the port number on which to run an HTTP server.
|
||||
** @param task The task.
|
||||
** @param port The port number of 0 to disable.
|
||||
*/
|
||||
void tf_task_set_http_port(tf_task_t* task, int port);
|
||||
|
||||
/**
|
||||
** Set the port number on which to run an HTTPS server.
|
||||
** @param task The task.
|
||||
** @param port The port number of 0 to disable.
|
||||
*/
|
||||
void tf_task_set_https_port(tf_task_t* task, int port);
|
||||
|
||||
/**
|
||||
** Set the path to the SQLite database.
|
||||
** @param task The task.
|
||||
** @param path The database path.
|
||||
*/
|
||||
void tf_task_set_db_path(tf_task_t* task, const char* path);
|
||||
|
||||
/**
|
||||
** Set the path to a zip file from which to load all static data.
|
||||
** @param task The task.
|
||||
** @param path The zip file path or NULL.
|
||||
*/
|
||||
void tf_task_set_zip_path(tf_task_t* task, const char* path);
|
||||
|
||||
/**
|
||||
** Get the path to the zipp file being used for static data.
|
||||
** @param task The task.
|
||||
** @return The zip file path or NULL.
|
||||
*/
|
||||
const char* tf_task_get_zip_path(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Set arbitrary named arguments that will be made available to the task.
|
||||
** @param task The task.
|
||||
** @param args A string of the form "key=value,other_key=other_value,..."
|
||||
*/
|
||||
void tf_task_set_args(tf_task_t* task, const char* args);
|
||||
|
||||
/**
|
||||
** Get whether this instance is configure to run in a single process.
|
||||
** @param task The running task.
|
||||
** @return true if all tasks are running in a single process.
|
||||
*/
|
||||
bool tf_task_get_one_proc(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Set whether all tasks should run in a single process. Only supported to
|
||||
** appease Apple's limitations.
|
||||
** @param task The running task.
|
||||
** @param one_proc True if subprocesses should not be used.
|
||||
*/
|
||||
void tf_task_set_one_proc(tf_task_t* task, bool one_proc);
|
||||
|
||||
/**
|
||||
** Start a task running its script.
|
||||
** @param task The task.
|
||||
*/
|
||||
void tf_task_activate(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Update a task until it is done or stopped.
|
||||
** @param task The task.
|
||||
*/
|
||||
void tf_task_run(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Run a script from file on disk.
|
||||
** @param task The task.
|
||||
** @param file The path to the script file to run.
|
||||
** @return 0 if there was a problem or 1 if the script was started.
|
||||
*/
|
||||
int tf_task_execute(tf_task_t* task, const char* file);
|
||||
|
||||
/**
|
||||
** Set a task as trusted or untrusted. Trusted tasks have more interface exposed to them.
|
||||
** @param task The task.
|
||||
** @param trusted true if the task is trusted.
|
||||
*/
|
||||
void tf_task_set_trusted(tf_task_t* task, bool trusted);
|
||||
|
||||
/**
|
||||
** Get the JS context from a task.
|
||||
** @param task The task.
|
||||
** @return The context.
|
||||
*/
|
||||
JSContext* tf_task_get_context(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Destroy a task.
|
||||
** @param task The task.
|
||||
*/
|
||||
void tf_task_destroy(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Convert a function to an integer handle that can be passed across processes.
|
||||
** @param task The running task.
|
||||
** @param to The task stub to which the handle will be passed.
|
||||
** @param function The functoin to export.
|
||||
** @return A handle representing the function.
|
||||
*/
|
||||
exportid_t tf_task_export_function(tf_task_t* task, tf_taskstub_t* to, JSValue function);
|
||||
|
||||
/**
|
||||
** Create a function that can be called from a handle to an exported function
|
||||
** from another task.
|
||||
** @param task The running task.
|
||||
** @param stub_id The task stub from which the function was exported.
|
||||
** @param export_id The handle to the function.
|
||||
** @return A function that, when called, invokes the corresponding function in
|
||||
** the remote task.
|
||||
*/
|
||||
JSValue tf_task_add_import(tf_task_t* task, taskid_t stub_id, exportid_t export_id);
|
||||
|
||||
/**
|
||||
** Get the event loop from a task.
|
||||
** @param task The task.
|
||||
** @return The loop.
|
||||
*/
|
||||
uv_loop_t* tf_task_get_loop(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Get the task from a JS context.
|
||||
** @param context The context.
|
||||
** @return The task.
|
||||
*/
|
||||
tf_task_t* tf_task_get(JSContext* context);
|
||||
|
||||
/**
|
||||
** Get the trace instance from a task.
|
||||
** @param task The task.
|
||||
** @return The trace instance.
|
||||
*/
|
||||
tf_trace_t* tf_task_get_trace(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Get the SSB instance from a task.
|
||||
** @param task The task.
|
||||
** @return The SSB instance.
|
||||
*/
|
||||
tf_ssb_t* tf_task_get_ssb(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Get the name of a task.
|
||||
** @param task The task.
|
||||
** @return The task's name as derived from the script it is running.
|
||||
*/
|
||||
const char* tf_task_get_name(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Print through a task's parent.
|
||||
** @param task The running task.
|
||||
** @param argc The number of arguments to print.
|
||||
** @param argv The arguments to print.
|
||||
*/
|
||||
void tf_task_print(tf_task_t* task, int argc, JSValueConst* argv);
|
||||
|
||||
/**
|
||||
** Allocate a promise object.
|
||||
** @param task The running task.
|
||||
** @param[out] out_promise The promise that was allocated.
|
||||
** @return The promise JS object.
|
||||
*/
|
||||
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise);
|
||||
|
||||
/**
|
||||
** Reject a promise.
|
||||
** @param task The running task.
|
||||
** @param promise The promise to reject.
|
||||
** @param error The value with which to reject the promise.
|
||||
*/
|
||||
void tf_task_reject_promise(tf_task_t* task, promiseid_t promise, JSValue error);
|
||||
|
||||
/**
|
||||
** Resolve a promise.
|
||||
** @param task The running task.
|
||||
** @param promise The promise to resolve.
|
||||
** @param result The value with which to resolve the promise.
|
||||
*/
|
||||
void tf_task_resolve_promise(tf_task_t* task, promiseid_t promise, JSValue result);
|
||||
|
||||
/**
|
||||
** Send a message referencing a promise across a packet stream.
|
||||
** @param from The task originating the message.
|
||||
** @param to The task handle receiving the message.
|
||||
** @param type The message type.
|
||||
** @param promise The promise.
|
||||
** @param payload The content of the message.
|
||||
*/
|
||||
void tf_task_send_promise_message(tf_task_t* from, tf_taskstub_t* to, tf_task_message_t type, promiseid_t promise, JSValue payload);
|
||||
|
||||
/**
|
||||
** Have a task handle a message from a packaet stream.
|
||||
** @param packetType The type of the message.
|
||||
** @param begin The data.
|
||||
** @param length The size of the data.
|
||||
** @param userData The task stub from which the packet was received.
|
||||
*/
|
||||
void tf_task_on_receive_packet(int packetType, const char* begin, size_t length, void* userData);
|
||||
|
||||
/**
|
||||
** Generate an unused task identifier representing the task stub from the running task.
|
||||
** @param task The running task.
|
||||
** @param stub A handle to the task requesting an identifier.
|
||||
** @return The new identifier.
|
||||
*/
|
||||
taskid_t tf_task_allocate_task_id(tf_task_t* task, tf_taskstub_t* stub);
|
||||
|
||||
/**
|
||||
** Remove a task stub from a task.
|
||||
** @param task The parent task.
|
||||
** @param child The task handle to remove.
|
||||
*/
|
||||
void tf_task_remove_child(tf_task_t* task, tf_taskstub_t* child);
|
||||
|
||||
void tf_task_report_error(tf_task_t* task, JSValue error);
|
||||
|
||||
JSValue tf_try_get_typed_array_buffer(JSContext* ctx, JSValueConst obj, size_t* pbyte_offset, size_t* pbyte_length, size_t* pbytes_per_element);
|
||||
uint8_t* tf_try_get_array_buffer(JSContext* ctx, size_t* psize, JSValueConst obj);
|
||||
|
||||
/**
|
||||
** Send an error to the parent task.
|
||||
** @param task The current task.
|
||||
** @param error The potential error.
|
||||
** @return true If the object was an error or exception and it was passed to
|
||||
** the parent task.
|
||||
*/
|
||||
bool tf_task_send_error_to_parent(tf_task_t* task, JSValue error);
|
||||
|
||||
/**
|
||||
** Get a report of recent disconnections.
|
||||
** @param task The task.
|
||||
** @return A JSON representation of recent disconnections that must be freed
|
||||
** with tf_free().
|
||||
*/
|
||||
char* tf_task_get_disconnections(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** Get a report of miscellaneous debug information.
|
||||
** @param task The task.
|
||||
** @return A JSON representation of various debug information that must be
|
||||
** freed with tf_free().
|
||||
*/
|
||||
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);
|
||||
|
||||
/** @} */
|
||||
|
@ -10,20 +10,76 @@
|
||||
#include "quickjs.h"
|
||||
#include "uv.h"
|
||||
|
||||
/** A task identifier. */
|
||||
typedef int taskid_t;
|
||||
|
||||
/** A packet stream. */
|
||||
typedef struct _tf_packetstream_t tf_packetstream_t;
|
||||
|
||||
/** A task. */
|
||||
typedef struct _tf_task_t tf_task_t;
|
||||
|
||||
/** A handle to another task. */
|
||||
typedef struct _tf_taskstub_t tf_taskstub_t;
|
||||
|
||||
/** Initialize task stub. Call before using the rest. */
|
||||
void tf_taskstub_startup();
|
||||
|
||||
/**
|
||||
** Register the task stub script interface.
|
||||
** @param context The JS context.
|
||||
** @return The task stub constructor.
|
||||
*/
|
||||
JSValue tf_taskstub_register(JSContext* context);
|
||||
|
||||
/**
|
||||
** Get a unique identifier for the task stub.
|
||||
** @param stub The task stub.
|
||||
** @return An identifier for the stub.
|
||||
*/
|
||||
taskid_t tf_taskstub_get_id(const tf_taskstub_t* stub);
|
||||
|
||||
/**
|
||||
** Get the JS object representing the task stub.
|
||||
** @param stub The task stub.
|
||||
** @return The JS object.
|
||||
*/
|
||||
JSValue tf_taskstub_get_task_object(const tf_taskstub_t* stub);
|
||||
|
||||
/**
|
||||
** Get the packet stream that can be used to communicate with the task stub.
|
||||
** @param stub The task stub.
|
||||
** @return The packet stream.
|
||||
*/
|
||||
tf_packetstream_t* tf_taskstub_get_stream(const tf_taskstub_t* stub);
|
||||
|
||||
/**
|
||||
** Get the task owning the task stub.
|
||||
** @param stub The task stub.
|
||||
** @return The task from which the task stub was created.
|
||||
*/
|
||||
tf_task_t* tf_taskstub_get_owner(const tf_taskstub_t* stub);
|
||||
|
||||
/**
|
||||
** Create a task stub representing the parent task of the running process.
|
||||
** @param task The running task.
|
||||
** @param file A file descriptor of a pipe connected to a parent process task.
|
||||
** @return The created task stub.
|
||||
*/
|
||||
tf_taskstub_t* tf_taskstub_create_parent(tf_task_t* task, uv_file file);
|
||||
|
||||
/**
|
||||
** Report an error to a task stub.
|
||||
** @param stub The stub to which to report th eerror.
|
||||
** @param error The error to report.
|
||||
*/
|
||||
void tf_taskstub_on_error(tf_taskstub_t* stub, JSValue error);
|
||||
|
||||
/**
|
||||
** Print to a task stub.
|
||||
** @param stub The task stub to which to print.
|
||||
** @param arguments The values to print.
|
||||
*/
|
||||
void tf_taskstub_on_print(tf_taskstub_t* stub, JSValue arguments);
|
||||
|
||||
/** @} */
|
||||
|
@ -58,6 +58,7 @@ static void _test_nop(const tf_test_options_t* options)
|
||||
assert(WEXITSTATUS(result) == 0);
|
||||
}
|
||||
|
||||
#if !defined(__HAIKU__)
|
||||
static void _test_sandbox(const tf_test_options_t* options)
|
||||
{
|
||||
_write_file("out/test.js",
|
||||
@ -91,6 +92,7 @@ static void _test_sandbox(const tf_test_options_t* options)
|
||||
unlink("out/test.js");
|
||||
unlink("out/child.js");
|
||||
}
|
||||
#endif
|
||||
|
||||
static void _test_child(const tf_test_options_t* options)
|
||||
{
|
||||
@ -881,7 +883,9 @@ void tf_tests(const tf_test_options_t* options)
|
||||
_tf_test_run(options, "ssb_id", tf_ssb_test_id_conversion, false);
|
||||
_tf_test_run(options, "ssb_following", tf_ssb_test_following, false);
|
||||
_tf_test_run(options, "nop", _test_nop, false);
|
||||
#if !defined(__HAIKU__)
|
||||
_tf_test_run(options, "sandbox", _test_sandbox, false);
|
||||
#endif
|
||||
_tf_test_run(options, "child", _test_child, false);
|
||||
_tf_test_run(options, "promise", _test_promise, false);
|
||||
_tf_test_run(options, "promise_remote_throw", _test_promise_remote_throw, false);
|
||||
|
@ -6,12 +6,21 @@
|
||||
** @{
|
||||
*/
|
||||
|
||||
/**
|
||||
** Options to control how tests are run.
|
||||
*/
|
||||
typedef struct _tf_test_options_t
|
||||
{
|
||||
/** The path to the Tilde Friends executable, in order to run subprocesses. */
|
||||
const char* exe_path;
|
||||
/** A comma-separated list of tests to run, or NULL. */
|
||||
const char* tests;
|
||||
} tf_test_options_t;
|
||||
|
||||
/**
|
||||
** Run tests.
|
||||
** @param options Test options.
|
||||
*/
|
||||
void tf_tests(const tf_test_options_t* options);
|
||||
|
||||
/** @} */
|
||||
|
128
src/tls.h
128
src/tls.h
@ -9,9 +9,19 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
** A TLS context. May have many tf_tls_session_t instances.
|
||||
*/
|
||||
typedef struct _tf_tls_context_t tf_tls_context_t;
|
||||
|
||||
/**
|
||||
** A TLS session. Belongs to one tf_tls_context_t and represents a single connection.
|
||||
*/
|
||||
typedef struct _tf_tls_session_t tf_tls_session_t;
|
||||
|
||||
/**
|
||||
** The state of a TLS handshake.
|
||||
*/
|
||||
typedef enum _tf_tls_handshake_t
|
||||
{
|
||||
k_tls_handshake_done,
|
||||
@ -19,31 +29,149 @@ typedef enum _tf_tls_handshake_t
|
||||
k_tls_handshake_failed,
|
||||
} tf_tls_handshake_t;
|
||||
|
||||
/**
|
||||
** Possible error statuses from tf_tls_session_read_plain.
|
||||
*/
|
||||
typedef enum _tf_tls_read_t
|
||||
{
|
||||
k_tls_read_zero = -1,
|
||||
k_tls_read_failed = -2,
|
||||
} tf_tls_read_t;
|
||||
|
||||
/**
|
||||
** Create a TLS context. Clean up with tf_tls_context_destroy().
|
||||
** @return A new TLS context.
|
||||
*/
|
||||
tf_tls_context_t* tf_tls_context_create();
|
||||
|
||||
/**
|
||||
** Set the TLS context's server certificate.
|
||||
** @param context The TLS context.
|
||||
** @param certificate The certificate in PEM format.
|
||||
** @return true if set successfully.
|
||||
*/
|
||||
bool tf_tls_context_set_certificate(tf_tls_context_t* context, const char* certificate);
|
||||
|
||||
/**
|
||||
** Set the TLS context's server certificate's private key.
|
||||
** @param context The TLS context.
|
||||
** @param private_key The private key in PEM format.
|
||||
** @return true if set successfully.
|
||||
*/
|
||||
bool tf_tls_context_set_private_key(tf_tls_context_t* context, const char* private_key);
|
||||
|
||||
/**
|
||||
** Add a trusted certificate.
|
||||
** @param context The TLS context.
|
||||
** @param certificate The certificate in PEM format.
|
||||
** @return true if the certificate was added to the trusted list successfully.
|
||||
*/
|
||||
bool tf_tls_context_add_trusted_certificate(tf_tls_context_t* context, const char* certificate);
|
||||
|
||||
/**
|
||||
** Create a TLS session from a context. Once created, call
|
||||
** tf_tls_session_handshake() until it returns k_tls_handshake_done. Call
|
||||
** tf_tls_session_[read/write]_[plain/encrypted]() as data is available.
|
||||
** @param context The TLS context. Clean up with tf_tls_session_destroy().
|
||||
** @return A new TLS session.
|
||||
*/
|
||||
tf_tls_session_t* tf_tls_context_create_session(tf_tls_context_t* context);
|
||||
|
||||
/**
|
||||
** Destroy a TLS context.
|
||||
** @param context The TLS contextx created by tf_tls_context_create().
|
||||
*/
|
||||
void tf_tls_context_destroy(tf_tls_context_t* context);
|
||||
|
||||
/**
|
||||
** Destroy a TLS session.
|
||||
** @param session A TLS sesssion created by tf_tls_context_create_session().
|
||||
*/
|
||||
void tf_tls_session_destroy(tf_tls_session_t* session);
|
||||
|
||||
/**
|
||||
** Set the remote hostname for a session.
|
||||
** @param session The TLS session.
|
||||
** @param hostname The hostname.
|
||||
*/
|
||||
void tf_tls_session_set_hostname(tf_tls_session_t* session, const char* hostname);
|
||||
|
||||
/**
|
||||
** Begin an outgoing TLS session.
|
||||
** @param session The TLS session.
|
||||
*/
|
||||
void tf_tls_session_start_accept(tf_tls_session_t* session);
|
||||
|
||||
/**
|
||||
** Begin an incoming TLS session.
|
||||
** @param session The TLS session.
|
||||
*/
|
||||
void tf_tls_session_start_connect(tf_tls_session_t* session);
|
||||
|
||||
/**
|
||||
** Begin the clean shutdown process for a TLS session.
|
||||
** @param session The TLS session.
|
||||
*/
|
||||
void tf_tls_session_shutdown(tf_tls_session_t* session);
|
||||
|
||||
/**
|
||||
** Get the certificate from the remote end of a TLS session if available.
|
||||
** @param session The TLS session.
|
||||
** @param buffer A buffer to receive the certificate.
|
||||
** @param bytes The size of the buffer.
|
||||
** @return The size of the returned certificate, or -1 on failure.
|
||||
*/
|
||||
int tf_tls_session_get_peer_certificate(tf_tls_session_t* session, char* buffer, size_t bytes);
|
||||
|
||||
/**
|
||||
** Update the TLS handshake. Call repeatedly as new data is available until it returns done.
|
||||
** @param session The TLS session.
|
||||
** @return The current state of the handshake process.
|
||||
*/
|
||||
tf_tls_handshake_t tf_tls_session_handshake(tf_tls_session_t* session);
|
||||
|
||||
/**
|
||||
** Read decrypted data from the TLS session.
|
||||
** @param session The TLS session.
|
||||
** @param buffer A buffer to receive the data.
|
||||
** @param bytes The size of the buffer.
|
||||
** @return The number of bytes returned.
|
||||
*/
|
||||
int tf_tls_session_read_plain(tf_tls_session_t* session, char* buffer, size_t bytes);
|
||||
|
||||
/**
|
||||
** Write unencrypted data to the TLS session.
|
||||
** @param session The TLS session.
|
||||
** @param buffer The data to encrypt.
|
||||
** @param bytes The size of the data.
|
||||
** @return 1 on success, 0 on failure.
|
||||
*/
|
||||
int tf_tls_session_write_plain(tf_tls_session_t* session, const char* buffer, size_t bytes);
|
||||
|
||||
/**
|
||||
** Read encrypted data from the TLS session that needs to be sent.
|
||||
** @param session The TLS session.
|
||||
** @param buffer A buffer to receive the data.
|
||||
** @param bytes The size of the buffer.
|
||||
** @return The number of bytes returned.
|
||||
*/
|
||||
int tf_tls_session_read_encrypted(tf_tls_session_t* session, char* buffer, size_t bytes);
|
||||
|
||||
/**
|
||||
** Write encrypted data to the TLS session.
|
||||
** @param session The TLS session.
|
||||
** @param buffer The encrypted data.
|
||||
** @param bytes The number of bytes written.
|
||||
*/
|
||||
int tf_tls_session_write_encrypted(tf_tls_session_t* session, const char* buffer, size_t bytes);
|
||||
|
||||
/**
|
||||
** Retrieve the last error from a TLS session.
|
||||
** @param session The TLS session.
|
||||
** @param buffer A buffer to receive the error text.
|
||||
** @param bytes The size of the buffer.
|
||||
** @return true if an error was retrieved.
|
||||
*/
|
||||
bool tf_tls_session_get_error(tf_tls_session_t* session, char* buffer, size_t bytes);
|
||||
|
||||
/** @} */
|
||||
|
@ -8,10 +8,30 @@
|
||||
|
||||
#include "quickjs.h"
|
||||
|
||||
/**
|
||||
** A TLS context instance.
|
||||
*/
|
||||
typedef struct _tf_tls_context_t tf_tls_context_t;
|
||||
|
||||
/**
|
||||
** Register TLS script interface.
|
||||
** @param context The TLS context.
|
||||
** @return the TlsContext constructor.
|
||||
*/
|
||||
JSValue tf_tls_context_register(JSContext* context);
|
||||
|
||||
/**
|
||||
** Get a TLS context instance from its JS object.
|
||||
** @param value A TlsContext JS object.
|
||||
** @return The corresponding instance.
|
||||
*/
|
||||
tf_tls_context_t* tf_tls_context_get(JSValue value);
|
||||
|
||||
/**
|
||||
** Get the number of active TLS context instances.
|
||||
** @return The number of TlsContext objects created that have not been
|
||||
** finalized.
|
||||
*/
|
||||
int tf_tls_context_get_count();
|
||||
|
||||
/** @} */
|
||||
|
75
src/trace.h
75
src/trace.h
@ -3,32 +3,103 @@
|
||||
/**
|
||||
** \defgroup trace Performance Tracing
|
||||
** Generates trace output that is compatible with speedscope.app,
|
||||
** chrome://tracing or ui.perfetto.dev for scrutining what each thread is doing
|
||||
** for optimization purposes.
|
||||
** chrome://tracing or ui.perfetto.dev for scrutinizing what each thread is
|
||||
** doing for optimization purposes.
|
||||
** @{
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
** A trace instance.
|
||||
*/
|
||||
typedef struct _tf_trace_t tf_trace_t;
|
||||
|
||||
/**
|
||||
** An SQLite database instance.
|
||||
*/
|
||||
typedef struct sqlite3 sqlite3;
|
||||
|
||||
/**
|
||||
** Create a trace instance. Can be used to produce a Chrome trace event
|
||||
** document for analyzing performance. Clean up with tf_trace_destroy().
|
||||
** @return A trace instance.
|
||||
*/
|
||||
tf_trace_t* tf_trace_create();
|
||||
|
||||
/**
|
||||
** Destroy a trace instance that was created with tf_trace_create().
|
||||
*/
|
||||
void tf_trace_destroy(tf_trace_t* trace);
|
||||
|
||||
/**
|
||||
** Set the name of the current process.
|
||||
** @param trace The trace instance.
|
||||
** @param name The name of the process.
|
||||
*/
|
||||
void tf_trace_set_process_name(tf_trace_t* trace, const char* name);
|
||||
|
||||
/**
|
||||
** Record counter values.
|
||||
** @param trace The trace instance.
|
||||
** @param name The counter group name.
|
||||
** @param argc The number of counters.
|
||||
** @param arg_names The counter names.
|
||||
** @param arg_values The counter values.
|
||||
*/
|
||||
void tf_trace_counter(tf_trace_t* trace, const char* name, int argc, const char** arg_names, const int64_t* arg_values);
|
||||
|
||||
/**
|
||||
** Begin tracing a time interval. End with tf_trace_end().
|
||||
** @param trace The trace instance.
|
||||
** @param name The interval name.
|
||||
*/
|
||||
void tf_trace_begin(tf_trace_t* trace, const char* name);
|
||||
|
||||
/**
|
||||
** End tracing the next time interval previously started with tf_trace_begin().
|
||||
** @param trace The trace instance.
|
||||
*/
|
||||
void tf_trace_end(tf_trace_t* trace);
|
||||
|
||||
/**
|
||||
** Export all currently stored trace data to a string.
|
||||
** @param trace The trace instance.
|
||||
** @return A string representation of the trace data.
|
||||
*/
|
||||
char* tf_trace_export(tf_trace_t* trace);
|
||||
|
||||
/**
|
||||
** A callback to collect trace data.
|
||||
** @param trace The trace instance.
|
||||
** @param buffer The trace data.
|
||||
** @param size The size of the trace data.
|
||||
** @param user_data User data registered with the callback.
|
||||
*/
|
||||
typedef void(tf_trace_write_callback_t)(tf_trace_t* trace, const char* buffer, size_t size, void* user_data);
|
||||
|
||||
/**
|
||||
** Replace the trace recording behavior.
|
||||
** @param trace The trace instance.
|
||||
** @param callback A callback that will be called instead of collecting the trace data in a buffer.
|
||||
** @param user_data User data to pass to the callback.
|
||||
*/
|
||||
void tf_trace_set_write_callback(tf_trace_t* trace, tf_trace_write_callback_t* callback, void* user_data);
|
||||
|
||||
/**
|
||||
** Inject raw trace data.
|
||||
** @param trace The trace instance.
|
||||
** @param buffer The trace data.
|
||||
** @param size The size of the trace data.
|
||||
*/
|
||||
void tf_trace_raw(tf_trace_t* trace, const char* buffer, size_t size);
|
||||
|
||||
/**
|
||||
** Register for trace-worthy events from sqlite and record them going forward.
|
||||
** @param trace The trace instance.
|
||||
** @param sqlite The SQLite database.
|
||||
*/
|
||||
void tf_trace_sqlite(tf_trace_t* trace, sqlite3* sqlite);
|
||||
|
||||
/** @} */
|
||||
|
@ -46,10 +46,10 @@ static JSValue _util_utf8_decode(JSContext* context, JSValueConst this_val, int
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t offset;
|
||||
size_t element_size;
|
||||
size_t offset = 0;
|
||||
size_t element_size = 0;
|
||||
JSValue buffer = tf_util_try_get_typed_array_buffer(context, argv[0], &offset, &length, &element_size);
|
||||
size_t size;
|
||||
size_t size = 0;
|
||||
if (!JS_IsException(buffer))
|
||||
{
|
||||
array = tf_util_try_get_array_buffer(context, &size, buffer);
|
||||
|
@ -1,2 +1,2 @@
|
||||
#define VERSION_NUMBER "0.0.16"
|
||||
#define VERSION_NAME "Now with 38% more process."
|
||||
#define VERSION_NUMBER "0.0.17-wip"
|
||||
#define VERSION_NAME "Please enjoy responsibly."
|
||||
|
@ -1,16 +1,20 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
if sys.platform == 'haiku1':
|
||||
print('Automation tests are disabled on Haiku.')
|
||||
exit(0)
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.firefox.service import Service
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
def exists_in_shadow_root(shadow_root, by, value):
|
||||
return lambda driver: shadow_root.find_element(by, value)
|
||||
|
||||
|
Reference in New Issue
Block a user