42 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
43 changed files with 2728 additions and 706 deletions

View File

@@ -18,7 +18,7 @@ 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
@@ -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": "&i6mkfmh4sW8PpILznWL8VxDzI3MX6V5OJWDs47Qxias=.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

@@ -308,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) {
@@ -328,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]]
);
@@ -354,55 +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
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' = 'vote' AND
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

@@ -632,8 +632,10 @@ class TfMessageElement extends LitElement {
}
allow_unread() {
return;
!this.channel.startsWith('@') && !this.channel.startsWith('%');
return (
this.channel == '@' ||
(!this.channel.startsWith('@') && !this.channel.startsWith('%'))
);
}
render_unread_icon() {

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,7 +208,7 @@ 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]
);
@@ -276,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;
@@ -425,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"
@@ -459,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}
@@ -475,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
@@ -240,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",

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

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

@@ -232,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");
@@ -495,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)
@@ -507,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;
@@ -519,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;
@@ -534,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)
@@ -568,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;
}
@@ -625,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;
@@ -761,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;
@@ -789,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);
@@ -949,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);
}
@@ -1046,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)
{
@@ -1055,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;
@@ -1064,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)
{
@@ -1081,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)
@@ -1094,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)
{
@@ -1116,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;
@@ -1131,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)
{
@@ -1155,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;
}
@@ -1333,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));
@@ -1552,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)
@@ -1651,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;
}
@@ -1745,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));
@@ -1778,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++)
{
@@ -1840,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++;
}
}
@@ -1900,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)
{
@@ -1939,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);
@@ -1978,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;
}
}
@@ -2176,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);
@@ -2288,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];
@@ -2314,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);
@@ -2331,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);
@@ -2344,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;
}
@@ -2446,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;
}
}
@@ -2458,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;
}
@@ -2740,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);
@@ -353,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);
@@ -489,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);
@@ -696,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;
@@ -766,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);
@@ -1604,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));
@@ -1481,51 +1482,86 @@ static void _tf_ssb_rpc_delete_blobs_work(tf_ssb_t* ssb, void* user_data)
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);
}
@@ -1583,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);
@@ -1716,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)
@@ -1725,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)
@@ -1781,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;
@@ -1875,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);
@@ -1883,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);
@@ -1666,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);
@@ -1717,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)
{
@@ -2015,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)
@@ -2025,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);
@@ -2035,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."