mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-02-27 10:21:40 -05:00
added load/save to service permission matrix
git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@7906 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
parent
96bf79daae
commit
7642ba1c1f
@ -23,22 +23,207 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "p3servicecontrol.h"
|
#include "p3servicecontrol.h"
|
||||||
#include "serialiser/rsserviceids.h"
|
#include "serialiser/rsserviceids.h"
|
||||||
#include "serialiser/rsserial.h"
|
#include "serialiser/rsserial.h"
|
||||||
|
#include "serialiser/rsbaseserial.h"
|
||||||
|
#include "pqi/p3cfgmgr.h"
|
||||||
|
|
||||||
/*******************************/
|
/*******************************/
|
||||||
// #define SERVICECONTROL_DEBUG 1
|
// #define SERVICECONTROL_DEBUG 1
|
||||||
/*******************************/
|
/*******************************/
|
||||||
|
|
||||||
|
static const uint8_t RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS = 0x01 ;
|
||||||
|
|
||||||
|
class RsServiceControlItem: public RsItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RsServiceControlItem(uint8_t item_subtype) : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_TYPE_SERVICE_CONTROL,item_subtype) {}
|
||||||
|
|
||||||
|
virtual uint32_t serial_size() const =0;
|
||||||
|
virtual bool serialise(uint8_t *data,uint32_t size) const =0;
|
||||||
|
|
||||||
|
bool serialise_header(void *data,uint32_t& pktsize,uint32_t& tlvsize, uint32_t& offset) const
|
||||||
|
{
|
||||||
|
tlvsize = serial_size() ;
|
||||||
|
offset = 0;
|
||||||
|
|
||||||
|
if (pktsize < tlvsize)
|
||||||
|
return false; /* not enough space */
|
||||||
|
|
||||||
|
pktsize = tlvsize;
|
||||||
|
|
||||||
|
if(!setRsItemHeader(data, tlvsize, PacketId(), tlvsize))
|
||||||
|
{
|
||||||
|
std::cerr << "RsFileTransferItem::serialise_header(): ERROR. Not enough size!" << std::endl;
|
||||||
|
return false ;
|
||||||
|
}
|
||||||
|
#ifdef RSSERIAL_DEBUG
|
||||||
|
std::cerr << "RsFileItemSerialiser::serialiseData() Header: " << ok << std::endl;
|
||||||
|
#endif
|
||||||
|
offset += 8;
|
||||||
|
|
||||||
|
return true ;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
class RsServicePermissionItem: public RsServiceControlItem, public RsServicePermissions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RsServicePermissionItem()
|
||||||
|
: RsServiceControlItem(RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS) {}
|
||||||
|
RsServicePermissionItem(const RsServicePermissions& perms)
|
||||||
|
: RsServiceControlItem(RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS),
|
||||||
|
RsServicePermissions(perms) {}
|
||||||
|
|
||||||
|
virtual uint32_t serial_size() const
|
||||||
|
{
|
||||||
|
uint32_t s = 8 ; // header
|
||||||
|
|
||||||
|
s += 4 ; // mServiceId
|
||||||
|
s += GetTlvStringSize(mServiceName) ;
|
||||||
|
s += 1 ; // mDefaultAllowed
|
||||||
|
s += 4 ; // mPeersAllowed.size()
|
||||||
|
s += mPeersAllowed.size() * RsPeerId::serial_size() ;
|
||||||
|
s += 4 ; // mPeersAllowed.size()
|
||||||
|
s += mPeersDenied.size() * RsPeerId::serial_size() ;
|
||||||
|
|
||||||
|
return s ;
|
||||||
|
}
|
||||||
|
virtual bool serialise(uint8_t *data,uint32_t size) const
|
||||||
|
{
|
||||||
|
uint32_t tlvsize,offset=0;
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
if(!serialise_header(data,size,tlvsize,offset))
|
||||||
|
return false ;
|
||||||
|
|
||||||
|
/* add mandatory parts first */
|
||||||
|
ok &= setRawUInt32(data, tlvsize, &offset, mServiceId);
|
||||||
|
ok &= SetTlvString(data, tlvsize, &offset, TLV_TYPE_STR_NAME, mServiceName);
|
||||||
|
ok &= setRawUInt8(data, tlvsize, &offset, mDefaultAllowed);
|
||||||
|
ok &= setRawUInt32(data, tlvsize, &offset, mPeersAllowed.size());
|
||||||
|
|
||||||
|
for(std::set<RsPeerId>::const_iterator it(mPeersAllowed.begin());it!=mPeersAllowed.end();++it)
|
||||||
|
(*it).serialise(data,tlvsize,offset) ;
|
||||||
|
|
||||||
|
ok &= setRawUInt32(data, tlvsize, &offset, mPeersDenied.size());
|
||||||
|
|
||||||
|
for(std::set<RsPeerId>::const_iterator it(mPeersDenied.begin());it!=mPeersDenied.end();++it)
|
||||||
|
(*it).serialise(data,tlvsize,offset) ;
|
||||||
|
|
||||||
|
if(offset != tlvsize)
|
||||||
|
{
|
||||||
|
std::cerr << "RsGxsChannelSerialiser::serialiseGxsChannelGroupItem() FAIL Size Error! " << std::endl;
|
||||||
|
ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok)
|
||||||
|
std::cerr << "RsGxsChannelSerialiser::serialiseGxsChannelGroupItem() NOK" << std::endl;
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RsServicePermissionItem *deserialise(uint8_t *data,uint32_t size)
|
||||||
|
{
|
||||||
|
RsServicePermissionItem *item = new RsServicePermissionItem ;
|
||||||
|
|
||||||
|
uint32_t offset = 8; // skip the header
|
||||||
|
uint32_t rssize = getRsItemSize(data);
|
||||||
|
bool ok = true ;
|
||||||
|
|
||||||
|
/* add mandatory parts first */
|
||||||
|
ok &= getRawUInt32(data, rssize, &offset, &item->mServiceId);
|
||||||
|
ok &= GetTlvString(data, rssize, &offset, TLV_TYPE_STR_NAME, item->mServiceName);
|
||||||
|
|
||||||
|
uint8_t v ;
|
||||||
|
ok &= getRawUInt8(data, rssize, &offset, &v) ;
|
||||||
|
|
||||||
|
if(v != 0 && v != 1)
|
||||||
|
ok = false ;
|
||||||
|
else
|
||||||
|
item->mDefaultAllowed = (bool)v;
|
||||||
|
|
||||||
|
uint32_t tmp ;
|
||||||
|
ok &= getRawUInt32(data, rssize, &offset, &tmp);
|
||||||
|
|
||||||
|
for(int i=0;i<tmp && offset < rssize;++i)
|
||||||
|
{
|
||||||
|
RsPeerId peer_id ;
|
||||||
|
ok &= peer_id.deserialise(data,rssize,offset) ;
|
||||||
|
item->mPeersAllowed.insert(peer_id) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok &= getRawUInt32(data, rssize, &offset, &tmp);
|
||||||
|
|
||||||
|
for(int i=0;i<tmp && offset < rssize;++i)
|
||||||
|
{
|
||||||
|
RsPeerId peer_id ;
|
||||||
|
ok &= peer_id.deserialise(data,rssize,offset) ;
|
||||||
|
item->mPeersDenied.insert(peer_id) ;
|
||||||
|
}
|
||||||
|
if (offset != rssize || !ok)
|
||||||
|
{
|
||||||
|
std::cerr << __PRETTY_FUNCTION__ << ": error while deserialising! Item will be dropped." << std::endl;
|
||||||
|
return NULL ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void clear() {}
|
||||||
|
virtual std::ostream& print(std::ostream& out,uint16_t)
|
||||||
|
{
|
||||||
|
std::cerr << __PRETTY_FUNCTION__ << ": not implemented!" << std::endl;
|
||||||
|
return out ;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ServiceControlSerialiser: public RsSerialType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ServiceControlSerialiser() : RsSerialType(RS_PKT_VERSION_SERVICE, RS_SERVICE_TYPE_SERVICE_CONTROL) {}
|
||||||
|
|
||||||
|
virtual uint32_t size (RsItem *item)
|
||||||
|
{
|
||||||
|
return dynamic_cast<RsServiceControlItem *>(item)->serial_size() ;
|
||||||
|
}
|
||||||
|
virtual bool serialise(RsItem *item, void *data, uint32_t *size)
|
||||||
|
{
|
||||||
|
return dynamic_cast<RsServiceControlItem *>(item)->serialise((uint8_t*)data,*size) ;
|
||||||
|
}
|
||||||
|
virtual RsItem *deserialise (void *data, uint32_t *size)
|
||||||
|
{
|
||||||
|
uint32_t rstype = getRsItemId(data);
|
||||||
|
|
||||||
|
if(RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype) || RS_SERVICE_TYPE_SERVICE_CONTROL != getRsItemService(rstype)) { return NULL; /* wrong type */ }
|
||||||
|
|
||||||
|
switch(getRsItemSubType(rstype))
|
||||||
|
{
|
||||||
|
case RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS:return RsServicePermissionItem::deserialise((uint8_t*)data, *size);
|
||||||
|
default:
|
||||||
|
return NULL ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
RsServiceControl *rsServiceControl = NULL;
|
RsServiceControl *rsServiceControl = NULL;
|
||||||
|
|
||||||
p3ServiceControl::p3ServiceControl(p3LinkMgr *linkMgr)
|
p3ServiceControl::p3ServiceControl(p3LinkMgr *linkMgr)
|
||||||
:RsServiceControl(), /* p3Config(configId), pqiMonitor(), */
|
:RsServiceControl(), p3Config(),
|
||||||
mLinkMgr(linkMgr), mOwnPeerId(linkMgr->getOwnId()),
|
mLinkMgr(linkMgr), mOwnPeerId(linkMgr->getOwnId()),
|
||||||
mCtrlMtx("p3ServiceControl"), mMonitorMtx("P3ServiceControl::Monitor")
|
mCtrlMtx("p3ServiceControl"), mMonitorMtx("P3ServiceControl::Monitor")
|
||||||
{
|
{
|
||||||
|
mSerialiser = new ServiceControlSerialiser ;
|
||||||
|
}
|
||||||
|
|
||||||
|
RsSerialiser *p3ServiceControl::setupSerialiser()
|
||||||
|
{
|
||||||
|
RsSerialiser *serial = new RsSerialiser;
|
||||||
|
serial->addSerialType(new ServiceControlSerialiser) ;
|
||||||
|
|
||||||
|
return serial ;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RsPeerId& p3ServiceControl::getOwnId()
|
const RsPeerId& p3ServiceControl::getOwnId()
|
||||||
@ -249,8 +434,10 @@ bool p3ServiceControl::updateServicesProvided(const RsPeerId &peerId, const RsPe
|
|||||||
std::cerr << std::endl;
|
std::cerr << std::endl;
|
||||||
|
|
||||||
mServicesProvided[peerId] = info;
|
mServicesProvided[peerId] = info;
|
||||||
updateFilterByPeer_locked(peerId);
|
updateFilterByPeer_locked(peerId);
|
||||||
return true;
|
|
||||||
|
IndicateConfigChanged() ;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* External Interface to RsServiceControl */
|
/* External Interface to RsServiceControl */
|
||||||
@ -286,7 +473,8 @@ bool p3ServiceControl::createDefaultPermissions_locked(uint32_t serviceId, std::
|
|||||||
perms.mDefaultAllowed = defaultOn;
|
perms.mDefaultAllowed = defaultOn;
|
||||||
|
|
||||||
mServicePermissionMap[serviceId] = perms;
|
mServicePermissionMap[serviceId] = perms;
|
||||||
return true;
|
IndicateConfigChanged() ;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -332,7 +520,8 @@ bool p3ServiceControl::updateServicePermissions(uint32_t serviceId, const RsServ
|
|||||||
|
|
||||||
// This is overkill - but will update everything.
|
// This is overkill - but will update everything.
|
||||||
updateAllFilters_locked();
|
updateAllFilters_locked();
|
||||||
return true;
|
IndicateConfigChanged() ;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -961,7 +1150,13 @@ bool p3ServiceControl::saveList(bool &cleanup, std::list<RsItem *> &saveList)
|
|||||||
std::cerr << "p3ServiceControl::saveList()";
|
std::cerr << "p3ServiceControl::saveList()";
|
||||||
std::cerr << std::endl;
|
std::cerr << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
cleanup = true ;
|
||||||
|
|
||||||
|
for(std::map<uint32_t, RsServicePermissions>::iterator it(mServicePermissionMap.begin());it!=mServicePermissionMap.end();++it)
|
||||||
|
{
|
||||||
|
RsServicePermissionItem *item = new RsServicePermissionItem(it->second) ;
|
||||||
|
saveList.push_back(item) ;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -971,6 +1166,15 @@ bool p3ServiceControl::loadList(std::list<RsItem *>& loadList)
|
|||||||
std::cerr << "p3ServiceControl::loadList()";
|
std::cerr << "p3ServiceControl::loadList()";
|
||||||
std::cerr << std::endl;
|
std::cerr << std::endl;
|
||||||
#endif
|
#endif
|
||||||
|
for(std::list<RsItem*>::const_iterator it(loadList.begin());it!=loadList.end();++it)
|
||||||
|
{
|
||||||
|
RsServicePermissionItem *item = dynamic_cast<RsServicePermissionItem*>(*it) ;
|
||||||
|
|
||||||
|
if(item == NULL)
|
||||||
|
continue ;
|
||||||
|
|
||||||
|
mServicePermissionMap[item->mServiceId] = *item ;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,9 @@ class ServicePeerFilter
|
|||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, const ServicePeerFilter &filter);
|
std::ostream &operator<<(std::ostream &out, const ServicePeerFilter &filter);
|
||||||
|
|
||||||
class p3ServiceControl: public RsServiceControl, public pqiMonitor /* , public p3Config */
|
class ServiceControlSerialiser ;
|
||||||
|
|
||||||
|
class p3ServiceControl: public RsServiceControl, public pqiMonitor, public p3Config
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -133,6 +135,7 @@ protected:
|
|||||||
// configuration.
|
// configuration.
|
||||||
virtual bool saveList(bool &cleanup, std::list<RsItem *>&);
|
virtual bool saveList(bool &cleanup, std::list<RsItem *>&);
|
||||||
virtual bool loadList(std::list<RsItem *>& load);
|
virtual bool loadList(std::list<RsItem *>& load);
|
||||||
|
virtual RsSerialiser *setupSerialiser() ;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -177,9 +180,6 @@ bool peerHasPermissionForService_locked(const RsPeerId &peerId, uint32_t service
|
|||||||
// derived from all the others.
|
// derived from all the others.
|
||||||
std::map<RsPeerId, ServicePeerFilter> mPeerFilterMap;
|
std::map<RsPeerId, ServicePeerFilter> mPeerFilterMap;
|
||||||
|
|
||||||
// Below here is saved in Configuration.
|
|
||||||
std::map<uint32_t, RsServicePermissions> mServicePermissionMap;
|
|
||||||
|
|
||||||
std::map<uint32_t, ServiceNotifications> mNotifications;
|
std::map<uint32_t, ServiceNotifications> mNotifications;
|
||||||
std::list<pqiServicePeer> mFriendNotifications;
|
std::list<pqiServicePeer> mFriendNotifications;
|
||||||
|
|
||||||
@ -188,7 +188,13 @@ bool peerHasPermissionForService_locked(const RsPeerId &peerId, uint32_t service
|
|||||||
|
|
||||||
// Separate mutex here - must not hold both at the same time!
|
// Separate mutex here - must not hold both at the same time!
|
||||||
RsMutex mMonitorMtx; /* below is protected */
|
RsMutex mMonitorMtx; /* below is protected */
|
||||||
std::multimap<uint32_t, pqiServiceMonitor *> mMonitors;
|
std::multimap<uint32_t, pqiServiceMonitor *> mMonitors;
|
||||||
|
|
||||||
|
ServiceControlSerialiser *mSerialiser ;
|
||||||
|
|
||||||
|
// Below here is saved in Configuration.
|
||||||
|
std::map<uint32_t, RsServicePermissions> mServicePermissionMap;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,23 +81,23 @@ std::ostream &operator<<(std::ostream &out, const RsServiceInfo &info);
|
|||||||
|
|
||||||
class RsServicePermissions
|
class RsServicePermissions
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RsServicePermissions();
|
RsServicePermissions();
|
||||||
|
|
||||||
bool peerHasPermission(const RsPeerId &peerId) const;
|
bool peerHasPermission(const RsPeerId &peerId) const;
|
||||||
|
|
||||||
void setPermission(const RsPeerId& peerId) ;
|
void setPermission(const RsPeerId& peerId) ;
|
||||||
void resetPermission(const RsPeerId& peerId) ;
|
void resetPermission(const RsPeerId& peerId) ;
|
||||||
|
|
||||||
uint32_t mServiceId;
|
uint32_t mServiceId;
|
||||||
std::string mServiceName;
|
std::string mServiceName;
|
||||||
|
|
||||||
bool mDefaultAllowed;
|
bool mDefaultAllowed;
|
||||||
// note only one of these is checked.
|
// note only one of these is checked.
|
||||||
// if DefaultAllowed = true, then only PeersDenied is checked.
|
// if DefaultAllowed = true, then only PeersDenied is checked.
|
||||||
// if DefaultAllowed = false, then only PeersAllowed is checked.
|
// if DefaultAllowed = false, then only PeersAllowed is checked.
|
||||||
std::set<RsPeerId> mPeersAllowed;
|
std::set<RsPeerId> mPeersAllowed;
|
||||||
std::set<RsPeerId> mPeersDenied;
|
std::set<RsPeerId> mPeersDenied;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RsServiceControl
|
class RsServiceControl
|
||||||
|
@ -1588,6 +1588,7 @@ int RsServer::StartupRetroShare()
|
|||||||
mConfigMgr->addConfiguration("p3History.cfg", mHistoryMgr);
|
mConfigMgr->addConfiguration("p3History.cfg", mHistoryMgr);
|
||||||
mConfigMgr->addConfiguration("p3Status.cfg", mStatusSrv);
|
mConfigMgr->addConfiguration("p3Status.cfg", mStatusSrv);
|
||||||
mConfigMgr->addConfiguration("turtle.cfg", tr);
|
mConfigMgr->addConfiguration("turtle.cfg", tr);
|
||||||
|
mConfigMgr->addConfiguration("servicecontrol.cfg", serviceCtrl);
|
||||||
#ifdef ENABLE_GROUTER
|
#ifdef ENABLE_GROUTER
|
||||||
mConfigMgr->addConfiguration("grouter.cfg", gr);
|
mConfigMgr->addConfiguration("grouter.cfg", gr);
|
||||||
#endif
|
#endif
|
||||||
|
@ -241,8 +241,6 @@ bool RsFileTransferItem::serialise_header(void *data,uint32_t& pktsize,uint32_t&
|
|||||||
|
|
||||||
pktsize = tlvsize;
|
pktsize = tlvsize;
|
||||||
|
|
||||||
bool ok = true;
|
|
||||||
|
|
||||||
if(!setRsItemHeader(data, tlvsize, PacketId(), tlvsize))
|
if(!setRsItemHeader(data, tlvsize, PacketId(), tlvsize))
|
||||||
{
|
{
|
||||||
std::cerr << "RsFileTransferItem::serialise_header(): ERROR. Not enough size!" << std::endl;
|
std::cerr << "RsFileTransferItem::serialise_header(): ERROR. Not enough size!" << std::endl;
|
||||||
|
@ -58,6 +58,7 @@ const uint16_t RS_SERVICE_TYPE_MAIL = 0x0022;
|
|||||||
const uint16_t RS_SERVICE_TYPE_DIRECT_MAIL = 0x0023;
|
const uint16_t RS_SERVICE_TYPE_DIRECT_MAIL = 0x0023;
|
||||||
const uint16_t RS_SERVICE_TYPE_DISTANT_MAIL = 0x0024;
|
const uint16_t RS_SERVICE_TYPE_DISTANT_MAIL = 0x0024;
|
||||||
const uint16_t RS_SERVICE_TYPE_GWEMAIL_MAIL = 0x0025;
|
const uint16_t RS_SERVICE_TYPE_GWEMAIL_MAIL = 0x0025;
|
||||||
|
const uint16_t RS_SERVICE_TYPE_SERVICE_CONTROL= 0x0026;
|
||||||
|
|
||||||
// Non essential services.
|
// Non essential services.
|
||||||
const uint16_t RS_SERVICE_TYPE_BANLIST = 0x0101;
|
const uint16_t RS_SERVICE_TYPE_BANLIST = 0x0101;
|
||||||
|
@ -1464,10 +1464,10 @@ void MainWindow::retroshareLinkActivated(const QUrl &url)
|
|||||||
RetroShareLink::process(links);
|
RetroShareLink::process(links);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::servicePermission()
|
//void MainWindow::servicePermission()
|
||||||
{
|
//{
|
||||||
ServicePermissionDialog::showYourself();
|
// ServicePermissionDialog::showYourself();
|
||||||
}
|
//}
|
||||||
|
|
||||||
SoundStatus *MainWindow::soundStatusInstance()
|
SoundStatus *MainWindow::soundStatusInstance()
|
||||||
{
|
{
|
||||||
|
@ -207,7 +207,7 @@ private slots:
|
|||||||
void newRsCollection();
|
void newRsCollection();
|
||||||
void showMessengerWindow();
|
void showMessengerWindow();
|
||||||
void showStatisticsWindow();
|
void showStatisticsWindow();
|
||||||
void servicePermission();
|
//void servicePermission();
|
||||||
|
|
||||||
#ifdef UNFINISHED
|
#ifdef UNFINISHED
|
||||||
void showApplWindow();
|
void showApplWindow();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user