18 Commits

Author SHA1 Message Date
a15bb8e994 Don't rely on being idle to do anything. Fixes JS job starvation on slow machines more.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-21 12:53:38 -04:00
6f487100cd Format. 2024-08-20 12:35:42 -04:00
0693a2315f Fix async job starvation if everything is running too slowly.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-20 12:26:34 -04:00
f360e886ff Make -t peer_exchange complete and test that something happened.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-19 12:29:40 -04:00
6ea08cc5dc Add the beginnings of a peers.exchange test and begin to fix fallout.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 7m3s
2024-08-15 12:48:24 -04:00
347c706d6f ci: undefined reference to arc4random_buf
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 7m11s
2024-08-15 12:12:58 -04:00
5f5e6616c7 Install graphviz for building docs.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 6m1s
2024-08-14 21:16:31 -04:00
657bcadc7e Work-in-progress, untested, naive peer exchange. Intended to be disabled by default by a setting.
Some checks failed
Build Tilde Friends / Build-All (push) Failing after 5m56s
2024-08-14 21:07:16 -04:00
107666cc60 Add a setting to toggle whether replication is allowed, to be able to make a pure room, or even less, node. 2024-08-14 20:02:46 -04:00
b37669184a doxygen -u # 1.9.8 2024-08-14 20:01:21 -04:00
163a01f224 sqlite 3.46.1. 2024-08-14 19:43:57 -04:00
3d58094199 Fix some sanitizer issues, and disable LTO in debug builds to save some iteration time. 2024-08-14 19:40:20 -04:00
463951a4f1 Track/show the origin of each broadcast (discovery/room/peer exchange). 2024-08-14 19:23:01 -04:00
34804d5162 Fix android crashing in c-ares and a makefile typo.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-14 18:55:34 -04:00
3895c33915 Implement prompt() for android. #72
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-14 12:45:22 -04:00
17f4eb1a56 Make it easier to copy ids from the profile view.
Some checks failed
Build Tilde Friends / Build-All (push) Has been cancelled
2024-08-11 16:26:24 -04:00
0abdffdea6 Fix OpenBSD.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-11 11:17:49 -04:00
d32999f178 Decouple DNS-based seed discovery from the broadcast timer.
Some checks are pending
Build Tilde Friends / Build-All (push) Waiting to run
2024-08-08 18:50:54 -04:00
24 changed files with 1031 additions and 392 deletions

View File

@ -10,6 +10,6 @@ jobs:
uses: actions/checkout@v4
with:
submodules: true
- run: sudo apt update && sudo apt install -y doxygen mingw-w64
- run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64
- run: make all -j`nproc` docs
- run: docker build .

659
Doxyfile

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ VERSION_CODE := 26
VERSION_NUMBER := 0.0.22-wip
VERSION_NAME := Look for the helpers.
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460000.zip
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3460100.zip
LIBUV_URL := https://dist.libuv.org/dist/v1.48.0/libuv-v1.48.0.tar.gz
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
@ -60,11 +60,10 @@ CFLAGS += \
-ffunction-sections \
-fdata-sections \
-fno-exceptions \
-g \
-flto
-g
LDFLAGS += \
-flto=auto \
-Wno-attributes
-Wno-attributes \
-flto=auto
ANDROID_MIN_SDK_VERSION := 24
ANDROID_TARGET_SDK_VERSION := 34
@ -172,7 +171,9 @@ $(ANDROID_TARGETS): CFLAGS += \
-funwind-tables
$(ANDROID_TARGETS): LDFLAGS += --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC
$(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og
$(RELEASE_TARGETS): CFLAGS += -DNDEBUG
$(RELEASE_TARGETS): CFLAGS += \
-DNDEBUG \
-flto
$(NONANDROID_RELEASE_TARGETS): CFLAGS += -O3
$(ANDROID_RELEASE_TARGETS): CFLAGS += -Oz
$(WINDOWS_TARGETS): CC = x86_64-w64-mingw32-gcc-win32
@ -1123,4 +1124,4 @@ docs:
.PHONY: docs
fdroid: out/apk/TildeFriends-release.fdroid.unsigned.apk
.PHONE: fdroid
.PHONY: fdroid

View File

@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🐌",
"previous": "&2xK//SIpjFb0+uT5I7MSAGJ3d1FKuI/rlzhcCQd3NME=.sha256"
"previous": "&xsmsLytB3VvoHphiFHZGGEvrCfTEVGXrGwobGTIYFPQ=.sha256"
}

View File

@ -188,6 +188,10 @@ class TfProfileElement extends LitElement {
}
}
copy_id() {
navigator.clipboard.writeText(this.id);
}
render() {
if (
this.id == this.whoami &&
@ -287,6 +291,14 @@ class TfProfileElement extends LitElement {
let description = this.editing?.description ?? profile.description;
return html`<div style="border: 2px solid black; background-color: rgba(255, 255, 255, 0.2); padding: 16px">
<tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)})
<div class="w3-row">
<div class="w3-col s1 w3-container w3-right">
<button class="w3-button w3-theme-d1 w3-ripple" @click=${this.copy_id}>Copy</button>
</div>
<div class="w3-rest w3-container">
<input type="text" class="w3-theme-d1" style="width: 100%; vertical-align: middle" readonly value=${this.id}></input>
</div>
</div>
<div style="display: flex; flex-direction: row; gap: 1em">
${edit_profile}
<div style="flex: 1 0 50%">

View File

@ -17,6 +17,12 @@ class TfTabConnectionsElement extends LitElement {
static styles = styles;
static k_broadcast_emojis = {
discovery: '🏓',
room: '🚪',
peer_exchange: '🕸',
};
constructor() {
super();
let self = this;
@ -92,6 +98,7 @@ class TfTabConnectionsElement extends LitElement {
Connect
</button>
<div class="w3-bar-item">
${TfTabConnectionsElement.k_broadcast_emojis[connection.origin]}
<tf-user id=${connection.pubkey} .users=${this.users}></tf-user>
${this.render_connection_summary(connection)}
</div>

View File

@ -23,13 +23,18 @@ const k_global_settings = {
room: {
type: 'boolean',
default_value: true,
description: 'Whether this instance should behave as a room.',
description: 'Enable peers to tunnel through this instance as a room.',
},
room_name: {
type: 'string',
default_value: 'tilde friends tunnel',
description: 'Name of the room.',
},
replicator: {
type: 'boolean',
default_value: true,
description: 'Enable message and blob replication.',
},
code_of_conduct: {
type: 'textarea',
default_value: undefined,
@ -66,9 +71,14 @@ const k_global_settings = {
},
seeds_host: {
type: 'string',
default_value: '',
default_value: 'seeds.tildefriends.net',
description: 'Hostname for seed connections.',
},
peer_exchange: {
type: 'boolean',
default_value: false,
description: 'Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.',
},
};
let gGlobalSettings = {

View File

@ -34,15 +34,23 @@
#define CARES_TYPEOF_ARES_SSIZE_T ssize_t
#endif
#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(_WIN32)
#if !defined(__ANDROID__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__OpenBSD__)
#define GETSERVBYNAME_R_ARGS 6
#define GETSERVBYPORT_R_ARGS 6
#define HAVE_GETSERVBYNAME_R 1
#define HAVE_GETSERVBYPORT_R 1
#endif
#if !defined(__APPLE__) && !defined(_WIN32) && !defined(__OpenBSD__)
#define HAVE_PIPE2 1
#endif
#if !defined(__APPLE__) && !defined(_WIN32)
#if defined(__OpenBSD__)
#define GETSERVBYNAME_R_ARGS 4
#define GETSERVBYPORT_R_ARGS 4
#endif
#if !defined(__APPLE__) && !defined(_WIN32) && !defined(__OpenBSD__)
#define HAVE_MALLOC_H 1
#define HAVE_EPOLL 1
#define HAVE_SYS_EPOLL_H 1
@ -129,7 +137,11 @@
#define HAVE_IFADDRS_H 1
#define HAVE_UNISTD_H 1
#define HAVE_WRITEV 1
#if defined(__ANDROID__) || defined(__APPLE__) || defined(__OpenBSD__)
#define HAVE_ARC4RANDOM_BUF 1
#else
#undef HAVE_ARC4RANDOM_BUF
#endif
#define HAVE_GETIFADDRS 1
#define HAVE_STAT 1
#define CARES_RANDOM_FILE "/dev/urandom"

40
deps/sqlite/shell.c vendored
View File

@ -604,11 +604,6 @@ zSkipValidUtf8(const char *z, int nAccept, long ccm);
# define CIO_WIN_WC_XLATE 0 /* Not exposing translation routines at all */
#endif
#if CIO_WIN_WC_XLATE
/* Character used to represent a known-incomplete UTF-8 char group (<28>) */
static WCHAR cBadGroup = 0xfffd;
#endif
#if CIO_WIN_WC_XLATE
static HANDLE handleOfFile(FILE *pf){
int fileDesc = _fileno(pf);
@ -12547,7 +12542,7 @@ static int expertFilter(
pCsr->pData = 0;
if( rc==SQLITE_OK ){
rc = idxPrintfPrepareStmt(pExpert->db, &pCsr->pData, &pVtab->base.zErrMsg,
"SELECT * FROM main.%Q WHERE sample()", pVtab->pTab->zName
"SELECT * FROM main.%Q WHERE sqlite_expert_sample()", pVtab->pTab->zName
);
}
@ -13421,7 +13416,7 @@ struct IdxRemCtx {
};
/*
** Implementation of scalar function rem().
** Implementation of scalar function sqlite_expert_rem().
*/
static void idxRemFunc(
sqlite3_context *pCtx,
@ -13434,7 +13429,7 @@ static void idxRemFunc(
assert( argc==2 );
iSlot = sqlite3_value_int(argv[0]);
assert( iSlot<=p->nSlot );
assert( iSlot<p->nSlot );
pSlot = &p->aSlot[iSlot];
switch( pSlot->eType ){
@ -13545,7 +13540,8 @@ static int idxPopulateOneStat1(
const char *zName = (const char*)sqlite3_column_text(pIndexXInfo, 0);
const char *zColl = (const char*)sqlite3_column_text(pIndexXInfo, 1);
zCols = idxAppendText(&rc, zCols,
"%sx.%Q IS rem(%d, x.%Q) COLLATE %s", zComma, zName, nCol, zName, zColl
"%sx.%Q IS sqlite_expert_rem(%d, x.%Q) COLLATE %s",
zComma, zName, nCol, zName, zColl
);
zOrder = idxAppendText(&rc, zOrder, "%s%d", zComma, ++nCol);
}
@ -13678,13 +13674,13 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
if( rc==SQLITE_OK ){
sqlite3 *dbrem = (p->iSample==100 ? p->db : p->dbv);
rc = sqlite3_create_function(
dbrem, "rem", 2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
rc = sqlite3_create_function(dbrem, "sqlite_expert_rem",
2, SQLITE_UTF8, (void*)pCtx, idxRemFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
p->db, "sample", 0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
rc = sqlite3_create_function(p->db, "sqlite_expert_sample",
0, SQLITE_UTF8, (void*)&samplectx, idxSampleFunc, 0, 0
);
}
@ -13736,6 +13732,9 @@ static int idxPopulateStat1(sqlite3expert *p, char **pzErr){
rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
}
sqlite3_create_function(p->db, "sqlite_expert_rem", 2, SQLITE_UTF8, 0,0,0,0);
sqlite3_create_function(p->db, "sqlite_expert_sample", 0,SQLITE_UTF8,0,0,0,0);
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
return rc;
}
@ -16836,8 +16835,8 @@ static int recoverError(
va_start(ap, zFmt);
if( zFmt ){
z = sqlite3_vmprintf(zFmt, ap);
va_end(ap);
}
va_end(ap);
sqlite3_free(p->zErrMsg);
p->zErrMsg = z;
p->errCode = errCode;
@ -27085,7 +27084,6 @@ static int do_meta_command(char *zLine, ShellState *p){
import_cleanup(&sCtx);
shell_out_of_memory();
}
nByte = strlen(zSql);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
zSql = 0;
@ -27104,16 +27102,21 @@ static int do_meta_command(char *zLine, ShellState *p){
sqlite3_finalize(pStmt);
pStmt = 0;
if( nCol==0 ) return 0; /* no columns, no error */
zSql = sqlite3_malloc64( nByte*2 + 20 + nCol*2 );
nByte = 64 /* space for "INSERT INTO", "VALUES(", ")\0" */
+ (zSchema ? strlen(zSchema)*2 + 2: 0) /* Quoted schema name */
+ strlen(zTable)*2 + 2 /* Quoted table name */
+ nCol*2; /* Space for ",?" for each column */
zSql = sqlite3_malloc64( nByte );
if( zSql==0 ){
import_cleanup(&sCtx);
shell_out_of_memory();
}
if( zSchema ){
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\".\"%w\" VALUES(?",
zSchema, zTable);
}else{
sqlite3_snprintf(nByte+20, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
sqlite3_snprintf(nByte, zSql, "INSERT INTO \"%w\" VALUES(?", zTable);
}
j = strlen30(zSql);
for(i=1; i<nCol; i++){
@ -27122,6 +27125,7 @@ static int do_meta_command(char *zLine, ShellState *p){
}
zSql[j++] = ')';
zSql[j] = 0;
assert( j<nByte );
if( eVerbose>=2 ){
oputf("Insert using: %s\n", zSql);
}

154
deps/sqlite/sqlite3.c vendored
View File

@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.46.0. By combining all the individual C code files into this
** version 3.46.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
** 96c92aba00c8375bc32fafcdf12429c58bd8.
** c9c2ab54ba1f5f46360f1b4f35d849cd3f08.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.46.0"
#define SQLITE_VERSION_NUMBER 3046000
#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e"
#define SQLITE_VERSION "3.46.1"
#define SQLITE_VERSION_NUMBER 3046001
#define SQLITE_SOURCE_ID "2024-08-13 09:16:08 c9c2ab54ba1f5f46360f1b4f35d849cd3f080e6fc2b6c60e91b16c63f69a1e33"
/*
** CAPI3REF: Run-Time Library Version Numbers
@ -19361,7 +19361,7 @@ struct SrcList {
#define WHERE_AGG_DISTINCT 0x0400 /* Query is "SELECT agg(DISTINCT ...)" */
#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
#define WHERE_RIGHT_JOIN 0x1000 /* Processing a RIGHT JOIN */
/* 0x2000 not currently used */
#define WHERE_KEEP_ALL_JOINS 0x2000 /* Do not do the omit-noop-join opt */
#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
/* 0x8000 not currently used */
@ -90173,7 +90173,8 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff
assert( iVar>0 );
if( v ){
Mem *pMem = &v->aVar[iVar-1];
assert( (v->db->flags & SQLITE_EnableQPSG)==0 );
assert( (v->db->flags & SQLITE_EnableQPSG)==0
|| (v->db->mDbFlags & DBFLAG_InternalFunc)!=0 );
if( 0==(pMem->flags & MEM_Null) ){
sqlite3_value *pRet = sqlite3ValueNew(v->db);
if( pRet ){
@ -90193,7 +90194,8 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff
*/
SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
assert( iVar>0 );
assert( (v->db->flags & SQLITE_EnableQPSG)==0 );
assert( (v->db->flags & SQLITE_EnableQPSG)==0
|| (v->db->mDbFlags & DBFLAG_InternalFunc)!=0 );
if( iVar>=32 ){
v->expmask |= 0x80000000;
}else{
@ -106950,7 +106952,7 @@ static void extendFJMatch(
static SQLITE_NOINLINE int isValidSchemaTableName(
const char *zTab, /* Name as it appears in the SQL */
Table *pTab, /* The schema table we are trying to match */
Schema *pSchema /* non-NULL if a database qualifier is present */
const char *zDb /* non-NULL if a database qualifier is present */
){
const char *zLegacy;
assert( pTab!=0 );
@ -106961,7 +106963,7 @@ static SQLITE_NOINLINE int isValidSchemaTableName(
if( sqlite3StrICmp(zTab+7, &PREFERRED_TEMP_SCHEMA_TABLE[7])==0 ){
return 1;
}
if( pSchema==0 ) return 0;
if( zDb==0 ) return 0;
if( sqlite3StrICmp(zTab+7, &LEGACY_SCHEMA_TABLE[7])==0 ) return 1;
if( sqlite3StrICmp(zTab+7, &PREFERRED_SCHEMA_TABLE[7])==0 ) return 1;
}else{
@ -107144,7 +107146,7 @@ static int lookupName(
}
}else if( sqlite3StrICmp(zTab, pTab->zName)!=0 ){
if( pTab->tnum!=1 ) continue;
if( !isValidSchemaTableName(zTab, pTab, pSchema) ) continue;
if( !isValidSchemaTableName(zTab, pTab, zDb) ) continue;
}
assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT && pItem->zAlias ){
@ -108876,6 +108878,9 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
** Resolve all names for all expression in an expression list. This is
** just like sqlite3ResolveExprNames() except that it works for an expression
** list rather than a single expression.
**
** The return value is SQLITE_OK (0) for success or SQLITE_ERROR (1) for a
** failure.
*/
SQLITE_PRIVATE int sqlite3ResolveExprListNames(
NameContext *pNC, /* Namespace to resolve expressions in. */
@ -108884,7 +108889,7 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames(
int i;
int savedHasAgg = 0;
Walker w;
if( pList==0 ) return WRC_Continue;
if( pList==0 ) return SQLITE_OK;
w.pParse = pNC->pParse;
w.xExprCallback = resolveExprStep;
w.xSelectCallback = resolveSelectStep;
@ -108898,7 +108903,7 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames(
#if SQLITE_MAX_EXPR_DEPTH>0
w.pParse->nHeight += pExpr->nHeight;
if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){
return WRC_Abort;
return SQLITE_ERROR;
}
#endif
sqlite3WalkExprNN(&w, pExpr);
@ -108915,10 +108920,10 @@ SQLITE_PRIVATE int sqlite3ResolveExprListNames(
(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin|NC_OrderAgg);
}
if( w.pParse->nErr>0 ) return WRC_Abort;
if( w.pParse->nErr>0 ) return SQLITE_ERROR;
}
pNC->ncFlags |= savedHasAgg;
return WRC_Continue;
return SQLITE_OK;
}
/*
@ -117457,7 +117462,7 @@ static int renameResolveTrigger(Parse *pParse){
/* ALWAYS() because if the table of the trigger does not exist, the
** error would have been hit before this point */
if( ALWAYS(pParse->pTriggerTab) ){
rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab);
rc = sqlite3ViewGetColumnNames(pParse, pParse->pTriggerTab)!=0;
}
/* Resolve symbols in WHEN clause */
@ -124426,8 +124431,9 @@ create_view_fail:
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
/*
** The Table structure pTable is really a VIEW. Fill in the names of
** the columns of the view in the pTable structure. Return the number
** of errors. If an error is seen leave an error message in pParse->zErrMsg.
** the columns of the view in the pTable structure. Return non-zero if
** there are errors. If an error is seen an error message is left
** in pParse->zErrMsg.
*/
static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){
Table *pSelTab; /* A fake table from which we get the result set */
@ -124550,7 +124556,7 @@ static SQLITE_NOINLINE int viewGetColumnNames(Parse *pParse, Table *pTable){
sqlite3DeleteColumnNames(db, pTable);
}
#endif /* SQLITE_OMIT_VIEW */
return nErr;
return nErr + pParse->nErr;
}
SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
assert( pTable!=0 );
@ -130848,6 +130854,8 @@ static void groupConcatValue(sqlite3_context *context){
sqlite3_result_error_toobig(context);
}else if( pAccum->accError==SQLITE_NOMEM ){
sqlite3_result_error_nomem(context);
}else if( pGCC->nAccum>0 && pAccum->nChar==0 ){
sqlite3_result_text(context, "", 1, SQLITE_STATIC);
}else{
const char *zText = sqlite3_str_value(pAccum);
sqlite3_result_text(context, zText, pAccum->nChar, SQLITE_TRANSIENT);
@ -133602,6 +133610,7 @@ SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList
pRet->pSrc->nSrc = 1;
pRet->pPrior = pLeft->pPrior;
pRet->op = pLeft->op;
if( pRet->pPrior ) pRet->selFlags |= SF_Values;
pLeft->pPrior = 0;
pLeft->op = TK_SELECT;
assert( pLeft->pNext==0 );
@ -166067,7 +166076,9 @@ static int whereLoopAddBtree(
" according to whereIsCoveringIndex()\n", pProbe->zName));
}
}
}else if( m==0 ){
}else if( m==0
&& (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700))
){
WHERETRACE(0x200,
("-> %s a covering index according to bitmasks\n",
pProbe->zName, m==0 ? "is" : "is not"));
@ -167956,6 +167967,10 @@ static void showAllWhereLoops(WhereInfo *pWInfo, WhereClause *pWC){
** the right-most table of a subquery that was flattened into the
** main query and that subquery was the right-hand operand of an
** inner join that held an ON or USING clause.
** 6) The ORDER BY clause has 63 or fewer terms
** 7) The omit-noop-join optimization is enabled.
**
** Items (1), (6), and (7) are checked by the caller.
**
** For example, given:
**
@ -168369,6 +168384,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
if( pOrderBy && pOrderBy->nExpr>=BMS ){
pOrderBy = 0;
wctrlFlags &= ~WHERE_WANT_DISTINCT;
wctrlFlags |= WHERE_KEEP_ALL_JOINS; /* Disable omit-noop-join opt */
}
/* The number of tables in the FROM clause is limited by the number of
@ -168669,10 +168685,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** in-line sqlite3WhereCodeOneLoopStart() for performance reasons.
*/
notReady = ~(Bitmask)0;
if( pWInfo->nLevel>=2
&& pResultSet!=0 /* these two combine to guarantee */
&& 0==(wctrlFlags & WHERE_AGG_DISTINCT) /* condition (1) above */
&& OptimizationEnabled(db, SQLITE_OmitNoopJoin)
if( pWInfo->nLevel>=2 /* Must be a join, or this opt8n is pointless */
&& pResultSet!=0 /* Condition (1) */
&& 0==(wctrlFlags & (WHERE_AGG_DISTINCT|WHERE_KEEP_ALL_JOINS)) /* (1),(6) */
&& OptimizationEnabled(db, SQLITE_OmitNoopJoin) /* (7) */
){
notReady = whereOmitNoopJoin(pWInfo, notReady);
nTabList = pWInfo->nLevel;
@ -168992,26 +169008,6 @@ whereBeginError:
}
#endif
#ifdef SQLITE_DEBUG
/*
** Return true if cursor iCur is opened by instruction k of the
** bytecode. Used inside of assert() only.
*/
static int cursorIsOpen(Vdbe *v, int iCur, int k){
while( k>=0 ){
VdbeOp *pOp = sqlite3VdbeGetOp(v,k--);
if( pOp->p1!=iCur ) continue;
if( pOp->opcode==OP_Close ) return 0;
if( pOp->opcode==OP_OpenRead ) return 1;
if( pOp->opcode==OP_OpenWrite ) return 1;
if( pOp->opcode==OP_OpenDup ) return 1;
if( pOp->opcode==OP_OpenAutoindex ) return 1;
if( pOp->opcode==OP_OpenEphemeral ) return 1;
}
return 0;
}
#endif /* SQLITE_DEBUG */
/*
** Generate the end of the WHERE loop. See comments on
** sqlite3WhereBegin() for additional information.
@ -169311,16 +169307,10 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** reference. Verify that this is harmless - that the
** table being referenced really is open.
*/
#ifdef SQLITE_ENABLE_OFFSET_SQL_FUNC
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
|| cursorIsOpen(v,pOp->p1,k)
|| pOp->opcode==OP_Offset
);
#else
assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
|| cursorIsOpen(v,pOp->p1,k)
);
#endif
if( pLoop->wsFlags & WHERE_IDX_ONLY ){
sqlite3ErrorMsg(pParse, "internal query planner error");
pParse->rc = SQLITE_INTERNAL;
}
}
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
@ -172591,9 +172581,9 @@ static void updateDeleteLimitError(
break;
}
}
if( (p->selFlags & SF_MultiValue)==0 &&
(mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 &&
cnt>mxSelect
if( (p->selFlags & (SF_MultiValue|SF_Values))==0
&& (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0
&& cnt>mxSelect
){
sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
}
@ -237004,7 +236994,11 @@ static int sqlite3Fts5ExprNew(
}
sqlite3_free(sParse.apPhrase);
*pzErr = sParse.zErr;
if( 0==*pzErr ){
*pzErr = sParse.zErr;
}else{
sqlite3_free(sParse.zErr);
}
return sParse.rc;
}
@ -239132,6 +239126,7 @@ static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
assert( pRight->eType==FTS5_STRING
|| pRight->eType==FTS5_TERM
|| pRight->eType==FTS5_EOF
|| (pRight->eType==FTS5_AND && pParse->bPhraseToAnd)
);
if( pLeft->eType==FTS5_AND ){
@ -251299,6 +251294,7 @@ static int fts5UpdateMethod(
rc = SQLITE_ERROR;
}else{
rc = fts5SpecialDelete(pTab, apVal);
bUpdateOrDelete = 1;
}
}else{
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
@ -252473,14 +252469,16 @@ static int sqlite3Fts5GetTokenizer(
if( pMod==0 ){
assert( nArg>0 );
rc = SQLITE_ERROR;
*pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
if( pzErr ) *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
}else{
rc = pMod->x.xCreate(
pMod->pUserData, (azArg?&azArg[1]:0), (nArg?nArg-1:0), &pConfig->pTok
);
pConfig->pTokApi = &pMod->x;
if( rc!=SQLITE_OK ){
if( pzErr ) *pzErr = sqlite3_mprintf("error in tokenizer constructor");
if( pzErr && rc!=SQLITE_NOMEM ){
*pzErr = sqlite3_mprintf("error in tokenizer constructor");
}
}else{
pConfig->ePattern = sqlite3Fts5TokenizerPattern(
pMod->x.xCreate, pConfig->pTok
@ -252539,7 +252537,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e", -1, SQLITE_TRANSIENT);
sqlite3_result_text(pCtx, "fts5: 2024-08-13 09:16:08 c9c2ab54ba1f5f46360f1b4f35d849cd3f080e6fc2b6c60e91b16c63f69a1e33", -1, SQLITE_TRANSIENT);
}
/*
@ -252574,17 +252572,23 @@ static int fts5IntegrityMethod(
assert( pzErr!=0 && *pzErr==0 );
UNUSED_PARAM(isQuick);
assert( pTab->p.pConfig->pzErrmsg==0 );
pTab->p.pConfig->pzErrmsg = pzErr;
rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
zSchema, zTabname);
rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM;
}else if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS5 table %s.%s: %s",
zSchema, zTabname, sqlite3_errstr(rc));
if( *pzErr==0 && rc!=SQLITE_OK ){
if( (rc&0xff)==SQLITE_CORRUPT ){
*pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
zSchema, zTabname);
rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM;
}else{
*pzErr = sqlite3_mprintf("unable to validate the inverted index for"
" FTS5 table %s.%s: %s",
zSchema, zTabname, sqlite3_errstr(rc));
}
}
sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
pTab->p.pConfig->pzErrmsg = 0;
return rc;
}
@ -254018,7 +254022,7 @@ static int fts5AsciiCreate(
int i;
memset(p, 0, sizeof(AsciiTokenizer));
memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar));
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){
fts5AsciiAddExceptions(p, zArg, 1);
@ -254029,6 +254033,7 @@ static int fts5AsciiCreate(
rc = SQLITE_ERROR;
}
}
if( rc==SQLITE_OK && i<nArg ) rc = SQLITE_ERROR;
if( rc!=SQLITE_OK ){
fts5AsciiDelete((Fts5Tokenizer*)p);
p = 0;
@ -254320,17 +254325,16 @@ static int fts5UnicodeCreate(
}
/* Search for a "categories" argument */
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
if( 0==sqlite3_stricmp(azArg[i], "categories") ){
zCat = azArg[i+1];
}
}
if( rc==SQLITE_OK ){
rc = unicodeSetCategories(p, zCat);
}
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
@ -254355,6 +254359,7 @@ static int fts5UnicodeCreate(
rc = SQLITE_ERROR;
}
}
if( i<nArg && rc==SQLITE_OK ) rc = SQLITE_ERROR;
}else{
rc = SQLITE_NOMEM;
@ -255237,7 +255242,7 @@ static int fts5TriCreate(
int i;
pNew->bFold = 1;
pNew->iFoldParam = 0;
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
for(i=0; rc==SQLITE_OK && i<nArg-1; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
@ -255255,6 +255260,7 @@ static int fts5TriCreate(
rc = SQLITE_ERROR;
}
}
if( i<nArg && rc==SQLITE_OK ) rc = SQLITE_ERROR;
if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
rc = SQLITE_ERROR;

View File

@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION "3.46.0"
#define SQLITE_VERSION_NUMBER 3046000
#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e"
#define SQLITE_VERSION "3.46.1"
#define SQLITE_VERSION_NUMBER 3046001
#define SQLITE_SOURCE_ID "2024-08-13 09:16:08 c9c2ab54ba1f5f46360f1b4f35d849cd3f080e6fc2b6c60e91b16c63f69a1e33"
/*
** CAPI3REF: Run-Time Library Version Numbers

View File

@ -15,6 +15,7 @@ import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.StrictMode;
import android.text.InputType;
import android.util.Base64;
import android.util.Log;
import android.view.KeyEvent;
@ -32,6 +33,7 @@ import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.io.BufferedReader;
@ -192,7 +194,24 @@ public class TildeFriendsActivity extends Activity {
web_view.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
new AlertDialog.Builder(view.getContext())
.setTitle("Tilde Friends")
.setMessage(message)
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
result.confirm();
}
})
.create()
.show();
return true;
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
new AlertDialog.Builder(view.getContext())
.setTitle("Tilde Friends")
.setMessage(message)
@ -216,15 +235,27 @@ public class TildeFriendsActivity extends Activity {
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
EditText input = new EditText(view.getContext());
input.setInputType(InputType.TYPE_CLASS_TEXT);
input.setText(defaultValue);
new AlertDialog.Builder(view.getContext())
.setTitle("Tilde Friends")
.setMessage(message)
.setNeutralButton(android.R.string.ok, new DialogInterface.OnClickListener()
.setView(input)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
result.confirm();
result.confirm(input.getText().toString());
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
{
public void onClick(DialogInterface dialog, int which)
{
result.cancel();
}
})
.create()

View File

@ -1071,7 +1071,7 @@ void* tf_http_get_user_data(tf_http_t* http)
const char* tf_http_get_cookie(const char* cookie_header, const char* name)
{
if (!cookie_header)
if (!cookie_header || !name)
{
return NULL;
}

View File

@ -77,6 +77,9 @@ enum
k_tf_ssb_rpc_message_body_length_max = 1 * 1024 * 1024,
k_debug_close_message_count = 256,
k_debug_close_connection_count = 32,
k_seed_expire_seconds = 10 * 60,
k_seed_check_interval_seconds = 5 * 50,
k_udp_discovery_expires_seconds = 10,
};
typedef struct _tf_ssb_broadcast_t tf_ssb_broadcast_t;
@ -116,7 +119,9 @@ typedef struct _tf_ssb_broadcast_t
tf_ssb_broadcast_t* next;
time_t ctime;
time_t mtime;
time_t expires_at;
char host[256];
tf_ssb_broadcast_origin_t origin;
struct sockaddr_in addr;
tf_ssb_connection_t* tunnel_connection;
uint8_t pub[crypto_sign_PUBLICKEYBYTES];
@ -260,8 +265,11 @@ typedef struct _tf_ssb_t
uv_thread_t thread_self;
bool is_room;
bool is_replicator;
bool is_peer_exchange;
char* room_name;
char seeds_host[256];
time_t last_seed_check;
tf_ssb_timer_t** timers;
int timers_count;
@ -357,7 +365,7 @@ static JSClassID _connection_class_id;
static int s_connection_index;
static int s_tunnel_index;
static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast);
static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast, int expires_seconds);
static void _tf_ssb_connection_client_send_hello(tf_ssb_connection_t* connection);
static void _tf_ssb_connection_close(tf_ssb_connection_t* connection, const char* reason);
static void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason);
@ -2190,6 +2198,7 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, const char* db_path
{
tf_ssb_t* ssb = tf_malloc(sizeof(tf_ssb_t));
memset(ssb, 0, sizeof(*ssb));
ssb->is_replicator = true;
const char* actual_key = network_key ? network_key : k_ssb_network_string;
if (sodium_hex2bin(ssb->network_key, sizeof(ssb->network_key), actual_key, strlen(actual_key), ": ", NULL, NULL))
@ -2943,10 +2952,10 @@ static void _tf_ssb_update_seeds_after_work(tf_ssb_t* ssb, int status, void* use
seeds_t* seeds = user_data;
for (int i = 0; i < seeds->seeds_count; i++)
{
tf_ssb_broadcast_t broadcast = { 0 };
tf_ssb_broadcast_t broadcast = { .origin = k_tf_ssb_broadcast_origin_peer_exchange };
if (_tf_ssb_parse_broadcast(seeds->seeds[i], &broadcast))
{
_tf_ssb_add_broadcast(ssb, &broadcast);
_tf_ssb_add_broadcast(ssb, &broadcast, k_seed_expire_seconds);
}
tf_free(seeds->seeds[i]);
}
@ -2971,11 +2980,12 @@ static void _tf_ssb_broadcast_timer(uv_timer_t* timer)
uv_free_interface_addresses(info, count);
}
seeds_t* seeds = tf_malloc(sizeof(seeds_t));
*seeds = (seeds_t) { 0 };
if (*ssb->seeds_host)
time_t now = time(NULL);
if (ssb->is_peer_exchange && *ssb->seeds_host && now - ssb->last_seed_check > k_seed_check_interval_seconds)
{
seeds_t* seeds = tf_malloc(sizeof(seeds_t));
*seeds = (seeds_t) { 0 };
ssb->last_seed_check = now;
tf_ssb_run_work(ssb, _tf_ssb_update_seeds_work, _tf_ssb_update_seeds_after_work, seeds);
}
}
@ -3090,7 +3100,7 @@ static void _tf_ssb_notify_broadcasts_changed(tf_ssb_t* ssb)
}
}
static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast)
static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broadcast, int expires_seconds)
{
if (memcmp(broadcast->pub, ssb->pub, sizeof(ssb->pub)) == 0)
{
@ -3104,6 +3114,7 @@ static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broad
if (node->tunnel_connection == broadcast->tunnel_connection && memcmp(node->pub, broadcast->pub, sizeof(node->pub)) == 0)
{
node->mtime = time(NULL);
node->expires_at = node->mtime + expires_seconds;
return;
}
}
@ -3116,6 +3127,7 @@ static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broad
node->addr.sin_addr.s_addr == broadcast->addr.sin_addr.s_addr && memcmp(node->pub, broadcast->pub, sizeof(node->pub)) == 0)
{
node->mtime = time(NULL);
node->expires_at = node->mtime + expires_seconds;
return;
}
}
@ -3132,12 +3144,22 @@ static void _tf_ssb_add_broadcast(tf_ssb_t* ssb, const tf_ssb_broadcast_t* broad
node->next = ssb->broadcasts;
node->ctime = time(NULL);
node->mtime = node->ctime;
node->expires_at = node->mtime + expires_seconds;
ssb->broadcasts = node;
ssb->broadcasts_count++;
_tf_ssb_notify_broadcasts_changed(ssb);
}
void tf_ssb_add_broadcast(tf_ssb_t* ssb, const char* connection, tf_ssb_broadcast_origin_t origin, int64_t expires_seconds)
{
tf_ssb_broadcast_t broadcast = { .origin = origin };
if (_tf_ssb_parse_broadcast(connection, &broadcast))
{
_tf_ssb_add_broadcast(ssb, &broadcast, expires_seconds);
}
}
static void _tf_ssb_on_broadcast_listener_recv(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags)
{
if (nread <= 0)
@ -3154,18 +3176,19 @@ static void _tf_ssb_on_broadcast_listener_recv(uv_udp_t* handle, ssize_t nread,
char* entry = strtok_r(buf->base, k_delim, &state);
while (entry)
{
tf_ssb_broadcast_t broadcast = { 0 };
tf_ssb_broadcast_t broadcast = { .origin = k_tf_ssb_broadcast_origin_discovery };
if (_tf_ssb_parse_broadcast(entry, &broadcast))
{
_tf_ssb_add_broadcast(ssb, &broadcast);
_tf_ssb_add_broadcast(ssb, &broadcast, k_udp_discovery_expires_seconds);
}
entry = strtok_r(NULL, k_delim, &state);
}
tf_free(buf->base);
}
void tf_ssb_visit_broadcasts(
tf_ssb_t* ssb, void (*callback)(const char* host, const struct sockaddr_in* addr, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data), void* user_data)
void tf_ssb_visit_broadcasts(tf_ssb_t* ssb,
void (*callback)(const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data),
void* user_data)
{
time_t now = time(NULL);
tf_ssb_broadcast_t* next = NULL;
@ -3175,7 +3198,7 @@ void tf_ssb_visit_broadcasts(
if (node->mtime - now < 60)
{
tf_trace_begin(ssb->trace, "broadcast");
callback(node->host, &node->addr, node->tunnel_connection, node->pub, user_data);
callback(node->host, &node->addr, node->origin, node->tunnel_connection, node->pub, user_data);
tf_trace_end(ssb->trace);
}
}
@ -3188,7 +3211,7 @@ static void _tf_ssb_broadcast_cleanup_timer(uv_timer_t* timer)
time_t now = time(NULL);
for (tf_ssb_broadcast_t** it = &ssb->broadcasts; *it;)
{
if (!(*it)->tunnel_connection && (*it)->mtime < now - 10)
if (!(*it)->tunnel_connection && now > (*it)->expires_at)
{
tf_ssb_broadcast_t* node = *it;
*it = node->next;
@ -3601,10 +3624,11 @@ void tf_ssb_notify_blob_want_added(tf_ssb_t* ssb, const char* id)
void tf_ssb_connection_add_room_attendant(tf_ssb_connection_t* connection, const char* id)
{
tf_ssb_broadcast_t broadcast = {
.origin = k_tf_ssb_broadcast_origin_room,
.tunnel_connection = connection,
};
tf_ssb_id_str_to_bin(broadcast.pub, id);
_tf_ssb_add_broadcast(connection->ssb, &broadcast);
_tf_ssb_add_broadcast(connection->ssb, &broadcast, 0);
}
void tf_ssb_connection_remove_room_attendant(tf_ssb_connection_t* connection, const char* id)
@ -3985,6 +4009,26 @@ const char* tf_ssb_get_room_name(tf_ssb_t* ssb)
return ssb->room_name;
}
bool tf_ssb_is_replicator(tf_ssb_t* ssb)
{
return ssb->is_replicator;
}
void tf_ssb_set_is_replicator(tf_ssb_t* ssb, bool is_replicator)
{
ssb->is_replicator = is_replicator;
}
bool tf_ssb_is_peer_exchange(tf_ssb_t* ssb)
{
return ssb->is_peer_exchange;
}
void tf_ssb_set_is_peer_exchange(tf_ssb_t* ssb, bool is_peer_exchange)
{
ssb->is_peer_exchange = is_peer_exchange;
}
void tf_ssb_set_room_name(tf_ssb_t* ssb, const char* room_name)
{
tf_free(ssb->room_name);
@ -3994,6 +4038,8 @@ void tf_ssb_set_room_name(tf_ssb_t* ssb, const char* room_name)
typedef struct _update_settings_t
{
bool is_room;
bool is_replicator;
bool is_peer_exchange;
char seeds_host[256];
char room_name[1024];
} update_settings_t;
@ -4051,6 +4097,8 @@ static void _tf_ssb_update_settings_work(tf_ssb_t* ssb, void* user_data)
{
update_settings_t* update = user_data;
update->is_room = _get_global_setting_bool(ssb, "room", true);
update->is_replicator = _get_global_setting_bool(ssb, "replicator", true);
update->is_peer_exchange = _get_global_setting_bool(ssb, "peer_exchange", true);
_get_global_setting_string(ssb, "room_name", update->room_name, sizeof(update->room_name));
_get_global_setting_string(ssb, "seeds_host", update->seeds_host, sizeof(update->seeds_host));
}
@ -4060,6 +4108,8 @@ static void _tf_ssb_update_settings_after_work(tf_ssb_t* ssb, int result, void*
update_settings_t* update = user_data;
tf_ssb_set_is_room(ssb, update->is_room);
tf_ssb_set_room_name(ssb, update->room_name);
tf_ssb_set_is_peer_exchange(ssb, update->is_peer_exchange);
tf_ssb_set_is_replicator(ssb, update->is_replicator);
snprintf(ssb->seeds_host, sizeof(ssb->seeds_host), "%s", update->seeds_host);
_tf_ssb_start_update_settings(ssb);
tf_free(update);

View File

@ -28,6 +28,8 @@ enum
k_ssb_rpc_flag_new_request = 0x10,
k_ssb_blob_bytes_max = 5 * 1024 * 1024,
k_ssb_peer_exchange_expires_seconds = 60 * 60,
};
/**
@ -41,6 +43,16 @@ typedef enum _tf_ssb_change_t
k_tf_ssb_change_update,
} tf_ssb_change_t;
/**
** The origin of a broadcast entry.
*/
typedef enum _tf_ssb_broadcast_origin_t
{
k_tf_ssb_broadcast_origin_discovery,
k_tf_ssb_broadcast_origin_room,
k_tf_ssb_broadcast_origin_peer_exchange,
} tf_ssb_broadcast_origin_t;
/**
** Flags describing the structure of a message.
*/
@ -299,8 +311,18 @@ bool tf_ssb_whoami(tf_ssb_t* ssb, char* out_id, size_t out_id_size);
** @param callback The callback.
** @param user_data User data for the callback.
*/
void tf_ssb_visit_broadcasts(
tf_ssb_t* ssb, void (*callback)(const char* host, const struct sockaddr_in* addr, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data), void* user_data);
void tf_ssb_visit_broadcasts(tf_ssb_t* ssb,
void (*callback)(const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data),
void* user_data);
/**
** Add a broadcast entry.
** @param ssb The SSB instance.
** @param connection The connection string to add.
** @param origin The origin of the broadcast entry.
** @param expires_seconds How long the broadcast entry should last.
*/
void tf_ssb_add_broadcast(tf_ssb_t* ssb, const char* connection, tf_ssb_broadcast_origin_t origin, int64_t expires_seconds);
/**
** Get the identities of all active connections.
@ -964,6 +986,34 @@ bool tf_ssb_is_room(tf_ssb_t* ssb);
*/
void tf_ssb_set_is_room(tf_ssb_t* ssb, bool is_room);
/**
** Get whether the running server supports replication of messages and blobs.
** @param ssb The SSB instance.
** @return True if the server is a replicator.
*/
bool tf_ssb_is_replicator(tf_ssb_t* ssb);
/**
** Set whether the running server supports replication of messages and blobs.
** @param ssb The SSB instance.
** @param is_replicator Whether to support replication.
*/
void tf_ssb_set_is_replicator(tf_ssb_t* ssb, bool is_replicator);
/**
** Get whether the running server participates in peer exchange.
** @param ssb The SSB instance.
** @return True if the server participates in peer exchange.
*/
bool tf_ssb_is_peer_exchange(tf_ssb_t* ssb);
/**
** Set whether the running server participates in peer exchange.
** @param ssb The SSB instance.
** @param is_peer_exchange Whether to participate in peer exchange.
*/
void tf_ssb_set_is_peer_exchange(tf_ssb_t* ssb, bool is_peer_exchange);
/**
** Get the name of the room hosted by the running server.
** @param ssb The SSB instance.

View File

@ -231,7 +231,11 @@ 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)
{
if (strlen(path) > strlen(".json") && strcasecmp(path + strlen(path) - strlen(".json"), ".json") == 0)
if (!path)
{
return;
}
else if (strlen(path) > strlen(".json") && strcasecmp(path + strlen(path) - strlen(".json"), ".json") == 0)
{
_tf_ssb_import_app_json(ssb, tf_ssb_get_loop(ssb), tf_ssb_get_context(ssb), user, path);
}

View File

@ -1450,12 +1450,25 @@ typedef struct _broadcasts_t
int length;
} broadcasts_t;
static void _tf_ssb_broadcasts_visit(const char* host, const struct sockaddr_in* addr, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
static void _tf_ssb_broadcasts_visit(
const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
{
broadcasts_t* broadcasts = user_data;
JSValue entry = JS_NewObject(broadcasts->context);
char pubkey[k_id_base64_len];
tf_ssb_id_bin_to_str(pubkey, sizeof(pubkey), pub);
switch (origin)
{
case k_tf_ssb_broadcast_origin_discovery:
JS_SetPropertyStr(broadcasts->context, entry, "origin", JS_NewString(broadcasts->context, "discovery"));
break;
case k_tf_ssb_broadcast_origin_room:
JS_SetPropertyStr(broadcasts->context, entry, "origin", JS_NewString(broadcasts->context, "room"));
break;
case k_tf_ssb_broadcast_origin_peer_exchange:
JS_SetPropertyStr(broadcasts->context, entry, "origin", JS_NewString(broadcasts->context, "peer_exchange"));
break;
}
if (tunnel)
{
JS_SetPropertyStr(broadcasts->context, entry, "tunnel", JS_DupValue(broadcasts->context, tf_ssb_connection_get_object(tunnel)));

View File

@ -19,6 +19,7 @@
#endif
static void _tf_ssb_connection_send_history_stream(tf_ssb_connection_t* connection, int32_t request_number, const char* author, int64_t sequence, bool keys, bool live);
static void _tf_ssb_rpc_send_peers_exchange(tf_ssb_connection_t* connection);
static void _tf_ssb_rpc_start_delete_blobs(tf_ssb_t* ssb, int delay_ms);
static int64_t _get_global_setting_int64(tf_ssb_t* ssb, const char* name, int64_t default_value)
@ -107,6 +108,12 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
{
return;
}
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
if (!tf_ssb_is_replicator(ssb))
{
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "blobs.get");
return;
}
tf_ssb_connection_add_request(connection, -request_number, "blobs.get", _tf_ssb_rpc_blobs_get_callback, NULL, NULL, NULL);
JSContext* context = tf_ssb_connection_get_context(connection);
@ -144,6 +151,11 @@ static void _tf_ssb_rpc_blobs_get(tf_ssb_connection_t* connection, uint8_t flags
static void _tf_ssb_rpc_blobs_has(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
if (!tf_ssb_is_replicator(ssb))
{
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "blobs.has");
return;
}
JSContext* context = tf_ssb_connection_get_context(connection);
JSValue ids = JS_GetPropertyStr(context, args, "args");
JSValue id = JS_GetPropertyUint32(context, ids, 0);
@ -242,8 +254,13 @@ static void _tf_ssb_rpc_request_more_blobs(tf_ssb_connection_t* connection)
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)
{
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);
if (!tf_ssb_is_replicator(ssb))
{
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "blobs.createWants");
return;
}
tf_ssb_blob_wants_t* blob_wants = tf_ssb_connection_get_blob_wants_state(connection);
tf_ssb_add_blob_want_added_callback(ssb, _tf_ssb_rpc_blob_wants_added_callback, NULL, connection);
blob_wants->request_number = request_number;
_tf_ssb_rpc_request_more_blobs(connection);
@ -855,6 +872,11 @@ static void _tf_ssb_rpc_createHistoryStream(
tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
if (!tf_ssb_is_replicator(ssb))
{
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "createHistoryStream");
return;
}
JSContext* context = tf_ssb_get_context(ssb);
JSValue arg_array = JS_GetPropertyStr(context, args, "args");
JSValue arg = JS_GetPropertyUint32(context, arg_array, 0);
@ -1157,6 +1179,12 @@ static void _tf_ssb_rpc_ebt_replicate_server(
{
return;
}
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
if (!tf_ssb_is_replicator(ssb))
{
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "ebt.replicate");
return;
}
_tf_ssb_rpc_ebt_replicate(connection, flags, request_number, args, message, size, user_data);
tf_ssb_connection_add_request(connection, -request_number, "ebt.replicate", _tf_ssb_rpc_ebt_replicate, NULL, NULL, NULL);
}
@ -1189,7 +1217,15 @@ static void _tf_ssb_rpc_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_chang
_tf_ssb_rpc_connection_tunnel_isRoom_callback, NULL, NULL);
JS_FreeValue(context, message);
_tf_ssb_rpc_send_ebt_replicate(connection);
if (tf_ssb_is_peer_exchange(ssb))
{
_tf_ssb_rpc_send_peers_exchange(connection);
}
if (tf_ssb_is_replicator(ssb))
{
_tf_ssb_rpc_send_ebt_replicate(connection);
}
}
}
else if (change == k_tf_ssb_change_remove)
@ -1303,6 +1339,135 @@ void tf_ssb_rpc_start_periodic(tf_ssb_t* ssb)
_tf_ssb_rpc_start_delete_blobs(ssb, 30 * 1000);
}
typedef struct _peers_exchange_t
{
tf_ssb_t* ssb;
JSValue peers;
} peers_exchange_t;
static void _tf_ssb_get_peers_exhange_callback(
const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
{
peers_exchange_t* data = user_data;
if (origin == k_tf_ssb_broadcast_origin_peer_exchange)
{
char fullid[k_id_base64_len] = { 0 };
tf_base64_encode(pub, sizeof(pub), fullid, sizeof(fullid));
char* dot = strchr(fullid, '.');
if (dot)
{
*dot = '\0';
}
char connection[1024] = { 0 };
snprintf(connection, sizeof(connection), "net:%s:%d~shs:%s", host, ntohs(addr->sin_port), fullid);
JSContext* context = tf_ssb_get_context(data->ssb);
JS_SetPropertyStr(context, data->peers, connection, JS_NewInt32(context, 0));
}
}
static JSValue _tf_ssb_get_peers_exchange(tf_ssb_t* ssb)
{
JSContext* context = tf_ssb_get_context(ssb);
JSValue peers = JS_NewObject(context);
tf_ssb_visit_broadcasts(ssb, _tf_ssb_get_peers_exhange_callback, &(peers_exchange_t) { .ssb = ssb, .peers = peers });
return peers;
}
static void _tf_ssb_rpc_peers_exchange_internal(
tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{
JSContext* context = tf_ssb_connection_get_context(connection);
if (_is_error(context, args))
{
return;
}
/* The peer that participated in the exchange is now a peer exchange entry, too. */
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
if (*tf_ssb_connection_get_host(connection))
{
char fullid[k_id_base64_len] = { 0 };
tf_ssb_connection_get_id(connection, fullid, sizeof(fullid));
char* dot = strchr(fullid, '.');
if (dot)
{
*dot = '\0';
}
char connection_string[1024] = { 0 };
snprintf(connection_string, sizeof(connection_string), "net:%s:%d~shs:%s", tf_ssb_connection_get_host(connection), tf_ssb_connection_get_port(connection), fullid + 1);
tf_ssb_add_broadcast(ssb, connection_string, k_tf_ssb_broadcast_origin_peer_exchange, k_ssb_peer_exchange_expires_seconds);
}
JSValue in_peers = JS_GetPropertyStr(context, args, "peers");
JSPropertyEnum* ptab = NULL;
uint32_t plen = 0;
if (JS_GetOwnPropertyNames(context, &ptab, &plen, in_peers, JS_GPN_STRING_MASK) == 0)
{
for (uint32_t i = 0; i < plen; ++i)
{
JSValue key = JS_AtomToString(context, ptab[i].atom);
JSPropertyDescriptor desc;
JSValue key_value = JS_NULL;
if (JS_GetOwnProperty(context, &desc, args, ptab[i].atom) == 1)
{
key_value = desc.value;
JS_FreeValue(context, desc.setter);
JS_FreeValue(context, desc.getter);
}
const char* connection = JS_ToCString(context, key);
int64_t timestamp = 0;
JS_ToInt64(context, &timestamp, key_value);
/* ADD BROADCAST connection: timestamp */
JS_FreeCString(context, connection);
JS_FreeValue(context, key);
JS_FreeValue(context, key_value);
}
for (uint32_t i = 0; i < plen; ++i)
{
JS_FreeAtom(context, ptab[i].atom);
}
js_free(context, ptab);
}
JS_FreeValue(context, in_peers);
}
static void _tf_ssb_rpc_send_peers_exchange(tf_ssb_connection_t* connection)
{
int32_t request_number = tf_ssb_connection_next_request_number(connection);
JSContext* context = tf_ssb_connection_get_context(connection);
JSValue message = JS_NewObject(context);
JSValue name = JS_NewArray(context);
JS_SetPropertyUint32(context, name, 0, JS_NewString(context, "peers"));
JS_SetPropertyUint32(context, name, 1, JS_NewString(context, "exchange"));
JS_SetPropertyStr(context, message, "name", name);
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
JS_SetPropertyStr(context, message, "peers", _tf_ssb_get_peers_exchange(ssb));
tf_ssb_connection_rpc_send_json(connection, k_ssb_rpc_flag_new_request, request_number, "peers.exchange", message, _tf_ssb_rpc_peers_exchange_internal, NULL, NULL);
JS_FreeValue(context, message);
}
static void _tf_ssb_rpc_peers_exchange(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{
tf_ssb_t* ssb = tf_ssb_connection_get_ssb(connection);
if (!tf_ssb_is_peer_exchange(ssb))
{
tf_ssb_connection_rpc_send_error_method_not_allowed(connection, flags, -request_number, "peers.exchange");
return;
}
_tf_ssb_rpc_peers_exchange_internal(connection, flags, request_number, args, message, size, user_data);
JSContext* context = tf_ssb_connection_get_context(connection);
JSValue out_message = JS_NewObject(context);
JS_SetPropertyStr(context, out_message, "peers", _tf_ssb_get_peers_exchange(ssb));
tf_ssb_connection_rpc_send_json(connection, flags, -request_number, NULL, out_message, NULL, NULL, NULL);
JS_FreeValue(context, out_message);
}
void tf_ssb_rpc_register(tf_ssb_t* ssb)
{
tf_ssb_add_connections_changed_callback(ssb, _tf_ssb_rpc_connections_changed_callback, NULL, NULL);
@ -1316,4 +1481,5 @@ void tf_ssb_rpc_register(tf_ssb_t* ssb)
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "room", "attendants", NULL }, _tf_ssb_rpc_room_attendants, NULL, NULL); /* SOURCE */
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "createHistoryStream", NULL }, _tf_ssb_rpc_createHistoryStream, NULL, NULL); /* SOURCE */
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "ebt", "replicate", NULL }, _tf_ssb_rpc_ebt_replicate_server, NULL, NULL); /* DUPLEX */
tf_ssb_add_rpc_callback(ssb, (const char*[]) { "peers", "exchange", NULL }, _tf_ssb_rpc_peers_exchange, NULL, NULL); /* ASYNC */
}

View File

@ -323,7 +323,7 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
uv_loop_close(&loop);
}
static void _broadcasts_visit(const char* host, const struct sockaddr_in* addr, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
static void _broadcasts_visit(const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
{
int* count = user_data;
(*count)++;
@ -747,7 +747,8 @@ static void _break_in_a_bit(tf_ssb_t* ssb, tf_ssb_connection_t* connection, cons
uv_timer_start(&data->timer, _close_callback, 3000, 0);
}
static void _ssb_test_room_broadcasts_visit(const char* host, const struct sockaddr_in* addr, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
static void _ssb_test_room_broadcasts_visit(
const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
{
tf_ssb_t* ssb = user_data;
char id[k_id_base64_len] = { 0 };
@ -869,4 +870,73 @@ void tf_ssb_test_encrypt(const tf_test_options_t* options)
printf("returned %d\n", WEXITSTATUS(result));
assert(WEXITSTATUS(result) == 0);
}
static void _count_broadcasts_callback(
const char* host, const struct sockaddr_in* addr, tf_ssb_broadcast_origin_t origin, tf_ssb_connection_t* tunnel, const uint8_t* pub, void* user_data)
{
int* count = user_data;
(*count)++;
}
static int _count_broadcasts(tf_ssb_t* ssb)
{
int count = 0;
tf_ssb_visit_broadcasts(ssb, _count_broadcasts_callback, &count);
return count;
}
void tf_ssb_test_peer_exchange(const tf_test_options_t* options)
{
uv_loop_t loop = { 0 };
uv_loop_init(&loop);
unlink("out/test_db0.sqlite");
tf_ssb_t* ssb0 = tf_ssb_create(&loop, NULL, "file:out/test_db0.sqlite", NULL);
tf_ssb_set_is_room(ssb0, false);
tf_ssb_set_is_replicator(ssb0, false);
tf_ssb_set_is_peer_exchange(ssb0, true);
tf_ssb_register(tf_ssb_get_context(ssb0), ssb0);
tf_ssb_server_open(ssb0, 12347);
unlink("out/test_db1.sqlite");
tf_ssb_t* ssb1 = tf_ssb_create(&loop, NULL, "file:out/test_db1.sqlite", NULL);
tf_ssb_set_is_room(ssb1, false);
tf_ssb_set_is_replicator(ssb1, false);
tf_ssb_set_is_peer_exchange(ssb1, true);
tf_ssb_register(tf_ssb_get_context(ssb1), ssb1);
tf_ssb_server_open(ssb1, 12348);
unlink("out/test_db2.sqlite");
tf_ssb_t* ssb2 = tf_ssb_create(&loop, NULL, "file:out/test_db2.sqlite", NULL);
tf_ssb_set_is_room(ssb2, false);
tf_ssb_set_is_replicator(ssb2, false);
tf_ssb_set_is_peer_exchange(ssb2, true);
tf_ssb_register(tf_ssb_get_context(ssb2), ssb2);
tf_ssb_server_open(ssb2, 12349);
char id0[k_id_base64_len] = { 0 };
tf_ssb_whoami(ssb0, id0, sizeof(id0));
uint8_t id0bin[k_id_bin_len];
tf_ssb_id_str_to_bin(id0bin, id0);
tf_ssb_connect(ssb1, "127.0.0.1", 12347, id0bin);
tf_ssb_connect(ssb2, "127.0.0.1", 12347, id0bin);
while (_count_broadcasts(ssb0) != 2 || _count_broadcasts(ssb1) != 1 || _count_broadcasts(ssb2) != 1)
{
uv_run(&loop, UV_RUN_ONCE);
}
tf_ssb_send_close(ssb0);
tf_ssb_send_close(ssb1);
tf_ssb_send_close(ssb2);
tf_ssb_destroy(ssb0);
tf_ssb_destroy(ssb1);
tf_ssb_destroy(ssb2);
uv_run(&loop, UV_RUN_DEFAULT);
uv_loop_close(&loop);
}
#endif

View File

@ -53,4 +53,10 @@ void tf_ssb_test_go_ssb_room(const tf_test_options_t* options);
*/
void tf_ssb_test_encrypt(const tf_test_options_t* options);
/**
** Test peer exchange.
** @param options The test options.
*/
void tf_ssb_test_peer_exchange(const tf_test_options_t* options);
/** @} */

View File

@ -140,8 +140,7 @@ typedef struct _tf_task_t
float idle_percent;
float thread_percent;
uv_idle_t idle;
uv_prepare_t prepare;
uv_async_t run_jobs_async;
uv_signal_t sig_term;
uv_signal_t sig_int;
@ -208,7 +207,6 @@ static void _tf_task_sendPromiseExportMessage(tf_task_t* from, tf_taskstub_t* to
static JSValue _tf_task_executeSource(tf_task_t* task, const char* source, const char* name);
static tf_taskstub_t* _tf_task_get_stub(tf_task_t* task, taskid_t id);
static void _tf_task_release_export(tf_taskstub_t* stub, exportid_t exportId);
static void _tf_task_run_jobs_prepare(uv_prepare_t* prepare);
static void _timeout_unlink(tf_task_t* task, timeout_t* timeout);
static void _timeout_closed(uv_handle_t* handle);
@ -1521,25 +1519,20 @@ static bool _tf_task_run_jobs(tf_task_t* task)
return 0;
}
static void _tf_task_run_jobs_idle(uv_idle_t* idle)
static void _tf_task_run_jobs_async(uv_async_t* async)
{
tf_task_t* task = idle->data;
if (!_tf_task_run_jobs(task))
tf_task_t* task = async->data;
if (_tf_task_run_jobs(task))
{
/* No more jobs. Don't try again as actively. */
uv_idle_stop(&task->idle);
uv_prepare_start(&task->prepare, _tf_task_run_jobs_prepare);
uv_async_send(async);
}
}
static void _tf_task_run_jobs_prepare(uv_prepare_t* prepare)
void tf_task_check_jobs(tf_task_t* task)
{
tf_task_t* task = prepare->data;
if (_tf_task_run_jobs(task))
if (JS_IsJobPending(task->_runtime))
{
/* More jobs. We can run again immediately. */
uv_idle_start(&task->idle, _tf_task_run_jobs_idle);
uv_prepare_stop(&task->prepare);
uv_async_send(&task->run_jobs_async);
}
}
@ -1636,13 +1629,9 @@ tf_task_t* tf_task_create()
uv_timer_init(&task->_loop, &task->gc_timer);
uv_timer_start(&task->gc_timer, _tf_task_gc_timer, 1000, 1000);
uv_unref((uv_handle_t*)&task->gc_timer);
task->idle.data = task;
uv_idle_init(&task->_loop, &task->idle);
uv_unref((uv_handle_t*)&task->idle);
task->prepare.data = task;
uv_prepare_init(&task->_loop, &task->prepare);
uv_unref((uv_handle_t*)&task->prepare);
uv_idle_start(&task->idle, _tf_task_run_jobs_idle);
task->run_jobs_async.data = task;
uv_async_init(&task->_loop, &task->run_jobs_async, _tf_task_run_jobs_async);
uv_unref((uv_handle_t*)&task->run_jobs_async);
task->sig_term.data = task;
uv_signal_init(&task->_loop, &task->sig_term);
uv_signal_start(&task->sig_term, _tf_task_signal_shutdown, SIGTERM);
@ -1910,14 +1899,13 @@ void tf_task_destroy(tf_task_t* task)
{
uv_close((uv_handle_t*)&task->gc_timer, _tf_task_on_handle_close);
}
uv_close((uv_handle_t*)&task->idle, _tf_task_on_handle_close);
uv_close((uv_handle_t*)&task->prepare, _tf_task_on_handle_close);
uv_close((uv_handle_t*)&task->run_jobs_async, _tf_task_on_handle_close);
uv_signal_stop(&task->sig_term);
uv_close((uv_handle_t*)&task->sig_term, _tf_task_on_handle_close);
uv_signal_stop(&task->sig_int);
uv_close((uv_handle_t*)&task->sig_int, _tf_task_on_handle_close);
while (task->trace_timer.data || task->gc_timer.data || task->idle.data || task->prepare.data || task->sig_term.data || task->sig_int.data)
while (task->trace_timer.data || task->gc_timer.data || task->run_jobs_async.data || task->sig_term.data || task->sig_int.data)
{
uv_run(&task->_loop, UV_RUN_ONCE);
}

View File

@ -364,4 +364,11 @@ tf_android_start_service_t* tf_task_get_android_start_service();
*/
tf_android_stop_service_t* tf_task_get_android_stop_service();
/**
** Check for JS jobs that need to be run. Generally to be called post-JS_Call
** in tf_util_report_error.
** @param task The task.
*/
void tf_task_check_jobs(tf_task_t* task);
/** @} */

View File

@ -915,6 +915,7 @@ void tf_tests(const tf_test_options_t* options)
_tf_test_run(options, "auto", _test_auto, false);
_tf_test_run(options, "go-ssb-room", tf_ssb_test_go_ssb_room, true);
_tf_test_run(options, "encrypt", tf_ssb_test_encrypt, false);
_tf_test_run(options, "peer_exchange", tf_ssb_test_peer_exchange, false);
tf_printf("Tests completed.\n");
#endif
}

View File

@ -211,6 +211,7 @@ static JSValue _util_print(JSContext* context, JSValueConst this_val, int argc,
bool tf_util_report_error(JSContext* context, JSValue value)
{
bool is_error = false;
tf_task_t* task = tf_task_get(context);
if (JS_IsError(context, value))
{
const char* string = JS_ToCString(context, value);
@ -226,13 +227,11 @@ bool tf_util_report_error(JSContext* context, JSValue value)
}
JS_FreeValue(context, stack);
tf_task_t* task = tf_task_get(context);
tf_task_send_error_to_parent(task, value);
is_error = true;
}
else if (JS_IsException(value))
{
tf_task_t* task = tf_task_get(context);
if (!tf_task_send_error_to_parent(task, value))
{
JSValue exception = JS_GetException(context);
@ -241,6 +240,7 @@ bool tf_util_report_error(JSContext* context, JSValue value)
}
is_error = true;
}
tf_task_check_jobs(task);
return is_error;
}