Merge branch 'main' into tasiaiso-nix
This commit is contained in:
commit
76d499f00b
@ -4,7 +4,8 @@
|
|||||||
<script>
|
<script>
|
||||||
const g_data = $data;
|
const g_data = $data;
|
||||||
</script>
|
</script>
|
||||||
<link rel="stylesheet" href="w3.css"></link>
|
<link rel="stylesheet" href="w3.css" />
|
||||||
|
<!-- prettier-ignore -->
|
||||||
<style>
|
<style>
|
||||||
/* 2018 Valiant Poppy */
|
/* 2018 Valiant Poppy */
|
||||||
.w3-theme-l5 {color:#000 !important; background-color:#fbf3f3 !important}
|
.w3-theme-l5 {color:#000 !important; background-color:#fbf3f3 !important}
|
||||||
|
@ -42,10 +42,27 @@ window.addEventListener('load', function () {
|
|||||||
} else if (description.type === 'textarea') {
|
} else if (description.type === 'textarea') {
|
||||||
return html`
|
return html`
|
||||||
<li class="w3-row">
|
<li class="w3-row">
|
||||||
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${key}</label>
|
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold"
|
||||||
|
>${key}</label
|
||||||
|
>
|
||||||
<div class="w3-rest w3-padding">${description.description}</div>
|
<div class="w3-rest w3-padding">${description.description}</div>
|
||||||
<textarea class="w3-input" style="vertical-align: top; resize: vertical" id=${'gs_' + key}>${description.value}</textarea>
|
<textarea
|
||||||
<button class="w3-button w3-right w3-quarter w3-theme-action" @click=${(e) => global_settings_set(key, e.srcElement.previousElementSibling.value)}>Set</button>
|
class="w3-input"
|
||||||
|
style="vertical-align: top; resize: vertical"
|
||||||
|
id=${'gs_' + key}
|
||||||
|
>
|
||||||
|
${description.value}</textarea
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="w3-button w3-right w3-quarter w3-theme-action"
|
||||||
|
@click=${(e) =>
|
||||||
|
global_settings_set(
|
||||||
|
key,
|
||||||
|
e.srcElement.previousElementSibling.value
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
Set
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
} else {
|
} else {
|
||||||
@ -61,13 +78,17 @@ window.addEventListener('load', function () {
|
|||||||
}
|
}
|
||||||
const user_template = (user, permissions) => html`
|
const user_template = (user, permissions) => html`
|
||||||
<li class="w3-card w3-margin">
|
<li class="w3-card w3-margin">
|
||||||
<button class="w3-button w3-theme-action" @click=${(e) => delete_user(user)}>Delete</button>
|
<button
|
||||||
|
class="w3-button w3-theme-action"
|
||||||
|
@click=${(e) => delete_user(user)}
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
${user}: ${permissions.map((x) => permission_template(x))}
|
${user}: ${permissions.map((x) => permission_template(x))}
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
const users_template = (users) =>
|
const users_template = (users) =>
|
||||||
html`
|
html` <header class="w3-container w3-theme-l2"><h2>Users</h2></header>
|
||||||
<header class="w3-container w3-theme-l2"><h2>Users</h2></header>
|
|
||||||
<ul class="w3-ul">
|
<ul class="w3-ul">
|
||||||
${Object.entries(users).map((u) => user_template(u[0], u[1]))}
|
${Object.entries(users).map((u) => user_template(u[0], u[1]))}
|
||||||
</ul>`;
|
</ul>`;
|
||||||
|
@ -118,7 +118,9 @@ async function main() {
|
|||||||
<ul class="w3-ul">` +
|
<ul class="w3-ul">` +
|
||||||
ids
|
ids
|
||||||
.map(
|
.map(
|
||||||
(id) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis">
|
(
|
||||||
|
id
|
||||||
|
) => `<li style="overflow: hidden; text-wrap: nowrap; text-overflow: ellipsis">
|
||||||
<button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button>
|
<button onclick="handler.export_id(event)" data-id="${id}" class="w3-button w3-theme">Export Identity</button>
|
||||||
<button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button>
|
<button onclick="handler.delete_id(event)" data-id="${id}" class="w3-button w3-theme">Delete Identity</button>
|
||||||
${id}
|
${id}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"type": "tildefriends-app",
|
"type": "tildefriends-app",
|
||||||
"emoji": "🐌",
|
"emoji": "🐌",
|
||||||
"previous": "&raSj7ozmSDNGmB6TtjDk7oOiTc33ZN+RrBMASJ2F4cA=.sha256"
|
"previous": "&wA6sLaDxtYeFdVCCu8jyhPsGYtGZEjbWQHeGOn0Yifg=.sha256"
|
||||||
}
|
}
|
||||||
|
@ -345,13 +345,15 @@ class TfElement extends LitElement {
|
|||||||
([k, v]) => html`
|
([k, v]) => html`
|
||||||
<button
|
<button
|
||||||
title=${v}
|
title=${v}
|
||||||
class="w3-bar-item w3-padding-large w3-hover-theme tab ${self.tab ==
|
class="w3-bar-item w3-padding w3-hover-theme tab ${self.tab == v
|
||||||
v
|
|
||||||
? 'w3-theme-l2'
|
? 'w3-theme-l2'
|
||||||
: 'w3-theme-l1'}"
|
: 'w3-theme-l1'}"
|
||||||
@click=${() => self.set_tab(v)}
|
@click=${() => self.set_tab(v)}
|
||||||
>
|
>
|
||||||
${k}
|
${k}
|
||||||
|
<span class=${self.tab == v ? '' : 'w3-hide-small'}
|
||||||
|
>${v.charAt(0).toUpperCase() + v.substring(1)}</span
|
||||||
|
>
|
||||||
</button>
|
</button>
|
||||||
`
|
`
|
||||||
)}
|
)}
|
||||||
@ -359,7 +361,9 @@ class TfElement extends LitElement {
|
|||||||
`;
|
`;
|
||||||
let contents = !this.loaded
|
let contents = !this.loaded
|
||||||
? this.loading
|
? this.loading
|
||||||
? html`<div class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge">
|
? html`<div
|
||||||
|
class="w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge"
|
||||||
|
>
|
||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
${this.render_tab()}`
|
${this.render_tab()}`
|
||||||
|
@ -295,14 +295,18 @@ class TfComposeElement extends LitElement {
|
|||||||
{
|
{
|
||||||
values: values,
|
values: values,
|
||||||
selectTemplate: function (item) {
|
selectTemplate: function (item) {
|
||||||
return item ? `[@${item.original.key}](${item.original.value})` : undefined;
|
return item
|
||||||
|
? `[@${item.original.key}](${item.original.value})`
|
||||||
|
: undefined;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
trigger: '&',
|
trigger: '&',
|
||||||
values: this.autocomplete,
|
values: this.autocomplete,
|
||||||
selectTemplate: function (item) {
|
selectTemplate: function (item) {
|
||||||
return item ? `![${item.original.key}](${item.original.value})` : undefined;
|
return item
|
||||||
|
? `![${item.original.key}](${item.original.value})`
|
||||||
|
: undefined;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -247,9 +247,7 @@ ${JSON.stringify(mention, null, 2)}</pre
|
|||||||
if (mentions.length) {
|
if (mentions.length) {
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
<fieldset
|
<fieldset style="padding: 0.5em; border: 1px solid black">
|
||||||
style="padding: 0.5em; border: 1px solid black"
|
|
||||||
>
|
|
||||||
<legend>Mentions</legend>
|
<legend>Mentions</legend>
|
||||||
${mentions.map((x) => self.render_mention(x))}
|
${mentions.map((x) => self.render_mention(x))}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@ -105,6 +105,16 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render_connection(connection) {
|
render_connection(connection) {
|
||||||
|
let requests = Object.values(
|
||||||
|
connection.requests.reduce(function (accumulator, value) {
|
||||||
|
let key = `${value.name}:${Math.sign(value.request_number)}`;
|
||||||
|
if (!accumulator[key]) {
|
||||||
|
accumulator[key] = Object.assign({count: 0}, value);
|
||||||
|
}
|
||||||
|
accumulator[key].count++;
|
||||||
|
return accumulator;
|
||||||
|
}, {})
|
||||||
|
);
|
||||||
return html`
|
return html`
|
||||||
<button
|
<button
|
||||||
class="w3-button w3-theme-d1"
|
class="w3-button w3-theme-d1"
|
||||||
@ -116,9 +126,20 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
${connection.tunnel !== undefined
|
${connection.tunnel !== undefined
|
||||||
? '🚇'
|
? '🚇'
|
||||||
: html`(${connection.host}:${connection.port})`}
|
: html`(${connection.host}:${connection.port})`}
|
||||||
<div>${connection.requests.map(x => html`
|
<div>
|
||||||
<span class="w3-tag w3-small">${x.request_number > 0 ? '🟩' : '🟥'} ${x.name}</span>
|
${requests.map(
|
||||||
`)}</div>
|
(x) => html`
|
||||||
|
<span class="w3-tag w3-small"
|
||||||
|
>${x.request_number > 0 ? '🟩' : '🟥'} ${x.name}
|
||||||
|
<span
|
||||||
|
class="w3-badge w3-white"
|
||||||
|
style=${x.count > 1 ? undefined : 'display: none'}
|
||||||
|
>${x.count}</span
|
||||||
|
></span
|
||||||
|
>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
${this.connections
|
${this.connections
|
||||||
.filter((x) => x.tunnel === this.connections.indexOf(connection))
|
.filter((x) => x.tunnel === this.connections.indexOf(connection))
|
||||||
@ -187,12 +208,16 @@ class TfTabConnectionsElement extends LitElement {
|
|||||||
${this.identities.map(
|
${this.identities.map(
|
||||||
(x) =>
|
(x) =>
|
||||||
html`<li class="w3-bar">
|
html`<li class="w3-bar">
|
||||||
${x == this.server_identity ?
|
${x == this.server_identity
|
||||||
html`<span class="w3-tag w3-medium w3-round w3-theme-l1">🖥 local server</span>` :
|
? html`<span class="w3-tag w3-medium w3-round w3-theme-l1"
|
||||||
undefined}
|
>🖥 local server</span
|
||||||
${this.my_identities.indexOf(x) != -1 ?
|
>`
|
||||||
html`<span class="w3-tag w3-medium w3-round w3-theme-d1">😎 you</span>` :
|
: undefined}
|
||||||
undefined}
|
${this.my_identities.indexOf(x) != -1
|
||||||
|
? html`<span class="w3-tag w3-medium w3-round w3-theme-d1"
|
||||||
|
>😎 you</span
|
||||||
|
>`
|
||||||
|
: undefined}
|
||||||
<tf-user id=${x} .users=${this.users}></tf-user>
|
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||||
</li>`
|
</li>`
|
||||||
)}
|
)}
|
||||||
|
@ -115,12 +115,16 @@ class TfTabNewsElement extends LitElement {
|
|||||||
></tf-profile>`
|
></tf-profile>`
|
||||||
: undefined;
|
: undefined;
|
||||||
let edit_profile;
|
let edit_profile;
|
||||||
if (!this.loading &&
|
if (
|
||||||
|
!this.loading &&
|
||||||
this.users[this.whoami]?.name === undefined &&
|
this.users[this.whoami]?.name === undefined &&
|
||||||
this.hash.substring(1) != this.whoami) {
|
this.hash.substring(1) != this.whoami
|
||||||
edit_profile = html`
|
) {
|
||||||
<div class="w3-panel w3-padding w3-round w3-card-4 w3-theme-l3">
|
edit_profile = html` <div
|
||||||
ℹ️ Follow your identity link ☝️ above to edit your profile and set your name.
|
class="w3-panel w3-padding w3-round w3-card-4 w3-theme-l3"
|
||||||
|
>
|
||||||
|
ℹ️ Follow your identity link ☝️ above to edit your profile and set your
|
||||||
|
name.
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
|
@ -19,6 +19,10 @@ class TfUserElement extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let image = html`<span
|
||||||
|
class="w3-theme-light w3-circle"
|
||||||
|
style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"
|
||||||
|
>?</span>`;
|
||||||
let name = this.users?.[this.id]?.name;
|
let name = this.users?.[this.id]?.name;
|
||||||
name =
|
name =
|
||||||
name !== undefined
|
name !== undefined
|
||||||
@ -26,22 +30,21 @@ class TfUserElement extends LitElement {
|
|||||||
: html`<a target="_top" href=${'#' + this.id}>${this.id}</a>`;
|
: html`<a target="_top" href=${'#' + this.id}>${this.id}</a>`;
|
||||||
|
|
||||||
if (this.users[this.id]) {
|
if (this.users[this.id]) {
|
||||||
let image = this.users[this.id].image;
|
let image_link = this.users[this.id].image;
|
||||||
image = typeof image == 'string' ? image : image?.link;
|
image_link = typeof image_link == 'string' ? image_link : image_link?.link;
|
||||||
return html` <div style="display: inline-block; font-weight: bold">
|
if (image_link !== undefined) {
|
||||||
<img
|
image = html`<img
|
||||||
style="width: 2em; height: 2em; vertical-align: middle; border-radius: 50%"
|
class="w3-circle"
|
||||||
?hidden=${image === undefined}
|
style="width: 2em; height: 2em; vertical-align: middle"
|
||||||
src="${image ? '/' + image + '/view' : undefined}"
|
src="/${image_link}/view"
|
||||||
/>
|
/>`;
|
||||||
${name}
|
|
||||||
</div>`;
|
|
||||||
} else {
|
|
||||||
return html` <div style="display: inline-block; font-weight: bold">
|
|
||||||
${name}
|
|
||||||
</div>`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return html` <div style="display: inline-block; font-weight: bold">
|
||||||
|
${image}
|
||||||
|
${name}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define('tf-user', TfUserElement);
|
customElements.define('tf-user', TfUserElement);
|
||||||
|
23
src/ssb.db.c
23
src/ssb.db.c
@ -737,12 +737,11 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||||
size_t out_previous_size, char* out_author, size_t out_author_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature,
|
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags)
|
||||||
size_t out_signature_size, int* out_flags)
|
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
sqlite3_stmt* statement;
|
sqlite3_stmt* statement;
|
||||||
const char* query = "SELECT id, previous, author, timestamp, json(content), hash, signature, flags FROM messages WHERE author = ?1 AND sequence = ?2";
|
const char* query = "SELECT id, previous, timestamp, json(content), hash, signature, flags FROM messages WHERE author = ?1 AND sequence = ?2";
|
||||||
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
sqlite3* db = tf_ssb_acquire_db_reader(ssb);
|
||||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||||
{
|
{
|
||||||
@ -766,29 +765,25 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
|||||||
snprintf(out_previous, out_previous_size, "%s", (const char*)sqlite3_column_text(statement, 1));
|
snprintf(out_previous, out_previous_size, "%s", (const char*)sqlite3_column_text(statement, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out_author)
|
|
||||||
{
|
|
||||||
snprintf(out_author, out_author_size, "%s", (const char*)sqlite3_column_text(statement, 2));
|
|
||||||
}
|
|
||||||
if (out_timestamp)
|
if (out_timestamp)
|
||||||
{
|
{
|
||||||
*out_timestamp = sqlite3_column_double(statement, 3);
|
*out_timestamp = sqlite3_column_double(statement, 2);
|
||||||
}
|
}
|
||||||
if (out_content)
|
if (out_content)
|
||||||
{
|
{
|
||||||
*out_content = tf_strdup((const char*)sqlite3_column_text(statement, 4));
|
*out_content = tf_strdup((const char*)sqlite3_column_text(statement, 3));
|
||||||
}
|
}
|
||||||
if (out_hash)
|
if (out_hash)
|
||||||
{
|
{
|
||||||
snprintf(out_hash, out_hash_size, "%s", (const char*)sqlite3_column_text(statement, 5));
|
snprintf(out_hash, out_hash_size, "%s", (const char*)sqlite3_column_text(statement, 4));
|
||||||
}
|
}
|
||||||
if (out_signature)
|
if (out_signature)
|
||||||
{
|
{
|
||||||
snprintf(out_signature, out_signature_size, "%s", (const char*)sqlite3_column_text(statement, 6));
|
snprintf(out_signature, out_signature_size, "%s", (const char*)sqlite3_column_text(statement, 5));
|
||||||
}
|
}
|
||||||
if (out_flags)
|
if (out_flags)
|
||||||
{
|
{
|
||||||
*out_flags = sqlite3_column_int(statement, 7);
|
*out_flags = sqlite3_column_int(statement, 6);
|
||||||
}
|
}
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@ -1834,8 +1829,8 @@ bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id)
|
|||||||
char hash[32];
|
char hash[32];
|
||||||
char signature[256];
|
char signature[256];
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, i, message_id, sizeof(message_id), previous, sizeof(previous), NULL, 0, ×tamp, &content, hash,
|
if (tf_ssb_db_get_message_by_author_and_sequence(
|
||||||
sizeof(hash), signature, sizeof(signature), &flags))
|
ssb, id, i, message_id, sizeof(message_id), previous, sizeof(previous), ×tamp, &content, hash, sizeof(hash), signature, sizeof(signature), &flags))
|
||||||
{
|
{
|
||||||
JSValue message = tf_ssb_format_message(context, previous, id, i, timestamp, hash, content, signature, flags);
|
JSValue message = tf_ssb_format_message(context, previous, id, i, timestamp, hash, content, signature, flags);
|
||||||
char calculated_id[k_id_base64_len];
|
char calculated_id[k_id_base64_len];
|
||||||
|
16
src/ssb.db.h
16
src/ssb.db.h
@ -122,13 +122,19 @@ JSValue tf_ssb_db_get_message_by_id(tf_ssb_t* ssb, const char* id, bool is_keys)
|
|||||||
** @param sequence The message sequence number.
|
** @param sequence The message sequence number.
|
||||||
** @param[out] out_message_id Populated with the message identifier.
|
** @param[out] out_message_id Populated with the message identifier.
|
||||||
** @param out_message_id_size The size of the out_message_id buffer.
|
** @param out_message_id_size The size of the out_message_id buffer.
|
||||||
|
** @param[out] out_previous Populated with the previous message identifier.
|
||||||
|
** @param out_previous_size The size of the out_previous buffer.
|
||||||
** @param[out] out_timestamp Populated with the timestamp.
|
** @param[out] out_timestamp Populated with the timestamp.
|
||||||
** @param[out] out_content Populated with the message content. Free with tf_free().
|
** @param[out] out_content Populated with the message content. Free with tf_free().
|
||||||
|
** @param[out] out_hash Populated with the message hash format.
|
||||||
|
** @param out_hash_size The size of the out_hash buffer.
|
||||||
|
** @param[out] out_signature Populated with the message signature.
|
||||||
|
** @param out_signature_size The size of the out_signature buffer.
|
||||||
|
** @param[out] out_flags Populated with flags describing the format of the message.
|
||||||
** @return True if the message was found and retrieved.
|
** @return True if the message was found and retrieved.
|
||||||
*/
|
*/
|
||||||
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* author, int64_t sequence, char* out_message_id, size_t out_message_id_size, char* out_previous,
|
||||||
size_t out_previous_size, char* out_author, size_t out_author_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature,
|
size_t out_previous_size, double* out_timestamp, char** out_content, char* out_hash, size_t out_hash_size, char* out_signature, size_t out_signature_size, int* out_flags);
|
||||||
size_t out_signature_size, int* out_flags);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Get information about the last message from an author.
|
** Get information about the last message from an author.
|
||||||
@ -380,6 +386,12 @@ bool tf_ssb_db_set_property(tf_ssb_t* ssb, const char* id, const char* key, cons
|
|||||||
*/
|
*/
|
||||||
void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callback)(const char* path, void* user_data), void* user_data);
|
void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callback)(const char* path, void* user_data), void* user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Verify an author's feed.
|
||||||
|
** @param ssb The SSB instance.
|
||||||
|
** @param id The author'd identity.
|
||||||
|
** @return true If the feed verified successfully.
|
||||||
|
*/
|
||||||
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id);
|
bool tf_ssb_db_verify(tf_ssb_t* ssb, const char* id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user