BixVReader: Add multi application support for IoRequests (#114)
fixes https://github.com/frankmorgner/vsmartcard/issues/105
This commit is contained in:
@@ -169,13 +169,17 @@ DWORD PipeReader::startServer() {
|
||||
}
|
||||
pipe=_pipe;
|
||||
eventpipe=_eventpipe;
|
||||
if (waitInsertIpr!=NULL) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (!waitInsertIpr.empty()) {
|
||||
// if I'm waiting for card insertion, verify if there's a card present
|
||||
if (initProtocols()) {
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK)
|
||||
waitInsertIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitInsertIpr=NULL;
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
}
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
state=SCARD_SWALLOWED;
|
||||
}
|
||||
}
|
||||
@@ -190,25 +194,34 @@ DWORD PipeReader::startServer() {
|
||||
powered=0;
|
||||
pipe=NULL;
|
||||
eventpipe=NULL;
|
||||
if (waitRemoveIpr!=NULL) {// card inserted
|
||||
if (!waitRemoveIpr.empty()) {
|
||||
// card inserted
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
OutputDebugString(L"[BixVReader]complete Wait Remove");
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Unmarked");
|
||||
waitRemoveIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Completed");
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
OutputDebugString(L"[BixVReader]complete Wait Remove");
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Unmarked");
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Completed");
|
||||
}
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
waitRemoveIpr=NULL;
|
||||
}
|
||||
if (waitInsertIpr!=NULL) {// card removed
|
||||
if (!waitInsertIpr.empty()) {
|
||||
// card removed
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
OutputDebugString(L"[BixVReader]cancel Wait Remove");
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Unmarked");
|
||||
waitInsertIpr->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Cancelled");
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
OutputDebugString(L"[BixVReader]cancel Wait Remove");
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Unmarked");
|
||||
ipr->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Cancelled");
|
||||
}
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
waitInsertIpr=NULL;
|
||||
}
|
||||
DisconnectNamedPipe(_pipe);
|
||||
DisconnectNamedPipe(_eventpipe);
|
||||
@@ -217,22 +230,28 @@ DWORD PipeReader::startServer() {
|
||||
OutputDebugString(L"[BixVReader]Pipe data");
|
||||
if (command==0)
|
||||
powered=0;
|
||||
if (command==0 && waitRemoveIpr!=NULL) {// card removed
|
||||
if (command==0 && !waitRemoveIpr.empty()) {
|
||||
// card removed
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
state=SCARD_ABSENT;
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK) {
|
||||
waitRemoveIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
waitRemoveIpr=NULL;
|
||||
}
|
||||
else if (command==1 && waitInsertIpr!=NULL) {// card inserted
|
||||
else if (command==1 && !waitInsertIpr.empty()) {
|
||||
// card inserted
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
state=SCARD_SWALLOWED;
|
||||
initProtocols();
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK) {
|
||||
waitInsertIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
waitInsertIpr=NULL;
|
||||
}
|
||||
}
|
||||
//}
|
||||
@@ -249,16 +268,18 @@ DWORD PipeReader::startServer() {
|
||||
|
||||
void PipeReader::shutdown() {
|
||||
state=SCARD_ABSENT;
|
||||
if (waitRemoveIpr!=NULL) {
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK)
|
||||
waitRemoveIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitRemoveIpr=NULL;
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
if (waitInsertIpr!=NULL) {
|
||||
while (!waitInsertIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK)
|
||||
waitInsertIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitInsertIpr=NULL;
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ void Reader::IoSmartCardIsPresent(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_
|
||||
else {
|
||||
// there's no smart card present, so leave the request pending; it will be completed later
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
waitInsertIpr=pRequest;
|
||||
waitInsertIpr.push_back(pRequest);
|
||||
IRequestCallbackCancel *callback;
|
||||
device->QueryInterface(__uuidof(IRequestCallbackCancel),(void**)&callback);
|
||||
pRequest->MarkCancelable(callback);
|
||||
@@ -54,7 +54,7 @@ void Reader::IoSmartCardIsAbsent(IWDFIoRequest* pRequest,SIZE_T inBufSize,SIZE_T
|
||||
else {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
// there's a smart card present, so leave the request pending; it will be completed later
|
||||
waitRemoveIpr=pRequest;
|
||||
waitRemoveIpr.push_back(pRequest);
|
||||
IRequestCallbackCancel *callback;
|
||||
|
||||
device->QueryInterface(__uuidof(IRequestCallbackCancel),(void**)&callback);
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <Winsock2.h>
|
||||
#include <vector>
|
||||
|
||||
class CMyDevice;
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
CMyDevice *device;
|
||||
CComPtr<IWDFIoRequest> waitRemoveIpr;
|
||||
CComPtr<IWDFIoRequest> waitInsertIpr;
|
||||
std::vector< CComPtr<IWDFIoRequest> > waitRemoveIpr;
|
||||
std::vector< CComPtr<IWDFIoRequest> > waitInsertIpr;
|
||||
inbuf;
|
||||
outbuf;
|
||||
|
||||
|
||||
@@ -205,13 +205,17 @@ DWORD TcpIpReader::startServer() {
|
||||
swprintf(log,L"[BixVReader]Event Socket connected:%i",AcceptEventSocket);
|
||||
OutputDebugString(log);
|
||||
|
||||
if (waitInsertIpr!=NULL) {
|
||||
if (!waitInsertIpr.empty()) {
|
||||
// if I'm waiting for card insertion, verify if there's a card present
|
||||
if (initProtocols()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK)
|
||||
waitInsertIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitInsertIpr=NULL;
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
}
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
state=SCARD_SWALLOWED;
|
||||
}
|
||||
}
|
||||
@@ -226,45 +230,62 @@ DWORD TcpIpReader::startServer() {
|
||||
powered=0;
|
||||
::shutdown(AcceptSocket,SD_BOTH);
|
||||
::shutdown(AcceptEventSocket,SD_BOTH);
|
||||
if (waitRemoveIpr!=NULL) {// card inserted
|
||||
OutputDebugString(L"[BixVReader]complete Wait Remove");
|
||||
if (!waitRemoveIpr.empty()) {
|
||||
// card inserted
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Unmarked");
|
||||
waitRemoveIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Completed");
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
OutputDebugString(L"[BixVReader]complete Wait Remove");
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Unmarked");
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Remove Completed");
|
||||
}
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
waitRemoveIpr=NULL;
|
||||
}
|
||||
if (waitInsertIpr!=NULL) {// card removed
|
||||
OutputDebugString(L"[BixVReader]cancel Wait Remove");
|
||||
if (!waitInsertIpr.empty()) {
|
||||
// card removed
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Unmarked");
|
||||
waitInsertIpr->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Cancelled");
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
OutputDebugString(L"[BixVReader]cancel Wait Remove");
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Unmarked");
|
||||
ipr->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
|
||||
OutputDebugString(L"[BixVReader]Wait Insert Cancelled");
|
||||
}
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
waitInsertIpr=NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
OutputDebugString(L"[BixVReader]Socket data");
|
||||
if (command==0)
|
||||
powered=0;
|
||||
if (command==0 && waitRemoveIpr!=NULL) {// card removed
|
||||
if (command==0 && !waitRemoveIpr.empty()) {
|
||||
// card removed
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
state=SCARD_ABSENT;
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK)
|
||||
waitRemoveIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitRemoveIpr=NULL;
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
}
|
||||
else if (command==1 && waitInsertIpr!=NULL) {// card inserted
|
||||
else if (command==1 && !waitInsertIpr.empty()) {
|
||||
// card inserted
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
state=SCARD_SWALLOWED;
|
||||
initProtocols();
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK)
|
||||
waitInsertIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitInsertIpr=NULL;
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
@@ -291,16 +312,18 @@ void TcpIpReader::shutdown() {
|
||||
serverThread=NULL;
|
||||
eventsocket=NULL;
|
||||
socket=NULL;
|
||||
if (waitRemoveIpr!=NULL) {
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK)
|
||||
waitRemoveIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitRemoveIpr=NULL;
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
if (waitInsertIpr!=NULL) {
|
||||
while (!waitInsertIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK)
|
||||
waitInsertIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitInsertIpr=NULL;
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,17 +106,21 @@ void VpcdReader::shutdown() {
|
||||
vicc_exit((struct vicc_ctx *) ctx);
|
||||
state=SCARD_ABSENT;
|
||||
ctx = NULL;
|
||||
if (waitRemoveIpr!=NULL) {
|
||||
if (!waitRemoveIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK)
|
||||
waitRemoveIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitRemoveIpr=NULL;
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
}
|
||||
if (waitInsertIpr!=NULL) {
|
||||
while (!waitInsertIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK)
|
||||
waitInsertIpr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitInsertIpr=NULL;
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK)
|
||||
ipr->Complete(HRESULT_FROM_WIN32(ERROR_CANCELLED));
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,19 +128,26 @@ void VpcdReader::signalRemoval(void) {
|
||||
if (cardPresent) {
|
||||
cardPresent = false;
|
||||
state=SCARD_ABSENT;
|
||||
if (waitRemoveIpr!=NULL) {
|
||||
if (!waitRemoveIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitRemoveIpr->UnmarkCancelable()==S_OK) {
|
||||
waitRemoveIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
while (!waitRemoveIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitRemoveIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
}
|
||||
waitRemoveIpr.pop_back();
|
||||
}
|
||||
waitRemoveIpr=NULL;
|
||||
}
|
||||
if (waitInsertIpr!=NULL) {
|
||||
if (!waitInsertIpr.empty()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK) {
|
||||
waitInsertIpr->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
ipr->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
|
||||
}
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
waitInsertIpr=NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,12 +155,16 @@ void VpcdReader::signalRemoval(void) {
|
||||
void VpcdReader::signalInsertion(void) {
|
||||
if (!cardPresent) {
|
||||
cardPresent = true;
|
||||
if (waitInsertIpr!=NULL) {
|
||||
while (!waitInsertIpr.empty()) {
|
||||
if (initProtocols()) {
|
||||
SectionLocker lock(device->m_RequestLock);
|
||||
if (waitInsertIpr->UnmarkCancelable()==S_OK)
|
||||
waitInsertIpr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
waitInsertIpr=NULL;
|
||||
while (!waitInsertIpr.empty()) {
|
||||
CComPtr<IWDFIoRequest> ipr = waitInsertIpr.back();
|
||||
if (ipr->UnmarkCancelable()==S_OK) {
|
||||
ipr->CompleteWithInformation(STATUS_SUCCESS, 0);
|
||||
}
|
||||
waitInsertIpr.pop_back();
|
||||
}
|
||||
state=SCARD_SWALLOWED;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,13 +318,19 @@ STDMETHODIMP_ (void) CMyDevice::OnCancel(IN IWDFIoRequest* pWdfRequest) {
|
||||
}
|
||||
Reader &reader=*readers[instance];
|
||||
|
||||
if (pWdfRequest==reader.waitRemoveIpr) {
|
||||
OutputDebugString(L"[BixVReader]Cancel Remove");
|
||||
reader.waitRemoveIpr=NULL;
|
||||
for (std::vector< CComPtr<IWDFIoRequest> >::iterator it = reader.waitRemoveIpr.begin();
|
||||
it != reader.waitRemoveIpr.end(); it++) {
|
||||
if (pWdfRequest == *it) {
|
||||
OutputDebugString(L"[BixVReader]Cancel Remove");
|
||||
reader.waitRemoveIpr.erase(it);
|
||||
}
|
||||
}
|
||||
else if (pWdfRequest==reader.waitInsertIpr) {
|
||||
OutputDebugString(L"[BixVReader]Cancel Insert");
|
||||
reader.waitInsertIpr=NULL;
|
||||
for (std::vector< CComPtr<IWDFIoRequest> >::iterator it = reader.waitInsertIpr.begin();
|
||||
it != reader.waitInsertIpr.end(); it++) {
|
||||
if (pWdfRequest == *it) {
|
||||
OutputDebugString(L"[BixVReader]Cancel Insert");
|
||||
reader.waitInsertIpr.erase(it);
|
||||
}
|
||||
}
|
||||
pWdfRequest->CompleteWithInformation(HRESULT_FROM_WIN32(ERROR_CANCELLED), 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user