Compare commits
16 Commits
Author | SHA1 | Date | |
---|---|---|---|
f0452704a1 | |||
b8b1f1ba80 | |||
caf7478da4 | |||
0e40ba78a4 | |||
d1eac6c9eb | |||
8f5201b2bc | |||
6022001d66 | |||
f018c367ed | |||
48c47f097a | |||
39ac215b5a | |||
7d562ce85c | |||
51b317233a | |||
87ce715011 | |||
ef5afc1e23 | |||
486212f22a | |||
0e8867dd6e |
20
Makefile
20
Makefile
@ -3,6 +3,10 @@
|
||||
MAKEFLAGS += --warn-undefined-variables
|
||||
MAKEFLAGS += --no-builtin-rules
|
||||
|
||||
VERSION_CODE := 8
|
||||
VERSION_NUMBER := 0.0.8
|
||||
VERSION_NAME := The secret ingredient is love.
|
||||
|
||||
PROJECT = tildefriends
|
||||
BUILD_DIR ?= out
|
||||
BUILD_TYPES := debug release windebug winrelease androiddebug androidrelease androiddebug-x86_64 androidrelease-x86_64
|
||||
@ -432,6 +436,18 @@ endef
|
||||
|
||||
$(foreach build_type,$(BUILD_TYPES),$(eval $(call build_rules,$(build_type))))
|
||||
|
||||
src/version.h : $(firstword $(MAKEFILE_LIST))
|
||||
@echo [version] $@
|
||||
@echo "#define VERSION_NUMBER \"$(VERSION_NUMBER)\"\n#define VERSION_NAME \"$(VERSION_NAME)\"\n" > $@
|
||||
|
||||
src/android/AndroidManifest.xml : $(firstword $(MAKEFILE_LIST))
|
||||
@echo [android_version] $@
|
||||
@sed -i \
|
||||
-e 's/versionCode=".*"/versionCode="$(VERSION_CODE)"/' \
|
||||
-e 's/versionName=".*"/versionName="$(VERSION_NUMBER)"/' \
|
||||
-e 's/android:minSdkVersion=".*"/android:minSdkVersion="$(ANDROID_MIN_SDK_VERSION)"/' \
|
||||
$@
|
||||
|
||||
# Android support.
|
||||
out/res/layout_activity_main.xml.flat: src/android/res/layout/activity_main.xml
|
||||
@mkdir -p $(dir $@)
|
||||
@ -491,10 +507,10 @@ out/%.apk: out/apk/%.unsigned.apk
|
||||
@echo [apksigner] $(notdir $@)
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks keystore.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --out $@ $<
|
||||
|
||||
apk: out/TildeFriends-debug.apk
|
||||
apk: out/TildeFriends-release.apk
|
||||
.PHONY: apk
|
||||
|
||||
apkgo: out/TildeFriends-debug.apk
|
||||
apkgo: out/TildeFriends-release.apk
|
||||
@adb install $<
|
||||
@adb shell am start com.unprompted.tildefriends/.MainActivity
|
||||
.PHONY: apkgo
|
||||
|
2
apps/sneaker/lit-all.min.js
vendored
2
apps/sneaker/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
apps/ssb/lit-all.min.js
vendored
2
apps/ssb/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -8,7 +8,9 @@ import * as tf_user from './tf-user.js';
|
||||
import * as tf_compose from './tf-compose.js';
|
||||
import * as tf_news from './tf-news.js';
|
||||
import * as tf_profile from './tf-profile.js';
|
||||
import * as tf_tab_mentions from './tf-tab-mentions.js';
|
||||
import * as tf_tab_news from './tf-tab-news.js';
|
||||
import * as tf_tab_news_feed from './tf-tab-news-feed.js';
|
||||
import * as tf_tab_search from './tf-tab-search.js';
|
||||
import * as tf_tab_connections from './tf-tab-connections.js';
|
||||
import * as tf_tag from './tf-tag.js';
|
@ -16,6 +16,7 @@ class TfElement extends LitElement {
|
||||
following: {type: Array},
|
||||
users: {type: Object},
|
||||
ids: {type: Array},
|
||||
tags: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
@ -32,6 +33,7 @@ class TfElement extends LitElement {
|
||||
this.following = [];
|
||||
this.users = {};
|
||||
this.loaded = false;
|
||||
this.tags = [];
|
||||
tfrpc.rpc.getBroadcasts().then(b => { self.broadcasts = b || []; });
|
||||
tfrpc.rpc.getConnections().then(c => { self.connections = c || []; });
|
||||
tfrpc.rpc.getHash().then(hash => self.set_hash(hash));
|
||||
@ -64,6 +66,8 @@ class TfElement extends LitElement {
|
||||
this.tab = 'search';
|
||||
} else if (this.hash === '#connections') {
|
||||
this.tab = 'connections';
|
||||
} else if (this.hash === '#mentions') {
|
||||
this.tab = 'mentions';
|
||||
} else {
|
||||
this.tab = 'news';
|
||||
}
|
||||
@ -251,12 +255,24 @@ class TfElement extends LitElement {
|
||||
`;
|
||||
}
|
||||
|
||||
async load_recent_tags() {
|
||||
this.tags = await tfrpc.rpc.query(`
|
||||
WITH recent AS (SELECT '#' || json_extract(content, '$.channel') AS tag
|
||||
FROM messages
|
||||
WHERE json_extract(content, '$.channel') IS NOT NULL
|
||||
ORDER BY timestamp DESC LIMIT 100)
|
||||
SELECT tag, COUNT(*) AS count FROM recent GROUP BY tag ORDER BY count DESC LIMIT 10
|
||||
`, []);
|
||||
}
|
||||
|
||||
async load() {
|
||||
let whoami = this.whoami;
|
||||
let tags = this.load_recent_tags();
|
||||
let [following, users] = await this.following_deep([whoami], 2, {});
|
||||
users = await this.fetch_about(following.sort(), users);
|
||||
this.following = following;
|
||||
this.users = users;
|
||||
await tags;
|
||||
console.log(`load finished ${whoami} => ${this.whoami}`);
|
||||
this.whoami = whoami;
|
||||
this.loaded = whoami;
|
||||
@ -273,6 +289,10 @@ class TfElement extends LitElement {
|
||||
return html`
|
||||
<tf-tab-connections .users=${this.users} .connections=${this.connections} .broadcasts=${this.broadcasts}></tf-tab-connections>
|
||||
`;
|
||||
} else if (this.tab === 'mentions') {
|
||||
return html`
|
||||
<tf-tab-mentions .following=${this.following} whoami=${this.whoami} .users=${this.users}}></tf-tab-mentions>
|
||||
`;
|
||||
} else if (this.tab === 'search') {
|
||||
return html`
|
||||
<tf-tab-search .following=${this.following} whoami=${this.whoami} .users=${this.users} query=${this.hash?.startsWith('#q=') ? decodeURIComponent(this.hash.substring(3)) : null}></tf-tab-search>
|
||||
@ -286,6 +306,8 @@ class TfElement extends LitElement {
|
||||
await tfrpc.rpc.setHash('#');
|
||||
} else if (tab === 'connections') {
|
||||
await tfrpc.rpc.setHash('#connections');
|
||||
} else if (tab === 'mentions') {
|
||||
await tfrpc.rpc.setHash('#mentions');
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,6 +326,7 @@ class TfElement extends LitElement {
|
||||
<div>
|
||||
<input type="button" class="tab" value="News" ?disabled=${self.tab == 'news'} @click=${() => self.set_tab('news')}></input>
|
||||
<input type="button" class="tab" value="Connections" ?disabled=${self.tab == 'connections'} @click=${() => self.set_tab('connections')}></input>
|
||||
<input type="button" class="tab" value="Mentions" ?disabled=${self.tab == 'mentions'} @click=${() => self.set_tab('mentions')}></input>
|
||||
<input type="button" class="tab" value="Search" ?disabled=${self.tab == 'search'} @click=${() => self.set_tab('search')}></input>
|
||||
</div>
|
||||
`;
|
||||
@ -316,6 +339,7 @@ class TfElement extends LitElement {
|
||||
return html`
|
||||
${this.render_id_picker()}
|
||||
${tabs}
|
||||
${this.tags.map(x => html`<tf-tag tag=${x.tag} count=${x.count}></tf-tag>`)}
|
||||
${contents}
|
||||
`;
|
||||
}
|
||||
|
@ -220,6 +220,26 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
render_channels() {
|
||||
let content = this.message?.content;
|
||||
if (this.decrypted?.type == 'post') {
|
||||
content = this.decrypted;
|
||||
}
|
||||
let channels = [];
|
||||
if (typeof content.channel === 'string') {
|
||||
channels.push(`#${content.channel}`);
|
||||
}
|
||||
if (Array.isArray(content.mentions)) {
|
||||
for (let mention of content.mentions) {
|
||||
if (typeof mention?.link === 'string' &&
|
||||
mention.link.startsWith('#')) {
|
||||
channels.push(mention.link);
|
||||
}
|
||||
}
|
||||
}
|
||||
return channels.map(x => html`<tf-tag tag=${x}></tf-tag>`);
|
||||
}
|
||||
|
||||
async try_decrypt(content) {
|
||||
let result = await tfrpc.rpc.try_decrypt(this.whoami, content);
|
||||
if (result) {
|
||||
@ -330,6 +350,7 @@ class TfMessageElement extends LitElement {
|
||||
`;
|
||||
let content_html =
|
||||
html`
|
||||
${this.render_channels()}
|
||||
<div @click=${this.body_click}>${body}</div>
|
||||
${this.render_mentions()}
|
||||
`;
|
||||
|
65
apps/ssb/tf-tab-mentions.js
Normal file
65
apps/ssb/tf-tab-mentions.js
Normal file
@ -0,0 +1,65 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
class TfTabMentionsElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
whoami: {type: String},
|
||||
users: {type: Object},
|
||||
following: {type: Array},
|
||||
expanded: {type: Object},
|
||||
messages: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
static styles = styles;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
let self = this;
|
||||
this.whoami = null;
|
||||
this.users = {};
|
||||
this.following = [];
|
||||
this.expanded = {};
|
||||
this.messages = [];
|
||||
}
|
||||
|
||||
async load() {
|
||||
console.log('Loading...', this.whoami);
|
||||
let results = await tfrpc.rpc.query(`
|
||||
SELECT messages.*
|
||||
FROM messages_fts(?)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?
|
||||
ORDER BY timestamp DESC limit 20
|
||||
`,
|
||||
['"' + this.whoami.replace('"', '""') + '"', JSON.stringify(this.following), this.whoami]);
|
||||
console.log('Done.');
|
||||
this.messages = results;
|
||||
}
|
||||
|
||||
on_expand(event) {
|
||||
if (event.detail.expanded) {
|
||||
let expand = {};
|
||||
expand[event.detail.id] = true;
|
||||
this.expanded = Object.assign({}, this.expanded, expand);
|
||||
} else {
|
||||
delete this.expanded[event.detail.id];
|
||||
this.expanded = Object.assign({}, this.expanded);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
if (!this.loading) {
|
||||
this.loading = true;
|
||||
this.load();
|
||||
}
|
||||
return html`
|
||||
<tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users} .expanded=${this.expanded} @tf-expand=${this.on_expand}></tf-news>
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define('tf-tab-mentions', TfTabMentionsElement);
|
@ -9,6 +9,7 @@ class TfTabSearchElement extends LitElement {
|
||||
users: {type: Object},
|
||||
following: {type: Array},
|
||||
query: {type: String},
|
||||
expanded: {type: Object},
|
||||
};
|
||||
}
|
||||
|
||||
@ -20,6 +21,7 @@ class TfTabSearchElement extends LitElement {
|
||||
this.whoami = null;
|
||||
this.users = {};
|
||||
this.following = [];
|
||||
this.expanded = {};
|
||||
}
|
||||
|
||||
async search(query) {
|
||||
@ -55,8 +57,20 @@ class TfTabSearchElement extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
on_expand(event) {
|
||||
if (event.detail.expanded) {
|
||||
let expand = {};
|
||||
expand[event.detail.id] = true;
|
||||
this.expanded = Object.assign({}, this.expanded, expand);
|
||||
} else {
|
||||
delete this.expanded[event.detail.id];
|
||||
this.expanded = Object.assign({}, this.expanded);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.query !== this.last_query) {
|
||||
this.last_query = this.query;
|
||||
this.search(this.query);
|
||||
}
|
||||
let self = this;
|
||||
@ -65,7 +79,7 @@ class TfTabSearchElement extends LitElement {
|
||||
<input type="text" id="search" value=${this.query} style="flex: 1" @keydown=${this.search_keydown}></input>
|
||||
<input type="button" value="Search" @click=${(event) => self.search(self.renderRoot.getElementById('search').value)}></input>
|
||||
</div>
|
||||
<tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users}></tf-news>
|
||||
<tf-news id="news" whoami=${this.whoami} .messages=${this.messages} .users=${this.users} .expanded=${this.expanded} @tf-expand=${this.on_expand}></tf-news>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
24
apps/ssb/tf-tag.js
Normal file
24
apps/ssb/tf-tag.js
Normal file
@ -0,0 +1,24 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
|
||||
class TfTagElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
tag: {type: String},
|
||||
count: {type: Number},
|
||||
};
|
||||
}
|
||||
|
||||
static styles = styles;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
render() {
|
||||
let number = this.count ? html` (${this.count})` : undefined;
|
||||
return html`<a href="#q=${this.tag}" style="display: inline-block; margin: 3px; border: 1px solid black; background-color: #444; padding: 4px; border-radius: 3px">${this.tag}${number}</a>`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define('tf-tag', TfTagElement);
|
@ -52,6 +52,8 @@ class TfNavigationElement extends LitElement {
|
||||
show_permissions: {type: Boolean},
|
||||
status: {type: Object},
|
||||
spark_lines: {type: Object},
|
||||
version: {type: Object},
|
||||
show_version: {type: Boolean},
|
||||
};
|
||||
}
|
||||
|
||||
@ -79,6 +81,9 @@ class TfNavigationElement extends LitElement {
|
||||
get_spark_line(key, options) {
|
||||
if (!this.spark_lines[key]) {
|
||||
let spark_line = document.createElement('tf-sparkline');
|
||||
spark_line.style.display = 'flex';
|
||||
spark_line.style.flexDirection = 'row';
|
||||
spark_line.style.flex = '0 100 10em';
|
||||
spark_line.title = key;
|
||||
if (options) {
|
||||
if (options.max) {
|
||||
@ -125,7 +130,8 @@ class TfNavigationElement extends LitElement {
|
||||
${k_global_style}
|
||||
</style>
|
||||
<div style="margin: 4px; display: flex; flex-direction: row; flex-wrap: nowrap; gap: 3px">
|
||||
<span>😎</span>
|
||||
<span style="cursor: pointer" @click=${() => this.show_version = !this.show_version}>😎</span>
|
||||
<span ?hidden=${!this.show_version} style="flex: 0 0; white-space: nowrap" title=${this.version?.name}>${this.version?.number}</span>
|
||||
<a accesskey="h" data-tip="Open home app." href="/" style="color: #fff; white-space: nowrap">TF</a>
|
||||
<a accesskey="a" data-tip="Open apps list." href="/~core/apps/">apps</a>
|
||||
<a accesskey="e" data-tip="Toggle the app editor." href="#" @click=${this.toggle_edit}>edit</a>
|
||||
@ -133,7 +139,7 @@ class TfNavigationElement extends LitElement {
|
||||
<span style="display: inline-block; vertical-align: top; white-space: pre; color: ${this.status.color ?? kErrorColor}">${this.status.message}</span>
|
||||
<span id="requests"></span>
|
||||
${this.render_permissions()}
|
||||
<span style="flex: 1; white-space: nowrap; overflow: hidden; margin: 0; padding: 0">${Object.keys(this.spark_lines).sort().map(x => this.spark_lines[x]).map(x => [x.dataset.emoji, x])}</span>
|
||||
<span style="flex: 1 1; display: flex; flex-direction: row; white-space: nowrap; margin: 0; padding: 0">${Object.keys(this.spark_lines).sort().map(x => this.spark_lines[x]).map(x => [x.dataset.emoji, x])}</span>
|
||||
<span style="flex: 0 0; white-space: nowrap">${this.render_login()}</span>
|
||||
</div>
|
||||
`;
|
||||
@ -315,7 +321,7 @@ class TfSparkLineElement extends LitElement {
|
||||
render() {
|
||||
let max = Math.round(10.0 * Math.max(...this.lines.map(line => line.values[line.values.length - 1]))) / 10.0;
|
||||
return html`
|
||||
<svg style="width: 10em; height: 1.4em; vertical-align: top; margin: 0; padding: 0; background: #000" viewBox="0 0 100 10" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg style="width-auto: object-fit: cover; margin: 0; padding: 0; background: #000" viewBox="0 0 100 10" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
|
||||
${this.lines.map(x => this.render_line(x))}
|
||||
<text x="0" y="1em" style="font: 8px sans-serif; fill: #fff">${max}</text>
|
||||
</svg>
|
||||
@ -765,6 +771,7 @@ function _receive_websocket_message(message) {
|
||||
if (window.location.hash) {
|
||||
send({event: "hashChange", hash: window.location.hash});
|
||||
}
|
||||
document.getElementsByTagName('tf-navigation')[0].version = message.version;
|
||||
send({action: 'enableStats', enabled: true});
|
||||
} else if (message && message.action == "ping") {
|
||||
send({action: "pong"});
|
||||
|
@ -456,7 +456,7 @@ async function getProcessBlob(blobId, key, options) {
|
||||
}
|
||||
broadcastEvent('onSessionBegin', [getUser(process, process)]);
|
||||
if (process.app) {
|
||||
process.app.send({action: "ready"});
|
||||
process.app.send({action: "ready", version: version()});
|
||||
process.sendPermissions();
|
||||
}
|
||||
await process.task.execute({name: appSourceName, source: appSource});
|
||||
|
2
deps/lit/lit-all.min.js
vendored
2
deps/lit/lit-all.min.js
vendored
File diff suppressed because one or more lines are too long
2
deps/lit/lit-all.min.js.map
vendored
2
deps/lit/lit-all.min.js.map
vendored
File diff suppressed because one or more lines are too long
1
deps/speedscope/demangle-cpp.1768f4cc.js.map
vendored
1
deps/speedscope/demangle-cpp.1768f4cc.js.map
vendored
File diff suppressed because one or more lines are too long
1
deps/speedscope/import.a03bf119.js.map
vendored
1
deps/speedscope/import.a03bf119.js.map
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
deps/speedscope/reset.8c46b7a1.css.map
vendored
1
deps/speedscope/reset.8c46b7a1.css.map
vendored
@ -1 +0,0 @@
|
||||
{"version":3,"sources":["reset.css"],"names":[],"mappings":"AAIA,2ZAaC,QAAS,CACT,SAAU,CACV,QAAS,CACT,cAAe,CACf,YAAa,CACb,sBACD,CAEA,8EAEC,aACD,CACA,KACC,aACD,CACA,MACC,eACD,CACA,aACC,WACD,CACA,oDAEC,UAAW,CACX,YACD,CACA,MACC,wBAAyB,CACzB,gBACD,CAIA,KACI,eAEJ,CACA,UAFI,WAKJ,CAHA,KAEI,aACJ","file":"reset.8c46b7a1.css","sourceRoot":"../../assets","sourcesContent":["/* http://meyerweb.com/eric/tools/css/reset/\n v2.0 | 20110126\n License: none (public domain)\n*/\nhtml, body, div, span, applet, object, iframe,\nh1, h2, h3, h4, h5, h6, p, blockquote, pre,\na, abbr, acronym, address, big, cite, code,\ndel, dfn, em, img, ins, kbd, q, s, samp,\nsmall, strike, strong, sub, sup, tt, var,\nb, u, i, center,\ndl, dt, dd, ol, ul, li,\nfieldset, form, label, legend,\ntable, caption, tbody, tfoot, thead, tr, th, td,\narticle, aside, canvas, details, embed,\nfigure, figcaption, footer, header, hgroup,\nmenu, nav, output, ruby, section, summary,\ntime, mark, audio, video {\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tfont-size: 100%;\n\tfont: inherit;\n\tvertical-align: baseline;\n}\n/* HTML5 display-role reset for older browsers */\narticle, aside, details, figcaption, figure,\nfooter, header, hgroup, menu, nav, section {\n\tdisplay: block;\n}\nbody {\n\tline-height: 1;\n}\nol, ul {\n\tlist-style: none;\n}\nblockquote, q {\n\tquotes: none;\n}\nblockquote:before, blockquote:after,\nq:before, q:after {\n\tcontent: '';\n\tcontent: none;\n}\ntable {\n\tborder-collapse: collapse;\n\tborder-spacing: 0;\n}\n\n/* Prevent overscrolling */\n/* https://stackoverflow.com/a/17899813 */\nhtml {\n overflow: hidden;\n height: 100%;\n}\nbody {\n height: 100%;\n overflow: auto;\n}"]}
|
1
deps/speedscope/source-map.438fa06b.js.map
vendored
1
deps/speedscope/source-map.438fa06b.js.map
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
deps/speedscope/speedscope.eee21de6.js.map
vendored
1
deps/speedscope/speedscope.eee21de6.js.map
vendored
File diff suppressed because one or more lines are too long
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.unprompted.tildefriends"
|
||||
versionCode="1"
|
||||
versionName="0.0.4">
|
||||
<uses-sdk android:minSdkVersion="16"/>
|
||||
versionCode="8"
|
||||
versionName="0.0.8">
|
||||
<uses-sdk android:minSdkVersion="26"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<application android:label="Tilde Friends" android:usesCleartextTraffic="true" android:debuggable="true">
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1"/>
|
||||
|
@ -38,6 +38,7 @@ import java.util.zip.ZipInputStream;
|
||||
public class MainActivity extends Activity {
|
||||
WebView web_view;
|
||||
String base_url;
|
||||
Process process;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -81,6 +82,8 @@ public class MainActivity extends Activity {
|
||||
String port_file_path = getFilesDir().toString() + "/port.txt";
|
||||
base_url = "http://127.0.0.1:12345/";
|
||||
|
||||
MainActivity activity = this;
|
||||
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@ -97,7 +100,9 @@ public class MainActivity extends Activity {
|
||||
if (event.context().toString().equals("port.txt")) {
|
||||
Log.w("tildefriends", "Observed file write: " + event.context().toString());
|
||||
base_url = "http://127.0.0.1:" + String.valueOf(read_port(port_file_path)) + "/";
|
||||
web_view.loadUrl(base_url);
|
||||
activity.runOnUiThread(() -> {
|
||||
web_view.loadUrl(base_url);
|
||||
});
|
||||
watcher.close();
|
||||
break;
|
||||
}
|
||||
@ -122,15 +127,11 @@ public class MainActivity extends Activity {
|
||||
builder.directory(getFilesDir());
|
||||
builder.inheritIO();
|
||||
try {
|
||||
builder.start();
|
||||
process = builder.start();
|
||||
} catch (java.io.IOException e) {
|
||||
Log.w("tildefriends", "IOException starting process: " + e.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (java.lang.InterruptedException e) {
|
||||
}
|
||||
web_view.getSettings().setJavaScriptEnabled(true);
|
||||
web_view.getSettings().setDatabaseEnabled(true);
|
||||
web_view.getSettings().setDomStorageEnabled(true);
|
||||
@ -188,6 +189,18 @@ public class MainActivity extends Activity {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
if (process != null) {
|
||||
Log.w("tildefriends", "Killing process.");
|
||||
process.destroyForcibly();
|
||||
Log.w("tildefriends", "Process killed.");
|
||||
process = null;
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState)
|
||||
{
|
||||
|
@ -19,7 +19,6 @@ typedef struct _database_t
|
||||
JSContext* context;
|
||||
JSValue object;
|
||||
void* task;
|
||||
sqlite3* db;
|
||||
const char* id;
|
||||
} database_t;
|
||||
|
||||
@ -62,16 +61,12 @@ static JSValue _database_create(JSContext* context, JSValueConst this_val, int a
|
||||
++_database_count;
|
||||
JSValue object = JS_NewObjectClass(context, _database_class_id);
|
||||
|
||||
tf_task_t* task = tf_task_get(context);
|
||||
sqlite3* db = tf_ssb_get_db(tf_task_get_ssb(task));
|
||||
|
||||
database_t* database = tf_malloc(sizeof(database_t));
|
||||
*database = (database_t)
|
||||
{
|
||||
.task = JS_GetContextOpaque(context),
|
||||
.context = context,
|
||||
.object = object,
|
||||
.db = db,
|
||||
};
|
||||
const char* id = JS_ToCString(context, argv[0]);
|
||||
database->id = tf_strdup(id);
|
||||
@ -105,8 +100,10 @@ static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc
|
||||
database_t* database = JS_GetOpaque(this_val, _database_class_id);
|
||||
if (database)
|
||||
{
|
||||
tf_ssb_t* ssb = tf_task_get_ssb(database->task);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "SELECT value FROM properties WHERE id = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t length;
|
||||
const char* keyString = JS_ToCStringLen(context, &length, argv[0]);
|
||||
@ -119,6 +116,7 @@ static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc
|
||||
JS_FreeCString(context, keyString);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
@ -129,7 +127,9 @@ JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSVal
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, $2, $3)", -1, &statement, NULL) == SQLITE_OK)
|
||||
tf_ssb_t* ssb = tf_task_get_ssb(database->task);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, $2, $3)", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t keyLength;
|
||||
const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
|
||||
@ -145,6 +145,7 @@ JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSVal
|
||||
JS_FreeCString(context, valueString);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
@ -156,9 +157,11 @@ static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
tf_ssb_t* ssb = tf_task_get_ssb(database->task);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (JS_IsNull(argv[1]) || JS_IsUndefined(argv[1]))
|
||||
{
|
||||
if (sqlite3_prepare(database->db, "INSERT INTO properties (id, key, value) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, "INSERT INTO properties (id, key, value) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t key_length;
|
||||
size_t set_length;
|
||||
@ -169,14 +172,14 @@ static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int
|
||||
sqlite3_bind_text(statement, 3, set, set_length, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_DONE)
|
||||
{
|
||||
exchanged = sqlite3_changes(database->db) != 0 ? JS_TRUE : JS_FALSE;
|
||||
exchanged = sqlite3_changes(db) != 0 ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
JS_FreeCString(context, key);
|
||||
JS_FreeCString(context, set);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
}
|
||||
else if (sqlite3_prepare(database->db, "UPDATE properties SET value = $1 WHERE id = $2 AND key = $3 AND value = $4", -1, &statement, NULL) == SQLITE_OK)
|
||||
else if (sqlite3_prepare(db, "UPDATE properties SET value = $1 WHERE id = $2 AND key = $3 AND value = $4", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t key_length;
|
||||
size_t expected_length;
|
||||
@ -190,13 +193,14 @@ static JSValue _database_exchange(JSContext* context, JSValueConst this_val, int
|
||||
sqlite3_bind_text(statement, 4, expected, expected_length, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_DONE)
|
||||
{
|
||||
exchanged = sqlite3_changes(database->db) != 0 ? JS_TRUE : JS_FALSE;
|
||||
exchanged = sqlite3_changes(db) != 0 ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
JS_FreeCString(context, key);
|
||||
JS_FreeCString(context, expected);
|
||||
JS_FreeCString(context, set);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
return exchanged;
|
||||
}
|
||||
@ -207,7 +211,9 @@ JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JS
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "DELETE FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK)
|
||||
tf_ssb_t* ssb = tf_task_get_ssb(database->task);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (sqlite3_prepare(db, "DELETE FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t keyLength;
|
||||
const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
|
||||
@ -219,6 +225,7 @@ JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JS
|
||||
JS_FreeCString(context, keyString);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
@ -230,7 +237,9 @@ JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, J
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = $1", -1, &statement, NULL) == SQLITE_OK)
|
||||
tf_ssb_t* ssb = tf_task_get_ssb(database->task);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = $1", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -243,6 +252,7 @@ JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, J
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
@ -254,7 +264,9 @@ JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc,
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
tf_ssb_t* ssb = tf_task_get_ssb(database->task);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare(db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
const char* pattern = JS_ToCString(context, argv[0]);
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK &&
|
||||
@ -273,6 +285,7 @@ JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc,
|
||||
JS_FreeCString(context, pattern);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -280,8 +293,8 @@ JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc,
|
||||
static JSValue _databases_list(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||
{
|
||||
tf_task_t* task = tf_task_get(context);
|
||||
sqlite3* db = tf_ssb_get_db(tf_task_get_ssb(task));
|
||||
|
||||
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
JSValue array = JS_UNDEFINED;
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, "SELECT DISTINCT id FROM properties WHERE id LIKE ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
@ -299,5 +312,6 @@ static JSValue _databases_list(JSContext* context, JSValueConst this_val, int ar
|
||||
JS_FreeCString(context, pattern);
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return array;
|
||||
}
|
||||
|
58
src/ssb.c
58
src/ssb.c
@ -180,9 +180,10 @@ typedef struct _tf_ssb_t
|
||||
tf_trace_t* trace;
|
||||
|
||||
const char* db_path;
|
||||
sqlite3* db;
|
||||
|
||||
uv_mutex_t db_readers_lock;
|
||||
uv_mutex_t db_writer_lock;
|
||||
sqlite3* db_writer;
|
||||
sqlite3** db_readers;
|
||||
int db_readers_count;
|
||||
|
||||
@ -681,7 +682,9 @@ void tf_ssb_connection_remove_new_message_request(tf_ssb_connection_t* connectio
|
||||
|
||||
void tf_ssb_connection_remove_request(tf_ssb_connection_t* connection, int32_t request_number)
|
||||
{
|
||||
tf_ssb_request_t* request = bsearch(&request_number, connection->requests, connection->requests_count, sizeof(tf_ssb_request_t), _request_compare);
|
||||
tf_ssb_request_t* request = connection->requests_count ?
|
||||
bsearch(&request_number, connection->requests, connection->requests_count, sizeof(tf_ssb_request_t), _request_compare) :
|
||||
NULL;
|
||||
if (request)
|
||||
{
|
||||
if (request->cleanup)
|
||||
@ -2097,6 +2100,7 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path
|
||||
}
|
||||
|
||||
uv_mutex_init(&ssb->db_readers_lock);
|
||||
uv_mutex_init(&ssb->db_writer_lock);
|
||||
|
||||
JS_NewClassID(&_connection_class_id);
|
||||
JSClassDef def =
|
||||
@ -2107,7 +2111,7 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path
|
||||
JS_NewClass(JS_GetRuntime(ssb->context), _connection_class_id, &def);
|
||||
|
||||
ssb->db_path = tf_strdup(db_path);
|
||||
sqlite3_open(db_path, &ssb->db);
|
||||
sqlite3_open_v2(db_path, &ssb->db_writer, SQLITE_OPEN_READWRITE | SQLITE_OPEN_URI, NULL);
|
||||
tf_ssb_db_init(ssb);
|
||||
|
||||
if (loop)
|
||||
@ -2140,11 +2144,6 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path
|
||||
return ssb;
|
||||
}
|
||||
|
||||
sqlite3* tf_ssb_get_db(tf_ssb_t* ssb)
|
||||
{
|
||||
return ssb->db;
|
||||
}
|
||||
|
||||
sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb)
|
||||
{
|
||||
sqlite3* db = NULL;
|
||||
@ -2155,7 +2154,7 @@ sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb)
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_open_v2(ssb->db_path, &db, SQLITE_OPEN_READONLY, NULL);
|
||||
sqlite3_open_v2(ssb->db_path, &db, SQLITE_OPEN_READONLY | SQLITE_OPEN_URI, NULL);
|
||||
tf_ssb_db_init_reader(db);
|
||||
tf_trace_sqlite(ssb->trace, db);
|
||||
}
|
||||
@ -2171,6 +2170,22 @@ void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db)
|
||||
uv_mutex_unlock(&ssb->db_readers_lock);
|
||||
}
|
||||
|
||||
sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb)
|
||||
{
|
||||
uv_mutex_lock(&ssb->db_writer_lock);
|
||||
sqlite3* writer = ssb->db_writer;
|
||||
assert(writer);
|
||||
ssb->db_writer = NULL;
|
||||
return writer;
|
||||
}
|
||||
|
||||
void tf_ssb_release_db_writer(tf_ssb_t* ssb, sqlite3* db)
|
||||
{
|
||||
assert(ssb->db_writer == NULL);
|
||||
ssb->db_writer = db;
|
||||
uv_mutex_unlock(&ssb->db_writer_lock);
|
||||
}
|
||||
|
||||
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb)
|
||||
{
|
||||
return ssb->loop;
|
||||
@ -2203,10 +2218,12 @@ void tf_ssb_get_private_key(tf_ssb_t* ssb, uint8_t* out_private, size_t private_
|
||||
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace)
|
||||
{
|
||||
ssb->trace = trace;
|
||||
if (trace && ssb->db)
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (trace && db)
|
||||
{
|
||||
tf_trace_sqlite(trace, ssb->db);
|
||||
tf_trace_sqlite(trace, db);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
|
||||
tf_trace_t* tf_ssb_get_trace(tf_ssb_t* ssb)
|
||||
@ -2334,7 +2351,7 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
||||
JS_FreeContext(ssb->context);
|
||||
JS_FreeRuntime(ssb->runtime);
|
||||
}
|
||||
sqlite3_close(ssb->db);
|
||||
sqlite3_close(ssb->db_writer);
|
||||
while (ssb->broadcasts)
|
||||
{
|
||||
tf_ssb_broadcast_t* broadcast = ssb->broadcasts;
|
||||
@ -2355,6 +2372,7 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
|
||||
}
|
||||
tf_free(ssb->db_readers);
|
||||
uv_mutex_destroy(&ssb->db_readers_lock);
|
||||
uv_mutex_destroy(&ssb->db_writer_lock);
|
||||
tf_free((void*)ssb->db_path);
|
||||
tf_free(ssb);
|
||||
}
|
||||
@ -2610,10 +2628,14 @@ static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, s
|
||||
{
|
||||
struct sockaddr server_addr;
|
||||
int len = (int)sizeof(server_addr);
|
||||
if (uv_tcp_getsockname(&ssb->server, &server_addr, &len) != 0 ||
|
||||
server_addr.sa_family != AF_INET)
|
||||
int r = uv_tcp_getsockname(&ssb->server, &server_addr, &len);
|
||||
if (r != 0)
|
||||
{
|
||||
tf_printf("Unable to get server's address.\n");
|
||||
tf_printf("Unable to get server's address: %s.\n", uv_strerror(r));
|
||||
}
|
||||
else if (server_addr.sa_family != AF_INET)
|
||||
{
|
||||
tf_printf("Unexpected address family: %d\n", server_addr.sa_family);
|
||||
}
|
||||
|
||||
char address_str[256];
|
||||
@ -2636,7 +2658,7 @@ static void _tf_ssb_send_broadcast(tf_ssb_t* ssb, struct sockaddr_in* address, s
|
||||
broadcast_addr.sin_addr.s_addr =
|
||||
(address->sin_addr.s_addr & netmask->sin_addr.s_addr) |
|
||||
(INADDR_BROADCAST & ~netmask->sin_addr.s_addr);
|
||||
int r = uv_udp_try_send(&ssb->broadcast_sender, &buf, 1, (struct sockaddr*)&broadcast_addr);
|
||||
r = uv_udp_try_send(&ssb->broadcast_sender, &buf, 1, (struct sockaddr*)&broadcast_addr);
|
||||
if (r < 0)
|
||||
{
|
||||
char broadcast_str[256] = { 0 };
|
||||
@ -3218,6 +3240,10 @@ void tf_ssb_notify_message_added(tf_ssb_t* ssb, const char* id)
|
||||
|
||||
for (tf_ssb_connection_t* connection = ssb->connections; connection; connection = connection->next)
|
||||
{
|
||||
if (!connection->message_requests_count)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
tf_ssb_connection_message_request_t* message_request =
|
||||
bsearch(
|
||||
author_string,
|
||||
|
@ -16,7 +16,6 @@
|
||||
typedef struct _tf_ssb_connections_t
|
||||
{
|
||||
tf_ssb_t* ssb;
|
||||
sqlite3* db;
|
||||
uv_timer_t timer;
|
||||
} tf_ssb_connections_t;
|
||||
|
||||
@ -55,7 +54,8 @@ static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connec
|
||||
{
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > $1) ORDER BY last_attempt LIMIT 1", -1, &statement, NULL) == SQLITE_OK)
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(connections->ssb);
|
||||
if (sqlite3_prepare(db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > $1) ORDER BY last_attempt LIMIT 1", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int(statement, 1, 60000) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
@ -69,8 +69,9 @@ static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connec
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare: %s\n", sqlite3_errmsg(connections->db));
|
||||
tf_printf("prepare: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
tf_ssb_release_db_reader(connections->ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -100,7 +101,6 @@ tf_ssb_connections_t* tf_ssb_connections_create(tf_ssb_t* ssb)
|
||||
tf_ssb_connections_t* connections = tf_malloc(sizeof(tf_ssb_connections_t));
|
||||
memset(connections, 0, sizeof(*connections));
|
||||
connections->ssb = ssb;
|
||||
connections->db = tf_ssb_get_db(ssb);
|
||||
|
||||
tf_ssb_add_connections_changed_callback(ssb, _tf_ssb_connections_changed_callback, NULL, connections);
|
||||
|
||||
@ -128,7 +128,8 @@ void tf_ssb_connections_destroy(tf_ssb_connections_t* connections)
|
||||
void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "INSERT INTO connections (host, port, key) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(connections->ssb);
|
||||
if (sqlite3_prepare(db, "INSERT INTO connections (host, port, key) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
|
||||
@ -137,17 +138,19 @@ void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* hos
|
||||
int r = sqlite3_step(statement);
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("tf_ssb_connections_store: %d, %s.\n", r, sqlite3_errmsg(connections->db));
|
||||
tf_printf("tf_ssb_connections_store: %d, %s.\n", r, sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(connections->ssb, db);
|
||||
}
|
||||
|
||||
void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "UPDATE connections SET last_attempt = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(connections->ssb);
|
||||
if (sqlite3_prepare(db, "UPDATE connections SET last_attempt = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
|
||||
@ -155,17 +158,19 @@ void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const c
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("tf_ssb_connections_set_attempted: %s.\n", sqlite3_errmsg(connections->db));
|
||||
tf_printf("tf_ssb_connections_set_attempted: %s.\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(connections->ssb, db);
|
||||
}
|
||||
|
||||
void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "UPDATE connections SET last_success = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(connections->ssb);
|
||||
if (sqlite3_prepare(db, "UPDATE connections SET last_success = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
|
||||
@ -173,9 +178,10 @@ void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const c
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("tf_ssb_connections_set_succeeded: %s.\n", sqlite3_errmsg(connections->db));
|
||||
tf_printf("tf_ssb_connections_set_succeeded: %s.\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(connections->ssb, db);
|
||||
}
|
||||
|
151
src/ssb.db.c
151
src/ssb.db.c
@ -67,7 +67,7 @@ void tf_ssb_db_init_reader(sqlite3* db)
|
||||
|
||||
void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
{
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
_tf_ssb_db_init_internal(db);
|
||||
_tf_ssb_db_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS messages ("
|
||||
@ -222,6 +222,7 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
||||
tf_printf("Adding sequence_before_author column.\n");
|
||||
_tf_ssb_db_exec(db, "ALTER TABLE messages ADD COLUMN sequence_before_author INTEGER");
|
||||
}
|
||||
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)
|
||||
@ -252,31 +253,37 @@ static bool _tf_ssb_db_previous_message_exists(sqlite3* db, const char* author,
|
||||
bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id, JSValue val, const char* signature, bool sequence_before_author)
|
||||
{
|
||||
bool stored = false;
|
||||
|
||||
JSValue previousval = JS_GetPropertyStr(context, val, "previous");
|
||||
const char* previous = JS_IsNull(previousval) ? NULL : JS_ToCString(context, previousval);
|
||||
JS_FreeValue(context, previousval);
|
||||
|
||||
JSValue authorval = JS_GetPropertyStr(context, val, "author");
|
||||
const char* author = JS_ToCString(context, authorval);
|
||||
JS_FreeValue(context, authorval);
|
||||
|
||||
int64_t sequence = -1;
|
||||
JSValue sequenceval = JS_GetPropertyStr(context, val, "sequence");
|
||||
JS_ToInt64(context, &sequence, sequenceval);
|
||||
JS_FreeValue(context, sequenceval);
|
||||
double timestamp = -1.0;
|
||||
JSValue timestampval = JS_GetPropertyStr(context, val, "timestamp");
|
||||
JS_ToFloat64(context, ×tamp, timestampval);
|
||||
JS_FreeValue(context, timestampval);
|
||||
|
||||
JSValue contentval = JS_GetPropertyStr(context, val, "content");
|
||||
JSValue content = JS_JSONStringify(context, contentval, JS_NULL, JS_NULL);
|
||||
size_t content_len;
|
||||
const char* contentstr = JS_ToCStringLen(context, &content_len, content);
|
||||
JS_FreeValue(context, contentval);
|
||||
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
int64_t last_row_id = -1;
|
||||
|
||||
if (_tf_ssb_db_previous_message_exists(db, author, sequence, previous))
|
||||
{
|
||||
double timestamp = -1.0;
|
||||
JSValue timestampval = JS_GetPropertyStr(context, val, "timestamp");
|
||||
JS_ToFloat64(context, ×tamp, timestampval);
|
||||
JS_FreeValue(context, timestampval);
|
||||
|
||||
JSValue contentval = JS_GetPropertyStr(context, val, "content");
|
||||
JSValue content = JS_JSONStringify(context, contentval, JS_NULL, JS_NULL);
|
||||
size_t content_len;
|
||||
const char* contentstr = JS_ToCStringLen(context, &content_len, content);
|
||||
JS_FreeValue(context, contentval);
|
||||
|
||||
const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature, sequence_before_author) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT DO NOTHING";
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -309,47 +316,46 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
JS_FreeCString(context, contentstr);
|
||||
JS_FreeValue(context, content);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("Previous message doesn't exist.\n");
|
||||
}
|
||||
|
||||
if (last_row_id != -1)
|
||||
{
|
||||
const char* query = "SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND json.value LIKE '&%%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL";
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int64(statement, 1, last_row_id) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
||||
{
|
||||
int r = SQLITE_OK;
|
||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||
{
|
||||
tf_ssb_notify_blob_want_added(ssb, (const char*)sqlite3_column_text(statement, 0));
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("%s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
if (last_row_id != -1)
|
||||
{
|
||||
const char* query = "SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND json.value LIKE '&%%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL";
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int64(statement, 1, last_row_id) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, k_blob_id_len - 1) == SQLITE_OK)
|
||||
{
|
||||
int r = SQLITE_OK;
|
||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||
{
|
||||
tf_ssb_notify_blob_want_added(ssb, (const char*)sqlite3_column_text(statement, 0));
|
||||
}
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
tf_printf("%s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
|
||||
|
||||
JS_FreeValue(context, previousval);
|
||||
JS_FreeCString(context, author);
|
||||
JS_FreeValue(context, authorval);
|
||||
JS_FreeCString(context, previous);
|
||||
JS_FreeCString(context, contentstr);
|
||||
JS_FreeValue(context, content);
|
||||
return stored;
|
||||
}
|
||||
|
||||
@ -357,8 +363,9 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_
|
||||
{
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
const char* query = "SELECT content FROM messages WHERE id = ?";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
@ -379,6 +386,7 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -386,8 +394,9 @@ bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id)
|
||||
{
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
const char* query = "SELECT COUNT(*) FROM blobs WHERE id = $1";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
@ -396,6 +405,7 @@ bool tf_ssb_db_blob_has(tf_ssb_t* ssb, const char* id)
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -403,8 +413,9 @@ bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_
|
||||
{
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
const char* query = "SELECT content FROM blobs WHERE id = $1";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
@ -428,13 +439,14 @@ bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char* out_id, size_t out_id_size, bool* out_new)
|
||||
{
|
||||
bool result = false;
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
|
||||
uint8_t hash[crypto_hash_sha256_BYTES];
|
||||
@ -464,8 +476,9 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
|
||||
if (rows)
|
||||
{
|
||||
@ -492,7 +505,8 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
bool found = false;
|
||||
sqlite3_stmt* statement;
|
||||
const char* query = "SELECT id, timestamp, content FROM messages WHERE author = $1 AND sequence = $2";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (sqlite3_prepare(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 &&
|
||||
@ -516,8 +530,9 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(tf_ssb_get_db(ssb)));
|
||||
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return found;
|
||||
}
|
||||
|
||||
@ -525,8 +540,9 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
||||
{
|
||||
bool found = false;
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
const char* query = "SELECT id, sequence FROM messages WHERE author = $1 AND sequence = (SELECT MAX(sequence) FROM messages WHERE author = $1)";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
@ -545,8 +561,9 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("prepare failed: %s\n", sqlite3_errmsg(tf_ssb_get_db(ssb)));
|
||||
tf_printf("%s: prepare failed: %s\n", __FUNCTION__, sqlite3_errmsg(db));
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return found;
|
||||
}
|
||||
|
||||
@ -682,7 +699,7 @@ static int _tf_ssb_sqlite_authorizer(void* user_data, int action_code, const cha
|
||||
JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds, void (*callback)(JSValue row, void* user_data), void* user_data)
|
||||
{
|
||||
JSValue result = JS_UNDEFINED;
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3_set_authorizer(db, _tf_ssb_sqlite_authorizer, ssb);
|
||||
@ -717,6 +734,7 @@ JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue bi
|
||||
result = JS_ThrowInternalError(context, "SQL Error %s: preparing \"%s\".", sqlite3_errmsg(db), query);
|
||||
}
|
||||
sqlite3_set_authorizer(db, NULL, NULL);
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -870,7 +888,7 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author)
|
||||
int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user)
|
||||
{
|
||||
int count = 0;
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare(db, "SELECT COUNT(*) FROM identities WHERE user = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -883,6 +901,7 @@ int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user)
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -907,7 +926,7 @@ bool tf_ssb_db_identity_create(tf_ssb_t* ssb, const char* user, uint8_t* out_pub
|
||||
bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_key, const char* private_key)
|
||||
{
|
||||
bool added = false;
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare(db, "INSERT INTO identities (user, public_key, private_key) VALUES (?, ?, ?) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -925,12 +944,13 @@ bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
return added;
|
||||
}
|
||||
|
||||
void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(const char* identity, void* user_data), void* user_data)
|
||||
{
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare(db, "SELECT public_key FROM identities WHERE user = ? ORDER BY public_key", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -943,11 +963,12 @@ void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
|
||||
void tf_ssb_db_identity_visit_all(tf_ssb_t* ssb, void (*callback)(const char* identity, void* user_data), void* user_data)
|
||||
{
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare(db, "SELECT public_key FROM identities ORDER BY public_key", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -957,13 +978,14 @@ void tf_ssb_db_identity_visit_all(tf_ssb_t* ssb, void (*callback)(const char* id
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
|
||||
bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const char* public_key, uint8_t* out_private_key, size_t private_key_size)
|
||||
{
|
||||
bool success = false;
|
||||
memset(out_private_key, 0, crypto_sign_SECRETKEYBYTES);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare(db, "SELECT private_key FROM identities WHERE user = ? AND public_key = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -979,6 +1001,7 @@ bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const c
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return success;
|
||||
}
|
||||
|
||||
@ -1046,7 +1069,7 @@ static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t***
|
||||
|
||||
if (depth < max_depth && !already_populated)
|
||||
{
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement = NULL;
|
||||
if (sqlite3_prepare(db, "SELECT json_extract(content, '$.contact'), json_extract(content, '$.following'), json_extract(content, '$.blocking') FROM messages WHERE author = ? AND json_extract(content, '$.type') = 'contact' ORDER BY sequence", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -1077,6 +1100,7 @@ static following_t* _get_following(tf_ssb_t* ssb, const char* id, following_t***
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
@ -1143,7 +1167,7 @@ JSValue tf_ssb_db_get_message_by_id( tf_ssb_t* ssb, const char* id, bool is_keys
|
||||
{
|
||||
JSValue result = JS_UNDEFINED;
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, "SELECT previous, author, id, sequence, timestamp, hash, content, signature, sequence_before_author FROM messages WHERE id = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -1178,12 +1202,13 @@ JSValue tf_ssb_db_get_message_by_id( tf_ssb_t* ssb, const char* id, bool is_keys
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, int* out_count)
|
||||
{
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
tf_ssb_db_stored_connection_t* result = NULL;
|
||||
int count = 0;
|
||||
|
||||
@ -1203,6 +1228,7 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
|
||||
*out_count = count;
|
||||
return result;
|
||||
@ -1210,7 +1236,7 @@ tf_ssb_db_stored_connection_t* tf_ssb_db_get_stored_connections(tf_ssb_t* ssb, i
|
||||
|
||||
void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int port, const char* pubkey)
|
||||
{
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, "DELETE FROM connections WHERE host = ? AND port = ? AND key = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -1223,4 +1249,5 @@ void tf_ssb_db_forget_stored_connection(tf_ssb_t* ssb, const char* address, int
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
}
|
||||
|
@ -87,9 +87,10 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
}
|
||||
|
||||
char app_blob_id[64] = { 0 };
|
||||
sqlite3_busy_timeout(tf_ssb_get_db(ssb), 10000);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_busy_timeout(db, 10000);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), "SELECT value FROM properties WHERE id = $1 AND key = 'path:' || $2", -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = $1 AND key = 'path:' || $2", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, path, -1, NULL) == SQLITE_OK &&
|
||||
@ -105,6 +106,7 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
|
||||
if (!*app_blob_id)
|
||||
{
|
||||
|
@ -69,9 +69,10 @@ typedef struct _tf_ssb_blob_wants_t
|
||||
tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path);
|
||||
void tf_ssb_destroy(tf_ssb_t* ssb);
|
||||
|
||||
sqlite3* tf_ssb_get_db(tf_ssb_t* ssb);
|
||||
sqlite3* tf_ssb_acquire_db_reader(tf_ssb_t* ssb);
|
||||
void tf_ssb_release_db_reader(tf_ssb_t* ssb, sqlite3* db);
|
||||
sqlite3* tf_ssb_acquire_db_writer(tf_ssb_t* ssb);
|
||||
void tf_ssb_release_db_writer(tf_ssb_t* ssb, sqlite3* db);
|
||||
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb);
|
||||
|
||||
void tf_ssb_generate_keys(tf_ssb_t* ssb);
|
||||
|
@ -20,7 +20,8 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
|
||||
sqlite3_stmt* statement;
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
JSValue apps = JS_UNDEFINED;
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), "SELECT value FROM properties WHERE id = $1 AND key = 'apps'", -1, &statement, NULL) == SQLITE_OK)
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = $1 AND key = 'apps'", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
@ -68,7 +69,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
|
||||
|
||||
JSValue json = JS_JSONStringify(context, out_apps, JS_NULL, JS_NULL);
|
||||
const char* text = JS_ToCString(context, json);
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'apps', $2)", -1, &statement, NULL) == SQLITE_OK)
|
||||
if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'apps', $2)", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, text, -1, NULL) == SQLITE_OK &&
|
||||
@ -77,6 +78,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char*
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
JS_FreeCString(context, text);
|
||||
JS_FreeValue(context, json);
|
||||
|
||||
@ -160,19 +162,21 @@ static void _tf_ssb_import_recursive_add_files(tf_ssb_t* ssb, uv_loop_t* loop, J
|
||||
static bool _tf_ssb_register_app(tf_ssb_t* ssb, const char* user, const char* app, const char* id)
|
||||
{
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3* db = tf_ssb_acquire_db_writer(ssb);
|
||||
if (sqlite3_prepare(db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, app, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_DONE)
|
||||
{
|
||||
result = sqlite3_changes(tf_ssb_get_db(ssb)) != 0;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
return result;
|
||||
{
|
||||
result = sqlite3_changes(db) != 0;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_writer(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void _tf_ssb_import_app_json(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* context, const char* user, const char* path)
|
||||
@ -206,7 +210,7 @@ static void _tf_ssb_import_app_json(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* c
|
||||
|
||||
if (_tf_ssb_register_app(ssb, user, app, id))
|
||||
{
|
||||
tf_printf("Registered %s path:%s as %s.\n", user, app, id);
|
||||
tf_printf("Registered %s path:%s as %s.\n", user, app, id);
|
||||
_tf_ssb_import_add_app(ssb, user, app);
|
||||
}
|
||||
}
|
||||
@ -229,7 +233,6 @@ static void _tf_ssb_import_app_json(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* c
|
||||
void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path)
|
||||
{
|
||||
uv_fs_t req = { 0 };
|
||||
sqlite3_busy_timeout(tf_ssb_get_db(ssb), 10000);
|
||||
int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &req, path, 0, NULL);
|
||||
if (r >= 0)
|
||||
{
|
||||
@ -334,7 +337,7 @@ static void _tf_ssb_import_app_json_from_zip(tf_ssb_t* ssb, unzFile zip, JSConte
|
||||
|
||||
if (_tf_ssb_register_app(ssb, user, app, id))
|
||||
{
|
||||
tf_printf("Registered %s path:%s as %s.\n", user, app, id);
|
||||
tf_printf("Registered %s path:%s as %s.\n", user, app, id);
|
||||
_tf_ssb_import_add_app(ssb, user, app);
|
||||
}
|
||||
}
|
||||
|
@ -1091,7 +1091,7 @@ static JSValue _tf_ssb_private_message_encrypt(JSContext* context, JSValueConst
|
||||
if (JS_IsUndefined(result))
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
|
||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 };
|
||||
if (_tf_ssb_get_private_key_curve25519(db, signer_user, signer_identity, private_key))
|
||||
@ -1167,6 +1167,7 @@ static JSValue _tf_ssb_private_message_encrypt(JSContext* context, JSValueConst
|
||||
{
|
||||
result = JS_ThrowInternalError(context, "Unable to get key for ID %s of user %s.", signer_identity, signer_user);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
|
||||
JS_FreeCString(context, signer_user);
|
||||
@ -1187,7 +1188,7 @@ static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst
|
||||
uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 };
|
||||
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
if (_tf_ssb_get_private_key_curve25519(db, user, identity, private_key))
|
||||
{
|
||||
uint8_t* decoded = tf_malloc(message_size);
|
||||
@ -1238,6 +1239,7 @@ static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst
|
||||
{
|
||||
result = JS_ThrowInternalError(context, "Private key not found for user %s with id %s.", user, identity);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
|
||||
JS_FreeCString(context, user);
|
||||
JS_FreeCString(context, identity);
|
||||
|
@ -1,11 +1,13 @@
|
||||
#include "ssb.rpc.h"
|
||||
|
||||
#include "log.h"
|
||||
#include "mem.h"
|
||||
#include "ssb.h"
|
||||
#include "ssb.db.h"
|
||||
#include "util.js.h"
|
||||
|
||||
#include "sqlite3.h"
|
||||
#include "uv.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
@ -141,7 +143,7 @@ static void _tf_ssb_rpc_request_more_blobs(tf_ssb_connection_t* connection)
|
||||
JSContext* context = tf_ssb_connection_get_context(connection);
|
||||
tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, "SELECT id FROM blob_wants_view WHERE id > ? ORDER BY id LIMIT 32", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -160,6 +162,7 @@ static void _tf_ssb_rpc_request_more_blobs(tf_ssb_connection_t* connection)
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_blobs_createWants(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
|
||||
@ -192,7 +195,7 @@ static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool defau
|
||||
{
|
||||
bool result = default_value;
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -209,6 +212,7 @@ static bool _get_global_setting_bool(tf_ssb_t* ssb, const char* name, bool defau
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -216,7 +220,7 @@ static bool _get_global_setting_string(tf_ssb_t* ssb, const char* name, char* ou
|
||||
{
|
||||
bool result = false;
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(db, "SELECT value FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
@ -236,6 +240,7 @@ static bool _get_global_setting_string(tf_ssb_t* ssb, const char* name, char* ou
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -417,9 +422,31 @@ typedef struct _blobs_get_t
|
||||
char id[k_blob_id_len];
|
||||
size_t received;
|
||||
size_t expected_size;
|
||||
char buffer[];
|
||||
bool done;
|
||||
tf_ssb_t* ssb;
|
||||
uv_work_t work;
|
||||
uint8_t buffer[];
|
||||
} blobs_get_t;
|
||||
|
||||
static void _tf_ssb_rpc_blob_store_work(uv_work_t* work)
|
||||
{
|
||||
blobs_get_t* get = work->data;
|
||||
tf_ssb_db_blob_store(get->ssb, get->buffer, get->received, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_blob_store_after_work(uv_work_t* work, int status)
|
||||
{
|
||||
blobs_get_t* get = work->data;
|
||||
if (status != 0)
|
||||
{
|
||||
tf_printf("uv_queue_work failed: %s\n", uv_strerror(status));
|
||||
}
|
||||
if (get->done)
|
||||
{
|
||||
tf_free(get);
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_connection_blobs_get_callback(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);
|
||||
@ -435,8 +462,17 @@ static void _tf_ssb_rpc_connection_blobs_get_callback(tf_ssb_connection_t* conne
|
||||
bool stored = false;
|
||||
if (JS_ToBool(context, args))
|
||||
{
|
||||
char id[256];
|
||||
stored = tf_ssb_db_blob_store(ssb, (uint8_t*)get->buffer, get->expected_size, id, sizeof(id), NULL);
|
||||
get->work.data = get;
|
||||
int r = uv_queue_work(tf_ssb_get_loop(ssb), &get->work, _tf_ssb_rpc_blob_store_work, _tf_ssb_rpc_blob_store_after_work);
|
||||
if (r)
|
||||
{
|
||||
tf_printf("uv_queue_work failed: %s\n", uv_strerror(r));
|
||||
get->work.data = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
stored = true;
|
||||
}
|
||||
}
|
||||
tf_ssb_connection_rpc_send(
|
||||
connection,
|
||||
@ -452,13 +488,18 @@ static void _tf_ssb_rpc_connection_blobs_get_callback(tf_ssb_connection_t* conne
|
||||
|
||||
static void _tf_ssb_rpc_connection_blobs_get_cleanup(tf_ssb_t* ssb, void* user_data)
|
||||
{
|
||||
tf_free(user_data);
|
||||
blobs_get_t* get = user_data;
|
||||
get->done = true;
|
||||
if (!get->work.data)
|
||||
{
|
||||
tf_free(get);
|
||||
}
|
||||
}
|
||||
|
||||
static void _tf_ssb_rpc_connection_blobs_get(tf_ssb_connection_t* connection, const char* blob_id, size_t size)
|
||||
{
|
||||
blobs_get_t* get = tf_malloc(sizeof(blobs_get_t) + size);
|
||||
*get = (blobs_get_t) { .expected_size = size };
|
||||
*get = (blobs_get_t) { .ssb = tf_ssb_connection_get_ssb(connection), .expected_size = size };
|
||||
snprintf(get->id, sizeof(get->id), "%s", blob_id);
|
||||
memset(get->buffer, 0, size);
|
||||
|
||||
@ -698,7 +739,7 @@ static void _tf_ssb_connection_send_history_stream_internal(tf_ssb_connection_t*
|
||||
{
|
||||
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
const int k_max = 32;
|
||||
int64_t max_sequence_seen = 0;
|
||||
@ -740,6 +781,7 @@ static void _tf_ssb_connection_send_history_stream_internal(tf_ssb_connection_t*
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
tf_ssb_release_db_reader(ssb, db);
|
||||
|
||||
if (max_sequence_seen == sequence + k_max - 1)
|
||||
{
|
||||
|
@ -127,9 +127,9 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
|
||||
uv_loop_t loop = { 0 };
|
||||
uv_loop_init(&loop);
|
||||
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, ":memory:");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:db0?mode=memory&cache=shared");
|
||||
tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, ":memory:");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:db1?mode=memory&cache=shared");
|
||||
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
|
||||
|
||||
uv_idle_t idle0 = { .data = ssb0 };
|
||||
@ -311,11 +311,11 @@ void tf_ssb_test_rooms(const tf_test_options_t* options)
|
||||
uv_loop_t loop = { 0 };
|
||||
uv_loop_init(&loop);
|
||||
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, ":memory:");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:db0?mode=memory&cache=shared");
|
||||
tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, ":memory:");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:db1?mode=memory&cache=shared");
|
||||
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
|
||||
tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, ":memory:");
|
||||
tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, "file:db2?mode=memory&cache=shared");
|
||||
tf_ssb_register(tf_ssb_get_context(ssb2), ssb2);
|
||||
|
||||
uv_idle_t idle0 = { .data = ssb0 };
|
||||
@ -555,7 +555,7 @@ void tf_ssb_test_bench(const tf_test_options_t* options)
|
||||
uv_loop_t loop = { 0 };
|
||||
uv_loop_init(&loop);
|
||||
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, ":memory:");
|
||||
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:db0?mode=memory&cache=shared");
|
||||
tf_ssb_generate_keys(ssb0);
|
||||
|
||||
char id0[k_id_base64_len] = { 0 };
|
||||
@ -579,7 +579,7 @@ void tf_ssb_test_bench(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);
|
||||
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, ":memory:");
|
||||
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:db1?mode=memory&cache=shared");
|
||||
tf_ssb_generate_keys(ssb1);
|
||||
uint8_t id0bin[k_id_bin_len];
|
||||
tf_ssb_id_str_to_bin(id0bin, id0);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "tlscontext.js.h"
|
||||
#include "trace.h"
|
||||
#include "util.js.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "backtrace.h"
|
||||
#include "quickjs.h"
|
||||
@ -35,8 +36,6 @@
|
||||
#define _countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
|
||||
#endif
|
||||
|
||||
static const char* k_version = "1.0";
|
||||
|
||||
static JSClassID _import_class_id;
|
||||
static int _count;
|
||||
|
||||
@ -675,9 +674,11 @@ static JSValue _tf_task_version(JSContext* context, JSValueConst this_val, int a
|
||||
{
|
||||
tf_task_t* task = JS_GetContextOpaque(context);
|
||||
tf_trace_begin(task->_trace, __func__);
|
||||
JSValue result = JS_NewString(task->_context, k_version);
|
||||
JSValue version = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, version, "number", JS_NewString(context, VERSION_NUMBER));
|
||||
JS_SetPropertyStr(context, version, "name", JS_NewString(context, VERSION_NAME));
|
||||
tf_trace_end(task->_trace);
|
||||
return result;
|
||||
return version;
|
||||
}
|
||||
|
||||
exportid_t tf_task_export_function(tf_task_t* task, tf_taskstub_t* to, JSValue function)
|
||||
|
@ -236,7 +236,7 @@ static void _test_database(const tf_test_options_t* options)
|
||||
unlink("out/testdb.sqlite");
|
||||
|
||||
char command[256];
|
||||
snprintf(command, sizeof(command), "%s run --ssb-port=0 --db-path=:memory: --db-path=out/testdb.sqlite -s out/test.js", options->exe_path);
|
||||
snprintf(command, sizeof(command), "%s run --ssb-port=0 --db-path=file:db?mode=memory\\&cache=shared -s out/test.js", options->exe_path);
|
||||
tf_printf("%s\n", command);
|
||||
int result = system(command);
|
||||
tf_printf("returned %d\n", WEXITSTATUS(result));
|
||||
|
3
src/version.h
Normal file
3
src/version.h
Normal file
@ -0,0 +1,3 @@
|
||||
#define VERSION_NUMBER "0.0.8"
|
||||
#define VERSION_NAME "The secret ingredient is love."
|
||||
|
@ -1,10 +1,10 @@
|
||||
#!/bin/bash
|
||||
#!/bin/bash -e
|
||||
|
||||
VERSION=$1
|
||||
NICKNAME=$2
|
||||
rm -rfv tildefriends-$VERSION
|
||||
svn export . tildefriends-$VERSION
|
||||
echo "tildefriends-$VERSION: $NICKNAME" > tildefriends-$VERSION/VERSION
|
||||
VERSION_NUMBER=`sed -n -e 's/^VERSION_NUMBER := //p' Makefile`
|
||||
VERSION_NAME=`sed -n -e 's/^VERSION_NAME := //p' Makefile`
|
||||
rm -rfv tildefriends-$VERSION_NUMBER
|
||||
svn export . tildefriends-$VERSION_NUMBER
|
||||
echo "tildefriends-$VERSION_NUMBER: $VERSION_NAME" > tildefriends-$VERSION_NUMBER/VERSION
|
||||
tar \
|
||||
--exclude=deps/libbacktrace/Isaac.Newton-Opticks.txt \
|
||||
--exclude=deps/libsodium/builds \
|
||||
@ -17,5 +17,7 @@ tar \
|
||||
--exclude=deps/sqlite/shell.c \
|
||||
--exclude=deps/zlib/contrib/vstudio \
|
||||
--exclude=deps/zlib/doc \
|
||||
-caf tildefriends-$VERSION.tar.xz tildefriends-$VERSION
|
||||
rm -rfv tildefriends-$VERSION
|
||||
-caf tildefriends-$VERSION_NUMBER.tar.xz tildefriends-$VERSION_NUMBER
|
||||
rm -rfv tildefriends-$VERSION_NUMBER
|
||||
make apk
|
||||
cp out/TildeFriends-release.apk TildeFriends-$VERSION_NUMBER.apk
|
||||
|
@ -1,4 +1,5 @@
|
||||
wget https://cdn.jsdelivr.net/gh/lit/dist@2.7.4/all/lit-all.min.js -O deps/lit/lit-all.min.js
|
||||
wget https://cdn.jsdelivr.net/gh/lit/dist@2.7.4/all/lit-all.min.js.map -O deps/lit/lit-all.min.js.map
|
||||
VERSION=2.7.5
|
||||
wget https://cdn.jsdelivr.net/gh/lit/dist@$VERSION/all/lit-all.min.js -O deps/lit/lit-all.min.js
|
||||
wget https://cdn.jsdelivr.net/gh/lit/dist@$VERSION/all/lit-all.min.js.map -O deps/lit/lit-all.min.js.map
|
||||
cp -fv deps/lit/* apps/ssb/
|
||||
cp -fv deps/lit/* apps/sneaker/
|
||||
|
Reference in New Issue
Block a user