Patch level :
Files correlati : Ricompilazione Demo : [ ] Commento : Aggiunto comandi di attivazione remota di assistenza, moduli e utenti git-svn-id: svn://10.65.10.50/branches/R_10_00@21360 c028cbd2-c16b-5b4b-a496-9718f37d4682
This commit is contained in:
parent
0f930a1600
commit
471f3a080d
@ -174,13 +174,14 @@ public:
|
|||||||
void GenerateFile(wxString& strFile);
|
void GenerateFile(wxString& strFile);
|
||||||
void ProcessFormCommand(wxString cmd, wxSocketBase& outs);
|
void ProcessFormCommand(wxString cmd, wxSocketBase& outs);
|
||||||
|
|
||||||
void ProcessActivation(int nModuble, bool act, wxSocketBase& outs);
|
void ProcessModuleActivation(int nModuble, bool act, wxSocketBase& outs);
|
||||||
bool ReturnInt(wxSocketBase& outs, unsigned int i);
|
bool ReturnInt(wxSocketBase& outs, unsigned int i);
|
||||||
bool ReturnBool(wxSocketBase&, bool b);
|
bool ReturnBool(wxSocketBase&, bool b);
|
||||||
|
|
||||||
unsigned int DecodePassword(const wxChar* strPassword, const wxChar* strApp);
|
unsigned int DecodePassword(const wxChar* strPassword, const wxChar* strApp);
|
||||||
void ProcessUserLogin(wxString cmd, wxSocketBase& sock);
|
void ProcessUserLogin(wxString cmd, wxSocketBase& sock);
|
||||||
bool ProcessUserLogout(wxString cmd, wxSocketBase& sock);
|
bool ProcessUserLogout(wxString cmd, wxSocketBase& sock);
|
||||||
|
bool ProcessYearActivation(wxString cmd);
|
||||||
|
|
||||||
virtual bool Initialization();
|
virtual bool Initialization();
|
||||||
virtual bool Deinitialization();
|
virtual bool Deinitialization();
|
||||||
@ -303,7 +304,18 @@ void TAuthorizationServer::GenerateIndex(wxString& strFilename)
|
|||||||
TXmlItem& td = tr.AddChild("td").SetAttr("width", "30%");
|
TXmlItem& td = tr.AddChild("td").SetAttr("width", "30%");
|
||||||
const bool hard = m_Dongle.hardware() == _dongle_hardlock;
|
const bool hard = m_Dongle.hardware() == _dongle_hardlock;
|
||||||
TXmlItem& img = td.AddChild("img");
|
TXmlItem& img = td.AddChild("img");
|
||||||
img.SetAttr("src", hard ? "hardlock.gif" : "eutron.gif");
|
switch (m_Dongle.hardware())
|
||||||
|
{
|
||||||
|
case _dongle_aladdin:
|
||||||
|
img.SetAttr("src", "aladdin.gif");
|
||||||
|
break;
|
||||||
|
case _dongle_hardlock:
|
||||||
|
img.SetAttr("src", "hardlock.gif");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
img.SetAttr("src", "eutron.gif");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
tr.AddChild("td").SetAttr("align", "center").AddChild("h1") << (hard ? "Hardlock EYE" : "Eutron Smartkey");
|
tr.AddChild("td").SetAttr("align", "center").AddChild("h1") << (hard ? "Hardlock EYE" : "Eutron Smartkey");
|
||||||
}
|
}
|
||||||
@ -658,7 +670,7 @@ void TAuthorizationServer::GenerateFile(wxString& strFilename)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TAuthorizationServer::ProcessActivation(int nModule, bool act, wxSocketBase& outs)
|
void TAuthorizationServer::ProcessModuleActivation(int nModule, bool act, wxSocketBase& outs)
|
||||||
{
|
{
|
||||||
if (nModule > 0)
|
if (nModule > 0)
|
||||||
{
|
{
|
||||||
@ -703,22 +715,23 @@ void TAuthorizationServer::ProcessFormCommand(wxString cmd, wxSocketBase& outs)
|
|||||||
const wxString gar1 = Garble(nModule, date);
|
const wxString gar1 = Garble(nModule, date);
|
||||||
const wxString gar2 = GarbleMD5(nModule, date);
|
const wxString gar2 = GarbleMD5(nModule, date);
|
||||||
if (KeyIsGood(key, gar1) || KeyIsGood(key, gar2))
|
if (KeyIsGood(key, gar1) || KeyIsGood(key, gar2))
|
||||||
ProcessActivation(nModule, true, outs);
|
ProcessModuleActivation(nModule, true, outs);
|
||||||
else
|
else
|
||||||
MessageBox("ERROR!", "You supplied the wrong activation code", outs);
|
MessageBox("ERROR!", "You supplied the wrong activation code", outs);
|
||||||
} else
|
} else
|
||||||
if (strName == "deactivate")
|
if (strName == "deactivate")
|
||||||
{
|
{
|
||||||
const int nModule = atoi(hashArgs.Get("module"));
|
const int nModule = atoi(hashArgs.Get("module"));
|
||||||
ProcessActivation(nModule, false, outs);
|
ProcessModuleActivation(nModule, false, outs);
|
||||||
} else
|
} else
|
||||||
if (strName == "year")
|
if (strName == "year")
|
||||||
{
|
{
|
||||||
const int year = hashArgs.GetInt("year");
|
const int year = hashArgs.GetInt("year");
|
||||||
const wxDateTime date = hashArgs.GetDate("date");
|
const wxDateTime date = hashArgs.GetDate("date");
|
||||||
const wxString key = hashArgs.Get("key");
|
const wxString key = hashArgs.Get("key");
|
||||||
const wxString gar = Garble(year, date);
|
const wxString gar1 = Garble(year, date);
|
||||||
if (KeyIsGood(key, gar))
|
const wxString gar2 = GarbleMD5(year, date);
|
||||||
|
if (KeyIsGood(key, gar1) || KeyIsGood(key, gar2))
|
||||||
{
|
{
|
||||||
m_Dongle.set_year_assist(year);
|
m_Dongle.set_year_assist(year);
|
||||||
m_Dongle.Burn();
|
m_Dongle.Burn();
|
||||||
@ -734,8 +747,9 @@ void TAuthorizationServer::ProcessFormCommand(wxString cmd, wxSocketBase& outs)
|
|||||||
const int users = atoi(hashArgs.Get("users"));
|
const int users = atoi(hashArgs.Get("users"));
|
||||||
const wxDateTime date = hashArgs.GetDate("date");
|
const wxDateTime date = hashArgs.GetDate("date");
|
||||||
const wxString key = hashArgs.Get("key");
|
const wxString key = hashArgs.Get("key");
|
||||||
const wxString gar = Garble(users, date);
|
const wxString gar1 = Garble(users, date);
|
||||||
if (KeyIsGood(key, gar))
|
const wxString gar2 = GarbleMD5(users, date);
|
||||||
|
if (KeyIsGood(key, gar1) || KeyIsGood(key, gar2))
|
||||||
{
|
{
|
||||||
m_Dongle.set_max_users(users);
|
m_Dongle.set_max_users(users);
|
||||||
m_Dongle.Burn();
|
m_Dongle.Burn();
|
||||||
@ -771,7 +785,7 @@ unsigned int TAuthorizationServer::DecodePassword(const wxChar* strPassword, con
|
|||||||
}
|
}
|
||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
// Per essereva valido deve essere divisibile per 883
|
// Per essere valido deve essere divisibile per 883
|
||||||
if (len >= 5 && (num%883) == 0)
|
if (len >= 5 && (num%883) == 0)
|
||||||
{
|
{
|
||||||
if (m_strLastPassword != strPassword || m_strLastApp != strApp)
|
if (m_strLastPassword != strPassword || m_strLastApp != strApp)
|
||||||
@ -800,9 +814,9 @@ unsigned int TAuthorizationServer::DecodePassword(const wxChar* strPassword, con
|
|||||||
|
|
||||||
void TAuthorizationServer::ProcessUserLogin(wxString cmd, wxSocketBase& sock)
|
void TAuthorizationServer::ProcessUserLogin(wxString cmd, wxSocketBase& sock)
|
||||||
{
|
{
|
||||||
wxChar strUser[32];
|
wxChar strUser[64];
|
||||||
wxChar strPassword[32];
|
wxChar strPassword[64];
|
||||||
wxChar strProgram[32];
|
wxChar strProgram[64];
|
||||||
int session;
|
int session;
|
||||||
|
|
||||||
cmd.Replace(",", " "); cmd.Replace(")", " ");
|
cmd.Replace(",", " "); cmd.Replace(")", " ");
|
||||||
@ -823,7 +837,7 @@ void TAuthorizationServer::ProcessUserLogin(wxString cmd, wxSocketBase& sock)
|
|||||||
{
|
{
|
||||||
if (m_Users.GetLicenses() >= m_Dongle.MaxUsers() && m_Users.Find(sock, NULL, session) == NULL)
|
if (m_Users.GetLicenses() >= m_Dongle.MaxUsers() && m_Users.Find(sock, NULL, session) == NULL)
|
||||||
{
|
{
|
||||||
WriteLog(_("*** Maximum number of users exceeded"));
|
WriteLog(_("*** Maximum number of users exceeded"), 1);
|
||||||
num = 0;
|
num = 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -835,7 +849,7 @@ void TAuthorizationServer::ProcessUserLogin(wxString cmd, wxSocketBase& sock)
|
|||||||
if (num > 0)
|
if (num > 0)
|
||||||
m_Users.AddConnection(sock, strUser, session);
|
m_Users.AddConnection(sock, strUser, session);
|
||||||
else
|
else
|
||||||
WriteLog(_("*** Bad password"));
|
WriteLog(_("*** Bad password"), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -847,8 +861,8 @@ void TAuthorizationServer::ProcessUserLogin(wxString cmd, wxSocketBase& sock)
|
|||||||
|
|
||||||
bool TAuthorizationServer::ProcessUserLogout(wxString cmd, wxSocketBase& sock)
|
bool TAuthorizationServer::ProcessUserLogout(wxString cmd, wxSocketBase& sock)
|
||||||
{
|
{
|
||||||
wxChar strUser[32] = "";
|
wxChar strUser[64] = "";
|
||||||
wxChar strProgram[32] = "";
|
wxChar strProgram[64] = "";
|
||||||
int session = -1;
|
int session = -1;
|
||||||
|
|
||||||
cmd.Replace(",", " "); cmd.Replace(")", " ");
|
cmd.Replace(",", " "); cmd.Replace(")", " ");
|
||||||
@ -876,10 +890,26 @@ bool TAuthorizationServer::ReturnBool(wxSocketBase& outs, bool b)
|
|||||||
return ReturnInt(outs, b ? 1 : 0);
|
return ReturnInt(outs, b ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TAuthorizationServer::ProcessYearActivation(wxString cmd)
|
||||||
|
{
|
||||||
|
char strKey[64] = "";
|
||||||
|
int nYear = 0;
|
||||||
|
bool done = false;
|
||||||
|
cmd.Replace(",", " "); cmd.Replace(")", " ");
|
||||||
|
const int nStr = wxSscanf(cmd, "DongleYearActivate(%d %s)", &nYear, strKey);
|
||||||
|
if (nStr == 2 && nYear >= 2091 && nYear < 3000 && DecodePassword(strKey, "ba1500") > 0)
|
||||||
|
{
|
||||||
|
m_Dongle.set_year_assist(nYear);
|
||||||
|
done = m_Dongle.Burn();
|
||||||
|
}
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
bool TAuthorizationServer::ProcessCommand(wxString cmd, wxSocketBase& outs)
|
bool TAuthorizationServer::ProcessCommand(wxString cmd, wxSocketBase& outs)
|
||||||
{
|
{
|
||||||
if (cmd.StartsWith("GET "))
|
if (cmd.StartsWith("GET "))
|
||||||
{
|
{
|
||||||
|
WriteLog(cmd);
|
||||||
const int stop = cmd.Find(" HTTP");
|
const int stop = cmd.Find(" HTTP");
|
||||||
wxString str;
|
wxString str;
|
||||||
if (stop > 4)
|
if (stop > 4)
|
||||||
@ -899,10 +929,13 @@ bool TAuthorizationServer::ProcessCommand(wxString cmd, wxSocketBase& outs)
|
|||||||
|
|
||||||
if (cmd.StartsWith("POST "))
|
if (cmd.StartsWith("POST "))
|
||||||
{
|
{
|
||||||
|
WriteLog(cmd);
|
||||||
ProcessFormCommand(cmd, outs);
|
ProcessFormCommand(cmd, outs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WriteLog(cmd, 1);
|
||||||
|
|
||||||
if (cmd.StartsWith("UserLogin"))
|
if (cmd.StartsWith("UserLogin"))
|
||||||
{
|
{
|
||||||
ProcessUserLogin(cmd, outs);
|
ProcessUserLogin(cmd, outs);
|
||||||
@ -941,6 +974,9 @@ bool TAuthorizationServer::ProcessCommand(wxString cmd, wxSocketBase& outs)
|
|||||||
if (cmd.StartsWith("DongleNumber"))
|
if (cmd.StartsWith("DongleNumber"))
|
||||||
return ReturnInt(outs, m_Dongle.Number());
|
return ReturnInt(outs, m_Dongle.Number());
|
||||||
|
|
||||||
|
if (cmd.StartsWith("DongleYearActivate"))
|
||||||
|
return ReturnBool(outs, ProcessYearActivation(cmd));
|
||||||
|
|
||||||
if (cmd.StartsWith("DongleYear"))
|
if (cmd.StartsWith("DongleYear"))
|
||||||
return ReturnInt(outs, m_Dongle.YearAssist());
|
return ReturnInt(outs, m_Dongle.YearAssist());
|
||||||
|
|
||||||
|
@ -156,9 +156,10 @@ BEGIN_EVENT_TABLE(TBaseServerApp, wxApp)
|
|||||||
EVT_IDLE(TBaseServerApp::OnIdle)
|
EVT_IDLE(TBaseServerApp::OnIdle)
|
||||||
END_EVENT_TABLE()
|
END_EVENT_TABLE()
|
||||||
|
|
||||||
void TBaseServerApp::WriteLog(const wxChar* str) const
|
void TBaseServerApp::WriteLog(const wxChar* str, int nPriority) const
|
||||||
{
|
{
|
||||||
if (m_log != NULL)
|
const bool doit = (m_log != NULL) && (m_bLogVerbose || nPriority > 0);
|
||||||
|
if (doit)
|
||||||
{
|
{
|
||||||
//raccatta data ed ora
|
//raccatta data ed ora
|
||||||
const wxString strNow = wxNow();
|
const wxString strNow = wxNow();
|
||||||
@ -306,8 +307,7 @@ bool TBaseServerApp::CanProcessCommand(wxString& cmd, wxSocketBase& outs)
|
|||||||
void TBaseServerApp::SendContent(wxFileInputStream& inf, wxSocketBase& sock)
|
void TBaseServerApp::SendContent(wxFileInputStream& inf, wxSocketBase& sock)
|
||||||
{
|
{
|
||||||
const size_t nSize = inf.GetSize();
|
const size_t nSize = inf.GetSize();
|
||||||
if (m_bLogVerbose)
|
WriteLog(wxString::Format("Sending %lu bytes", nSize), 0);
|
||||||
WriteLog(wxString::Format("Sending %lu bytes", nSize));
|
|
||||||
|
|
||||||
const size_t BUF_TEMP_SIZE = 1024*1024; // was 1024*16
|
const size_t BUF_TEMP_SIZE = 1024*1024; // was 1024*16
|
||||||
char* buf = new char[BUF_TEMP_SIZE];
|
char* buf = new char[BUF_TEMP_SIZE];
|
||||||
@ -322,7 +322,7 @@ void TBaseServerApp::SendContent(wxFileInputStream& inf, wxSocketBase& sock)
|
|||||||
delete buf;
|
delete buf;
|
||||||
|
|
||||||
if (nTotalWritten < nSize)
|
if (nTotalWritten < nSize)
|
||||||
WriteLog(wxString::Format("I sent %lu of %lu bytes only.", nTotalWritten, nSize));
|
WriteLog(wxString::Format("I sent %lu of %lu bytes only.", nTotalWritten, nSize), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TBaseServerApp::SendFile(wxString strFilename, wxSocketBase& sock)
|
void TBaseServerApp::SendFile(wxString strFilename, wxSocketBase& sock)
|
||||||
@ -458,6 +458,8 @@ const wxChar* TBaseServerApp::GetAppName() const
|
|||||||
|
|
||||||
bool TBaseServerApp::ProcessCommand(wxString cmd, wxSocketBase& outs)
|
bool TBaseServerApp::ProcessCommand(wxString cmd, wxSocketBase& outs)
|
||||||
{
|
{
|
||||||
|
WriteLog(cmd, 0);
|
||||||
|
|
||||||
bool bProcessed = true;
|
bool bProcessed = true;
|
||||||
if (cmd.StartsWith("POST "))
|
if (cmd.StartsWith("POST "))
|
||||||
{
|
{
|
||||||
@ -492,15 +494,14 @@ void TBaseServerApp::OnServerEvent(wxSocketEvent& e)
|
|||||||
sock->SetEventHandler(*this, SOCKET_ID);
|
sock->SetEventHandler(*this, SOCKET_ID);
|
||||||
sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
|
sock->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
|
||||||
sock->Notify(true);
|
sock->Notify(true);
|
||||||
if (m_bLogVerbose)
|
|
||||||
WriteLog(_("Connection accepted."));
|
WriteLog(_("Connection accepted."));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
WriteLog(_("Connection REFUSED!"));
|
WriteLog(_("Connection REFUSED!"), 1);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
WriteLog(_("Unhandled server event!"));
|
WriteLog(_("Unhandled server event!"), 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -541,8 +542,7 @@ void TBaseServerApp::OnSocketEvent(wxSocketEvent& e)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case wxSOCKET_LOST:
|
case wxSOCKET_LOST:
|
||||||
if (m_bLogVerbose)
|
WriteLog(_("--- Socket lost."), 0);
|
||||||
WriteLog("--- Socket lost.");
|
|
||||||
if (sock != NULL)
|
if (sock != NULL)
|
||||||
{
|
{
|
||||||
for (int i = m_Sockets.GetCount()-1; i >= 0; i--)
|
for (int i = m_Sockets.GetCount()-1; i >= 0; i--)
|
||||||
@ -575,9 +575,6 @@ void TBaseServerApp::OnIdle(wxIdleEvent& evt)
|
|||||||
sock.SetNotify(wxSOCKET_LOST_FLAG);
|
sock.SetNotify(wxSOCKET_LOST_FLAG);
|
||||||
|
|
||||||
wxString& str = cmd.m_Command;
|
wxString& str = cmd.m_Command;
|
||||||
//scrive sul log solo se chiacchierone!
|
|
||||||
if (m_bLogVerbose)
|
|
||||||
WriteLog(str);
|
|
||||||
|
|
||||||
if (CanProcessCommand(str, sock))
|
if (CanProcessCommand(str, sock))
|
||||||
{
|
{
|
||||||
|
@ -151,7 +151,7 @@ public:
|
|||||||
wxString UnformatString(const wxString& strFormString) const;
|
wxString UnformatString(const wxString& strFormString) const;
|
||||||
size_t ParseArguments(wxString args, THashTable& hashArgs) const;
|
size_t ParseArguments(wxString args, THashTable& hashArgs) const;
|
||||||
|
|
||||||
virtual void WriteLog(const wxChar* str) const; // Writes on log file if present
|
virtual void WriteLog(const wxChar* str, int nPriority = 0) const; // Writes on log file if present
|
||||||
virtual bool Ok() const { return m_server != NULL && m_server->Ok(); }
|
virtual bool Ok() const { return m_server != NULL && m_server->Ok(); }
|
||||||
virtual bool Initialization() { return true; }
|
virtual bool Initialization() { return true; }
|
||||||
virtual bool Deinitialization() { return true; }
|
virtual bool Deinitialization() { return true; }
|
||||||
|
@ -744,7 +744,7 @@ bool TDongle::read_words(unsigned short reg, unsigned short len, unsigned short*
|
|||||||
memcpy(ud, &_eutron_key->data[4], (*number)*2);
|
memcpy(ud, &_eutron_key->data[4], (*number)*2);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GetServerApp().WriteLog("*** EUTRON read error");
|
GetServerApp().WriteLog("*** EUTRON read error", 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
len -= *number;
|
len -= *number;
|
||||||
@ -776,7 +776,7 @@ bool TDongle::write_words(unsigned short reg, unsigned short len, unsigned short
|
|||||||
err = HL_WRITE(address, data[r]);
|
err = HL_WRITE(address, data[r]);
|
||||||
if (err != STATUS_OK)
|
if (err != STATUS_OK)
|
||||||
{
|
{
|
||||||
GetServerApp().WriteLog("*** HARDLOCK write error");
|
GetServerApp().WriteLog("*** HARDLOCK write error", 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -801,7 +801,7 @@ bool TDongle::write_words(unsigned short reg, unsigned short len, unsigned short
|
|||||||
ok = _eutron_key->status == ST_OK;
|
ok = _eutron_key->status == ST_OK;
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
GetServerApp().WriteLog("*** EUTRON write error");
|
GetServerApp().WriteLog("*** EUTRON write error", 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
reg += *number;
|
reg += *number;
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include <wx/datetime.h>
|
#include <wx/datetime.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum TDongleHardware { _dongle_unknown, _dongle_hardlock, _dongle_eutron };
|
enum TDongleHardware { _dongle_unknown, _dongle_hardlock, _dongle_eutron, _dongle_aladdin };
|
||||||
enum TDongleType { _no_dongle, _user_dongle, _developer_dongle, _aga_dongle, _prassi_dongle, _procom_dongle };
|
enum TDongleType { _no_dongle, _user_dongle, _developer_dongle, _aga_dongle, _prassi_dongle, _procom_dongle };
|
||||||
enum { MAX_DONGLE_ASSIST = 8 };
|
enum { MAX_DONGLE_ASSIST = 8 };
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ void TLurchServer::GenerateFile(wxString& strFilename)
|
|||||||
{
|
{
|
||||||
const wxString& strApp = arr[i];
|
const wxString& strApp = arr[i];
|
||||||
const bool bLurch = strApp == strLurchName;
|
const bool bLurch = strApp == strLurchName;
|
||||||
const wxString strHost = GetConfigString("Host", "127.0.0.1", strApp);
|
const wxString strHost = GetConfigString("Host", wxGetHostName(), strApp);
|
||||||
const int nPort = GetConfigInt("Port", 3883, strApp);
|
const int nPort = GetConfigInt("Port", 3883, strApp);
|
||||||
const wxString strIcon = GetConfigString("Icon", "euro.gif", strApp);
|
const wxString strIcon = GetConfigString("Icon", "euro.gif", strApp);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user