fix testcase FTBFS
This commit is contained in:
parent
067d489141
commit
f7025fd630
@ -41,7 +41,7 @@ static char *old_dlsearchpath;
|
||||
* @return #GNUNET_OK on success
|
||||
*/
|
||||
int
|
||||
TALER_MINT_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg)
|
||||
TALER_MINT_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg)
|
||||
{
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ extern struct TALER_MINTDB_Plugin *plugin;
|
||||
* @return #GNUNET_OK on success
|
||||
*/
|
||||
int
|
||||
TALER_MINT_plugin_load (struct GNUNET_CONFIGURATION_Handle *cfg);
|
||||
TALER_MINT_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -148,14 +148,15 @@ postgres_drop_temporary (void *cls,
|
||||
/**
|
||||
* Create the necessary tables if they are not present
|
||||
*
|
||||
* @param pc our overall context
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param temporary should we use a temporary schema
|
||||
* @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
|
||||
*/
|
||||
static int
|
||||
postgres_create_tables (struct PostgresClosure *pc,
|
||||
postgres_create_tables (void *cls,
|
||||
int temporary)
|
||||
{
|
||||
struct PostgresClosure *pc = cls;
|
||||
PGresult *result;
|
||||
PGconn *conn;
|
||||
|
||||
@ -168,8 +169,8 @@ postgres_create_tables (struct PostgresClosure *pc,
|
||||
GNUNET_break (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if ((GNUNET_YES == temporary)
|
||||
&& (GNUNET_SYSERR == set_temporary_schema (conn)))
|
||||
if ( (GNUNET_YES == temporary) &&
|
||||
(GNUNET_SYSERR == set_temporary_schema (conn)))
|
||||
{
|
||||
PQfinish (conn);
|
||||
return GNUNET_SYSERR;
|
||||
@ -287,6 +288,7 @@ postgres_create_tables (struct PostgresClosure *pc,
|
||||
",wire TEXT NOT NULL"
|
||||
")");
|
||||
#undef SQLEXEC
|
||||
|
||||
PQfinish (conn);
|
||||
return GNUNET_OK;
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#ifndef TALER_MINT_HTTPD_H
|
||||
#define TALER_MINT_HTTPD_H
|
||||
|
||||
#include <microhttpd.h>
|
||||
|
||||
/**
|
||||
* Cut-and-choose size for refreshing.
|
||||
|
@ -13,15 +13,13 @@
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mint/test_mint_db.c
|
||||
* @brief test cases for DB interaction functions
|
||||
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "mint_db.h"
|
||||
#include "plugin.h"
|
||||
|
||||
static int result;
|
||||
|
||||
@ -45,7 +43,7 @@ static int result;
|
||||
/**
|
||||
* Checks if the given reserve has the given amount of balance and expiry
|
||||
*
|
||||
* @param db the database connection
|
||||
* @param session the database connection
|
||||
* @param pub the public key of the reserve
|
||||
* @param value balance value
|
||||
* @param fraction balance fraction
|
||||
@ -54,16 +52,21 @@ static int result;
|
||||
* @return #GNUNET_OK if the given reserve has the same balance and expiration
|
||||
* as the given parameters; #GNUNET_SYSERR if not
|
||||
*/
|
||||
int
|
||||
check_reserve (PGconn *db,
|
||||
static int
|
||||
check_reserve (struct TALER_MINTDB_Session *session,
|
||||
struct GNUNET_CRYPTO_EddsaPublicKey *pub,
|
||||
uint32_t value, uint32_t fraction, const char *currency,
|
||||
uint32_t value,
|
||||
uint32_t fraction,
|
||||
const char *currency,
|
||||
uint64_t expiry)
|
||||
{
|
||||
struct Reserve reserve;
|
||||
reserve.pub = pub;
|
||||
|
||||
FAILIF (GNUNET_OK != TALER_MINT_DB_reserve_get (db, &reserve));
|
||||
FAILIF (GNUNET_OK !=
|
||||
plugin->reserve_get (plugin->cls,
|
||||
session,
|
||||
&reserve));
|
||||
FAILIF (value != reserve.balance.value);
|
||||
FAILIF (fraction != reserve.balance.fraction);
|
||||
FAILIF (0 != strcmp (currency, reserve.balance.currency));
|
||||
@ -81,7 +84,8 @@ struct DenomKeyPair
|
||||
struct GNUNET_CRYPTO_rsa_PublicKey *pub;
|
||||
};
|
||||
|
||||
struct DenomKeyPair *
|
||||
|
||||
static struct DenomKeyPair *
|
||||
create_denom_key_pair (unsigned int size)
|
||||
{
|
||||
struct DenomKeyPair *dkp;
|
||||
@ -93,6 +97,7 @@ create_denom_key_pair (unsigned int size)
|
||||
return dkp;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
destroy_denon_key_pair (struct DenomKeyPair *dkp)
|
||||
{
|
||||
@ -107,13 +112,15 @@ destroy_denon_key_pair (struct DenomKeyPair *dkp)
|
||||
* @param cls closure
|
||||
* @param args remaining command-line arguments
|
||||
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
|
||||
* @param config configuration
|
||||
* @param cfg configuration
|
||||
*/
|
||||
static void
|
||||
run (void *cls, char *const *args, const char *cfgfile,
|
||||
const struct GNUNET_CONFIGURATION_Handle *config)
|
||||
run (void *cls,
|
||||
char *const *args,
|
||||
const char *cfgfile,
|
||||
const struct GNUNET_CONFIGURATION_Handle *cfg)
|
||||
{
|
||||
PGconn *db;
|
||||
struct TALER_MINTDB_Session *session;
|
||||
struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub;
|
||||
struct Reserve reserve;
|
||||
struct GNUNET_TIME_Absolute expiry;
|
||||
@ -139,23 +146,27 @@ run (void *cls, char *const *args, const char *cfgfile,
|
||||
\"address\": \"foobar\"}";
|
||||
unsigned int cnt;
|
||||
|
||||
db = NULL;
|
||||
dkp = NULL;
|
||||
rh = NULL;
|
||||
wire = NULL;
|
||||
ZR_BLK (&cbc);
|
||||
ZR_BLK (&cbc2);
|
||||
if (GNUNET_OK != TALER_MINT_DB_init ("postgres:///taler"))
|
||||
if (GNUNET_OK !=
|
||||
TALER_MINT_plugin_load (cfg))
|
||||
{
|
||||
result = 1;
|
||||
return;
|
||||
}
|
||||
if (GNUNET_OK != TALER_MINT_DB_create_tables (GNUNET_YES))
|
||||
if (GNUNET_OK !=
|
||||
plugin->create_tables (plugin->cls,
|
||||
GNUNET_YES))
|
||||
{
|
||||
result = 2;
|
||||
goto drop;
|
||||
}
|
||||
if (NULL == (db = TALER_MINT_DB_get_connection(GNUNET_YES)))
|
||||
if (NULL ==
|
||||
(session = plugin->get_session (plugin->cls,
|
||||
GNUNET_YES)))
|
||||
{
|
||||
result = 3;
|
||||
goto drop;
|
||||
@ -168,60 +179,85 @@ run (void *cls, char *const *args, const char *cfgfile,
|
||||
expiry = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (),
|
||||
GNUNET_TIME_UNIT_HOURS);
|
||||
result = 4;
|
||||
FAILIF (GNUNET_OK != TALER_MINT_DB_reserves_in_insert (db,
|
||||
&reserve,
|
||||
&amount,
|
||||
expiry));
|
||||
FAILIF (GNUNET_OK != check_reserve (db,
|
||||
&reserve_pub,
|
||||
amount.value,
|
||||
amount.fraction,
|
||||
amount.currency,
|
||||
expiry.abs_value_us));
|
||||
FAILIF (GNUNET_OK != TALER_MINT_DB_reserves_in_insert (db,
|
||||
&reserve,
|
||||
&amount,
|
||||
expiry));
|
||||
FAILIF (GNUNET_OK != check_reserve (db,
|
||||
&reserve_pub,
|
||||
++amount.value,
|
||||
++amount.fraction,
|
||||
amount.currency,
|
||||
expiry.abs_value_us));
|
||||
FAILIF (GNUNET_OK !=
|
||||
plugin->reserves_in_insert (plugin->cls,
|
||||
session,
|
||||
&reserve,
|
||||
&amount,
|
||||
expiry));
|
||||
FAILIF (GNUNET_OK !=
|
||||
check_reserve (session,
|
||||
&reserve_pub,
|
||||
amount.value,
|
||||
amount.fraction,
|
||||
amount.currency,
|
||||
expiry.abs_value_us));
|
||||
FAILIF (GNUNET_OK !=
|
||||
plugin->reserves_in_insert (plugin->cls,
|
||||
session,
|
||||
&reserve,
|
||||
&amount,
|
||||
expiry));
|
||||
FAILIF (GNUNET_OK !=
|
||||
check_reserve (session,
|
||||
&reserve_pub,
|
||||
++amount.value,
|
||||
++amount.fraction,
|
||||
amount.currency,
|
||||
expiry.abs_value_us));
|
||||
dkp = create_denom_key_pair (1024);
|
||||
RND_BLK(&h_blind);
|
||||
RND_BLK(&cbc.reserve_sig);
|
||||
cbc.denom_pub = dkp->pub;
|
||||
cbc.sig = GNUNET_CRYPTO_rsa_sign (dkp->priv, &h_blind, sizeof (h_blind));
|
||||
(void) memcpy (&cbc.reserve_pub, &reserve_pub, sizeof (reserve_pub));
|
||||
(void) memcpy (&cbc.reserve_pub,
|
||||
&reserve_pub,
|
||||
sizeof (reserve_pub));
|
||||
amount.value--;
|
||||
amount.fraction--;
|
||||
FAILIF (GNUNET_OK != TALER_MINT_DB_insert_collectable_blindcoin (db,
|
||||
&h_blind,
|
||||
amount,
|
||||
&cbc));
|
||||
FAILIF (GNUNET_OK != check_reserve (db,
|
||||
&reserve_pub,
|
||||
amount.value,
|
||||
amount.fraction,
|
||||
amount.currency,
|
||||
expiry.abs_value_us));
|
||||
FAILIF (GNUNET_YES != TALER_MINT_DB_get_collectable_blindcoin (db,
|
||||
&h_blind,
|
||||
&cbc2));
|
||||
FAILIF (GNUNET_OK !=
|
||||
plugin->insert_collectable_blindcoin (plugin->cls,
|
||||
session,
|
||||
&h_blind,
|
||||
amount,
|
||||
&cbc));
|
||||
FAILIF (GNUNET_OK !=
|
||||
check_reserve (session,
|
||||
&reserve_pub,
|
||||
amount.value,
|
||||
amount.fraction,
|
||||
amount.currency,
|
||||
expiry.abs_value_us));
|
||||
FAILIF (GNUNET_YES !=
|
||||
plugin->get_collectable_blindcoin (plugin->cls,
|
||||
session,
|
||||
&h_blind,
|
||||
&cbc2));
|
||||
FAILIF (NULL == cbc2.denom_pub);
|
||||
FAILIF (0 != memcmp (&cbc2.reserve_sig, &cbc.reserve_sig, sizeof (cbc2.reserve_sig)));
|
||||
FAILIF (0 != memcmp (&cbc2.reserve_pub, &cbc.reserve_pub, sizeof (cbc2.reserve_pub)));
|
||||
FAILIF (GNUNET_OK != GNUNET_CRYPTO_rsa_verify (&h_blind, cbc2.sig, dkp->pub));
|
||||
rh_head = rh = TALER_MINT_DB_get_reserve_history (db, &reserve_pub);
|
||||
FAILIF (0 != memcmp (&cbc2.reserve_sig,
|
||||
&cbc.reserve_sig,
|
||||
sizeof (cbc2.reserve_sig)));
|
||||
FAILIF (0 != memcmp (&cbc2.reserve_pub,
|
||||
&cbc.reserve_pub,
|
||||
sizeof (cbc2.reserve_pub)));
|
||||
FAILIF (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_rsa_verify (&h_blind,
|
||||
cbc2.sig,
|
||||
dkp->pub));
|
||||
rh = plugin->get_reserve_history (plugin->cls,
|
||||
session,
|
||||
&reserve_pub);
|
||||
FAILIF (NULL == rh);
|
||||
rh_head = rh;
|
||||
for (cnt=0; NULL != rh_head; rh_head=rh_head->next, cnt++)
|
||||
{
|
||||
switch (rh_head->type)
|
||||
{
|
||||
case TALER_MINT_DB_RO_BANK_TO_MINT:
|
||||
bt = rh_head->details.bank;
|
||||
FAILIF (0 != memcmp (&bt->reserve_pub, &reserve_pub, sizeof (reserve_pub)));
|
||||
FAILIF (0 != memcmp (&bt->reserve_pub,
|
||||
&reserve_pub,
|
||||
sizeof (reserve_pub)));
|
||||
FAILIF (1 != bt->amount.value);
|
||||
FAILIF (1 != bt->amount.fraction);
|
||||
FAILIF (0 != strcmp (CURRENCY, bt->amount.currency));
|
||||
@ -251,17 +287,35 @@ run (void *cls, char *const *args, const char *cfgfile,
|
||||
deposit.transaction_id =
|
||||
GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
|
||||
deposit.amount = amount;
|
||||
FAILIF (GNUNET_OK != TALER_MINT_DB_insert_deposit (db, &deposit));
|
||||
FAILIF (GNUNET_YES != TALER_MINT_DB_have_deposit (db, &deposit));
|
||||
(void) memcpy (&deposit2, &deposit, sizeof (deposit));
|
||||
FAILIF (GNUNET_OK !=
|
||||
plugin->insert_deposit (plugin->cls,
|
||||
session, &deposit));
|
||||
FAILIF (GNUNET_YES !=
|
||||
plugin->have_deposit (plugin->cls,
|
||||
session,
|
||||
&deposit));
|
||||
(void) memcpy (&deposit2,
|
||||
&deposit,
|
||||
sizeof (deposit));
|
||||
deposit2.transaction_id++; /* should fail if transaction id is different */
|
||||
FAILIF (GNUNET_NO != TALER_MINT_DB_have_deposit (db, &deposit2));
|
||||
FAILIF (GNUNET_NO !=
|
||||
plugin->have_deposit (plugin->cls,
|
||||
session,
|
||||
&deposit2));
|
||||
deposit2.transaction_id = deposit.transaction_id;
|
||||
RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
|
||||
FAILIF (GNUNET_NO != TALER_MINT_DB_have_deposit (db, &deposit2));
|
||||
(void) memcpy (&deposit2.merchant_pub, &deposit.merchant_pub, sizeof (deposit.merchant_pub));
|
||||
FAILIF (GNUNET_NO !=
|
||||
plugin->have_deposit (plugin->cls,
|
||||
session,
|
||||
&deposit2));
|
||||
(void) memcpy (&deposit2.merchant_pub,
|
||||
&deposit.merchant_pub,
|
||||
sizeof (deposit.merchant_pub));
|
||||
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
|
||||
FAILIF (GNUNET_NO != TALER_MINT_DB_have_deposit (db, &deposit2));
|
||||
FAILIF (GNUNET_NO !=
|
||||
plugin->have_deposit (plugin->cls,
|
||||
session,
|
||||
&deposit2));
|
||||
result = 0;
|
||||
|
||||
drop:
|
||||
@ -270,8 +324,10 @@ run (void *cls, char *const *args, const char *cfgfile,
|
||||
if (NULL != rh)
|
||||
TALER_MINT_DB_free_reserve_history (rh);
|
||||
rh = NULL;
|
||||
if (NULL != db)
|
||||
GNUNET_break (GNUNET_OK == TALER_MINT_DB_drop_temporary (db));
|
||||
if (NULL != session)
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
plugin->drop_temporary (plugin->cls,
|
||||
session));
|
||||
if (NULL != dkp)
|
||||
destroy_denon_key_pair (dkp);
|
||||
if (NULL != cbc.sig)
|
||||
@ -285,7 +341,8 @@ run (void *cls, char *const *args, const char *cfgfile,
|
||||
|
||||
|
||||
int
|
||||
main (int argc, char *const argv[])
|
||||
main (int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
static const struct GNUNET_GETOPT_CommandLineOption options[] = {
|
||||
GNUNET_GETOPT_OPTION_END
|
||||
|
@ -13,17 +13,15 @@
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mint/test_mint_deposits.c
|
||||
* @brief testcase for mint deposits
|
||||
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include <libpq-fe.h>
|
||||
#include <gnunet/gnunet_util_lib.h>
|
||||
#include "mint_db.h"
|
||||
#include "plugin.h"
|
||||
#include "db_pq.h"
|
||||
#include "taler-mint-httpd.h"
|
||||
|
||||
@ -43,11 +41,6 @@
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* DB connection handle
|
||||
*/
|
||||
static PGconn *conn;
|
||||
|
||||
/**
|
||||
* Should we not interact with a temporary table?
|
||||
*/
|
||||
@ -59,64 +52,19 @@ static int persistent;
|
||||
static int result;
|
||||
|
||||
|
||||
int
|
||||
TALER_MINT_DB_init_deposits (PGconn *conn, int tmp)
|
||||
{
|
||||
const char *tmp_str = (1 == tmp) ? "TEMPORARY" : "";
|
||||
char *sql;
|
||||
PGresult *res;
|
||||
int ret;
|
||||
|
||||
res = NULL;
|
||||
(void) GNUNET_asprintf (&sql,
|
||||
"CREATE %1$s TABLE IF NOT EXISTS deposits ("
|
||||
" coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (length(coin_pub)=32)"
|
||||
",denom_pub BYTEA NOT NULL CHECK (length(denom_pub)=32)"
|
||||
",transaction_id INT8 NOT NULL"
|
||||
",amount_value INT4 NOT NULL"
|
||||
",amount_fraction INT4 NOT NULL"
|
||||
",amount_currency VARCHAR(4) NOT NULL"
|
||||
",merchant_pub BYTEA NOT NULL"
|
||||
",h_contract BYTEA NOT NULL CHECK (length(h_contract)=64)"
|
||||
",h_wire BYTEA NOT NULL CHECK (length(h_wire)=64)"
|
||||
",coin_sig BYTEA NOT NULL CHECK (length(coin_sig)=64)"
|
||||
",wire TEXT NOT NULL"
|
||||
")",
|
||||
tmp_str);
|
||||
res = PQexec (conn, sql);
|
||||
GNUNET_free (sql);
|
||||
if (PGRES_COMMAND_OK != PQresultStatus (res))
|
||||
{
|
||||
break_db_err (res);
|
||||
ret = GNUNET_SYSERR;
|
||||
}
|
||||
else
|
||||
ret = GNUNET_OK;
|
||||
PQclear (res);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
|
||||
{
|
||||
if (NULL != conn)
|
||||
PQfinish (conn);
|
||||
conn = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Main function that will be run by the scheduler.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param args remaining command-line arguments
|
||||
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
|
||||
* @param config configuration
|
||||
* @param cfg configuration
|
||||
*/
|
||||
static void
|
||||
run (void *cls, char *const *args, const char *cfgfile,
|
||||
const struct GNUNET_CONFIGURATION_Handle *config)
|
||||
run (void *cls,
|
||||
char *const *args,
|
||||
const char *cfgfile,
|
||||
const struct GNUNET_CONFIGURATION_Handle *cfg)
|
||||
{
|
||||
static const char wire[] = "{"
|
||||
"\"type\":\"SEPA\","
|
||||
@ -126,13 +74,16 @@ run (void *cls, char *const *args, const char *cfgfile,
|
||||
"}";
|
||||
struct Deposit *deposit;
|
||||
uint64_t transaction_id;
|
||||
struct TALER_MINTDB_Session *session;
|
||||
|
||||
deposit = NULL;
|
||||
GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
|
||||
&do_shutdown, NULL);
|
||||
EXITIF (NULL == (conn = PQconnectdb(DB_URI)));
|
||||
EXITIF (GNUNET_OK != TALER_MINT_DB_init_deposits (conn, !persistent));
|
||||
EXITIF (GNUNET_OK != TALER_MINT_DB_prepare (conn));
|
||||
EXITIF (GNUNET_OK != TALER_MINT_plugin_load (cfg));
|
||||
EXITIF (GNUNET_OK !=
|
||||
plugin->create_tables (plugin->cls,
|
||||
! persistent));
|
||||
session = plugin->get_session (plugin->cls,
|
||||
! persistent);
|
||||
EXITIF (NULL == session);
|
||||
deposit = GNUNET_malloc (sizeof (struct Deposit) + sizeof (wire));
|
||||
/* Makeup a random coin public key */
|
||||
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
|
||||
@ -151,20 +102,25 @@ run (void *cls, char *const *args, const char *cfgfile,
|
||||
strcpy (deposit->amount.currency, MINT_CURRENCY);
|
||||
/* Copy wireformat */
|
||||
deposit->wire = json_loads (wire, 0, NULL);
|
||||
EXITIF (GNUNET_OK != TALER_MINT_DB_insert_deposit (conn,
|
||||
deposit));
|
||||
EXITIF (GNUNET_OK != TALER_MINT_DB_have_deposit (conn,
|
||||
deposit));
|
||||
EXITIF (GNUNET_OK !=
|
||||
plugin->insert_deposit (plugin->cls,
|
||||
session,
|
||||
deposit));
|
||||
EXITIF (GNUNET_OK !=
|
||||
plugin->have_deposit (plugin->cls,
|
||||
session,
|
||||
deposit));
|
||||
result = GNUNET_OK;
|
||||
|
||||
EXITIF_exit:
|
||||
GNUNET_free_non_null (deposit);
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char *const argv[])
|
||||
int
|
||||
main (int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
static const struct GNUNET_GETOPT_CommandLineOption options[] = {
|
||||
{'T', "persist", NULL,
|
||||
|
Loading…
Reference in New Issue
Block a user