/*******************************************************************************
* libretroshare/src/pqi: p3servicecontrol.cc *
* *
* libretroshare: retroshare core library *
* *
* Copyright (C) 2014-2014 Robert Fernie *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see . *
* *
*******************************************************************************/
#include
#include "p3servicecontrol.h"
#include "rsitems/rsserviceids.h"
#include "rsitems/rsitem.h"
#include "serialiser/rstypeserializer.h"
#include "rsitems/rsnxsitems.h"
#include "pqi/p3cfgmgr.h"
#include "pqi/pqiservice.h"
#include "retroshare/rspeers.h"
#include "retroshare/rsevents.h"
/*******************************/
// #define SERVICECONTROL_DEBUG 1
/*******************************/
static const uint8_t RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS = 0x01 ;
class RsServiceControlItem: public RsItem
{
public:
explicit RsServiceControlItem(uint8_t item_subtype) : RsItem(RS_PKT_VERSION_SERVICE,RS_SERVICE_TYPE_SERVICE_CONTROL,item_subtype) {}
};
class RsServicePermissionItem: public RsServiceControlItem, public RsServicePermissions
{
public:
RsServicePermissionItem(): RsServiceControlItem(RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS) {}
explicit RsServicePermissionItem(const RsServicePermissions& perms) : RsServiceControlItem(RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS), RsServicePermissions(perms) {}
virtual void serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx)
{
RsTypeSerializer::serial_process(j,ctx,mServiceId,"mServiceId") ;
RsTypeSerializer::serial_process (j,ctx,TLV_TYPE_STR_NAME,mServiceName,"mServiceName") ;
RsTypeSerializer::serial_process (j,ctx,mDefaultAllowed,"mDefaultAllowed") ;
RsTypeSerializer::serial_process (j,ctx,mPeersAllowed,"mPeersAllowed");
RsTypeSerializer::serial_process (j,ctx,mPeersDenied ,"mPeersDenied");
}
virtual void clear() {}
};
class ServiceControlSerialiser: public RsServiceSerializer
{
public:
ServiceControlSerialiser() : RsServiceSerializer(RS_SERVICE_TYPE_SERVICE_CONTROL) {}
virtual RsItem *create_item(uint16_t service,uint8_t item_subtype) const
{
if(RS_SERVICE_TYPE_SERVICE_CONTROL != service)
return NULL;
switch(item_subtype)
{
case RS_PKT_SUBTYPE_SERVICE_CONTROL_SERVICE_PERMISSIONS: return new RsServicePermissionItem();
default:
return NULL ;
}
}
};
RsServiceControl *rsServiceControl = NULL;
p3ServiceControl::p3ServiceControl(p3LinkMgr *linkMgr)
: RsServiceControl(), p3Config(),
mLinkMgr(linkMgr), mOwnPeerId(linkMgr->getOwnId()),
mCtrlMtx("p3ServiceControl"), mMonitorMtx("P3ServiceControl::Monitor"),
mServiceServer(NULL)
{
mSerialiser = new ServiceControlSerialiser ;
}
RsSerialiser *p3ServiceControl::setupSerialiser()
{
RsSerialiser *serial = new RsSerialiser;
serial->addSerialType(new ServiceControlSerialiser) ;
return serial ;
}
const RsPeerId& p3ServiceControl::getOwnId()
{
return mOwnPeerId;
}
bool p3ServiceControl::getServiceItemNames(uint32_t serviceId,std::map& names)
{
if(mServiceServer != NULL)
return mServiceServer->getServiceItemNames(serviceId,names) ;
return false ;
}
/* Interface for Services */
bool p3ServiceControl::registerService(const RsServiceInfo &info, bool defaultOn)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
std::map::iterator it;
it = mOwnServices.find(info.mServiceType);
if (it != mOwnServices.end())
{
std::cerr << "p3ServiceControl::registerService() ERROR Duplicate Service ID";
std::cerr << std::endl;
return false;
}
/* sanity check ServiceInfo */
mOwnServices[info.mServiceType] = info;
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::registerService() Registered ServiceID: " << info.mServiceType;
std::cerr << std::endl;
std::cerr << "p3ServiceControl::registerService() ServiceName: " << info.mServiceName;
std::cerr << std::endl;
#endif
/* create default permissions for this service
* this will be overwritten by configuration (if it exists).
*/
createDefaultPermissions_locked(info.mServiceType, info.mServiceName, defaultOn);
return true;
}
bool p3ServiceControl::deregisterService(uint32_t serviceId)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
std::map::iterator it;
it = mOwnServices.find(serviceId);
if (it == mOwnServices.end())
{
std::cerr << "p3ServiceControl::deregisterService() ERROR No matching Service ID";
std::cerr << std::endl;
return false;
}
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::deregisterService() Removed ServiceID: " << serviceId;
std::cerr << std::endl;
#endif
mOwnServices.erase(it);
return true;
}
/* Interface for Services */
bool p3ServiceControl::registerServiceMonitor(pqiServiceMonitor *monitor, uint32_t serviceId)
{
RsStackMutex stack(mMonitorMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::registerServiceMonitor() for ServiceId: ";
std::cerr << serviceId;
std::cerr << std::endl;
#endif
mMonitors.insert(std::make_pair(serviceId, monitor));
return true;
}
bool p3ServiceControl::deregisterServiceMonitor(pqiServiceMonitor *monitor)
{
RsStackMutex stack(mMonitorMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::deregisterServiceMonitor()";
std::cerr << std::endl;
#endif
std::multimap::iterator it;
for(it = mMonitors.begin(); it != mMonitors.end(); )
{
if (it->second == monitor)
{
mMonitors.erase(it++);
}
else
{
++it;
}
}
return true;
}
/* Interface to p3ServiceInfo */
void p3ServiceControl::getServiceChanges(std::set &updateSet)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::getServiceChanges()";
std::cerr << std::endl;
#endif
std::set::iterator it;
for (it = mUpdatedSet.begin(); it != mUpdatedSet.end(); ++it)
{
updateSet.insert(*it);
}
mUpdatedSet.clear();
}
std::string p3ServiceControl::getServiceName(uint32_t service_id)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
std::map::const_iterator it = mOwnServices.find(service_id) ;
if(it == mOwnServices.end())
{
std::cerr << "(EE) Cannot find own service for ID = " << std::hex << service_id << std::dec << std::endl;
return std::string();
}
return it->second.mServiceName;
}
bool p3ServiceControl::getOwnServices(RsPeerServiceInfo &info)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::getOwnServices()";
std::cerr << std::endl;
#endif
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
info.mPeerId.clear() ;
info.mServiceList = mOwnServices;
return true;
}
bool p3ServiceControl::getServicesAllowed(const RsPeerId &peerId, RsPeerServiceInfo &info)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::getServicesAllowed(" << peerId.toStdString() << ")";
std::cerr << std::endl;
#endif
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
info.mPeerId = peerId;
// For each registered Service.. check if peer has permissions.
std::map::iterator it;
for(it = mOwnServices.begin(); it != mOwnServices.end(); ++it)
{
if (peerHasPermissionForService_locked(peerId, it->first))
{
info.mServiceList[it->first] = it->second;
}
}
return true;
}
bool p3ServiceControl::peerHasPermissionForService_locked(const RsPeerId &peerId, uint32_t serviceId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::peerHasPermissionForService_locked()";
std::cerr << std::endl;
#endif
std::map::iterator it;
it = mServicePermissionMap.find(serviceId);
if (it == mServicePermissionMap.end())
{
return false;
}
return it->second.peerHasPermission(peerId);
}
// This is used by both RsServiceControl and p3ServiceInfo.
bool p3ServiceControl::getServicesProvided(const RsPeerId &peerId, RsPeerServiceInfo &info)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::getServicesProvided()";
std::cerr << std::endl;
#endif
std::map::iterator it;
it = mServicesProvided.find(peerId);
if (it == mServicesProvided.end())
{
return false;
}
info = it->second;
return true;
}
bool p3ServiceControl::updateServicesProvided(const RsPeerId &peerId, const RsPeerServiceInfo &info)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateServicesProvided() from: " << peerId.toStdString();
std::cerr << std::endl;
std::cerr << info;
std::cerr << std::endl;
#endif
mServicesProvided[peerId] = info;
updateFilterByPeer_locked(peerId);
IndicateConfigChanged() ;
return true;
}
/* External Interface to RsServiceControl */
bool p3ServiceControl::getServicePermissions(uint32_t serviceId, RsServicePermissions &permissions)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::getServicePermissions()";
std::cerr << std::endl;
#endif
std::map::iterator it;
it = mServicePermissionMap.find(serviceId);
if (it == mServicePermissionMap.end())
{
return false;
}
permissions = it->second;
return true;
}
bool p3ServiceControl::createDefaultPermissions_locked(uint32_t serviceId, const std::string& serviceName, bool defaultOn)
{
std::map::iterator it;
it = mServicePermissionMap.find(serviceId);
if (it == mServicePermissionMap.end())
{
RsServicePermissions perms;
perms.mServiceId = serviceId;
perms.mServiceName = serviceName;
perms.mDefaultAllowed = defaultOn;
mServicePermissionMap[serviceId] = perms;
IndicateConfigChanged() ;
return true;
}
return false;
}
bool p3ServiceControl::updateServicePermissions(uint32_t serviceId, const RsServicePermissions &permissions)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateServicePermissions()";
std::cerr << std::endl;
#endif
std::map::iterator it;
it = mServicePermissionMap.find(serviceId);
if (it == mServicePermissionMap.end())
{
std::cerr << "p3ServiceControl::updateServicePermissions()";
std::cerr << " ERROR missing previous permissions";
std::cerr << std::endl;
// ERROR.
return false;
}
std::list onlinePeers;
mLinkMgr->getOnlineList(onlinePeers);
std::list::const_iterator pit;
if (it != mServicePermissionMap.end())
{
for(pit = onlinePeers.begin(); pit != onlinePeers.end(); ++pit)
{
if (it->second.peerHasPermission(*pit) !=
permissions.peerHasPermission(*pit))
{
mUpdatedSet.insert(*pit);
}
}
}
it->second = permissions;
it->second.mServiceId = serviceId; // just to make sure!
// This is overkill - but will update everything.
updateAllFilters_locked();
IndicateConfigChanged() ;
return true;
}
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
// This is the basic system...
// which will be a little slow (2x maps).
//
// Potential improvement is to push the RsPeerFilter
// to the pqiStreamer, (when items have been sorted by peers already!).
// but we will do this later.
bool p3ServiceControl::checkFilter(uint32_t serviceId, const RsPeerId &peerId)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::checkFilter() ";
std::cerr << " ServiceId: " << serviceId;
#endif
std::map::iterator it;
it = mOwnServices.find(serviceId);
if (it != mOwnServices.end())
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << " ServiceName: " << it->second.mServiceName;
#endif
}
else
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << " ServiceName: Unknown! ";
#endif
}
#ifdef SERVICECONTROL_DEBUG
std::cerr << " PeerId: " << peerId.toStdString();
std::cerr << std::endl;
#endif
// must allow ServiceInfo through, or we have nothing!
if (serviceId == RsServiceInfo::RsServiceInfoUIn16ToFullServiceId(RS_SERVICE_TYPE_SERVICEINFO))
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::checkFilter() Allowed SERVICEINFO";
std::cerr << std::endl;
#endif
return true;
}
std::map::const_iterator pit;
pit = mPeerFilterMap.find(peerId);
if (pit == mPeerFilterMap.end())
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::checkFilter() Denied No PeerId";
std::cerr << std::endl;
#endif
return false;
}
if (pit->second.mDenyAll)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::checkFilter() Denied Peer.DenyAll";
std::cerr << std::endl;
#endif
return false;
}
if (pit->second.mAllowAll)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::checkFilter() Allowed Peer.AllowAll";
std::cerr << std::endl;
#endif
return true;
}
std::set::const_iterator sit;
sit = pit->second.mAllowedServices.find(serviceId);
if (sit == pit->second.mAllowedServices.end())
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::checkFilter() Denied !Peer.find(serviceId)";
std::cerr << std::endl;
#endif
return false;
}
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::checkFilter() Allowed Peer.find(serviceId)";
std::cerr << std::endl;
#endif
return true;
}
bool versionOkay(uint16_t version_major, uint16_t version_minor,
uint16_t min_version_major, uint16_t min_version_minor)
{
if (version_major > min_version_major)
{
return true;
}
if (version_major == min_version_major)
{
return (version_minor >= min_version_minor);
}
return false;
}
bool ServiceInfoCompatible(const RsServiceInfo &info1, const RsServiceInfo &info2)
{
// Id, or Name mismatch.
if ((info1.mServiceType != info2.mServiceType) ||
(info1.mServiceName != info2.mServiceName))
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "servicesCompatible: Type/Name mismatch";
std::cerr << std::endl;
std::cerr << "Info1 ID: " << info1.mServiceType;
std::cerr << " " << info1.mServiceName;
std::cerr << std::endl;
std::cerr << "Info2 ID: " << info2.mServiceType;
std::cerr << " " << info2.mServiceName;
std::cerr << std::endl;
#endif
return false;
}
// ensure that info1 meets minimum requirements for info2
if (!versionOkay(info1.mVersionMajor, info1.mVersionMinor,
info2.mMinVersionMajor, info2.mMinVersionMinor))
{
return false;
}
// ensure that info2 meets minimum requirements for info1
if (!versionOkay(info2.mVersionMajor, info2.mVersionMinor,
info1.mMinVersionMajor, info1.mMinVersionMinor))
{
return false;
}
return true;
}
bool p3ServiceControl::updateFilterByPeer(const RsPeerId &peerId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateFilterByPeer()";
std::cerr << std::endl;
#endif
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
return updateFilterByPeer_locked(peerId);
}
bool p3ServiceControl::updateAllFilters()
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateAllFilters()";
std::cerr << std::endl;
#endif
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
return updateAllFilters_locked();
}
bool p3ServiceControl::updateAllFilters_locked()
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateAllFilters_locked()";
std::cerr << std::endl;
#endif
// Create a peerSet from ServicesProvided + PeerFilters.
// This will completely refresh the Filters.
std::set peerSet;
std::set::const_iterator pit;
std::map::const_iterator it;
for(it = mServicesProvided.begin(); it != mServicesProvided.end(); ++it)
{
peerSet.insert(it->first);
}
std::map::const_iterator fit;
for(fit = mPeerFilterMap.begin(); fit != mPeerFilterMap.end(); ++fit)
{
peerSet.insert(fit->first);
}
for(pit = peerSet.begin(); pit != peerSet.end(); ++pit)
{
updateFilterByPeer_locked(*pit);
}
return true;
}
// create filter. (the easy way).
bool p3ServiceControl::updateFilterByPeer_locked(const RsPeerId &peerId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateFilterByPeer_locked() : " << peerId.toStdString();
std::cerr << std::endl;
#endif
ServicePeerFilter originalFilter;
ServicePeerFilter peerFilter;
std::map::iterator fit;
fit = mPeerFilterMap.find(peerId);
if (fit != mPeerFilterMap.end())
{
originalFilter = fit->second;
}
std::map::iterator it;
it = mServicesProvided.find(peerId);
if (it == mServicesProvided.end())
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateFilterByPeer_locked() Empty ... Clearing";
std::cerr << std::endl;
#endif
// empty, remove...
recordFilterChanges_locked(peerId, originalFilter, peerFilter);
if (fit != mPeerFilterMap.end())
{
std::cerr << std::endl;
mPeerFilterMap.erase(fit);
}
return true;
}
// For each registered Service.. check if services compatible.
// then check for permissions.
// similar maps, can iterate through in parallel.
std::map::const_iterator oit = mOwnServices.begin();
std::map::const_iterator eoit = mOwnServices.end();
std::map::const_iterator tit = it->second.mServiceList.begin();
std::map::const_iterator etit = it->second.mServiceList.end();
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateFilterByPeer_locked() Comparing lists";
std::cerr << std::endl;
#endif
while((oit != eoit) && (tit != etit))
{
if (oit->first == tit->first)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "\tChecking Matching Service ID: " << oit->first;
std::cerr << std::endl;
#endif
/* match of service IDs */
/* check if compatible */
if (ServiceInfoCompatible(oit->second, tit->second))
{
if (peerHasPermissionForService_locked(peerId, oit->first))
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "\t\tMatched Service ID: " << oit->first;
std::cerr << std::endl;
#endif
peerFilter.mAllowedServices.insert(oit->first);
}
}
++oit;
++tit;
}
else
{
if (oit->first < tit->first)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "\tSkipping Only Own Service ID: " << oit->first;
std::cerr << std::endl;
#endif
++oit;
}
else
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "\tSkipping Only Peer Service ID: " << tit->first;
std::cerr << std::endl;
#endif
++tit;
}
}
}
// small optimisations.
if (peerFilter.mAllowedServices.empty())
{
peerFilter.mDenyAll = true;
}
else
{
peerFilter.mDenyAll = false;
if (peerFilter.mAllowedServices.size() == mOwnServices.size())
{
peerFilter.mAllowAll = true;
}
}
// update or remove.
if (peerFilter.mDenyAll)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateFilterByPeer_locked() Empty(2) ... Clearing";
std::cerr << std::endl;
#endif
if (fit != mPeerFilterMap.end())
{
mPeerFilterMap.erase(fit);
}
}
else
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updateFilterByPeer_locked() Installing PeerFilter";
std::cerr << std::endl;
#endif
mPeerFilterMap[peerId] = peerFilter;
}
recordFilterChanges_locked(peerId, originalFilter, peerFilter);
using Evt_t = RsPeerStateChangedEvent;
if(rsEvents)
rsEvents->postEvent(std::unique_ptr(new Evt_t(peerId)));
return true;
}
void p3ServiceControl::recordFilterChanges_locked(const RsPeerId &peerId,
ServicePeerFilter &originalFilter, ServicePeerFilter &updatedFilter)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::recordFilterChanges_locked()";
std::cerr << std::endl;
std::cerr << "PeerId: " << peerId.toStdString();
std::cerr << std::endl;
std::cerr << "OriginalFilter: " << originalFilter;
std::cerr << std::endl;
std::cerr << "UpdatedFilter: " << updatedFilter;
std::cerr << std::endl;
#endif
/* find differences */
//std::map changes;
std::set::const_iterator it1, it2, eit1, eit2;
it1 = originalFilter.mAllowedServices.begin();
eit1 = originalFilter.mAllowedServices.end();
it2 = updatedFilter.mAllowedServices.begin();
eit2 = updatedFilter.mAllowedServices.end();
while((it1 != eit1) && (it2 != eit2))
{
if (*it1 < *it2)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "Removed Service: " << *it1;
std::cerr << std::endl;
#endif
// removal
//changes[*it1] = false;
filterChangeRemoved_locked(peerId, *it1);
++it1;
}
else if (*it2 < *it1)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "Added Service: " << *it2;
std::cerr << std::endl;
#endif
// addition.
filterChangeAdded_locked(peerId, *it2);
//changes[*it2] = true;
++it2;
}
else
{
++it1;
++it2;
}
}
// Handle the unfinished Set.
for(; it1 != eit1; ++it1)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "Removed Service: " << *it1;
std::cerr << std::endl;
#endif
// removal
//changes[*it1] = false;
filterChangeRemoved_locked(peerId, *it1);
}
for(; it2 != eit2; ++it2)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "Added Service: " << *it2;
std::cerr << std::endl;
#endif
// addition.
//changes[*it2] = true;
filterChangeAdded_locked(peerId, *it2);
}
// Can remove changes map... as only used below.
#if 0
// now we to store for later notifications.
std::map::const_iterator cit;
for(cit = changes.begin(); cit != changes.end(); ++cit)
{
ServiceNotifications ¬es = mNotifications[cit->first];
if (cit->second)
{
notes.mAdded.insert(peerId);
}
else
{
notes.mRemoved.insert(peerId);
}
}
#endif
}
// when they go offline, etc.
void p3ServiceControl::removePeer(const RsPeerId &peerId)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::removePeer() : " << peerId.toStdString();
std::cerr << std::endl;
#endif
ServicePeerFilter originalFilter;
bool hadFilter = false;
{
std::map::iterator fit;
fit = mPeerFilterMap.find(peerId);
if (fit != mPeerFilterMap.end())
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::removePeer() clearing mPeerFilterMap";
std::cerr << std::endl;
#endif
hadFilter = true;
originalFilter = fit->second;
mPeerFilterMap.erase(fit);
}
else
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::removePeer() Nothing in mPeerFilterMap";
std::cerr << std::endl;
#endif
}
}
{
std::map::iterator sit;
sit = mServicesProvided.find(peerId);
if (sit != mServicesProvided.end())
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::removePeer() clearing mServicesProvided";
std::cerr << std::endl;
#endif
mServicesProvided.erase(sit);
}
else
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::removePeer() Nothing in mServicesProvided";
std::cerr << std::endl;
#endif
}
}
if (hadFilter)
{
ServicePeerFilter emptyFilter;
recordFilterChanges_locked(peerId, originalFilter, emptyFilter);
}
}
/****************************************************************************/
/****************************************************************************/
// need to provide list of connected peers per service.
// these are collected here.
void p3ServiceControl::filterChangeRemoved_locked(const RsPeerId &peerId, uint32_t serviceId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::filterChangeRemoved_locked(" << peerId.toStdString();
std::cerr << ", " << serviceId << ")";
std::cerr << std::endl;
#endif
//std::map >::iterator mit;
std::set &peerSet = mServicePeerMap[serviceId];
std::set::iterator sit;
sit = peerSet.find(peerId);
if (sit != peerSet.end())
{
peerSet.erase(sit);
}
else
{
// ERROR
std::cerr << "p3ServiceControl::filterChangeRemoved_locked() ERROR NOT FOUND";
std::cerr << std::endl;
}
// Add to Notifications too.
ServiceNotifications ¬es = mNotifications[serviceId];
notes.mRemoved.insert(peerId);
}
void p3ServiceControl::filterChangeAdded_locked(const RsPeerId &peerId, uint32_t serviceId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::filterChangeAdded_locked(" << peerId.toStdString();
std::cerr << ", " << serviceId << ")";
std::cerr << std::endl;
#endif
//std::map >::iterator mit;
std::set &peerSet = mServicePeerMap[serviceId];
// This bit is only for error checking.
std::set::iterator sit = peerSet.find(peerId);
if (sit != peerSet.end())
{
// ERROR.
std::cerr << "p3ServiceControl::filterChangeAdded_locked() ERROR NOT FOUND";
std::cerr << std::endl;
}
peerSet.insert(peerId);
// Add to Notifications too.
ServiceNotifications ¬es = mNotifications[serviceId];
notes.mAdded.insert(peerId);
}
void p3ServiceControl::getPeersConnected(uint32_t serviceId, std::set &peerSet)
{
RS_STACK_MUTEX(mCtrlMtx);
std::map >::iterator mit;
mit = mServicePeerMap.find(serviceId);
if (mit != mServicePeerMap.end())
{
peerSet = mit->second;
}
else
{
peerSet.clear();
}
}
bool p3ServiceControl::isPeerConnected(uint32_t serviceId, const RsPeerId &peerId)
{
RS_STACK_MUTEX(mCtrlMtx);
std::map >::iterator mit;
mit = mServicePeerMap.find(serviceId);
if (mit != mServicePeerMap.end())
{
std::set::iterator sit;
sit = mit->second.find(peerId);
return (sit != mit->second.end());
}
return false;
}
/****************************************************************************/
/****************************************************************************/
void p3ServiceControl::tick()
{
notifyAboutFriends();
notifyServices();
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::tick()";
std::cerr << std::endl;
#endif
}
// configuration.
bool p3ServiceControl::saveList(bool &cleanup, std::list &saveList)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::saveList()";
std::cerr << std::endl;
#endif
cleanup = true ;
for(std::map::iterator it(mServicePermissionMap.begin());it!=mServicePermissionMap.end();++it)
{
RsServicePermissionItem *item = new RsServicePermissionItem(it->second) ;
saveList.push_back(item) ;
}
return true;
}
bool p3ServiceControl::loadList(std::list& loadList)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::loadList()";
std::cerr << std::endl;
#endif
for(std::list::const_iterator it(loadList.begin());it!=loadList.end();++it)
{
RsServicePermissionItem *item = dynamic_cast(*it) ;
if(item != NULL)
mServicePermissionMap[item->mServiceId] = *item ;
delete *it ;
}
loadList.clear() ;
return true;
}
/****************************************************************************/
/****************************************************************************/
// pqiMonitor.
void p3ServiceControl::statusChange(const std::list &plist)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::statusChange()";
std::cerr << std::endl;
#endif
std::list::const_iterator pit;
for(pit = plist.begin(); pit != plist.end(); ++pit)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::statusChange() for peer: ";
std::cerr << " peer: " << (pit->id).toStdString();
std::cerr << " state: " << pit->state;
std::cerr << " actions: " << pit->actions;
std::cerr << std::endl;
#endif
if (pit->state & RS_PEER_S_FRIEND)
{
// Connected / Disconnected. (interal actions).
if (pit->actions & RS_PEER_CONNECTED)
{
updatePeerConnect(pit->id);
}
else if (pit->actions & RS_PEER_DISCONNECTED)
{
updatePeerDisconnect(pit->id);
}
// Added / Removed. (pass on notifications).
if (pit->actions & RS_PEER_NEW)
{
updatePeerNew(pit->id);
}
}
else
{
if (pit->actions & RS_PEER_MOVED)
{
updatePeerRemoved(pit->id);
}
}
}
return;
}
// Update Peer status.
void p3ServiceControl::updatePeerConnect(const RsPeerId &peerId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updatePeerConnect(): " << peerId.toStdString();
std::cerr << std::endl;
#else
(void)peerId;
#endif
return;
}
void p3ServiceControl::updatePeerDisconnect(const RsPeerId &peerId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updatePeerDisconnect(): " << peerId.toStdString();
std::cerr << std::endl;
#endif
removePeer(peerId);
return;
}
// Update Peer status.
void p3ServiceControl::updatePeerNew(const RsPeerId &peerId)
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updatePeerNew(): " << peerId.toStdString();
std::cerr << std::endl;
#endif
pqiServicePeer peer;
peer.id = peerId;
peer.actions = RS_SERVICE_PEER_NEW;
mFriendNotifications.push_back(peer);
return;
}
void p3ServiceControl::updatePeerRemoved(const RsPeerId &peerId)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::updatePeerRemoved(): " << peerId.toStdString();
std::cerr << std::endl;
#endif
removePeer(peerId);
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
pqiServicePeer peer;
peer.id = peerId;
peer.actions = RS_SERVICE_PEER_REMOVED;
mFriendNotifications.push_back(peer);
return;
}
/****************************************************************************/
/****************************************************************************/
void p3ServiceControl::notifyAboutFriends()
{
std::list friendNotifications;
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
if (mFriendNotifications.empty())
{
return;
}
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::notifyAboutFriends(): Something has changed!";
std::cerr << std::endl;
#endif
mFriendNotifications.swap(friendNotifications);
}
{
RsStackMutex stack(mMonitorMtx); /***** LOCK STACK MUTEX ****/
std::multimap::const_iterator sit;
for(sit = mMonitors.begin(); sit != mMonitors.end(); ++sit)
{
sit->second->statusChange(friendNotifications);
}
}
}
void p3ServiceControl::notifyServices()
{
std::map notifications;
{
RsStackMutex stack(mCtrlMtx); /***** LOCK STACK MUTEX ****/
if (mNotifications.empty())
{
return;
}
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::notifyServices()";
std::cerr << std::endl;
#endif
mNotifications.swap(notifications);
}
{
RsStackMutex stack(mMonitorMtx); /***** LOCK STACK MUTEX ****/
std::map::const_iterator it;
std::multimap::const_iterator sit, eit;
for(it = notifications.begin(); it != notifications.end(); ++it)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::notifyServices(): Notifications for Service: " << it->first;
std::cerr << std::endl;
#endif
sit = mMonitors.lower_bound(it->first);
eit = mMonitors.upper_bound(it->first);
if (sit == eit)
{
/* nothing to notify - skip */
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::notifyServices(): Noone Monitoring ... skipping";
std::cerr << std::endl;
#endif
continue;
}
std::list peers;
std::set::const_iterator pit;
for(pit = it->second.mAdded.begin();
pit != it->second.mAdded.end(); ++pit)
{
pqiServicePeer peer;
peer.id = *pit;
peer.actions = RS_SERVICE_PEER_CONNECTED;
peers.push_back(peer);
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::notifyServices(): Peer: " << *pit << " CONNECTED";
std::cerr << std::endl;
#endif
}
for(pit = it->second.mRemoved.begin();
pit != it->second.mRemoved.end(); ++pit)
{
pqiServicePeer peer;
peer.id = *pit;
peer.actions = RS_SERVICE_PEER_DISCONNECTED;
peers.push_back(peer);
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::notifyServices(): Peer: " << *pit << " DISCONNECTED";
std::cerr << std::endl;
#endif
}
for(; sit != eit; ++sit)
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "p3ServiceControl::notifyServices(): Sending to Monitoring Service";
std::cerr << std::endl;
#endif
sit->second->statusChange(peers);
}
}
}
}
/****************************************************************************/
/****************************************************************************/
RsServicePermissions::RsServicePermissions()
:mServiceId(0), mServiceName(), mDefaultAllowed(false)
{
return;
}
bool RsServicePermissions::peerHasPermission(const RsPeerId &peerId) const
{
#ifdef SERVICECONTROL_DEBUG
std::cerr << "RsServicePermissions::peerHasPermission()";
std::cerr << std::endl;
#endif
std::set::const_iterator it;
if (mDefaultAllowed)
{
it = mPeersDenied.find(peerId);
return (it == mPeersDenied.end());
}
else
{
it = mPeersAllowed.find(peerId);
return (it != mPeersAllowed.end());
}
}
void RsServicePermissions::setPermission(const RsPeerId& peerId)
{
std::set::const_iterator it;
if (mDefaultAllowed)
{
it = mPeersDenied.find(peerId);
mPeersDenied.erase(it) ;
}
else
mPeersAllowed.insert(peerId);
}
void RsServicePermissions::resetPermission(const RsPeerId& peerId)
{
std::set::const_iterator it;
if (!mDefaultAllowed)
{
it = mPeersAllowed.find(peerId);
mPeersAllowed.erase(it) ;
}
else
mPeersDenied.insert(peerId);
}
RsServiceInfo::RsServiceInfo(
const uint16_t service_type,
const std::string& service_name,
const uint16_t version_major,
const uint16_t version_minor,
const uint16_t min_version_major,
const uint16_t min_version_minor)
:mServiceName(service_name),
mServiceType(RsServiceInfoUIn16ToFullServiceId(service_type)),
mVersionMajor(version_major),
mVersionMinor(version_minor),
mMinVersionMajor(min_version_major),
mMinVersionMinor(min_version_minor)
{
return;
}
unsigned int RsServiceInfo::RsServiceInfoUIn16ToFullServiceId(uint16_t serviceType)
{
return (((uint32_t) RS_PKT_VERSION_SERVICE) << 24) + (((uint32_t) serviceType) << 8);
}
RsServiceInfo::RsServiceInfo()
:mServiceName("unknown"),
mServiceType(0),
mVersionMajor(0),
mVersionMinor(0),
mMinVersionMajor(0),
mMinVersionMinor(0)
{
return;
}
std::ostream &operator<<(std::ostream &out, const ServicePeerFilter &filter)
{
out << "ServicePeerFilter DenyAll: " << filter.mDenyAll;
out << " AllowAll: " << filter.mAllowAll;
out << " Matched Services: ";
std::set::const_iterator it;
for(it = filter.mAllowedServices.begin(); it != filter.mAllowedServices.end(); ++it)
{
out << *it << " ";
}
out << std::endl;
return out;
}