Page MenuHomePhabricator

No OneTemporary

diff --git a/lib/ops/user-store-ops.js b/lib/ops/user-store-ops.js
index 935e9493a..f8fe7c031 100644
--- a/lib/ops/user-store-ops.js
+++ b/lib/ops/user-store-ops.js
@@ -1,92 +1,90 @@
// @flow
import { type BaseStoreOpsHandlers } from './base-ops.js';
import type { UserInfo, UserInfos } from '../types/user-types.js';
import { values } from '../utils/objects.js';
// client types
export type ReplaceUserOperation = {
+type: 'replace_user',
+payload: UserInfo,
};
export type RemoveUsersOperation = {
+type: 'remove_users',
+payload: { +ids: $ReadOnlyArray<string> },
};
export type RemoveAllUsersOperation = {
+type: 'remove_all_users',
};
export type UserStoreOperation =
| ReplaceUserOperation
| RemoveUsersOperation
| RemoveAllUsersOperation;
// SQLite types
export type ClientDBUserInfo = {
+id: string,
- +username: ?string,
- +relationshipStatus?: string,
- +avatar?: ?string,
+ +userInfo: string,
};
export type ClientDBReplaceUserOperation = {
+type: 'replace_user',
+payload: ClientDBUserInfo,
};
export type ClientDBUserStoreOperation =
| ClientDBReplaceUserOperation
| RemoveUsersOperation
| RemoveAllUsersOperation;
function convertUserInfosToReplaceUserOps(
userInfos: UserInfos,
): $ReadOnlyArray<UserStoreOperation> {
return values(userInfos).map(userInfo => ({
type: 'replace_user',
payload: userInfo,
}));
}
const userStoreOpsHandlers: BaseStoreOpsHandlers<
UserInfos,
UserStoreOperation,
ClientDBUserStoreOperation,
UserInfos,
ClientDBUserInfo,
> = {
processStoreOperations(
userInfos: UserInfos,
ops: $ReadOnlyArray<UserStoreOperation>,
): UserInfos {
if (ops.length === 0) {
return userInfos;
}
let processedUserInfos = { ...userInfos };
for (const operation: UserStoreOperation of ops) {
if (operation.type === 'replace_user') {
processedUserInfos[operation.payload.id] = operation.payload;
} else if (operation.type === 'remove_users') {
for (const id of operation.payload.ids) {
delete processedUserInfos[id];
}
} else if (operation.type === 'remove_all_users') {
processedUserInfos = {};
}
}
return processedUserInfos;
},
convertOpsToClientDBOps(): $ReadOnlyArray<ClientDBUserStoreOperation> {
return [];
},
translateClientDBData(): UserInfos {
return {};
},
};
export { userStoreOpsHandlers, convertUserInfosToReplaceUserOps };
diff --git a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h
index d4121625f..822d52d74 100644
--- a/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h
+++ b/native/cpp/CommonCpp/DatabaseManagers/DatabaseQueryExecutor.h
@@ -1,80 +1,85 @@
#pragma once
#include "../CryptoTools/Persist.h"
#include "entities/Draft.h"
#include "entities/Media.h"
#include "entities/Message.h"
#include "entities/MessageStoreThread.h"
#include "entities/OlmPersistAccount.h"
#include "entities/OlmPersistSession.h"
#include "entities/PersistItem.h"
#include "entities/Report.h"
#include "entities/Thread.h"
+#include "entities/UserInfo.h"
#include <string>
namespace comm {
/**
* if any initialization/cleaning up steps are required for specific
* database managers they should appear in constructors/destructors
* following the RAII pattern
*/
class DatabaseQueryExecutor {
public:
virtual std::string getDraft(std::string key) const = 0;
virtual std::unique_ptr<Thread> getThread(std::string threadID) const = 0;
virtual void updateDraft(std::string key, std::string text) const = 0;
virtual bool moveDraft(std::string oldKey, std::string newKey) const = 0;
virtual std::vector<Draft> getAllDrafts() const = 0;
virtual void removeAllDrafts() const = 0;
virtual void removeAllMessages() const = 0;
virtual std::vector<std::pair<Message, std::vector<Media>>>
getAllMessages() const = 0;
virtual void removeMessages(const std::vector<std::string> &ids) const = 0;
virtual void
removeMessagesForThreads(const std::vector<std::string> &threadIDs) const = 0;
virtual void replaceMessage(const Message &message) const = 0;
virtual void rekeyMessage(std::string from, std::string to) const = 0;
virtual void removeAllMedia() const = 0;
virtual void replaceMessageStoreThreads(
const std::vector<MessageStoreThread> &threads) const = 0;
virtual void
removeMessageStoreThreads(const std::vector<std::string> &ids) const = 0;
virtual void removeAllMessageStoreThreads() const = 0;
virtual std::vector<MessageStoreThread> getAllMessageStoreThreads() const = 0;
virtual void
removeMediaForMessages(const std::vector<std::string> &msg_ids) const = 0;
virtual void removeMediaForMessage(std::string msg_id) const = 0;
virtual void
removeMediaForThreads(const std::vector<std::string> &thread_ids) const = 0;
virtual void replaceMedia(const Media &media) const = 0;
virtual void rekeyMediaContainers(std::string from, std::string to) const = 0;
virtual std::vector<Thread> getAllThreads() const = 0;
virtual void removeThreads(std::vector<std::string> ids) const = 0;
virtual void replaceThread(const Thread &thread) const = 0;
virtual void removeAllThreads() const = 0;
virtual void replaceReport(const Report &report) const = 0;
virtual void removeReports(const std::vector<std::string> &ids) const = 0;
virtual void removeAllReports() const = 0;
virtual std::vector<Report> getAllReports() const = 0;
virtual void
setPersistStorageItem(std::string key, std::string item) const = 0;
virtual void removePersistStorageItem(std::string key) const = 0;
virtual std::string getPersistStorageItem(std::string key) const = 0;
+ virtual void replaceUser(const UserInfo &user_info) const = 0;
+ virtual void removeUsers(const std::vector<std::string> &ids) const = 0;
+ virtual void removeAllUsers() const = 0;
+ virtual std::vector<UserInfo> getAllUsers() const = 0;
virtual void beginTransaction() const = 0;
virtual void commitTransaction() const = 0;
virtual void rollbackTransaction() const = 0;
virtual std::vector<OlmPersistSession> getOlmPersistSessionsData() const = 0;
virtual std::optional<std::string> getOlmPersistAccountData() const = 0;
virtual void storeOlmPersistData(crypto::Persist persist) const = 0;
virtual void setNotifyToken(std::string token) const = 0;
virtual void clearNotifyToken() const = 0;
virtual void setCurrentUserID(std::string userID) const = 0;
virtual std::string getCurrentUserID() const = 0;
virtual void setMetadata(std::string entry_name, std::string data) const = 0;
virtual void clearMetadata(std::string entry_name) const = 0;
virtual std::string getMetadata(std::string entry_name) const = 0;
};
} // namespace comm
diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
index f311253c0..6590d2583 100644
--- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
+++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
@@ -1,1374 +1,1397 @@
#include "SQLiteQueryExecutor.h"
#include "Logger.h"
#include "sqlite_orm.h"
#include "entities/Metadata.h"
+#include "entities/UserInfo.h"
#include <fstream>
#include <iostream>
#include <thread>
#ifndef EMSCRIPTEN
#include "CommSecureStore.h"
#endif
#define ACCOUNT_ID 1
namespace comm {
using namespace sqlite_orm;
std::string SQLiteQueryExecutor::sqliteFilePath;
std::string SQLiteQueryExecutor::encryptionKey;
std::once_flag SQLiteQueryExecutor::initialized;
int SQLiteQueryExecutor::sqlcipherEncryptionKeySize = 64;
std::string SQLiteQueryExecutor::secureStoreEncryptionKeyID =
"comm.encryptionKey";
bool create_table(sqlite3 *db, std::string query, std::string tableName) {
char *error;
sqlite3_exec(db, query.c_str(), nullptr, nullptr, &error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error creating '" << tableName << "' table: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool create_drafts_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS drafts (threadID TEXT UNIQUE PRIMARY KEY, "
"text TEXT);";
return create_table(db, query, "drafts");
}
bool rename_threadID_to_key(sqlite3 *db) {
sqlite3_stmt *key_column_stmt;
sqlite3_prepare_v2(
db,
"SELECT name AS col_name FROM pragma_table_xinfo ('drafts') WHERE "
"col_name='key';",
-1,
&key_column_stmt,
nullptr);
sqlite3_step(key_column_stmt);
auto num_bytes = sqlite3_column_bytes(key_column_stmt, 0);
sqlite3_finalize(key_column_stmt);
if (num_bytes) {
return true;
}
char *error;
sqlite3_exec(
db,
"ALTER TABLE drafts RENAME COLUMN `threadID` TO `key`;",
nullptr,
nullptr,
&error);
if (error) {
std::ostringstream stringStream;
stringStream << "Error occurred renaming threadID column in drafts table "
<< "to key: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
return true;
}
bool create_persist_account_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS olm_persist_account("
"id INTEGER UNIQUE PRIMARY KEY NOT NULL, "
"account_data TEXT NOT NULL);";
return create_table(db, query, "olm_persist_account");
}
bool create_persist_sessions_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS olm_persist_sessions("
"target_user_id TEXT UNIQUE PRIMARY KEY NOT NULL, "
"session_data TEXT NOT NULL);";
return create_table(db, query, "olm_persist_sessions");
}
bool drop_messages_table(sqlite3 *db) {
char *error;
sqlite3_exec(db, "DROP TABLE IF EXISTS messages;", nullptr, nullptr, &error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error dropping 'messages' table: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool recreate_messages_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS messages ( "
"id TEXT UNIQUE PRIMARY KEY NOT NULL, "
"local_id TEXT, "
"thread TEXT NOT NULL, "
"user TEXT NOT NULL, "
"type INTEGER NOT NULL, "
"future_type INTEGER, "
"content TEXT, "
"time INTEGER NOT NULL);";
return create_table(db, query, "messages");
}
bool create_messages_idx_thread_time(sqlite3 *db) {
char *error;
sqlite3_exec(
db,
"CREATE INDEX IF NOT EXISTS messages_idx_thread_time "
"ON messages (thread, time);",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error creating (thread, time) index on messages table: "
<< error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool create_media_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS media ( "
"id TEXT UNIQUE PRIMARY KEY NOT NULL, "
"container TEXT NOT NULL, "
"thread TEXT NOT NULL, "
"uri TEXT NOT NULL, "
"type TEXT NOT NULL, "
"extras TEXT NOT NULL);";
return create_table(db, query, "media");
}
bool create_media_idx_container(sqlite3 *db) {
char *error;
sqlite3_exec(
db,
"CREATE INDEX IF NOT EXISTS media_idx_container "
"ON media (container);",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error creating (container) index on media table: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool create_threads_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS threads ( "
"id TEXT UNIQUE PRIMARY KEY NOT NULL, "
"type INTEGER NOT NULL, "
"name TEXT, "
"description TEXT, "
"color TEXT NOT NULL, "
"creation_time BIGINT NOT NULL, "
"parent_thread_id TEXT, "
"containing_thread_id TEXT, "
"community TEXT, "
"members TEXT NOT NULL, "
"roles TEXT NOT NULL, "
"current_user TEXT NOT NULL, "
"source_message_id TEXT, "
"replies_count INTEGER NOT NULL);";
return create_table(db, query, "threads");
}
bool update_threadID_for_pending_threads_in_drafts(sqlite3 *db) {
char *error;
sqlite3_exec(
db,
"UPDATE drafts SET key = "
"REPLACE(REPLACE(REPLACE(REPLACE(key, 'type4/', ''),"
"'type5/', ''),'type6/', ''),'type7/', '')"
"WHERE key LIKE 'pending/%'",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error update pending threadIDs on drafts table: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool enable_write_ahead_logging_mode(sqlite3 *db) {
char *error;
sqlite3_exec(db, "PRAGMA journal_mode=wal;", nullptr, nullptr, &error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error enabling write-ahead logging mode: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool create_metadata_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS metadata ( "
"name TEXT UNIQUE PRIMARY KEY NOT NULL, "
"data TEXT);";
return create_table(db, query, "metadata");
}
bool add_not_null_constraint_to_drafts(sqlite3 *db) {
char *error;
sqlite3_exec(
db,
"CREATE TABLE IF NOT EXISTS temporary_drafts ("
"key TEXT UNIQUE PRIMARY KEY NOT NULL, "
"text TEXT NOT NULL);"
"INSERT INTO temporary_drafts SELECT * FROM drafts "
"WHERE key IS NOT NULL AND text IS NOT NULL;"
"DROP TABLE drafts;"
"ALTER TABLE temporary_drafts RENAME TO drafts;",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error adding NOT NULL constraint to drafts table: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool add_not_null_constraint_to_metadata(sqlite3 *db) {
char *error;
sqlite3_exec(
db,
"CREATE TABLE IF NOT EXISTS temporary_metadata ("
"name TEXT UNIQUE PRIMARY KEY NOT NULL, "
"data TEXT NOT NULL);"
"INSERT INTO temporary_metadata SELECT * FROM metadata "
"WHERE data IS NOT NULL;"
"DROP TABLE metadata;"
"ALTER TABLE temporary_metadata RENAME TO metadata;",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error adding NOT NULL constraint to metadata table: "
<< error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool add_avatar_column_to_threads_table(sqlite3 *db) {
char *error;
sqlite3_exec(
db,
"ALTER TABLE threads ADD COLUMN avatar TEXT;",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error adding avatar column to threads table: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool add_pinned_count_column_to_threads(sqlite3 *db) {
sqlite3_stmt *pinned_column_stmt;
sqlite3_prepare_v2(
db,
"SELECT name AS col_name FROM pragma_table_xinfo ('threads') WHERE "
"col_name='pinned_count';",
-1,
&pinned_column_stmt,
nullptr);
sqlite3_step(pinned_column_stmt);
auto num_bytes = sqlite3_column_bytes(pinned_column_stmt, 0);
sqlite3_finalize(pinned_column_stmt);
if (num_bytes) {
return true;
}
char *error;
sqlite3_exec(
db,
"ALTER TABLE threads ADD COLUMN pinned_count INTEGER NOT NULL DEFAULT 0;",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error adding pinned_count column to threads table: "
<< error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
bool create_message_store_threads_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS message_store_threads ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" start_reached INTEGER NOT NULL,"
" last_navigated_to BIGINT NOT NULL,"
" last_pruned BIGINT NOT NULL"
");";
return create_table(db, query, "message_store_threads");
}
bool create_reports_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS reports ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" report TEXT NOT NULL"
");";
return create_table(db, query, "reports");
}
bool create_persist_storage_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS persist_storage ("
" key TEXT UNIQUE PRIMARY KEY NOT NULL,"
" item TEXT NOT NULL"
");";
return create_table(db, query, "persist_storage");
}
bool recreate_message_store_threads_table(sqlite3 *db) {
char *errMsg = 0;
// 1. Create table without `last_navigated_to` or `last_pruned`.
std::string create_new_table_query =
"CREATE TABLE IF NOT EXISTS temp_message_store_threads ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" start_reached INTEGER NOT NULL"
");";
if (sqlite3_exec(db, create_new_table_query.c_str(), NULL, NULL, &errMsg) !=
SQLITE_OK) {
Logger::log(
"Error creating temp_message_store_threads: " + std::string{errMsg});
sqlite3_free(errMsg);
return false;
}
// 2. Dump data from existing `message_store_threads` table into temp table.
std::string copy_data_query =
"INSERT INTO temp_message_store_threads (id, start_reached)"
"SELECT id, start_reached FROM message_store_threads;";
if (sqlite3_exec(db, copy_data_query.c_str(), NULL, NULL, &errMsg) !=
SQLITE_OK) {
Logger::log(
"Error dumping data from existing message_store_threads to "
"temp_message_store_threads: " +
std::string{errMsg});
sqlite3_free(errMsg);
return false;
}
// 3. Drop the existing `message_store_threads` table.
std::string drop_old_table_query = "DROP TABLE message_store_threads;";
if (sqlite3_exec(db, drop_old_table_query.c_str(), NULL, NULL, &errMsg) !=
SQLITE_OK) {
Logger::log(
"Error dropping message_store_threads table: " + std::string{errMsg});
sqlite3_free(errMsg);
return false;
}
// 4. Rename the temp table back to `message_store_threads`.
std::string rename_table_query =
"ALTER TABLE temp_message_store_threads RENAME TO message_store_threads;";
if (sqlite3_exec(db, rename_table_query.c_str(), NULL, NULL, &errMsg) !=
SQLITE_OK) {
Logger::log(
"Error renaming temp_message_store_threads to message_store_threads: " +
std::string{errMsg});
sqlite3_free(errMsg);
return false;
}
return true;
}
bool create_users_table(sqlite3 *db) {
std::string query =
"CREATE TABLE IF NOT EXISTS users ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" user_info TEXT NOT NULL"
");";
return create_table(db, query, "users");
}
bool create_schema(sqlite3 *db) {
char *error;
sqlite3_exec(
db,
"CREATE TABLE IF NOT EXISTS drafts ("
" key TEXT UNIQUE PRIMARY KEY NOT NULL,"
" text TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS messages ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" local_id TEXT,"
" thread TEXT NOT NULL,"
" user TEXT NOT NULL,"
" type INTEGER NOT NULL,"
" future_type INTEGER,"
" content TEXT,"
" time INTEGER NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS olm_persist_account ("
" id INTEGER UNIQUE PRIMARY KEY NOT NULL,"
" account_data TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS olm_persist_sessions ("
" target_user_id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" session_data TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS media ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" container TEXT NOT NULL,"
" thread TEXT NOT NULL,"
" uri TEXT NOT NULL,"
" type TEXT NOT NULL,"
" extras TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS threads ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" type INTEGER NOT NULL,"
" name TEXT,"
" description TEXT,"
" color TEXT NOT NULL,"
" creation_time BIGINT NOT NULL,"
" parent_thread_id TEXT,"
" containing_thread_id TEXT,"
" community TEXT,"
" members TEXT NOT NULL,"
" roles TEXT NOT NULL,"
" current_user TEXT NOT NULL,"
" source_message_id TEXT,"
" replies_count INTEGER NOT NULL,"
" avatar TEXT,"
" pinned_count INTEGER NOT NULL DEFAULT 0"
");"
"CREATE TABLE IF NOT EXISTS metadata ("
" name TEXT UNIQUE PRIMARY KEY NOT NULL,"
" data TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS message_store_threads ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" start_reached INTEGER NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS reports ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" report TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS persist_storage ("
" key TEXT UNIQUE PRIMARY KEY NOT NULL,"
" item TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS users ("
" id TEXT UNIQUE PRIMARY KEY NOT NULL,"
" user_info TEXT NOT NULL"
");"
"CREATE INDEX IF NOT EXISTS media_idx_container"
" ON media (container);"
"CREATE INDEX IF NOT EXISTS messages_idx_thread_time"
" ON messages (thread, time);",
nullptr,
nullptr,
&error);
if (!error) {
return true;
}
std::ostringstream stringStream;
stringStream << "Error creating tables: " << error;
Logger::log(stringStream.str());
sqlite3_free(error);
return false;
}
void set_encryption_key(sqlite3 *db) {
std::string set_encryption_key_query =
"PRAGMA key = \"x'" + SQLiteQueryExecutor::encryptionKey + "'\";";
char *error_set_key;
sqlite3_exec(
db, set_encryption_key_query.c_str(), nullptr, nullptr, &error_set_key);
if (error_set_key) {
std::ostringstream error_message;
error_message << "Failed to set encryption key: " << error_set_key;
throw std::system_error(
ECANCELED, std::generic_category(), error_message.str());
}
}
int get_database_version(sqlite3 *db) {
sqlite3_stmt *user_version_stmt;
sqlite3_prepare_v2(
db, "PRAGMA user_version;", -1, &user_version_stmt, nullptr);
sqlite3_step(user_version_stmt);
int current_user_version = sqlite3_column_int(user_version_stmt, 0);
sqlite3_finalize(user_version_stmt);
return current_user_version;
}
bool set_database_version(sqlite3 *db, int db_version) {
std::stringstream update_version;
update_version << "PRAGMA user_version=" << db_version << ";";
auto update_version_str = update_version.str();
char *error;
sqlite3_exec(db, update_version_str.c_str(), nullptr, nullptr, &error);
if (!error) {
return true;
}
std::ostringstream errorStream;
errorStream << "Error setting database version to " << db_version << ": "
<< error;
Logger::log(errorStream.str());
sqlite3_free(error);
return false;
}
void trace_queries(sqlite3 *db) {
int error_code = sqlite3_trace_v2(
db,
SQLITE_TRACE_PROFILE,
[](unsigned, void *, void *preparedStatement, void *) {
sqlite3_stmt *statement = (sqlite3_stmt *)preparedStatement;
char *sql = sqlite3_expanded_sql(statement);
if (sql != nullptr) {
std::string sqlStr(sql);
// TODO: send logs to backup here
}
return 0;
},
NULL);
if (error_code != SQLITE_OK) {
std::ostringstream error_message;
error_message << "Failed to set trace callback, error code: " << error_code;
throw std::system_error(
ECANCELED, std::generic_category(), error_message.str());
}
}
void on_database_open(sqlite3 *db) {
set_encryption_key(db);
trace_queries(db);
}
bool file_exists(const std::string &file_path) {
std::ifstream file(file_path.c_str());
return file.good();
}
void attempt_delete_file(
const std::string &file_path,
const char *error_message) {
if (std::remove(file_path.c_str())) {
throw std::system_error(errno, std::generic_category(), error_message);
}
}
void attempt_rename_file(
const std::string &old_path,
const std::string &new_path,
const char *error_message) {
if (std::rename(old_path.c_str(), new_path.c_str())) {
throw std::system_error(errno, std::generic_category(), error_message);
}
}
bool is_database_queryable(sqlite3 *db, bool use_encryption_key) {
char *err_msg;
sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db);
// According to SQLCipher documentation running some SELECT is the only way to
// check for key validity
if (use_encryption_key) {
set_encryption_key(db);
}
sqlite3_exec(
db, "SELECT COUNT(*) FROM sqlite_master;", nullptr, nullptr, &err_msg);
sqlite3_close(db);
return !err_msg;
}
void validate_encryption() {
std::string temp_encrypted_db_path =
SQLiteQueryExecutor::sqliteFilePath + "_temp_encrypted";
bool temp_encrypted_exists = file_exists(temp_encrypted_db_path);
bool default_location_exists =
file_exists(SQLiteQueryExecutor::sqliteFilePath);
if (temp_encrypted_exists && default_location_exists) {
Logger::log(
"Previous encryption attempt failed. Repeating encryption process from "
"the beginning.");
attempt_delete_file(
temp_encrypted_db_path,
"Failed to delete corrupted encrypted database.");
} else if (temp_encrypted_exists && !default_location_exists) {
Logger::log(
"Moving temporary encrypted database to default location failed in "
"previous encryption attempt. Repeating rename step.");
attempt_rename_file(
temp_encrypted_db_path,
SQLiteQueryExecutor::sqliteFilePath,
"Failed to move encrypted database to default location.");
return;
} else if (!default_location_exists) {
Logger::log(
"Database not present yet. It will be created encrypted under default "
"path.");
return;
}
sqlite3 *db;
if (is_database_queryable(db, true)) {
Logger::log(
"Database exists under default path and it is correctly encrypted.");
return;
}
if (!is_database_queryable(db, false)) {
Logger::log(
"Database exists but it is encrypted with key that was lost. "
"Attempting database deletion. New encrypted one will be created.");
attempt_delete_file(
SQLiteQueryExecutor::sqliteFilePath.c_str(),
"Failed to delete database encrypted with lost key.");
return;
} else {
Logger::log(
"Database exists but it is not encrypted. Attempting encryption "
"process.");
}
sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db);
std::string createEncryptedCopySQL = "ATTACH DATABASE '" +
temp_encrypted_db_path +
"' AS encrypted_comm "
"KEY \"x'" +
SQLiteQueryExecutor::encryptionKey +
"'\";"
"SELECT sqlcipher_export('encrypted_comm');"
"DETACH DATABASE encrypted_comm;";
char *encryption_error;
sqlite3_exec(
db, createEncryptedCopySQL.c_str(), nullptr, nullptr, &encryption_error);
if (encryption_error) {
throw std::system_error(
ECANCELED,
std::generic_category(),
"Failed to create encrypted copy of the original database.");
}
sqlite3_close(db);
attempt_delete_file(
SQLiteQueryExecutor::sqliteFilePath,
"Failed to delete unencrypted database.");
attempt_rename_file(
temp_encrypted_db_path,
SQLiteQueryExecutor::sqliteFilePath,
"Failed to move encrypted database to default location.");
Logger::log("Encryption completed successfully.");
}
typedef bool ShouldBeInTransaction;
typedef std::function<bool(sqlite3 *)> MigrateFunction;
typedef std::pair<MigrateFunction, ShouldBeInTransaction> SQLiteMigration;
std::vector<std::pair<unsigned int, SQLiteMigration>> migrations{
{{1, {create_drafts_table, true}},
{2, {rename_threadID_to_key, true}},
{4, {create_persist_account_table, true}},
{5, {create_persist_sessions_table, true}},
{15, {create_media_table, true}},
{16, {drop_messages_table, true}},
{17, {recreate_messages_table, true}},
{18, {create_messages_idx_thread_time, true}},
{19, {create_media_idx_container, true}},
{20, {create_threads_table, true}},
{21, {update_threadID_for_pending_threads_in_drafts, true}},
{22, {enable_write_ahead_logging_mode, false}},
{23, {create_metadata_table, true}},
{24, {add_not_null_constraint_to_drafts, true}},
{25, {add_not_null_constraint_to_metadata, true}},
{26, {add_avatar_column_to_threads_table, true}},
{27, {add_pinned_count_column_to_threads, true}},
{28, {create_message_store_threads_table, true}},
{29, {create_reports_table, true}},
{30, {create_persist_storage_table, true}},
{31, {recreate_message_store_threads_table, true}},
{32, {create_users_table, true}}}};
enum class MigrationResult { SUCCESS, FAILURE, NOT_APPLIED };
MigrationResult applyMigrationWithTransaction(
sqlite3 *db,
const MigrateFunction &migrate,
int index) {
sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr);
auto db_version = get_database_version(db);
if (index <= db_version) {
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return MigrationResult::NOT_APPLIED;
}
auto rc = migrate(db);
if (!rc) {
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return MigrationResult::FAILURE;
}
auto database_version_set = set_database_version(db, index);
if (!database_version_set) {
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return MigrationResult::FAILURE;
}
sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr);
return MigrationResult::SUCCESS;
}
MigrationResult applyMigrationWithoutTransaction(
sqlite3 *db,
const MigrateFunction &migrate,
int index) {
auto db_version = get_database_version(db);
if (index <= db_version) {
return MigrationResult::NOT_APPLIED;
}
auto rc = migrate(db);
if (!rc) {
return MigrationResult::FAILURE;
}
sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr);
auto inner_db_version = get_database_version(db);
if (index <= inner_db_version) {
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return MigrationResult::NOT_APPLIED;
}
auto database_version_set = set_database_version(db, index);
if (!database_version_set) {
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return MigrationResult::FAILURE;
}
sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr);
return MigrationResult::SUCCESS;
}
bool set_up_database(sqlite3 *db) {
auto write_ahead_enabled = enable_write_ahead_logging_mode(db);
if (!write_ahead_enabled) {
return false;
}
sqlite3_exec(db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr);
auto db_version = get_database_version(db);
auto latest_version = migrations.back().first;
if (db_version == latest_version) {
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return true;
}
if (db_version != 0 || !create_schema(db) ||
!set_database_version(db, latest_version)) {
sqlite3_exec(db, "ROLLBACK;", nullptr, nullptr, nullptr);
return false;
}
sqlite3_exec(db, "END TRANSACTION;", nullptr, nullptr, nullptr);
return true;
}
void SQLiteQueryExecutor::migrate() {
validate_encryption();
sqlite3 *db;
sqlite3_open(SQLiteQueryExecutor::sqliteFilePath.c_str(), &db);
on_database_open(db);
std::stringstream db_path;
db_path << "db path: " << SQLiteQueryExecutor::sqliteFilePath.c_str()
<< std::endl;
Logger::log(db_path.str());
auto db_version = get_database_version(db);
std::stringstream version_msg;
version_msg << "db version: " << db_version << std::endl;
Logger::log(version_msg.str());
if (db_version == 0) {
auto db_created = set_up_database(db);
if (!db_created) {
sqlite3_close(db);
Logger::log("Database structure creation error.");
throw std::runtime_error("Database structure creation error");
}
Logger::log("Database structure created.");
sqlite3_close(db);
return;
}
for (const auto &[idx, migration] : migrations) {
const auto &[applyMigration, shouldBeInTransaction] = migration;
MigrationResult migrationResult;
if (shouldBeInTransaction) {
migrationResult = applyMigrationWithTransaction(db, applyMigration, idx);
} else {
migrationResult =
applyMigrationWithoutTransaction(db, applyMigration, idx);
}
if (migrationResult == MigrationResult::NOT_APPLIED) {
continue;
}
std::stringstream migration_msg;
if (migrationResult == MigrationResult::FAILURE) {
migration_msg << "migration " << idx << " failed." << std::endl;
Logger::log(migration_msg.str());
sqlite3_close(db);
throw std::runtime_error(migration_msg.str());
}
if (migrationResult == MigrationResult::SUCCESS) {
migration_msg << "migration " << idx << " succeeded." << std::endl;
Logger::log(migration_msg.str());
}
}
sqlite3_close(db);
}
auto &SQLiteQueryExecutor::getStorage() {
static auto storage = make_storage(
SQLiteQueryExecutor::sqliteFilePath,
make_index("messages_idx_thread_time", &Message::thread, &Message::time),
make_index("media_idx_container", &Media::container),
make_table(
"drafts",
make_column("key", &Draft::key, unique(), primary_key()),
make_column("text", &Draft::text)),
make_table(
"messages",
make_column("id", &Message::id, unique(), primary_key()),
make_column("local_id", &Message::local_id),
make_column("thread", &Message::thread),
make_column("user", &Message::user),
make_column("type", &Message::type),
make_column("future_type", &Message::future_type),
make_column("content", &Message::content),
make_column("time", &Message::time)),
make_table(
"olm_persist_account",
make_column("id", &OlmPersistAccount::id, unique(), primary_key()),
make_column("account_data", &OlmPersistAccount::account_data)),
make_table(
"olm_persist_sessions",
make_column(
"target_user_id",
&OlmPersistSession::target_user_id,
unique(),
primary_key()),
make_column("session_data", &OlmPersistSession::session_data)),
make_table(
"media",
make_column("id", &Media::id, unique(), primary_key()),
make_column("container", &Media::container),
make_column("thread", &Media::thread),
make_column("uri", &Media::uri),
make_column("type", &Media::type),
make_column("extras", &Media::extras)),
make_table(
"threads",
make_column("id", &Thread::id, unique(), primary_key()),
make_column("type", &Thread::type),
make_column("name", &Thread::name),
make_column("description", &Thread::description),
make_column("color", &Thread::color),
make_column("creation_time", &Thread::creation_time),
make_column("parent_thread_id", &Thread::parent_thread_id),
make_column("containing_thread_id", &Thread::containing_thread_id),
make_column("community", &Thread::community),
make_column("members", &Thread::members),
make_column("roles", &Thread::roles),
make_column("current_user", &Thread::current_user),
make_column("source_message_id", &Thread::source_message_id),
make_column("replies_count", &Thread::replies_count),
make_column("avatar", &Thread::avatar),
make_column("pinned_count", &Thread::pinned_count, default_value(0))),
make_table(
"metadata",
make_column("name", &Metadata::name, unique(), primary_key()),
make_column("data", &Metadata::data)),
make_table(
"message_store_threads",
make_column("id", &MessageStoreThread::id, unique(), primary_key()),
make_column("start_reached", &MessageStoreThread::start_reached)),
make_table(
"reports",
make_column("id", &Report::id, unique(), primary_key()),
make_column("report", &Report::report)),
make_table(
"persist_storage",
make_column("key", &PersistItem::key, unique(), primary_key()),
- make_column("item", &PersistItem::item))
+ make_column("item", &PersistItem::item)),
+ make_table(
+ "users",
+ make_column("id", &UserInfo::id, unique(), primary_key()),
+ make_column("user_info", &UserInfo::user_info))
);
storage.on_open = on_database_open;
return storage;
}
SQLiteQueryExecutor::SQLiteQueryExecutor() {
SQLiteQueryExecutor::migrate();
}
SQLiteQueryExecutor::SQLiteQueryExecutor(std::string sqliteFilePath) {
SQLiteQueryExecutor::sqliteFilePath = sqliteFilePath;
SQLiteQueryExecutor::migrate();
}
std::string SQLiteQueryExecutor::getDraft(std::string key) const {
std::unique_ptr<Draft> draft =
SQLiteQueryExecutor::getStorage().get_pointer<Draft>(key);
return (draft == nullptr) ? "" : draft->text;
}
std::unique_ptr<Thread>
SQLiteQueryExecutor::getThread(std::string threadID) const {
return SQLiteQueryExecutor::getStorage().get_pointer<Thread>(threadID);
}
void SQLiteQueryExecutor::updateDraft(std::string key, std::string text) const {
Draft draft = {key, text};
SQLiteQueryExecutor::getStorage().replace(draft);
}
bool SQLiteQueryExecutor::moveDraft(std::string oldKey, std::string newKey)
const {
std::unique_ptr<Draft> draft =
SQLiteQueryExecutor::getStorage().get_pointer<Draft>(oldKey);
if (draft == nullptr) {
return false;
}
draft->key = newKey;
SQLiteQueryExecutor::getStorage().replace(*draft);
SQLiteQueryExecutor::getStorage().remove<Draft>(oldKey);
return true;
}
std::vector<Draft> SQLiteQueryExecutor::getAllDrafts() const {
return SQLiteQueryExecutor::getStorage().get_all<Draft>();
}
void SQLiteQueryExecutor::removeAllDrafts() const {
SQLiteQueryExecutor::getStorage().remove_all<Draft>();
}
void SQLiteQueryExecutor::removeAllMessages() const {
SQLiteQueryExecutor::getStorage().remove_all<Message>();
}
std::vector<std::pair<Message, std::vector<Media>>>
SQLiteQueryExecutor::getAllMessages() const {
auto rows = SQLiteQueryExecutor::getStorage().select(
columns(
&Message::id,
&Message::local_id,
&Message::thread,
&Message::user,
&Message::type,
&Message::future_type,
&Message::content,
&Message::time,
&Media::id,
&Media::container,
&Media::thread,
&Media::uri,
&Media::type,
&Media::extras),
left_join<Media>(on(c(&Message::id) == &Media::container)),
order_by(&Message::id));
std::vector<std::pair<Message, std::vector<Media>>> allMessages;
allMessages.reserve(rows.size());
std::string prev_msg_idx{};
for (auto &row : rows) {
auto msg_id = std::get<0>(row);
if (msg_id == prev_msg_idx) {
allMessages.back().second.push_back(Media{
std::get<8>(row),
std::move(std::get<9>(row)),
std::move(std::get<10>(row)),
std::move(std::get<11>(row)),
std::move(std::get<12>(row)),
std::move(std::get<13>(row)),
});
} else {
std::vector<Media> mediaForMsg;
if (!std::get<8>(row).empty()) {
mediaForMsg.push_back(Media{
std::get<8>(row),
std::move(std::get<9>(row)),
std::move(std::get<10>(row)),
std::move(std::get<11>(row)),
std::move(std::get<12>(row)),
std::move(std::get<13>(row)),
});
}
allMessages.push_back(std::make_pair(
Message{
msg_id,
std::move(std::get<1>(row)),
std::move(std::get<2>(row)),
std::move(std::get<3>(row)),
std::get<4>(row),
std::move(std::get<5>(row)),
std::move(std::get<6>(row)),
std::get<7>(row)},
mediaForMsg));
prev_msg_idx = msg_id;
}
}
return allMessages;
}
void SQLiteQueryExecutor::removeMessages(
const std::vector<std::string> &ids) const {
SQLiteQueryExecutor::getStorage().remove_all<Message>(
where(in(&Message::id, ids)));
}
void SQLiteQueryExecutor::removeMessagesForThreads(
const std::vector<std::string> &threadIDs) const {
SQLiteQueryExecutor::getStorage().remove_all<Message>(
where(in(&Message::thread, threadIDs)));
}
void SQLiteQueryExecutor::replaceMessage(const Message &message) const {
SQLiteQueryExecutor::getStorage().replace(message);
}
void SQLiteQueryExecutor::rekeyMessage(std::string from, std::string to) const {
auto msg = SQLiteQueryExecutor::getStorage().get<Message>(from);
msg.id = to;
SQLiteQueryExecutor::getStorage().replace(msg);
SQLiteQueryExecutor::getStorage().remove<Message>(from);
}
void SQLiteQueryExecutor::removeAllMedia() const {
SQLiteQueryExecutor::getStorage().remove_all<Media>();
}
void SQLiteQueryExecutor::removeMediaForMessages(
const std::vector<std::string> &msg_ids) const {
SQLiteQueryExecutor::getStorage().remove_all<Media>(
where(in(&Media::container, msg_ids)));
}
void SQLiteQueryExecutor::removeMediaForMessage(std::string msg_id) const {
SQLiteQueryExecutor::getStorage().remove_all<Media>(
where(c(&Media::container) == msg_id));
}
void SQLiteQueryExecutor::removeMediaForThreads(
const std::vector<std::string> &thread_ids) const {
SQLiteQueryExecutor::getStorage().remove_all<Media>(
where(in(&Media::thread, thread_ids)));
}
void SQLiteQueryExecutor::replaceMedia(const Media &media) const {
SQLiteQueryExecutor::getStorage().replace(media);
}
void SQLiteQueryExecutor::rekeyMediaContainers(std::string from, std::string to)
const {
SQLiteQueryExecutor::getStorage().update_all(
set(c(&Media::container) = to), where(c(&Media::container) == from));
}
void SQLiteQueryExecutor::replaceMessageStoreThreads(
const std::vector<MessageStoreThread> &threads) const {
for (auto &thread : threads) {
SQLiteQueryExecutor::getStorage().replace(thread);
}
}
void SQLiteQueryExecutor::removeAllMessageStoreThreads() const {
SQLiteQueryExecutor::getStorage().remove_all<MessageStoreThread>();
}
void SQLiteQueryExecutor::removeMessageStoreThreads(
const std::vector<std::string> &ids) const {
SQLiteQueryExecutor::getStorage().remove_all<MessageStoreThread>(
where(in(&MessageStoreThread::id, ids)));
}
std::vector<MessageStoreThread>
SQLiteQueryExecutor::getAllMessageStoreThreads() const {
return SQLiteQueryExecutor::getStorage().get_all<MessageStoreThread>();
}
std::vector<Thread> SQLiteQueryExecutor::getAllThreads() const {
return SQLiteQueryExecutor::getStorage().get_all<Thread>();
};
void SQLiteQueryExecutor::removeThreads(std::vector<std::string> ids) const {
SQLiteQueryExecutor::getStorage().remove_all<Thread>(
where(in(&Thread::id, ids)));
};
void SQLiteQueryExecutor::replaceThread(const Thread &thread) const {
SQLiteQueryExecutor::getStorage().replace(thread);
};
void SQLiteQueryExecutor::removeAllThreads() const {
SQLiteQueryExecutor::getStorage().remove_all<Thread>();
};
void SQLiteQueryExecutor::replaceReport(const Report &report) const {
SQLiteQueryExecutor::getStorage().replace(report);
}
void SQLiteQueryExecutor::removeAllReports() const {
SQLiteQueryExecutor::getStorage().remove_all<Report>();
}
void SQLiteQueryExecutor::removeReports(
const std::vector<std::string> &ids) const {
SQLiteQueryExecutor::getStorage().remove_all<Report>(
where(in(&Report::id, ids)));
}
std::vector<Report> SQLiteQueryExecutor::getAllReports() const {
return SQLiteQueryExecutor::getStorage().get_all<Report>();
}
void SQLiteQueryExecutor::setPersistStorageItem(
std::string key,
std::string item) const {
PersistItem entry{
key,
item,
};
SQLiteQueryExecutor::getStorage().replace(entry);
}
void SQLiteQueryExecutor::removePersistStorageItem(std::string key) const {
SQLiteQueryExecutor::getStorage().remove<PersistItem>(key);
}
std::string SQLiteQueryExecutor::getPersistStorageItem(std::string key) const {
std::unique_ptr<PersistItem> entry =
SQLiteQueryExecutor::getStorage().get_pointer<PersistItem>(key);
return (entry == nullptr) ? "" : entry->item;
}
+void SQLiteQueryExecutor::replaceUser(const UserInfo &user_info) const {
+ SQLiteQueryExecutor::getStorage().replace(user_info);
+}
+
+void SQLiteQueryExecutor::removeAllUsers() const {
+ SQLiteQueryExecutor::getStorage().remove_all<UserInfo>();
+}
+
+void SQLiteQueryExecutor::removeUsers(
+ const std::vector<std::string> &ids) const {
+ SQLiteQueryExecutor::getStorage().remove_all<UserInfo>(
+ where(in(&UserInfo::id, ids)));
+}
+
+std::vector<UserInfo> SQLiteQueryExecutor::getAllUsers() const {
+ return SQLiteQueryExecutor::getStorage().get_all<UserInfo>();
+}
+
void SQLiteQueryExecutor::beginTransaction() const {
SQLiteQueryExecutor::getStorage().begin_transaction();
}
void SQLiteQueryExecutor::commitTransaction() const {
SQLiteQueryExecutor::getStorage().commit();
}
void SQLiteQueryExecutor::rollbackTransaction() const {
SQLiteQueryExecutor::getStorage().rollback();
}
std::vector<OlmPersistSession>
SQLiteQueryExecutor::getOlmPersistSessionsData() const {
return SQLiteQueryExecutor::getStorage().get_all<OlmPersistSession>();
}
std::optional<std::string>
SQLiteQueryExecutor::getOlmPersistAccountData() const {
std::vector<OlmPersistAccount> result =
SQLiteQueryExecutor::getStorage().get_all<OlmPersistAccount>();
if (result.size() > 1) {
throw std::system_error(
ECANCELED,
std::generic_category(),
"Multiple records found for the olm_persist_account table");
}
return (result.size() == 0)
? std::nullopt
: std::optional<std::string>(result[0].account_data);
}
void SQLiteQueryExecutor::storeOlmPersistData(crypto::Persist persist) const {
OlmPersistAccount persistAccount = {
ACCOUNT_ID, std::string(persist.account.begin(), persist.account.end())};
SQLiteQueryExecutor::getStorage().replace(persistAccount);
for (auto it = persist.sessions.begin(); it != persist.sessions.end(); it++) {
OlmPersistSession persistSession = {
it->first, std::string(it->second.begin(), it->second.end())};
SQLiteQueryExecutor::getStorage().replace(persistSession);
}
}
void SQLiteQueryExecutor::setNotifyToken(std::string token) const {
this->setMetadata("notify_token", token);
}
void SQLiteQueryExecutor::clearNotifyToken() const {
this->clearMetadata("notify_token");
}
void SQLiteQueryExecutor::setCurrentUserID(std::string userID) const {
this->setMetadata("current_user_id", userID);
}
std::string SQLiteQueryExecutor::getCurrentUserID() const {
return this->getMetadata("current_user_id");
}
void SQLiteQueryExecutor::setMetadata(std::string entry_name, std::string data)
const {
Metadata entry{
entry_name,
data,
};
SQLiteQueryExecutor::getStorage().replace(entry);
}
void SQLiteQueryExecutor::clearMetadata(std::string entry_name) const {
SQLiteQueryExecutor::getStorage().remove<Metadata>(entry_name);
}
std::string SQLiteQueryExecutor::getMetadata(std::string entry_name) const {
std::unique_ptr<Metadata> entry =
SQLiteQueryExecutor::getStorage().get_pointer<Metadata>(entry_name);
return (entry == nullptr) ? "" : entry->data;
}
#ifndef EMSCRIPTEN
void SQLiteQueryExecutor::clearSensitiveData() {
if (file_exists(SQLiteQueryExecutor::sqliteFilePath) &&
std::remove(SQLiteQueryExecutor::sqliteFilePath.c_str())) {
std::ostringstream errorStream;
errorStream << "Failed to delete database file. Details: "
<< strerror(errno);
throw std::system_error(errno, std::generic_category(), errorStream.str());
}
SQLiteQueryExecutor::assign_encryption_key();
SQLiteQueryExecutor::migrate();
}
void SQLiteQueryExecutor::initialize(std::string &databasePath) {
std::call_once(SQLiteQueryExecutor::initialized, [&databasePath]() {
SQLiteQueryExecutor::sqliteFilePath = databasePath;
CommSecureStore commSecureStore{};
folly::Optional<std::string> maybeEncryptionKey =
commSecureStore.get(SQLiteQueryExecutor::secureStoreEncryptionKeyID);
if (file_exists(databasePath) && maybeEncryptionKey) {
SQLiteQueryExecutor::encryptionKey = maybeEncryptionKey.value();
return;
}
SQLiteQueryExecutor::assign_encryption_key();
});
}
void SQLiteQueryExecutor::assign_encryption_key() {
CommSecureStore commSecureStore{};
std::string encryptionKey = comm::crypto::Tools::generateRandomHexString(
SQLiteQueryExecutor::sqlcipherEncryptionKeySize);
commSecureStore.set(
SQLiteQueryExecutor::secureStoreEncryptionKeyID, encryptionKey);
SQLiteQueryExecutor::encryptionKey = encryptionKey;
}
#endif
} // namespace comm
diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
index eb31d67fa..e01db2d1b 100644
--- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
+++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
@@ -1,89 +1,94 @@
#pragma once
#include "../CryptoTools/Persist.h"
#include "DatabaseQueryExecutor.h"
#include "entities/Draft.h"
+#include "entities/UserInfo.h"
#include <mutex>
#include <string>
namespace comm {
class SQLiteQueryExecutor : public DatabaseQueryExecutor {
static void migrate();
static auto &getStorage();
static std::once_flag initialized;
static int sqlcipherEncryptionKeySize;
static std::string secureStoreEncryptionKeyID;
#ifndef EMSCRIPTEN
static void assign_encryption_key();
#endif
public:
static std::string sqliteFilePath;
static std::string encryptionKey;
SQLiteQueryExecutor();
SQLiteQueryExecutor(std::string sqliteFilePath);
std::unique_ptr<Thread> getThread(std::string threadID) const override;
std::string getDraft(std::string key) const override;
void updateDraft(std::string key, std::string text) const override;
bool moveDraft(std::string oldKey, std::string newKey) const override;
std::vector<Draft> getAllDrafts() const override;
void removeAllDrafts() const override;
void removeAllMessages() const override;
std::vector<std::pair<Message, std::vector<Media>>>
getAllMessages() const override;
void removeMessages(const std::vector<std::string> &ids) const override;
void removeMessagesForThreads(
const std::vector<std::string> &threadIDs) const override;
void replaceMessage(const Message &message) const override;
void rekeyMessage(std::string from, std::string to) const override;
void replaceMessageStoreThreads(
const std::vector<MessageStoreThread> &threads) const override;
void
removeMessageStoreThreads(const std::vector<std::string> &ids) const override;
void removeAllMessageStoreThreads() const override;
std::vector<MessageStoreThread> getAllMessageStoreThreads() const override;
void removeAllMedia() const override;
void removeMediaForMessages(
const std::vector<std::string> &msg_ids) const override;
void removeMediaForMessage(std::string msg_id) const override;
void removeMediaForThreads(
const std::vector<std::string> &thread_ids) const override;
void replaceMedia(const Media &media) const override;
void rekeyMediaContainers(std::string from, std::string to) const override;
std::vector<Thread> getAllThreads() const override;
void removeThreads(std::vector<std::string> ids) const override;
void replaceThread(const Thread &thread) const override;
void removeAllThreads() const override;
void replaceReport(const Report &report) const override;
void removeReports(const std::vector<std::string> &ids) const override;
void removeAllReports() const override;
std::vector<Report> getAllReports() const override;
void setPersistStorageItem(std::string key, std::string item) const override;
void removePersistStorageItem(std::string key) const override;
std::string getPersistStorageItem(std::string key) const override;
+ void replaceUser(const UserInfo &user_info) const override;
+ void removeUsers(const std::vector<std::string> &ids) const override;
+ void removeAllUsers() const override;
+ std::vector<UserInfo> getAllUsers() const override;
void beginTransaction() const override;
void commitTransaction() const override;
void rollbackTransaction() const override;
std::vector<OlmPersistSession> getOlmPersistSessionsData() const override;
std::optional<std::string> getOlmPersistAccountData() const override;
void storeOlmPersistData(crypto::Persist persist) const override;
void setNotifyToken(std::string token) const override;
void clearNotifyToken() const override;
void setCurrentUserID(std::string userID) const override;
std::string getCurrentUserID() const override;
void setMetadata(std::string entry_name, std::string data) const override;
void clearMetadata(std::string entry_name) const override;
std::string getMetadata(std::string entry_name) const override;
#ifndef EMSCRIPTEN
static void clearSensitiveData();
static void initialize(std::string &databasePath);
#endif
};
} // namespace comm
diff --git a/native/cpp/CommonCpp/DatabaseManagers/entities/UserInfo.h b/native/cpp/CommonCpp/DatabaseManagers/entities/UserInfo.h
new file mode 100644
index 000000000..668c7a3ff
--- /dev/null
+++ b/native/cpp/CommonCpp/DatabaseManagers/entities/UserInfo.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <string>
+
+namespace comm {
+
+struct UserInfo {
+ std::string id;
+ std::string user_info;
+};
+
+} // namespace comm
diff --git a/web/cpp/SQLiteQueryExecutorBindings.cpp b/web/cpp/SQLiteQueryExecutorBindings.cpp
index 1f78d4320..db60e9747 100644
--- a/web/cpp/SQLiteQueryExecutorBindings.cpp
+++ b/web/cpp/SQLiteQueryExecutorBindings.cpp
@@ -1,76 +1,83 @@
#include "SQLiteQueryExecutor.cpp"
#include <emscripten/bind.h>
#include <vector>
namespace comm {
using namespace emscripten;
EMSCRIPTEN_BINDINGS(SQLiteQueryExecutor) {
value_object<Draft>("Draft")
.field("key", &Draft::key)
.field("text", &Draft::text);
value_object<Report>("Report")
.field("id", &Report::id)
.field("report", &Report::report);
value_object<PersistItem>("PersistItem")
.field("key", &PersistItem::key)
.field("item", &PersistItem::item);
+ value_object<UserInfo>("UserInfo")
+ .field("id", &UserInfo::id)
+ .field("userInfo", &UserInfo::user_info);
class_<SQLiteQueryExecutor>("SQLiteQueryExecutor")
.constructor<std::string>()
.function("updateDraft", &SQLiteQueryExecutor::updateDraft)
.function("moveDraft", &SQLiteQueryExecutor::moveDraft)
.function("getAllDrafts", &SQLiteQueryExecutor::getAllDrafts)
.function("removeAllDrafts", &SQLiteQueryExecutor::removeAllDrafts)
.function("setMetadata", &SQLiteQueryExecutor::setMetadata)
.function("clearMetadata", &SQLiteQueryExecutor::clearMetadata)
.function("getMetadata", &SQLiteQueryExecutor::getMetadata)
.function("replaceReport", &SQLiteQueryExecutor::replaceReport)
.function("removeReports", &SQLiteQueryExecutor::removeReports)
.function("removeAllReports", &SQLiteQueryExecutor::removeAllReports)
.function("getAllReports", &SQLiteQueryExecutor::getAllReports)
.function(
"setPersistStorageItem", &SQLiteQueryExecutor::setPersistStorageItem)
.function(
"removePersistStorageItem",
&SQLiteQueryExecutor::removePersistStorageItem)
.function(
- "getPersistStorageItem", &SQLiteQueryExecutor::getPersistStorageItem);
+ "getPersistStorageItem", &SQLiteQueryExecutor::getPersistStorageItem)
+ .function("replaceUser", &SQLiteQueryExecutor::replaceUser)
+ .function("removeUsers", &SQLiteQueryExecutor::removeUsers)
+ .function("removeAllUsers", &SQLiteQueryExecutor::removeAllUsers)
+ .function("getAllUsers", &SQLiteQueryExecutor::getAllUsers);
}
} // namespace comm
namespace emscripten {
namespace internal {
template <typename T, typename Allocator>
struct BindingType<std::vector<T, Allocator>> {
using ValBinding = BindingType<val>;
using WireType = ValBinding::WireType;
static WireType toWireType(const std::vector<T, Allocator> &vec) {
std::vector<val> valVec(vec.begin(), vec.end());
return BindingType<val>::toWireType(val::array(valVec));
}
static std::vector<T, Allocator> fromWireType(WireType value) {
return vecFromJSArray<T>(ValBinding::fromWireType(value));
}
};
template <typename T>
struct TypeID<
T,
typename std::enable_if_t<std::is_same<
typename Canonicalized<T>::type,
std::vector<
typename Canonicalized<T>::type::value_type,
typename Canonicalized<T>::type::allocator_type>>::value>> {
static constexpr TYPEID get() {
return TypeID<val>::get();
}
};
} // namespace internal
} // namespace emscripten
diff --git a/web/database/_generated/comm-query-executor.js b/web/database/_generated/comm-query-executor.js
index 79a9f653d..1a986e377 100644
--- a/web/database/_generated/comm-query-executor.js
+++ b/web/database/_generated/comm-query-executor.js
@@ -1,156 +1,156 @@
// @generated
var Module = (() => {
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
return (
function(Module) {
Module = Module || {};
var e;e||(e=typeof Module !== 'undefined' ? Module : {});var aa,ba;e.ready=new Promise(function(a,b){aa=a;ba=b});var ca=Object.assign({},e),da="./this.program",ea="object"==typeof window,fa="function"==typeof importScripts,ha="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,m="",ia,ja,ka,fs,la,ma;
if(ha)m=fa?require("path").dirname(m)+"/":__dirname+"/",ma=()=>{la||(fs=require("fs"),la=require("path"))},ia=function(a,b){ma();a=la.normalize(a);return fs.readFileSync(a,b?void 0:"utf8")},ka=a=>{a=ia(a,!0);a.buffer||(a=new Uint8Array(a));return a},ja=(a,b,c)=>{ma();a=la.normalize(a);fs.readFile(a,function(d,f){d?c(d):b(f.buffer)})},1<process.argv.length&&(da=process.argv[1].replace(/\\/g,"/")),process.argv.slice(2),e.inspect=function(){return"[Emscripten Module object]"};else if(ea||fa)fa?m=self.location.href:
"undefined"!=typeof document&&document.currentScript&&(m=document.currentScript.src),_scriptDir&&(m=_scriptDir),0!==m.indexOf("blob:")?m=m.substr(0,m.replace(/[?#].*/,"").lastIndexOf("/")+1):m="",ia=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.send(null);return b.responseText},fa&&(ka=a=>{var b=new XMLHttpRequest;b.open("GET",a,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),ja=(a,b,c)=>{var d=new XMLHttpRequest;d.open("GET",a,!0);d.responseType="arraybuffer";
d.onload=()=>{200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};var na=e.print||console.log.bind(console),u=e.printErr||console.warn.bind(console);Object.assign(e,ca);ca=null;e.thisProgram&&(da=e.thisProgram);var oa;e.wasmBinary&&(oa=e.wasmBinary);var noExitRuntime=e.noExitRuntime||!0;"object"!=typeof WebAssembly&&x("no native wasm support detected");var pa,qa=!1,ra="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;
function sa(a,b,c){var d=b+c;for(c=b;a[c]&&!(c>=d);)++c;if(16<c-b&&a.buffer&&ra)return ra.decode(a.subarray(b,c));for(d="";b<c;){var f=a[b++];if(f&128){var g=a[b++]&63;if(192==(f&224))d+=String.fromCharCode((f&31)<<6|g);else{var k=a[b++]&63;f=224==(f&240)?(f&15)<<12|g<<6|k:(f&7)<<18|g<<12|k<<6|a[b++]&63;65536>f?d+=String.fromCharCode(f):(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023))}}else d+=String.fromCharCode(f)}return d}function y(a,b){return a?sa(z,a,b):""}
function ta(a,b,c,d){if(!(0<d))return 0;var f=c;d=c+d-1;for(var g=0;g<a.length;++g){var k=a.charCodeAt(g);if(55296<=k&&57343>=k){var h=a.charCodeAt(++g);k=65536+((k&1023)<<10)|h&1023}if(127>=k){if(c>=d)break;b[c++]=k}else{if(2047>=k){if(c+1>=d)break;b[c++]=192|k>>6}else{if(65535>=k){if(c+2>=d)break;b[c++]=224|k>>12}else{if(c+3>=d)break;b[c++]=240|k>>18;b[c++]=128|k>>12&63}b[c++]=128|k>>6&63}b[c++]=128|k&63}}b[c]=0;return c-f}
function ua(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=d?(b+=4,++c):b+=3}return b}var va,A,z,C,wa,D,E,xa,ya;function za(){var a=pa.buffer;va=a;e.HEAP8=A=new Int8Array(a);e.HEAP16=C=new Int16Array(a);e.HEAP32=D=new Int32Array(a);e.HEAPU8=z=new Uint8Array(a);e.HEAPU16=wa=new Uint16Array(a);e.HEAPU32=E=new Uint32Array(a);e.HEAPF32=xa=new Float32Array(a);e.HEAPF64=ya=new Float64Array(a)}var Aa,Ba=[],Ca=[],Da=[];
function Ea(){var a=e.preRun.shift();Ba.unshift(a)}var Fa=0,Ha=null,Ia=null;function Ja(){Fa++;e.monitorRunDependencies&&e.monitorRunDependencies(Fa)}function Ka(){Fa--;e.monitorRunDependencies&&e.monitorRunDependencies(Fa);if(0==Fa&&(null!==Ha&&(clearInterval(Ha),Ha=null),Ia)){var a=Ia;Ia=null;a()}}function x(a){if(e.onAbort)e.onAbort(a);a="Aborted("+a+")";u(a);qa=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ba(a);throw a;}var La;La="comm-query-executor.wasm";
if(!La.startsWith("data:application/octet-stream;base64,")){var Ma=La;La=e.locateFile?e.locateFile(Ma,m):m+Ma}
function Na(a){var b=La;try{a:{try{if(b==La&&oa){var c=new Uint8Array(oa);break a}if(ka){c=ka(b);break a}throw"sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS)";}catch(g){x(g)}c=void 0}var d=new WebAssembly.Module(c);var f=new WebAssembly.Instance(d,a)}catch(g){throw a=g.toString(),u("failed to compile wasm module: "+a),(a.includes("imported Memory")||a.includes("memory import"))&&u("Memory size incompatibility issues may be due to changing INITIAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set INITIAL_MEMORY at runtime to something smaller than it was at compile time)."),
g;}return[f,d]}var F,G;function Oa(a){for(;0<a.length;)a.shift()(e)}function Pa(a){this.La=a-24;this.Tc=function(b){E[this.La+4>>2]=b};this.Qc=function(b){E[this.La+8>>2]=b};this.Rc=function(){D[this.La>>2]=0};this.Ub=function(){A[this.La+12>>0]=0};this.Sc=function(){A[this.La+13>>0]=0};this.wb=function(b,c){this.Wa();this.Tc(b);this.Qc(c);this.Rc();this.Ub();this.Sc()};this.Wa=function(){E[this.La+16>>2]=0}}
var Qa=0,Ra=(a,b)=>{for(var c=0,d=a.length-1;0<=d;d--){var f=a[d];"."===f?a.splice(d,1):".."===f?(a.splice(d,1),c++):c&&(a.splice(d,1),c--)}if(b)for(;c;c--)a.unshift("..");return a},J=a=>{var b="/"===a.charAt(0),c="/"===a.substr(-1);(a=Ra(a.split("/").filter(d=>!!d),!b).join("/"))||b||(a=".");a&&c&&(a+="/");return(b?"/":"")+a},Sa=a=>{var b=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/.exec(a).slice(1);a=b[0];b=b[1];if(!a&&!b)return".";b&&(b=b.substr(0,b.length-1));return a+b},L=
a=>{if("/"===a)return"/";a=J(a);a=a.replace(/\/$/,"");var b=a.lastIndexOf("/");return-1===b?a:a.substr(b+1)},Ta=(a,b)=>J(a+"/"+b);function Ua(){if("object"==typeof crypto&&"function"==typeof crypto.getRandomValues){var a=new Uint8Array(1);return()=>{crypto.getRandomValues(a);return a[0]}}if(ha)try{var b=require("crypto");return()=>b.randomBytes(1)[0]}catch(c){}return()=>x("randomDevice")}
function Va(){for(var a="",b=!1,c=arguments.length-1;-1<=c&&!b;c--){b=0<=c?arguments[c]:M.cwd();if("string"!=typeof b)throw new TypeError("Arguments to path.resolve must be strings");if(!b)return"";a=b+"/"+a;b="/"===b.charAt(0)}a=Ra(a.split("/").filter(d=>!!d),!b).join("/");return(b?"/":"")+a||"."}
var Wa=(a,b)=>{function c(k){for(var h=0;h<k.length&&""===k[h];h++);for(var n=k.length-1;0<=n&&""===k[n];n--);return h>n?[]:k.slice(h,n-h+1)}a=Va(a).substr(1);b=Va(b).substr(1);a=c(a.split("/"));b=c(b.split("/"));for(var d=Math.min(a.length,b.length),f=d,g=0;g<d;g++)if(a[g]!==b[g]){f=g;break}d=[];for(g=f;g<a.length;g++)d.push("..");d=d.concat(b.slice(f));return d.join("/")};function Xa(a,b){var c=Array(ua(a)+1);a=ta(a,c,0,c.length);b&&(c.length=a);return c}var Ya=[];
function Za(a,b){Ya[a]={input:[],output:[],Ab:b};M.pc(a,$a)}
var $a={open:function(a){var b=Ya[a.node.rdev];if(!b)throw new M.Ga(43);a.tty=b;a.seekable=!1},close:function(a){a.tty.Ab.flush(a.tty)},flush:function(a){a.tty.Ab.flush(a.tty)},read:function(a,b,c,d){if(!a.tty||!a.tty.Ab.Ic)throw new M.Ga(60);for(var f=0,g=0;g<d;g++){try{var k=a.tty.Ab.Ic(a.tty)}catch(h){throw new M.Ga(29);}if(void 0===k&&0===f)throw new M.Ga(6);if(null===k||void 0===k)break;f++;b[c+g]=k}f&&(a.node.timestamp=Date.now());return f},write:function(a,b,c,d){if(!a.tty||!a.tty.Ab.kc)throw new M.Ga(60);
try{for(var f=0;f<d;f++)a.tty.Ab.kc(a.tty,b[c+f])}catch(g){throw new M.Ga(29);}d&&(a.node.timestamp=Date.now());return f}},ab={Ic:function(a){if(!a.input.length){var b=null;if(ha){var c=Buffer.alloc(256),d=0;try{d=fs.readSync(process.stdin.fd,c,0,256,-1)}catch(f){if(f.toString().includes("EOF"))d=0;else throw f;}0<d?b=c.slice(0,d).toString("utf-8"):b=null}else"undefined"!=typeof window&&"function"==typeof window.prompt?(b=window.prompt("Input: "),null!==b&&(b+="\n")):"function"==typeof readline&&
(b=readline(),null!==b&&(b+="\n"));if(!b)return null;a.input=Xa(b,!0)}return a.input.shift()},kc:function(a,b){null===b||10===b?(na(sa(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},flush:function(a){a.output&&0<a.output.length&&(na(sa(a.output,0)),a.output=[])}},bb={kc:function(a,b){null===b||10===b?(u(sa(a.output,0)),a.output=[]):0!=b&&a.output.push(b)},flush:function(a){a.output&&0<a.output.length&&(u(sa(a.output,0)),a.output=[])}};
function cb(a){a=65536*Math.ceil(a/65536);var b=db(65536,a);if(!b)return 0;z.fill(0,b,b+a);return b}
var N={fb:null,Qa:function(){return N.createNode(null,"/",16895,0)},createNode:function(a,b,c,d){if(M.vd(c)||M.isFIFO(c))throw new M.Ga(63);N.fb||(N.fb={dir:{node:{bb:N.Ia.bb,Ta:N.Ia.Ta,lookup:N.Ia.lookup,ib:N.Ia.ib,rename:N.Ia.rename,unlink:N.Ia.unlink,rmdir:N.Ia.rmdir,readdir:N.Ia.readdir,symlink:N.Ia.symlink},stream:{cb:N.Ka.cb}},file:{node:{bb:N.Ia.bb,Ta:N.Ia.Ta},stream:{cb:N.Ka.cb,read:N.Ka.read,write:N.Ka.write,Cb:N.Ka.Cb,qb:N.Ka.qb,zb:N.Ka.zb}},link:{node:{bb:N.Ia.bb,Ta:N.Ia.Ta,readlink:N.Ia.readlink},
stream:{}},vc:{node:{bb:N.Ia.bb,Ta:N.Ia.Ta},stream:M.$c}});c=M.createNode(a,b,c,d);M.Sa(c.mode)?(c.Ia=N.fb.dir.node,c.Ka=N.fb.dir.stream,c.Ja={}):M.isFile(c.mode)?(c.Ia=N.fb.file.node,c.Ka=N.fb.file.stream,c.Pa=0,c.Ja=null):M.yb(c.mode)?(c.Ia=N.fb.link.node,c.Ka=N.fb.link.stream):M.Lb(c.mode)&&(c.Ia=N.fb.vc.node,c.Ka=N.fb.vc.stream);c.timestamp=Date.now();a&&(a.Ja[b]=c,a.timestamp=c.timestamp);return c},Xd:function(a){return a.Ja?a.Ja.subarray?a.Ja.subarray(0,a.Pa):new Uint8Array(a.Ja):new Uint8Array(0)},
Ec:function(a,b){var c=a.Ja?a.Ja.length:0;c>=b||(b=Math.max(b,c*(1048576>c?2:1.125)>>>0),0!=c&&(b=Math.max(b,256)),c=a.Ja,a.Ja=new Uint8Array(b),0<a.Pa&&a.Ja.set(c.subarray(0,a.Pa),0))},Jd:function(a,b){if(a.Pa!=b)if(0==b)a.Ja=null,a.Pa=0;else{var c=a.Ja;a.Ja=new Uint8Array(b);c&&a.Ja.set(c.subarray(0,Math.min(b,a.Pa)));a.Pa=b}},Ia:{bb:function(a){var b={};b.dev=M.Lb(a.mode)?a.id:1;b.ino=a.id;b.mode=a.mode;b.nlink=1;b.uid=0;b.gid=0;b.rdev=a.rdev;M.Sa(a.mode)?b.size=4096:M.isFile(a.mode)?b.size=a.Pa:
M.yb(a.mode)?b.size=a.link.length:b.size=0;b.atime=new Date(a.timestamp);b.mtime=new Date(a.timestamp);b.ctime=new Date(a.timestamp);b.Xc=4096;b.blocks=Math.ceil(b.size/b.Xc);return b},Ta:function(a,b){void 0!==b.mode&&(a.mode=b.mode);void 0!==b.timestamp&&(a.timestamp=b.timestamp);void 0!==b.size&&N.Jd(a,b.size)},lookup:function(){throw M.Zb[44];},ib:function(a,b,c,d){return N.createNode(a,b,c,d)},rename:function(a,b,c){if(M.Sa(a.mode)){try{var d=M.hb(b,c)}catch(g){}if(d)for(var f in d.Ja)throw new M.Ga(55);
}delete a.parent.Ja[a.name];a.parent.timestamp=Date.now();a.name=c;b.Ja[c]=a;b.timestamp=a.parent.timestamp;a.parent=b},unlink:function(a,b){delete a.Ja[b];a.timestamp=Date.now()},rmdir:function(a,b){var c=M.hb(a,b),d;for(d in c.Ja)throw new M.Ga(55);delete a.Ja[b];a.timestamp=Date.now()},readdir:function(a){var b=[".",".."],c;for(c in a.Ja)a.Ja.hasOwnProperty(c)&&b.push(c);return b},symlink:function(a,b,c){a=N.createNode(a,b,41471,0);a.link=c;return a},readlink:function(a){if(!M.yb(a.mode))throw new M.Ga(28);
return a.link}},Ka:{read:function(a,b,c,d,f){var g=a.node.Ja;if(f>=a.node.Pa)return 0;a=Math.min(a.node.Pa-f,d);if(8<a&&g.subarray)b.set(g.subarray(f,f+a),c);else for(d=0;d<a;d++)b[c+d]=g[f+d];return a},write:function(a,b,c,d,f,g){b.buffer===A.buffer&&(g=!1);if(!d)return 0;a=a.node;a.timestamp=Date.now();if(b.subarray&&(!a.Ja||a.Ja.subarray)){if(g)return a.Ja=b.subarray(c,c+d),a.Pa=d;if(0===a.Pa&&0===f)return a.Ja=b.slice(c,c+d),a.Pa=d;if(f+d<=a.Pa)return a.Ja.set(b.subarray(c,c+d),f),d}N.Ec(a,f+
d);if(a.Ja.subarray&&b.subarray)a.Ja.set(b.subarray(c,c+d),f);else for(g=0;g<d;g++)a.Ja[f+g]=b[c+g];a.Pa=Math.max(a.Pa,f+d);return d},cb:function(a,b,c){1===c?b+=a.position:2===c&&M.isFile(a.node.mode)&&(b+=a.node.Pa);if(0>b)throw new M.Ga(28);return b},Cb:function(a,b,c){N.Ec(a.node,b+c);a.node.Pa=Math.max(a.node.Pa,b+c)},qb:function(a,b,c,d,f){if(!M.isFile(a.node.mode))throw new M.Ga(43);a=a.node.Ja;if(f&2||a.buffer!==va){if(0<c||c+b<a.length)a.subarray?a=a.subarray(c,c+b):a=Array.prototype.slice.call(a,
c,c+b);c=!0;b=cb(b);if(!b)throw new M.Ga(48);A.set(a,b)}else c=!1,b=a.byteOffset;return{La:b,tc:c}},zb:function(a,b,c,d,f){if(!M.isFile(a.node.mode))throw new M.Ga(43);if(f&2)return 0;N.Ka.write(a,b,0,d,c,!1);return 0}}};function eb(a,b,c){var d="al "+a;ja(a,f=>{f||x('Loading data file "'+a+'" failed (no arrayBuffer).');b(new Uint8Array(f));d&&Ka(d)},()=>{if(c)c();else throw'Loading data file "'+a+'" failed.';});d&&Ja(d)}
var M={root:null,Fb:[],Cc:{},streams:[],Bd:1,eb:null,Ac:"/",cc:!1,Lc:!0,Ga:null,Zb:{},md:null,Rb:0,Oa:(a,b={})=>{a=Va(M.cwd(),a);if(!a)return{path:"",node:null};b=Object.assign({Xb:!0,nc:0},b);if(8<b.nc)throw new M.Ga(32);a=Ra(a.split("/").filter(k=>!!k),!1);for(var c=M.root,d="/",f=0;f<a.length;f++){var g=f===a.length-1;if(g&&b.parent)break;c=M.hb(c,a[f]);d=J(d+"/"+a[f]);M.ob(c)&&(!g||g&&b.Xb)&&(c=c.Eb.root);if(!g||b.Xa)for(g=0;M.yb(c.mode);)if(c=M.readlink(d),d=Va(Sa(d),c),c=M.Oa(d,{nc:b.nc+1}).node,
40<g++)throw new M.Ga(32);}return{path:d,node:c}},kb:a=>{for(var b;;){if(M.Nb(a))return a=a.Qa.Mc,b?"/"!==a[a.length-1]?a+"/"+b:a+b:a;b=b?a.name+"/"+b:a.name;a=a.parent}},bc:(a,b)=>{for(var c=0,d=0;d<b.length;d++)c=(c<<5)-c+b.charCodeAt(d)|0;return(a+c>>>0)%M.eb.length},Jc:a=>{var b=M.bc(a.parent.id,a.name);a.rb=M.eb[b];M.eb[b]=a},Kc:a=>{var b=M.bc(a.parent.id,a.name);if(M.eb[b]===a)M.eb[b]=a.rb;else for(b=M.eb[b];b;){if(b.rb===a){b.rb=a.rb;break}b=b.rb}},hb:(a,b)=>{var c=M.yd(a);if(c)throw new M.Ga(c,
a);for(c=M.eb[M.bc(a.id,b)];c;c=c.rb){var d=c.name;if(c.parent.id===a.id&&d===b)return c}return M.lookup(a,b)},createNode:(a,b,c,d)=>{a=new M.Pc(a,b,c,d);M.Jc(a);return a},Wb:a=>{M.Kc(a)},Nb:a=>a===a.parent,ob:a=>!!a.Eb,isFile:a=>32768===(a&61440),Sa:a=>16384===(a&61440),yb:a=>40960===(a&61440),Lb:a=>8192===(a&61440),vd:a=>24576===(a&61440),isFIFO:a=>4096===(a&61440),isSocket:a=>49152===(a&49152),nd:{r:0,"r+":2,w:577,"w+":578,a:1089,"a+":1090},Ad:a=>{var b=M.nd[a];if("undefined"==typeof b)throw Error("Unknown file open mode: "+
a);return b},Gc:a=>{var b=["r","w","rw"][a&3];a&512&&(b+="w");return b},mb:(a,b)=>{if(M.Lc)return 0;if(!b.includes("r")||a.mode&292){if(b.includes("w")&&!(a.mode&146)||b.includes("x")&&!(a.mode&73))return 2}else return 2;return 0},yd:a=>{var b=M.mb(a,"x");return b?b:a.Ia.lookup?0:2},jc:(a,b)=>{try{return M.hb(a,b),20}catch(c){}return M.mb(a,"wx")},Pb:(a,b,c)=>{try{var d=M.hb(a,b)}catch(f){return f.Ma}if(a=M.mb(a,"wx"))return a;if(c){if(!M.Sa(d.mode))return 54;if(M.Nb(d)||M.kb(d)===M.cwd())return 10}else if(M.Sa(d.mode))return 31;
return 0},zd:(a,b)=>a?M.yb(a.mode)?32:M.Sa(a.mode)&&("r"!==M.Gc(b)||b&512)?31:M.mb(a,M.Gc(b)):44,Uc:4096,Cd:(a=0,b=M.Uc)=>{for(;a<=b;a++)if(!M.streams[a])return a;throw new M.Ga(33);},lb:a=>M.streams[a],zc:(a,b,c)=>{M.Ib||(M.Ib=function(){this.Wa={}},M.Ib.prototype={},Object.defineProperties(M.Ib.prototype,{object:{get:function(){return this.node},set:function(d){this.node=d}},flags:{get:function(){return this.Wa.flags},set:function(d){this.Wa.flags=d}},position:{get:function(){return this.Wa.position},
set:function(d){this.Wa.position=d}}}));a=Object.assign(new M.Ib,a);b=M.Cd(b,c);a.fd=b;return M.streams[b]=a},ad:a=>{M.streams[a]=null},$c:{open:a=>{a.Ka=M.qd(a.node.rdev).Ka;a.Ka.open&&a.Ka.open(a)},cb:()=>{throw new M.Ga(70);}},ic:a=>a>>8,$d:a=>a&255,pb:(a,b)=>a<<8|b,pc:(a,b)=>{M.Cc[a]={Ka:b}},qd:a=>M.Cc[a],Hc:a=>{var b=[];for(a=[a];a.length;){var c=a.pop();b.push(c);a.push.apply(a,c.Fb)}return b},Oc:(a,b)=>{function c(k){M.Rb--;return b(k)}function d(k){if(k){if(!d.kd)return d.kd=!0,c(k)}else++g>=
f.length&&c(null)}"function"==typeof a&&(b=a,a=!1);M.Rb++;1<M.Rb&&u("warning: "+M.Rb+" FS.syncfs operations in flight at once, probably just doing extra work");var f=M.Hc(M.root.Qa),g=0;f.forEach(k=>{if(!k.type.Oc)return d(null);k.type.Oc(k,a,d)})},Qa:(a,b,c)=>{var d="/"===c,f=!c;if(d&&M.root)throw new M.Ga(10);if(!d&&!f){var g=M.Oa(c,{Xb:!1});c=g.path;g=g.node;if(M.ob(g))throw new M.Ga(10);if(!M.Sa(g.mode))throw new M.Ga(54);}b={type:a,ce:b,Mc:c,Fb:[]};a=a.Qa(b);a.Qa=b;b.root=a;d?M.root=a:g&&(g.Eb=
b,g.Qa&&g.Qa.Fb.push(b));return a},ge:a=>{a=M.Oa(a,{Xb:!1});if(!M.ob(a.node))throw new M.Ga(28);a=a.node;var b=a.Eb,c=M.Hc(b);Object.keys(M.eb).forEach(d=>{for(d=M.eb[d];d;){var f=d.rb;c.includes(d.Qa)&&M.Wb(d);d=f}});a.Eb=null;a.Qa.Fb.splice(a.Qa.Fb.indexOf(b),1)},lookup:(a,b)=>a.Ia.lookup(a,b),ib:(a,b,c)=>{var d=M.Oa(a,{parent:!0}).node;a=L(a);if(!a||"."===a||".."===a)throw new M.Ga(28);var f=M.jc(d,a);if(f)throw new M.Ga(f);if(!d.Ia.ib)throw new M.Ga(63);return d.Ia.ib(d,a,b,c)},create:(a,b)=>
M.ib(a,(void 0!==b?b:438)&4095|32768,0),mkdir:(a,b)=>M.ib(a,(void 0!==b?b:511)&1023|16384,0),ae:(a,b)=>{a=a.split("/");for(var c="",d=0;d<a.length;++d)if(a[d]){c+="/"+a[d];try{M.mkdir(c,b)}catch(f){if(20!=f.Ma)throw f;}}},Qb:(a,b,c)=>{"undefined"==typeof c&&(c=b,b=438);return M.ib(a,b|8192,c)},symlink:(a,b)=>{if(!Va(a))throw new M.Ga(44);var c=M.Oa(b,{parent:!0}).node;if(!c)throw new M.Ga(44);b=L(b);var d=M.jc(c,b);if(d)throw new M.Ga(d);if(!c.Ia.symlink)throw new M.Ga(63);return c.Ia.symlink(c,b,
a)},rename:(a,b)=>{var c=Sa(a),d=Sa(b),f=L(a),g=L(b);var k=M.Oa(a,{parent:!0});var h=k.node;k=M.Oa(b,{parent:!0});k=k.node;if(!h||!k)throw new M.Ga(44);if(h.Qa!==k.Qa)throw new M.Ga(75);var n=M.hb(h,f);a=Wa(a,d);if("."!==a.charAt(0))throw new M.Ga(28);a=Wa(b,c);if("."!==a.charAt(0))throw new M.Ga(55);try{var q=M.hb(k,g)}catch(p){}if(n!==q){b=M.Sa(n.mode);if(f=M.Pb(h,f,b))throw new M.Ga(f);if(f=q?M.Pb(k,g,b):M.jc(k,g))throw new M.Ga(f);if(!h.Ia.rename)throw new M.Ga(63);if(M.ob(n)||q&&M.ob(q))throw new M.Ga(10);
if(k!==h&&(f=M.mb(h,"w")))throw new M.Ga(f);M.Kc(n);try{h.Ia.rename(n,k,g)}catch(p){throw p;}finally{M.Jc(n)}}},rmdir:a=>{var b=M.Oa(a,{parent:!0}).node;a=L(a);var c=M.hb(b,a),d=M.Pb(b,a,!0);if(d)throw new M.Ga(d);if(!b.Ia.rmdir)throw new M.Ga(63);if(M.ob(c))throw new M.Ga(10);b.Ia.rmdir(b,a);M.Wb(c)},readdir:a=>{a=M.Oa(a,{Xa:!0}).node;if(!a.Ia.readdir)throw new M.Ga(54);return a.Ia.readdir(a)},unlink:a=>{var b=M.Oa(a,{parent:!0}).node;if(!b)throw new M.Ga(44);a=L(a);var c=M.hb(b,a),d=M.Pb(b,a,!1);
if(d)throw new M.Ga(d);if(!b.Ia.unlink)throw new M.Ga(63);if(M.ob(c))throw new M.Ga(10);b.Ia.unlink(b,a);M.Wb(c)},readlink:a=>{a=M.Oa(a).node;if(!a)throw new M.Ga(44);if(!a.Ia.readlink)throw new M.Ga(28);return Va(M.kb(a.parent),a.Ia.readlink(a))},stat:(a,b)=>{a=M.Oa(a,{Xa:!b}).node;if(!a)throw new M.Ga(44);if(!a.Ia.bb)throw new M.Ga(63);return a.Ia.bb(a)},lstat:a=>M.stat(a,!0),chmod:(a,b,c)=>{a="string"==typeof a?M.Oa(a,{Xa:!c}).node:a;if(!a.Ia.Ta)throw new M.Ga(63);a.Ia.Ta(a,{mode:b&4095|a.mode&
-4096,timestamp:Date.now()})},lchmod:(a,b)=>{M.chmod(a,b,!0)},fchmod:(a,b)=>{a=M.lb(a);if(!a)throw new M.Ga(8);M.chmod(a.node,b)},chown:(a,b,c,d)=>{a="string"==typeof a?M.Oa(a,{Xa:!d}).node:a;if(!a.Ia.Ta)throw new M.Ga(63);a.Ia.Ta(a,{timestamp:Date.now()})},lchown:(a,b,c)=>{M.chown(a,b,c,!0)},fchown:(a,b,c)=>{a=M.lb(a);if(!a)throw new M.Ga(8);M.chown(a.node,b,c)},truncate:(a,b)=>{if(0>b)throw new M.Ga(28);a="string"==typeof a?M.Oa(a,{Xa:!0}).node:a;if(!a.Ia.Ta)throw new M.Ga(63);if(M.Sa(a.mode))throw new M.Ga(31);
if(!M.isFile(a.mode))throw new M.Ga(28);var c=M.mb(a,"w");if(c)throw new M.Ga(c);a.Ia.Ta(a,{size:b,timestamp:Date.now()})},od:(a,b)=>{a=M.lb(a);if(!a)throw new M.Ga(8);if(0===(a.flags&2097155))throw new M.Ga(28);M.truncate(a.node,b)},Vd:(a,b,c)=>{a=M.Oa(a,{Xa:!0}).node;a.Ia.Ta(a,{timestamp:Math.max(b,c)})},open:(a,b,c)=>{if(""===a)throw new M.Ga(44);b="string"==typeof b?M.Ad(b):b;c=b&64?("undefined"==typeof c?438:c)&4095|32768:0;if("object"==typeof a)var d=a;else{a=J(a);try{d=M.Oa(a,{Xa:!(b&131072)}).node}catch(g){}}var f=
!1;if(b&64)if(d){if(b&128)throw new M.Ga(20);}else d=M.ib(a,c,0),f=!0;if(!d)throw new M.Ga(44);M.Lb(d.mode)&&(b&=-513);if(b&65536&&!M.Sa(d.mode))throw new M.Ga(54);if(!f&&(c=M.zd(d,b)))throw new M.Ga(c);b&512&&!f&&M.truncate(d,0);b&=-131713;d=M.zc({node:d,path:M.kb(d),flags:b,seekable:!0,position:0,Ka:d.Ka,Ud:[],error:!1});d.Ka.open&&d.Ka.open(d);!e.logReadFiles||b&1||(M.mc||(M.mc={}),a in M.mc||(M.mc[a]=1));return d},close:a=>{if(M.Db(a))throw new M.Ga(8);a.ac&&(a.ac=null);try{a.Ka.close&&a.Ka.close(a)}catch(b){throw b;
}finally{M.ad(a.fd)}a.fd=null},Db:a=>null===a.fd,cb:(a,b,c)=>{if(M.Db(a))throw new M.Ga(8);if(!a.seekable||!a.Ka.cb)throw new M.Ga(70);if(0!=c&&1!=c&&2!=c)throw new M.Ga(28);a.position=a.Ka.cb(a,b,c);a.Ud=[];return a.position},read:(a,b,c,d,f)=>{if(0>d||0>f)throw new M.Ga(28);if(M.Db(a))throw new M.Ga(8);if(1===(a.flags&2097155))throw new M.Ga(8);if(M.Sa(a.node.mode))throw new M.Ga(31);if(!a.Ka.read)throw new M.Ga(28);var g="undefined"!=typeof f;if(!g)f=a.position;else if(!a.seekable)throw new M.Ga(70);
b=a.Ka.read(a,b,c,d,f);g||(a.position+=b);return b},write:(a,b,c,d,f,g)=>{if(0>d||0>f)throw new M.Ga(28);if(M.Db(a))throw new M.Ga(8);if(0===(a.flags&2097155))throw new M.Ga(8);if(M.Sa(a.node.mode))throw new M.Ga(31);if(!a.Ka.write)throw new M.Ga(28);a.seekable&&a.flags&1024&&M.cb(a,0,2);var k="undefined"!=typeof f;if(!k)f=a.position;else if(!a.seekable)throw new M.Ga(70);b=a.Ka.write(a,b,c,d,f,g);k||(a.position+=b);return b},Cb:(a,b,c)=>{if(M.Db(a))throw new M.Ga(8);if(0>b||0>=c)throw new M.Ga(28);
if(0===(a.flags&2097155))throw new M.Ga(8);if(!M.isFile(a.node.mode)&&!M.Sa(a.node.mode))throw new M.Ga(43);if(!a.Ka.Cb)throw new M.Ga(138);a.Ka.Cb(a,b,c)},qb:(a,b,c,d,f)=>{if(0!==(d&2)&&0===(f&2)&&2!==(a.flags&2097155))throw new M.Ga(2);if(1===(a.flags&2097155))throw new M.Ga(2);if(!a.Ka.qb)throw new M.Ga(43);return a.Ka.qb(a,b,c,d,f)},zb:(a,b,c,d,f)=>a&&a.Ka.zb?a.Ka.zb(a,b,c,d,f):0,be:()=>0,dc:(a,b,c)=>{if(!a.Ka.dc)throw new M.Ga(59);return a.Ka.dc(a,b,c)},readFile:(a,b={})=>{b.flags=b.flags||0;
b.encoding=b.encoding||"binary";if("utf8"!==b.encoding&&"binary"!==b.encoding)throw Error('Invalid encoding type "'+b.encoding+'"');var c,d=M.open(a,b.flags);a=M.stat(a).size;var f=new Uint8Array(a);M.read(d,f,0,a,0);"utf8"===b.encoding?c=sa(f,0):"binary"===b.encoding&&(c=f);M.close(d);return c},writeFile:(a,b,c={})=>{c.flags=c.flags||577;a=M.open(a,c.flags,c.mode);if("string"==typeof b){var d=new Uint8Array(ua(b)+1);b=ta(b,d,0,d.length);M.write(a,d,0,b,void 0,c.Zc)}else if(ArrayBuffer.isView(b))M.write(a,
b,0,b.byteLength,void 0,c.Zc);else throw Error("Unsupported data type");M.close(a)},cwd:()=>M.Ac,chdir:a=>{a=M.Oa(a,{Xa:!0});if(null===a.node)throw new M.Ga(44);if(!M.Sa(a.node.mode))throw new M.Ga(54);var b=M.mb(a.node,"x");if(b)throw new M.Ga(b);M.Ac=a.path},dd:()=>{M.mkdir("/tmp");M.mkdir("/home");M.mkdir("/home/web_user")},cd:()=>{M.mkdir("/dev");M.pc(M.pb(1,3),{read:()=>0,write:(b,c,d,f)=>f});M.Qb("/dev/null",M.pb(1,3));Za(M.pb(5,0),ab);Za(M.pb(6,0),bb);M.Qb("/dev/tty",M.pb(5,0));M.Qb("/dev/tty1",
M.pb(6,0));var a=Ua();M.$a("/dev","random",a);M.$a("/dev","urandom",a);M.mkdir("/dev/shm");M.mkdir("/dev/shm/tmp")},gd:()=>{M.mkdir("/proc");var a=M.mkdir("/proc/self");M.mkdir("/proc/self/fd");M.Qa({Qa:()=>{var b=M.createNode(a,"fd",16895,73);b.Ia={lookup:(c,d)=>{var f=M.lb(+d);if(!f)throw new M.Ga(8);c={parent:null,Qa:{Mc:"fake"},Ia:{readlink:()=>f.path}};return c.parent=c}};return b}},{},"/proc/self/fd")},hd:()=>{e.stdin?M.$a("/dev","stdin",e.stdin):M.symlink("/dev/tty","/dev/stdin");e.stdout?
M.$a("/dev","stdout",null,e.stdout):M.symlink("/dev/tty","/dev/stdout");e.stderr?M.$a("/dev","stderr",null,e.stderr):M.symlink("/dev/tty1","/dev/stderr");M.open("/dev/stdin",0);M.open("/dev/stdout",1);M.open("/dev/stderr",1)},Dc:()=>{M.Ga||(M.Ga=function(a,b){this.node=b;this.Kd=function(c){this.Ma=c};this.Kd(a);this.message="FS error"},M.Ga.prototype=Error(),M.Ga.prototype.constructor=M.Ga,[44].forEach(a=>{M.Zb[a]=new M.Ga(a);M.Zb[a].stack="<generic error, no stack>"}))},Pd:()=>{M.Dc();M.eb=Array(4096);
M.Qa(N,{},"/");M.dd();M.cd();M.gd();M.md={MEMFS:N}},wb:(a,b,c)=>{M.wb.cc=!0;M.Dc();e.stdin=a||e.stdin;e.stdout=b||e.stdout;e.stderr=c||e.stderr;M.hd()},de:()=>{M.wb.cc=!1;for(var a=0;a<M.streams.length;a++){var b=M.streams[a];b&&M.close(b)}},$b:(a,b)=>{var c=0;a&&(c|=365);b&&(c|=146);return c},Wd:(a,b)=>{a=M.Vb(a,b);return a.exists?a.object:null},Vb:(a,b)=>{try{var c=M.Oa(a,{Xa:!b});a=c.path}catch(f){}var d={Nb:!1,exists:!1,error:0,name:null,path:null,object:null,Dd:!1,Fd:null,Ed:null};try{c=M.Oa(a,
{parent:!0}),d.Dd=!0,d.Fd=c.path,d.Ed=c.node,d.name=L(a),c=M.Oa(a,{Xa:!b}),d.exists=!0,d.path=c.path,d.object=c.node,d.name=c.node.name,d.Nb="/"===c.path}catch(f){d.error=f.Ma}return d},xc:(a,b)=>{a="string"==typeof a?a:M.kb(a);for(b=b.split("/").reverse();b.length;){var c=b.pop();if(c){var d=J(a+"/"+c);try{M.mkdir(d)}catch(f){}a=d}}return d},ed:(a,b,c,d,f)=>{a="string"==typeof a?a:M.kb(a);b=J(a+"/"+b);return M.create(b,M.$b(d,f))},Jb:(a,b,c,d,f,g)=>{var k=b;a&&(a="string"==typeof a?a:M.kb(a),k=b?
J(a+"/"+b):a);a=M.$b(d,f);k=M.create(k,a);if(c){if("string"==typeof c){b=Array(c.length);d=0;for(f=c.length;d<f;++d)b[d]=c.charCodeAt(d);c=b}M.chmod(k,a|146);b=M.open(k,577);M.write(b,c,0,c.length,0,g);M.close(b);M.chmod(k,a)}return k},$a:(a,b,c,d)=>{a=Ta("string"==typeof a?a:M.kb(a),b);b=M.$b(!!c,!!d);M.$a.ic||(M.$a.ic=64);var f=M.pb(M.$a.ic++,0);M.pc(f,{open:g=>{g.seekable=!1},close:()=>{d&&d.buffer&&d.buffer.length&&d(10)},read:(g,k,h,n)=>{for(var q=0,p=0;p<n;p++){try{var r=c()}catch(v){throw new M.Ga(29);
}if(void 0===r&&0===q)throw new M.Ga(6);if(null===r||void 0===r)break;q++;k[h+p]=r}q&&(g.node.timestamp=Date.now());return q},write:(g,k,h,n)=>{for(var q=0;q<n;q++)try{d(k[h+q])}catch(p){throw new M.Ga(29);}n&&(g.node.timestamp=Date.now());return q}});return M.Qb(a,b,f)},Yb:a=>{if(a.ec||a.wd||a.link||a.Ja)return!0;if("undefined"!=typeof XMLHttpRequest)throw Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread.");
if(ia)try{a.Ja=Xa(ia(a.url),!0),a.Pa=a.Ja.length}catch(b){throw new M.Ga(29);}else throw Error("Cannot load without read() or XMLHttpRequest.");},wc:(a,b,c,d,f)=>{function g(){this.hc=!1;this.Wa=[]}function k(p,r,v,l,t){p=p.node.Ja;if(t>=p.length)return 0;l=Math.min(p.length-t,l);if(p.slice)for(var w=0;w<l;w++)r[v+w]=p[t+w];else for(w=0;w<l;w++)r[v+w]=p.get(t+w);return l}g.prototype.get=function(p){if(!(p>this.length-1||0>p)){var r=p%this.chunkSize;return this.Kb(p/this.chunkSize|0)[r]}};g.prototype.Ub=
function(p){this.Kb=p};g.prototype.uc=function(){var p=new XMLHttpRequest;p.open("HEAD",c,!1);p.send(null);if(!(200<=p.status&&300>p.status||304===p.status))throw Error("Couldn't load "+c+". Status: "+p.status);var r=Number(p.getResponseHeader("Content-length")),v,l=(v=p.getResponseHeader("Accept-Ranges"))&&"bytes"===v;p=(v=p.getResponseHeader("Content-Encoding"))&&"gzip"===v;var t=1048576;l||(t=r);var w=this;w.Ub(B=>{var K=B*t,H=(B+1)*t-1;H=Math.min(H,r-1);if("undefined"==typeof w.Wa[B]){var Ga=
w.Wa;if(K>H)throw Error("invalid range ("+K+", "+H+") or no bytes requested!");if(H>r-1)throw Error("only "+r+" bytes available! programmer error!");var I=new XMLHttpRequest;I.open("GET",c,!1);r!==t&&I.setRequestHeader("Range","bytes="+K+"-"+H);I.responseType="arraybuffer";I.overrideMimeType&&I.overrideMimeType("text/plain; charset=x-user-defined");I.send(null);if(!(200<=I.status&&300>I.status||304===I.status))throw Error("Couldn't load "+c+". Status: "+I.status);K=void 0!==I.response?new Uint8Array(I.response||
[]):Xa(I.responseText||"",!0);Ga[B]=K}if("undefined"==typeof w.Wa[B])throw Error("doXHR failed!");return w.Wa[B]});if(p||!r)t=r=1,t=r=this.Kb(0).length,na("LazyFiles on gzip forces download of the whole file when length is accessed");this.Wc=r;this.Vc=t;this.hc=!0};if("undefined"!=typeof XMLHttpRequest){if(!fa)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var h=new g;Object.defineProperties(h,{length:{get:function(){this.hc||
this.uc();return this.Wc}},chunkSize:{get:function(){this.hc||this.uc();return this.Vc}}});h={ec:!1,Ja:h}}else h={ec:!1,url:c};var n=M.ed(a,b,h,d,f);h.Ja?n.Ja=h.Ja:h.url&&(n.Ja=null,n.url=h.url);Object.defineProperties(n,{Pa:{get:function(){return this.Ja.length}}});var q={};Object.keys(n.Ka).forEach(p=>{var r=n.Ka[p];q[p]=function(){M.Yb(n);return r.apply(null,arguments)}});q.read=(p,r,v,l,t)=>{M.Yb(n);return k(p,r,v,l,t)};q.qb=(p,r,v)=>{M.Yb(n);var l=cb(r);if(!l)throw new M.Ga(48);k(p,A,l,r,v);
return{La:l,tc:!0}};n.Ka=q;return n},yc:(a,b,c,d,f,g,k,h,n,q)=>{function p(l){function t(w){q&&q();h||M.Jb(a,b,w,d,f,n);g&&g();Ka(v)}fb.Yd(l,r,t,()=>{k&&k();Ka(v)})||t(l)}var r=b?Va(J(a+"/"+b)):a,v="cp "+r;Ja(v);"string"==typeof c?eb(c,l=>p(l),k):p(c)},indexedDB:()=>window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB,rc:()=>"EM_FS_"+window.location.pathname,sc:20,Bb:"FILE_DATA",ee:(a,b,c)=>{b=b||(()=>{});c=c||(()=>{});var d=M.indexedDB();try{var f=d.open(M.rc(),M.sc)}catch(g){return c(g)}f.onupgradeneeded=
()=>{na("creating db");f.result.createObjectStore(M.Bb)};f.onsuccess=()=>{var g=f.result.transaction([M.Bb],"readwrite"),k=g.objectStore(M.Bb),h=0,n=0,q=a.length;a.forEach(p=>{p=k.put(M.Vb(p).object.Ja,p);p.onsuccess=()=>{h++;h+n==q&&(0==n?b():c())};p.onerror=()=>{n++;h+n==q&&(0==n?b():c())}});g.onerror=c};f.onerror=c},Zd:(a,b,c)=>{b=b||(()=>{});c=c||(()=>{});var d=M.indexedDB();try{var f=d.open(M.rc(),M.sc)}catch(g){return c(g)}f.onupgradeneeded=c;f.onsuccess=()=>{var g=f.result;try{var k=g.transaction([M.Bb],
"readonly")}catch(r){c(r);return}var h=k.objectStore(M.Bb),n=0,q=0,p=a.length;a.forEach(r=>{var v=h.get(r);v.onsuccess=()=>{M.Vb(r).exists&&M.unlink(r);M.Jb(Sa(r),L(r),v.result,!0,!0,!0);n++;n+q==p&&(0==q?b():c())};v.onerror=()=>{q++;n+q==p&&(0==q?b():c())}});k.onerror=c};f.onerror=c}};function O(a,b,c){if("/"===b.charAt(0))return b;if(-100===a)a=M.cwd();else{a=M.lb(a);if(!a)throw new M.Ga(8);a=a.path}if(0==b.length){if(!c)throw new M.Ga(44);return a}return J(a+"/"+b)}
function gb(a,b,c){try{var d=a(b)}catch(f){if(f&&f.node&&J(b)!==J(M.kb(f.node)))return-54;throw f;}D[c>>2]=d.dev;D[c+4>>2]=0;D[c+8>>2]=d.ino;D[c+12>>2]=d.mode;D[c+16>>2]=d.nlink;D[c+20>>2]=d.uid;D[c+24>>2]=d.gid;D[c+28>>2]=d.rdev;D[c+32>>2]=0;G=[d.size>>>0,(F=d.size,1<=+Math.abs(F)?0<F?(Math.min(+Math.floor(F/4294967296),4294967295)|0)>>>0:~~+Math.ceil((F-+(~~F>>>0))/4294967296)>>>0:0)];D[c+40>>2]=G[0];D[c+44>>2]=G[1];D[c+48>>2]=4096;D[c+52>>2]=d.blocks;G=[Math.floor(d.atime.getTime()/1E3)>>>0,(F=
Math.floor(d.atime.getTime()/1E3),1<=+Math.abs(F)?0<F?(Math.min(+Math.floor(F/4294967296),4294967295)|0)>>>0:~~+Math.ceil((F-+(~~F>>>0))/4294967296)>>>0:0)];D[c+56>>2]=G[0];D[c+60>>2]=G[1];D[c+64>>2]=0;G=[Math.floor(d.mtime.getTime()/1E3)>>>0,(F=Math.floor(d.mtime.getTime()/1E3),1<=+Math.abs(F)?0<F?(Math.min(+Math.floor(F/4294967296),4294967295)|0)>>>0:~~+Math.ceil((F-+(~~F>>>0))/4294967296)>>>0:0)];D[c+72>>2]=G[0];D[c+76>>2]=G[1];D[c+80>>2]=0;G=[Math.floor(d.ctime.getTime()/1E3)>>>0,(F=Math.floor(d.ctime.getTime()/
1E3),1<=+Math.abs(F)?0<F?(Math.min(+Math.floor(F/4294967296),4294967295)|0)>>>0:~~+Math.ceil((F-+(~~F>>>0))/4294967296)>>>0:0)];D[c+88>>2]=G[0];D[c+92>>2]=G[1];D[c+96>>2]=0;G=[d.ino>>>0,(F=d.ino,1<=+Math.abs(F)?0<F?(Math.min(+Math.floor(F/4294967296),4294967295)|0)>>>0:~~+Math.ceil((F-+(~~F>>>0))/4294967296)>>>0:0)];D[c+104>>2]=G[0];D[c+108>>2]=G[1];return 0}var hb=void 0;function ib(){hb+=4;return D[hb-4>>2]}function P(a){a=M.lb(a);if(!a)throw new M.Ga(8);return a}
function jb(a){return E[a>>2]+4294967296*D[a+4>>2]}var kb={};function lb(a){for(;a.length;){var b=a.pop();a.pop()(b)}}function mb(a){return this.fromWireType(D[a>>2])}var nb={},ob={},pb={};function qb(a){if(void 0===a)return"_unknown";a=a.replace(/[^a-zA-Z0-9_]/g,"$");var b=a.charCodeAt(0);return 48<=b&&57>=b?"_"+a:a}function rb(a,b){a=qb(a);return(new Function("body","return function "+a+'() {\n "use strict"; return body.apply(this, arguments);\n};\n'))(b)}
function sb(a){var b=Error,c=rb(a,function(d){this.name=a;this.message=d;d=Error(d).stack;void 0!==d&&(this.stack=this.toString()+"\n"+d.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(b.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message};return c}var tb=void 0;function ub(a){throw new tb(a);}
function Q(a,b,c){function d(h){h=c(h);h.length!==a.length&&ub("Mismatched type converter count");for(var n=0;n<a.length;++n)R(a[n],h[n])}a.forEach(function(h){pb[h]=b});var f=Array(b.length),g=[],k=0;b.forEach((h,n)=>{ob.hasOwnProperty(h)?f[n]=ob[h]:(g.push(h),nb.hasOwnProperty(h)||(nb[h]=[]),nb[h].push(()=>{f[n]=ob[h];++k;k===g.length&&d(f)}))});0===g.length&&d(f)}
function vb(a){switch(a){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+a);}}var wb=void 0;function S(a){for(var b="";z[a];)b+=wb[z[a++]];return b}var xb=void 0;function T(a){throw new xb(a);}
function R(a,b,c={}){if(!("argPackAdvance"in b))throw new TypeError("registerType registeredInstance requires argPackAdvance");var d=b.name;a||T('type "'+d+'" must have a positive integer typeid pointer');if(ob.hasOwnProperty(a)){if(c.ud)return;T("Cannot register type '"+d+"' twice")}ob[a]=b;delete pb[a];nb.hasOwnProperty(a)&&(b=nb[a],delete nb[a],b.forEach(f=>f()))}function yb(a){T(a.Ha.Ra.Na.name+" instance already deleted")}var zb=!1;function Ab(){}
function Bb(a){--a.count.value;0===a.count.value&&(a.Va?a.Ya.jb(a.Va):a.Ra.Na.jb(a.La))}function Cb(a,b,c){if(b===c)return a;if(void 0===c.Za)return null;a=Cb(a,b,c.Za);return null===a?null:c.jd(a)}var Db={},Eb=[];function Fb(){for(;Eb.length;){var a=Eb.pop();a.Ha.vb=!1;a["delete"]()}}var Gb=void 0,Hb={};function Ib(a,b){for(void 0===b&&T("ptr should not be undefined");a.Za;)b=a.Hb(b),a=a.Za;return Hb[b]}
function Jb(a,b){b.Ra&&b.La||ub("makeClassHandle requires ptr and ptrType");!!b.Ya!==!!b.Va&&ub("Both smartPtrType and smartPtr must be specified");b.count={value:1};return Kb(Object.create(a,{Ha:{value:b}}))}function Kb(a){if("undefined"===typeof FinalizationRegistry)return Kb=b=>b,a;zb=new FinalizationRegistry(b=>{Bb(b.Ha)});Kb=b=>{var c=b.Ha;c.Va&&zb.register(b,{Ha:c},b);return b};Ab=b=>{zb.unregister(b)};return Kb(a)}function U(){}
function Lb(a,b,c){if(void 0===a[b].Ua){var d=a[b];a[b]=function(){a[b].Ua.hasOwnProperty(arguments.length)||T("Function '"+c+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+a[b].Ua+")!");return a[b].Ua[arguments.length].apply(this,arguments)};a[b].Ua=[];a[b].Ua[d.ub]=d}}
function Mb(a,b){e.hasOwnProperty(a)?(T("Cannot register public name '"+a+"' twice"),Lb(e,a,a),e.hasOwnProperty(void 0)&&T("Cannot register multiple overloads of a function with the same number of arguments (undefined)!"),e[a].Ua[void 0]=b):e[a]=b}function Nb(a,b,c,d,f,g,k,h){this.name=a;this.constructor=b;this.xb=c;this.jb=d;this.Za=f;this.pd=g;this.Hb=k;this.jd=h;this.Hd=[]}
function Ob(a,b,c){for(;b!==c;)b.Hb||T("Expected null or instance of "+c.name+", got an instance of "+b.name),a=b.Hb(a),b=b.Za;return a}function Pb(a,b){if(null===b)return this.fc&&T("null is not a valid "+this.name),0;b.Ha||T('Cannot pass "'+Qb(b)+'" as a '+this.name);b.Ha.La||T("Cannot pass deleted object as a pointer of type "+this.name);return Ob(b.Ha.La,b.Ha.Ra.Na,this.Na)}
function Rb(a,b){if(null===b){this.fc&&T("null is not a valid "+this.name);if(this.Ob){var c=this.lc();null!==a&&a.push(this.jb,c);return c}return 0}b.Ha||T('Cannot pass "'+Qb(b)+'" as a '+this.name);b.Ha.La||T("Cannot pass deleted object as a pointer of type "+this.name);!this.Mb&&b.Ha.Ra.Mb&&T("Cannot convert argument of type "+(b.Ha.Ya?b.Ha.Ya.name:b.Ha.Ra.name)+" to parameter type "+this.name);c=Ob(b.Ha.La,b.Ha.Ra.Na,this.Na);if(this.Ob)switch(void 0===b.Ha.Va&&T("Passing raw pointer to smart pointer is illegal"),
this.Od){case 0:b.Ha.Ya===this?c=b.Ha.Va:T("Cannot convert argument of type "+(b.Ha.Ya?b.Ha.Ya.name:b.Ha.Ra.name)+" to parameter type "+this.name);break;case 1:c=b.Ha.Va;break;case 2:if(b.Ha.Ya===this)c=b.Ha.Va;else{var d=b.clone();c=this.Id(c,Sb(function(){d["delete"]()}));null!==a&&a.push(this.jb,c)}break;default:T("Unsupporting sharing policy")}return c}
function Tb(a,b){if(null===b)return this.fc&&T("null is not a valid "+this.name),0;b.Ha||T('Cannot pass "'+Qb(b)+'" as a '+this.name);b.Ha.La||T("Cannot pass deleted object as a pointer of type "+this.name);b.Ha.Ra.Mb&&T("Cannot convert argument of type "+b.Ha.Ra.name+" to parameter type "+this.name);return Ob(b.Ha.La,b.Ha.Ra.Na,this.Na)}
function V(a,b,c,d){this.name=a;this.Na=b;this.fc=c;this.Mb=d;this.Ob=!1;this.jb=this.Id=this.lc=this.Nc=this.Od=this.Gd=void 0;void 0!==b.Za?this.toWireType=Rb:(this.toWireType=d?Pb:Tb,this.ab=null)}function Ub(a,b){e.hasOwnProperty(a)||ub("Replacing nonexistant public symbol");e[a]=b;e[a].ub=void 0}
function Vb(a,b){var c=[];return function(){c.length=0;Object.assign(c,arguments);if(a.includes("j")){var d=e["dynCall_"+a];d=c&&c.length?d.apply(null,[b].concat(c)):d.call(null,b)}else d=Aa.get(b).apply(null,c);return d}}function W(a,b){a=S(a);var c=a.includes("j")?Vb(a,b):Aa.get(b);"function"!=typeof c&&T("unknown function pointer with signature "+a+": "+b);return c}var Wb=void 0;function Xb(a){a=Yb(a);var b=S(a);X(a);return b}
function Zb(a,b){function c(g){f[g]||ob[g]||(pb[g]?pb[g].forEach(c):(d.push(g),f[g]=!0))}var d=[],f={};b.forEach(c);throw new Wb(a+": "+d.map(Xb).join([", "]));}function $b(a){var b=Function;if(!(b instanceof Function))throw new TypeError("new_ called with constructor type "+typeof b+" which is not a function");var c=rb(b.name||"unknownFunctionName",function(){});c.prototype=b.prototype;c=new c;a=b.apply(c,a);return a instanceof Object?a:c}
function ac(a,b,c,d,f){var g=b.length;2>g&&T("argTypes array size mismatch! Must at least get return value and 'this' types!");var k=null!==b[1]&&null!==c,h=!1;for(c=1;c<b.length;++c)if(null!==b[c]&&void 0===b[c].ab){h=!0;break}var n="void"!==b[0].name,q="",p="";for(c=0;c<g-2;++c)q+=(0!==c?", ":"")+"arg"+c,p+=(0!==c?", ":"")+"arg"+c+"Wired";a="return function "+qb(a)+"("+q+") {\nif (arguments.length !== "+(g-2)+") {\nthrowBindingError('function "+a+" called with ' + arguments.length + ' arguments, expected "+
(g-2)+" args!');\n}\n";h&&(a+="var destructors = [];\n");var r=h?"destructors":"null";q="throwBindingError invoker fn runDestructors retType classParam".split(" ");d=[T,d,f,lb,b[0],b[1]];k&&(a+="var thisWired = classParam.toWireType("+r+", this);\n");for(c=0;c<g-2;++c)a+="var arg"+c+"Wired = argType"+c+".toWireType("+r+", arg"+c+"); // "+b[c+2].name+"\n",q.push("argType"+c),d.push(b[c+2]);k&&(p="thisWired"+(0<p.length?", ":"")+p);a+=(n?"var rv = ":"")+"invoker(fn"+(0<p.length?", ":"")+p+");\n";if(h)a+=
"runDestructors(destructors);\n";else for(c=k?1:2;c<b.length;++c)g=1===c?"thisWired":"arg"+(c-2)+"Wired",null!==b[c].ab&&(a+=g+"_dtor("+g+"); // "+b[c].name+"\n",q.push(g+"_dtor"),d.push(b[c].ab));n&&(a+="var ret = retType.fromWireType(rv);\nreturn ret;\n");q.push(a+"}\n");return $b(q).apply(null,d)}function bc(a,b){for(var c=[],d=0;d<a;d++)c.push(E[b+4*d>>2]);return c}var cc=[],Y=[{},{value:void 0},{value:null},{value:!0},{value:!1}];function dc(a){4<a&&0===--Y[a].oc&&(Y[a]=void 0,cc.push(a))}
var ec=a=>{a||T("Cannot use deleted val. handle = "+a);return Y[a].value},Sb=a=>{switch(a){case void 0:return 1;case null:return 2;case !0:return 3;case !1:return 4;default:var b=cc.length?cc.pop():Y.length;Y[b]={oc:1,value:a};return b}};function Qb(a){if(null===a)return"null";var b=typeof a;return"object"===b||"array"===b||"function"===b?a.toString():""+a}
function fc(a,b){switch(b){case 2:return function(c){return this.fromWireType(xa[c>>2])};case 3:return function(c){return this.fromWireType(ya[c>>3])};default:throw new TypeError("Unknown float type: "+a);}}
function gc(a,b,c){switch(b){case 0:return c?function(d){return A[d]}:function(d){return z[d]};case 1:return c?function(d){return C[d>>1]}:function(d){return wa[d>>1]};case 2:return c?function(d){return D[d>>2]}:function(d){return E[d>>2]};default:throw new TypeError("Unknown integer type: "+a);}}var hc="undefined"!=typeof TextDecoder?new TextDecoder("utf-16le"):void 0;
function ic(a,b){var c=a>>1;for(var d=c+b/2;!(c>=d)&&wa[c];)++c;c<<=1;if(32<c-a&&hc)return hc.decode(z.subarray(a,c));c="";for(d=0;!(d>=b/2);++d){var f=C[a+2*d>>1];if(0==f)break;c+=String.fromCharCode(f)}return c}function jc(a,b,c){void 0===c&&(c=2147483647);if(2>c)return 0;c-=2;var d=b;c=c<2*a.length?c/2:a.length;for(var f=0;f<c;++f)C[b>>1]=a.charCodeAt(f),b+=2;C[b>>1]=0;return b-d}function kc(a){return 2*a.length}
function lc(a,b){for(var c=0,d="";!(c>=b/4);){var f=D[a+4*c>>2];if(0==f)break;++c;65536<=f?(f-=65536,d+=String.fromCharCode(55296|f>>10,56320|f&1023)):d+=String.fromCharCode(f)}return d}function mc(a,b,c){void 0===c&&(c=2147483647);if(4>c)return 0;var d=b;c=d+c-4;for(var f=0;f<a.length;++f){var g=a.charCodeAt(f);if(55296<=g&&57343>=g){var k=a.charCodeAt(++f);g=65536+((g&1023)<<10)|k&1023}D[b>>2]=g;b+=4;if(b+4>c)break}D[b>>2]=0;return b-d}
function nc(a){for(var b=0,c=0;c<a.length;++c){var d=a.charCodeAt(c);55296<=d&&57343>=d&&++c;b+=4}return b}function oc(a,b){var c=ob[a];void 0===c&&T(b+" has unknown type "+Xb(a));return c}var pc={};function qc(a){var b=pc[a];return void 0===b?S(a):b}var rc=[];function sc(a){var b=rc.length;rc.push(a);return b}function tc(a,b){for(var c=Array(a),d=0;d<a;++d)c[d]=oc(E[b+4*d>>2],"parameter "+d);return c}var uc=[];function vc(a){var b=ua(a)+1,c=wc(b);c&&ta(a,A,c,b);return c}
function xc(a,b,c){function d(n){return(n=n.toTimeString().match(/\(([A-Za-z ]+)\)$/))?n[1]:"GMT"}var f=(new Date).getFullYear(),g=new Date(f,0,1),k=new Date(f,6,1);f=g.getTimezoneOffset();var h=k.getTimezoneOffset();D[a>>2]=60*Math.max(f,h);D[b>>2]=Number(f!=h);a=d(g);b=d(k);a=vc(a);b=vc(b);h<f?(E[c>>2]=a,E[c+4>>2]=b):(E[c>>2]=b,E[c+4>>2]=a)}function yc(a,b,c){yc.Yc||(yc.Yc=!0,xc(a,b,c))}var zc;zc=ha?()=>{var a=process.hrtime();return 1E3*a[0]+a[1]/1E6}:()=>performance.now();var Ac={};
function Bc(){if(!Cc){var a={USER:"web_user",LOGNAME:"web_user",PATH:"/",PWD:"/",HOME:"/home/web_user",LANG:("object"==typeof navigator&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8",_:da||"./this.program"},b;for(b in Ac)void 0===Ac[b]?delete a[b]:a[b]=Ac[b];var c=[];for(b in a)c.push(b+"="+a[b]);Cc=c}return Cc}var Cc;function Dc(a){return 0===a%4&&(0!==a%100||0===a%400)}var Ec=[31,29,31,30,31,30,31,31,30,31,30,31],Fc=[31,28,31,30,31,30,31,31,30,31,30,31];
function Gc(a,b,c,d){function f(l,t,w){for(l="number"==typeof l?l.toString():l||"";l.length<t;)l=w[0]+l;return l}function g(l,t){return f(l,t,"0")}function k(l,t){function w(K){return 0>K?-1:0<K?1:0}var B;0===(B=w(l.getFullYear()-t.getFullYear()))&&0===(B=w(l.getMonth()-t.getMonth()))&&(B=w(l.getDate()-t.getDate()));return B}function h(l){switch(l.getDay()){case 0:return new Date(l.getFullYear()-1,11,29);case 1:return l;case 2:return new Date(l.getFullYear(),0,3);case 3:return new Date(l.getFullYear(),
0,2);case 4:return new Date(l.getFullYear(),0,1);case 5:return new Date(l.getFullYear()-1,11,31);case 6:return new Date(l.getFullYear()-1,11,30)}}function n(l){var t=l.sb;for(l=new Date((new Date(l.tb+1900,0,1)).getTime());0<t;){var w=l.getMonth(),B=(Dc(l.getFullYear())?Ec:Fc)[w];if(t>B-l.getDate())t-=B-l.getDate()+1,l.setDate(1),11>w?l.setMonth(w+1):(l.setMonth(0),l.setFullYear(l.getFullYear()+1));else{l.setDate(l.getDate()+t);break}}w=new Date(l.getFullYear()+1,0,4);t=h(new Date(l.getFullYear(),
0,4));w=h(w);return 0>=k(t,l)?0>=k(w,l)?l.getFullYear()+1:l.getFullYear():l.getFullYear()-1}var q=D[d+40>>2];d={Sd:D[d>>2],Rd:D[d+4>>2],Sb:D[d+8>>2],qc:D[d+12>>2],Tb:D[d+16>>2],tb:D[d+20>>2],gb:D[d+24>>2],sb:D[d+28>>2],fe:D[d+32>>2],Qd:D[d+36>>2],Td:q?y(q):""};c=y(c);q={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d",
"%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var p in q)c=c.replace(new RegExp(p,"g"),q[p]);var r="Sunday Monday Tuesday Wednesday Thursday Friday Saturday".split(" "),v="January February March April May June July August September October November December".split(" ");q={"%a":function(l){return r[l.gb].substring(0,3)},"%A":function(l){return r[l.gb]},"%b":function(l){return v[l.Tb].substring(0,3)},"%B":function(l){return v[l.Tb]},
"%C":function(l){return g((l.tb+1900)/100|0,2)},"%d":function(l){return g(l.qc,2)},"%e":function(l){return f(l.qc,2," ")},"%g":function(l){return n(l).toString().substring(2)},"%G":function(l){return n(l)},"%H":function(l){return g(l.Sb,2)},"%I":function(l){l=l.Sb;0==l?l=12:12<l&&(l-=12);return g(l,2)},"%j":function(l){for(var t=0,w=0;w<=l.Tb-1;t+=(Dc(l.tb+1900)?Ec:Fc)[w++]);return g(l.qc+t,3)},"%m":function(l){return g(l.Tb+1,2)},"%M":function(l){return g(l.Rd,2)},"%n":function(){return"\n"},"%p":function(l){return 0<=
l.Sb&&12>l.Sb?"AM":"PM"},"%S":function(l){return g(l.Sd,2)},"%t":function(){return"\t"},"%u":function(l){return l.gb||7},"%U":function(l){return g(Math.floor((l.sb+7-l.gb)/7),2)},"%V":function(l){var t=Math.floor((l.sb+7-(l.gb+6)%7)/7);2>=(l.gb+371-l.sb-2)%7&&t++;if(t)53==t&&(w=(l.gb+371-l.sb)%7,4==w||3==w&&Dc(l.tb)||(t=1));else{t=52;var w=(l.gb+7-l.sb-1)%7;(4==w||5==w&&Dc(l.tb%400-1))&&t++}return g(t,2)},"%w":function(l){return l.gb},"%W":function(l){return g(Math.floor((l.sb+7-(l.gb+6)%7)/7),2)},
"%y":function(l){return(l.tb+1900).toString().substring(2)},"%Y":function(l){return l.tb+1900},"%z":function(l){l=l.Qd;var t=0<=l;l=Math.abs(l)/60;return(t?"+":"-")+String("0000"+(l/60*100+l%60)).slice(-4)},"%Z":function(l){return l.Td},"%%":function(){return"%"}};c=c.replace(/%%/g,"\x00\x00");for(p in q)c.includes(p)&&(c=c.replace(new RegExp(p,"g"),q[p](d)));c=c.replace(/\0\0/g,"%");p=Xa(c,!1);if(p.length>b)return 0;A.set(p,a);return p.length-1}
function Hc(a,b,c,d){a||(a=this);this.parent=a;this.Qa=a.Qa;this.Eb=null;this.id=M.Bd++;this.name=b;this.mode=c;this.Ia={};this.Ka={};this.rdev=d}Object.defineProperties(Hc.prototype,{read:{get:function(){return 365===(this.mode&365)},set:function(a){a?this.mode|=365:this.mode&=-366}},write:{get:function(){return 146===(this.mode&146)},set:function(a){a?this.mode|=146:this.mode&=-147}},wd:{get:function(){return M.Sa(this.mode)}},ec:{get:function(){return M.Lb(this.mode)}}});M.Pc=Hc;M.Pd();var fb;
e.FS_createPath=M.xc;e.FS_createDataFile=M.Jb;e.FS_createPreloadedFile=M.yc;e.FS_unlink=M.unlink;e.FS_createLazyFile=M.wc;e.FS_createDevice=M.$a;tb=e.InternalError=sb("InternalError");for(var Ic=Array(256),Jc=0;256>Jc;++Jc)Ic[Jc]=String.fromCharCode(Jc);wb=Ic;xb=e.BindingError=sb("BindingError");
U.prototype.isAliasOf=function(a){if(!(this instanceof U&&a instanceof U))return!1;var b=this.Ha.Ra.Na,c=this.Ha.La,d=a.Ha.Ra.Na;for(a=a.Ha.La;b.Za;)c=b.Hb(c),b=b.Za;for(;d.Za;)a=d.Hb(a),d=d.Za;return b===d&&c===a};
U.prototype.clone=function(){this.Ha.La||yb(this);if(this.Ha.Gb)return this.Ha.count.value+=1,this;var a=Kb,b=Object,c=b.create,d=Object.getPrototypeOf(this),f=this.Ha;a=a(c.call(b,d,{Ha:{value:{count:f.count,vb:f.vb,Gb:f.Gb,La:f.La,Ra:f.Ra,Va:f.Va,Ya:f.Ya}}}));a.Ha.count.value+=1;a.Ha.vb=!1;return a};U.prototype["delete"]=function(){this.Ha.La||yb(this);this.Ha.vb&&!this.Ha.Gb&&T("Object already scheduled for deletion");Ab(this);Bb(this.Ha);this.Ha.Gb||(this.Ha.Va=void 0,this.Ha.La=void 0)};
U.prototype.isDeleted=function(){return!this.Ha.La};U.prototype.deleteLater=function(){this.Ha.La||yb(this);this.Ha.vb&&!this.Ha.Gb&&T("Object already scheduled for deletion");Eb.push(this);1===Eb.length&&Gb&&Gb(Fb);this.Ha.vb=!0;return this};e.getInheritedInstanceCount=function(){return Object.keys(Hb).length};e.getLiveInheritedInstances=function(){var a=[],b;for(b in Hb)Hb.hasOwnProperty(b)&&a.push(Hb[b]);return a};e.flushPendingDeletes=Fb;e.setDelayFunction=function(a){Gb=a;Eb.length&&Gb&&Gb(Fb)};
V.prototype.rd=function(a){this.Nc&&(a=this.Nc(a));return a};V.prototype.Bc=function(a){this.jb&&this.jb(a)};V.prototype.argPackAdvance=8;V.prototype.readValueFromPointer=mb;V.prototype.deleteObject=function(a){if(null!==a)a["delete"]()};
V.prototype.fromWireType=function(a){function b(){return this.Ob?Jb(this.Na.xb,{Ra:this.Gd,La:c,Ya:this,Va:a}):Jb(this.Na.xb,{Ra:this,La:a})}var c=this.rd(a);if(!c)return this.Bc(a),null;var d=Ib(this.Na,c);if(void 0!==d){if(0===d.Ha.count.value)return d.Ha.La=c,d.Ha.Va=a,d.clone();d=d.clone();this.Bc(a);return d}d=this.Na.pd(c);d=Db[d];if(!d)return b.call(this);d=this.Mb?d.bd:d.pointerType;var f=Cb(c,this.Na,d.Na);return null===f?b.call(this):this.Ob?Jb(d.Na.xb,{Ra:d,La:f,Ya:this,Va:a}):Jb(d.Na.xb,
{Ra:d,La:f})};Wb=e.UnboundTypeError=sb("UnboundTypeError");e.count_emval_handles=function(){for(var a=0,b=5;b<Y.length;++b)void 0!==Y[b]&&++a;return a};e.get_first_emval=function(){for(var a=5;a<Y.length;++a)if(void 0!==Y[a])return Y[a];return null};
var Lc={a:function(a){return wc(a+24)+24},b:function(a,b,c){(new Pa(a)).wb(b,c);Qa++;throw a;},Z:function(a,b){try{return a=y(a),M.chmod(a,b),0}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return-c.Ma}},ba:function(a,b,c){try{b=y(b);b=O(a,b);if(c&-8)return-28;var d=M.Oa(b,{Xa:!0}).node;if(!d)return-44;a="";c&4&&(a+="r");c&2&&(a+="w");c&1&&(a+="x");return a&&M.mb(d,a)?-2:0}catch(f){if("undefined"==typeof M||!(f instanceof M.Ga))throw f;return-f.Ma}},_:function(a,b){try{return M.fchmod(a,
-b),0}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return-c.Ma}},Y:function(a,b,c){try{return M.fchown(a,b,c),0}catch(d){if("undefined"==typeof M||!(d instanceof M.Ga))throw d;return-d.Ma}},d:function(a,b,c){hb=c;try{var d=P(a);switch(b){case 0:var f=ib();return 0>f?-28:M.zc(d,f).fd;case 1:case 2:return 0;case 3:return d.flags;case 4:return f=ib(),d.flags|=f,0;case 5:return f=ib(),C[f+0>>1]=2,0;case 6:case 7:return 0;case 16:case 8:return-28;case 9:return D[Kc()>>2]=28,-1;default:return-28}}catch(g){if("undefined"==
+b),0}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return-c.Ma}},Y:function(a,b,c){try{return M.fchown(a,b,c),0}catch(d){if("undefined"==typeof M||!(d instanceof M.Ga))throw d;return-d.Ma}},e:function(a,b,c){hb=c;try{var d=P(a);switch(b){case 0:var f=ib();return 0>f?-28:M.zc(d,f).fd;case 1:case 2:return 0;case 3:return d.flags;case 4:return f=ib(),d.flags|=f,0;case 5:return f=ib(),C[f+0>>1]=2,0;case 6:case 7:return 0;case 16:case 8:return-28;case 9:return D[Kc()>>2]=28,-1;default:return-28}}catch(g){if("undefined"==
typeof M||!(g instanceof M.Ga))throw g;return-g.Ma}},X:function(a,b){try{var c=P(a);return gb(M.stat,c.path,b)}catch(d){if("undefined"==typeof M||!(d instanceof M.Ga))throw d;return-d.Ma}},D:function(a,b,c){try{b=c+2097152>>>0<4194305-!!b?(b>>>0)+4294967296*c:NaN;if(isNaN(b))return-61;M.od(a,b);return 0}catch(d){if("undefined"==typeof M||!(d instanceof M.Ga))throw d;return-d.Ma}},S:function(a,b){try{if(0===b)return-28;var c=M.cwd(),d=ua(c)+1;if(b<d)return-68;ta(c,z,a,b);return d}catch(f){if("undefined"==
typeof M||!(f instanceof M.Ga))throw f;return-f.Ma}},H:function(a,b,c){hb=c;try{var d=P(a);switch(b){case 21509:case 21505:return d.tty?0:-59;case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:return d.tty?0:-59;case 21519:if(!d.tty)return-59;var f=ib();return D[f>>2]=0;case 21520:return d.tty?-28:-59;case 21531:return f=ib(),M.dc(d,b,f);case 21523:return d.tty?0:-59;case 21524:return d.tty?0:-59;default:x("bad ioctl syscall "+b)}}catch(g){if("undefined"==typeof M||!(g instanceof M.Ga))throw g;
return-g.Ma}},V:function(a,b){try{return a=y(a),gb(M.lstat,a,b)}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return-c.Ma}},P:function(a,b,c){try{return b=y(b),b=O(a,b),b=J(b),"/"===b[b.length-1]&&(b=b.substr(0,b.length-1)),M.mkdir(b,c,0),0}catch(d){if("undefined"==typeof M||!(d instanceof M.Ga))throw d;return-d.Ma}},U:function(a,b,c,d){try{b=y(b);var f=d&256;b=O(a,b,d&4096);return gb(f?M.lstat:M.stat,b,c)}catch(g){if("undefined"==typeof M||!(g instanceof M.Ga))throw g;return-g.Ma}},
u:function(a,b,c,d){hb=d;try{b=y(b);b=O(a,b);var f=d?ib():0;return M.open(b,c,f).fd}catch(g){if("undefined"==typeof M||!(g instanceof M.Ga))throw g;return-g.Ma}},M:function(a,b,c,d){try{b=y(b);b=O(a,b);if(0>=d)return-28;var f=M.readlink(b),g=Math.min(d,ua(f)),k=A[c+g];ta(f,z,c,d+1);A[c+g]=k;return g}catch(h){if("undefined"==typeof M||!(h instanceof M.Ga))throw h;return-h.Ma}},L:function(a,b,c,d){try{return b=y(b),d=y(d),b=O(a,b),d=O(c,d),M.rename(b,d),0}catch(f){if("undefined"==typeof M||!(f instanceof
M.Ga))throw f;return-f.Ma}},q:function(a){try{return a=y(a),M.rmdir(a),0}catch(b){if("undefined"==typeof M||!(b instanceof M.Ga))throw b;return-b.Ma}},W:function(a,b){try{return a=y(a),gb(M.stat,a,b)}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return-c.Ma}},r:function(a,b,c){try{return b=y(b),b=O(a,b),0===c?M.unlink(b):512===c?M.rmdir(b):x("Invalid flags passed to unlinkat"),0}catch(d){if("undefined"==typeof M||!(d instanceof M.Ga))throw d;return-d.Ma}},J:function(a,b,c){try{b=
-y(b);b=O(a,b,!0);if(c){var d=jb(c),f=D[c+8>>2];g=1E3*d+f/1E6;c+=16;d=jb(c);f=D[c+8>>2];k=1E3*d+f/1E6}else var g=Date.now(),k=g;M.Vd(b,g,k);return 0}catch(h){if("undefined"==typeof M||!(h instanceof M.Ga))throw h;return-h.Ma}},k:function(a){var b=kb[a];delete kb[a];var c=b.lc,d=b.jb,f=b.Fc,g=f.map(k=>k.td).concat(f.map(k=>k.Md));Q([a],g,k=>{var h={};f.forEach((n,q)=>{var p=k[q],r=n.Kb,v=n.sd,l=k[q+f.length],t=n.Ld,w=n.Nd;h[n.ld]={read:B=>p.fromWireType(r(v,B)),write:(B,K)=>{var H=[];t(w,B,l.toWireType(H,
+y(b);b=O(a,b,!0);if(c){var d=jb(c),f=D[c+8>>2];g=1E3*d+f/1E6;c+=16;d=jb(c);f=D[c+8>>2];k=1E3*d+f/1E6}else var g=Date.now(),k=g;M.Vd(b,g,k);return 0}catch(h){if("undefined"==typeof M||!(h instanceof M.Ga))throw h;return-h.Ma}},h:function(a){var b=kb[a];delete kb[a];var c=b.lc,d=b.jb,f=b.Fc,g=f.map(k=>k.td).concat(f.map(k=>k.Md));Q([a],g,k=>{var h={};f.forEach((n,q)=>{var p=k[q],r=n.Kb,v=n.sd,l=k[q+f.length],t=n.Ld,w=n.Nd;h[n.ld]={read:B=>p.fromWireType(r(v,B)),write:(B,K)=>{var H=[];t(w,B,l.toWireType(H,
K));lb(H)}}});return[{name:b.name,fromWireType:function(n){var q={},p;for(p in h)q[p]=h[p].read(n);d(n);return q},toWireType:function(n,q){for(var p in h)if(!(p in q))throw new TypeError('Missing field: "'+p+'"');var r=c();for(p in h)h[p].write(r,q[p]);null!==n&&n.push(d,r);return r},argPackAdvance:8,readValueFromPointer:mb,ab:d}]})},E:function(){},da:function(a,b,c,d,f){var g=vb(c);b=S(b);R(a,{name:b,fromWireType:function(k){return!!k},toWireType:function(k,h){return h?d:f},argPackAdvance:8,readValueFromPointer:function(k){if(1===
c)var h=A;else if(2===c)h=C;else if(4===c)h=D;else throw new TypeError("Unknown boolean type size: "+b);return this.fromWireType(h[k>>g])},ab:null})},s:function(a,b,c,d,f,g,k,h,n,q,p,r,v){p=S(p);g=W(f,g);h&&(h=W(k,h));q&&(q=W(n,q));v=W(r,v);var l=qb(p);Mb(l,function(){Zb("Cannot construct "+p+" due to unbound types",[d])});Q([a,b,c],d?[d]:[],function(t){t=t[0];if(d){var w=t.Na;var B=w.xb}else B=U.prototype;t=rb(l,function(){if(Object.getPrototypeOf(this)!==K)throw new xb("Use 'new' to construct "+
p);if(void 0===H.nb)throw new xb(p+" has no accessible constructor");var I=H.nb[arguments.length];if(void 0===I)throw new xb("Tried to invoke ctor of "+p+" with invalid number of parameters ("+arguments.length+") - expected ("+Object.keys(H.nb).toString()+") parameters instead!");return I.apply(this,arguments)});var K=Object.create(B,{constructor:{value:t}});t.prototype=K;var H=new Nb(p,t,K,v,w,g,h,q);w=new V(p,H,!0,!1);B=new V(p+"*",H,!1,!1);var Ga=new V(p+" const*",H,!1,!0);Db[a]={pointerType:B,
bd:Ga};Ub(l,t);return[w,B,Ga]})},fa:function(a,b,c,d,f,g,k){var h=bc(c,d);b=S(b);g=W(f,g);Q([],[a],function(n){function q(){Zb("Cannot call "+p+" due to unbound types",h)}n=n[0];var p=n.name+"."+b;b.startsWith("@@")&&(b=Symbol[b.substring(2)]);var r=n.Na.constructor;void 0===r[b]?(q.ub=c-1,r[b]=q):(Lb(r,b,p),r[b].Ua[c-1]=q);Q([],h,function(v){v=[v[0],null].concat(v.slice(1));v=ac(p,v,null,g,k);void 0===r[b].Ua?(v.ub=c-1,r[b]=v):r[b].Ua[c-1]=v;return[]});return[]})},o:function(a,b,c,d,f,g){0<b||x();
var k=bc(b,c);f=W(d,f);Q([],[a],function(h){h=h[0];var n="constructor "+h.name;void 0===h.Na.nb&&(h.Na.nb=[]);if(void 0!==h.Na.nb[b-1])throw new xb("Cannot register multiple constructors with identical number of parameters ("+(b-1)+") for class '"+h.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!");h.Na.nb[b-1]=()=>{Zb("Cannot construct "+h.name+" due to unbound types",k)};Q([],k,function(q){q.splice(1,0,null);h.Na.nb[b-1]=ac(n,q,null,f,g);
-return[]});return[]})},e:function(a,b,c,d,f,g,k,h){var n=bc(c,d);b=S(b);g=W(f,g);Q([],[a],function(q){function p(){Zb("Cannot call "+r+" due to unbound types",n)}q=q[0];var r=q.name+"."+b;b.startsWith("@@")&&(b=Symbol[b.substring(2)]);h&&q.Na.Hd.push(b);var v=q.Na.xb,l=v[b];void 0===l||void 0===l.Ua&&l.className!==q.name&&l.ub===c-2?(p.ub=c-2,p.className=q.name,v[b]=p):(Lb(v,b,r),v[b].Ua[c-2]=p);Q([],n,function(t){t=ac(r,t,q,g,k);void 0===v[b].Ua?(t.ub=c-2,v[b]=t):v[b].Ua[c-2]=t;return[]});return[]})},
+return[]});return[]})},d:function(a,b,c,d,f,g,k,h){var n=bc(c,d);b=S(b);g=W(f,g);Q([],[a],function(q){function p(){Zb("Cannot call "+r+" due to unbound types",n)}q=q[0];var r=q.name+"."+b;b.startsWith("@@")&&(b=Symbol[b.substring(2)]);h&&q.Na.Hd.push(b);var v=q.Na.xb,l=v[b];void 0===l||void 0===l.Ua&&l.className!==q.name&&l.ub===c-2?(p.ub=c-2,p.className=q.name,v[b]=p):(Lb(v,b,r),v[b].Ua[c-2]=p);Q([],n,function(t){t=ac(r,t,q,g,k);void 0===v[b].Ua?(t.ub=c-2,v[b]=t):v[b].Ua[c-2]=t;return[]});return[]})},
ca:function(a,b){b=S(b);R(a,{name:b,fromWireType:function(c){var d=ec(c);dc(c);return d},toWireType:function(c,d){return Sb(d)},argPackAdvance:8,readValueFromPointer:mb,ab:null})},x:function(a,b,c){c=vb(c);b=S(b);R(a,{name:b,fromWireType:function(d){return d},toWireType:function(d,f){return f},argPackAdvance:8,readValueFromPointer:fc(b,c),ab:null})},f:function(a,b,c,d,f){b=S(b);-1===f&&(f=4294967295);f=vb(c);var g=h=>h;if(0===d){var k=32-8*c;g=h=>h<<k>>>k}c=b.includes("unsigned")?function(h,n){return n>>>
0}:function(h,n){return n};R(a,{name:b,fromWireType:g,toWireType:c,argPackAdvance:8,readValueFromPointer:gc(b,f,0!==d),ab:null})},c:function(a,b,c){function d(g){g>>=2;var k=E;return new f(va,k[g+1],k[g])}var f=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array][b];c=S(c);R(a,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{ud:!0})},y:function(a,b){b=S(b);var c="std::string"===b;R(a,{name:b,fromWireType:function(d){var f=E[d>>2],g=d+4;if(c)for(var k=
g,h=0;h<=f;++h){var n=g+h;if(h==f||0==z[n]){k=y(k,n-k);if(void 0===q)var q=k;else q+=String.fromCharCode(0),q+=k;k=n+1}}else{q=Array(f);for(h=0;h<f;++h)q[h]=String.fromCharCode(z[g+h]);q=q.join("")}X(d);return q},toWireType:function(d,f){f instanceof ArrayBuffer&&(f=new Uint8Array(f));var g="string"==typeof f;g||f instanceof Uint8Array||f instanceof Uint8ClampedArray||f instanceof Int8Array||T("Cannot pass non-string to std::string");var k=c&&g?ua(f):f.length;var h=wc(4+k+1),n=h+4;E[h>>2]=k;if(c&&
-g)ta(f,z,n,k+1);else if(g)for(g=0;g<k;++g){var q=f.charCodeAt(g);255<q&&(X(n),T("String has UTF-16 code units that do not fit in 8 bits"));z[n+g]=q}else for(g=0;g<k;++g)z[n+g]=f[g];null!==d&&d.push(X,h);return h},argPackAdvance:8,readValueFromPointer:mb,ab:function(d){X(d)}})},l:function(a,b,c){c=S(c);if(2===b){var d=ic;var f=jc;var g=kc;var k=()=>wa;var h=1}else 4===b&&(d=lc,f=mc,g=nc,k=()=>E,h=2);R(a,{name:c,fromWireType:function(n){for(var q=E[n>>2],p=k(),r,v=n+4,l=0;l<=q;++l){var t=n+4+l*b;if(l==
-q||0==p[t>>h])v=d(v,t-v),void 0===r?r=v:(r+=String.fromCharCode(0),r+=v),v=t+b}X(n);return r},toWireType:function(n,q){"string"!=typeof q&&T("Cannot pass non-string to C++ string type "+c);var p=g(q),r=wc(4+p+b);E[r>>2]=p>>h;f(q,r+4,p+b);null!==n&&n.push(X,r);return r},argPackAdvance:8,readValueFromPointer:mb,ab:function(n){X(n)}})},m:function(a,b,c,d,f,g){kb[a]={name:S(b),lc:W(c,d),jb:W(f,g),Fc:[]}},j:function(a,b,c,d,f,g,k,h,n,q){kb[a].Fc.push({ld:S(b),td:c,Kb:W(d,f),sd:g,Md:k,Ld:W(h,n),Nd:q})},
+g)ta(f,z,n,k+1);else if(g)for(g=0;g<k;++g){var q=f.charCodeAt(g);255<q&&(X(n),T("String has UTF-16 code units that do not fit in 8 bits"));z[n+g]=q}else for(g=0;g<k;++g)z[n+g]=f[g];null!==d&&d.push(X,h);return h},argPackAdvance:8,readValueFromPointer:mb,ab:function(d){X(d)}})},m:function(a,b,c){c=S(c);if(2===b){var d=ic;var f=jc;var g=kc;var k=()=>wa;var h=1}else 4===b&&(d=lc,f=mc,g=nc,k=()=>E,h=2);R(a,{name:c,fromWireType:function(n){for(var q=E[n>>2],p=k(),r,v=n+4,l=0;l<=q;++l){var t=n+4+l*b;if(l==
+q||0==p[t>>h])v=d(v,t-v),void 0===r?r=v:(r+=String.fromCharCode(0),r+=v),v=t+b}X(n);return r},toWireType:function(n,q){"string"!=typeof q&&T("Cannot pass non-string to C++ string type "+c);var p=g(q),r=wc(4+p+b);E[r>>2]=p>>h;f(q,r+4,p+b);null!==n&&n.push(X,r);return r},argPackAdvance:8,readValueFromPointer:mb,ab:function(n){X(n)}})},j:function(a,b,c,d,f,g){kb[a]={name:S(b),lc:W(c,d),jb:W(f,g),Fc:[]}},g:function(a,b,c,d,f,g,k,h,n,q){kb[a].Fc.push({ld:S(b),td:c,Kb:W(d,f),sd:g,Md:k,Ld:W(h,n),Nd:q})},
ea:function(a,b){b=S(b);R(a,{xd:!0,name:b,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},w:function(){return Date.now()},z:function(a,b,c){a=ec(a);b=oc(b,"emval::as");var d=[],f=Sb(d);E[c>>2]=f;return b.toWireType(d,a)},ka:function(a,b,c,d){a=rc[a];b=ec(b);c=qc(c);a(b,c,null,d)},ia:dc,ja:function(a,b){var c=tc(a,b),d=c[0];b=d.name+"_$"+c.slice(1).map(function(p){return p.name}).join("_")+"$";var f=uc[b];if(void 0!==f)return f;f=["retType"];for(var g=[d],k="",h=0;h<a-1;++h)k+=
(0!==h?", ":"")+"arg"+h,f.push("argType"+h),g.push(c[1+h]);var n="return function "+qb("methodCaller_"+b)+"(handle, name, destructors, args) {\n",q=0;for(h=0;h<a-1;++h)n+=" var arg"+h+" = argType"+h+".readValueFromPointer(args"+(q?"+"+q:"")+");\n",q+=c[h+1].argPackAdvance;n+=" var rv = handle[name]("+k+");\n";for(h=0;h<a-1;++h)c[h+1].deleteObject&&(n+=" argType"+h+".deleteObject(arg"+h+");\n");d.xd||(n+=" return retType.toWireType(destructors, rv);\n");f.push(n+"};\n");a=$b(f).apply(null,
g);f=sc(a);return uc[b]=f},A:function(a,b){a=ec(a);b=ec(b);return Sb(a[b])},B:function(a){4<a&&(Y[a].oc+=1)},la:function(){return Sb([])},ha:function(a){return Sb(qc(a))},ga:function(a){var b=ec(a);lb(b);dc(a)},n:function(a,b){a=oc(a,"_emval_take_value");a=a.readValueFromPointer(b);return Sb(a)},$:function(a,b){a=new Date(1E3*jb(a));D[b>>2]=a.getSeconds();D[b+4>>2]=a.getMinutes();D[b+8>>2]=a.getHours();D[b+12>>2]=a.getDate();D[b+16>>2]=a.getMonth();D[b+20>>2]=a.getFullYear()-1900;D[b+24>>2]=a.getDay();
var c=new Date(a.getFullYear(),0,1);D[b+28>>2]=(a.getTime()-c.getTime())/864E5|0;D[b+36>>2]=-(60*a.getTimezoneOffset());var d=(new Date(a.getFullYear(),6,1)).getTimezoneOffset();c=c.getTimezoneOffset();D[b+32>>2]=(d!=c&&a.getTimezoneOffset()==Math.min(c,d))|0},N:function(a,b,c,d,f,g){try{var k=M.lb(d);if(!k)return-8;var h=M.qb(k,a,f,b,c),n=h.La;D[g>>2]=h.tc;return n}catch(q){if("undefined"==typeof M||!(q instanceof M.Ga))throw q;return-q.Ma}},O:function(a,b,c,d,f,g){try{var k=M.lb(f);if(k&&c&2){var h=
-z.slice(a,a+b);M.zb(k,h,g,b,d)}}catch(n){if("undefined"==typeof M||!(n instanceof M.Ga))throw n;return-n.Ma}},aa:yc,h:function(){x("")},K:function(){return 2147483648},v:zc,i:function(a){var b=z.length;a>>>=0;if(2147483648<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);var f=Math;d=Math.max(a,d);f=f.min.call(f,2147483648,d+(65536-d%65536)%65536);a:{try{pa.grow(f-va.byteLength+65535>>>16);za();var g=1;break a}catch(k){}g=void 0}if(g)return!0}return!1},Q:function(a,b){var c=
-0;Bc().forEach(function(d,f){var g=b+c;f=E[a+4*f>>2]=g;for(g=0;g<d.length;++g)A[f++>>0]=d.charCodeAt(g);A[f>>0]=0;c+=d.length+1});return 0},R:function(a,b){var c=Bc();E[a>>2]=c.length;var d=0;c.forEach(function(f){d+=f.length+1});E[b>>2]=d;return 0},g:function(a){try{var b=P(a);M.close(b);return 0}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return c.Ma}},I:function(a,b){try{var c=P(a);A[b>>0]=c.tty?2:M.Sa(c.mode)?3:M.yb(c.mode)?7:4;return 0}catch(d){if("undefined"==typeof M||!(d instanceof
+z.slice(a,a+b);M.zb(k,h,g,b,d)}}catch(n){if("undefined"==typeof M||!(n instanceof M.Ga))throw n;return-n.Ma}},aa:yc,k:function(){x("")},K:function(){return 2147483648},v:zc,l:function(a){var b=z.length;a>>>=0;if(2147483648<a)return!1;for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,a+100663296);var f=Math;d=Math.max(a,d);f=f.min.call(f,2147483648,d+(65536-d%65536)%65536);a:{try{pa.grow(f-va.byteLength+65535>>>16);za();var g=1;break a}catch(k){}g=void 0}if(g)return!0}return!1},Q:function(a,b){var c=
+0;Bc().forEach(function(d,f){var g=b+c;f=E[a+4*f>>2]=g;for(g=0;g<d.length;++g)A[f++>>0]=d.charCodeAt(g);A[f>>0]=0;c+=d.length+1});return 0},R:function(a,b){var c=Bc();E[a>>2]=c.length;var d=0;c.forEach(function(f){d+=f.length+1});E[b>>2]=d;return 0},i:function(a){try{var b=P(a);M.close(b);return 0}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return c.Ma}},I:function(a,b){try{var c=P(a);A[b>>0]=c.tty?2:M.Sa(c.mode)?3:M.yb(c.mode)?7:4;return 0}catch(d){if("undefined"==typeof M||!(d instanceof
M.Ga))throw d;return d.Ma}},t:function(a,b,c,d){try{a:{var f=P(a);a=b;for(var g=b=0;g<c;g++){var k=E[a>>2],h=E[a+4>>2];a+=8;var n=M.read(f,A,k,h,void 0);if(0>n){var q=-1;break a}b+=n;if(n<h)break}q=b}D[d>>2]=q;return 0}catch(p){if("undefined"==typeof M||!(p instanceof M.Ga))throw p;return p.Ma}},C:function(a,b,c,d,f){try{b=c+2097152>>>0<4194305-!!b?(b>>>0)+4294967296*c:NaN;if(isNaN(b))return 61;var g=P(a);M.cb(g,b,d);G=[g.position>>>0,(F=g.position,1<=+Math.abs(F)?0<F?(Math.min(+Math.floor(F/4294967296),
4294967295)|0)>>>0:~~+Math.ceil((F-+(~~F>>>0))/4294967296)>>>0:0)];D[f>>2]=G[0];D[f+4>>2]=G[1];g.ac&&0===b&&0===d&&(g.ac=null);return 0}catch(k){if("undefined"==typeof M||!(k instanceof M.Ga))throw k;return k.Ma}},T:function(a){try{var b=P(a);return b.Ka&&b.Ka.fsync?-b.Ka.fsync(b):0}catch(c){if("undefined"==typeof M||!(c instanceof M.Ga))throw c;return c.Ma}},p:function(a,b,c,d){try{a:{var f=P(a);a=b;for(var g=b=0;g<c;g++){var k=E[a>>2],h=E[a+4>>2];a+=8;var n=M.write(f,A,k,h,void 0);if(0>n){var q=
-1;break a}b+=n}q=b}E[d>>2]=q;return 0}catch(p){if("undefined"==typeof M||!(p instanceof M.Ga))throw p;return p.Ma}},F:function(){},G:function(a,b,c,d){return Gc(a,b,c,d)}},Z=function(){function a(c){e.asm=c.exports;pa=e.asm.ma;za();Aa=e.asm.ra;Ca.unshift(e.asm.na);Ka("wasm-instantiate")}var b={a:Lc};Ja("wasm-instantiate");if(e.instantiateWasm)try{return e.instantiateWasm(b,a)}catch(c){return u("Module.instantiateWasm callback failed with error: "+c),!1}b=Na(b);a(b[0]);return e.asm}();
e.___wasm_call_ctors=Z.na;var Kc=e.___errno_location=Z.oa,wc=e._malloc=Z.pa,X=e._free=Z.qa,Yb=e.___getTypeName=Z.sa;e.___embind_register_native_and_builtin_types=Z.ta;var db=e._emscripten_builtin_memalign=Z.ua;e.___cxa_is_pointer_type=Z.va;e.dynCall_iiiij=Z.wa;e.dynCall_iij=Z.xa;e.dynCall_iijii=Z.ya;e.dynCall_iiji=Z.za;e.dynCall_iiiiiij=Z.Aa;e.dynCall_jiji=Z.Ba;e.dynCall_viijii=Z.Ca;e.dynCall_iiiiij=Z.Da;e.dynCall_iiiiijj=Z.Ea;e.dynCall_iiiiiijj=Z.Fa;e.addRunDependency=Ja;e.removeRunDependency=Ka;
e.FS_createPath=M.xc;e.FS_createDataFile=M.Jb;e.FS_createPreloadedFile=M.yc;e.FS_createLazyFile=M.wc;e.FS_createDevice=M.$a;e.FS_unlink=M.unlink;e.FS=M;var Mc;Ia=function Nc(){Mc||Oc();Mc||(Ia=Nc)};
function Oc(){function a(){if(!Mc&&(Mc=!0,e.calledRun=!0,!qa)){e.noFSInit||M.wb.cc||M.wb();M.Lc=!1;Oa(Ca);aa(e);if(e.onRuntimeInitialized)e.onRuntimeInitialized();if(e.postRun)for("function"==typeof e.postRun&&(e.postRun=[e.postRun]);e.postRun.length;){var b=e.postRun.shift();Da.unshift(b)}Oa(Da)}}if(!(0<Fa)){if(e.preRun)for("function"==typeof e.preRun&&(e.preRun=[e.preRun]);e.preRun.length;)Ea();Oa(Ba);0<Fa||(e.setStatus?(e.setStatus("Running..."),setTimeout(function(){setTimeout(function(){e.setStatus("")},
1);a()},1)):a())}}if(e.preInit)for("function"==typeof e.preInit&&(e.preInit=[e.preInit]);0<e.preInit.length;)e.preInit.pop()();Oc();
return Module
}
);
})();
export default Module;
\ No newline at end of file
diff --git a/web/database/_generated/comm_query_executor.wasm b/web/database/_generated/comm_query_executor.wasm
index 25c261ceb..cda7af290 100755
Binary files a/web/database/_generated/comm_query_executor.wasm and b/web/database/_generated/comm_query_executor.wasm differ
diff --git a/web/database/types/sqlite-query-executor.js b/web/database/types/sqlite-query-executor.js
index eca568c39..b115cd978 100644
--- a/web/database/types/sqlite-query-executor.js
+++ b/web/database/types/sqlite-query-executor.js
@@ -1,32 +1,38 @@
// @flow
import type { ClientDBReport } from 'lib/ops/report-store-ops.js';
+import type { ClientDBUserInfo } from 'lib/ops/user-store-ops.js';
import type { ClientDBDraftInfo } from 'lib/types/draft-types.js';
declare export class SQLiteQueryExecutor {
constructor(sqliteFilePath: string): void;
updateDraft(key: string, text: string): void;
moveDraft(oldKey: string, newKey: string): boolean;
getAllDrafts(): ClientDBDraftInfo[];
removeAllDrafts(): void;
setMetadata(entryName: string, data: string): void;
clearMetadata(entryName: string): void;
getMetadata(entryName: string): string;
replaceReport(report: ClientDBReport): void;
removeReports(ids: $ReadOnlyArray<string>): void;
removeAllReports(): void;
getAllReports(): ClientDBReport[];
setPersistStorageItem(key: string, item: string): void;
removePersistStorageItem(key: string): void;
getPersistStorageItem(key: string): string;
+ replaceUser(user_info: ClientDBUserInfo): void;
+ removeUsers(ids: $ReadOnlyArray<string>): void;
+ removeAllUsers(): void;
+ getAllUsers(): ClientDBUserInfo[];
+
// method is provided to manually signal that a C++ object
// is no longer needed and can be deleted
delete(): void;
}
export type SQLiteQueryExecutorType = typeof SQLiteQueryExecutor;

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 6:29 AM (19 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2690482
Default Alt Text
(127 KB)

Event Timeline