mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2025-06-03 00:00:20 +00:00
channels: redirection valgrind fixes
This commit is contained in:
parent
9dd77ae14a
commit
6c2ebb2b58
@ -354,17 +354,22 @@ static void audin_alsa_close(IAudinDevice* device)
|
|||||||
|
|
||||||
DEBUG_DVC("");
|
DEBUG_DVC("");
|
||||||
|
|
||||||
SetEvent(alsa->stopEvent);
|
if (alsa->stopEvent)
|
||||||
WaitForSingleObject(alsa->thread, INFINITE);
|
{
|
||||||
CloseHandle(alsa->stopEvent);
|
SetEvent(alsa->stopEvent);
|
||||||
CloseHandle(alsa->thread);
|
WaitForSingleObject(alsa->thread, INFINITE);
|
||||||
|
|
||||||
|
CloseHandle(alsa->stopEvent);
|
||||||
|
alsa->stopEvent = NULL;
|
||||||
|
|
||||||
|
CloseHandle(alsa->thread);
|
||||||
|
alsa->thread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (alsa->buffer)
|
if (alsa->buffer)
|
||||||
free(alsa->buffer);
|
free(alsa->buffer);
|
||||||
alsa->buffer = NULL;
|
alsa->buffer = NULL;
|
||||||
|
|
||||||
alsa->stopEvent = NULL;
|
|
||||||
alsa->thread = NULL;
|
|
||||||
alsa->receive = NULL;
|
alsa->receive = NULL;
|
||||||
alsa->user_data = NULL;
|
alsa->user_data = NULL;
|
||||||
}
|
}
|
||||||
|
@ -417,6 +417,12 @@ static int audin_plugin_terminated(IWTSPlugin* pPlugin)
|
|||||||
audin->device = NULL;
|
audin->device = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(audin->subsystem);
|
||||||
|
audin->subsystem = NULL;
|
||||||
|
|
||||||
|
free(audin->device_name);
|
||||||
|
audin->device_name = NULL;
|
||||||
|
|
||||||
free(audin->listener_callback);
|
free(audin->listener_callback);
|
||||||
free(audin);
|
free(audin);
|
||||||
|
|
||||||
|
@ -581,11 +581,17 @@ static void drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVO
|
|||||||
|
|
||||||
static void drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
|
static void drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
|
||||||
{
|
{
|
||||||
MessagePipe_PostQuit(drdynvc->MsgPipe, 0);
|
if (drdynvc->MsgPipe)
|
||||||
WaitForSingleObject(drdynvc->thread, INFINITE);
|
{
|
||||||
|
MessagePipe_PostQuit(drdynvc->MsgPipe, 0);
|
||||||
|
WaitForSingleObject(drdynvc->thread, INFINITE);
|
||||||
|
|
||||||
MessagePipe_Free(drdynvc->MsgPipe);
|
MessagePipe_Free(drdynvc->MsgPipe);
|
||||||
CloseHandle(drdynvc->thread);
|
drdynvc->MsgPipe = NULL;
|
||||||
|
|
||||||
|
CloseHandle(drdynvc->thread);
|
||||||
|
drdynvc->thread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
drdynvc->channelEntryPoints.pVirtualChannelClose(drdynvc->OpenHandle);
|
drdynvc->channelEntryPoints.pVirtualChannelClose(drdynvc->OpenHandle);
|
||||||
|
|
||||||
|
@ -428,6 +428,7 @@ int dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelI
|
|||||||
IFCALL(context->OnChannelDisconnected, context, channel->channel_name, channel->pInterface);
|
IFCALL(context->OnChannelDisconnected, context, channel->channel_name, channel->pInterface);
|
||||||
|
|
||||||
free(channel->channel_name);
|
free(channel->channel_name);
|
||||||
|
channel->channel_name = NULL;
|
||||||
|
|
||||||
DEBUG_DVC("dvcman_close_channel: channel %d closed", ChannelId);
|
DEBUG_DVC("dvcman_close_channel: channel %d closed", ChannelId);
|
||||||
ichannel = (IWTSVirtualChannel*) channel;
|
ichannel = (IWTSVirtualChannel*) channel;
|
||||||
|
@ -662,7 +662,7 @@ static void rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
|||||||
Stream_Write(s, Stream_Buffer(device->data), data_len);
|
Stream_Write(s, Stream_Buffer(device->data), data_len);
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
WLog_INFO(TAG, "registered device #%d: %s (type=%d id=%d)\n",
|
WLog_INFO(TAG, "registered device #%d: %s (type=%d id=%d)",
|
||||||
count, device->name, device->type, device->id);
|
count, device->name, device->type, device->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
#include <freerdp/channels/rdpdr.h>
|
#include <freerdp/channels/rdpdr.h>
|
||||||
#include <freerdp/channels/log.h>
|
#include <freerdp/channels/log.h>
|
||||||
|
|
||||||
#define TAG CHANNELS_TAG("rdprd.client")
|
#define TAG CHANNELS_TAG("rdpdr.client")
|
||||||
|
|
||||||
typedef struct rdpdr_plugin rdpdrPlugin;
|
typedef struct rdpdr_plugin rdpdrPlugin;
|
||||||
|
|
||||||
|
@ -920,20 +920,20 @@ static void rdpsnd_virtual_channel_event_data_received(rdpsndPlugin* plugin,
|
|||||||
static VOID VCAPITYPE rdpsnd_virtual_channel_open_event(DWORD openHandle, UINT event,
|
static VOID VCAPITYPE rdpsnd_virtual_channel_open_event(DWORD openHandle, UINT event,
|
||||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||||
{
|
{
|
||||||
rdpsndPlugin* plugin;
|
rdpsndPlugin* rdpsnd;
|
||||||
|
|
||||||
plugin = (rdpsndPlugin*) rdpsnd_get_open_handle_data(openHandle);
|
rdpsnd = (rdpsndPlugin*) rdpsnd_get_open_handle_data(openHandle);
|
||||||
|
|
||||||
if (!plugin)
|
if (!rdpsnd)
|
||||||
{
|
{
|
||||||
WLog_ERR(TAG, "rdpsnd_virtual_channel_open_event: error no match\n");
|
WLog_ERR(TAG, "rdpsnd_virtual_channel_open_event: error no match");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (event)
|
switch (event)
|
||||||
{
|
{
|
||||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
case CHANNEL_EVENT_DATA_RECEIVED:
|
||||||
rdpsnd_virtual_channel_event_data_received(plugin, pData, dataLength, totalLength, dataFlags);
|
rdpsnd_virtual_channel_event_data_received(rdpsnd, pData, dataLength, totalLength, dataFlags);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||||
@ -949,16 +949,16 @@ static void* rdpsnd_virtual_channel_client_thread(void* arg)
|
|||||||
{
|
{
|
||||||
wStream* data;
|
wStream* data;
|
||||||
wMessage message;
|
wMessage message;
|
||||||
rdpsndPlugin* plugin = (rdpsndPlugin*) arg;
|
rdpsndPlugin* rdpsnd = (rdpsndPlugin*) arg;
|
||||||
|
|
||||||
rdpsnd_process_connect(plugin);
|
rdpsnd_process_connect(rdpsnd);
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
if (!MessageQueue_Wait(plugin->MsgPipe->In))
|
if (!MessageQueue_Wait(rdpsnd->MsgPipe->In))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (MessageQueue_Peek(plugin->MsgPipe->In, &message, TRUE))
|
if (MessageQueue_Peek(rdpsnd->MsgPipe->In, &message, TRUE))
|
||||||
{
|
{
|
||||||
if (message.id == WMQ_QUIT)
|
if (message.id == WMQ_QUIT)
|
||||||
break;
|
break;
|
||||||
@ -966,11 +966,18 @@ static void* rdpsnd_virtual_channel_client_thread(void* arg)
|
|||||||
if (message.id == 0)
|
if (message.id == 0)
|
||||||
{
|
{
|
||||||
data = (wStream*) message.wParam;
|
data = (wStream*) message.wParam;
|
||||||
rdpsnd_recv_pdu(plugin, data);
|
rdpsnd_recv_pdu(rdpsnd, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rdpsnd->ScheduleThread)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(rdpsnd->ScheduleThread, INFINITE);
|
||||||
|
CloseHandle(rdpsnd->ScheduleThread);
|
||||||
|
rdpsnd->ScheduleThread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ExitThread(0);
|
ExitThread(0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -998,9 +1005,17 @@ static void rdpsnd_virtual_channel_event_connected(rdpsndPlugin* plugin, LPVOID
|
|||||||
|
|
||||||
static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
|
static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
|
||||||
{
|
{
|
||||||
MessagePipe_PostQuit(rdpsnd->MsgPipe, 0);
|
if (rdpsnd->MsgPipe)
|
||||||
WaitForSingleObject(rdpsnd->thread, INFINITE);
|
{
|
||||||
CloseHandle(rdpsnd->thread);
|
MessagePipe_PostQuit(rdpsnd->MsgPipe, 0);
|
||||||
|
WaitForSingleObject(rdpsnd->thread, INFINITE);
|
||||||
|
|
||||||
|
MessagePipe_Free(rdpsnd->MsgPipe);
|
||||||
|
rdpsnd->MsgPipe = NULL;
|
||||||
|
|
||||||
|
CloseHandle(rdpsnd->thread);
|
||||||
|
rdpsnd->thread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
rdpsnd->channelEntryPoints.pVirtualChannelClose(rdpsnd->OpenHandle);
|
rdpsnd->channelEntryPoints.pVirtualChannelClose(rdpsnd->OpenHandle);
|
||||||
|
|
||||||
@ -1010,16 +1025,14 @@ static void rdpsnd_virtual_channel_event_terminated(rdpsndPlugin* rdpsnd)
|
|||||||
rdpsnd->data_in = NULL;
|
rdpsnd->data_in = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MessagePipe_Free(rdpsnd->MsgPipe);
|
|
||||||
|
|
||||||
if (rdpsnd->device)
|
if (rdpsnd->device)
|
||||||
IFCALL(rdpsnd->device->Free, rdpsnd->device);
|
IFCALL(rdpsnd->device->Free, rdpsnd->device);
|
||||||
|
|
||||||
if (rdpsnd->subsystem)
|
free(rdpsnd->subsystem);
|
||||||
free(rdpsnd->subsystem);
|
rdpsnd->subsystem = NULL;
|
||||||
|
|
||||||
if (rdpsnd->device_name)
|
free(rdpsnd->device_name);
|
||||||
free(rdpsnd->device_name);
|
rdpsnd->device_name = NULL;
|
||||||
|
|
||||||
rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
rdpsnd_free_audio_formats(rdpsnd->ServerFormats, rdpsnd->NumberOfServerFormats);
|
||||||
rdpsnd->NumberOfServerFormats = 0;
|
rdpsnd->NumberOfServerFormats = 0;
|
||||||
|
@ -308,26 +308,32 @@ static UINT32 smartcard_ListReadersA_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD
|
|||||||
LPSTR mszReaders = NULL;
|
LPSTR mszReaders = NULL;
|
||||||
DWORD cchReaders = 0;
|
DWORD cchReaders = 0;
|
||||||
IRP* irp = operation->irp;
|
IRP* irp = operation->irp;
|
||||||
|
|
||||||
cchReaders = SCARD_AUTOALLOCATE;
|
cchReaders = SCARD_AUTOALLOCATE;
|
||||||
|
|
||||||
status = ret.ReturnCode = SCardListReadersA(operation->hContext, (LPCSTR) call->mszGroups, (LPSTR) &mszReaders, &cchReaders);
|
status = ret.ReturnCode = SCardListReadersA(operation->hContext, (LPCSTR) call->mszGroups, (LPSTR) &mszReaders, &cchReaders);
|
||||||
|
|
||||||
ret.msz = (BYTE*) mszReaders;
|
ret.msz = (BYTE*) mszReaders;
|
||||||
ret.cBytes = cchReaders;
|
ret.cBytes = cchReaders;
|
||||||
|
|
||||||
if (status)
|
if (call->mszGroups)
|
||||||
|
{
|
||||||
|
free(call->mszGroups);
|
||||||
|
call->mszGroups = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != SCARD_S_SUCCESS)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
smartcard_trace_list_readers_return(smartcard, &ret, FALSE);
|
smartcard_trace_list_readers_return(smartcard, &ret, FALSE);
|
||||||
status = smartcard_pack_list_readers_return(smartcard, irp->output, &ret);
|
status = smartcard_pack_list_readers_return(smartcard, irp->output, &ret);
|
||||||
|
|
||||||
if (status)
|
if (status != SCARD_S_SUCCESS)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
if (mszReaders)
|
if (mszReaders)
|
||||||
SCardFreeMemory(operation->hContext, mszReaders);
|
SCardFreeMemory(operation->hContext, mszReaders);
|
||||||
|
|
||||||
if (call->mszGroups)
|
|
||||||
free(call->mszGroups);
|
|
||||||
|
|
||||||
return ret.ReturnCode;
|
return ret.ReturnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,8 +346,10 @@ static UINT32 smartcard_ListReadersW_Decode(SMARTCARD_DEVICE* smartcard, SMARTCA
|
|||||||
return STATUS_NO_MEMORY;
|
return STATUS_NO_MEMORY;
|
||||||
|
|
||||||
status = smartcard_unpack_list_readers_call(smartcard, irp->input, call);
|
status = smartcard_unpack_list_readers_call(smartcard, irp->input, call);
|
||||||
|
|
||||||
smartcard_trace_list_readers_call(smartcard, call, TRUE);
|
smartcard_trace_list_readers_call(smartcard, call, TRUE);
|
||||||
operation->hContext = smartcard_scard_context_native_from_redir(smartcard, &(call->hContext));
|
operation->hContext = smartcard_scard_context_native_from_redir(smartcard, &(call->hContext));
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,15 +360,26 @@ static UINT32 smartcard_ListReadersW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD
|
|||||||
LPWSTR mszReaders = NULL;
|
LPWSTR mszReaders = NULL;
|
||||||
DWORD cchReaders = 0;
|
DWORD cchReaders = 0;
|
||||||
IRP* irp = operation->irp;
|
IRP* irp = operation->irp;
|
||||||
|
|
||||||
cchReaders = SCARD_AUTOALLOCATE;
|
cchReaders = SCARD_AUTOALLOCATE;
|
||||||
status = ret.ReturnCode = SCardListReadersW(operation->hContext, (LPCWSTR) call->mszGroups, (LPWSTR) &mszReaders, &cchReaders);
|
|
||||||
|
status = ret.ReturnCode = SCardListReadersW(operation->hContext,
|
||||||
|
(LPCWSTR) call->mszGroups, (LPWSTR) &mszReaders, &cchReaders);
|
||||||
|
|
||||||
ret.msz = (BYTE*) mszReaders;
|
ret.msz = (BYTE*) mszReaders;
|
||||||
ret.cBytes = cchReaders * 2;
|
ret.cBytes = cchReaders * 2;
|
||||||
|
|
||||||
|
if (call->mszGroups)
|
||||||
|
{
|
||||||
|
free(call->mszGroups);
|
||||||
|
call->mszGroups = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (status != SCARD_S_SUCCESS)
|
if (status != SCARD_S_SUCCESS)
|
||||||
return status;
|
return status;
|
||||||
|
|
||||||
smartcard_trace_list_readers_return(smartcard, &ret, TRUE);
|
smartcard_trace_list_readers_return(smartcard, &ret, TRUE);
|
||||||
|
|
||||||
status = smartcard_pack_list_readers_return(smartcard, irp->output, &ret);
|
status = smartcard_pack_list_readers_return(smartcard, irp->output, &ret);
|
||||||
|
|
||||||
if (status != SCARD_S_SUCCESS)
|
if (status != SCARD_S_SUCCESS)
|
||||||
@ -369,9 +388,6 @@ static UINT32 smartcard_ListReadersW_Call(SMARTCARD_DEVICE* smartcard, SMARTCARD
|
|||||||
if (mszReaders)
|
if (mszReaders)
|
||||||
SCardFreeMemory(operation->hContext, mszReaders);
|
SCardFreeMemory(operation->hContext, mszReaders);
|
||||||
|
|
||||||
if (call->mszGroups)
|
|
||||||
free(call->mszGroups);
|
|
||||||
|
|
||||||
return ret.ReturnCode;
|
return ret.ReturnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1436,7 +1452,7 @@ UINT32 smartcard_irp_device_control_decode(SMARTCARD_DEVICE* smartcard, SMARTCAR
|
|||||||
{
|
{
|
||||||
offset = (RDPDR_DEVICE_IO_REQUEST_LENGTH + RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH);
|
offset = (RDPDR_DEVICE_IO_REQUEST_LENGTH + RDPDR_DEVICE_IO_CONTROL_REQ_HDR_LENGTH);
|
||||||
smartcard_unpack_read_size_align(smartcard, irp->input,
|
smartcard_unpack_read_size_align(smartcard, irp->input,
|
||||||
Stream_GetPosition(irp->input) - offset, 8);
|
Stream_GetPosition(irp->input) - offset, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (((size_t) Stream_GetPosition(irp->input)) < Stream_Length(irp->input))
|
if (((size_t) Stream_GetPosition(irp->input)) < Stream_Length(irp->input))
|
||||||
@ -1482,6 +1498,7 @@ UINT32 smartcard_irp_device_control_call(SMARTCARD_DEVICE* smartcard, SMARTCARD_
|
|||||||
irp = operation->irp;
|
irp = operation->irp;
|
||||||
call = operation->call;
|
call = operation->call;
|
||||||
ioControlCode = operation->ioControlCode;
|
ioControlCode = operation->ioControlCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* [MS-RDPESC] 3.2.5.1: Sending Outgoing Messages:
|
* [MS-RDPESC] 3.2.5.1: Sending Outgoing Messages:
|
||||||
* the output buffer length SHOULD be set to 2048
|
* the output buffer length SHOULD be set to 2048
|
||||||
@ -1490,6 +1507,7 @@ UINT32 smartcard_irp_device_control_call(SMARTCARD_DEVICE* smartcard, SMARTCARD_
|
|||||||
* about it, but we still reserve at least 2048 bytes.
|
* about it, but we still reserve at least 2048 bytes.
|
||||||
*/
|
*/
|
||||||
Stream_EnsureRemainingCapacity(irp->output, 2048);
|
Stream_EnsureRemainingCapacity(irp->output, 2048);
|
||||||
|
|
||||||
/* Device Control Response */
|
/* Device Control Response */
|
||||||
Stream_Seek_UINT32(irp->output); /* OutputBufferLength (4 bytes) */
|
Stream_Seek_UINT32(irp->output); /* OutputBufferLength (4 bytes) */
|
||||||
Stream_Seek(irp->output, SMARTCARD_COMMON_TYPE_HEADER_LENGTH); /* CommonTypeHeader (8 bytes) */
|
Stream_Seek(irp->output, SMARTCARD_COMMON_TYPE_HEADER_LENGTH); /* CommonTypeHeader (8 bytes) */
|
||||||
|
@ -534,10 +534,9 @@ int uds_connect(const char* path)
|
|||||||
BOOL freerdp_tcp_resolve_hostname(const char* hostname)
|
BOOL freerdp_tcp_resolve_hostname(const char* hostname)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
struct addrinfo hints;
|
struct addrinfo hints = { 0 };
|
||||||
struct addrinfo* result = NULL;
|
struct addrinfo* result = NULL;
|
||||||
|
|
||||||
ZeroMemory(&hints, sizeof(hints));
|
|
||||||
hints.ai_family = AF_UNSPEC;
|
hints.ai_family = AF_UNSPEC;
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
@ -369,6 +369,7 @@ int WLog_ParseFilters()
|
|||||||
DWORD nSize;
|
DWORD nSize;
|
||||||
int status;
|
int status;
|
||||||
char** strs;
|
char** strs;
|
||||||
|
|
||||||
nSize = GetEnvironmentVariableA("WLOG_FILTER", NULL, 0);
|
nSize = GetEnvironmentVariableA("WLOG_FILTER", NULL, 0);
|
||||||
|
|
||||||
if (nSize < 1)
|
if (nSize < 1)
|
||||||
@ -393,6 +394,10 @@ int WLog_ParseFilters()
|
|||||||
p = env;
|
p = env;
|
||||||
count = 0;
|
count = 0;
|
||||||
strs = (char**) calloc(g_FilterCount, sizeof(char*));
|
strs = (char**) calloc(g_FilterCount, sizeof(char*));
|
||||||
|
|
||||||
|
if (!strs)
|
||||||
|
return -1;
|
||||||
|
|
||||||
strs[count++] = p;
|
strs[count++] = p;
|
||||||
|
|
||||||
while ((p = strchr(p, ',')) != NULL)
|
while ((p = strchr(p, ',')) != NULL)
|
||||||
@ -700,9 +705,9 @@ void WLog_Init()
|
|||||||
|
|
||||||
void WLog_Uninit()
|
void WLog_Uninit()
|
||||||
{
|
{
|
||||||
wLog* root = WLog_GetRoot();
|
|
||||||
DWORD index;
|
DWORD index;
|
||||||
wLog* child = NULL;
|
wLog* child = NULL;
|
||||||
|
wLog* root = WLog_GetRoot();
|
||||||
|
|
||||||
for (index = 0; index < root->ChildrenCount; index++)
|
for (index = 0; index < root->ChildrenCount; index++)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user