55 Commits

Author SHA1 Message Date
94b7703ca9 build: Let's build 0.0.32.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 34m51s
2025-06-25 12:58:25 -04:00
a391dd1316 update: prettier
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-25 12:57:29 -04:00
b6ba5211b7 update: CodeMirror.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 37m36s
2025-06-23 21:34:27 -04:00
8e8e130045 docs: Ready 0.0.32 release notes.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 37m36s
2025-06-23 12:52:31 -04:00
1f40bc1a0f core: Fix the one place where we called a JS function and didn't check for jobs to run as a result. Fixes getting stuck in the intro as a non-admin.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 38m21s
2025-06-22 18:53:20 -04:00
5437212222 build: Fix android.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 38m48s
2025-06-18 20:25:49 -04:00
a8ab845cd2 docs: Minor usage cleanup.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 20:13:16 -04:00
8cee6dc98b docs: Fix copy+paste lies.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 19:58:25 -04:00
70c2b73414 welcome: Introducing hermietildefriends.svg.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 19:43:24 -04:00
98013c4422 welcome: Direct to the latest release, obviously.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 19:16:34 -04:00
e9e22b762d docs: Make this format a little prettier-friendly.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 19:08:47 -04:00
620db19936 docs: Auto-generate a usage.md from the command-line interface.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 19:00:13 -04:00
94a79dd62c ssb: How did I not have this index? Makes channel unread queries significantly faster.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 33m43s
2025-06-18 18:26:11 -04:00
b56c3efde0 prettier
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 33m8s
2025-06-18 12:37:41 -04:00
066827f8f1 ssb: Deconstruct and instrument the channels unread query.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 12:36:59 -04:00
c3b65d9cd8 update: CodeMirror.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-18 12:12:57 -04:00
a15b916b06 ssb: Only print broadcast failures once.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m33s
2025-06-16 22:26:23 -04:00
31d0a5c233 intro: Don't meddle with settings as non-admin. #129
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m57s
2025-06-16 12:20:09 -04:00
140179e80a intro: Fix grammar. #128
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-16 12:12:50 -04:00
53cba2d7e4 Move all but the deleting off of the writer when deleting blobs and messages. Trying to eliminate a period of unresponsiveness near launch.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m41s
2025-06-15 08:46:07 -04:00
e54312d3b8 ssb: Only admins are offered the option to enable peer exchange.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m49s
2025-06-14 20:27:19 -04:00
cadc27b7b5 ssb: Filter out invalid RPC flags.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m12s
2025-06-12 12:50:38 -04:00
388b829ec1 ssb: Allow unread status for the mentions tab.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-12 12:25:58 -04:00
67861f0f33 ssb: Add some options to encourage getting connected to the connections sidebar section.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m46s
2025-06-11 20:53:51 -04:00
a1f1eb34d5 ssb: sequence: Sequential 32-bit integer starting at 1.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m2s
2025-06-11 20:12:23 -04:00
2a6789063e ssb: Show/hide "Mark All Read" and the unread line more correctly.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m53s
2025-06-11 19:22:21 -04:00
cbf1273a55 ssb: Squeeze some blood from the following_perf stone.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-11 19:12:59 -04:00
8143a23ced format 2025-06-11 18:52:56 -04:00
3c17810747 welcome: Update the welcome page to be a bit more direct and relevant. #124
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m17s
2025-06-11 18:39:15 -04:00
bea7a2e9ed core: Use JS_NewTypedArray.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m29s
2025-06-11 12:56:27 -04:00
2f0a2ac6b0 core: Prefer JS_AtomToCString() over JS_AtomToString() + JS_ToCString().
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-11 12:46:52 -04:00
18908b6b56 core: Ignore websocket errors when navigating away from the page (ie, Abnormal closure when switching apps). #60
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m21s
2025-06-11 12:10:28 -04:00
b135a210cc core: Avoid trivial snprintfs.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 34m10s
2025-06-10 21:17:55 -04:00
3a2a829940 ssb: Disabling room support only disables the ability to tunnel through ourselves, not receive tunneled connections.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-10 20:36:59 -04:00
e56dd2dd2d core: Remove hitch tracking. Hasn't been an active problem, and the traces are sufficient to diagnose.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m36s
2025-06-10 12:44:56 -04:00
3f41a48bc7 ssb: Use message refs to get channel contents, not full-text search. Much faster for certain channels.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m43s
2025-06-09 12:16:09 -04:00
65ed53281a ssb: Shutdown hygiene. #108
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 36m20s
2025-06-07 18:00:05 -04:00
1121557a2e update: CodeMirror.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-07 17:29:45 -04:00
d4a7b86ee7 ssb: Revise private scanning slightly.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 34m58s
2025-06-07 15:10:37 -04:00
626c18b04e ssb: Fix some correctly identified private messages not showing up in the private message feed. 2025-06-07 15:04:08 -04:00
bfa97ed7c7 log: Disable stdout buffering. I want to see output sooner in journalctl.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 34m18s
2025-06-07 13:24:55 -04:00
deae4d5367 ssb: Obviously close the readers before the writer. 2025-06-07 13:19:46 -04:00
899605c860 ssb: Actually prevent the sqlite WAL file from growing out of control by truncating when it grows to about 64MB of pages.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 33m59s
2025-06-07 11:39:31 -04:00
dc9a279991 ssb: Free sqlite3_exec errors. 2025-06-07 10:36:44 -04:00
2a53892581 update: sqlite 3.50.1.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 34m58s
2025-06-07 08:04:34 -04:00
6bef0eb764 prettier
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m55s
2025-06-04 20:48:04 -04:00
462b40640c ssb: Don't show messages as unread when not in channels that allow unread status.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m29s
2025-06-04 19:56:02 -04:00
72e1b2025c core: Chasing shutdown issues some more. Logging. No more task promises once shutdown starts. #108
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-04 19:47:54 -04:00
fc7c4b1257 docs: Add a quick doc of how connecting Manyverse to tildefriends.net is supposed to work.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 32m3s
2025-06-04 18:45:18 -04:00
6c22c59056 room: Fix multiple issues with the core room app. Good gravy, it's like I don't want people to connect.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-04 18:24:31 -04:00
94c2b1184f prettier
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-04 18:01:39 -04:00
45231d703d ssb: Split recent votes into their own sidebar item as an experiment. #122 2025-06-04 18:00:46 -04:00
7882fcbe8f ssb: The red border is too much and stacks poorly. Let's try the same unread icon from the sidebar.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m51s
2025-06-04 12:24:04 -04:00
3bbc8c4d35 ssb: Consolidate the buttons around the compose UI. #122
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 33m53s
2025-06-02 21:47:35 -04:00
8ae10dc80b ssb: Add some more visibility that you're above the unread line. #122
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2025-06-02 21:24:03 -04:00
56 changed files with 3032 additions and 791 deletions

View File

@ -1051,7 +1051,7 @@ EXAMPLE_RECURSIVE = NO
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
IMAGE_PATH = docs/images/
# 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

View File

@ -18,12 +18,12 @@ MAKEFLAGS += --no-builtin-rules
VERSION_CODE := 38
VERSION_CODE_IOS := 14
VERSION_NUMBER := 0.0.32-wip
VERSION_NUMBER := 0.0.32
VERSION_NAME := This program kills fascists.
IPHONEOS_VERSION_MIN=14.0
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500000.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
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
@ -1483,6 +1483,18 @@ help: ## Display this help message.
.PHONY: help
.DEFAULT_GOAL := help
docs: debug
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
.PHONY: docs

View File

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "💡",
"previous": "&ckE7T/dt9f1xV8jNSgXVcXYkAzMhU9689MRQbhOi9Wo=.sha256"
"previous": "&eN6DNPpQUNhGvxneLuLPgsOXR6qyFZ7u+MAz0b4fa7k=.sha256"
}

View File

@ -5,7 +5,10 @@ async function main() {
}
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/');
}
});

View File

@ -82,7 +82,7 @@
<div class="w3-container w3-large w3-left-align">
<p>
Secure Scuttlebutt is a social network whose technical operation
attempts to mirrors human social interaction.
attempts to mirror human social interaction.
</p>
<ul>
<li>

View File

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🚪",
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
"previous": "&DJwkqNfYWtW9yBtJQMseEXm7l04Enpi+yAxZulLq9Vk=.sha256"
}

View File

@ -2,8 +2,8 @@ async function main() {
print(core.url);
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
let port = await ssb.port();
let id = (await ssb.getServerIdentity()).substring(1);
let room = `net:${host}:${port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
let id = (await ssb.getServerIdentity()).substring(1).split('.')[0];
let room = `net:${host}:${port}~shs:${id}:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
await app.setDocument(`
<body style="color: #fff">
<h1>Server</h1>

View File

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🦀",
"previous": "&klcMVQ9g0ielAoM3WjHRnyWQs4rs8fxmy6PbXIaK2ZU=.sha256"
"previous": "&Rn4Eg5ev5qhrYRnwxPB0DiEwO7VdGMDGp7tL/W7bRZo=.sha256"
}

View File

@ -106,6 +106,15 @@ tfrpc.register(async function sync() {
tfrpc.register(async function 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 () {
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());

View File

@ -128,7 +128,13 @@ class TfElement extends LitElement {
}
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));
index = index != -1 ? index + delta : 0;
tfrpc.rpc.setHash(
@ -302,11 +308,7 @@ class TfElement extends LitElement {
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
}
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
ranges.push([
Math.max(i - k_chunk_size, 0),
Math.min(cache.range[0], i + k_chunk_size),
false,
]);
ranges.push([Math.max(i - k_chunk_size, 0), i, false]);
}
} else {
for (let i = 0; i < latest; i += k_chunk_size) {
@ -322,7 +324,7 @@ class TfElement extends LitElement {
messages.rowid > ?1 AND
messages.rowid <= ?2 AND
json(messages.content) LIKE '"%'
ORDER BY sequence DESC
ORDER BY messages.rowid DESC
`,
[range[0], range[1]]
);
@ -348,49 +350,84 @@ class TfElement extends LitElement {
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) {
let start_time = new Date();
let latest_private = this.get_latest_private(following);
let channels = await tfrpc.rpc.query(
`
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
JOIN json_each(?2) AS following ON messages.author = following.value
WHERE
messages.content ->> 'type' = 'post' AND
messages.content ->> 'root' IS NULL AND
messages.author != ?4
GROUP by channel
UNION
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
JOIN messages_refs ON messages.id = messages_refs.message
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
JOIN json_each(?2) AS following ON messages.author = following.value
WHERE
messages.content ->> 'type' = 'post' AND
messages.content ->> 'root' IS NULL AND
messages.author != ?4
GROUP by channel
UNION
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
UNION
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
`,
[
JSON.stringify(this.channels),
JSON.stringify(following),
'"' + this.whoami.replace('"', '""') + '"',
this.whoami,
]
);
const k_args = [
JSON.stringify(this.channels),
JSON.stringify(following),
'"' + this.whoami.replace('"', '""') + '"',
this.whoami,
];
let channels = (
await Promise.all([
this.query_timed(
`
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
JOIN json_each(?2) AS following ON messages.author = following.value
WHERE
messages.content ->> 'type' = 'post' AND
messages.content ->> 'root' IS NULL AND
messages.author != ?4
GROUP by channel
`,
k_args
),
this.query_timed(
`
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
JOIN messages_refs ON messages.id = messages_refs.message
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
JOIN json_each(?2) AS following ON messages.author = following.value
WHERE
messages.content ->> 'type' = 'post' AND
messages.content ->> 'root' IS NULL AND
messages.author != ?4
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]) {

View File

@ -446,12 +446,15 @@ class TfComposeElement extends LitElement {
self.apps = await tfrpc.rpc.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
</button>`;
} else {
return html`<button
class="w3-button w3-theme-d1"
class="w3-button w3-bar-item w3-theme-d1"
@click=${() => (this.apps = null)}
>
Discard App
@ -472,18 +475,9 @@ class TfComposeElement extends LitElement {
if (draft.content_warning !== undefined) {
return html`
<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>
</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();
}
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() {
let self = this;
let draft = self.get_draft();
@ -559,7 +578,7 @@ class TfComposeElement extends LitElement {
draft.encrypt_to !== undefined
? undefined
: html`<button
class="w3-button w3-theme-d1"
class="w3-button w3-bar-item w3-theme-d1"
@click=${() => this.set_encrypt([])}
>
🔐
@ -614,13 +633,43 @@ class TfComposeElement extends LitElement {
>
Submit
</button>
<button class="w3-button w3-theme-d1" @click=${this.attach}>
Attach
</button>
${this.render_attach_app_button()} ${encrypt}
<button class="w3-button w3-theme-d1" @click=${this.discard}>
Discard
</button>
<div class="w3-dropdown-click">
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
⚙️
</button>
<div class="w3-dropdown-content w3-bar-block">
${this.get_draft().content_warning === undefined
? 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>
</div>
`;

View File

@ -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 tfutils from './tf-utils.js';
import * as emojis from './emojis.js';
@ -407,7 +414,7 @@ class TfMessageElement extends LitElement {
class_background() {
return this.message?.decrypted
? 'w3-pale-red'
: this.message?.rowid >= this.channel_unread
: this.allow_unread() && this.message?.rowid >= this.channel_unread
? 'w3-theme-d2'
: 'w3-theme-d4';
}
@ -499,7 +506,10 @@ class TfMessageElement extends LitElement {
return html`
<header class="w3-bar">
<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>
${is_encrypted} ${this.render_menu()}
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
@ -621,6 +631,19 @@ class TfMessageElement extends LitElement {
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() {
let content = this.message?.content;
if (this.message?.decrypted?.type == 'post') {

View File

@ -14,6 +14,7 @@ class TfNewsElement extends LitElement {
channel: {type: String},
channel_unread: {type: Number},
recent_reactions: {type: Array},
hash: {type: String},
};
}
@ -193,15 +194,21 @@ class TfNewsElement extends LitElement {
return result;
}
unread_allowed() {
return !this.hash?.startsWith('#%') && !this.hash?.startsWith('#@');
}
load_and_render(messages) {
let messages_by_id = this.process_messages(messages);
let final_messages = this.group_following(
this.finalize_messages(messages_by_id)
);
let unread_rowid = -1;
for (let message of final_messages) {
if (message.rowid >= this.channel_unread) {
unread_rowid = message.rowid;
if (this.unread_allowed()) {
for (let message of final_messages) {
if (message.rowid >= this.channel_unread) {
unread_rowid = message.rowid;
}
}
}
return html`

View File

@ -177,10 +177,10 @@ class TfTabNewsFeedElement extends LitElement {
WHERE messages.content ->> 'channel' = ?4
UNION
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)
JOIN messages ON messages.rowid = messages_fts.rowid
FROM messages_refs
JOIN messages ON messages.id = messages_refs.message
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
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
@ -191,7 +191,6 @@ class TfTabNewsFeedElement extends LitElement {
start_time,
end_time,
this.hash.substring(2),
'"#' + this.hash.substring(2).replace('"', '""') + '"',
]
);
let t1 = new Date();
@ -209,11 +208,29 @@ class TfTabNewsFeedElement extends LitElement {
WHERE
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
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]
);
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 {
let t0 = new Date();
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() {
this.loading++;
this.loading_canceled = false;
@ -407,9 +431,16 @@ class TfTabNewsFeedElement extends LitElement {
if (!this.hash.startsWith('#%')) {
more = html`
<p>
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
Mark All Read
</button>
${this.unread_allowed()
? html`
<button
class="w3-button w3-theme-d1"
@click=${this.mark_all_read}
>
Mark All Read
</button>
`
: undefined}
<button
?disabled=${this.loading}
class="w3-button w3-theme-d1"
@ -441,7 +472,7 @@ class TfTabNewsFeedElement extends LitElement {
`;
}
return cache(html`
${!this.hash.startsWith('#%')
${this.unread_allowed()
? html`<button
class="w3-button w3-theme-d1"
@click=${this.mark_all_read}
@ -457,6 +488,7 @@ class TfTabNewsFeedElement extends LitElement {
.following=${this.following}
.drafts=${this.drafts}
.expanded=${this.expanded}
hash=${this.hash}
channel=${this.channel()}
channel_unread=${this.channels_unread?.[this.channel()]}
.recent_reactions=${this.recent_reactions}

View File

@ -25,6 +25,7 @@ class TfTabNewsElement extends LitElement {
connections: {type: Array},
private_messages: {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) {
self.drafts = JSON.parse(d || '{}');
});
this.check_peer_exchange();
}
connectedCallback() {
@ -60,6 +62,14 @@ class TfTabNewsElement extends LitElement {
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() {
let news = this.shadowRoot?.getElementById('news');
if (news) {
@ -164,6 +174,15 @@ class TfTabNewsElement extends LitElement {
.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() {
return html`
<div
@ -202,6 +221,12 @@ class TfTabNewsElement extends LitElement {
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
>${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
href="#🔐"
class="w3-bar-item w3-button"
@ -234,6 +259,26 @@ class TfTabNewsElement extends LitElement {
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
<h4 style="margin: 0">Connections</h4>
</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
.filter((x) => x.id)
.map(

View File

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "👋",
"previous": "&1o8MrBHfH42NnO+ruajwCmW/DUCb+IT1qtnAZI/agyo=.sha256"
"previous": "&fY3YUKPuH/wqOgKPVNJu1vWEHCXf5fToL2qiVXMRmxc=.sha256"
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -28,23 +28,12 @@
<b>😎 Tilde Friends</b>
</h1>
<h1 class="w3-xxlarge w3-text-green">
<b
>the Secure Scuttlebutt decentralized social network client that's
<i>fancy🎩</i></b
>
<b>a Secure Scuttlebutt decentralized social network client</b>
</h1>
<p>
In addition to participating in Secure Scuttlebutt, Tilde Friends is
a platform for building, running, and sharing applications.
</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
class="w3-button w3-blue w3-padding-large"
href="https://www.tildefriends.net/~core/ssb/"
@ -52,7 +41,7 @@
>
<a
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
>
<a
@ -70,35 +59,6 @@
href="https://www.tildefriends.net/~cory/tildeblog/"
><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 class="w3-col l4 m6">
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
@ -123,8 +83,100 @@
<a href="https://www.tildefriends.net/"
>https://www.tildefriends.net/</a
>.
<div class="w3-cell-row">
<div class="w3-container w3-cell">
<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">
<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>
</div>
<div class="w3-container w3-cell">
<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>Create an account to identify yourself with that instance.</li>
<li>
Describe yourself in your profile in the <b>ssb</b> app. Give
yourself a name and an avatar if you like.
@ -158,11 +210,11 @@
<!-- SSB Section -->
<div class="w3-light-grey">
<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/"
><img
class="w3-image w3-round-large"
src="ssb.png"
class="w3-image"
src="hermietildefriends.svg"
alt="Secure Scuttlebutt"
/></a>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

View File

@ -8,6 +8,7 @@ let gFiles = {};
let gApp = {files: {}, emoji: '📦'};
let gEditor;
let gOriginalInput;
let gUnloading;
let kErrorColor = '#dc322f';
let kDisconnectColor = '#f00';
@ -1560,27 +1561,31 @@ function connectSocket(path) {
_receive_websocket_message(JSON.parse(event.data));
};
gSocket.onclose = function (event) {
const k_codes = {
1000: 'Normal closure',
1001: 'Going away',
1002: 'Protocol error',
1003: 'Unsupported data',
1005: 'No status received',
1006: 'Abnormal closure',
1007: 'Invalid frame payload data',
1008: 'Policy violation',
1009: 'Message too big',
1010: 'Missing extension',
1011: 'Internal error',
1012: 'Service restart',
1013: 'Try again later',
1014: 'Bad gateway',
1015: 'TLS handshake',
};
setStatusMessage(
'🔴 Closed: ' + (k_codes[event.code] || event.code),
kDisconnectColor
);
if (gUnloading) {
setStatusMessage('⚪ Closing...', kStatusColor);
} else {
const k_codes = {
1000: 'Normal closure',
1001: 'Going away',
1002: 'Protocol error',
1003: 'Unsupported data',
1005: 'No status received',
1006: 'Abnormal closure',
1007: 'Invalid frame payload data',
1008: 'Policy violation',
1009: 'Message too big',
1010: 'Missing extension',
1011: 'Internal error',
1012: 'Service restart',
1013: 'Try again later',
1014: 'Bad gateway',
1015: 'TLS handshake',
};
setStatusMessage(
'🔴 Closed: ' + (k_codes[event.code] || event.code),
kDisconnectColor
);
}
};
}
}
@ -1854,6 +1859,9 @@ window.addEventListener('load', function () {
window.addEventListener('blur', blur);
window.addEventListener('message', message, false);
window.addEventListener('online', connectSocket);
window.addEventListener('beforeunload', function () {
gUnloading = true;
});
document.getElementById('name').value = window.location.pathname;
document
.getElementById('closeEditor')

File diff suppressed because one or more lines are too long

223
deps/codemirror_src/package-lock.json generated vendored
View File

@ -83,18 +83,18 @@
}
},
"node_modules/@codemirror/lang-json": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@lezer/json": "^1.0.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.0.tgz",
"integrity": "sha512-A7+f++LodNNc1wGgoRDTt78cOwWm9KVezApgjOMp1W4hM0898nsqBXwF+sbePE7ZRcjN7Sa1Z5m2oN27XkmEjQ==",
"version": "6.11.1",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.1.tgz",
"integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
@ -133,9 +133,9 @@
}
},
"node_modules/@codemirror/theme-one-dark": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
@ -144,11 +144,12 @@
}
},
"node_modules/@codemirror/view": {
"version": "6.37.0",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.0.tgz",
"integrity": "sha512-ghHIeRGfWB8h9Tc3sMdr7D5zp4sZvlCzG36Xjdh2ymmfAwvSaCJAAsL3HLyLEnHcE953+5Uox1bx5OS+YCW/7Q==",
"version": "6.37.2",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.2.tgz",
"integrity": "sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==",
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
@ -323,9 +324,9 @@
}
},
"node_modules/@rollup/pluginutils": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
"dependencies": {
"@types/estree": "^1.0.0",
"estree-walker": "^2.0.2",
@ -344,9 +345,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
"integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz",
"integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==",
"cpu": [
"arm"
],
@ -356,9 +357,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz",
"integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz",
"integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==",
"cpu": [
"arm64"
],
@ -368,9 +369,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz",
"integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz",
"integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==",
"cpu": [
"arm64"
],
@ -380,9 +381,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz",
"integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz",
"integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==",
"cpu": [
"x64"
],
@ -392,9 +393,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz",
"integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz",
"integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==",
"cpu": [
"arm64"
],
@ -404,9 +405,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz",
"integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz",
"integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==",
"cpu": [
"x64"
],
@ -416,9 +417,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz",
"integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz",
"integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==",
"cpu": [
"arm"
],
@ -428,9 +429,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz",
"integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz",
"integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==",
"cpu": [
"arm"
],
@ -440,9 +441,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz",
"integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz",
"integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==",
"cpu": [
"arm64"
],
@ -452,9 +453,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz",
"integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz",
"integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==",
"cpu": [
"arm64"
],
@ -464,9 +465,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz",
"integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz",
"integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==",
"cpu": [
"loong64"
],
@ -476,9 +477,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz",
"integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz",
"integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==",
"cpu": [
"ppc64"
],
@ -488,9 +489,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz",
"integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz",
"integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==",
"cpu": [
"riscv64"
],
@ -500,9 +501,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz",
"integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz",
"integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==",
"cpu": [
"riscv64"
],
@ -512,9 +513,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz",
"integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz",
"integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==",
"cpu": [
"s390x"
],
@ -524,9 +525,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz",
"integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz",
"integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==",
"cpu": [
"x64"
],
@ -536,9 +537,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz",
"integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz",
"integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==",
"cpu": [
"x64"
],
@ -548,9 +549,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz",
"integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz",
"integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==",
"cpu": [
"arm64"
],
@ -560,9 +561,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz",
"integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz",
"integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==",
"cpu": [
"ia32"
],
@ -572,9 +573,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz",
"integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz",
"integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==",
"cpu": [
"x64"
],
@ -584,9 +585,9 @@
]
},
"node_modules/@types/estree": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
},
"node_modules/@types/resolve": {
"version": "1.20.2",
@ -594,9 +595,9 @@
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
},
"node_modules/acorn": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"bin": {
"acorn": "bin/acorn"
@ -612,9 +613,9 @@
"dev": true
},
"node_modules/codemirror": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/commands": "^6.0.0",
@ -745,11 +746,11 @@
}
},
"node_modules/rollup": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
"version": "4.44.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz",
"integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==",
"dependencies": {
"@types/estree": "1.0.7"
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@ -759,26 +760,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.41.1",
"@rollup/rollup-android-arm64": "4.41.1",
"@rollup/rollup-darwin-arm64": "4.41.1",
"@rollup/rollup-darwin-x64": "4.41.1",
"@rollup/rollup-freebsd-arm64": "4.41.1",
"@rollup/rollup-freebsd-x64": "4.41.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.41.1",
"@rollup/rollup-linux-arm-musleabihf": "4.41.1",
"@rollup/rollup-linux-arm64-gnu": "4.41.1",
"@rollup/rollup-linux-arm64-musl": "4.41.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.41.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.1",
"@rollup/rollup-linux-riscv64-gnu": "4.41.1",
"@rollup/rollup-linux-riscv64-musl": "4.41.1",
"@rollup/rollup-linux-s390x-gnu": "4.41.1",
"@rollup/rollup-linux-x64-gnu": "4.41.1",
"@rollup/rollup-linux-x64-musl": "4.41.1",
"@rollup/rollup-win32-arm64-msvc": "4.41.1",
"@rollup/rollup-win32-ia32-msvc": "4.41.1",
"@rollup/rollup-win32-x64-msvc": "4.41.1",
"@rollup/rollup-android-arm-eabi": "4.44.0",
"@rollup/rollup-android-arm64": "4.44.0",
"@rollup/rollup-darwin-arm64": "4.44.0",
"@rollup/rollup-darwin-x64": "4.44.0",
"@rollup/rollup-freebsd-arm64": "4.44.0",
"@rollup/rollup-freebsd-x64": "4.44.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.44.0",
"@rollup/rollup-linux-arm-musleabihf": "4.44.0",
"@rollup/rollup-linux-arm64-gnu": "4.44.0",
"@rollup/rollup-linux-arm64-musl": "4.44.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.44.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.0",
"@rollup/rollup-linux-riscv64-gnu": "4.44.0",
"@rollup/rollup-linux-riscv64-musl": "4.44.0",
"@rollup/rollup-linux-s390x-gnu": "4.44.0",
"@rollup/rollup-linux-x64-gnu": "4.44.0",
"@rollup/rollup-linux-x64-musl": "4.44.0",
"@rollup/rollup-win32-arm64-msvc": "4.44.0",
"@rollup/rollup-win32-ia32-msvc": "4.44.0",
"@rollup/rollup-win32-x64-msvc": "4.44.0",
"fsevents": "~2.3.2"
}
},
@ -853,9 +854,9 @@
}
},
"node_modules/terser": {
"version": "5.40.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.40.0.tgz",
"integrity": "sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==",
"version": "5.43.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
"dev": true,
"dependencies": {
"@jridgewell/source-map": "^0.3.3",

102
deps/sqlite/sqlite3.c vendored
View File

@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.50.0. By combining all the individual C code files into this
** version 3.50.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
** dfc790f998f450d9c35e3ba1c8c89c17466c with changes in files:
** b77dc5e0f596d2140d9ac682b2893ff65d3a with changes in files:
**
**
*/
@ -465,9 +465,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.50.0"
#define SQLITE_VERSION_NUMBER 3050000
#define SQLITE_SOURCE_ID "2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742"
#define SQLITE_VERSION "3.50.1"
#define SQLITE_VERSION_NUMBER 3050001
#define SQLITE_SOURCE_ID "2025-06-06 14:52:32 b77dc5e0f596d2140d9ac682b2893ff65d3a4140aa86067a3efebe29dc914c95"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -18703,6 +18703,7 @@ struct CollSeq {
#define SQLITE_AFF_INTEGER 0x44 /* 'D' */
#define SQLITE_AFF_REAL 0x45 /* 'E' */
#define SQLITE_AFF_FLEXNUM 0x46 /* 'F' */
#define SQLITE_AFF_DEFER 0x58 /* 'X' - defer computation until later */
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
@ -43874,21 +43875,20 @@ static int unixShmLock(
/* Check that, if this to be a blocking lock, no locks that occur later
** in the following list than the lock being obtained are already held:
**
** 1. Checkpointer lock (ofst==1).
** 2. Write lock (ofst==0).
** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
** 1. Recovery lock (ofst==2).
** 2. Checkpointer lock (ofst==1).
** 3. Write lock (ofst==0).
** 4. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
**
** In other words, if this is a blocking lock, none of the locks that
** occur later in the above list than the lock being obtained may be
** held.
**
** It is not permitted to block on the RECOVER lock.
*/
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
{
u16 lockMask = (p->exclMask|p->sharedMask);
assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
(ofst!=2) /* not RECOVER */
(ofst!=2 || lockMask==0)
&& (ofst!=1 || lockMask==0 || lockMask==2)
&& (ofst!=0 || lockMask<3)
&& (ofst<3 || lockMask<(1<<ofst))
@ -49849,7 +49849,11 @@ static int winHandleLockTimeout(
if( res==WAIT_OBJECT_0 ){
ret = TRUE;
}else if( res==WAIT_TIMEOUT ){
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
rc = SQLITE_BUSY_TIMEOUT;
#else
rc = SQLITE_BUSY;
#endif
}else{
/* Some other error has occurred */
rc = SQLITE_IOERR_LOCK;
@ -51660,21 +51664,20 @@ static int winShmLock(
/* Check that, if this to be a blocking lock, no locks that occur later
** in the following list than the lock being obtained are already held:
**
** 1. Checkpointer lock (ofst==1).
** 2. Write lock (ofst==0).
** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
** 1. Recovery lock (ofst==2).
** 2. Checkpointer lock (ofst==1).
** 3. Write lock (ofst==0).
** 4. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
**
** In other words, if this is a blocking lock, none of the locks that
** occur later in the above list than the lock being obtained may be
** held.
**
** It is not permitted to block on the RECOVER lock.
*/
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
{
u16 lockMask = (p->exclMask|p->sharedMask);
assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
(ofst!=2) /* not RECOVER */
(ofst!=2 || lockMask==0)
&& (ofst!=1 || lockMask==0 || lockMask==2)
&& (ofst!=0 || lockMask<3)
&& (ofst<3 || lockMask<(1<<ofst))
@ -58750,6 +58753,9 @@ struct Pager {
Wal *pWal; /* Write-ahead log used by "journal_mode=wal" */
char *zWal; /* File name for write-ahead log */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
sqlite3 *dbWal;
#endif
};
/*
@ -65631,6 +65637,11 @@ static int pagerOpenWal(Pager *pPager){
pPager->fd, pPager->zWal, pPager->exclusiveMode,
pPager->journalSizeLimit, &pPager->pWal
);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
if( rc==SQLITE_OK ){
sqlite3WalDb(pPager->pWal, pPager->dbWal);
}
#endif
}
pagerFixMaplimit(pPager);
@ -65750,6 +65761,7 @@ SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){
** blocking locks are required.
*/
SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){
pPager->dbWal = db;
if( pagerUseWal(pPager) ){
sqlite3WalDb(pPager->pWal, db);
}
@ -68923,7 +68935,6 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
rc = walIndexReadHdr(pWal, pChanged);
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
walDisableBlocking(pWal);
if( rc==SQLITE_BUSY_TIMEOUT ){
rc = SQLITE_BUSY;
*pCnt |= WAL_RETRY_BLOCKED_MASK;
@ -68938,6 +68949,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
** WAL_RETRY this routine will be called again and will probably be
** right on the second iteration.
*/
(void)walEnableBlocking(pWal);
if( pWal->apWiData[0]==0 ){
/* This branch is taken when the xShmMap() method returns SQLITE_BUSY.
** We assume this is a transient condition, so return WAL_RETRY. The
@ -68954,6 +68966,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
rc = SQLITE_BUSY_RECOVERY;
}
}
walDisableBlocking(pWal);
if( rc!=SQLITE_OK ){
return rc;
}
@ -75228,6 +75241,13 @@ static SQLITE_NOINLINE int btreeBeginTrans(
(void)sqlite3PagerWalWriteLock(pPager, 0);
unlockBtreeIfUnused(pBt);
}
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT)
if( rc==SQLITE_BUSY_TIMEOUT ){
/* If a blocking lock timed out, break out of the loop here so that
** the busy-handler is not invoked. */
break;
}
#endif
}while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
btreeInvokeBusyHandler(pBt) );
sqlite3PagerWalDb(pPager, 0);
@ -105039,7 +105059,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
assert( pCsr->eCurType==CURTYPE_SORTER );
assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
< 0x7fffffff );
szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1);
szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField);
sz = SZ_VDBESORTER(nWorker+1);
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
@ -110389,7 +110409,9 @@ SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){
pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
);
}
if( op==TK_VECTOR ){
if( op==TK_VECTOR
|| (op==TK_FUNCTION && pExpr->affExpr==SQLITE_AFF_DEFER)
){
assert( ExprUseXList(pExpr) );
return sqlite3ExprAffinity(pExpr->x.pList->a[0].pExpr);
}
@ -110582,7 +110604,9 @@ SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, const Expr *pExpr){
p = p->pLeft;
continue;
}
if( op==TK_VECTOR ){
if( op==TK_VECTOR
|| (op==TK_FUNCTION && p->affExpr==SQLITE_AFF_DEFER)
){
assert( ExprUseXList(p) );
p = p->x.pList->a[0].pExpr;
continue;
@ -145364,7 +145388,7 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){
}
pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iLeftCol);
sqlite3SrcItemColumnUsed(&pSrc->a[iLeft], iLeftCol);
if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 ){
if( (pSrc->a[0].fg.jointype & JT_LTORJ)!=0 && pParse->nErr==0 ){
/* This branch runs if the query contains one or more RIGHT or FULL
** JOINs. If only a single table on the left side of this join
** contains the zName column, then this branch is a no-op.
@ -145380,6 +145404,8 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){
*/
ExprList *pFuncArgs = 0; /* Arguments to the coalesce() */
static const Token tkCoalesce = { "coalesce", 8 };
assert( pE1!=0 );
ExprSetProperty(pE1, EP_CanBeNull);
while( tableAndColumnIndex(pSrc, iLeft+1, i, zName, &iLeft, &iLeftCol,
pRight->fg.isSynthUsing)!=0 ){
if( pSrc->a[iLeft].fg.isUsing==0
@ -145396,7 +145422,13 @@ static int sqlite3ProcessJoin(Parse *pParse, Select *p){
if( pFuncArgs ){
pFuncArgs = sqlite3ExprListAppend(pParse, pFuncArgs, pE1);
pE1 = sqlite3ExprFunction(pParse, pFuncArgs, &tkCoalesce, 0);
if( pE1 ){
pE1->affExpr = SQLITE_AFF_DEFER;
}
}
}else if( (pSrc->a[i+1].fg.jointype & JT_LEFT)!=0 && pParse->nErr==0 ){
assert( pE1!=0 );
ExprSetProperty(pE1, EP_CanBeNull);
}
pE2 = sqlite3CreateColumnExpr(db, pSrc, i+1, iRightCol);
sqlite3SrcItemColumnUsed(pRight, iRightCol);
@ -149004,9 +149036,9 @@ static int compoundHasDifferentAffinities(Select *p){
** from 2015-02-09.)
**
** (3) If the subquery is the right operand of a LEFT JOIN then
** (3a) the subquery may not be a join and
** (3b) the FROM clause of the subquery may not contain a virtual
** table and
** (3a) the subquery may not be a join
** (**) Was (3b): "the FROM clause of the subquery may not contain
** a virtual table"
** (**) Was: "The outer query may not have a GROUP BY." This case
** is now managed correctly
** (3d) the outer query may not be DISTINCT.
@ -149222,7 +149254,7 @@ static int flattenSubquery(
*/
if( (pSubitem->fg.jointype & (JT_OUTER|JT_LTORJ))!=0 ){
if( pSubSrc->nSrc>1 /* (3a) */
|| IsVirtual(pSubSrc->a[0].pSTab) /* (3b) */
/**** || IsVirtual(pSubSrc->a[0].pSTab) (3b)-omitted */
|| (p->selFlags & SF_Distinct)!=0 /* (3d) */
|| (pSubitem->fg.jointype & JT_RIGHT)!=0 /* (26) */
){
@ -161722,12 +161754,13 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pLevel->iLeftJoin==0 ){
/* If a partial index is driving the loop, try to eliminate WHERE clause
** terms from the query that must be true due to the WHERE clause of
** the partial index.
** the partial index. This optimization does not work on an outer join,
** as shown by:
**
** 2019-11-02 ticket 623eff57e76d45f6: This optimization does not work
** for a LEFT JOIN.
** 2019-11-02 ticket 623eff57e76d45f6 (LEFT JOIN)
** 2025-05-29 forum post 7dee41d32506c4ae (RIGHT JOIN)
*/
if( pIdx->pPartIdxWhere ){
if( pIdx->pPartIdxWhere && pLevel->pRJ==0 ){
whereApplyPartialIndexConstraints(pIdx->pPartIdxWhere, iCur, pWC);
}
}else{
@ -209021,8 +209054,10 @@ static int jsonBlobChangePayloadSize(
nExtra = 1;
}else if( szType==13 ){
nExtra = 2;
}else{
}else if( szType==14 ){
nExtra = 4;
}else{
nExtra = 8;
}
if( szPayload<=11 ){
nNeeded = 0;
@ -213407,6 +213442,8 @@ SQLITE_PRIVATE int sqlite3JsonTableFunctions(sqlite3 *db){
#endif
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char*,int*); /* In the SQLite core */
/* #include <stddef.h> */
/*
** If building separately, we will need some setup that is normally
** found in sqliteInt.h
@ -235449,6 +235486,7 @@ SQLITE_EXTENSION_INIT1
/* #include <string.h> */
/* #include <assert.h> */
/* #include <stddef.h> */
#ifndef SQLITE_AMALGAMATION
@ -257192,7 +257230,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742", -1, SQLITE_TRANSIENT);
sqlite3_result_text(pCtx, "fts5: 2025-06-06 14:52:32 b77dc5e0f596d2140d9ac682b2893ff65d3a4140aa86067a3efebe29dc914c95", -1, SQLITE_TRANSIENT);
}
/*

View File

@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.50.0"
#define SQLITE_VERSION_NUMBER 3050000
#define SQLITE_SOURCE_ID "2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742"
#define SQLITE_VERSION "3.50.1"
#define SQLITE_VERSION_NUMBER 3050001
#define SQLITE_SOURCE_ID "2025-06-06 14:52:32 b77dc5e0f596d2140d9ac682b2893ff65d3a4140aa86067a3efebe29dc914c95"
/*
** CAPI3REF: Run-Time Library Version Numbers

View 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.
![Manyverse connections tab](manyverse_connections_tab.png)
Open the `Connections Panel`.
![Manyverse connections panel](manyverse_connections_panel.png)
Use the `Add Connection` button at the bottom right to open the dialog to enter
a connections string to add a new connection.
![Manyverse connections panel](manyverse_paste_invite_code.png)
Copy the tildefriends.net room code from https://www.tildefriends.net/~cory/room/.
![Tilde Friends room code](tildefriends_room_app.png)
Paste.
On mobile especially, make sure the full text is pasted without modification.
![Manyverse invite code](manyverse_code.png)
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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

288
docs/usage.md Normal file
View File

@ -0,0 +1,288 @@
# 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.
-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.
```

View 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

6
package-lock.json generated
View File

@ -11,9 +11,9 @@
}
},
"node_modules/prettier": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.1.tgz",
"integrity": "sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==",
"bin": {
"prettier": "bin/prettier.cjs"
},

View File

@ -2,7 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends"
android:versionCode="38"
android:versionName="0.0.32-wip">
android:versionName="0.0.32">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<application

View File

@ -813,6 +813,11 @@ void tf_http_destroy(tf_http_t* http)
return;
}
if (!http->is_shutting_down)
{
tf_printf("tf_http_destroy\n");
}
http->is_shutting_down = true;
http->is_in_destroy = true;

View File

@ -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);
for (; count < (int)plen && count < headers_length / 2; ++count)
{
JSValue key = JS_AtomToString(context, ptab[count].atom);
JSPropertyDescriptor desc;
JSValue key_value = JS_NULL;
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.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);
JS_FreeValue(context, key);
JS_FreeValue(context, key_value);
}
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);
}
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)
{
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);
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);
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((void*)value);
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);
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);
snprintf(blob_id, sizeof(blob_id), "%s", value);
tf_string_set(blob_id, sizeof(blob_id), value);
tf_free(app_path);
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);
tf_ssb_db_add_blob_wants(db, blob_id);
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_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;
}
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 };
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;
}
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");
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
{
@ -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");
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
{
@ -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, "/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, "/trace", _httpd_endpoint_trace, NULL, task);
tf_http_add_handler(http, "/ebt", _httpd_endpoint_ebt, NULL, task);

View File

@ -11,7 +11,8 @@
** @{
*/
#include "quickjs.h"
/** A JS context. */
typedef struct JSContext JSContext;
/**
** An HTTP server instance.

View File

@ -169,8 +169,8 @@ typedef struct _command_t
const command_t k_commands[] = {
{ "run", _tf_command_run, "Run tildefriends (default)." },
{ "sandbox", _tf_command_sandbox, "Run a sandboxed tildefriends sandbox process (used internally)." },
{ "import", _tf_command_import, "Import apps to SSB." },
{ "export", _tf_command_export, "Export apps from SSB." },
{ "import", _tf_command_import, "Import apps from file to the database." },
{ "export", _tf_command_export, "Export apps from the database to file." },
{ "publish", _tf_command_publish, "Append a 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." },
@ -185,6 +185,18 @@ const command_t k_commands[] = {
{ "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[])
{
#if !defined(__ANDROID__)
@ -233,8 +245,9 @@ static int _tf_command_test(const char* file, int argc, char* argv[])
if (show_usage)
{
tf_printf("\n%s test [options]\n\n", file);
tf_printf("options\n");
tf_printf("\nUsage: %s test [options]\n\n", file);
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(" -h, --help Show this usage information.\n");
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)
{
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(" -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);
@ -357,7 +371,8 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
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(" -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);
@ -473,7 +488,8 @@ static int _tf_command_publish(const char* file, int argc, char* argv[])
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(" -u, --user user User owning identity with which to publish.\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)))
{
JSContext* context = tf_ssb_get_context(ssb);
int64_t sequence = 0;
int32_t sequence = 0;
char previous[k_id_base64_len] = { 0 };
tf_ssb_db_get_latest_message_by_author(ssb, identity, &sequence, previous, sizeof(previous));
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)
{
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(" -u, --user user User owning identity with which to publish (optional).\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));
if (encrypted)
{
int64_t sequence = 0;
int32_t sequence = 0;
char previous[k_id_base64_len] = { 0 };
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)
{
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(" -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");
@ -825,7 +843,8 @@ static int _tf_command_get_blob(const char* file, int argc, char* argv[])
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(" -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");
@ -927,7 +946,8 @@ static int _tf_command_has_blob(const char* file, int argc, char* argv[])
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(" -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");
@ -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)
{
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(" -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");
@ -1073,7 +1094,8 @@ static int _tf_command_get_sequence(const char* file, int argc, char* argv[])
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(" -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");
@ -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_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;
tf_printf("%" PRId64 "\n", sequence);
tf_printf("%d\n", sequence);
tf_ssb_destroy(ssb);
tf_free((void*)default_db_path);
return result;
@ -1126,7 +1148,8 @@ static int _tf_command_get_identity(const char* file, int argc, char* argv[])
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(" -d, --db-path db_path SQLite database path (default: %s).\n", default_db_path);
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)
{
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(" -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");
@ -1242,7 +1266,8 @@ static int _tf_command_get_contacts(const char* file, int argc, char* argv[])
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(" -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");
@ -1304,7 +1329,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
const char* identity = NULL;
const char* default_db_path = _get_db_path();
const char* db_path = default_db_path;
int64_t sequence = 0;
int32_t sequence = 0;
bool show_usage = false;
while (!show_usage)
@ -1333,7 +1358,7 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
identity = optarg;
break;
case 's':
sequence = atoll(optarg);
sequence = atoi(optarg);
break;
case 'd':
db_path = optarg;
@ -1343,7 +1368,8 @@ static int _tf_command_verify(const char* file, int argc, char* argv[])
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(" -i, --identity identity Identity to verify.\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)
{
tf_printf("\n%s run [options]\n\n", file);
tf_printf("options\n");
tf_printf("\nUsage: %s run [options]\n\n", file);
#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(" -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");
@ -1757,6 +1786,9 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
if (show_usage)
{
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(" -h, --help Show this usage information.\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
int main(int argc, char* argv[])
{
setvbuf(stdout, NULL, _IONBF, 0);
_startup(argc, argv);
ares_library_init(0);

View File

@ -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* 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));
memset(data, 0, sizeof(*data));
@ -1062,9 +1062,7 @@ static void _socket_onShutdown(uv_shutdown_t* request, int status)
}
else
{
char error[256];
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_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "uv_shutdown: %s", uv_strerror(status)));
}
tf_free(request);
}

172
src/ssb.c
View File

@ -34,9 +34,6 @@
#define CYAN "\e[1;36m"
#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;
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;
} 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
{
bool own_context;
@ -183,6 +186,7 @@ typedef struct _tf_ssb_t
sqlite3* db_writer;
sqlite3** db_readers;
int db_readers_count;
int db_ref_count;
uv_loop_t own_loop;
uv_loop_t* loop;
@ -194,6 +198,9 @@ typedef struct _tf_ssb_t
uv_timer_t request_activity_timer;
uv_tcp_t server;
tf_ssb_broadcast_result_t* broadcast_results;
int broadcast_results_count;
uint8_t network_key[32];
uint8_t pub[crypto_sign_PUBLICKEYBYTES];
@ -237,9 +244,6 @@ typedef struct _tf_ssb_t
int32_t thread_busy_count;
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;
int ref_count;
@ -368,8 +372,6 @@ static int s_connection_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 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_destroy(tf_ssb_connection_t* connection, const char* reason);
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--;
tf_trace_begin(connection->ssb->trace, "scheduled callback");
PRE_CALLBACK(connection->ssb, scheduled.callback);
scheduled.callback(connection, false, scheduled.user_data);
POST_CALLBACK(connection->ssb, scheduled.callback);
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. */
tf_trace_begin(connection->ssb->trace, "scheduled callback (skip)");
PRE_CALLBACK(connection->ssb, callback);
callback(connection, true, user_data);
POST_CALLBACK(connection->ssb, callback);
tf_trace_end(connection->ssb->trace);
}
else
@ -679,7 +677,7 @@ void tf_ssb_connection_schedule_idle(tf_ssb_connection_t* connection, const char
.callback = callback,
.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++;
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,
.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);
connection->requests = tf_resize_vec(connection->requests, sizeof(tf_ssb_request_t) * (connection->requests_count + 1));
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,
.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++;
}
@ -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;
}
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)
{
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;
tf_trace_begin(ssb->trace, "connections_changed");
PRE_CALLBACK(ssb, node->callback);
node->callback(ssb, change, connection, node->user_data);
POST_CALLBACK(ssb, node->callback);
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;
if (connection->connect_callback)
{
PRE_CALLBACK(connection->ssb, connection->connect_callback);
connection->connect_callback(connection, NULL, connection->connect_callback_user_data);
POST_CALLBACK(connection->ssb, connection->connect_callback);
connection->connect_callback = 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))
{
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_FreeValue(context, name);
@ -1777,9 +1755,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
char buffer[64];
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
tf_trace_begin(connection->ssb->trace, buffer);
PRE_CALLBACK(connection->ssb, callback);
callback(connection, flags, request_number, val, message, size, user_data);
POST_CALLBACK(connection->ssb, callback);
tf_trace_end(connection->ssb->trace);
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_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);
POST_CALLBACK(connection->ssb, it->callback);
tf_trace_end(connection->ssb->trace);
found = true;
break;
@ -1840,9 +1814,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
char buffer[64];
snprintf(buffer, sizeof(buffer), "request %s:%d", request_name, request_number);
tf_trace_begin(connection->ssb->trace, buffer);
PRE_CALLBACK(connection->ssb, callback);
callback(connection, flags, request_number, JS_UNDEFINED, message, size, user_data);
POST_CALLBACK(connection->ssb, callback);
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)
{
uint8_t flags = *connection->rpc_recv_buffer;
uint8_t flags = (*connection->rpc_recv_buffer) & 0xf;
uint32_t body_len;
int32_t request_number;
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;
}
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];
int64_t actual_previous_sequence = 0;
int32_t actual_previous_sequence = 0;
bool have_previous = false;
if (previous_id)
{
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;
}
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, "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);
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)
{
PRE_CALLBACK(connection->ssb, connection->connect_callback);
connection->connect_callback(NULL, reason, connection->connect_callback_user_data);
POST_CALLBACK(connection->ssb, connection->connect_callback);
connection->connect_callback = 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_trace_sqlite(ssb->trace, db);
ssb->db_ref_count++;
uv_mutex_unlock(&ssb->db_readers_lock);
sqlite3_set_authorizer(db, NULL, NULL);
return db;
@ -2535,10 +2506,17 @@ void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db)
{
sqlite3_db_release_memory(db);
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[ssb->db_readers_count++] = db;
uv_mutex_unlock(&ssb->db_readers_lock);
tf_trace_end(ssb->trace);
if (destroy)
{
tf_ssb_destroy(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);
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)
{
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));
}
}
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;
if (ssb->db_readers)
{
@ -2872,9 +2850,15 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
tf_free(ssb->room_name);
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;
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_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++);
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->connect_callback = callback;
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);
}
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);
tf_ssb_ref(ssb);
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);
}
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->invite, invite, sizeof(connect->invite));
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);
}
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)
{
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_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);
if (r < 0)
{
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));
}
_tf_ssb_update_broadcast_result(ssb, (struct sockaddr*)&broadcast_addr, address_str, r);
}
typedef struct _seeds_t
@ -3551,9 +3563,7 @@ static void _tf_ssb_notify_broadcasts_changed(tf_ssb_t* ssb)
if (node->callback)
{
tf_trace_begin(ssb->trace, "broadcasts changed");
PRE_CALLBACK(ssb, node->callback);
node->callback(ssb, node->user_data);
POST_CALLBACK(ssb, node->callback);
tf_trace_end(ssb->trace);
}
}
@ -3657,9 +3667,7 @@ void tf_ssb_visit_broadcasts(tf_ssb_t* ssb,
if (node->mtime - now < 60)
{
tf_trace_begin(ssb->trace, "broadcast");
PRE_CALLBACK(ssb, callback);
callback(node->host, &node->addr, node->origin, node->tunnel_connection, node->pub, user_data);
POST_CALLBACK(ssb, callback);
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++;
}
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;
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;
tf_trace_begin(ssb->trace, "message added callback");
PRE_CALLBACK(ssb, node->callback);
node->callback(ssb, author, sequence, id, node->user_data);
POST_CALLBACK(ssb, node->callback);
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;
tf_trace_begin(ssb->trace, "blob want added callback");
PRE_CALLBACK(ssb, node->callback);
node->callback(ssb, id, node->user_data);
POST_CALLBACK(ssb, node->callback);
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;
}
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)
{
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)
{
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);
POST_CALLBACK(data->connection->ssb, data->after_work_callback);
tf_trace_end(data->connection->ssb->trace);
}
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)
{
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);
POST_CALLBACK(data->ssb, data->after_work_callback);
tf_trace_end(data->ssb->trace);
}
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);
}
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_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)
{
tf_ssb_timer_t* timer = handle->data;
PRE_CALLBACK(timer->ssb, timer->callback);
timer->callback(timer->ssb, timer->user_data);
POST_CALLBACK(timer->ssb, timer->callback);
uv_close((uv_handle_t*)handle, _tf_ssb_on_timer_close);
}

View File

@ -61,9 +61,9 @@ static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connec
{
if (sqlite3_bind_int(statement, 1, 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);
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;
}
sqlite3_finalize(statement);
@ -246,8 +246,8 @@ void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* hos
*update = (tf_ssb_connections_update_t) {
.port = port,
};
snprintf(update->host, sizeof(update->host), "%s", host);
snprintf(update->key, sizeof(update->key), "%s", key);
tf_string_set(update->host, sizeof(update->host), host);
tf_string_set(update->key, sizeof(update->key), key);
_tf_ssb_connections_queue_update(connections, update);
}
@ -258,8 +258,8 @@ void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const c
.port = port,
.attempted = true,
};
snprintf(update->host, sizeof(update->host), "%s", host);
snprintf(update->key, sizeof(update->key), "%s", key);
tf_string_set(update->host, sizeof(update->host), host);
tf_string_set(update->key, sizeof(update->key), key);
_tf_ssb_connections_queue_update(connections, update);
}
@ -270,8 +270,8 @@ void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const c
.port = port,
.succeeded = true,
};
snprintf(update->host, sizeof(update->host), "%s", host);
snprintf(update->key, sizeof(update->key), "%s", key);
tf_string_set(update->host, sizeof(update->host), host);
tf_string_set(update->key, sizeof(update->key), key);
_tf_ssb_connections_queue_update(connections, update);
}

View File

@ -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));
}
if (error)
{
sqlite3_free(error);
}
return result;
}
@ -41,6 +45,13 @@ static void _tf_ssb_db_exec(sqlite3* db, const char* statement)
if (result != SQLITE_OK)
{
tf_printf("Error running '%s': %s.\n", statement, error ? error : sqlite3_errmsg(db));
}
if (error)
{
sqlite3_free(error);
}
if (result != SQLITE_OK)
{
abort();
}
}
@ -76,6 +87,26 @@ static int _tf_ssb_db_busy_handler(void* user_data, int count)
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)
{
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);
_tf_ssb_db_init_internal(db);
sqlite3_wal_hook(db, _tf_ssb_db_wal_hook, NULL);
sqlite3_stmt* statement = NULL;
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_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_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_by_author_index");
_tf_ssb_db_exec(db, "DROP INDEX IF EXISTS messages_id_index");
@ -463,7 +496,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
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;
if (sequence == 1)
@ -475,7 +508,7 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
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_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)
{
exists = sqlite3_column_int(statement, 0) != 0;
@ -487,7 +520,7 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
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)
{
int64_t last_row_id = -1;
@ -502,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 &&
(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_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)
@ -536,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
** 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;
}
@ -593,7 +626,7 @@ typedef struct _message_store_t
int flags;
char previous[k_id_base64_len];
char author[k_id_base64_len];
int64_t sequence;
int32_t sequence;
double timestamp;
const char* content;
size_t length;
@ -729,9 +762,9 @@ void tf_ssb_db_store_message(
const char* author = JS_ToCString(context, authorval);
JS_FreeValue(context, authorval);
int64_t sequence = -1;
int32_t sequence = -1;
JSValue sequenceval = JS_GetPropertyStr(context, val, "sequence");
JS_ToInt64(context, &sequence, sequenceval);
JS_ToInt32(context, &sequence, sequenceval);
JS_FreeValue(context, sequenceval);
double timestamp = -1.0;
@ -757,10 +790,10 @@ void tf_ssb_db_store_message(
.callback = callback,
.user_data = user_data,
};
snprintf(store->id, sizeof(store->id), "%s", id);
snprintf(store->previous, sizeof(store->previous), "%s", previous ? previous : "");
snprintf(store->author, sizeof(store->author), "%s", author);
snprintf(store->signature, sizeof(store->signature), "%s", signature);
tf_string_set(store->id, sizeof(store->id), id);
tf_string_set(store->previous, sizeof(store->previous), previous ? previous : "");
tf_string_set(store->author, sizeof(store->author), author);
tf_string_set(store->signature, sizeof(store->signature), signature);
JS_FreeCString(context, author);
JS_FreeCString(context, previous);
@ -917,7 +950,7 @@ void tf_ssb_db_blob_get_async(tf_ssb_t* ssb, const char* id, tf_ssb_db_blob_get_
.callback = callback,
.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);
}
@ -1014,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)
{
snprintf(out_id, out_id_size, "%s", id);
tf_string_set(out_id, out_id_size, id);
}
if (out_new)
{
@ -1023,7 +1056,7 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
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)
{
bool found = false;
@ -1032,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);
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)
{
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)
{
@ -1049,7 +1082,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
}
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)
@ -1062,11 +1095,11 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
}
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)
{
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)
{
@ -1084,7 +1117,7 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
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;
sqlite3_stmt* statement;
@ -1099,7 +1132,7 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
{
if (out_sequence)
{
*out_sequence = sqlite3_column_int64(statement, 1);
*out_sequence = sqlite3_column_int(statement, 1);
}
if (out_message_id)
{
@ -1123,7 +1156,7 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
{
if (out_sequence)
{
*out_sequence = sqlite3_column_int64(statement, 0);
*out_sequence = sqlite3_column_int(statement, 0);
}
found = true;
}
@ -1301,19 +1334,19 @@ JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue bi
}
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);
JS_SetPropertyStr(context, value, "previous", (previous && *previous) ? JS_NewString(context, previous) : JS_NULL);
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));
}
else
{
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, "hash", JS_NewString(context, hash));
@ -1520,14 +1553,14 @@ typedef struct _following_t following_t;
typedef struct _following_t
{
char id[k_id_base64_len];
bool populated;
int depth;
following_t** following;
following_t** blocking;
int following_count;
int blocking_count;
int depth;
int ref_count;
int block_ref_count;
bool populated;
} following_t;
static int _following_compare(const void* a, const void* b)
@ -1619,7 +1652,7 @@ static following_t* _make_following_node(const char* id, following_t*** followin
(*following_count)++;
memset(entry, 0, sizeof(*entry));
entry->depth = INT_MAX;
snprintf(entry->id, sizeof(entry->id), "%s", id);
tf_string_set(entry->id, sizeof(entry->id), id);
}
return entry;
}
@ -1713,7 +1746,7 @@ static sqlite3_stmt* _make_following_statement(sqlite3* db)
"SELECT content ->> '$.contact' AS contact, content ->> '$.following', content ->> '$.blocking' "
"FROM messages "
"WHERE author = ? AND content ->> '$.type' = 'contact' AND contact IS NOT NULL "
"ORDER BY content ->> '$.contact', sequence",
"ORDER BY sequence",
-1, &statement, NULL) != SQLITE_OK)
{
tf_printf("prepare failed: %s", sqlite3_errmsg(db));
@ -1746,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));
memset(result, 0, sizeof(tf_ssb_following_t) * (actual_following_count + 1));
int write_index = 0;
for (int i = 0; i < following_count; i++)
{
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].following_count = following[i]->following_count;
result[write_index].blocking_count = following[i]->blocking_count;
result[write_index].followed_by_count = following[i]->ref_count;
result[write_index].blocked_by_count = following[i]->block_ref_count;
result[write_index].depth = following[i]->depth;
result[write_index] = (tf_ssb_following_t) {
.following_count = following[i]->following_count,
.blocking_count = following[i]->blocking_count,
.followed_by_count = following[i]->ref_count,
.blocked_by_count = following[i]->block_ref_count,
.depth = following[i]->depth,
};
tf_string_set(result[write_index].id, sizeof(result[write_index].id), following[i]->id);
write_index++;
}
}
result[write_index] = (tf_ssb_following_t) { 0 };
for (int i = 0; i < following_count; i++)
{
@ -1808,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)
{
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++;
}
}
@ -1868,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 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));
if (is_keys)
{
@ -1907,8 +1941,8 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
.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));
snprintf(result[count].pubkey, sizeof(result[count].pubkey), "%s", (const char*)sqlite3_column_text(statement, 2));
tf_string_set(result[count].address, sizeof(result[count].address), (const char*)sqlite3_column_text(statement, 0));
tf_string_set(result[count].pubkey, sizeof(result[count].pubkey), (const char*)sqlite3_column_text(statement, 2));
count++;
}
sqlite3_finalize(statement);
@ -1946,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)
{
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;
}
}
@ -2144,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 &&
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;
}
sqlite3_finalize(statement);
@ -2256,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);
}
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);
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))
{
for (int64_t i = 1; i <= sequence; i++)
for (int32_t i = 1; i <= sequence; i++)
{
char message_id[k_id_base64_len];
char previous[256];
@ -2282,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),
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;
}
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)
{
_tf_ssb_db_set_flags(ssb, message_id, calculated_flags);
@ -2299,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))
{
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;
}
JS_FreeValue(context, message);
@ -2312,7 +2346,7 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id, int64_t debug_sequence, boo
}
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;
break;
}
@ -2414,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)
{
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;
}
}
@ -2426,7 +2460,7 @@ bool tf_ssb_db_get_global_setting_string(sqlite3* db, const char* name, char* ou
}
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;
}
@ -2708,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));
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);

View File

@ -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.
** @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);
/**
@ -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.
** @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.
@ -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.
*/
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. */
typedef struct _tf_ssb_following_t
@ -460,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.
** @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.

View File

@ -102,7 +102,7 @@ static ebt_entry_t* _ebt_get_entry(tf_ssb_ebt_t* ebt, const char* id)
.in = -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++;
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))
{
JSValue key = JS_AtomToString(context, ptab[i].atom);
const char* author = JS_ToCString(context, key);
int64_t sequence = -1;
JS_ToInt64(context, &sequence, in_clock);
const char* author = JS_AtomToCString(context, ptab[i].atom);
int32_t sequence = -1;
JS_ToInt32(context, &sequence, in_clock);
ebt_entry_t* entry = _ebt_get_entry(ebt, author);
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_FreeValue(context, key);
}
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));
}
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;
}
}
@ -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);
if (visible)
{
int64_t* sequences = NULL;
int32_t* sequences = NULL;
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);
sequences = tf_resize_vec(sequences, (i + 1) * sizeof(int64_t));
sequences = tf_resize_vec(sequences, (i + 1) * sizeof(int32_t));
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] = "";
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);
uv_mutex_lock(&work->ebt->mutex);
_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[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++;
}
}
@ -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->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;
}
}
@ -350,7 +348,7 @@ tf_ssb_ebt_clock_t* tf_ssb_ebt_get_messages_to_send(tf_ssb_ebt_t* ebt)
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);
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);
}
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);
ebt_entry_t* entry = _ebt_get_entry(ebt, id);

View File

@ -19,7 +19,7 @@ typedef struct _tf_ssb_ebt_clock_entry_t
/** The identity. */
char id[k_id_base64_len];
/** The sequence number. */
int64_t value;
int32_t value;
} 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 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.
@ -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 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.

View File

@ -142,8 +142,7 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
JSPropertyDescriptor desc;
if (JS_GetOwnProperty(context, &desc, files, ptab[i].atom) == 1)
{
JSValue key = JS_AtomToString(context, ptab[i].atom);
const char* file_name = JS_ToCString(context, key);
const char* file_name = JS_AtomToCString(context, ptab[i].atom);
const char* blob_id = JS_ToCString(context, desc.value);
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_FreeValue(context, key);
JS_FreeCString(context, blob_id);
JS_FreeValue(context, desc.value);
JS_FreeValue(context, desc.setter);

View File

@ -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.
** @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.
@ -651,7 +651,7 @@ void tf_ssb_remove_broadcasts_changed_callback(tf_ssb_t* ssb, tf_ssb_broadcasts_
** @param id The message identifier.
** @param user_data The user data.
*/
typedef void(tf_ssb_message_added_callback_t)(tf_ssb_t* ssb, const char* 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.
@ -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 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.
@ -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);
/**
** Register a callback to be called when the main thread blocks for an
** unreasonable amount of time.
** @param ssb The SSB instance.
** @param callback The callback to call.
** @param user_data User data to pass to the callback.
*/
void tf_ssb_set_hitch_callback(tf_ssb_t* ssb, void (*callback)(const char* name, uint64_t duration_ns, void* user_data), void* user_data);
/**
** Get the queue of messages in the progress of being stored.
** @param ssb The SSB instance.

View File

@ -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);
*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);
result = JS_NewPromiseCapability(context, work->promise);
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_step(statement) == SQLITE_DONE && sqlite3_changes(db) == 1)
{
error = NULL;
if (sqlite3_exec(db, "COMMIT TRANSACTION", NULL, NULL, &error) != SQLITE_OK)
char* commit_error = NULL;
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
@ -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));
}
if (error)
{
sqlite3_free(error);
}
}
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);
*work = (swap_with_server_identity_t) { 0 };
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);
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);
@ -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);
*work = (get_private_key_t) { .context = context };
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);
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];
bool got_private_key;
char previous_id[512];
int64_t previous_sequence;
int32_t previous_sequence;
JSContext* context;
JSValue promise[2];
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);
*work = (append_message_t) { .context = context, .message = JS_DupValue(context, argv[2]) };
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, user);
@ -1596,7 +1604,7 @@ static void _tf_ssb_cleanup_value(tf_ssb_t* ssb, void* user_data)
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);
JSValue callback = JS_MKPTR(JS_TAG_OBJECT, user_data);

View File

@ -16,7 +16,7 @@
#include <time.h>
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_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);
@ -134,7 +134,7 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
*work = (blobs_get_work_t) {
.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);
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) {
.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);
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)
{
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++;
}
}
@ -270,7 +270,7 @@ static void _tf_ssb_request_blob_wants_after_work(tf_ssb_connection_t* connectio
}
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);
@ -349,11 +349,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)
{
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);
JSValue arg_array = JS_GetPropertyStr(context, args, "args");
@ -364,54 +359,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))
{
const char* target_str = JS_ToCString(context, target);
tf_ssb_connection_t* target_connection = tf_ssb_connection_get(ssb, target_str);
if (target_connection)
if (!tf_ssb_is_room(ssb))
{
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);
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "tunnel.connect");
}
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))
{
@ -611,7 +614,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);
*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);
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
@ -713,7 +716,7 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
{
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;
JSValue key_value = JS_NULL;
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
@ -722,7 +725,6 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
JS_FreeValue(context, desc.setter);
JS_FreeValue(context, desc.getter);
}
const char* blob_id = JS_ToCString(context, key);
int64_t size = 0;
JS_ToInt64(context, &size, key_value);
if (--blob_wants->wants_sent == 0)
@ -736,18 +738,17 @@ static void _tf_ssb_rpc_connection_blobs_createWants_callback(
.connection = connection,
.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);
}
else
{
blob_get_t* get = tf_malloc(sizeof(blob_get_t));
*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);
}
JS_FreeCString(context, blob_id);
JS_FreeValue(context, key);
JS_FreeValue(context, key_value);
}
for (uint32_t i = 0; i < plen; ++i)
@ -862,13 +863,13 @@ typedef struct _tf_ssb_connection_send_history_stream_t
{
int32_t request_number;
char author[k_id_base64_len];
int64_t sequence;
int32_t sequence;
bool keys;
bool live;
bool end_request;
bool out_finished;
int64_t out_max_sequence_seen;
int32_t out_max_sequence_seen;
char** out_messages;
int out_messages_count;
} tf_ssb_connection_send_history_stream_t;
@ -889,8 +890,8 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
"sequence < ?3 ORDER BY sequence",
-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 &&
sqlite3_bind_int64(statement, 3, request->sequence + k_max) == 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_int(statement, 3, request->sequence + k_max) == SQLITE_OK)
{
JSMallocFunctions funcs = { 0 };
tf_get_js_malloc_functions(&funcs);
@ -901,7 +902,7 @@ static void _tf_ssb_connection_send_history_stream_work(tf_ssb_connection_t* con
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
{
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),
sqlite3_column_int64(statement, 3), sqlite3_column_double(statement, 4), (const char*)sqlite3_column_text(statement, 5),
@ -996,7 +997,7 @@ static void _tf_ssb_connection_send_history_stream_callback(tf_ssb_connection_t*
}
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))
{
@ -1008,7 +1009,7 @@ static void _tf_ssb_connection_send_history_stream(
.live = live,
.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);
}
}
@ -1035,8 +1036,8 @@ static void _tf_ssb_rpc_createHistoryStream(
JSValue live = JS_GetPropertyStr(context, arg, "live");
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;
int64_t sequence = 0;
JS_ToInt64(context, &sequence, seq);
int32_t sequence = 0;
JS_ToInt32(context, &sequence, seq);
const char* author = JS_ToCString(context, id);
_tf_ssb_connection_send_history_stream(connection, -request_number, author, sequence, is_keys, is_live, true);
@ -1257,7 +1258,7 @@ typedef struct _invite_use_t
;
uint8_t private_key[512];
char previous_id[64];
int64_t previous_sequence;
int32_t previous_sequence;
char host[256];
int port;
@ -1337,7 +1338,7 @@ static void _tf_ssb_rpc_invite_use_callback(
.ssb = ssb,
.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_get_private_key(ssb, work->private_key, sizeof(work->private_key));
tf_ssb_connection_get_id(connection, work->pub, sizeof(work->pub));
@ -1463,23 +1464,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);
}
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
{
int deleted;
@ -1495,58 +1479,91 @@ static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
tf_ssb_release_db_reader(ssb, db);
if (age <= 0)
{
_tf_ssb_rpc_checkpoint(ssb);
return;
}
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 timestamp = now - age * 1000ULL;
const int k_limit = 128;
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)
{
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);
}
sqlite3_stmt* statement;
char** ids = NULL;
int ids_count = 0;
db = tf_ssb_acquire_db_reader(ssb);
if (sqlite3_prepare_v2(db,
"DELETE FROM blobs WHERE blobs.id IN ("
" SELECT blobs.id FROM blobs "
" JOIN messages_refs ON blobs.id = messages_refs.ref "
" JOIN messages ON messages.id = messages_refs.message "
" WHERE blobs.created < ?1 / 1000 "
" GROUP BY blobs.id HAVING MAX(messages.timestamp) < ?1 LIMIT ?2)",
"SELECT blobs.id FROM blobs "
"JOIN messages_refs ON blobs.id = messages_refs.ref "
"JOIN messages ON messages.id = messages_refs.message "
"WHERE blobs.created < ?1 / 1000 "
"GROUP BY blobs.id HAVING MAX(messages.timestamp) < ?1 LIMIT ?2",
-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)
{
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)
{
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
{
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;
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)
@ -1602,28 +1619,57 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
JS_FreeValue(context, json);
JS_FreeValue(context, array);
db = tf_ssb_acquire_db_writer(ssb);
sqlite3_stmt* statement;
if (sqlite3_prepare_v2(db,
"DELETE FROM messages WHERE id IN ("
" SELECT id FROM messages WHERE author NOT IN (SELECT value FROM json_each(?)) ORDER BY rowid DESC LIMIT 1024"
")",
-1, &statement, NULL) == SQLITE_OK)
char** ids = NULL;
int ids_count = 0;
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_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));
}
else
{
delete->deleted += sqlite3_changes(db);
}
}
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);
@ -1632,7 +1678,6 @@ static void _tf_ssb_rpc_delete_feeds_work(tf_ssb_t* ssb, void* user_data)
delete->duration_ms = (uv_hrtime() - start_ns) / 1000000LL;
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)
@ -1736,7 +1781,7 @@ static void _tf_ssb_rpc_peers_exchange_internal(
{
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;
JSValue key_value = JS_NULL;
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
@ -1745,12 +1790,10 @@ static void _tf_ssb_rpc_peers_exchange_internal(
JS_FreeValue(context, desc.setter);
JS_FreeValue(context, desc.getter);
}
const char* connection = JS_ToCString(context, key);
int64_t timestamp = 0;
JS_ToInt64(context, &timestamp, key_value);
/* ADD BROADCAST connection: timestamp */
JS_FreeCString(context, connection);
JS_FreeValue(context, key);
JS_FreeValue(context, key_value);
}
for (uint32_t i = 0; i < plen; ++i)
@ -1801,7 +1844,7 @@ typedef struct _invite_t
int32_t request_number;
bool accepted;
char previous_id[256];
int64_t previous_sequence;
int32_t previous_sequence;
char* message;
} invite_t;
@ -1895,7 +1938,7 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
JSValue feed = JS_GetPropertyStr(context, object, "feed");
tf_ssb_connection_get_id(connection, work->invite_public_key, sizeof(work->invite_public_key));
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_FreeValue(context, feed);
JS_FreeValue(context, object);
@ -1903,7 +1946,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);
}
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];
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));

View File

@ -114,7 +114,7 @@ static int _ssb_test_count_messages(tf_ssb_t* ssb)
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;
}
@ -811,7 +811,7 @@ static void _break_in_a_bit(tf_ssb_t* ssb, tf_ssb_connection_t* connection, cons
.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_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)
{
tf_ssb_t* ssb = user_data;
int64_t sequence = -1;
int32_t sequence = -1;
char id[k_id_base64_len] = { 0 };
snprintf(id, sizeof(id), "@%s", identity);
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)
@ -1154,7 +1154,7 @@ void tf_ssb_test_replicate(const tf_test_options_t* options)
JSValue obj = JS_NewObject(context1);
JS_SetPropertyStr(context1, obj, "type", JS_NewString(context1, "contact"));
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];
snprintf(contact, sizeof(contact), "@%s", public[0]);
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)
{
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(ssb1, false);
@ -1434,9 +1436,9 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
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);
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_printf("max_sequence=%" PRId64 "\n", max_sequence);
tf_printf("max_sequence=%d\n", max_sequence);
assert(max_sequence == 5);
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;
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);
tf_ssb_acquire_db_writer(ssb0);
@ -1454,7 +1456,7 @@ void tf_ssb_test_triggers(const tf_test_options_t* options)
max_sequence = 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);
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();
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);
while (ids[count])

View File

@ -84,16 +84,10 @@ typedef struct _promise_stack_t
uint32_t hash;
int count;
const char* stack;
void* cstack[32];
void* cstack[31];
int cstack_count;
} 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
@ -170,8 +164,6 @@ typedef struct _tf_task_t
timeout_t* timeouts;
hitch_t hitches[32];
uint64_t last_gc_ns;
int64_t last_gc_duration_ns;
} tf_task_t;
@ -558,6 +550,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);
tf_task_check_jobs(to);
tf_trace_end(to->_trace);
JS_FreeValue(to->_context, this_val);
@ -930,28 +923,6 @@ char* tf_task_get_debug(tf_task_t* task)
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)
{
tf_task_t* task = JS_GetContextOpaque(context);
@ -1196,7 +1167,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);
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);
return result;
@ -1250,6 +1221,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));
}
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 };
memcpy(task->_promise_stacks[index].cstack, buffer, sizeof(void*) * count);
task->_promise_stack_count++;
@ -1291,7 +1263,7 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
size_t length = 0;
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
stack_hash = tf_util_fnv32a((const void*)stack, (int)length, 0);
void* buffer[32];
void* buffer[31];
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
stack_hash = tf_util_fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
_add_promise_stack(task, stack_hash, stack, buffer, count);
@ -1301,19 +1273,19 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
JS_FreeValue(task->_context, error);
}
promiseid_t promiseId;
promiseid_t promise_id;
do
{
promiseId = task->_nextPromise++;
} while (_tf_task_find_promise(task, promiseId) || !promiseId);
promise_id = task->_nextPromise++;
} while (_tf_task_find_promise(task, promise_id) || !promise_id);
promise_t promise = {
.id = promiseId,
.id = promise_id,
.values = { JS_NULL, JS_NULL },
.stack_hash = stack_hash,
};
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));
if (task->_promise_count - index)
{
@ -1321,7 +1293,12 @@ JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
}
task->_promises[index] = promise;
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;
}
@ -1383,7 +1360,7 @@ static void _promise_release_for_task(tf_task_t* task, taskid_t task_id)
const promise_t* promise = &task->_promises[i];
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;
}
}
@ -1661,24 +1638,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);
}
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)
{
assert(!task->_activated);
@ -1712,7 +1671,6 @@ void tf_task_activate(tf_task_t* task)
tf_ssb_set_trace(task->_ssb, task->_trace);
tf_ssb_register(context, task->_ssb);
tf_api_register(context);
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
if (task->_args)
{
@ -1819,6 +1777,11 @@ JSValue tf_taskstub_kill(tf_taskstub_t* stub);
void tf_task_destroy(tf_task_t* task)
{
if (!task->_shutting_down)
{
tf_printf("tf_task_destroy\n");
}
task->_shutting_down = true;
while (task->_children)
@ -2005,7 +1968,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)
{
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)
@ -2015,7 +1978,7 @@ void tf_task_set_zip_path(tf_task_t* task, const char* zip_path)
unzClose(task->_zip);
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)
{
task->_zip = unzOpen(zip_path);
@ -2025,7 +1988,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)
{
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)

View File

@ -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);
/**
** 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.
** @param pipe_fd A file descriptor with which to communicate with the invoking

View File

@ -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)
{
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)

View File

@ -500,11 +500,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 array_buffer = JS_NewArrayBufferCopy(context, data, size);
JSValue global = JS_GetGlobalObject(context);
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
JSValue result = JS_CallConstructor(context, constructor, 1, &array_buffer);
JS_FreeValue(context, constructor);
JS_FreeValue(context, global);
JSValue args[] = {
array_buffer,
JS_NewInt64(context, 0),
JS_NewInt64(context, size),
};
JSValue result = JS_NewTypedArray(context, tf_countof(args), args, JS_TYPED_ARRAY_UINT8C);
JS_FreeValue(context, array_buffer);
return result;
}
@ -612,7 +613,7 @@ static int _tf_util_backtrace_single_callback(void* data, uintptr_t pc, const ch
{
char** stack = data;
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;
*stack = tf_resize_vec(*stack, current + length + 1);
memcpy(*stack + current, line, length + 1);
@ -700,3 +701,18 @@ uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start)
}
return result;
}
size_t tf_string_set(char* buffer, size_t size, const char* string)
{
size_t length = string ? strlen(string) : 0;
length = tf_min(length, size - 1);
if (size)
{
if (length)
{
memcpy(buffer, string, length);
}
buffer[length] = 0;
}
return length;
}

View File

@ -233,4 +233,13 @@ bool tf_util_is_mobile();
*/
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start);
/**
** Populate a string buffer, truncating if necessary.
** @param buffer The buffer.
** @param size The size of the buffer.
** @param string The value to set.
** @return The number of bytes set, not including the NULL terminator.
*/
size_t tf_string_set(char* buffer, size_t size, const char* string);
/** @} */

View File

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