Compare commits

..

2 Commits

Author SHA1 Message Date
d67e47ae4b
nix(module): module boilerplate 2025-02-20 10:27:18 +01:00
b43b8da9ab
nix (test): fix build 2025-02-20 10:17:14 +01:00
100 changed files with 430 additions and 552 deletions

View File

@ -16,9 +16,9 @@ MAKEFLAGS += --no-builtin-rules
## LD := Linker. ## LD := Linker.
## ANDROID_SDK := Path to the Android SDK. ## ANDROID_SDK := Path to the Android SDK.
VERSION_CODE := 34 VERSION_CODE := 33
VERSION_CODE_IOS := 11 VERSION_CODE_IOS := 8
VERSION_NUMBER := 0.0.29-wip VERSION_NUMBER := 0.0.28-wip
VERSION_NAME := This program kills fascists. VERSION_NAME := This program kills fascists.
IPHONEOS_VERSION_MIN=14.0 IPHONEOS_VERSION_MIN=14.0
@ -1419,7 +1419,7 @@ dist-ios: iosrelease-app
mkdir -p out/Payload/tildefriends.app mkdir -p out/Payload/tildefriends.app
cp -avR out/tildefriends-iosrelease.app/* out/Payload/tildefriends.app/ cp -avR out/tildefriends-iosrelease.app/* out/Payload/tildefriends.app/
cp src/ios/tildefriends.png out/Payload/tildefriends.app/ cp src/ios/tildefriends.png out/Payload/tildefriends.app/
xcrun -sdk iphoneos actool --compile out/Payload/tildefriends.app/ --platform iphoneos --minimum-deployment-target $(IPHONEOS_VERSION_MIN) --app-icon AppIcon src/ios/icons/Assets.xcassets src/ios/icons/*.png --output-partial-info-plist out/actool.plist cp src/ios/icons/Assets.car out/Payload/tildefriends.app/
cp src/ios/distribution.mobileprovision out/Payload/tildefriends.app/embedded.mobileprovision cp src/ios/distribution.mobileprovision out/Payload/tildefriends.app/embedded.mobileprovision
xcrun -sdk iphoneos codesign -f -s 'Apple Distribution' --entitlements src/ios/Entitlements.plist --generate-entitlement-der out/Payload/tildefriends.app xcrun -sdk iphoneos codesign -f -s 'Apple Distribution' --entitlements src/ios/Entitlements.plist --generate-entitlement-der out/Payload/tildefriends.app
cd out; zip -r tildefriends.ipa Payload; cd .. cd out; zip -r tildefriends.ipa Payload; cd ..

View File

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

View File

@ -1,5 +1,5 @@
{ {
"type": "tildefriends-app", "type": "tildefriends-app",
"emoji": "🦀", "emoji": "🦀",
"previous": "&Qae0CxJGEH7OspuapuXX/GurmV+VBtQiMhHRmCBKCwU=.sha256" "previous": "&jAAzd36Nmpw0sRA1Dx9wLiIwGX+q//+S/Han+RLlEOw=.sha256"
} }

View File

@ -206,21 +206,6 @@ class TfTabConnectionsElement extends LitElement {
}); });
} }
toggle_accordian(id) {
let element = this.renderRoot.getElementById(id);
element.classList.toggle('w3-hide');
}
valid_connections() {
return this.connections.filter((x) => x.tunnel === undefined);
}
valid_broadcasts() {
return this.broadcasts
.filter((x) => x.address)
.filter((x) => this.connections.map((c) => c.id).indexOf(x.pubkey) == -1);
}
render() { render() {
let self = this; let self = this;
return html` return html`
@ -235,33 +220,27 @@ class TfTabConnectionsElement extends LitElement {
> >
Connect Connect
</button> </button>
<h2 <h2>Broadcasts</h2>
class="w3-button w3-block w3-theme-d1" <ul class="w3-ul w3-border">
@click=${() => self.toggle_accordian('connections')} ${this.broadcasts
> .filter((x) => x.address)
Connections (${this.valid_connections().length}) .filter(
</h2> (x) => self.connections.map((c) => c.id).indexOf(x.pubkey) == -1
<ul class="w3-ul w3-border" id="connections"> )
${this.valid_connections().map( .map((x) => self.render_broadcast(x))}
(x) => html` <li class="w3-bar">${this.render_connection(x)}</li> `
)}
</ul> </ul>
<h2 <h2>Connections</h2>
class="w3-button w3-block w3-theme-d1" <ul class="w3-ul w3-border">
@click=${() => self.toggle_accordian('broadcasts')} ${this.connections
> .filter((x) => x.tunnel === undefined)
Broadcasts (${this.valid_broadcasts().length}) .map(
</h2> (x) => html`
<ul class="w3-ul w3-border w3-hide" id="broadcasts"> <li class="w3-bar">${this.render_connection(x)}</li>
${this.valid_broadcasts().map((x) => self.render_broadcast(x))} `
)}
</ul> </ul>
<h2 <h2>Stored Connections</h2>
class="w3-button w3-block w3-theme-d1" <ul class="w3-ul w3-border">
@click=${() => self.toggle_accordian('stored_connections')}
>
Stored Connections (${this.stored_connections.length})
</h2>
<ul class="w3-ul w3-border w3-hide" id="stored_connections">
${this.stored_connections.map( ${this.stored_connections.map(
(x) => html` (x) => html`
<li> <li>
@ -288,13 +267,8 @@ class TfTabConnectionsElement extends LitElement {
` `
)} )}
</ul> </ul>
<h2 <h2>Local Accounts</h2>
class="w3-button w3-block w3-theme-d1" <div class="w3-container">
@click=${() => self.toggle_accordian('local_accounts')}
>
Local Accounts (${this.identities.length})
</h2>
<div class="w3-container w3-hide" id="local_accounts">
${this.identities.map( ${this.identities.map(
(x) => (x) =>
html`<div html`<div

View File

@ -1,26 +1,53 @@
import * as core from './core.js'; import * as core from './core.js';
let g_next_id = 1;
let g_calls = {};
let gSessionIndex = 0; let gSessionIndex = 0;
/**
* TODOC
* @returns
*/
function makeSessionId() {
return 'session_' + (gSessionIndex++).toString();
}
/**
* TODOC
* @returns
*/
function App() { function App() {
this._on_output = null;
this._send_queue = []; this._send_queue = [];
this.calls = {};
this._next_call_id = 1;
return this; return this;
} }
/**
* TODOC
* @param {*} callback
*/
App.prototype.readOutput = function (callback) {
this._on_output = callback;
};
/**
* TODOC
* @param {*} api
* @returns
*/
App.prototype.makeFunction = function (api) { App.prototype.makeFunction = function (api) {
let self = this; let self = this;
let result = function () { let result = function () {
let id = self._next_call_id++; let id = g_next_id++;
while (!id || self.calls[id]) { while (!id || g_calls[id]) {
id = self._next_call_id++; id = g_next_id++;
} }
let promise = new Promise(function (resolve, reject) { let promise = new Promise(function (resolve, reject) {
self.calls[id] = {resolve: resolve, reject: reject}; g_calls[id] = {resolve: resolve, reject: reject};
}); });
let message = { let message = {
action: 'tfrpc', message: 'tfrpc',
method: api[0], method: api[0],
params: [...arguments], params: [...arguments],
id: id, id: id,
@ -32,6 +59,10 @@ App.prototype.makeFunction = function (api) {
return result; return result;
}; };
/**
* TODOC
* @param {*} message
*/
App.prototype.send = function (message) { App.prototype.send = function (message) {
if (this._send_queue) { if (this._send_queue) {
if (this._on_output) { if (this._on_output) {
@ -46,6 +77,11 @@ App.prototype.send = function (message) {
} }
}; };
/**
* TODOC
* @param {*} request
* @param {*} response
*/
exports.app_socket = async function socket(request, response) { exports.app_socket = async function socket(request, response) {
let process; let process;
let options = {}; let options = {};
@ -66,16 +102,10 @@ exports.app_socket = async function socket(request, response) {
try { try {
message = JSON.parse(event.data); message = JSON.parse(event.data);
} catch (error) { } catch (error) {
print( print('ERROR', error, event.data, event.data.length, event.opCode);
'WebSocket error:',
error,
event.data,
event.data.length,
event.opCode
);
return; return;
} }
if (!process && message.action == 'hello') { if (message.action == 'hello') {
let packageOwner; let packageOwner;
let packageName; let packageName;
let blobId; let blobId;
@ -92,7 +122,7 @@ exports.app_socket = async function socket(request, response) {
if (!blobId) { if (!blobId) {
response.send( response.send(
JSON.stringify({ JSON.stringify({
action: 'tfrpc', message: 'tfrpc',
method: 'error', method: 'error',
params: [message.path + ' not found'], params: [message.path + ' not found'],
id: -1, id: -1,
@ -133,7 +163,7 @@ exports.app_socket = async function socket(request, response) {
options.packageOwner = packageOwner; options.packageOwner = packageOwner;
options.packageName = packageName; options.packageName = packageName;
options.url = message.url; options.url = message.url;
let sessionId = 'session_' + (gSessionIndex++).toString(); let sessionId = makeSessionId();
if (blobId) { if (blobId) {
if (message.edit_only) { if (message.edit_only) {
response.send( response.send(
@ -145,24 +175,9 @@ exports.app_socket = async function socket(request, response) {
} }
} }
if (process) { if (process) {
process.client_api.tfrpc = function (message) { process.app.readOutput(function (message) {
if (message.id) {
let calls = process?.app?.calls;
if (calls) {
let call = calls[message.id];
if (call) {
if (message.error !== undefined) {
call.reject(message.error);
} else {
call.resolve(message.result);
}
delete calls[message.id];
}
}
}
};
process.app._on_output = (message) =>
response.send(JSON.stringify(message), 0x1); response.send(JSON.stringify(message), 0x1);
});
process.app.send(); process.app.send();
} }
@ -191,13 +206,26 @@ exports.app_socket = async function socket(request, response) {
if (process && process.timeout > 0) { if (process && process.timeout > 0) {
setTimeout(ping, process.timeout); setTimeout(ping, process.timeout);
} }
} else { } else if (message.action == 'resetPermission') {
if (process) { if (process) {
if (process.client_api[message.action]) { process.resetPermission(message.permission);
process.client_api[message.action](message); }
} else if (process.eventHandlers['message']) { } else if (message.action == 'setActiveIdentity') {
await core.invoke(process.eventHandlers['message'], [message]); process.setActiveIdentity(message.identity);
} else if (message.action == 'createIdentity') {
await process.createIdentity();
} else if (message.message == 'tfrpc') {
if (message.id && g_calls[message.id]) {
if (message.error !== undefined) {
g_calls[message.id].reject(message.error);
} else {
g_calls[message.id].resolve(message.result);
} }
delete g_calls[message.id];
}
} else {
if (process && process.eventHandlers['message']) {
await core.invoke(process.eventHandlers['message'], [message]);
} }
} }
} else if (event.opCode == 0x8) { } else if (event.opCode == 0x8) {

View File

@ -1325,7 +1325,7 @@ function _receive_websocket_message(message) {
line.append(key, message.stats[key]); line.append(key, message.stats[key]);
} }
} }
} else if (message && message.action === 'tfrpc' && message.method) { } else if (message && message.message === 'tfrpc' && message.method) {
let api = k_api[message.method]; let api = k_api[message.method];
let id = message.id; let id = message.id;
let params = message.params; let params = message.params;
@ -1333,14 +1333,14 @@ function _receive_websocket_message(message) {
Promise.resolve(api.func(...params)) Promise.resolve(api.func(...params))
.then(function (result) { .then(function (result) {
send({ send({
action: 'tfrpc', message: 'tfrpc',
id: id, id: id,
result: result, result: result,
}); });
}) })
.catch(function (error) { .catch(function (error) {
send({ send({
action: 'tfrpc', message: 'tfrpc',
id: id, id: id,
error: error, error: error,
}); });

View File

@ -3,22 +3,31 @@ import * as http from './http.js';
let gProcesses = {}; let gProcesses = {};
let gStatsTimer = false; let gStatsTimer = false;
let g_handler_index = 0; let kPingInterval = 60 * 1000;
const k_ping_interval = 60 * 1000; /**
* TODOC
function printError(error) { * @param {*} out
* @param {*} error
*/
function printError(out, error) {
if (error.stackTrace) { if (error.stackTrace) {
print(error.fileName + ':' + error.lineNumber + ': ' + error.message); out.print(error.fileName + ':' + error.lineNumber + ': ' + error.message);
print(error.stackTrace); out.print(error.stackTrace);
} else { } else {
for (let [k, v] of Object.entries(error)) { for (let [k, v] of Object.entries(error)) {
print(k, v); out.print(k, v);
} }
print(error.toString()); out.print(error.toString());
} }
} }
/**
* TODOC
* @param {*} handlers
* @param {*} argv
* @returns
*/
function invoke(handlers, argv) { function invoke(handlers, argv) {
let promises = []; let promises = [];
if (handlers) { if (handlers) {
@ -39,6 +48,12 @@ function invoke(handlers, argv) {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {*} eventName
* @param {*} argv
* @returns
*/
function broadcastEvent(eventName, argv) { function broadcastEvent(eventName, argv) {
let promises = []; let promises = [];
for (let process of Object.values(gProcesses)) { for (let process of Object.values(gProcesses)) {
@ -49,6 +64,11 @@ function broadcastEvent(eventName, argv) {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {*} message
* @returns
*/
function broadcast(message) { function broadcast(message) {
let sender = this; let sender = this;
let promises = []; let promises = [];
@ -65,6 +85,12 @@ function broadcast(message) {
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {String} eventName
* @param {*} argv
* @returns
*/
function broadcastAppEventToUser( function broadcastAppEventToUser(
user, user,
packageOwner, packageOwner,
@ -87,6 +113,12 @@ function broadcastAppEventToUser(
return Promise.all(promises); return Promise.all(promises);
} }
/**
* TODOC
* @param {*} caller
* @param {*} process
* @returns
*/
function getUser(caller, process) { function getUser(caller, process) {
return { return {
key: process.key, key: process.key,
@ -97,6 +129,12 @@ function getUser(caller, process) {
}; };
} }
/**
* TODOC
* @param {*} user
* @param {*} process
* @returns
*/
async function getApps(user, process) { async function getApps(user, process) {
if ( if (
process.credentials && process.credentials &&
@ -123,13 +161,28 @@ async function getApps(user, process) {
return {}; return {};
} }
/**
* TODOC
* @param {*} from
* @param {*} to
* @param {*} message
* @returns
*/
function postMessageInternal(from, to, message) { function postMessageInternal(from, to, message) {
if (to.eventHandlers['message']) { if (to.eventHandlers['message']) {
return invoke(to.eventHandlers['message'], [getUser(from, from), message]); return invoke(to.eventHandlers['message'], [getUser(from, from), message]);
} }
} }
/**
* TODOC
* @param {*} blobId
* @param {*} key
* @param {*} options
* @returns
*/
async function getProcessBlob(blobId, key, options) { async function getProcessBlob(blobId, key, options) {
// TODO(tasiaiso): break this down ?
let process = gProcesses[key]; let process = gProcesses[key];
if (!process && !(options && 'create' in options && !options.create)) { if (!process && !(options && 'create' in options && !options.create)) {
let resolveReady; let resolveReady;
@ -148,7 +201,7 @@ async function getProcessBlob(blobId, key, options) {
} }
process.lastActive = Date.now(); process.lastActive = Date.now();
process.lastPing = null; process.lastPing = null;
process.timeout = k_ping_interval; process.timeout = kPingInterval;
process.ready = new Promise(function (resolve, reject) { process.ready = new Promise(function (resolve, reject) {
resolveReady = resolve; resolveReady = resolve;
rejectReady = reject; rejectReady = reject;
@ -408,10 +461,10 @@ async function getProcessBlob(blobId, key, options) {
if (process.app) { if (process.app) {
process.app.makeFunction(['error'])(error); process.app.makeFunction(['error'])(error);
} else { } else {
printError(error); printError({print: print}, error);
} }
} catch (e) { } catch (e) {
printError(error); printError({print: print}, error);
} }
}; };
imports.ssb = Object.fromEntries( imports.ssb = Object.fromEntries(
@ -596,26 +649,17 @@ async function getProcessBlob(blobId, key, options) {
permissions: await imports.core.permissionsGranted(), permissions: await imports.core.permissionsGranted(),
}); });
}; };
process.client_api = { process.resetPermission = async function resetPermission(permission) {
createIdentity: function () { let user = process?.credentials?.session?.name;
return process.createIdentity(); await ssb.setUserPermission(
}, user,
resetPermission: async function resetPermission(message) { options?.packageOwner,
let user = process?.credentials?.session?.name; options?.packageName,
await ssb.setUserPermission( permission,
user, undefined
options?.packageOwner, );
options?.packageName, return process.sendPermissions();
message.permission,
undefined
);
return process.sendPermissions();
},
setActiveIdentity: function setActiveIdentity(message) {
return process.setActiveIdentity(message.identity);
},
}; };
ssb.registerImports(imports, process);
process.task.setImports(imports); process.task.setImports(imports);
process.task.activate(); process.task.activate();
let source = await ssb.blobGet(blobId); let source = await ssb.blobGet(blobId);
@ -642,7 +686,7 @@ async function getProcessBlob(blobId, key, options) {
); );
} }
} catch (e) { } catch (e) {
printError(e); printError({print: print}, e);
} }
broadcastEvent('onSessionBegin', [getUser(process, process)]); broadcastEvent('onSessionBegin', [getUser(process, process)]);
if (process.app) { if (process.app) {
@ -656,10 +700,14 @@ async function getProcessBlob(blobId, key, options) {
sendStats(); sendStats();
} }
} catch (error) { } catch (error) {
if (process?.app && process?.task?.onError) { if (process.app) {
process.task.onError(error); if (process?.task?.onError) {
process.task.onError(error);
} else {
printError({print: print}, error);
}
} else { } else {
printError(error); printError({print: print}, error);
} }
rejectReady(error); rejectReady(error);
} }
@ -679,6 +727,9 @@ ssb.addEventListener('connections', function () {
broadcastEvent('onConnectionsChanged', []); broadcastEvent('onConnectionsChanged', []);
}); });
/**
* TODOC
*/
async function loadSettings() { async function loadSettings() {
let data = {}; let data = {};
try { try {
@ -697,6 +748,9 @@ async function loadSettings() {
return data; return data;
} }
/**
* TODOC
*/
function sendStats() { function sendStats() {
let apps = Object.values(gProcesses) let apps = Object.values(gProcesses)
.filter((process) => process.app) .filter((process) => process.app)
@ -712,6 +766,8 @@ function sendStats() {
} }
} }
let g_handler_index = 0;
exports.callAppHandler = async function callAppHandler( exports.callAppHandler = async function callAppHandler(
response, response,
app_blob_id, app_blob_id,

View File

@ -31,8 +31,8 @@ pkgs.stdenv.mkDerivation rec {
domain = "dev.tildefriends.net"; domain = "dev.tildefriends.net";
owner = "cory"; owner = "cory";
repo = "tildefriends"; repo = "tildefriends";
rev = "v${version}"; rev = "f02423d0846fefd5ab21fa4542fb77ce5714547c";
hash = "sha256-vcLJCXgIrjC37t9oavK2QMRcMJljghuzKDYxQ4nyTcE="; hash = "sha256-QyM7wmViXJc4r8uTu4oE/HO3Z9tzNbFIX2+AOTQz9ZY=";
fetchSubmodules = true; fetchSubmodules = true;
}; };

File diff suppressed because one or more lines are too long

View File

@ -115,9 +115,9 @@
} }
}, },
"node_modules/@codemirror/search": { "node_modules/@codemirror/search": {
"version": "6.5.10", "version": "6.5.9",
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.10.tgz", "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.9.tgz",
"integrity": "sha512-RMdPdmsrUf53pb2VwflKGHEe1XVM07hI7vV2ntgw1dmqhimpatSJKva4VA9h4TLUDOD4EIF02201oZurpnEFsg==", "integrity": "sha512-7DdQ9aaZMMxuWB1u6IIFWWuK9NocVZwvo4nG8QjJTS6oZGvteoLSiXw3EbVZVlO08Ri2ltO89JVInMpfcJxhtg==",
"dependencies": { "dependencies": {
"@codemirror/state": "^6.0.0", "@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0", "@codemirror/view": "^6.0.0",

View File

@ -4,8 +4,7 @@
- run the tests - run the tests
- format + prettier - format + prettier
- update metadata/en-US/changelogs - update metadata/en-US/changelogs
- git tag v1.2.3 - git tag
- git tag -f latest_release
- push - push
- make a release on gitea - make a release on gitea
- upload the artifacts - upload the artifacts

View File

@ -1,18 +0,0 @@
# Upgrading
Tilde Friends can be upgraded simply by running a new executable against an
existing database.
Tilde Friends writes all data to a `db.sqlite` file, either in
`~/.local/share/tildefriends/` or in the working directory where it is run,
depending on the platform and whether each one already exists. Run with
`tildefriends run -d DB_PATH` to specify the path to the database explicitly.
This file can be copied and moved across machines as needed like any [sqlite3
database](https://www.sqlite.org/onefile.html).
Schema changes and compatibility breaks have been rare, by design. In general,
upgrading is not expected to require any manual intervention and likely does
not involve any automatic migration, either. Downgrading is not well-supported
but will probably just work excepting rare changes that will be called out in
the changelog.

View File

@ -35,5 +35,27 @@
graphviz graphviz
]; ];
}; };
nixosModules.default = {
config,
lib,
...
}: let
# Shorter name to access final settings a
# user of hello.nix module HAS ACTUALLY SET.
# cfg is a typical convention.
cfg = config.services.tildefriends;
in {
options.services.tildefriends = {
enable = lib.mkEnableOption "Enable Tilde Friends";
};
config = lib.mkIf cfg.enable {
systemd.services.tildefriends = {
wantedBy = ["multi-user.target"];
serviceConfig.ExecStart = "${pkgs.tildefriends}/bin/tildefriends";
};
};
};
}); });
} }

View File

@ -1,5 +1,3 @@
* Allow specifying all global settings from the command-line (CLI usage changed).
* Replication improvements.
* An iOS build is on TestFlight. * An iOS build is on TestFlight.
* macOS targets are debug and release like everywhere else. * macOS targets are debug and release like everywhere else.
* Running from a subdirectory is fine. * Running from a subdirectory is fine.
@ -7,9 +5,10 @@
* Invite fixes. * Invite fixes.
* Follow/block UI fixes. * Follow/block UI fixes.
* Mobile automatically logs in. * Mobile automatically logs in.
* Updates: * Allow specifying all global settings from the command-line.
* UpdateS:
* CodeMirror * CodeMirror
* OpenSSL 3.4.1 * OpenSSL 3.4.1
* libbacktrace * libbacktrace
* speedscope 1.22.2
* sqlite 3.49.1 * sqlite 3.49.1
* speedscope 1.22.2

6
package-lock.json generated
View File

@ -11,9 +11,9 @@
} }
}, },
"node_modules/prettier": { "node_modules/prettier": {
"version": "3.5.2", "version": "3.5.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
"integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", "integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
"bin": { "bin": {
"prettier": "bin/prettier.cjs" "prettier": "bin/prettier.cjs"
}, },

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unprompted.tildefriends" package="com.unprompted.tildefriends"
android:versionCode="34" android:versionCode="33"
android:versionName="0.0.29-wip"> android:versionName="0.0.28-wip">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<application <application

View File

@ -1,19 +0,0 @@
#include "api.js.h"
#include "log.h"
#include <quickjs.h>
static JSValue _tf_api_register_imports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
return JS_UNDEFINED;
}
void tf_api_register(JSContext* context)
{
JSValue global = JS_GetGlobalObject(context);
JSValue ssb = JS_GetPropertyStr(context, global, "ssb");
JS_SetPropertyStr(context, ssb, "registerImports", JS_NewCFunction(context, _tf_api_register_imports, "registerImports", 2));
JS_FreeValue(context, ssb);
JS_FreeValue(context, global);
}

View File

@ -1,18 +0,0 @@
#pragma once
/**
** \defgroup api_js JS API
** Functions that are ultimately exposed to apps.
** @{
*/
/** A JS context. */
typedef struct JSContext JSContext;
/**
** Register JS API functions.
** @param context The JS context.
*/
void tf_api_register(JSContext* context);
/** @} */

View File

@ -84,7 +84,6 @@ typedef struct _tf_http_listener_t
typedef struct _tf_http_t typedef struct _tf_http_t
{ {
bool is_shutting_down; bool is_shutting_down;
bool is_in_destroy;
tf_http_listener_t** listeners; tf_http_listener_t** listeners;
int listeners_count; int listeners_count;
@ -396,7 +395,7 @@ static void _http_add_body_bytes(tf_http_connection_t* connection, const void* d
if (fin) if (fin)
{ {
if (connection->request && connection->request->on_message) if (connection->request->on_message)
{ {
tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "websocket"); tf_trace_begin(connection->http->trace, connection->trace_name ? connection->trace_name : "websocket");
connection->request->on_message(connection->request, connection->fragment_length ? connection->fragment_op_code : op_code, connection->request->on_message(connection->request, connection->fragment_length ? connection->fragment_op_code : op_code,
@ -787,13 +786,7 @@ static void _http_free_listener_on_close(uv_handle_t* handle)
void tf_http_destroy(tf_http_t* http) void tf_http_destroy(tf_http_t* http)
{ {
if (http->is_in_destroy)
{
return;
}
http->is_shutting_down = true; http->is_shutting_down = true;
http->is_in_destroy = true;
for (int i = 0; i < http->connections_count; i++) for (int i = 0; i < http->connections_count; i++)
{ {
@ -852,10 +845,6 @@ void tf_http_destroy(tf_http_t* http)
tf_free(http); tf_free(http);
} }
else
{
http->is_in_destroy = false;
}
} }
const char* tf_http_status_text(int status) const char* tf_http_status_text(int status)
@ -974,42 +963,9 @@ static void _http_write(tf_http_connection_t* connection, const void* data, size
} }
} }
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size) void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size)
{ {
uint8_t* copy = tf_malloc(size + 16); _http_write(request->connection, data, size);
bool fin = true;
size_t header = 1;
copy[0] = (fin ? (1 << 7) : 0) | (op_code & 0xf);
if (size < 126)
{
copy[1] = size;
header += 1;
}
else if (size < (1 << 16))
{
copy[1] = 126;
copy[2] = (size >> 8) & 0xff;
copy[3] = (size >> 0) & 0xff;
header += 3;
}
else
{
uint32_t high = ((uint64_t)size >> 32) & 0xffffffff;
uint32_t low = (size >> 0) & 0xffffffff;
copy[1] = 127;
copy[2] = (high >> 24) & 0xff;
copy[3] = (high >> 16) & 0xff;
copy[4] = (high >> 8) & 0xff;
copy[5] = (high >> 0) & 0xff;
copy[6] = (low >> 24) & 0xff;
copy[7] = (low >> 16) & 0xff;
copy[8] = (low >> 8) & 0xff;
copy[9] = (low >> 0) & 0xff;
header += 9;
}
memcpy(copy + header, data, size);
_http_write(request->connection, copy, header + size);
tf_free(copy);
} }
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length) void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)

View File

@ -209,11 +209,10 @@ const char* tf_http_get_cookie(const char* cookie_header, const char* name);
** Send a websocket message. ** Send a websocket message.
** @param request The HTTP request which was previously updated to a websocket ** @param request The HTTP request which was previously updated to a websocket
** session with tf_http_request_websocket_upgrade(). ** session with tf_http_request_websocket_upgrade().
** @param op_code Websocket op code.
** @param data The message data. ** @param data The message data.
** @param size The size of data. ** @param size The size of data.
*/ */
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size); void tf_http_request_send(tf_http_request_t* request, const void* data, size_t size);
/** /**
** Upgrade an HTTP request to a websocket session. ** Upgrade an HTTP request to a websocket session.

View File

@ -152,9 +152,44 @@ static JSValue _httpd_response_send(JSContext* context, JSValueConst this_val, i
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id); tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
int opcode = 0x1; int opcode = 0x1;
JS_ToInt32(context, &opcode, argv[1]); JS_ToInt32(context, &opcode, argv[1]);
size_t length = 0; uint64_t length = 0;
const char* message = JS_ToCStringLen(context, &length, argv[0]); size_t length_size = 0;
tf_http_request_websocket_send(request, opcode, message, length); const char* message = JS_ToCStringLen(context, &length_size, argv[0]);
length = length_size;
uint8_t* copy = tf_malloc(length + 16);
bool fin = true;
size_t header = 1;
copy[0] = (fin ? (1 << 7) : 0) | (opcode & 0xf);
if (length < 126)
{
copy[1] = length;
header += 1;
}
else if (length < (1 << 16))
{
copy[1] = 126;
copy[2] = (length >> 8) & 0xff;
copy[3] = (length >> 0) & 0xff;
header += 3;
}
else
{
uint32_t high = (length >> 32) & 0xffffffff;
uint32_t low = (length >> 0) & 0xffffffff;
copy[1] = 127;
copy[2] = (high >> 24) & 0xff;
copy[3] = (high >> 16) & 0xff;
copy[4] = (high >> 8) & 0xff;
copy[5] = (high >> 0) & 0xff;
copy[6] = (low >> 24) & 0xff;
copy[7] = (low >> 16) & 0xff;
copy[8] = (low >> 8) & 0xff;
copy[9] = (low >> 0) & 0xff;
header += 9;
}
memcpy(copy + header, message, length);
tf_http_request_send(request, copy, header + length);
tf_free(copy);
JS_FreeCString(context, message); JS_FreeCString(context, message);
return JS_UNDEFINED; return JS_UNDEFINED;
} }

View File

@ -13,13 +13,13 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.0.29</string> <string>0.0.28</string>
<key>CFBundleSupportedPlatforms</key> <key>CFBundleSupportedPlatforms</key>
<array> <array>
<string>iPhoneOS</string> <string>iPhoneOS</string>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>11</string> <string>8</string>
<key>DTPlatformName</key> <key>DTPlatformName</key>
<string>iphoneos</string> <string>iphoneos</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>

BIN
src/ios/icons/Assets.car Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

View File

@ -1 +1 @@
{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]} {"images":[{"idiom":"ios-marketing","scale":"1x","size":"1024x1024","filename":"icon-ios-marketing-1-1024-1024.png"}],"info":{"author":"xcode","version":1}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View File

@ -0,0 +1 @@
{"info": {"version": 1, "author": "xcode"}}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

BIN
src/ios/icons/ios/100.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
src/ios/icons/ios/114.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
src/ios/icons/ios/128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
src/ios/icons/ios/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

BIN
src/ios/icons/ios/172.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/ios/icons/ios/180.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/ios/icons/ios/196.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
src/ios/icons/ios/216.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/ios/icons/ios/256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/ios/icons/ios/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
src/ios/icons/ios/48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
src/ios/icons/ios/50.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
src/ios/icons/ios/512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
src/ios/icons/ios/55.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/ios/icons/ios/64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
src/ios/icons/ios/88.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
src/ios/icons/ios/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

BIN
src/ios/icons/uploaded.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@ -1057,7 +1057,7 @@ static int _tf_command_get_profile(const char* file, int argc, char* argv[])
sqlite3_close(db); sqlite3_close(db);
tf_free((void*)profile); tf_free((void*)profile);
tf_free((void*)default_db_path); tf_free((void*)default_db_path);
return profile != NULL ? EXIT_SUCCESS : EXIT_FAILURE; return profile != NULL;
} }
static int _tf_command_get_contacts(const char* file, int argc, char* argv[]) static int _tf_command_get_contacts(const char* file, int argc, char* argv[])
@ -1299,12 +1299,10 @@ static int _tf_run_task(const tf_run_args_t* args, int index)
sqlite3* db = tf_ssb_acquire_db_reader(ssb); sqlite3* db = tf_ssb_acquire_db_reader(ssb);
int64_t http_port = 0; int64_t http_port = 0;
int64_t https_port = 0; int64_t https_port = 0;
char out_http_port_file[512] = "";
tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port); tf_ssb_db_get_global_setting_int64(db, "http_port", &http_port);
tf_ssb_db_get_global_setting_int64(db, "https_port", &https_port); tf_ssb_db_get_global_setting_int64(db, "https_port", &https_port);
tf_ssb_db_get_global_setting_string(db, "out_http_port_file", out_http_port_file, sizeof(out_http_port_file));
tf_ssb_release_db_reader(ssb, db); tf_ssb_release_db_reader(ssb, db);
if (http_port || https_port || *out_http_port_file) if (http_port || https_port)
{ {
if (args->zip) if (args->zip)
{ {

View File

@ -22,6 +22,8 @@ static int64_t s_tls_malloc_size;
static int64_t s_js_malloc_size; static int64_t s_js_malloc_size;
static int64_t s_sqlite_malloc_size; static int64_t s_sqlite_malloc_size;
extern uint32_t fnv32a(const void* buffer, int length, uint32_t start);
static size_t _tf_mem_round_up(size_t size) static size_t _tf_mem_round_up(size_t size)
{ {
return (size + 7) & ~7; return (size + 7) & ~7;
@ -154,7 +156,7 @@ static void _tf_mem_summarize(void* ptr, size_t size, int frames_count, void* co
{ {
summary_t* summary = user_data; summary_t* summary = user_data;
tf_mem_allocation_t allocation = { tf_mem_allocation_t allocation = {
.stack_hash = tf_util_fnv32a(frames, sizeof(void*) * frames_count, 0), .stack_hash = fnv32a(frames, sizeof(void*) * frames_count, 0),
.count = 1, .count = 1,
.size = size, .size = size,
.frames_count = frames_count, .frames_count = frames_count,

View File

@ -746,16 +746,13 @@ static bool _tf_ssb_connection_get_request_callback(
*out_name = request->name; *out_name = request->name;
} }
request->last_active = uv_now(connection->ssb->loop); request->last_active = uv_now(connection->ssb->loop);
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_connection_is_closing(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection))) if (connection->flags & k_tf_ssb_connect_flag_one_shot)
{ {
if ((connection->flags & k_tf_ssb_connect_flag_one_shot)) uv_timer_start(&connection->activity_timer, _tf_ssb_connection_activity_timer, k_activity_timeout_ms, 0);
{ }
uv_timer_start(&connection->activity_timer, _tf_ssb_connection_activity_timer, k_activity_timeout_ms, 0); if (uv_timer_get_due_in(&connection->ssb->request_activity_timer) == 0)
} {
if (uv_timer_get_due_in(&connection->ssb->request_activity_timer) == 0) uv_timer_start(&connection->ssb->request_activity_timer, _tf_ssb_request_activity_timer, k_rpc_active_ms, 0);
{
uv_timer_start(&connection->ssb->request_activity_timer, _tf_ssb_request_activity_timer, k_rpc_active_ms, 0);
}
} }
return true; return true;
} }
@ -802,16 +799,13 @@ void tf_ssb_connection_add_request(tf_ssb_connection_t* connection, int32_t requ
connection->requests_count++; connection->requests_count++;
connection->ssb->request_count++; connection->ssb->request_count++;
} }
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_connection_is_closing(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection))) if (connection->flags & k_tf_ssb_connect_flag_one_shot)
{ {
if (connection->flags & k_tf_ssb_connect_flag_one_shot) uv_timer_start(&connection->activity_timer, _tf_ssb_connection_activity_timer, k_activity_timeout_ms, 0);
{ }
uv_timer_start(&connection->activity_timer, _tf_ssb_connection_activity_timer, k_activity_timeout_ms, 0); if (uv_timer_get_due_in(&connection->ssb->request_activity_timer) == 0)
} {
if (uv_timer_get_due_in(&connection->ssb->request_activity_timer) == 0) uv_timer_start(&connection->ssb->request_activity_timer, _tf_ssb_request_activity_timer, k_rpc_active_ms, 0);
{
uv_timer_start(&connection->ssb->request_activity_timer, _tf_ssb_request_activity_timer, k_rpc_active_ms, 0);
}
} }
_tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_update, connection); _tf_ssb_notify_connections_changed(connection->ssb, k_tf_ssb_change_update, connection);
connection->last_notified_active = now_ms; connection->last_notified_active = now_ms;
@ -2003,7 +1997,7 @@ static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const ch
if (!connection->is_closing) if (!connection->is_closing)
{ {
connection->is_closing = true; connection->is_closing = true;
uv_timer_start(&connection->linger_timer, _tf_ssb_connection_linger_timer, ssb->shutting_down ? 0 : 5000, 0); uv_timer_start(&connection->linger_timer, _tf_ssb_connection_linger_timer, 5000, 0);
_tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_update, connection); _tf_ssb_notify_connections_changed(ssb, k_tf_ssb_change_update, connection);
} }
if (connection->connect_callback) if (connection->connect_callback)

View File

@ -1277,7 +1277,7 @@ static int _following_compare(const void* a, const void* b)
static bool _has_following_entry(const char* id, following_t** list, int count) static bool _has_following_entry(const char* id, following_t** list, int count)
{ {
return count ? bsearch(id, list, count, sizeof(following_t*), _following_compare) != NULL : false; return count ? bsearch(id, list, count, sizeof(following_t*), _following_compare) != 0 : false;
} }
static bool _add_following_entry(following_t*** list, int* count, following_t* add) static bool _add_following_entry(following_t*** list, int* count, following_t* add)
@ -1432,22 +1432,21 @@ static void _populate_follows_and_blocks(tf_ssb_t* ssb, following_t* entry, foll
static void _get_following( static void _get_following(
tf_ssb_t* ssb, following_t* entry, following_t*** following, int* following_count, int depth, int max_depth, block_node_t* active_blocks, bool include_blocks) tf_ssb_t* ssb, following_t* entry, following_t*** following, int* following_count, int depth, int max_depth, block_node_t* active_blocks, bool include_blocks)
{ {
int old_depth = entry->depth;
entry->depth = tf_min(depth, entry->depth); entry->depth = tf_min(depth, entry->depth);
if (depth < max_depth && depth < old_depth) if (depth < max_depth && !entry->populated && !_is_blocked_by_active_blocks(entry->id, active_blocks))
{ {
if (!_is_blocked_by_active_blocks(entry->id, active_blocks)) entry->populated = true;
{ _populate_follows_and_blocks(ssb, entry, following, following_count, active_blocks, include_blocks);
if (!entry->populated)
{
entry->populated = true;
_populate_follows_and_blocks(ssb, entry, following, following_count, active_blocks, include_blocks);
}
if (depth < max_depth)
{
block_node_t blocks = { .entry = entry, .parent = active_blocks }; block_node_t blocks = { .entry = entry, .parent = active_blocks };
for (int i = 0; i < entry->following_count; i++) for (int i = 0; i < entry->following_count; i++)
{ {
_get_following(ssb, entry->following[i], following, following_count, depth + 1, max_depth, &blocks, include_blocks); if (!_has_following_entry(entry->following[i]->id, entry->blocking, entry->blocking_count))
{
_get_following(ssb, entry->following[i], following, following_count, depth + 1, max_depth, &blocks, include_blocks);
}
} }
} }
} }
@ -2331,120 +2330,3 @@ bool tf_ssb_db_has_invite(sqlite3* db, const char* id)
} }
return has; return has;
} }
static void _tf_ssb_db_get_identity_info_visit(const char* identity, void* user_data)
{
tf_ssb_identity_info_t* info = user_data;
info->identity = tf_resize_vec(info->identity, (info->count + 1) * sizeof(char*));
info->name = tf_resize_vec(info->name, (info->count + 1) * sizeof(char*));
char buffer[k_id_base64_len];
snprintf(buffer, sizeof(buffer), "@%s", identity);
info->identity[info->count] = tf_strdup(buffer);
info->name[info->count] = NULL;
info->count++;
}
tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* user, const char* package_owner, const char* package_name)
{
tf_ssb_identity_info_t* info = tf_malloc(sizeof(tf_ssb_identity_info_t));
*info = (tf_ssb_identity_info_t) { 0 };
char id[k_id_base64_len] = "";
if (tf_ssb_db_user_has_permission(ssb, NULL, user, "administration"))
{
if (tf_ssb_whoami(ssb, id, sizeof(id)))
{
_tf_ssb_db_get_identity_info_visit(*id == '@' ? id + 1 : id, info);
}
}
tf_ssb_db_identity_visit(ssb, user, _tf_ssb_db_get_identity_info_visit, info);
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
sqlite3_stmt* statement = NULL;
int result = sqlite3_prepare(db,
"SELECT author, name FROM ( "
" SELECT "
" messages.author, "
" RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, "
" messages.content ->> 'name' AS name "
" FROM messages "
" JOIN identities ON messages.author = ('@' || identities.public_key) "
" WHERE "
" (identities.user = ? OR identities.public_key = ?) AND "
" json_extract(messages.content, '$.type') = 'about' AND "
" content ->> 'about' = messages.author AND name IS NOT NULL) "
"WHERE author_rank = 1 ",
-1, &statement, NULL);
if (result == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, *id == '@' ? id + 1 : id, -1, NULL) == SQLITE_OK)
{
int r = SQLITE_OK;
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
{
const char* identity = (const char*)sqlite3_column_text(statement, 0);
const char* name = (const char*)sqlite3_column_text(statement, 1);
for (int i = 0; i < info->count; i++)
{
if (!info->name[i] && strcmp(info->identity[i], identity) == 0)
{
info->name[i] = tf_strdup(name);
break;
}
}
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s.\n", sqlite3_errmsg(db));
}
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_ssb_release_db_reader(ssb, db);
size_t size = sizeof(tf_ssb_identity_info_t) + sizeof(char*) * info->count * 2;
for (int i = 0; i < info->count; i++)
{
size += strlen(info->identity[i]) + 1;
size += info->name[i] ? strlen(info->name[i]) + 1 : 0;
}
tf_ssb_identity_info_t* copy = tf_malloc(size);
*copy = *info;
copy->identity = (const char**)(copy + 1);
copy->name = (const char**)(copy + 1) + copy->count;
char* p = (char*)((const char**)(copy + 1) + copy->count * 2);
for (int i = 0; i < info->count; i++)
{
size_t length = strlen(info->identity[i]);
memcpy(p, info->identity[i], length + 1);
copy->identity[i] = p;
p += length + 1;
tf_free((void*)info->identity[i]);
if (info->name[i])
{
length = strlen(info->name[i]);
memcpy(p, info->name[i], length + 1);
copy->name[i] = p;
p += length + 1;
tf_free((void*)info->name[i]);
}
else
{
copy->name[i] = NULL;
}
}
tf_free(info->name);
tf_free(info->identity);
tf_free(info);
return copy;
}

View File

@ -564,29 +564,4 @@ int tf_ssb_sqlite_authorizer(void* user_data, int action_code, const char* arg0,
*/ */
bool tf_ssb_db_has_invite(sqlite3* db, const char* id); bool tf_ssb_db_has_invite(sqlite3* db, const char* id);
/**
** Identity information
*/
typedef struct _tf_ssb_identity_info_t
{
/** An array of identities. */
const char** identity;
/** A array of identities, one for each identity. */
const char** name;
/** The number of elements in both the identity and name arrays. */
int count;
/** The active identity. */
char active_identity[k_id_base64_len];
} tf_ssb_identity_info_t;
/**
** Get available identities, names, and the active identity for a user.
** @param ssb The SSB instance.
** @param user The user name.
** @param package_owner The owner of the package for which identity info is being fetched.
** @param package_name The name of the package for which identity info is being fetched.
** @return A struct of identities, names, and the active identity. Free with tf_free().
*/
tf_ssb_identity_info_t* tf_ssb_db_get_identity_info(tf_ssb_t* ssb, const char* user, const char* package_owner, const char* package_name);
/** @} */ /** @} */

View File

@ -613,14 +613,87 @@ typedef struct _identity_info_work_t
const char* name; const char* name;
const char* package_owner; const char* package_owner;
const char* package_name; const char* package_name;
tf_ssb_identity_info_t* info; int count;
char** identities;
char** names;
int result;
char active_identity[k_id_base64_len];
JSValue promise[2]; JSValue promise[2];
} identity_info_work_t; } identity_info_work_t;
static void _tf_ssb_getIdentityInfo_visit(const char* identity, void* data)
{
identity_info_work_t* request = data;
request->identities = tf_resize_vec(request->identities, (request->count + 1) * sizeof(char*));
request->names = tf_resize_vec(request->names, (request->count + 1) * sizeof(char*));
char buffer[k_id_base64_len];
snprintf(buffer, sizeof(buffer), "@%s", identity);
request->identities[request->count] = tf_strdup(buffer);
request->names[request->count] = NULL;
request->count++;
}
static void _tf_ssb_getIdentityInfo_work(tf_ssb_t* ssb, void* user_data) static void _tf_ssb_getIdentityInfo_work(tf_ssb_t* ssb, void* user_data)
{ {
identity_info_work_t* request = user_data; identity_info_work_t* request = user_data;
request->info = tf_ssb_db_get_identity_info(ssb, request->name, request->package_owner, request->package_name); char id[k_id_base64_len] = "";
if (tf_ssb_db_user_has_permission(ssb, NULL, request->name, "administration"))
{
if (tf_ssb_whoami(ssb, id, sizeof(id)))
{
_tf_ssb_getIdentityInfo_visit(*id == '@' ? id + 1 : id, request);
}
}
tf_ssb_db_identity_visit(ssb, request->name, _tf_ssb_getIdentityInfo_visit, request);
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
sqlite3_stmt* statement = NULL;
request->result = sqlite3_prepare(db,
"SELECT author, name FROM ( "
" SELECT "
" messages.author, "
" RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank, "
" messages.content ->> 'name' AS name "
" FROM messages "
" JOIN identities ON messages.author = ('@' || identities.public_key) "
" WHERE "
" (identities.user = ? OR identities.public_key = ?) AND "
" json_extract(messages.content, '$.type') = 'about' AND "
" content ->> 'about' = messages.author AND name IS NOT NULL) "
"WHERE author_rank = 1 ",
-1, &statement, NULL);
if (request->result == SQLITE_OK)
{
if (sqlite3_bind_text(statement, 1, request->name, -1, NULL) == SQLITE_OK && sqlite3_bind_text(statement, 2, *id == '@' ? id + 1 : id, -1, NULL) == SQLITE_OK)
{
int r = SQLITE_OK;
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
{
const char* identity = (const char*)sqlite3_column_text(statement, 0);
const char* name = (const char*)sqlite3_column_text(statement, 1);
for (int i = 0; i < request->count; i++)
{
if (!request->names[i] && strcmp(request->identities[i], identity) == 0)
{
request->names[i] = tf_strdup(name);
break;
}
}
}
}
sqlite3_finalize(statement);
}
else
{
tf_printf("prepare failed: %s.\n", sqlite3_errmsg(db));
}
tf_ssb_db_identity_get_active(db, request->name, request->package_owner, request->package_name, request->active_identity, sizeof(request->active_identity));
if (!*request->active_identity && request->count)
{
snprintf(request->active_identity, sizeof(request->active_identity), "%s", request->identities[0]);
}
tf_ssb_release_db_reader(ssb, db);
} }
static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void* user_data) static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void* user_data)
@ -630,20 +703,20 @@ static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void*
JSValue result = JS_NewObject(context); JSValue result = JS_NewObject(context);
JSValue identities = JS_NewArray(context); JSValue identities = JS_NewArray(context);
for (int i = 0; i < request->info->count; i++) for (int i = 0; i < request->count; i++)
{ {
JS_SetPropertyUint32(context, identities, i, JS_NewString(context, request->info->identity[i])); JS_SetPropertyUint32(context, identities, i, JS_NewString(context, request->identities[i]));
} }
JS_SetPropertyStr(context, result, "identities", identities); JS_SetPropertyStr(context, result, "identities", identities);
JSValue names = JS_NewObject(context); JSValue names = JS_NewObject(context);
for (int i = 0; i < request->info->count; i++) for (int i = 0; i < request->count; i++)
{ {
JS_SetPropertyStr(context, names, request->info->identity[i], JS_NewString(context, request->info->name[i] ? request->info->name[i] : request->info->identity[i])); JS_SetPropertyStr(context, names, request->identities[i], JS_NewString(context, request->names[i] ? request->names[i] : request->identities[i]));
} }
JS_SetPropertyStr(context, result, "names", names); JS_SetPropertyStr(context, result, "names", names);
JS_SetPropertyStr(context, result, "identity", JS_NewString(context, request->info->active_identity)); JS_SetPropertyStr(context, result, "identity", JS_NewString(context, request->active_identity));
JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 1, &result); JSValue error = JS_Call(context, request->promise[0], JS_UNDEFINED, 1, &result);
tf_util_report_error(context, error); tf_util_report_error(context, error);
@ -652,10 +725,16 @@ static void _tf_ssb_getIdentityInfo_after_work(tf_ssb_t* ssb, int status, void*
JS_FreeValue(context, request->promise[0]); JS_FreeValue(context, request->promise[0]);
JS_FreeValue(context, request->promise[1]); JS_FreeValue(context, request->promise[1]);
for (int i = 0; i < request->count; i++)
{
tf_free(request->identities[i]);
tf_free(request->names[i]);
}
tf_free(request->identities);
tf_free(request->names);
tf_free((void*)request->name); tf_free((void*)request->name);
tf_free((void*)request->package_owner); tf_free((void*)request->package_owner);
tf_free((void*)request->package_name); tf_free((void*)request->package_name);
tf_free(request->info);
tf_free(request); tf_free(request);
} }

View File

@ -83,19 +83,6 @@ static void _tf_ssb_rpc_blobs_get_after_work(tf_ssb_connection_t* connection, in
tf_free(work); tf_free(work);
} }
static void _tf_ssb_blobs_get_callback(tf_ssb_connection_t* connection, bool skip, void* user_data)
{
blobs_get_work_t* work = user_data;
if (!skip)
{
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_blobs_get_work, _tf_ssb_rpc_blobs_get_after_work, work);
}
else
{
_tf_ssb_rpc_blobs_get_after_work(connection, -1, work);
}
}
static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{ {
if (flags & k_ssb_rpc_flag_end_error) if (flags & k_ssb_rpc_flag_end_error)
@ -135,7 +122,7 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
.request_number = request_number, .request_number = request_number,
}; };
snprintf(work->id, sizeof(work->id), "%s", id); snprintf(work->id, sizeof(work->id), "%s", id);
tf_ssb_connection_schedule_idle(connection, id, _tf_ssb_blobs_get_callback, work); tf_ssb_connection_run_work(connection, _tf_ssb_rpc_blobs_get_work, _tf_ssb_rpc_blobs_get_after_work, work);
JS_FreeCString(context, id); JS_FreeCString(context, id);
JS_FreeValue(context, arg); JS_FreeValue(context, arg);
@ -593,7 +580,6 @@ static void _tf_ssb_rpc_connection_blobs_get_callback(
bool stored = true; bool stored = true;
tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_stream | k_ssb_rpc_flag_end_error, -request_number, NULL, tf_ssb_connection_rpc_send(connection, k_ssb_rpc_flag_json | k_ssb_rpc_flag_stream | k_ssb_rpc_flag_end_error, -request_number, NULL,
(const uint8_t*)(stored ? "true" : "false"), strlen(stored ? "true" : "false"), NULL, NULL, NULL); (const uint8_t*)(stored ? "true" : "false"), strlen(stored ? "true" : "false"), NULL, NULL, NULL);
tf_ssb_connection_remove_request(connection, -request_number);
} }
} }
@ -1115,23 +1101,6 @@ static void _tf_ssb_rpc_ebt_replicate_resend_clock(tf_ssb_connection_t* connecti
} }
} }
static void _tf_ssb_rpc_ebt_schedule_send_clock(tf_ssb_connection_t* connection)
{
tf_ssb_ebt_t* ebt = tf_ssb_connection_get_ebt(connection);
int pending = tf_ssb_ebt_get_send_clock_pending(ebt) + 1;
tf_ssb_ebt_set_send_clock_pending(ebt, pending);
if (pending == 1)
{
resend_clock_t* resend = tf_malloc(sizeof(resend_clock_t));
*resend = (resend_clock_t) {
.connection = connection,
.request_number = -tf_ssb_connection_get_ebt_request_number(connection),
.pending = pending,
};
tf_ssb_connection_schedule_idle(connection, "ebt.clock", _tf_ssb_rpc_ebt_replicate_resend_clock, resend);
}
}
static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data) static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{ {
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection); tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
@ -1139,7 +1108,6 @@ static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t f
if (_is_error(context, args)) if (_is_error(context, args))
{ {
/* TODO: Send createHistoryStream. */ /* TODO: Send createHistoryStream. */
tf_ssb_connection_set_ebt_request_number(connection, 0);
tf_ssb_connection_remove_request(connection, -request_number); tf_ssb_connection_remove_request(connection, -request_number);
return; return;
} }
@ -1172,7 +1140,18 @@ static void _tf_ssb_rpc_ebt_replicate(tf_ssb_connection_t* connection, uint8_t f
if (resend_clock && tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection)) if (resend_clock && tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection))
{ {
_tf_ssb_rpc_ebt_schedule_send_clock(connection); int pending = tf_ssb_ebt_get_send_clock_pending(ebt) + 1;
tf_ssb_ebt_set_send_clock_pending(ebt, pending);
if (pending == 1)
{
resend_clock_t* resend = tf_malloc(sizeof(resend_clock_t));
*resend = (resend_clock_t) {
.connection = connection,
.request_number = request_number,
.pending = pending,
};
tf_ssb_connection_schedule_idle(connection, "ebt.clock", _tf_ssb_rpc_ebt_replicate_resend_clock, resend);
}
} }
JS_FreeValue(context, name); JS_FreeValue(context, name);
JS_FreeValue(context, author); JS_FreeValue(context, author);
@ -1876,25 +1855,10 @@ static void _tf_ssb_rpc_invite_use(tf_ssb_connection_t* connection, uint8_t flag
tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work); tf_ssb_connection_run_work(connection, _tf_ssb_rpc_invite_use_work, _tf_ssb_rpc_invite_use_after_work, work);
} }
static void _tf_ssb_rpc_message_added_callback(tf_ssb_t* ssb, const char* id, void* user_data)
{
tf_ssb_connection_t* connections[256];
int count = tf_ssb_get_connections(ssb, connections, tf_countof(connections));
for (int i = 0; i < count; i++)
{
tf_ssb_connection_t* connection = connections[i];
if (tf_ssb_connection_is_connected(connection) && !tf_ssb_is_shutting_down(tf_ssb_connection_get_ssb(connection)) && !tf_ssb_connection_is_closing(connection))
{
_tf_ssb_rpc_ebt_schedule_send_clock(connections[i]);
}
}
}
void tf_ssb_rpc_register(tf_ssb_t* ssb) void tf_ssb_rpc_register(tf_ssb_t* ssb)
{ {
tf_ssb_add_connections_changed_callback(ssb, _tf_ssb_rpc_connections_changed_callback, NULL, NULL); tf_ssb_add_connections_changed_callback(ssb, _tf_ssb_rpc_connections_changed_callback, NULL, NULL);
tf_ssb_add_broadcasts_changed_callback(ssb, _tf_ssb_rpc_broadcasts_changed_callback, NULL, NULL); tf_ssb_add_broadcasts_changed_callback(ssb, _tf_ssb_rpc_broadcasts_changed_callback, NULL, NULL);
tf_ssb_add_message_added_callback(ssb, _tf_ssb_rpc_message_added_callback, NULL, NULL);
tf_ssb_add_rpc_callback(ssb, "gossip.ping", _tf_ssb_rpc_gossip_ping, NULL, NULL); /* DUPLEX */ tf_ssb_add_rpc_callback(ssb, "gossip.ping", _tf_ssb_rpc_gossip_ping, NULL, NULL); /* DUPLEX */
tf_ssb_add_rpc_callback(ssb, "blobs.get", _tf_ssb_rpc_blobs_get, NULL, NULL); /* SOURCE */ tf_ssb_add_rpc_callback(ssb, "blobs.get", _tf_ssb_rpc_blobs_get, NULL, NULL); /* SOURCE */
tf_ssb_add_rpc_callback(ssb, "blobs.has", _tf_ssb_rpc_blobs_has, NULL, NULL); /* ASYNC */ tf_ssb_add_rpc_callback(ssb, "blobs.has", _tf_ssb_rpc_blobs_has, NULL, NULL); /* ASYNC */

View File

@ -1,6 +1,5 @@
#include "task.h" #include "task.h"
#include "api.js.h"
#include "bcrypt.js.h" #include "bcrypt.js.h"
#include "database.js.h" #include "database.js.h"
#include "file.js.h" #include "file.js.h"
@ -164,7 +163,6 @@ typedef struct _tf_task_t
promise_stack_t* _promise_stacks; promise_stack_t* _promise_stacks;
int _promise_stack_count; int _promise_stack_count;
bool _promise_stack_debug;
timeout_t* timeouts; timeout_t* timeouts;
@ -1253,13 +1251,10 @@ static void _add_promise_stack(tf_task_t* task, uint32_t hash, const char* stack
static void _remove_promise_stack(tf_task_t* task, uint32_t hash) static void _remove_promise_stack(tf_task_t* task, uint32_t hash)
{ {
if (task->_promise_stack_debug) promise_stack_t* found = bsearch(&hash, task->_promise_stacks, task->_promise_stack_count, sizeof(promise_stack_t), _promise_stack_compare);
if (found)
{ {
promise_stack_t* found = bsearch(&hash, task->_promise_stacks, task->_promise_stack_count, sizeof(promise_stack_t), _promise_stack_compare); found->count--;
if (found)
{
found->count--;
}
} }
} }
@ -1275,26 +1270,33 @@ static void _tf_task_free_promise(tf_task_t* task, promiseid_t id)
} }
} }
uint32_t fnv32a(const void* buffer, int length, uint32_t start)
{
uint32_t result = 0x811c9dc5;
for (int i = 0; i < length; i++)
{
result ^= ((const uint8_t*)buffer)[i];
result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
}
return result;
}
JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise) JSValue tf_task_allocate_promise(tf_task_t* task, promiseid_t* out_promise)
{ {
uint32_t stack_hash = 0; JSValue error = JS_ThrowInternalError(task->_context, "promise callstack");
if (task->_promise_stack_debug) JSValue exception = JS_GetException(task->_context);
{ JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack");
JSValue error = JS_ThrowInternalError(task->_context, "promise callstack"); size_t length = 0;
JSValue exception = JS_GetException(task->_context); const char* stack = JS_ToCStringLen(task->_context, &length, stack_value);
JSValue stack_value = JS_GetPropertyStr(task->_context, exception, "stack"); uint32_t stack_hash = fnv32a((const void*)stack, (int)length, 0);
size_t length = 0; void* buffer[32];
const char* stack = JS_ToCStringLen(task->_context, &length, stack_value); int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer));
stack_hash = tf_util_fnv32a((const void*)stack, (int)length, 0); stack_hash = fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash);
void* buffer[32]; _add_promise_stack(task, stack_hash, stack, buffer, count);
int count = tf_util_backtrace(buffer, sizeof(buffer) / sizeof(*buffer)); JS_FreeCString(task->_context, stack);
stack_hash = tf_util_fnv32a((const void*)buffer, sizeof(void*) * count, stack_hash); JS_FreeValue(task->_context, stack_value);
_add_promise_stack(task, stack_hash, stack, buffer, count); JS_FreeValue(task->_context, exception);
JS_FreeCString(task->_context, stack); JS_FreeValue(task->_context, error);
JS_FreeValue(task->_context, stack_value);
JS_FreeValue(task->_context, exception);
JS_FreeValue(task->_context, error);
}
promiseid_t promiseId; promiseid_t promiseId;
do do
@ -1589,10 +1591,6 @@ tf_task_t* tf_task_create()
*task = (tf_task_t) { 0 }; *task = (tf_task_t) { 0 };
++_count; ++_count;
char buffer[8] = { 0 };
size_t buffer_size = sizeof(buffer);
task->_promise_stack_debug = uv_os_getenv("TF_PROMISE_DEBUG", buffer, &buffer_size) == 0 && strcmp(buffer, "1") == 0;
JSMallocFunctions funcs = { 0 }; JSMallocFunctions funcs = { 0 };
tf_get_js_malloc_functions(&funcs); tf_get_js_malloc_functions(&funcs);
task->_runtime = JS_NewRuntime2(&funcs, NULL); task->_runtime = JS_NewRuntime2(&funcs, NULL);
@ -1699,7 +1697,6 @@ void tf_task_activate(tf_task_t* task)
task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key); task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key);
tf_ssb_set_trace(task->_ssb, task->_trace); tf_ssb_set_trace(task->_ssb, task->_trace);
tf_ssb_register(context, task->_ssb); tf_ssb_register(context, task->_ssb);
tf_api_register(context);
tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task); tf_ssb_set_hitch_callback(task->_ssb, _tf_task_record_hitch, task);
if (task->_args) if (task->_args)

View File

@ -683,14 +683,3 @@ bool tf_util_is_mobile()
{ {
return TF_IS_MOBILE != 0; return TF_IS_MOBILE != 0;
} }
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start)
{
uint32_t result = 0x811c9dc5;
for (int i = 0; i < length; i++)
{
result ^= ((const uint8_t*)buffer)[i];
result += (result << 1) + (result << 4) + (result << 7) + (result << 8) + (result << 24);
}
return result;
}

View File

@ -224,13 +224,4 @@ void tf_util_document_settings(const char* line_prefix);
*/ */
bool tf_util_is_mobile(); bool tf_util_is_mobile();
/**
** Compute a 32-bit hash of a buffer.
** @param buffer The data.
** @param length The size of the buffer in bytes.
** @param start The hash seed.
** @return The computed hash.
*/
uint32_t tf_util_fnv32a(const void* buffer, int length, uint32_t start);
/** @} */ /** @} */

View File

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

View File

@ -25,7 +25,6 @@ def fix_title(entry):
return entry.title.split('\n')[0] return entry.title.split('\n')[0]
def get_entries(): def get_entries():
seen = set()
results = [] results = []
for name, url in k_feeds.items(): for name, url in k_feeds.items():
feed = feedparser.parse(url) feed = feedparser.parse(url)
@ -42,13 +41,9 @@ def get_entries():
continue continue
if entry.summary.startswith('<a href='): if entry.summary.startswith('<a href='):
for m in re.findall(r'<a href="(.*?)">.*?</a>$\s*^([^\n]+)$', entry.summary, re.S | re.M): for m in re.findall(r'<a href="(.*?)">.*?</a>$\s*^([^\n]+)$', entry.summary, re.S | re.M):
if not m[0] in seen: results.append((time.mktime(entry.get('updated_parsed')), name, m[0], m[1]))
seen.add(m[0])
results.append((time.mktime(entry.get('updated_parsed')), name, m[0], m[1]))
else: else:
if not entry.link in seen: results.append((time.mktime(entry.get('updated_parsed')), name, entry.link, entry.title.split('\n')[0]))
seen.add(entry.link)
results.append((time.mktime(entry.get('updated_parsed')), name, entry.link, entry.title.split('\n')[0]))
results.sort() results.sort()
results.reverse() results.reverse()
return results return results