Compare commits
80 Commits
v0.0.31
...
052663efbe
| Author | SHA1 | Date | |
|---|---|---|---|
| 052663efbe | |||
| 8f84ff2611 | |||
| 37e1c5d97b | |||
| cef526bcf3 | |||
| 6af36cafa9 | |||
| fca859d93d | |||
| 2178300d8d | |||
| 636bdcce6b | |||
| 94b7703ca9 | |||
| a391dd1316 | |||
| b6ba5211b7 | |||
| 8e8e130045 | |||
| 1f40bc1a0f | |||
| 5437212222 | |||
| a8ab845cd2 | |||
| 8cee6dc98b | |||
| 70c2b73414 | |||
| 98013c4422 | |||
| e9e22b762d | |||
| 620db19936 | |||
| 94a79dd62c | |||
| b56c3efde0 | |||
| 066827f8f1 | |||
| c3b65d9cd8 | |||
| a15b916b06 | |||
| 31d0a5c233 | |||
| 140179e80a | |||
| 53cba2d7e4 | |||
| e54312d3b8 | |||
| cadc27b7b5 | |||
| 388b829ec1 | |||
| 67861f0f33 | |||
| a1f1eb34d5 | |||
| 2a6789063e | |||
| cbf1273a55 | |||
| 8143a23ced | |||
| 3c17810747 | |||
| bea7a2e9ed | |||
| 2f0a2ac6b0 | |||
| 18908b6b56 | |||
| b135a210cc | |||
| 3a2a829940 | |||
| e56dd2dd2d | |||
| 3f41a48bc7 | |||
| 65ed53281a | |||
| 1121557a2e | |||
| d4a7b86ee7 | |||
| 626c18b04e | |||
| bfa97ed7c7 | |||
| deae4d5367 | |||
| 899605c860 | |||
| dc9a279991 | |||
| 2a53892581 | |||
| 6bef0eb764 | |||
| 462b40640c | |||
| 72e1b2025c | |||
| fc7c4b1257 | |||
| 6c22c59056 | |||
| 94c2b1184f | |||
| 45231d703d | |||
| 7882fcbe8f | |||
| 3bbc8c4d35 | |||
| 8ae10dc80b | |||
| 9b11c2c629 | |||
| e2a231fb4a | |||
| 8a9502d1f2 | |||
| 534438df63 | |||
| 45a4feec96 | |||
| aa7a32395e | |||
| ab9f57f044 | |||
| 4040d6aa08 | |||
| 1c96f5c35e | |||
| 4d3e42812d | |||
| f7b3711d4f | |||
| 2408e076ff | |||
| 6f71ffb477 | |||
| 214433f36a | |||
| 309b22732e | |||
| 6fe7687b2a | |||
| a8cbf757ff |
2
Doxyfile
@@ -1051,7 +1051,7 @@ EXAMPLE_RECURSIVE = NO
|
|||||||
# that contain images that are to be included in the documentation (see the
|
# that contain images that are to be included in the documentation (see the
|
||||||
# \image command).
|
# \image command).
|
||||||
|
|
||||||
IMAGE_PATH =
|
IMAGE_PATH = docs/images/
|
||||||
|
|
||||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||||
|
|||||||
21
GNUmakefile
@@ -16,14 +16,14 @@ MAKEFLAGS += --no-builtin-rules
|
|||||||
## LD := Linker.
|
## LD := Linker.
|
||||||
## ANDROID_SDK := Path to the Android SDK.
|
## ANDROID_SDK := Path to the Android SDK.
|
||||||
|
|
||||||
VERSION_CODE := 37
|
VERSION_CODE := 39
|
||||||
VERSION_CODE_IOS := 13
|
VERSION_CODE_IOS := 15
|
||||||
VERSION_NUMBER := 0.0.31
|
VERSION_NUMBER := 0.0.33-wip
|
||||||
VERSION_NAME := This program kills fascists.
|
VERSION_NAME := This program kills fascists.
|
||||||
|
|
||||||
IPHONEOS_VERSION_MIN=14.0
|
IPHONEOS_VERSION_MIN=14.0
|
||||||
|
|
||||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3490200.zip
|
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500100.zip
|
||||||
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
||||||
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||||
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
||||||
@@ -650,6 +650,7 @@ SODIUM_SOURCES := \
|
|||||||
deps/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
|
deps/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
|
||||||
deps/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c \
|
deps/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c \
|
||||||
deps/libsodium/src/libsodium/crypto_core/softaes/softaes.c \
|
deps/libsodium/src/libsodium/crypto_core/softaes/softaes.c \
|
||||||
|
deps/libsodium/src/libsodium/crypto_generichash/crypto_generichash.c \
|
||||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
|
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
|
||||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c \
|
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c \
|
||||||
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c \
|
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c \
|
||||||
@@ -1483,6 +1484,18 @@ help: ## Display this help message.
|
|||||||
.PHONY: help
|
.PHONY: help
|
||||||
.DEFAULT_GOAL := help
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
docs: debug
|
||||||
docs: ## Build HTML docs.
|
docs: ## Build HTML docs.
|
||||||
|
@echo '# CLI Usage\n' > docs/usage.md
|
||||||
|
@echo "## tildefriends -h" >> docs/usage.md
|
||||||
|
@echo '\n```' >> docs/usage.md
|
||||||
|
@out/debug/tildefriends -h >> docs/usage.md
|
||||||
|
@echo '```' >> docs/usage.md
|
||||||
|
@for command in $$(out/debug/tildefriends -h | grep -Po '[A-Za-z_]*(?= - )'); do
|
||||||
|
@ echo "\n## tildefriends $$command -h" >> docs/usage.md
|
||||||
|
@ echo '\n```' >> docs/usage.md
|
||||||
|
@ out/debug/tildefriends $$command -h >> docs/usage.md
|
||||||
|
@ echo '```' >> docs/usage.md
|
||||||
|
@done
|
||||||
@doxygen
|
@doxygen
|
||||||
.PHONY: docs
|
.PHONY: docs
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "💡",
|
"emoji": "💡",
|
||||||
"previous": "&ckE7T/dt9f1xV8jNSgXVcXYkAzMhU9689MRQbhOi9Wo=.sha256"
|
"previous": "&eN6DNPpQUNhGvxneLuLPgsOXR6qyFZ7u+MAz0b4fa7k=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tfrpc.register(async function complete() {
|
tfrpc.register(async function complete() {
|
||||||
if ((await core.globalSettingsGet('index')) == '/~core/intro/') {
|
if (
|
||||||
|
core.user?.credentials?.permissions?.administration &&
|
||||||
|
(await core.globalSettingsGet('index')) == '/~core/intro/'
|
||||||
|
) {
|
||||||
return await core.globalSettingsSet('index', '/~core/ssb/');
|
return await core.globalSettingsSet('index', '/~core/ssb/');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
<div class="w3-container w3-large w3-left-align">
|
<div class="w3-container w3-large w3-left-align">
|
||||||
<p>
|
<p>
|
||||||
Secure Scuttlebutt is a social network whose technical operation
|
Secure Scuttlebutt is a social network whose technical operation
|
||||||
attempts to mirrors human social interaction.
|
attempts to mirror human social interaction.
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🚪",
|
"emoji": "🚪",
|
||||||
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
|
"previous": "&DJwkqNfYWtW9yBtJQMseEXm7l04Enpi+yAxZulLq9Vk=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ async function main() {
|
|||||||
print(core.url);
|
print(core.url);
|
||||||
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
|
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
|
||||||
let port = await ssb.port();
|
let port = await ssb.port();
|
||||||
let id = (await ssb.getServerIdentity()).substring(1);
|
let id = (await ssb.getServerIdentity()).substring(1).split('.')[0];
|
||||||
let room = `net:${host}:${port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
let room = `net:${host}:${port}~shs:${id}:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||||
await app.setDocument(`
|
await app.setDocument(`
|
||||||
<body style="color: #fff">
|
<body style="color: #fff">
|
||||||
<h1>Server</h1>
|
<h1>Server</h1>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🦀",
|
"emoji": "🦀",
|
||||||
"previous": "&0Lxm4IgS3mpvSccP3bg7wNPACtLKMTbie51ea/vJbeg=.sha256"
|
"previous": "&lOCyez82jwCUs8R/Ynx2nBK598+qDZ7RhGnWOaNQO9U=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,15 @@ tfrpc.register(async function sync() {
|
|||||||
tfrpc.register(async function url() {
|
tfrpc.register(async function url() {
|
||||||
return core.url;
|
return core.url;
|
||||||
});
|
});
|
||||||
|
tfrpc.register(async function globalSettingsGet(key) {
|
||||||
|
return core.globalSettingsGet(key);
|
||||||
|
});
|
||||||
|
tfrpc.register(async function globalSettingsSet(key, value) {
|
||||||
|
return core.globalSettingsSet(key, value);
|
||||||
|
});
|
||||||
|
tfrpc.register(function isAdministrator() {
|
||||||
|
return core.user?.credentials?.permissions?.administration;
|
||||||
|
});
|
||||||
|
|
||||||
core.register('onBroadcastsChanged', async function () {
|
core.register('onBroadcastsChanged', async function () {
|
||||||
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ class TfElement extends LitElement {
|
|||||||
url: {type: String},
|
url: {type: String},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
is_administrator: {type: Boolean},
|
||||||
|
stay_connected: {type: Boolean},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +75,10 @@ class TfElement extends LitElement {
|
|||||||
async initial_load() {
|
async initial_load() {
|
||||||
let whoami = await tfrpc.rpc.getActiveIdentity();
|
let whoami = await tfrpc.rpc.getActiveIdentity();
|
||||||
let ids = (await tfrpc.rpc.getIdentities()) || [];
|
let ids = (await tfrpc.rpc.getIdentities()) || [];
|
||||||
|
this.is_administrator = await tfrpc.rpc.isAdministrator();
|
||||||
|
this.stay_connected =
|
||||||
|
this.is_administrator &&
|
||||||
|
(await tfrpc.rpc.globalSettingsGet('stay_connected'));
|
||||||
this.url = await tfrpc.rpc.url();
|
this.url = await tfrpc.rpc.url();
|
||||||
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
|
||||||
this.guest = !this.whoami?.length;
|
this.guest = !this.whoami?.length;
|
||||||
@@ -128,7 +134,13 @@ class TfElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
next_channel(delta) {
|
next_channel(delta) {
|
||||||
let channel_names = ['', '@', '🔐', ...this.channels.map((x) => '#' + x)];
|
let channel_names = [
|
||||||
|
'',
|
||||||
|
'@',
|
||||||
|
'👍',
|
||||||
|
'🔐',
|
||||||
|
...this.channels.map((x) => '#' + x),
|
||||||
|
];
|
||||||
let index = channel_names.indexOf(this.hash.substring(1));
|
let index = channel_names.indexOf(this.hash.substring(1));
|
||||||
index = index != -1 ? index + delta : 0;
|
index = index != -1 ? index + delta : 0;
|
||||||
tfrpc.rpc.setHash(
|
tfrpc.rpc.setHash(
|
||||||
@@ -302,11 +314,7 @@ class TfElement extends LitElement {
|
|||||||
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
||||||
}
|
}
|
||||||
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
|
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
|
||||||
ranges.push([
|
ranges.push([Math.max(i - k_chunk_size, 0), i, false]);
|
||||||
Math.max(i - k_chunk_size, 0),
|
|
||||||
Math.min(cache.range[0], i + k_chunk_size),
|
|
||||||
false,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < latest; i += k_chunk_size) {
|
for (let i = 0; i < latest; i += k_chunk_size) {
|
||||||
@@ -322,7 +330,7 @@ class TfElement extends LitElement {
|
|||||||
messages.rowid > ?1 AND
|
messages.rowid > ?1 AND
|
||||||
messages.rowid <= ?2 AND
|
messages.rowid <= ?2 AND
|
||||||
json(messages.content) LIKE '"%'
|
json(messages.content) LIKE '"%'
|
||||||
ORDER BY sequence DESC
|
ORDER BY messages.rowid DESC
|
||||||
`,
|
`,
|
||||||
[range[0], range[1]]
|
[range[0], range[1]]
|
||||||
);
|
);
|
||||||
@@ -348,42 +356,93 @@ class TfElement extends LitElement {
|
|||||||
return [cache.latest, cache.messages];
|
return [cache.latest, cache.messages];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async query_timed(sql, args) {
|
||||||
|
let start = new Date();
|
||||||
|
let result = await tfrpc.rpc.query(sql, args);
|
||||||
|
let end = new Date();
|
||||||
|
console.log((end - start) / 1000, sql);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
async load_channels_latest(following) {
|
async load_channels_latest(following) {
|
||||||
let start_time = new Date();
|
let start_time = new Date();
|
||||||
let latest_private = this.get_latest_private(following);
|
let latest_private = this.get_latest_private(following);
|
||||||
let channels = await tfrpc.rpc.query(
|
const k_args = [
|
||||||
`
|
JSON.stringify(this.channels),
|
||||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
JSON.stringify(following),
|
||||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
'"' + this.whoami.replace('"', '""') + '"',
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
this.whoami,
|
||||||
WHERE
|
];
|
||||||
messages.content ->> 'type' = 'post' AND
|
let channels = (
|
||||||
messages.content ->> 'root' IS NULL AND
|
await Promise.all([
|
||||||
messages.author != ?4
|
this.query_timed(
|
||||||
GROUP by channel
|
`
|
||||||
UNION
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
WHERE
|
WHERE
|
||||||
messages.content ->> 'type' = 'post' AND
|
messages.content ->> 'type' = 'post' AND
|
||||||
messages.content ->> 'root' IS NULL AND
|
messages.content ->> 'root' IS NULL AND
|
||||||
messages.author != ?4
|
messages.author != ?4
|
||||||
UNION
|
GROUP by channel
|
||||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
`,
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
k_args
|
||||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
),
|
||||||
WHERE messages.author != ?4
|
this.query_timed(
|
||||||
`,
|
`
|
||||||
[
|
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
JSON.stringify(this.channels),
|
JOIN messages_refs ON messages.id = messages_refs.message
|
||||||
JSON.stringify(following),
|
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||||
'"' + this.whoami.replace('"', '""') + '"',
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
this.whoami,
|
WHERE
|
||||||
]
|
messages.content ->> 'type' = 'post' AND
|
||||||
);
|
messages.content ->> 'root' IS NULL AND
|
||||||
this.channels_latest = Object.fromEntries(
|
messages.author != ?4
|
||||||
channels.map((x) => [x.channel, x.rowid])
|
GROUP by channel
|
||||||
);
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
|
this.query_timed(
|
||||||
|
`
|
||||||
|
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
|
WHERE
|
||||||
|
messages.content ->> 'type' = 'post' AND
|
||||||
|
messages.content ->> 'root' IS NULL AND
|
||||||
|
messages.author != ?4
|
||||||
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
|
this.query_timed(
|
||||||
|
`
|
||||||
|
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||||
|
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||||
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
|
WHERE messages.author != ?4
|
||||||
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
|
this.query_timed(
|
||||||
|
`
|
||||||
|
SELECT '👍' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||||
|
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||||
|
WHERE
|
||||||
|
messages.content ->> 'type' = 'vote' AND
|
||||||
|
messages.author != ?4
|
||||||
|
`,
|
||||||
|
k_args
|
||||||
|
),
|
||||||
|
])
|
||||||
|
).flat();
|
||||||
|
let latest = {};
|
||||||
|
for (let row of channels) {
|
||||||
|
if (!latest[row.channel]) {
|
||||||
|
latest[row.channel] = row.rowid;
|
||||||
|
} else {
|
||||||
|
latest[row.channel] = Math.max(row.rowid, latest[row.channel]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.channels_latest = latest;
|
||||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
console.log('channels took', (new Date() - start_time) / 1000.0);
|
||||||
let self = this;
|
let self = this;
|
||||||
start_time = new Date();
|
start_time = new Date();
|
||||||
@@ -605,6 +664,18 @@ class TfElement extends LitElement {
|
|||||||
tfrpc.rpc.sync();
|
tfrpc.rpc.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async toggle_stay_connected() {
|
||||||
|
let stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
|
||||||
|
let new_stay_connected = !this.stay_connected;
|
||||||
|
try {
|
||||||
|
if (new_stay_connected != stay_connected) {
|
||||||
|
await tfrpc.rpc.globalSettingsSet('stay_connected', new_stay_connected);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
@@ -627,14 +698,26 @@ class TfElement extends LitElement {
|
|||||||
class="w3-bar w3-theme-l1"
|
class="w3-bar w3-theme-l1"
|
||||||
style="position: static; top: 0; z-index: 10"
|
style="position: static; top: 0; z-index: 10"
|
||||||
>
|
>
|
||||||
<button
|
${this.is_administrator
|
||||||
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
? html`
|
||||||
(this.connections?.some((x) => x.flags.one_shot) ? ' w3-spin' : '')}
|
<button
|
||||||
style="width: 1.5em; height: 1.5em; padding: 8px"
|
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
|
||||||
@click=${this.refresh}
|
(this.connections?.some((x) => x.flags.one_shot)
|
||||||
>
|
? ' w3-spin'
|
||||||
↻
|
: '')}
|
||||||
</button>
|
style="width: 1.5em; height: 1.5em; padding: 8px"
|
||||||
|
@click=${this.refresh}
|
||||||
|
>
|
||||||
|
↻
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="w3-bar-item w3-button w3-ripple"
|
||||||
|
@click=${this.toggle_stay_connected}
|
||||||
|
>
|
||||||
|
${this.stay_connected ? '🔗' : '⛓️💥'}
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
${Object.entries(k_tabs).map(
|
${Object.entries(k_tabs).map(
|
||||||
([k, v]) => html`
|
([k, v]) => html`
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -446,12 +446,15 @@ class TfComposeElement extends LitElement {
|
|||||||
self.apps = await tfrpc.rpc.apps();
|
self.apps = await tfrpc.rpc.apps();
|
||||||
}
|
}
|
||||||
if (!this.apps) {
|
if (!this.apps) {
|
||||||
return html`<button class="w3-button w3-theme-d1" @click=${attach_app}>
|
return html`<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${attach_app}
|
||||||
|
>
|
||||||
Attach App
|
Attach App
|
||||||
</button>`;
|
</button>`;
|
||||||
} else {
|
} else {
|
||||||
return html`<button
|
return html`<button
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
@click=${() => (this.apps = null)}
|
@click=${() => (this.apps = null)}
|
||||||
>
|
>
|
||||||
Discard App
|
Discard App
|
||||||
@@ -472,18 +475,9 @@ class TfComposeElement extends LitElement {
|
|||||||
if (draft.content_warning !== undefined) {
|
if (draft.content_warning !== undefined) {
|
||||||
return html`
|
return html`
|
||||||
<div class="w3-container w3-padding">
|
<div class="w3-container w3-padding">
|
||||||
<p>
|
|
||||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
|
|
||||||
<label for="cw">CW</label>
|
|
||||||
</p>
|
|
||||||
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input>
|
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
} else {
|
|
||||||
return html`
|
|
||||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning('')}></input>
|
|
||||||
<label for="cw">CW</label>
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,6 +540,31 @@ class TfComposeElement extends LitElement {
|
|||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle_menu(event) {
|
||||||
|
event.srcElement.parentNode
|
||||||
|
.querySelector('.w3-dropdown-content')
|
||||||
|
.classList.toggle('w3-show');
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
this._click_callback = this.document_click.bind(this);
|
||||||
|
document.body.addEventListener('mouseup', this._click_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
document.body.removeEventListener('mouseup', this._click_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
document_click(event) {
|
||||||
|
let content = this.renderRoot.querySelector('.w3-dropdown-content');
|
||||||
|
let target = event.target;
|
||||||
|
if (content && !content.contains(target)) {
|
||||||
|
content.classList.remove('w3-show');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
let draft = self.get_draft();
|
let draft = self.get_draft();
|
||||||
@@ -559,7 +578,7 @@ class TfComposeElement extends LitElement {
|
|||||||
draft.encrypt_to !== undefined
|
draft.encrypt_to !== undefined
|
||||||
? undefined
|
? undefined
|
||||||
: html`<button
|
: html`<button
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
@click=${() => this.set_encrypt([])}
|
@click=${() => this.set_encrypt([])}
|
||||||
>
|
>
|
||||||
🔐
|
🔐
|
||||||
@@ -614,13 +633,43 @@ class TfComposeElement extends LitElement {
|
|||||||
>
|
>
|
||||||
Submit
|
Submit
|
||||||
</button>
|
</button>
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.attach}>
|
<div class="w3-dropdown-click">
|
||||||
Attach
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
</button>
|
⚙️
|
||||||
${this.render_attach_app_button()} ${encrypt}
|
</button>
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.discard}>
|
<div class="w3-dropdown-content w3-bar-block">
|
||||||
Discard
|
${this.get_draft().content_warning === undefined
|
||||||
</button>
|
? html`
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${() => self.set_content_warning('')}
|
||||||
|
>
|
||||||
|
Add Content Warning
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${() => self.set_content_warning(undefined)}
|
||||||
|
>
|
||||||
|
Remove Content Warning
|
||||||
|
</button>
|
||||||
|
`}
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${this.attach}
|
||||||
|
>
|
||||||
|
Attach
|
||||||
|
</button>
|
||||||
|
${this.render_attach_app_button()} ${encrypt}
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-theme-d1"
|
||||||
|
@click=${this.discard}
|
||||||
|
>
|
||||||
|
Discard
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
import {LitElement, html, repeat, render, unsafeHTML} from './lit-all.min.js';
|
import {
|
||||||
|
LitElement,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
repeat,
|
||||||
|
render,
|
||||||
|
unsafeHTML,
|
||||||
|
} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import * as tfutils from './tf-utils.js';
|
import * as tfutils from './tf-utils.js';
|
||||||
import * as emojis from './emojis.js';
|
import * as emojis from './emojis.js';
|
||||||
@@ -86,12 +93,18 @@ class TfMessageElement extends LitElement {
|
|||||||
|
|
||||||
render_votes() {
|
render_votes() {
|
||||||
function normalize_expression(expression) {
|
function normalize_expression(expression) {
|
||||||
if (expression === 'Like' || expression === 'like' || !expression) {
|
if (
|
||||||
return '👍';
|
expression === 'Unlike' ||
|
||||||
} else if (expression === 'Unlike' || expression === 'unlike') {
|
expression === 'unlike' ||
|
||||||
|
expression == 'undig'
|
||||||
|
) {
|
||||||
return '👎';
|
return '👎';
|
||||||
} else if (expression === 'heart') {
|
} else if (expression === 'heart') {
|
||||||
return '❤️';
|
return '❤️';
|
||||||
|
} else if (
|
||||||
|
(expression ?? '').split('').every((x) => x.charCodeAt(0) < 256)
|
||||||
|
) {
|
||||||
|
return '👍';
|
||||||
} else {
|
} else {
|
||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
@@ -297,31 +310,35 @@ class TfMessageElement extends LitElement {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expanded_key() {
|
||||||
|
return this.message?.id || this.messages?.map((x) => x.id).join(':');
|
||||||
|
}
|
||||||
|
|
||||||
set_expanded(expanded, tag) {
|
set_expanded(expanded, tag) {
|
||||||
|
let key = this.expanded_key();
|
||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent('tf-expand', {
|
new CustomEvent('tf-expand', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
composed: true,
|
composed: true,
|
||||||
detail: {id: (this.message.id || '') + (tag || ''), expanded: expanded},
|
detail: {id: key + (tag || ''), expanded: expanded},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle_expanded(tag) {
|
toggle_expanded(tag) {
|
||||||
this.set_expanded(
|
let key = this.expanded_key();
|
||||||
!this.expanded[(this.message.id || '') + (tag || '')],
|
this.set_expanded(!this.expanded[key + (tag || '')], tag);
|
||||||
tag
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is_expanded(tag) {
|
is_expanded(tag) {
|
||||||
return this.expanded[(this.message.id || '') + (tag || '')];
|
let key = this.expanded_key();
|
||||||
|
return this.expanded[key + (tag || '')];
|
||||||
}
|
}
|
||||||
|
|
||||||
render_children() {
|
render_children() {
|
||||||
let self = this;
|
let self = this;
|
||||||
if (this.message.child_messages?.length) {
|
if (this.message.child_messages?.length) {
|
||||||
if (!this.expanded[this.message.id]) {
|
if (!this.expanded[this.expanded_key()]) {
|
||||||
return html`
|
return html`
|
||||||
<button
|
<button
|
||||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
@@ -397,7 +414,7 @@ class TfMessageElement extends LitElement {
|
|||||||
class_background() {
|
class_background() {
|
||||||
return this.message?.decrypted
|
return this.message?.decrypted
|
||||||
? 'w3-pale-red'
|
? 'w3-pale-red'
|
||||||
: this.message?.rowid >= this.channel_unread
|
: this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||||
? 'w3-theme-d2'
|
? 'w3-theme-d2'
|
||||||
: 'w3-theme-d4';
|
: 'w3-theme-d4';
|
||||||
}
|
}
|
||||||
@@ -489,7 +506,10 @@ class TfMessageElement extends LitElement {
|
|||||||
return html`
|
return html`
|
||||||
<header class="w3-bar">
|
<header class="w3-bar">
|
||||||
<span class="w3-bar-item">
|
<span class="w3-bar-item">
|
||||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
${this.render_unread_icon()}<tf-user
|
||||||
|
id=${this.message.author}
|
||||||
|
.users=${this.users}
|
||||||
|
></tf-user>
|
||||||
</span>
|
</span>
|
||||||
${is_encrypted} ${this.render_menu()}
|
${is_encrypted} ${this.render_menu()}
|
||||||
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
|
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
|
||||||
@@ -574,6 +594,56 @@ class TfMessageElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content_group_by_author() {
|
||||||
|
let sorted = this.message.messages
|
||||||
|
.map((x) => [
|
||||||
|
x.author,
|
||||||
|
x.content.blocking !== undefined
|
||||||
|
? x.content.blocking
|
||||||
|
? 'is blocking'
|
||||||
|
: 'is no longer blocking'
|
||||||
|
: x.content.following !== undefined
|
||||||
|
? x.content.following
|
||||||
|
? 'is following'
|
||||||
|
: 'is no longer following'
|
||||||
|
: '',
|
||||||
|
x.content.contact,
|
||||||
|
x,
|
||||||
|
])
|
||||||
|
.sort();
|
||||||
|
let result = [];
|
||||||
|
let last;
|
||||||
|
let group;
|
||||||
|
for (let row of sorted) {
|
||||||
|
if (last && last[0] == row[0] && last[1] == row[1]) {
|
||||||
|
group.push(row[2]);
|
||||||
|
} else {
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], users: group});
|
||||||
|
}
|
||||||
|
last = row;
|
||||||
|
group = [row[2]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group) {
|
||||||
|
result.push({author: last[0], action: last[1], users: group});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
allow_unread() {
|
||||||
|
return (
|
||||||
|
this.channel == '@' ||
|
||||||
|
(!this.channel.startsWith('@') && !this.channel.startsWith('%'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render_unread_icon() {
|
||||||
|
return this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||||
|
? html`✉️`
|
||||||
|
: undefined;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let content = this.message?.content;
|
let content = this.message?.content;
|
||||||
if (this.message?.decrypted?.type == 'post') {
|
if (this.message?.decrypted?.type == 'post') {
|
||||||
@@ -582,29 +652,94 @@ class TfMessageElement extends LitElement {
|
|||||||
let class_background = this.class_background();
|
let class_background = this.class_background();
|
||||||
let self = this;
|
let self = this;
|
||||||
if (this.message?.type === 'contact_group') {
|
if (this.message?.type === 'contact_group') {
|
||||||
return this.render_frame(
|
if (this.expanded[this.expanded_key()]) {
|
||||||
html` ${this.message.messages.map(
|
return this.render_frame(html`
|
||||||
(x) =>
|
<div class="w3-padding">
|
||||||
html`<tf-message
|
${this.message.messages.map(
|
||||||
.message=${x}
|
(x) =>
|
||||||
whoami=${this.whoami}
|
html`<tf-message
|
||||||
.users=${this.users}
|
.message=${x}
|
||||||
.drafts=${this.drafts}
|
whoami=${this.whoami}
|
||||||
.expanded=${this.expanded}
|
.users=${this.users}
|
||||||
channel=${this.channel}
|
.drafts=${this.drafts}
|
||||||
channel_unread=${this.channel_unread}
|
.expanded=${this.expanded}
|
||||||
></tf-message>`
|
channel=${this.channel}
|
||||||
)}`
|
channel_unread=${this.channel_unread}
|
||||||
);
|
></tf-message>`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(false)}
|
||||||
|
>
|
||||||
|
Collapse
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
} else {
|
||||||
|
return this.render_frame(html`
|
||||||
|
<div class="w3-padding">
|
||||||
|
${this.content_group_by_author().map(
|
||||||
|
(x) => html`
|
||||||
|
<div>
|
||||||
|
<tf-user id=${x.author} .users=${this.users}></tf-user>
|
||||||
|
${x.action}
|
||||||
|
${x.users.map(
|
||||||
|
(y) => html`
|
||||||
|
<tf-user
|
||||||
|
id=${y}
|
||||||
|
.users=${this.users}
|
||||||
|
icon_only="true"
|
||||||
|
></tf-user>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||||
|
style="box-sizing: border-box"
|
||||||
|
@click=${() => self.set_expanded(true)}
|
||||||
|
>
|
||||||
|
Expand
|
||||||
|
</button>
|
||||||
|
`);
|
||||||
|
}
|
||||||
} else if (this.message.placeholder) {
|
} else if (this.message.placeholder) {
|
||||||
return this.render_frame(
|
return this.render_frame(
|
||||||
html`<div class="w3-padding">
|
html`<div>
|
||||||
<p>
|
<div class="w3-bar">
|
||||||
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
|
<a
|
||||||
>${this.message.id}</a
|
class="w3-bar-item w3-panel w3-round-xlarge w3-theme-d1 w3-margin w3-button"
|
||||||
|
target="_top"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
>
|
>
|
||||||
(placeholder)
|
This message is not currently available.
|
||||||
</p>
|
</a>
|
||||||
|
<div class="w3-bar-item w3-right">
|
||||||
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
|
%
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||||
|
style="right: 48px"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
target="_top"
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
|
>View Message</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-border-bottom"
|
||||||
|
@click=${this.copy_id}
|
||||||
|
>
|
||||||
|
Copy ID
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>${this.render_votes()}</div>
|
<div>${this.render_votes()}</div>
|
||||||
${(this.message.child_messages || []).map(
|
${(this.message.child_messages || []).map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
@@ -631,7 +766,7 @@ class TfMessageElement extends LitElement {
|
|||||||
}
|
}
|
||||||
if (content.image !== undefined) {
|
if (content.image !== undefined) {
|
||||||
image = html`
|
image = html`
|
||||||
<div><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
<div @click=${this.body_click}><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
if (content.description !== undefined) {
|
if (content.description !== undefined) {
|
||||||
@@ -654,25 +789,60 @@ class TfMessageElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
} else if (content.type == 'contact') {
|
} else if (content.type == 'contact') {
|
||||||
return html`
|
return this.render_frame(html`
|
||||||
<div class="w3-padding">
|
<div class="w3-bar">
|
||||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
<div class="w3-bar-item">
|
||||||
is
|
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||||
${content.blocking === true
|
is
|
||||||
? 'blocking'
|
${content.blocking === true
|
||||||
: content.blocking === false
|
? 'blocking'
|
||||||
? 'no longer blocking'
|
: content.blocking === false
|
||||||
: content.following === true
|
? 'no longer blocking'
|
||||||
? 'following'
|
: content.following === true
|
||||||
: content.following === false
|
? 'following'
|
||||||
? 'no longer following'
|
: content.following === false
|
||||||
: '?'}
|
? 'no longer following'
|
||||||
<tf-user
|
: '?'}
|
||||||
id=${this.message.content.contact}
|
<tf-user
|
||||||
.users=${this.users}
|
id=${this.message.content.contact}
|
||||||
></tf-user>
|
.users=${this.users}
|
||||||
|
></tf-user>
|
||||||
|
</div>
|
||||||
|
<div class="w3-bar-item w3-right">
|
||||||
|
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||||
|
%
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||||
|
style="right: 48px"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
target="_top"
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||||
|
>View Message</a
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item w3-border-bottom"
|
||||||
|
@click=${this.copy_id}
|
||||||
|
>
|
||||||
|
Copy ID
|
||||||
|
</button>
|
||||||
|
${this.drafts[this.message?.id] === undefined
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
class="w3-button w3-bar-item"
|
||||||
|
@click=${this.show_reply}
|
||||||
|
>
|
||||||
|
↩️ Reply
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${this.render_votes()} ${this.render_actions()}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`);
|
||||||
} else if (content.type == 'post') {
|
} else if (content.type == 'post') {
|
||||||
let self = this;
|
let self = this;
|
||||||
let body;
|
let body;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class TfNewsElement extends LitElement {
|
|||||||
channel: {type: String},
|
channel: {type: String},
|
||||||
channel_unread: {type: Number},
|
channel_unread: {type: Number},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
hash: {type: String},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +167,10 @@ class TfNewsElement extends LitElement {
|
|||||||
if (message?.content?.type === 'contact') {
|
if (message?.content?.type === 'contact') {
|
||||||
group.push(message);
|
group.push(message);
|
||||||
} else {
|
} else {
|
||||||
if (group.length > 0) {
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: 'contact_group',
|
||||||
@@ -177,7 +181,10 @@ class TfNewsElement extends LitElement {
|
|||||||
result.push(message);
|
result.push(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (group.length > 0) {
|
if (group.length == 1) {
|
||||||
|
result.push(group[0]);
|
||||||
|
group = [];
|
||||||
|
} else if (group.length > 1) {
|
||||||
result.push({
|
result.push({
|
||||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||||
type: 'contact_group',
|
type: 'contact_group',
|
||||||
@@ -187,15 +194,21 @@ class TfNewsElement extends LitElement {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unread_allowed() {
|
||||||
|
return !this.hash?.startsWith('#%') && !this.hash?.startsWith('#@');
|
||||||
|
}
|
||||||
|
|
||||||
load_and_render(messages) {
|
load_and_render(messages) {
|
||||||
let messages_by_id = this.process_messages(messages);
|
let messages_by_id = this.process_messages(messages);
|
||||||
let final_messages = this.group_following(
|
let final_messages = this.group_following(
|
||||||
this.finalize_messages(messages_by_id)
|
this.finalize_messages(messages_by_id)
|
||||||
);
|
);
|
||||||
let unread_rowid = -1;
|
let unread_rowid = -1;
|
||||||
for (let message of final_messages) {
|
if (this.unread_allowed()) {
|
||||||
if (message.rowid >= this.channel_unread) {
|
for (let message of final_messages) {
|
||||||
unread_rowid = message.rowid;
|
if (message.rowid >= this.channel_unread) {
|
||||||
|
unread_rowid = message.rowid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
import {LitElement, html, until, unsafeHTML} from './lit-all.min.js';
|
||||||
import * as tfrpc from '/static/tfrpc.js';
|
import * as tfrpc from '/static/tfrpc.js';
|
||||||
import * as tfutils from './tf-utils.js';
|
import * as tfutils from './tf-utils.js';
|
||||||
import {styles} from './tf-styles.js';
|
import {styles} from './tf-styles.js';
|
||||||
@@ -166,6 +166,74 @@ class TfProfileElement extends LitElement {
|
|||||||
navigator.clipboard.writeText(this.id);
|
navigator.clipboard.writeText(this.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_image(link) {
|
||||||
|
let div = document.createElement('div');
|
||||||
|
div.style.left = 0;
|
||||||
|
div.style.top = 0;
|
||||||
|
div.style.width = '100%';
|
||||||
|
div.style.height = '100%';
|
||||||
|
div.style.position = 'fixed';
|
||||||
|
div.style.background = '#000';
|
||||||
|
div.style.zIndex = 100;
|
||||||
|
div.style.display = 'grid';
|
||||||
|
let img = document.createElement('img');
|
||||||
|
img.src = link;
|
||||||
|
img.style.maxWidth = '100%';
|
||||||
|
img.style.maxHeight = '100%';
|
||||||
|
img.style.display = 'block';
|
||||||
|
img.style.margin = 'auto';
|
||||||
|
img.style.objectFit = 'contain';
|
||||||
|
img.style.width = '100%';
|
||||||
|
div.appendChild(img);
|
||||||
|
function image_close(event) {
|
||||||
|
document.body.removeChild(div);
|
||||||
|
window.removeEventListener('keydown', image_close);
|
||||||
|
}
|
||||||
|
div.onclick = image_close;
|
||||||
|
window.addEventListener('keydown', image_close);
|
||||||
|
document.body.appendChild(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
body_click(event) {
|
||||||
|
if (event.srcElement.tagName == 'IMG') {
|
||||||
|
this.show_image(event.srcElement.src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle_account_list(event) {
|
||||||
|
let content = event.srcElement.nextElementSibling;
|
||||||
|
if (content.classList.toggle('w3-hide')) {
|
||||||
|
event.srcElement.innerText = 'Show Followed Accounts';
|
||||||
|
} else {
|
||||||
|
event.srcElement.innerText = 'Hide Followed Accounts';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async load_follows() {
|
||||||
|
let accounts = await tfrpc.rpc.following([this.id], 1);
|
||||||
|
return html`
|
||||||
|
<div class="w3-container">
|
||||||
|
<button
|
||||||
|
class="w3-button w3-block w3-theme-d1"
|
||||||
|
@click=${this.toggle_account_list}
|
||||||
|
>
|
||||||
|
Show Followed Accounts
|
||||||
|
</button>
|
||||||
|
<div class="w3-hide w3-card">
|
||||||
|
<ul class="w3-ul w3-theme-d4 w3-border-theme">
|
||||||
|
${Object.keys(accounts).map(
|
||||||
|
(x) => html`
|
||||||
|
<li class="w3-border-theme">
|
||||||
|
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||||
|
</li>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
this.load();
|
this.load();
|
||||||
let self = this;
|
let self = this;
|
||||||
@@ -254,7 +322,7 @@ class TfProfileElement extends LitElement {
|
|||||||
<header class="w3-container">
|
<header class="w3-container">
|
||||||
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
||||||
</header>
|
</header>
|
||||||
<div class="w3-container">
|
<div class="w3-container" @click=${this.body_click}>
|
||||||
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
||||||
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
||||||
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
||||||
@@ -280,6 +348,7 @@ class TfProfileElement extends LitElement {
|
|||||||
Blocked by ${profile.blocked} identities.
|
Blocked by ${profile.blocked} identities.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
${until(this.load_follows(), html`<p>Loading accounts followed...</p>`)}
|
||||||
<footer class="w3-container">
|
<footer class="w3-container">
|
||||||
<p>
|
<p>
|
||||||
${edit}
|
${edit}
|
||||||
|
|||||||
@@ -308,6 +308,12 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
<div class="w3-bar-item">
|
<div class="w3-bar-item">
|
||||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||||
<div><small>${x.address}:${x.port}</small></div>
|
<div><small>${x.address}:${x.port}</small></div>
|
||||||
|
<div>
|
||||||
|
<small
|
||||||
|
>Last connection:
|
||||||
|
${new Date(x.last_success * 1000)}</small
|
||||||
|
>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${this.render_message(x)}
|
${this.render_message(x)}
|
||||||
|
|||||||
@@ -177,10 +177,10 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
WHERE messages.content ->> 'channel' = ?4
|
WHERE messages.content ->> 'channel' = ?4
|
||||||
UNION
|
UNION
|
||||||
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
FROM messages_fts(?5)
|
FROM messages_refs
|
||||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
JOIN messages ON messages.id = messages_refs.message
|
||||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||||
JOIN json_tree(messages.content, '$.mentions') AS mention ON mention.value = '#' || ?4
|
WHERE messages_refs.ref = '#' || ?4
|
||||||
)
|
)
|
||||||
SELECT TRUE AS is_primary, all_news.* FROM all_news
|
SELECT TRUE AS is_primary, all_news.* FROM all_news
|
||||||
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
||||||
@@ -191,7 +191,6 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
start_time,
|
start_time,
|
||||||
end_time,
|
end_time,
|
||||||
this.hash.substring(2),
|
this.hash.substring(2),
|
||||||
'"#' + this.hash.substring(2).replace('"', '""') + '"',
|
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
let t1 = new Date();
|
let t1 = new Date();
|
||||||
@@ -209,11 +208,29 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
WHERE
|
WHERE
|
||||||
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
|
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
|
||||||
json(messages.content) LIKE '"%'
|
json(messages.content) LIKE '"%'
|
||||||
ORDER BY messages.sequence DESC LIMIT 20
|
ORDER BY messages.rowid DESC LIMIT 20
|
||||||
`,
|
`,
|
||||||
[JSON.stringify(this.private_messages), start_time, end_time]
|
[JSON.stringify(this.private_messages), start_time, end_time]
|
||||||
);
|
);
|
||||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||||
|
} else if (this.hash == '#👍') {
|
||||||
|
result = await tfrpc.rpc.query(
|
||||||
|
`
|
||||||
|
WITH votes AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
|
FROM messages
|
||||||
|
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||||
|
WHERE
|
||||||
|
messages.content ->> 'type' = 'vote' AND
|
||||||
|
(?2 IS NULL OR messages.timestamp >= ?2) AND messages.timestamp < ?3
|
||||||
|
ORDER BY timestamp DESC limit 20)
|
||||||
|
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||||
|
FROM votes
|
||||||
|
JOIN messages ON messages.id = votes.content ->> '$.vote.link'
|
||||||
|
UNION
|
||||||
|
SELECT TRUE AS is_primary, * FROM votes
|
||||||
|
`,
|
||||||
|
[JSON.stringify(this.following), start_time, end_time]
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
let t0 = new Date();
|
let t0 = new Date();
|
||||||
let initial_messages = await tfrpc.rpc.query(
|
let initial_messages = await tfrpc.rpc.query(
|
||||||
@@ -258,6 +275,13 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unread_allowed() {
|
||||||
|
return (
|
||||||
|
this.hash == '#@' ||
|
||||||
|
(!this.hash.startsWith('#%') && !this.hash.startsWith('#@'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async load_more() {
|
async load_more() {
|
||||||
this.loading++;
|
this.loading++;
|
||||||
this.loading_canceled = false;
|
this.loading_canceled = false;
|
||||||
@@ -407,9 +431,16 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
if (!this.hash.startsWith('#%')) {
|
if (!this.hash.startsWith('#%')) {
|
||||||
more = html`
|
more = html`
|
||||||
<p>
|
<p>
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
${this.unread_allowed()
|
||||||
Mark All Read
|
? html`
|
||||||
</button>
|
<button
|
||||||
|
class="w3-button w3-theme-d1"
|
||||||
|
@click=${this.mark_all_read}
|
||||||
|
>
|
||||||
|
Mark All Read
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
<button
|
<button
|
||||||
?disabled=${this.loading}
|
?disabled=${this.loading}
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-theme-d1"
|
||||||
@@ -441,9 +472,14 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
return cache(html`
|
return cache(html`
|
||||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
${this.unread_allowed()
|
||||||
Mark All Read
|
? html`<button
|
||||||
</button>
|
class="w3-button w3-theme-d1"
|
||||||
|
@click=${this.mark_all_read}
|
||||||
|
>
|
||||||
|
Mark All Read
|
||||||
|
</button>`
|
||||||
|
: undefined}
|
||||||
<tf-news
|
<tf-news
|
||||||
id="news"
|
id="news"
|
||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
@@ -452,6 +488,7 @@ class TfTabNewsFeedElement extends LitElement {
|
|||||||
.following=${this.following}
|
.following=${this.following}
|
||||||
.drafts=${this.drafts}
|
.drafts=${this.drafts}
|
||||||
.expanded=${this.expanded}
|
.expanded=${this.expanded}
|
||||||
|
hash=${this.hash}
|
||||||
channel=${this.channel()}
|
channel=${this.channel()}
|
||||||
channel_unread=${this.channels_unread?.[this.channel()]}
|
channel_unread=${this.channels_unread?.[this.channel()]}
|
||||||
.recent_reactions=${this.recent_reactions}
|
.recent_reactions=${this.recent_reactions}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
connections: {type: Array},
|
connections: {type: Array},
|
||||||
private_messages: {type: Array},
|
private_messages: {type: Array},
|
||||||
recent_reactions: {type: Array},
|
recent_reactions: {type: Array},
|
||||||
|
peer_exchange: {type: Boolean},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ class TfTabNewsElement extends LitElement {
|
|||||||
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
|
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
|
||||||
self.drafts = JSON.parse(d || '{}');
|
self.drafts = JSON.parse(d || '{}');
|
||||||
});
|
});
|
||||||
|
this.check_peer_exchange();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@@ -60,6 +62,14 @@ class TfTabNewsElement extends LitElement {
|
|||||||
document.body.removeEventListener('keypress', this.on_keypress.bind(this));
|
document.body.removeEventListener('keypress', this.on_keypress.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async check_peer_exchange() {
|
||||||
|
if (await tfrpc.rpc.isAdministrator()) {
|
||||||
|
this.peer_exchange = await tfrpc.rpc.globalSettingsGet('peer_exchange');
|
||||||
|
} else {
|
||||||
|
this.peer_exchange = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
load_latest() {
|
load_latest() {
|
||||||
let news = this.shadowRoot?.getElementById('news');
|
let news = this.shadowRoot?.getElementById('news');
|
||||||
if (news) {
|
if (news) {
|
||||||
@@ -164,6 +174,15 @@ class TfTabNewsElement extends LitElement {
|
|||||||
.map((x) => x[0]);
|
.map((x) => x[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
tfrpc.rpc.sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
async enable_peer_exchange() {
|
||||||
|
await tfrpc.rpc.globalSettingsSet('peer_exchange', true);
|
||||||
|
await this.check_peer_exchange();
|
||||||
|
}
|
||||||
|
|
||||||
render_sidebar() {
|
render_sidebar() {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
@@ -202,6 +221,12 @@ class TfTabNewsElement extends LitElement {
|
|||||||
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
|
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
|
||||||
>${this.unread_status('@')}@mentions</a
|
>${this.unread_status('@')}@mentions</a
|
||||||
>
|
>
|
||||||
|
<a
|
||||||
|
href="#👍"
|
||||||
|
class="w3-bar-item w3-button"
|
||||||
|
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||||
|
>${this.unread_status('👍')}👍votes</a
|
||||||
|
>
|
||||||
<a
|
<a
|
||||||
href="#🔐"
|
href="#🔐"
|
||||||
class="w3-bar-item w3-button"
|
class="w3-bar-item w3-button"
|
||||||
@@ -234,13 +259,39 @@ class TfTabNewsElement extends LitElement {
|
|||||||
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
|
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
|
||||||
<h4 style="margin: 0">Connections</h4>
|
<h4 style="margin: 0">Connections</h4>
|
||||||
</a>
|
</a>
|
||||||
|
${this.connections?.filter((x) => x.id)?.length == 0
|
||||||
|
? html`
|
||||||
|
<button
|
||||||
|
class=${'w3-bar-item w3-button' +
|
||||||
|
(this.connections?.some((x) => x.flags.one_shot)
|
||||||
|
? ' w3-spin'
|
||||||
|
: '')}
|
||||||
|
@click=${this.refresh}
|
||||||
|
>
|
||||||
|
↻ Sync now
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class=${'w3-bar-item w3-button' +
|
||||||
|
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
||||||
|
@click=${this.enable_peer_exchange}
|
||||||
|
>
|
||||||
|
Enable peer exchange
|
||||||
|
</button>
|
||||||
|
`
|
||||||
|
: undefined}
|
||||||
${this.connections
|
${this.connections
|
||||||
.filter((x) => x.id && !x.destroy_reason)
|
.filter((x) => x.id)
|
||||||
.map(
|
.map(
|
||||||
(x) => html`
|
(x) => html`
|
||||||
<tf-user
|
<tf-user
|
||||||
class="w3-bar-item"
|
class="w3-bar-item"
|
||||||
style="max-width: 100%"
|
style=${x.destroy_reason
|
||||||
|
? 'border-left: 4px solid red; border-right: 4px solid red'
|
||||||
|
: x.connected
|
||||||
|
? x.flags?.one_shot
|
||||||
|
? 'border-left: 4px solid blue; border-right: 4px solid blue'
|
||||||
|
: 'border-left: 4px solid green; border-right: 4px solid green'
|
||||||
|
: ''}
|
||||||
id=${x.id}
|
id=${x.id}
|
||||||
fallback_name=${x.host}
|
fallback_name=${x.host}
|
||||||
.users=${this.users}
|
.users=${this.users}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ class TfUserElement extends LitElement {
|
|||||||
return {
|
return {
|
||||||
id: {type: String},
|
id: {type: String},
|
||||||
fallback_name: {type: String},
|
fallback_name: {type: String},
|
||||||
|
icon_only: {type: Boolean},
|
||||||
users: {type: Object},
|
users: {type: Object},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -17,6 +18,7 @@ class TfUserElement extends LitElement {
|
|||||||
super();
|
super();
|
||||||
this.id = null;
|
this.id = null;
|
||||||
this.fallback_name = null;
|
this.fallback_name = null;
|
||||||
|
this.icon_only = false;
|
||||||
this.users = {};
|
this.users = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,9 +34,11 @@ class TfUserElement extends LitElement {
|
|||||||
>😎</span
|
>😎</span
|
||||||
>`;
|
>`;
|
||||||
let name = this.users?.[this.id]?.name;
|
let name = this.users?.[this.id]?.name;
|
||||||
name = html`<a target="_top" href=${'#' + this.id}
|
name = this.icon_only
|
||||||
>${name ?? this.fallback_name ?? this.id}</a
|
? undefined
|
||||||
>`;
|
: html`<a target="_top" href=${'#' + this.id}
|
||||||
|
>${name ?? this.fallback_name ?? this.id}</a
|
||||||
|
>`;
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
let image_link = user.image;
|
let image_link = user.image;
|
||||||
@@ -48,6 +52,7 @@ class TfUserElement extends LitElement {
|
|||||||
class=${'w3-theme-l4 ' + shape}
|
class=${'w3-theme-l4 ' + shape}
|
||||||
style="width: 2em; height: 2em; vertical-align: middle; object-fit: cover"
|
style="width: 2em; height: 2em; vertical-align: middle; object-fit: cover"
|
||||||
src="/${image_link}/view"
|
src="/${image_link}/view"
|
||||||
|
alt=${name ?? this.fallback_name ?? this.id}
|
||||||
/>`;
|
/>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "👋",
|
"emoji": "👋",
|
||||||
"previous": "&1o8MrBHfH42NnO+ruajwCmW/DUCb+IT1qtnAZI/agyo=.sha256"
|
"previous": "&3puDxDNnf6C+YXpFysYLgxFMAy54/AO9V7Xpja6qO/k=.sha256"
|
||||||
}
|
}
|
||||||
|
|||||||
1521
apps/welcome/hermietildefriends.svg
Normal file
|
After Width: | Height: | Size: 86 KiB |
@@ -28,23 +28,12 @@
|
|||||||
<b>😎 Tilde Friends</b>
|
<b>😎 Tilde Friends</b>
|
||||||
</h1>
|
</h1>
|
||||||
<h1 class="w3-xxlarge w3-text-green">
|
<h1 class="w3-xxlarge w3-text-green">
|
||||||
<b
|
<b>a Secure Scuttlebutt decentralized social network client</b>
|
||||||
>the Secure Scuttlebutt decentralized social network client that's
|
|
||||||
<i>fancy🎩</i></b
|
|
||||||
>
|
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
In addition to participating in Secure Scuttlebutt, Tilde Friends is
|
In addition to participating in Secure Scuttlebutt, Tilde Friends is
|
||||||
a platform for building, running, and sharing applications.
|
a platform for building, running, and sharing applications.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
Available for lots of devices:
|
|
||||||
<i class="fa-brands fa-linux w3-xlarge"></i>
|
|
||||||
<i class="fa-brands fa-android w3-xlarge"></i>
|
|
||||||
<i class="fa-brands fa-apple w3-xlarge"></i>
|
|
||||||
<i class="fa fa-mobile-screen w3-xlarge"></i>
|
|
||||||
<i class="fa-brands fa-windows w3-xlarge"></i>
|
|
||||||
</p>
|
|
||||||
<a
|
<a
|
||||||
class="w3-button w3-blue w3-padding-large"
|
class="w3-button w3-blue w3-padding-large"
|
||||||
href="https://www.tildefriends.net/~core/ssb/"
|
href="https://www.tildefriends.net/~core/ssb/"
|
||||||
@@ -52,7 +41,7 @@
|
|||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="w3-button w3-black w3-padding-large"
|
class="w3-button w3-black w3-padding-large"
|
||||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||||
><i class="fa fa-download"></i> Download</a
|
><i class="fa fa-download"></i> Download</a
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
@@ -70,35 +59,6 @@
|
|||||||
href="https://www.tildefriends.net/~cory/tildeblog/"
|
href="https://www.tildefriends.net/~cory/tildeblog/"
|
||||||
><i class="fa fa-solid fa-square-rss"></i> Blog</a
|
><i class="fa fa-solid fa-square-rss"></i> Blog</a
|
||||||
>
|
>
|
||||||
<p>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
|
||||||
><img src="f-droid.svg" style="height: 2em; margin: 0" /> Get it
|
|
||||||
on F-Droid</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
|
||||||
>
|
|
||||||
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
|
||||||
Get Linux 64-bit AppImage
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
|
||||||
>
|
|
||||||
<img src="googleplay.svg" style="height: 2em; margin: 0" />
|
|
||||||
Get it on Google Play (Open Testing)
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
|
||||||
href="https://testflight.apple.com/join/tXxgtSpE"
|
|
||||||
>
|
|
||||||
<img src="ios.svg" style="height: 2em; margin: 0" />
|
|
||||||
Get it on iOS (TestFlight)
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="w3-col l4 m6">
|
<div class="w3-col l4 m6">
|
||||||
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
||||||
@@ -116,15 +76,119 @@
|
|||||||
<h2>First-time user checklist:</h2>
|
<h2>First-time user checklist:</h2>
|
||||||
<ol type="1" style="text-align: left">
|
<ol type="1" style="text-align: left">
|
||||||
<li>
|
<li>
|
||||||
<a href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
<a
|
||||||
|
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||||
>Download</a
|
>Download</a
|
||||||
>
|
>
|
||||||
Tilde Friends or use
|
Tilde Friends or use
|
||||||
<a href="https://www.tildefriends.net/"
|
<a href="https://www.tildefriends.net/"
|
||||||
>https://www.tildefriends.net/</a
|
>https://www.tildefriends.net/</a
|
||||||
>.
|
>.
|
||||||
|
<div class="w3-cell-row">
|
||||||
|
<div class="w3-container w3-cell w3-mobile">
|
||||||
|
<h3>Mobile</h3>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
|
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||||
|
><img src="f-droid.svg" style="height: 2em; margin: 0" />
|
||||||
|
Get it on F-Droid</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
|
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="googleplay.svg"
|
||||||
|
style="height: 2em; margin: 0"
|
||||||
|
/>
|
||||||
|
Get it on Google Play (Open Testing)
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||||
|
href="https://testflight.apple.com/join/tXxgtSpE"
|
||||||
|
>
|
||||||
|
<img src="ios.svg" style="height: 2em; margin: 0" />
|
||||||
|
Get it on iOS (TestFlight)
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>Just launch the app.</p>
|
||||||
|
</div>
|
||||||
|
<div class="w3-container w3-cell w3-mobile">
|
||||||
|
<h3>Web</h3>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-blue w3-padding-large"
|
||||||
|
href="https://www.tildefriends.net/~core/ssb/"
|
||||||
|
>🦀 Try It</a
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="/login?return=/~core/intro"
|
||||||
|
>Register an account with tildefriends.net</a
|
||||||
|
>
|
||||||
|
to take it for a spin right away.
|
||||||
|
</p>
|
||||||
|
<h3>PeachCloud</h3>
|
||||||
|
<p>
|
||||||
|
Tilde Friends is also a part of 🍑☁️<a
|
||||||
|
href="https://peach-docs.commoninternet.net/"
|
||||||
|
>PeachCloud</a
|
||||||
|
>, which is available on
|
||||||
|
<a href="https://apps.yunohost.org/app/peachpub"
|
||||||
|
>YunoHost</a
|
||||||
|
>
|
||||||
|
for accessible self-hosting.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="w3-container w3-cell w3-mobile">
|
||||||
|
<h3>Desktop</h3>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-black w3-padding-large"
|
||||||
|
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||||
|
><i class="fa fa-download"></i> Download</a
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
class="w3-button w3-round-large w3-padding w3-blue-gray"
|
||||||
|
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
||||||
|
>
|
||||||
|
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
||||||
|
Get Linux 64-bit AppImage
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Tilde Friends is distributed as a single executable file (or
|
||||||
|
source that you can
|
||||||
|
<a href="http://dev.tildefriends.net">build yourself</a>)
|
||||||
|
and stores all of its data in a single
|
||||||
|
file(<code>db.sqlite</code>). You can generally download the
|
||||||
|
latest executable from
|
||||||
|
<a
|
||||||
|
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||||
|
>releases</a
|
||||||
|
>
|
||||||
|
for your platform, mark it as executable (<code
|
||||||
|
>chmod +x tildefriends*</code
|
||||||
|
>
|
||||||
|
on macOS and Linux), and run. Run with <code>-h</code> to
|
||||||
|
learn more.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Tilde Friends will run in the console and provide a web
|
||||||
|
interface at
|
||||||
|
<a href="http://localhost:12345/">http://localhost:12345/</a
|
||||||
|
>. You will have to register a username and password to sign
|
||||||
|
into your instance.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
After a <a href="/~core/intro">brief introduction</a>, Tilde
|
||||||
|
Friends will take you to the Secure Scuttlebutt social network
|
||||||
|
app.
|
||||||
|
</p>
|
||||||
</li>
|
</li>
|
||||||
<li>Create an account to identify yourself with that instance.</li>
|
|
||||||
<li>
|
<li>
|
||||||
Describe yourself in your profile in the <b>ssb</b> app. Give
|
Describe yourself in your profile in the <b>ssb</b> app. Give
|
||||||
yourself a name and an avatar if you like.
|
yourself a name and an avatar if you like.
|
||||||
@@ -158,11 +222,11 @@
|
|||||||
<!-- SSB Section -->
|
<!-- SSB Section -->
|
||||||
<div class="w3-light-grey">
|
<div class="w3-light-grey">
|
||||||
<div class="w3-row-padding w3-padding-64">
|
<div class="w3-row-padding w3-padding-64">
|
||||||
<div class="w3-col l4 m6 s4">
|
<div class="w3-col l4 m6 s4 w3-center">
|
||||||
<a href="https://scuttlebutt.nz/"
|
<a href="https://scuttlebutt.nz/"
|
||||||
><img
|
><img
|
||||||
class="w3-image w3-round-large"
|
class="w3-image"
|
||||||
src="ssb.png"
|
src="hermietildefriends.svg"
|
||||||
alt="Secure Scuttlebutt"
|
alt="Secure Scuttlebutt"
|
||||||
/></a>
|
/></a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 50 KiB |
@@ -8,6 +8,7 @@ let gFiles = {};
|
|||||||
let gApp = {files: {}, emoji: '📦'};
|
let gApp = {files: {}, emoji: '📦'};
|
||||||
let gEditor;
|
let gEditor;
|
||||||
let gOriginalInput;
|
let gOriginalInput;
|
||||||
|
let gUnloading;
|
||||||
|
|
||||||
let kErrorColor = '#dc322f';
|
let kErrorColor = '#dc322f';
|
||||||
let kDisconnectColor = '#f00';
|
let kDisconnectColor = '#f00';
|
||||||
@@ -1560,27 +1561,31 @@ function connectSocket(path) {
|
|||||||
_receive_websocket_message(JSON.parse(event.data));
|
_receive_websocket_message(JSON.parse(event.data));
|
||||||
};
|
};
|
||||||
gSocket.onclose = function (event) {
|
gSocket.onclose = function (event) {
|
||||||
const k_codes = {
|
if (gUnloading) {
|
||||||
1000: 'Normal closure',
|
setStatusMessage('⚪ Closing...', kStatusColor);
|
||||||
1001: 'Going away',
|
} else {
|
||||||
1002: 'Protocol error',
|
const k_codes = {
|
||||||
1003: 'Unsupported data',
|
1000: 'Normal closure',
|
||||||
1005: 'No status received',
|
1001: 'Going away',
|
||||||
1006: 'Abnormal closure',
|
1002: 'Protocol error',
|
||||||
1007: 'Invalid frame payload data',
|
1003: 'Unsupported data',
|
||||||
1008: 'Policy violation',
|
1005: 'No status received',
|
||||||
1009: 'Message too big',
|
1006: 'Abnormal closure',
|
||||||
1010: 'Missing extension',
|
1007: 'Invalid frame payload data',
|
||||||
1011: 'Internal error',
|
1008: 'Policy violation',
|
||||||
1012: 'Service restart',
|
1009: 'Message too big',
|
||||||
1013: 'Try again later',
|
1010: 'Missing extension',
|
||||||
1014: 'Bad gateway',
|
1011: 'Internal error',
|
||||||
1015: 'TLS handshake',
|
1012: 'Service restart',
|
||||||
};
|
1013: 'Try again later',
|
||||||
setStatusMessage(
|
1014: 'Bad gateway',
|
||||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
1015: 'TLS handshake',
|
||||||
kDisconnectColor
|
};
|
||||||
);
|
setStatusMessage(
|
||||||
|
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||||
|
kDisconnectColor
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1854,6 +1859,9 @@ window.addEventListener('load', function () {
|
|||||||
window.addEventListener('blur', blur);
|
window.addEventListener('blur', blur);
|
||||||
window.addEventListener('message', message, false);
|
window.addEventListener('message', message, false);
|
||||||
window.addEventListener('online', connectSocket);
|
window.addEventListener('online', connectSocket);
|
||||||
|
window.addEventListener('beforeunload', function () {
|
||||||
|
gUnloading = true;
|
||||||
|
});
|
||||||
document.getElementById('name').value = window.location.pathname;
|
document.getElementById('name').value = window.location.pathname;
|
||||||
document
|
document
|
||||||
.getElementById('closeEditor')
|
.getElementById('closeEditor')
|
||||||
|
|||||||
@@ -25,14 +25,14 @@
|
|||||||
}:
|
}:
|
||||||
pkgs.stdenv.mkDerivation rec {
|
pkgs.stdenv.mkDerivation rec {
|
||||||
pname = "tildefriends";
|
pname = "tildefriends";
|
||||||
version = "0.0.30";
|
version = "0.0.32";
|
||||||
|
|
||||||
src = pkgs.fetchFromGitea {
|
src = pkgs.fetchFromGitea {
|
||||||
domain = "dev.tildefriends.net";
|
domain = "dev.tildefriends.net";
|
||||||
owner = "cory";
|
owner = "cory";
|
||||||
repo = "tildefriends";
|
repo = "tildefriends";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-t5yvouzSL2j/ge1VHLqzIZ+Avqj4iEDt7L+yrHoTZAQ=";
|
hash = "sha256-Dk0NOEQIg2LeENySK0+MgpZEtfsClGq6dZL+eOOpE0U=";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2
deps/codemirror/cm6.js
vendored
223
deps/codemirror_src/package-lock.json
generated
vendored
@@ -83,18 +83,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/lang-json": {
|
"node_modules/@codemirror/lang-json": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
|
||||||
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@lezer/json": "^1.0.0"
|
"@lezer/json": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/language": {
|
"node_modules/@codemirror/language": {
|
||||||
"version": "6.11.0",
|
"version": "6.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.1.tgz",
|
||||||
"integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==",
|
"integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
"@codemirror/view": "^6.23.0",
|
"@codemirror/view": "^6.23.0",
|
||||||
@@ -133,9 +133,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/theme-one-dark": {
|
"node_modules/@codemirror/theme-one-dark": {
|
||||||
"version": "6.1.2",
|
"version": "6.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
|
||||||
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
|
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/language": "^6.0.0",
|
"@codemirror/language": "^6.0.0",
|
||||||
"@codemirror/state": "^6.0.0",
|
"@codemirror/state": "^6.0.0",
|
||||||
@@ -144,11 +144,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codemirror/view": {
|
"node_modules/@codemirror/view": {
|
||||||
"version": "6.36.8",
|
"version": "6.37.2",
|
||||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.8.tgz",
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.2.tgz",
|
||||||
"integrity": "sha512-yoRo4f+FdnD01fFt4XpfpMCcCAo9QvZOtbrXExn4SqzH32YC6LgzqxfLZw/r6Ge65xyY03mK/UfUqrVw1gFiFg==",
|
"integrity": "sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/state": "^6.5.0",
|
"@codemirror/state": "^6.5.0",
|
||||||
|
"crelt": "^1.0.6",
|
||||||
"style-mod": "^4.1.0",
|
"style-mod": "^4.1.0",
|
||||||
"w3c-keyname": "^2.2.4"
|
"w3c-keyname": "^2.2.4"
|
||||||
}
|
}
|
||||||
@@ -323,9 +324,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/pluginutils": {
|
"node_modules/@rollup/pluginutils": {
|
||||||
"version": "5.1.4",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
||||||
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
|
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "^1.0.0",
|
"@types/estree": "^1.0.0",
|
||||||
"estree-walker": "^2.0.2",
|
"estree-walker": "^2.0.2",
|
||||||
@@ -344,9 +345,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz",
|
||||||
"integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==",
|
"integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -356,9 +357,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-android-arm64": {
|
"node_modules/@rollup/rollup-android-arm64": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz",
|
||||||
"integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==",
|
"integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -368,9 +369,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz",
|
||||||
"integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==",
|
"integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -380,9 +381,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-darwin-x64": {
|
"node_modules/@rollup/rollup-darwin-x64": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz",
|
||||||
"integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==",
|
"integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -392,9 +393,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz",
|
||||||
"integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==",
|
"integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -404,9 +405,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz",
|
||||||
"integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==",
|
"integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -416,9 +417,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz",
|
||||||
"integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==",
|
"integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -428,9 +429,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz",
|
||||||
"integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==",
|
"integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -440,9 +441,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz",
|
||||||
"integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==",
|
"integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -452,9 +453,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz",
|
||||||
"integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==",
|
"integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -464,9 +465,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz",
|
||||||
"integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==",
|
"integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"loong64"
|
"loong64"
|
||||||
],
|
],
|
||||||
@@ -476,9 +477,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz",
|
||||||
"integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==",
|
"integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ppc64"
|
"ppc64"
|
||||||
],
|
],
|
||||||
@@ -488,9 +489,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz",
|
||||||
"integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==",
|
"integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -500,9 +501,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz",
|
||||||
"integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==",
|
"integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"riscv64"
|
"riscv64"
|
||||||
],
|
],
|
||||||
@@ -512,9 +513,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz",
|
||||||
"integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==",
|
"integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"s390x"
|
"s390x"
|
||||||
],
|
],
|
||||||
@@ -524,9 +525,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz",
|
||||||
"integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==",
|
"integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -536,9 +537,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz",
|
||||||
"integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==",
|
"integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -548,9 +549,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz",
|
||||||
"integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==",
|
"integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -560,9 +561,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz",
|
||||||
"integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==",
|
"integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -572,9 +573,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz",
|
||||||
"integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==",
|
"integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -584,9 +585,9 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/@types/estree": {
|
"node_modules/@types/estree": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||||
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
|
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/resolve": {
|
"node_modules/@types/resolve": {
|
||||||
"version": "1.20.2",
|
"version": "1.20.2",
|
||||||
@@ -594,9 +595,9 @@
|
|||||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||||
},
|
},
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.14.1",
|
"version": "8.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"acorn": "bin/acorn"
|
"acorn": "bin/acorn"
|
||||||
@@ -612,9 +613,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/codemirror": {
|
"node_modules/codemirror": {
|
||||||
"version": "6.0.1",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.0.0",
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
"@codemirror/commands": "^6.0.0",
|
"@codemirror/commands": "^6.0.0",
|
||||||
@@ -745,11 +746,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "4.41.1",
|
"version": "4.44.0",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz",
|
||||||
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
|
"integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/estree": "1.0.7"
|
"@types/estree": "1.0.8"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
@@ -759,26 +760,26 @@
|
|||||||
"npm": ">=8.0.0"
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-android-arm-eabi": "4.41.1",
|
"@rollup/rollup-android-arm-eabi": "4.44.0",
|
||||||
"@rollup/rollup-android-arm64": "4.41.1",
|
"@rollup/rollup-android-arm64": "4.44.0",
|
||||||
"@rollup/rollup-darwin-arm64": "4.41.1",
|
"@rollup/rollup-darwin-arm64": "4.44.0",
|
||||||
"@rollup/rollup-darwin-x64": "4.41.1",
|
"@rollup/rollup-darwin-x64": "4.44.0",
|
||||||
"@rollup/rollup-freebsd-arm64": "4.41.1",
|
"@rollup/rollup-freebsd-arm64": "4.44.0",
|
||||||
"@rollup/rollup-freebsd-x64": "4.41.1",
|
"@rollup/rollup-freebsd-x64": "4.44.0",
|
||||||
"@rollup/rollup-linux-arm-gnueabihf": "4.41.1",
|
"@rollup/rollup-linux-arm-gnueabihf": "4.44.0",
|
||||||
"@rollup/rollup-linux-arm-musleabihf": "4.41.1",
|
"@rollup/rollup-linux-arm-musleabihf": "4.44.0",
|
||||||
"@rollup/rollup-linux-arm64-gnu": "4.41.1",
|
"@rollup/rollup-linux-arm64-gnu": "4.44.0",
|
||||||
"@rollup/rollup-linux-arm64-musl": "4.41.1",
|
"@rollup/rollup-linux-arm64-musl": "4.44.0",
|
||||||
"@rollup/rollup-linux-loongarch64-gnu": "4.41.1",
|
"@rollup/rollup-linux-loongarch64-gnu": "4.44.0",
|
||||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.1",
|
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.0",
|
||||||
"@rollup/rollup-linux-riscv64-gnu": "4.41.1",
|
"@rollup/rollup-linux-riscv64-gnu": "4.44.0",
|
||||||
"@rollup/rollup-linux-riscv64-musl": "4.41.1",
|
"@rollup/rollup-linux-riscv64-musl": "4.44.0",
|
||||||
"@rollup/rollup-linux-s390x-gnu": "4.41.1",
|
"@rollup/rollup-linux-s390x-gnu": "4.44.0",
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.41.1",
|
"@rollup/rollup-linux-x64-gnu": "4.44.0",
|
||||||
"@rollup/rollup-linux-x64-musl": "4.41.1",
|
"@rollup/rollup-linux-x64-musl": "4.44.0",
|
||||||
"@rollup/rollup-win32-arm64-msvc": "4.41.1",
|
"@rollup/rollup-win32-arm64-msvc": "4.44.0",
|
||||||
"@rollup/rollup-win32-ia32-msvc": "4.41.1",
|
"@rollup/rollup-win32-ia32-msvc": "4.44.0",
|
||||||
"@rollup/rollup-win32-x64-msvc": "4.41.1",
|
"@rollup/rollup-win32-x64-msvc": "4.44.0",
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -853,9 +854,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.40.0",
|
"version": "5.43.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.40.0.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
||||||
"integrity": "sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==",
|
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
|||||||
953
deps/sqlite/shell.c
vendored
4284
deps/sqlite/sqlite3.c
vendored
146
deps/sqlite/sqlite3.h
vendored
@@ -133,7 +133,7 @@ extern "C" {
|
|||||||
**
|
**
|
||||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||||
** SQLite source code has been stored in the
|
** SQLite source code has been stored in the
|
||||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
** <a href="http://fossil-scm.org/">Fossil configuration management
|
||||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||||
** a string which identifies a particular check-in of SQLite
|
** a string which identifies a particular check-in of SQLite
|
||||||
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
||||||
@@ -146,9 +146,9 @@ extern "C" {
|
|||||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||||
** [sqlite_version()] and [sqlite_source_id()].
|
** [sqlite_version()] and [sqlite_source_id()].
|
||||||
*/
|
*/
|
||||||
#define SQLITE_VERSION "3.49.2"
|
#define SQLITE_VERSION "3.50.1"
|
||||||
#define SQLITE_VERSION_NUMBER 3049002
|
#define SQLITE_VERSION_NUMBER 3050001
|
||||||
#define SQLITE_SOURCE_ID "2025-05-07 10:39:52 17144570b0d96ae63cd6f3edca39e27ebd74925252bbaf6723bcb2f6b4861fb1"
|
#define SQLITE_SOURCE_ID "2025-06-06 14:52:32 b77dc5e0f596d2140d9ac682b2893ff65d3a4140aa86067a3efebe29dc914c95"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Run-Time Library Version Numbers
|
** CAPI3REF: Run-Time Library Version Numbers
|
||||||
@@ -1163,6 +1163,12 @@ struct sqlite3_io_methods {
|
|||||||
** the value that M is to be set to. Before returning, the 32-bit signed
|
** the value that M is to be set to. Before returning, the 32-bit signed
|
||||||
** integer is overwritten with the previous value of M.
|
** integer is overwritten with the previous value of M.
|
||||||
**
|
**
|
||||||
|
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
|
||||||
|
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
|
||||||
|
** VFS to block when taking a SHARED lock to connect to a wal mode database.
|
||||||
|
** This is used to implement the functionality associated with
|
||||||
|
** SQLITE_SETLK_BLOCK_ON_CONNECT.
|
||||||
|
**
|
||||||
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
||||||
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
||||||
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
||||||
@@ -1259,6 +1265,7 @@ struct sqlite3_io_methods {
|
|||||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||||
#define SQLITE_FCNTL_RESET_CACHE 42
|
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||||
#define SQLITE_FCNTL_NULL_IO 43
|
#define SQLITE_FCNTL_NULL_IO 43
|
||||||
|
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
||||||
|
|
||||||
/* deprecated names */
|
/* deprecated names */
|
||||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||||
@@ -1989,13 +1996,16 @@ struct sqlite3_mem_methods {
|
|||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
||||||
** the default size of lookaside memory on each [database connection].
|
** the default size of [lookaside memory] on each [database connection].
|
||||||
** The first argument is the
|
** The first argument is the
|
||||||
** size of each lookaside buffer slot and the second is the number of
|
** size of each lookaside buffer slot ("sz") and the second is the number of
|
||||||
** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
|
** slots allocated to each database connection ("cnt").)^
|
||||||
** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
|
** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
|
||||||
** option to [sqlite3_db_config()] can be used to change the lookaside
|
** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
|
||||||
** configuration on individual connections.)^ </dd>
|
** be used to change the lookaside configuration on individual connections.)^
|
||||||
|
** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
|
||||||
|
** default lookaside configuration at compile-time.
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
||||||
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
||||||
@@ -2232,31 +2242,50 @@ struct sqlite3_mem_methods {
|
|||||||
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
||||||
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
||||||
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
||||||
** configuration of the lookaside memory allocator within a database
|
** configuration of the [lookaside memory allocator] within a database
|
||||||
** connection.
|
** connection.
|
||||||
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
||||||
** in the [DBCONFIG arguments|usual format].
|
** in the [DBCONFIG arguments|usual format].
|
||||||
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
||||||
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
||||||
** should have a total of five parameters.
|
** should have a total of five parameters.
|
||||||
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
|
** <ol>
|
||||||
|
** <li><p>The first argument ("buf") is a
|
||||||
** pointer to a memory buffer to use for lookaside memory.
|
** pointer to a memory buffer to use for lookaside memory.
|
||||||
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
|
** The first argument may be NULL in which case SQLite will allocate the
|
||||||
** may be NULL in which case SQLite will allocate the
|
** lookaside buffer itself using [sqlite3_malloc()].
|
||||||
** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
|
** <li><P>The second argument ("sz") is the
|
||||||
** size of each lookaside buffer slot. ^The third argument is the number of
|
** size of each lookaside buffer slot. Lookaside is disabled if "sz"
|
||||||
** slots. The size of the buffer in the first argument must be greater than
|
** is less than 8. The "sz" argument should be a multiple of 8 less than
|
||||||
** or equal to the product of the second and third arguments. The buffer
|
** 65536. If "sz" does not meet this constraint, it is reduced in size until
|
||||||
** must be aligned to an 8-byte boundary. ^If the second argument to
|
** it does.
|
||||||
** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
|
** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
|
||||||
** rounded down to the next smaller multiple of 8. ^(The lookaside memory
|
** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
|
||||||
|
** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
|
||||||
|
** parameter is usually chosen so that the product of "sz" and "cnt" is less
|
||||||
|
** than 1,000,000.
|
||||||
|
** </ol>
|
||||||
|
** <p>If the "buf" argument is not NULL, then it must
|
||||||
|
** point to a memory buffer with a size that is greater than
|
||||||
|
** or equal to the product of "sz" and "cnt".
|
||||||
|
** The buffer must be aligned to an 8-byte boundary.
|
||||||
|
** The lookaside memory
|
||||||
** configuration for a database connection can only be changed when that
|
** configuration for a database connection can only be changed when that
|
||||||
** connection is not currently using lookaside memory, or in other words
|
** connection is not currently using lookaside memory, or in other words
|
||||||
** when the "current value" returned by
|
** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
|
||||||
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
|
||||||
** Any attempt to change the lookaside memory configuration when lookaside
|
** Any attempt to change the lookaside memory configuration when lookaside
|
||||||
** memory is in use leaves the configuration unchanged and returns
|
** memory is in use leaves the configuration unchanged and returns
|
||||||
** [SQLITE_BUSY].)^</dd>
|
** [SQLITE_BUSY].
|
||||||
|
** If the "buf" argument is NULL and an attempt
|
||||||
|
** to allocate memory based on "sz" and "cnt" fails, then
|
||||||
|
** lookaside is silently disabled.
|
||||||
|
** <p>
|
||||||
|
** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
|
||||||
|
** default lookaside configuration at initialization. The
|
||||||
|
** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
|
||||||
|
** configuration at compile-time. Typical values for lookaside are 1200 for
|
||||||
|
** "sz" and 40 to 100 for "cnt".
|
||||||
|
** </dd>
|
||||||
**
|
**
|
||||||
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
||||||
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
||||||
@@ -2993,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
|
|||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Set the Setlk Timeout
|
||||||
|
** METHOD: sqlite3
|
||||||
|
**
|
||||||
|
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
|
||||||
|
** the VFS supports blocking locks, it sets the timeout in ms used by
|
||||||
|
** eligible locks taken on wal mode databases by the specified database
|
||||||
|
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
|
||||||
|
** not support blocking locks, this function is a no-op.
|
||||||
|
**
|
||||||
|
** Passing 0 to this function disables blocking locks altogether. Passing
|
||||||
|
** -1 to this function requests that the VFS blocks for a long time -
|
||||||
|
** indefinitely if possible. The results of passing any other negative value
|
||||||
|
** are undefined.
|
||||||
|
**
|
||||||
|
** Internally, each SQLite database handle store two timeout values - the
|
||||||
|
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||||
|
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||||
|
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||||
|
** values, this function sets only the setlk-timeout value. Therefore,
|
||||||
|
** to configure separate busy-timeout and setlk-timeout values for a single
|
||||||
|
** database handle, call sqlite3_busy_timeout() followed by this function.
|
||||||
|
**
|
||||||
|
** Whenever the number of connections to a wal mode database falls from
|
||||||
|
** 1 to 0, the last connection takes an exclusive lock on the database,
|
||||||
|
** then checkpoints and deletes the wal file. While it is doing this, any
|
||||||
|
** new connection that tries to read from the database fails with an
|
||||||
|
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
|
||||||
|
** passed to this API, the new connection blocks until the exclusive lock
|
||||||
|
** has been released.
|
||||||
|
*/
|
||||||
|
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** CAPI3REF: Flags for sqlite3_setlk_timeout()
|
||||||
|
*/
|
||||||
|
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Convenience Routines For Running Queries
|
** CAPI3REF: Convenience Routines For Running Queries
|
||||||
** METHOD: sqlite3
|
** METHOD: sqlite3
|
||||||
@@ -5108,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
|||||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||||
** sqlite3_step(). Failure to reset the prepared statement using
|
** sqlite3_step(). Failure to reset the prepared statement using
|
||||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
||||||
** sqlite3_step() began
|
** sqlite3_step() began
|
||||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||||
@@ -7004,6 +7071,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
|
|||||||
**
|
**
|
||||||
** ^The second argument is a pointer to the function to invoke when a
|
** ^The second argument is a pointer to the function to invoke when a
|
||||||
** row is updated, inserted or deleted in a rowid table.
|
** row is updated, inserted or deleted in a rowid table.
|
||||||
|
** ^The update hook is disabled by invoking sqlite3_update_hook()
|
||||||
|
** with a NULL pointer as the second parameter.
|
||||||
** ^The first argument to the callback is a copy of the third argument
|
** ^The first argument to the callback is a copy of the third argument
|
||||||
** to sqlite3_update_hook().
|
** to sqlite3_update_hook().
|
||||||
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
||||||
@@ -11486,9 +11555,10 @@ SQLITE_API void sqlite3session_table_filter(
|
|||||||
** is inserted while a session object is enabled, then later deleted while
|
** is inserted while a session object is enabled, then later deleted while
|
||||||
** the same session object is disabled, no INSERT record will appear in the
|
** the same session object is disabled, no INSERT record will appear in the
|
||||||
** changeset, even though the delete took place while the session was disabled.
|
** changeset, even though the delete took place while the session was disabled.
|
||||||
** Or, if one field of a row is updated while a session is disabled, and
|
** Or, if one field of a row is updated while a session is enabled, and
|
||||||
** another field of the same row is updated while the session is enabled, the
|
** then another field of the same row is updated while the session is disabled,
|
||||||
** resulting changeset will contain an UPDATE change that updates both fields.
|
** the resulting changeset will contain an UPDATE change that updates both
|
||||||
|
** fields.
|
||||||
*/
|
*/
|
||||||
SQLITE_API int sqlite3session_changeset(
|
SQLITE_API int sqlite3session_changeset(
|
||||||
sqlite3_session *pSession, /* Session object */
|
sqlite3_session *pSession, /* Session object */
|
||||||
@@ -11560,8 +11630,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
|
|||||||
** database zFrom the contents of the two compatible tables would be
|
** database zFrom the contents of the two compatible tables would be
|
||||||
** identical.
|
** identical.
|
||||||
**
|
**
|
||||||
** It an error if database zFrom does not exist or does not contain the
|
** Unless the call to this function is a no-op as described above, it is an
|
||||||
** required compatible table.
|
** error if database zFrom does not exist or does not contain the required
|
||||||
|
** compatible table.
|
||||||
**
|
**
|
||||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||||
@@ -11696,7 +11767,7 @@ SQLITE_API int sqlite3changeset_start_v2(
|
|||||||
** The following flags may passed via the 4th parameter to
|
** The following flags may passed via the 4th parameter to
|
||||||
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
||||||
**
|
**
|
||||||
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
|
||||||
** Invert the changeset while iterating through it. This is equivalent to
|
** Invert the changeset while iterating through it. This is equivalent to
|
||||||
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
||||||
** It is an error to specify this flag with a patchset.
|
** It is an error to specify this flag with a patchset.
|
||||||
@@ -12011,19 +12082,6 @@ SQLITE_API int sqlite3changeset_concat(
|
|||||||
void **ppOut /* OUT: Buffer containing output changeset */
|
void **ppOut /* OUT: Buffer containing output changeset */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
|
|
||||||
*/
|
|
||||||
SQLITE_API int sqlite3changeset_upgrade(
|
|
||||||
sqlite3 *db,
|
|
||||||
const char *zDb,
|
|
||||||
int nIn, const void *pIn, /* Input changeset */
|
|
||||||
int *pnOut, void **ppOut /* OUT: Inverse of input */
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** CAPI3REF: Changegroup Handle
|
** CAPI3REF: Changegroup Handle
|
||||||
**
|
**
|
||||||
|
|||||||
4
deps/sqlite/sqlite3ext.h
vendored
@@ -366,6 +366,8 @@ struct sqlite3_api_routines {
|
|||||||
/* Version 3.44.0 and later */
|
/* Version 3.44.0 and later */
|
||||||
void *(*get_clientdata)(sqlite3*,const char*);
|
void *(*get_clientdata)(sqlite3*,const char*);
|
||||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||||
|
/* Version 3.50.0 and later */
|
||||||
|
int (*setlk_timeout)(sqlite3*,int,int);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -699,6 +701,8 @@ typedef int (*sqlite3_loadext_entry)(
|
|||||||
/* Version 3.44.0 and later */
|
/* Version 3.44.0 and later */
|
||||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||||
|
/* Version 3.50.0 and later */
|
||||||
|
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||||
|
|
||||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||||
|
|||||||
40
docs/connecting_manyverse.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Connecting with Manyverse
|
||||||
|
|
||||||
|
Communication with [Manyverse](https://www.manyver.se/) should Just Work (tm).
|
||||||
|
|
||||||
|
This document is intended as a cheat sheet for the instances where it doesn't.
|
||||||
|
If your experience differs, please share so we can make things better.
|
||||||
|
|
||||||
|
## Connecting Manyverse to the tildefriends.net room
|
||||||
|
|
||||||
|
Open the `Connections` tab. This is from the desktop app, but mobile is similar.
|
||||||
|
|
||||||
|

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

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

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

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

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