mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2025-12-26 14:41:25 +00:00
main-channel: Automatic convert functions to methods
Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
This commit is contained in:
parent
fef97b14e5
commit
9fd105e925
@ -380,7 +380,7 @@ void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
|
||||
|
||||
mcc->priv->mig_wait_connect = FALSE;
|
||||
mcc->priv->mig_connect_ok = success;
|
||||
main_channel_on_migrate_connected(channel, success, seamless);
|
||||
channel->on_migrate_connected(success, seamless);
|
||||
} else {
|
||||
if (success) {
|
||||
mcc->pipe_add_empty_msg(SPICE_MSG_MAIN_MIGRATE_CANCEL);
|
||||
@ -743,7 +743,7 @@ static void main_channel_marshall_notify(RedChannelClient *rcc,
|
||||
static void main_channel_fill_migrate_dst_info(MainChannel *main_channel,
|
||||
SpiceMigrationDstInfo *dst_info)
|
||||
{
|
||||
const RedsMigSpice *mig_dst = main_channel_get_migration_target(main_channel);
|
||||
const RedsMigSpice *mig_dst = main_channel->get_migration_target();
|
||||
dst_info->port = mig_dst->port;
|
||||
dst_info->sport = mig_dst->sport;
|
||||
dst_info->host_size = strlen(mig_dst->host) + 1;
|
||||
@ -800,7 +800,7 @@ static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, MainChannel
|
||||
const RedsMigSpice *mig_target;
|
||||
|
||||
rcc->init_send_data(SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST);
|
||||
mig_target = main_channel_get_migration_target(channel);
|
||||
mig_target = channel->get_migration_target();
|
||||
migrate.port = mig_target->port;
|
||||
migrate.sport = mig_target->sport;
|
||||
migrate.host_size = strlen(mig_target->host) + 1;
|
||||
|
||||
@ -26,18 +26,13 @@
|
||||
#include "main-channel.h"
|
||||
#include "main-channel-client.h"
|
||||
|
||||
int main_channel_is_connected(MainChannel *main_chan)
|
||||
{
|
||||
return main_chan && main_chan->is_connected();
|
||||
}
|
||||
|
||||
XXX_CAST(RedChannelClient, MainChannelClient, MAIN_CHANNEL_CLIENT);
|
||||
|
||||
RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
|
||||
RedClient *MainChannel::get_client_by_link_id(uint32_t connection_id)
|
||||
{
|
||||
RedChannelClient *rcc;
|
||||
|
||||
FOREACH_CLIENT(main_chan, rcc) {
|
||||
FOREACH_CLIENT(this, rcc) {
|
||||
MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
|
||||
if (main_channel_client_get_connection_id(mcc) == connection_id) {
|
||||
return rcc->get_client();
|
||||
@ -57,16 +52,16 @@ static void main_channel_push_channels(MainChannelClient *mcc)
|
||||
mcc->pipe_add_type(RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
|
||||
}
|
||||
|
||||
void main_channel_push_mouse_mode(MainChannel *main_chan, SpiceMouseMode current_mode,
|
||||
void MainChannel::push_mouse_mode(SpiceMouseMode current_mode,
|
||||
int is_client_mouse_allowed)
|
||||
{
|
||||
main_chan->pipes_add(main_mouse_mode_item_new(current_mode, is_client_mouse_allowed));
|
||||
pipes_add(main_mouse_mode_item_new(current_mode, is_client_mouse_allowed));
|
||||
}
|
||||
|
||||
void main_channel_push_agent_connected(MainChannel *main_chan)
|
||||
void MainChannel::push_agent_connected()
|
||||
{
|
||||
RedChannelClient *rcc;
|
||||
FOREACH_CLIENT(main_chan, rcc) {
|
||||
FOREACH_CLIENT(this, rcc) {
|
||||
if (rcc->test_remote_cap(SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) {
|
||||
rcc->pipe_add_type(RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS);
|
||||
} else {
|
||||
@ -75,9 +70,9 @@ void main_channel_push_agent_connected(MainChannel *main_chan)
|
||||
}
|
||||
}
|
||||
|
||||
void main_channel_push_agent_disconnected(MainChannel *main_chan)
|
||||
void MainChannel::push_agent_disconnected()
|
||||
{
|
||||
main_chan->pipes_add_type(RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
|
||||
pipes_add_type(RED_PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
|
||||
}
|
||||
|
||||
static void main_channel_push_migrate_data_item(MainChannel *main_chan)
|
||||
@ -109,9 +104,9 @@ bool MainChannelClient::handle_migrate_data(uint32_t size, void *message)
|
||||
size);
|
||||
}
|
||||
|
||||
void main_channel_push_multi_media_time(MainChannel *main_chan, uint32_t time)
|
||||
void MainChannel::push_multi_media_time(uint32_t time)
|
||||
{
|
||||
main_chan->pipes_add(main_multi_media_time_item_new(time));
|
||||
pipes_add(main_multi_media_time_item_new(time));
|
||||
}
|
||||
|
||||
static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
|
||||
@ -130,15 +125,15 @@ static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice
|
||||
}
|
||||
|
||||
void
|
||||
main_channel_registered_new_channel(MainChannel *main_chan, RedChannel *channel)
|
||||
MainChannel::registered_new_channel(RedChannel *channel)
|
||||
{
|
||||
main_chan->pipes_add(registered_channel_item_new(channel));
|
||||
pipes_add(registered_channel_item_new(channel));
|
||||
}
|
||||
|
||||
void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
|
||||
void MainChannel::migrate_switch(RedsMigSpice *mig_target)
|
||||
{
|
||||
main_channel_fill_mig_target(main_chan, mig_target);
|
||||
main_chan->pipes_add_type(RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
|
||||
main_channel_fill_mig_target(this, mig_target);
|
||||
pipes_add_type(RED_PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
|
||||
}
|
||||
|
||||
bool MainChannelClient::handle_message(uint16_t type, uint32_t size, void *message)
|
||||
@ -259,56 +254,55 @@ static int main_channel_connect_seamless(MainChannel *main_channel)
|
||||
return main_channel->num_clients_mig_wait;
|
||||
}
|
||||
|
||||
int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
|
||||
int try_seamless)
|
||||
int MainChannel::migrate_connect(RedsMigSpice *mig_target, int try_seamless)
|
||||
{
|
||||
main_channel_fill_mig_target(main_channel, mig_target);
|
||||
main_channel->num_clients_mig_wait = 0;
|
||||
main_channel_fill_mig_target(this, mig_target);
|
||||
num_clients_mig_wait = 0;
|
||||
|
||||
if (!main_channel_is_connected(main_channel)) {
|
||||
if (!is_connected()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!try_seamless) {
|
||||
return main_channel_connect_semi_seamless(main_channel);
|
||||
return main_channel_connect_semi_seamless(this);
|
||||
} else {
|
||||
RedChannelClient *rcc;
|
||||
GList *clients = main_channel->get_clients();
|
||||
GList *clients = get_clients();
|
||||
|
||||
/* just test the first one */
|
||||
rcc = (RedChannelClient*) g_list_nth_data(clients, 0);
|
||||
|
||||
if (!rcc->test_remote_cap(SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
|
||||
return main_channel_connect_semi_seamless(main_channel);
|
||||
return main_channel_connect_semi_seamless(this);
|
||||
} else {
|
||||
return main_channel_connect_seamless(main_channel);
|
||||
return main_channel_connect_seamless(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void main_channel_migrate_cancel_wait(MainChannel *main_chan)
|
||||
void MainChannel::migrate_cancel_wait()
|
||||
{
|
||||
RedChannelClient *rcc;
|
||||
|
||||
FOREACH_CLIENT(main_chan, rcc) {
|
||||
FOREACH_CLIENT(this, rcc) {
|
||||
MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
|
||||
main_channel_client_migrate_cancel_wait(mcc);
|
||||
}
|
||||
main_chan->num_clients_mig_wait = 0;
|
||||
num_clients_mig_wait = 0;
|
||||
}
|
||||
|
||||
int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
|
||||
int MainChannel::migrate_src_complete(int success)
|
||||
{
|
||||
int semi_seamless_count = 0;
|
||||
RedChannelClient *rcc;
|
||||
|
||||
if (!main_chan->get_clients()) {
|
||||
red_channel_warning(main_chan, "no peer connected");
|
||||
if (!get_clients()) {
|
||||
red_channel_warning(this, "no peer connected");
|
||||
return 0;
|
||||
}
|
||||
|
||||
FOREACH_CLIENT(main_chan, rcc) {
|
||||
FOREACH_CLIENT(this, rcc) {
|
||||
MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc);
|
||||
if (main_channel_client_migrate_src_complete(mcc, success))
|
||||
semi_seamless_count++;
|
||||
@ -316,18 +310,17 @@ int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
|
||||
return semi_seamless_count;
|
||||
}
|
||||
|
||||
void main_channel_on_migrate_connected(MainChannel *main_channel,
|
||||
gboolean success, gboolean seamless)
|
||||
void MainChannel::on_migrate_connected(gboolean success, gboolean seamless)
|
||||
{
|
||||
spice_assert(main_channel->num_clients_mig_wait);
|
||||
spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
|
||||
if (!--main_channel->num_clients_mig_wait) {
|
||||
reds_on_main_migrate_connected(main_channel->get_server(),
|
||||
spice_assert(num_clients_mig_wait);
|
||||
spice_assert(!seamless || num_clients_mig_wait == 1);
|
||||
if (!--num_clients_mig_wait) {
|
||||
reds_on_main_migrate_connected(get_server(),
|
||||
seamless && success);
|
||||
}
|
||||
}
|
||||
|
||||
const RedsMigSpice* main_channel_get_migration_target(MainChannel *main_chan)
|
||||
const RedsMigSpice* MainChannel::get_migration_target()
|
||||
{
|
||||
return &main_chan->mig_target;
|
||||
return &mig_target;
|
||||
}
|
||||
|
||||
@ -38,8 +38,39 @@ struct RedsMigSpice {
|
||||
int sport;
|
||||
};
|
||||
|
||||
struct MainChannel;
|
||||
|
||||
MainChannel *main_channel_new(RedsState *reds);
|
||||
|
||||
/* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
|
||||
MainChannelClient *main_channel_link(MainChannel *, RedClient *client,
|
||||
RedStream *stream, uint32_t link_id, int migration,
|
||||
RedChannelCapabilities *caps);
|
||||
|
||||
struct MainChannel final: public RedChannel
|
||||
{
|
||||
RedClient *get_client_by_link_id(uint32_t link_id);
|
||||
void push_mouse_mode(SpiceMouseMode current_mode, int is_client_mouse_allowed);
|
||||
void push_agent_connected();
|
||||
void push_agent_disconnected();
|
||||
void push_multi_media_time(uint32_t time);
|
||||
/* tell MainChannel we have a new channel ready */
|
||||
void registered_new_channel(RedChannel *channel);
|
||||
|
||||
/* switch host migration */
|
||||
void migrate_switch(RedsMigSpice *mig_target);
|
||||
|
||||
/* semi seamless migration */
|
||||
|
||||
/* returns the number of clients that we are waiting for their connection.
|
||||
* try_seamless = 'true' when the seamless-migration=on in qemu command line */
|
||||
int migrate_connect(RedsMigSpice *mig_target, int try_seamless);
|
||||
void migrate_cancel_wait();
|
||||
const RedsMigSpice* get_migration_target();
|
||||
/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
|
||||
int migrate_src_complete(int success);
|
||||
void on_migrate_connected(gboolean success, gboolean seamless);
|
||||
|
||||
MainChannel(RedsState *reds);
|
||||
|
||||
void on_connect(RedClient *client, RedStream *stream, int migration,
|
||||
@ -50,39 +81,6 @@ struct MainChannel final: public RedChannel
|
||||
int num_clients_mig_wait;
|
||||
};
|
||||
|
||||
MainChannel *main_channel_new(RedsState *reds);
|
||||
RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
|
||||
/* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
|
||||
MainChannelClient *main_channel_link(MainChannel *, RedClient *client,
|
||||
RedStream *stream, uint32_t link_id, int migration,
|
||||
RedChannelCapabilities *caps);
|
||||
void main_channel_push_mouse_mode(MainChannel *main_chan, SpiceMouseMode current_mode,
|
||||
int is_client_mouse_allowed);
|
||||
void main_channel_push_agent_connected(MainChannel *main_chan);
|
||||
void main_channel_push_agent_disconnected(MainChannel *main_chan);
|
||||
void main_channel_push_multi_media_time(MainChannel *main_chan, uint32_t time);
|
||||
/* tell MainChannel we have a new channel ready */
|
||||
void main_channel_registered_new_channel(MainChannel *main_chan,
|
||||
RedChannel *channel);
|
||||
|
||||
int main_channel_is_connected(MainChannel *main_chan);
|
||||
|
||||
/* switch host migration */
|
||||
void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
|
||||
|
||||
/* semi seamless migration */
|
||||
|
||||
/* returns the number of clients that we are waiting for their connection.
|
||||
* try_seamless = 'true' when the seamless-migration=on in qemu command line */
|
||||
int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
|
||||
int try_seamless);
|
||||
void main_channel_migrate_cancel_wait(MainChannel *main_chan);
|
||||
const RedsMigSpice* main_channel_get_migration_target(MainChannel *main_chan);
|
||||
/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
|
||||
int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
|
||||
void main_channel_on_migrate_connected(MainChannel *main_channel,
|
||||
gboolean success, gboolean seamless);
|
||||
|
||||
#include "pop-visibility.h"
|
||||
|
||||
#endif /* MAIN_CHANNEL_H_ */
|
||||
|
||||
@ -334,7 +334,7 @@ void reds_register_channel(RedsState *reds, RedChannel *channel)
|
||||
}
|
||||
reds->channels = g_list_prepend(reds->channels, channel);
|
||||
// create new channel in the client if possible
|
||||
main_channel_registered_new_channel(reds->main_channel, channel);
|
||||
reds->main_channel->registered_new_channel(channel);
|
||||
}
|
||||
|
||||
void reds_unregister_channel(RedsState *reds, RedChannel *channel)
|
||||
@ -484,7 +484,7 @@ static RedCharDeviceWriteBuffer *vdagent_new_write_buffer(RedCharDeviceVDIPort *
|
||||
|
||||
static int reds_main_channel_connected(RedsState *reds)
|
||||
{
|
||||
return main_channel_is_connected(reds->main_channel);
|
||||
return reds->main_channel && reds->main_channel->is_connected();
|
||||
}
|
||||
|
||||
void reds_client_disconnect(RedsState *reds, RedClient *client)
|
||||
@ -605,7 +605,8 @@ static void reds_set_mouse_mode(RedsState *reds, SpiceMouseMode mode)
|
||||
red_qxl_set_mouse_mode(qxl, mode);
|
||||
}
|
||||
|
||||
main_channel_push_mouse_mode(reds->main_channel, reds->mouse_mode, reds->is_client_mouse_allowed);
|
||||
reds->main_channel->push_mouse_mode(reds->mouse_mode,
|
||||
reds->is_client_mouse_allowed);
|
||||
}
|
||||
|
||||
gboolean reds_config_get_agent_mouse(const RedsState *reds)
|
||||
@ -640,8 +641,8 @@ static void reds_update_mouse_mode(RedsState *reds)
|
||||
return;
|
||||
}
|
||||
if (reds->main_channel) {
|
||||
main_channel_push_mouse_mode(reds->main_channel, reds->mouse_mode,
|
||||
reds->is_client_mouse_allowed);
|
||||
reds->main_channel->push_mouse_mode(reds->mouse_mode,
|
||||
reds->is_client_mouse_allowed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -668,7 +669,7 @@ static void reds_agent_remove(RedsState *reds)
|
||||
reds_update_mouse_mode(reds);
|
||||
if (reds_main_channel_connected(reds) &&
|
||||
!reds->main_channel->is_waiting_for_migrate_data()) {
|
||||
main_channel_push_agent_disconnected(reds->main_channel);
|
||||
reds->main_channel->push_agent_disconnected();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1507,14 +1508,14 @@ bool reds_handle_migrate_data(RedsState *reds, MainChannelClient *mcc,
|
||||
if (agent_dev->priv->agent_attached) { // agent was attached before migration data has arrived
|
||||
if (!reds->vdagent) {
|
||||
spice_assert(agent_dev->priv->plug_generation > 0);
|
||||
main_channel_push_agent_disconnected(reds->main_channel);
|
||||
reds->main_channel->push_agent_disconnected();
|
||||
spice_debug("agent is no longer connected");
|
||||
} else {
|
||||
if (agent_dev->priv->plug_generation > 1) {
|
||||
/* red_char_device_state_reset takes care of not making the device wait for migration data */
|
||||
spice_debug("agent has been detached and reattached before receiving migration data");
|
||||
main_channel_push_agent_disconnected(reds->main_channel);
|
||||
main_channel_push_agent_connected(reds->main_channel);
|
||||
reds->main_channel->push_agent_disconnected();
|
||||
reds->main_channel->push_agent_connected();
|
||||
} else {
|
||||
spice_debug("restoring state from mig_data");
|
||||
return reds_agent_state_restore(reds, mig_data);
|
||||
@ -1532,7 +1533,7 @@ bool reds_handle_migrate_data(RedsState *reds, MainChannelClient *mcc,
|
||||
RedClient *client = mcc->get_client();
|
||||
/* red_char_device_client_remove disables waiting for migration data */
|
||||
red_char_device_client_remove(agent_dev, client);
|
||||
main_channel_push_agent_connected(reds->main_channel);
|
||||
reds->main_channel->push_agent_connected();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2040,8 +2041,7 @@ static void reds_handle_other_links(RedsState *reds, RedLinkInfo *link)
|
||||
|
||||
link_mess = link->link_mess;
|
||||
if (reds->main_channel) {
|
||||
client = main_channel_get_client_by_link_id(reds->main_channel,
|
||||
link_mess->connection_id);
|
||||
client = reds->main_channel->get_client_by_link_id(link_mess->connection_id);
|
||||
}
|
||||
|
||||
// TODO: MC: broke migration (at least for the dont-drop-connection kind).
|
||||
@ -2649,8 +2649,7 @@ static void reds_send_mm_time(RedsState *reds)
|
||||
return;
|
||||
}
|
||||
spice_debug("trace");
|
||||
main_channel_push_multi_media_time(reds->main_channel,
|
||||
reds_get_mm_time() - reds->mm_time_latency);
|
||||
reds->main_channel->push_multi_media_time(reds_get_mm_time() - reds->mm_time_latency);
|
||||
}
|
||||
|
||||
void reds_set_client_mm_time_latency(RedsState *reds, RedClient *client, uint32_t latency)
|
||||
@ -3057,7 +3056,7 @@ static void reds_mig_finished(RedsState *reds, int completed)
|
||||
if (reds->src_do_seamless_migrate && completed) {
|
||||
reds_migrate_channels_seamless(reds);
|
||||
} else {
|
||||
main_channel_migrate_src_complete(reds->main_channel, completed);
|
||||
reds->main_channel->migrate_src_complete(completed);
|
||||
}
|
||||
|
||||
if (completed) {
|
||||
@ -3074,7 +3073,7 @@ static void migrate_timeout(RedsState *reds)
|
||||
spice_assert(reds->mig_wait_connect || reds->mig_wait_disconnect);
|
||||
if (reds->mig_wait_connect) {
|
||||
/* we will fall back to the switch host scheme when migration completes */
|
||||
main_channel_migrate_cancel_wait(reds->main_channel);
|
||||
reds->main_channel->migrate_cancel_wait();
|
||||
/* in case part of the client haven't yet completed the previous migration, disconnect them */
|
||||
reds_mig_target_client_disconnect_all(reds);
|
||||
reds_mig_cleanup(reds);
|
||||
@ -3162,7 +3161,7 @@ static RedCharDevice *attach_to_red_agent(RedsState *reds, SpiceCharDeviceInstan
|
||||
} else {
|
||||
/* we will associate the client with the char device, upon reds_on_main_agent_start,
|
||||
* in response to MSGC_AGENT_START */
|
||||
main_channel_push_agent_connected(reds->main_channel);
|
||||
reds->main_channel->push_agent_connected();
|
||||
}
|
||||
|
||||
return dev;
|
||||
@ -4227,7 +4226,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *reds, const cha
|
||||
|
||||
if (reds->expect_migrate) {
|
||||
spice_debug("consecutive calls without migration. Canceling previous call");
|
||||
main_channel_migrate_src_complete(reds->main_channel, FALSE);
|
||||
reds->main_channel->migrate_src_complete(FALSE);
|
||||
}
|
||||
|
||||
sif = SPICE_UPCAST(SpiceMigrateInterface, reds->migration_interface->base.sif);
|
||||
@ -4250,8 +4249,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_connect(SpiceServer *reds, const cha
|
||||
try_seamless = reds->seamless_migration_enabled &&
|
||||
reds->main_channel->test_remote_cap(SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS);
|
||||
/* main channel will take care of clients that are still during migration (at target)*/
|
||||
if (main_channel_migrate_connect(reds->main_channel, reds->config->mig_spice,
|
||||
try_seamless)) {
|
||||
if (reds->main_channel->migrate_connect(reds->config->mig_spice, try_seamless)) {
|
||||
reds_mig_started(reds);
|
||||
} else {
|
||||
if (reds->clients == NULL) {
|
||||
@ -4329,7 +4327,7 @@ SPICE_GNUC_VISIBLE int spice_server_migrate_switch(SpiceServer *reds)
|
||||
spice_warning("spice_server_migrate_switch called without migrate_info set");
|
||||
return 0;
|
||||
}
|
||||
main_channel_migrate_switch(reds->main_channel, reds->config->mig_spice);
|
||||
reds->main_channel->migrate_switch(reds->config->mig_spice);
|
||||
reds_mig_release(reds->config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user