/*
 * libretroshare/src/pqi: p3peermgr.cc
 *
 * 3P/PQI network interface for RetroShare.
 *
 * Copyright 2007-2011 by Robert Fernie.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License Version 2 as published by the Free Software Foundation.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA.
 *
 * Please report all bugs and problems to "retroshare@lunamutt.com".
 *
 */

#include "rsserver/p3face.h"
#include "util/rsnet.h"
#include "pqi/authgpg.h"
#include "pqi/authssl.h"

#include "pqi/p3peermgr.h"
#include "pqi/p3linkmgr.h"
#include "pqi/p3netmgr.h"
#include "pqi/p3historymgr.h"

//#include "pqi/p3dhtmgr.h" // Only need it for constants.
//#include "tcponudp/tou.h"
//#include "util/extaddrfinder.h"
//#include "util/dnsresolver.h"

#include "util/rsprint.h"
#include "util/rsstring.h"
#include "util/rsdebug.h"

#include "rsitems/rsconfigitems.h"

#include "retroshare/rsiface.h" // Needed for rsicontrol (should remove this dependancy)
#include "retroshare/rspeers.h" // Needed for Group Parameters.
#include "retroshare/rsbanlist.h" // Needed for banned IPs

/* Network setup States */

//Defined and used in /libretroshare/src/pqi/p3netmgr.cc
//const uint32_t RS_NET_NEEDS_RESET = 	0x0000;
//const uint32_t RS_NET_UNKNOWN = 	0x0001;
//const uint32_t RS_NET_UPNP_INIT = 	0x0002;
//const uint32_t RS_NET_UPNP_SETUP =  	0x0003;
//const uint32_t RS_NET_EXT_SETUP =  	0x0004;
//const uint32_t RS_NET_DONE =    	0x0005;
//const uint32_t RS_NET_LOOPBACK =    	0x0006;
//const uint32_t RS_NET_DOWN =    	0x0007;

//const uint32_t MIN_TIME_BETWEEN_NET_RESET = 		5;

//const uint32_t PEER_IP_CONNECT_STATE_MAX_LIST_SIZE =     	4;

static struct RsLog::logInfo p3peermgrzoneInfo = {RsLog::Default, "p3peermgr"};
#define p3peermgrzone &p3peermgrzoneInfo

/****
 * #define PEER_DEBUG 1
 ***/

#define MAX_AVAIL_PERIOD 230 //times a peer stay in available state when not connected
#define MIN_RETRY_PERIOD 140

static const std::string kConfigDefaultProxyServerIpAddr = "127.0.0.1";
static const uint16_t    kConfigDefaultProxyServerPortTor = 9050; // standard port.
static const uint16_t    kConfigDefaultProxyServerPortI2P = 4447; // I2Pd's standard port

static const std::string kConfigKeyExtIpFinder = "USE_EXTR_IP_FINDER";
static const std::string kConfigKeyProxyServerIpAddrTor = "PROXY_SERVER_IPADDR";
static const std::string kConfigKeyProxyServerPortTor = "PROXY_SERVER_PORT";
static const std::string kConfigKeyProxyServerIpAddrI2P = "PROXY_SERVER_IPADDR_I2P";
static const std::string kConfigKeyProxyServerPortI2P = "PROXY_SERVER_PORT_I2P";
	
void  printConnectState(std::ostream &out, peerState &peer);

peerState::peerState()
	:netMode(RS_NET_MODE_UNKNOWN), vs_disc(RS_VS_DISC_FULL), vs_dht(RS_VS_DHT_FULL), lastcontact(0), 
	 hiddenNode(false), hiddenPort(0), hiddenType(RS_HIDDEN_TYPE_NONE)
{
        sockaddr_storage_clear(localaddr);
        sockaddr_storage_clear(serveraddr);

	return;
}

std::string textPeerConnectState(peerState &state)
{
	std::string out = "Id: " + state.id.toStdString() + "\n";
	rs_sprintf_append(out, "NetMode: %lu\n", state.netMode);
	rs_sprintf_append(out, "VisState: Disc: %u Dht: %u\n", state.vs_disc, state.vs_dht);
	
	out += "laddr: ";
	out += sockaddr_storage_tostring(state.localaddr);
	out += "\neaddr: ";
	out += sockaddr_storage_tostring(state.serveraddr);
	out += "\n";
	
	return out;
}


p3PeerMgrIMPL::p3PeerMgrIMPL(const RsPeerId& ssl_own_id, const RsPgpId& gpg_own_id, const std::string& gpg_own_name, const std::string& ssl_own_location)
	:p3Config(), mPeerMtx("p3PeerMgr"), mStatusChanged(false)
{

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		mLinkMgr = NULL;
		mNetMgr = NULL;

		/* setup basics of own state */
		mOwnState.id = ssl_own_id ;
		mOwnState.gpg_id = gpg_own_id ;
		mOwnState.name = gpg_own_name ;
		mOwnState.location = ssl_own_location ;
		mOwnState.netMode = RS_NET_MODE_UPNP; // Default to UPNP.
		mOwnState.vs_disc = RS_VS_DISC_FULL;
		mOwnState.vs_dht = RS_VS_DHT_FULL;
	
		// setup default ProxyServerAddress.
		// Tor
		sockaddr_storage_clear(mProxyServerAddressTor);
		sockaddr_storage_ipv4_aton(mProxyServerAddressTor,
				kConfigDefaultProxyServerIpAddr.c_str());
		sockaddr_storage_ipv4_setport(mProxyServerAddressTor,
				kConfigDefaultProxyServerPortTor);
		// I2P
		sockaddr_storage_clear(mProxyServerAddressI2P);
		sockaddr_storage_ipv4_aton(mProxyServerAddressI2P,
				kConfigDefaultProxyServerIpAddr.c_str());
		sockaddr_storage_ipv4_setport(mProxyServerAddressI2P,
				kConfigDefaultProxyServerPortI2P);

		mProxyServerStatusTor = RS_NET_PROXY_STATUS_UNKNOWN ;
		mProxyServerStatusI2P = RS_NET_PROXY_STATUS_UNKNOWN;
	}
	
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgr() Startup" << std::endl;
#endif


	return;
}

void    p3PeerMgrIMPL::setManagers(p3LinkMgrIMPL *linkMgr, p3NetMgrIMPL *netMgr)
{
	mLinkMgr = linkMgr;
	mNetMgr = netMgr;
}


bool p3PeerMgrIMPL::setupHiddenNode(const std::string &hiddenAddress, const uint16_t hiddenPort)
{
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::setupHiddenNode()";
		std::cerr << " Address: " << hiddenAddress;
		std::cerr << " Port: " << hiddenPort;
		std::cerr << std::endl;
#endif

		mOwnState.hiddenNode = true;
		mOwnState.hiddenPort = hiddenPort;
		mOwnState.hiddenDomain = hiddenAddress;
		mOwnState.hiddenType = hiddenDomainToHiddenType(hiddenAddress);
	}

	forceHiddenNode();
	return true;
}


bool p3PeerMgrIMPL::forceHiddenNode()
{
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
		if (RS_NET_MODE_HIDDEN != mOwnState.netMode)
		{
#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::forceHiddenNode() Required!";
			std::cerr << std::endl;
#endif
		}
		mOwnState.hiddenNode = true;
		mOwnState.hiddenType = hiddenDomainToHiddenType(mOwnState.hiddenDomain);

		// force external address - otherwise its invalid.
		sockaddr_storage_clear(mOwnState.serveraddr);
		sockaddr_storage_ipv4_aton(mOwnState.serveraddr, "0.0.0.0");
		sockaddr_storage_ipv4_setport(mOwnState.serveraddr, 0);
	}

	setOwnNetworkMode(RS_NET_MODE_HIDDEN);

	// switch off DHT too.
	setOwnVisState(mOwnState.vs_disc, RS_VS_DHT_OFF);

	// Force the Port.
	struct sockaddr_storage loopback;
	sockaddr_storage_clear(loopback);
	sockaddr_storage_ipv4_aton(loopback, "127.0.0.1");
	uint16_t port = sockaddr_storage_port(mOwnState.localaddr); 
	sockaddr_storage_ipv4_setport(loopback, port); 

	setLocalAddress(AuthSSL::getAuthSSL()->OwnId(), loopback);

	mNetMgr->setIPServersEnabled(false);

	IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
	return true;
}


bool p3PeerMgrIMPL::setOwnNetworkMode(uint32_t netMode)
{
	bool changed = false;
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::setOwnNetworkMode() :";
		std::cerr << " Existing netMode: " << mOwnState.netMode;
		std::cerr << " Input netMode: " << netMode;
		std::cerr << std::endl;
#endif

		if (mOwnState.netMode != (netMode & RS_NET_MODE_ACTUAL))
		{
			mOwnState.netMode = (netMode & RS_NET_MODE_ACTUAL);
			IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
			changed = true;
		}
	}
	
	// Pass on Flags to NetMgr.
	mNetMgr->setNetworkMode((netMode & RS_NET_MODE_ACTUAL));
	return changed;
}

bool p3PeerMgrIMPL::setOwnVisState(uint16_t vs_disc, uint16_t vs_dht)
{
	bool changed = false;
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		std::string out;
		rs_sprintf(out, "p3PeerMgr::setOwnVisState() Existing vis: %u/%u Input vis: %u/%u", 
				   mOwnState.vs_disc, mOwnState.vs_dht, vs_disc, vs_dht);
		rslog(RSL_WARNING, p3peermgrzone, out);

#ifdef PEER_DEBUG
		std::cerr << out.c_str() << std::endl;
#endif

		if (mOwnState.vs_disc != vs_disc || mOwnState.vs_dht != vs_dht) 
		{
			mOwnState.vs_disc = vs_disc;
			mOwnState.vs_dht = vs_dht;
			changed = true;
			IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
		}
	}
	
	// Pass on Flags to NetMgr.
	mNetMgr->setVisState(vs_disc, vs_dht);

	return changed;
}


void p3PeerMgrIMPL::tick()
{
    static const time_t INTERVAL_BETWEEN_LOCATION_CLEANING = 300 ; // Remove unused locations and clean IPs every 10 minutes.

    static time_t last_friends_check = time(NULL) ; // first cleaning after 1 hour.

    time_t now = time(NULL) ;

    if(now > INTERVAL_BETWEEN_LOCATION_CLEANING + last_friends_check )
    {
#ifdef PEER_DEBUG
        std::cerr << "p3PeerMgrIMPL::tick(): cleaning unused locations." << std::endl ;
#endif

        rslog(RSL_WARNING, p3peermgrzone, "p3PeerMgr::tick() removeUnusedLocations()");

        removeUnusedLocations() ;

#ifdef PEER_DEBUG
        std::cerr << "p3PeerMgrIMPL::tick(): cleaning banned/old IPs." << std::endl ;
#endif
        removeBannedIps() ;

        last_friends_check = now ;
    }
}


/********************************  Network Status  *********************************
 * Configuration Loading / Saving.
 */


const RsPeerId& p3PeerMgrIMPL::getOwnId()
{
                return AuthSSL::getAuthSSL()->OwnId();
}


bool p3PeerMgrIMPL::getOwnNetStatus(peerState &state)
{
        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
        state = mOwnState;
	return true;
}

bool p3PeerMgrIMPL::isFriend(const RsPeerId& id)
{
#ifdef PEER_DEBUG_COMMON
                std::cerr << "p3PeerMgrIMPL::isFriend(" << id << ") called" << std::endl;
#endif
        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
        bool ret = (mFriendList.end() != mFriendList.find(id));
#ifdef PEER_DEBUG_COMMON
                std::cerr << "p3PeerMgrIMPL::isFriend(" << id << ") returning : " << ret << std::endl;
#endif
        return ret;
}

bool    p3PeerMgrIMPL::getPeerName(const RsPeerId &ssl_id, std::string &name)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
		return false;
	}

	name = it->second.name + " (" + it->second.location + ")";
	return true;
}

bool    p3PeerMgrIMPL::getGpgId(const RsPeerId &ssl_id, RsPgpId &gpgId)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
		return false;
	}

	gpgId = it->second.gpg_id;
	return true;
}

/**** HIDDEN STUFF ****/

bool p3PeerMgrIMPL::isHidden()
{
	RS_STACK_MUTEX(mPeerMtx);
	return mOwnState.hiddenNode;
}

/**
 * @brief checks the hidden type of the own peer.
 * @param type type to check
 * @return true when the peer has the same hidden type than type
 */
bool	p3PeerMgrIMPL::isHidden(const uint32_t type)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	switch (type) {
	case RS_HIDDEN_TYPE_TOR:
		return mOwnState.hiddenType == RS_HIDDEN_TYPE_TOR;
		break;
	case RS_HIDDEN_TYPE_I2P:
		return mOwnState.hiddenType == RS_HIDDEN_TYPE_I2P;
		break;
	default:
#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::isHidden(" << type << ") unkown type -> false";
		std::cerr << std::endl;
#endif
		return false;
		break;
	}
}

bool    p3PeerMgrIMPL::isHiddenPeer(const RsPeerId &ssl_id)
{
	return isHiddenPeer(ssl_id, RS_HIDDEN_TYPE_NONE);
}

/**
 * @brief checks the hidden type of a given ssl id. When type RS_HIDDEN_TYPE_NONE is choosen it returns the 'hiddenNode' value instead
 * @param ssl_id to check
 * @param type type to check. Use RS_HIDDEN_TYPE_NONE to check 'hiddenNode' value
 * @return true when the peer has the same hidden type than type
 */
bool	p3PeerMgrIMPL::isHiddenPeer(const RsPeerId &ssl_id, const uint32_t type)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::isHiddenPeer(" << ssl_id << ") Missing Peer => false";
		std::cerr << std::endl;
#endif

		return false;
	}

#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::isHiddenPeer(" << ssl_id << ") = " << (it->second).hiddenNode;
	std::cerr << std::endl;
#endif
	switch (type) {
	case RS_HIDDEN_TYPE_TOR:
		return (it->second).hiddenType == RS_HIDDEN_TYPE_TOR;
		break;
	case RS_HIDDEN_TYPE_I2P:
		return (it->second).hiddenType == RS_HIDDEN_TYPE_I2P;
		break;
	default:
		return (it->second).hiddenNode;
		break;
	}
}

bool hasEnding (std::string const &fullString, std::string const &ending) {
	if (fullString.length() < ending.length())
		return false;

	return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending));
}

/**
 * @brief resolves the hidden type (tor or i2p) from a domain
 * @param domain to check
 * @return RS_HIDDEN_TYPE_TOR, RS_HIDDEN_TYPE_I2P or RS_HIDDEN_TYPE_NONE
 *
 * Tor: ^[a-z2-7]{16}\.onion$
 *
 * I2P: There is more than one address:
 *       - pub. key in base64
 *       - hash in base32 ( ^[a-z2-7]{52}\.b32\.i2p$ )
 *       - "normal" .i2p domains
 */
uint32_t p3PeerMgrIMPL::hiddenDomainToHiddenType(const std::string &domain)
{
	if(hasEnding(domain, ".onion"))
		return RS_HIDDEN_TYPE_TOR;
	if(hasEnding(domain, ".i2p"))
		return RS_HIDDEN_TYPE_I2P;

#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::hiddenDomainToHiddenType() unknown hidden type: " << domain;
		std::cerr << std::endl;
#endif
	return RS_HIDDEN_TYPE_UNKNOWN;
}

/**
 * @brief returns the hidden type of a peer
 * @param ssl_id peer id
 * @return hidden type
 */
uint32_t p3PeerMgrIMPL::getHiddenType(const RsPeerId &ssl_id)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	if (ssl_id == AuthSSL::getAuthSSL()->OwnId())
		return mOwnState.hiddenType;

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::getHiddenType(" << ssl_id << ") Missing Peer => false";
		std::cerr << std::endl;
#endif

		return false;
	}

#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::getHiddenType(" << ssl_id << ") = " << (it->second).hiddenType;
	std::cerr << std::endl;
#endif
	return (it->second).hiddenType;
}

/**
 * @brief sets hidden domain and port for a given ssl ID
 * @param ssl_id peer to set domain and port for
 * @param domain_addr
 * @param domain_port
 * @return true on success
 */
bool p3PeerMgrIMPL::setHiddenDomainPort(const RsPeerId &ssl_id, const std::string &domain_addr, const uint16_t domain_port)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::setHiddenDomainPort()";
	std::cerr << std::endl;
#endif

	std::string domain = domain_addr;
	// trim whitespace!
	size_t pos = domain.find_last_not_of(" \t\n");
	if (std::string::npos != pos)
	{
		domain = domain.substr(0, pos + 1);
	}
	pos = domain.find_first_not_of(" \t\n");
	if (std::string::npos != pos)
	{
		domain = domain.substr(pos);
	}

	IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/

	if (ssl_id == AuthSSL::getAuthSSL()->OwnId()) 
	{
		mOwnState.hiddenNode = true;
		mOwnState.hiddenDomain = domain;
		mOwnState.hiddenPort = domain_port;
		mOwnState.hiddenType = hiddenDomainToHiddenType(domain);
#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::setHiddenDomainPort() Set own State";
		std::cerr << std::endl;
#endif
		return true;
	}

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::setHiddenDomainPort() Peer Not Found";
		std::cerr << std::endl;
#endif
		return false;
	}

	it->second.hiddenDomain = domain;
	it->second.hiddenPort = domain_port;
	it->second.hiddenNode = true;
	it->second.hiddenType = hiddenDomainToHiddenType(domain);
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::setHiddenDomainPort() Set Peers State";
	std::cerr << std::endl;
#endif

	return true;
}

/**
 * @brief sets the proxy server address for a hidden service
 * @param type hidden service type
 * @param proxy_addr proxy address
 * @return true on success
 */
bool p3PeerMgrIMPL::setProxyServerAddress(const uint32_t type, const struct sockaddr_storage &proxy_addr)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	switch (type) {
	case RS_HIDDEN_TYPE_I2P:
		if (!sockaddr_storage_same(mProxyServerAddressI2P, proxy_addr))
		{
			IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
			mProxyServerAddressI2P = proxy_addr;
		}
		break;
	case RS_HIDDEN_TYPE_TOR:
		if (!sockaddr_storage_same(mProxyServerAddressTor, proxy_addr))
		{
			IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
			mProxyServerAddressTor = proxy_addr;
		}
		break;
	case RS_HIDDEN_TYPE_UNKNOWN:
	default:
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::setProxyServerAddress() unknown hidden type " << type << " -> false";
	std::cerr << std::endl;
#endif
		return false;
	}

	return true;
}

bool p3PeerMgrIMPL::resetOwnExternalAddressList()
{
    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

    mOwnState.ipAddrs.mLocal.mAddrs.clear() ;
    mOwnState.ipAddrs.mExt.mAddrs.clear() ;

    IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/

    return true ;
}

/**
 * @brief returs proxy server status for a hidden service proxy
 * @param type hidden service type
 * @param proxy_status
 * @return true on success
 */
bool p3PeerMgrIMPL::getProxyServerStatus(const uint32_t type, uint32_t& proxy_status)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	switch (type) {
	case RS_HIDDEN_TYPE_I2P:
		proxy_status = mProxyServerStatusI2P;
		break;
	case RS_HIDDEN_TYPE_TOR:
		proxy_status = mProxyServerStatusTor;
		break;
	case RS_HIDDEN_TYPE_UNKNOWN:
	default:
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::getProxyServerStatus() unknown hidden type " << type << " -> false";
	std::cerr << std::endl;
#endif
		return false;
	}

	return true;
}

/**
 * @brief returs proxy server address for a hidden service proxy
 * @param type hidden service type
 * @param proxy_addr
 * @return true on success
 */
bool p3PeerMgrIMPL::getProxyServerAddress(const uint32_t type, struct sockaddr_storage &proxy_addr)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	switch (type) {
	case RS_HIDDEN_TYPE_I2P:
		proxy_addr = mProxyServerAddressI2P;
		break;
	case RS_HIDDEN_TYPE_TOR:
		proxy_addr = mProxyServerAddressTor;
		break;
	case RS_HIDDEN_TYPE_UNKNOWN:
	default:
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::getProxyServerAddress() unknown hidden type " << type << " -> false";
	std::cerr << std::endl;
#endif
		return false;
	}
	return true;
}

/**
 * @brief looks up the proxy address and domain/port that have to be used when connecting to a peer
 * @param ssl_id peer to connect to
 * @param proxy_addr proxy address to be used
 * @param domain_addr domain to connect to
 * @param domain_port port to connect to
 * @return true on success
 */
bool p3PeerMgrIMPL::getProxyAddress(const RsPeerId &ssl_id, struct sockaddr_storage &proxy_addr, std::string &domain_addr, uint16_t &domain_port)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
		return false;
	}

	if (!it->second.hiddenNode)
	{
		return false;
	}

	domain_addr = it->second.hiddenDomain;
	domain_port = it->second.hiddenPort;

	switch (it->second.hiddenType) {
	case RS_HIDDEN_TYPE_I2P:
		proxy_addr = mProxyServerAddressI2P;
		break;
	case RS_HIDDEN_TYPE_TOR:
		proxy_addr = mProxyServerAddressTor;
		break;
	case RS_HIDDEN_TYPE_UNKNOWN:
	default:
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::getProxyAddress() no valid hidden type (" << it->second.hiddenType << ") for peer id " << ssl_id << " -> false";
	std::cerr << std::endl;
#endif
		return false;
	}
	return true;
}

// Placeholder until we implement this functionality.
uint32_t p3PeerMgrIMPL::getConnectionType(const RsPeerId &/*sslId*/)
{
	return RS_NET_CONN_TYPE_FRIEND;
}

int p3PeerMgrIMPL::getFriendCount(bool ssl, bool online)
{
	if (online) {
		// count only online id's
		std::list<RsPeerId> onlineIds;
		mLinkMgr->getOnlineList(onlineIds);

		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		std::set<RsPgpId> gpgIds;
		int count = 0;

		std::map<RsPeerId, peerState>::iterator it;
		for(it = mFriendList.begin(); it != mFriendList.end(); ++it) {
			if (online && std::find(onlineIds.begin(), onlineIds.end(), it->first) == onlineIds.end()) {
				continue;
			}
			if (ssl) {
				// count ssl id's only
				count++;
			} else {
				// count unique gpg id's
				gpgIds.insert(it->second.gpg_id);
			}
		}

		return ssl ? count : gpgIds.size();
	}

	if (ssl) {
		// count all ssl id's
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
		return mFriendList.size();
	}

	// count all gpg id's
	std::list<RsPgpId> gpgIds;
	AuthGPG::getAuthGPG()->getGPGAcceptedList(gpgIds);

	// add own gpg id, if we have more than one location
	std::list<RsPeerId> ownSslIds;
	getAssociatedPeers(AuthGPG::getAuthGPG()->getGPGOwnId(), ownSslIds);

	return gpgIds.size() + ((ownSslIds.size() > 0) ? 1 : 0);
}

bool p3PeerMgrIMPL::getFriendNetStatus(const RsPeerId &id, peerState &state)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(id);
	if (it == mFriendList.end())
	{
		return false;
	}

	state = it->second;
	return true;
}


bool p3PeerMgrIMPL::getOthersNetStatus(const RsPeerId &id, peerState &state)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mOthersList.find(id);
	if (it == mOthersList.end())
	{
		return false;
	}

	state = it->second;
	return true;
}

int p3PeerMgrIMPL::getConnectAddresses(const RsPeerId &id, 
					struct sockaddr_storage &lAddr, struct sockaddr_storage &eAddr, 
					pqiIpAddrSet &histAddrs, std::string &dyndns)
{

	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	
	/* check for existing */
	std::map<RsPeerId, peerState>::iterator it;
	it = mFriendList.find(id);
	if (it == mFriendList.end())
	{
		/* ERROR */
		std::cerr << "p3PeerMgrIMPL::getConnectAddresses() ERROR unknown Peer";
		std::cerr << std::endl;
		return 0;
	}
	
	lAddr = it->second.localaddr;
	eAddr = it->second.serveraddr;
	histAddrs = it->second.ipAddrs;
	dyndns = it->second.dyndns;

	return 1;
}



bool    p3PeerMgrIMPL::haveOnceConnected()
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check for existing */
        std::map<RsPeerId, peerState>::iterator it;
	for(it = mFriendList.begin(); it != mFriendList.end(); ++it)
	{
		if (it->second.lastcontact > 0)
		{
#ifdef PEER_DEBUG
 			std::cerr << "p3PeerMgrIMPL::haveOnceConnected() lastcontact: ";
			std::cerr << time(NULL) - it->second.lastcontact << " for id: " << it->first;
			std::cerr << std::endl;
#endif

			return true;
		}
	}

#ifdef PEER_DEBUG
 	std::cerr << "p3PeerMgrIMPL::haveOnceConnected() all Last Contacts = 0";
	std::cerr << std::endl;
#endif

	return false;
}



/*******************************************************************/
/*******************************************************************/

bool p3PeerMgrIMPL::addFriend(const RsPeerId& input_id, const RsPgpId& input_gpg_id, uint32_t netMode, uint16_t vs_disc, uint16_t vs_dht, time_t lastContact,ServicePermissionFlags service_flags)
{
	bool notifyLinkMgr = false;
	RsPeerId id = input_id ;
	RsPgpId gpg_id = input_gpg_id ;

	rslog(RSL_WARNING, p3peermgrzone, "p3PeerMgr::addFriend() id: " + id.toStdString());

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/


		if (id == AuthSSL::getAuthSSL()->OwnId()) 
		{
#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::addFriend() cannot add own id as a friend." << std::endl;
#endif
			/* (1) already exists */
			return false;
		}
		/* so four possibilities
		 * (1) already exists as friend -> do nothing.
		 * (2) is in others list -> move over.
		 * (3) is non-existant -> create new one.
		 */

#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::addFriend() " << id << "; gpg_id : " << gpg_id << std::endl;
#endif

		std::map<RsPeerId, peerState>::iterator it;
		if (mFriendList.end() != mFriendList.find(id))
		{
#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::addFriend() Already Exists" << std::endl;
#endif
			/* (1) already exists */
			return true;
		}

		//Authentication is now tested at connection time, we don't store the ssl cert anymore
		//
		if (!AuthGPG::getAuthGPG()->isGPGAccepted(gpg_id) &&  gpg_id != AuthGPG::getAuthGPG()->getGPGOwnId())
		{
#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::addFriend() gpg is not accepted" << std::endl;
#endif
			/* no auth */
			return false;
		}


		/* check if it is in others */
		if (mOthersList.end() != (it = mOthersList.find(id)))
		{
			/* (2) in mOthersList -> move over */
#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::addFriend() Move from Others" << std::endl;
#endif

			mFriendList[id] = it->second;
			mOthersList.erase(it);

			it = mFriendList.find(id);

			/* setup connectivity parameters */
			it->second.vs_disc = vs_disc;
			it->second.vs_dht = vs_dht;
			
			it->second.netMode  = netMode;
			it->second.lastcontact = lastContact;

			mStatusChanged = true;

			notifyLinkMgr = true;

			IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
		}
		else
		{
#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::addFriend() Creating New Entry" << std::endl;
#endif

			/* create a new entry */
			peerState pstate;

			pstate.id = id;
			pstate.gpg_id = gpg_id;
			pstate.name = AuthGPG::getAuthGPG()->getGPGName(gpg_id);
			
			pstate.vs_disc = vs_disc;
			pstate.vs_dht = vs_dht;
			pstate.netMode = netMode;
			pstate.lastcontact = lastContact;

			/* addr & timestamps -> auto cleared */

			mFriendList[id] = pstate;

			mStatusChanged = true;

			notifyLinkMgr = true;

			IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
		}
	}

	if (notifyLinkMgr)
	{
		mLinkMgr->addFriend(id, vs_dht != RS_VS_DHT_OFF);
	}

	service_flags &= servicePermissionFlags(gpg_id) ; // Always reduce the permissions. 
#ifdef RS_CHATSERVER //Defined by chatserver
	setServicePermissionFlags(gpg_id,RS_NODE_PERM_NONE) ;
#else
	setServicePermissionFlags(gpg_id,service_flags) ;
#endif

#ifdef PEER_DEBUG
	printPeerLists(std::cerr);
	mLinkMgr->printPeerLists(std::cerr);
#endif
	
	return true;
}

bool p3PeerMgrIMPL::removeFriend(const RsPgpId &id)
{
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::removeFriend() for id : " << id << std::endl;
	std::cerr << "p3PeerMgrIMPL::removeFriend() mFriendList.size() : " << mFriendList.size() << std::endl;
#endif

        rslog(RSL_WARNING, p3peermgrzone, "p3PeerMgr::removeFriend() id: " + id.toStdString());

	std::list<RsPeerId> sslid_toRemove; // This is a list of SSLIds.
	rsPeers->getAssociatedSSLIds(id,sslid_toRemove) ;

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		/* move to othersList */
        //bool success = false;
		std::map<RsPeerId, peerState>::iterator it;
		//remove ssl and gpg_ids
		for(it = mFriendList.begin(); it != mFriendList.end(); ++it)
		{
			if (find(sslid_toRemove.begin(),sslid_toRemove.end(),it->second.id) != sslid_toRemove.end())
			{
#ifdef PEER_DEBUG
				std::cerr << "p3PeerMgrIMPL::removeFriend() friend found in the list." << id << std::endl;
#endif
				peerState peer = it->second;

				sslid_toRemove.push_back(it->second.id);

				mOthersList[it->second.id] = peer;
				mStatusChanged = true;

				//success = true;
			}
		}

		for(std::list<RsPeerId>::iterator rit = sslid_toRemove.begin(); rit != sslid_toRemove.end(); ++rit)
			if (mFriendList.end() != (it = mFriendList.find(*rit))) 
				mFriendList.erase(it);

		std::map<RsPgpId,ServicePermissionFlags>::iterator it2 = mFriendsPermissionFlags.find(id) ; 

		if(it2 != mFriendsPermissionFlags.end())
			mFriendsPermissionFlags.erase(it2);

#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::removeFriend() new mFriendList.size() : " << mFriendList.size() << std::endl;
#endif
	}

	std::list<RsPeerId>::iterator rit;
	for(rit = sslid_toRemove.begin(); rit != sslid_toRemove.end(); ++rit)
	{
		mLinkMgr->removeFriend(*rit);
	}

	/* remove id from all groups */

	std::list<RsPgpId> ids ;
	ids.push_back(id) ;
    assignPeersToGroup(RsNodeGroupId(), ids, false);

	IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/

#ifdef PEER_DEBUG
	printPeerLists(std::cerr);
	mLinkMgr->printPeerLists(std::cerr);
#endif

        return !sslid_toRemove.empty();
}
bool p3PeerMgrIMPL::removeFriend(const RsPeerId &id, bool removePgpId)
{

#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::removeFriend() for id : " << id << std::endl;
	std::cerr << "p3PeerMgrIMPL::removeFriend() mFriendList.size() : " << mFriendList.size() << std::endl;
#endif

        rslog(RSL_WARNING, p3peermgrzone, "p3PeerMgr::removeFriend() id: " + id.toStdString());

	std::list<RsPeerId> sslid_toRemove; // This is a list of SSLIds.
	std::list<RsPgpId> pgpid_toRemove; // This is a list of SSLIds.

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		/* move to othersList */
		//bool success = false;
		std::map<RsPeerId, peerState>::iterator it;
		//remove ssl and gpg_ids
		for(it = mFriendList.begin(); it != mFriendList.end(); ++it)
		{
			if (it->second.id == id)
			{
#ifdef PEER_DEBUG
				std::cerr << "p3PeerMgrIMPL::removeFriend() friend found in the list." << id << std::endl;
#endif
				peerState peer = it->second;

				sslid_toRemove.push_back(it->second.id);
				if(removePgpId)
					pgpid_toRemove.push_back(it->second.gpg_id);

				mOthersList[id] = peer;
				mStatusChanged = true;

				//success = true;
			}
		}

		for(std::list<RsPeerId>::iterator rit = sslid_toRemove.begin(); rit != sslid_toRemove.end(); ++rit)
			if (mFriendList.end() != (it = mFriendList.find(*rit))) 
				mFriendList.erase(it);

		std::map<RsPgpId,ServicePermissionFlags>::iterator it2 ;

		for(std::list<RsPgpId>::iterator rit = pgpid_toRemove.begin(); rit != pgpid_toRemove.end(); ++rit)
			if (mFriendsPermissionFlags.end() != (it2 = mFriendsPermissionFlags.find(*rit))) 
				mFriendsPermissionFlags.erase(it2);

#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::removeFriend() new mFriendList.size() : " << mFriendList.size() << std::endl;
#endif
	}

	std::list<RsPeerId>::iterator rit;
	for(rit = sslid_toRemove.begin(); rit != sslid_toRemove.end(); ++rit)
	{
		mLinkMgr->removeFriend(*rit);
	}

	/* remove id from all groups */

    assignPeersToGroup(RsNodeGroupId(), pgpid_toRemove, false);

	IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/

#ifdef PEER_DEBUG
	printPeerLists(std::cerr);
	mLinkMgr->printPeerLists(std::cerr);
#endif

        return !sslid_toRemove.empty();
}


void p3PeerMgrIMPL::printPeerLists(std::ostream &out)
{
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		out << "p3PeerMgrIMPL::printPeerLists() Friend List";
		out << std::endl;


		std::map<RsPeerId, peerState>::iterator it;
		for(it = mFriendList.begin(); it != mFriendList.end(); ++it)
		{
			out << "\t SSL ID: " << it->second.id;
			out << "\t GPG ID: " << it->second.gpg_id;
			out << std::endl;
		}

		out << "p3PeerMgrIMPL::printPeerLists() Others List";
		out << std::endl;
		for(it = mOthersList.begin(); it != mOthersList.end(); ++it)
		{
			out << "\t SSL ID: " << it->second.id;
			out << "\t GPG ID: " << it->second.gpg_id;
			out << std::endl;
		}
	}

	return;
}



/*******************************************************************/
/*******************************************************************/


/**********************************************************************
 **********************************************************************
 ******************** External Setup **********************************
 **********************************************************************
 **********************************************************************/


/* This function should only be called from NetMgr,
 * as it doesn't call back to there.
 */

bool 	p3PeerMgrIMPL::UpdateOwnAddress(const struct sockaddr_storage &localAddr, const struct sockaddr_storage &extAddr)
{
#ifdef PEER_DEBUG
    std::cerr << "p3PeerMgrIMPL::UpdateOwnAddress(";
    std::cerr << sockaddr_storage_tostring(localAddr);
    std::cerr << ", ";
    std::cerr << sockaddr_storage_tostring(extAddr);
    std::cerr << ")" << std::endl;
#endif

    if(!rsBanList->isAddressAccepted(localAddr, RSBANLIST_CHECKING_FLAGS_BLACKLIST))
    {
        std::cerr << "(SS) Trying to set own IP to a banned IP " << sockaddr_storage_iptostring(localAddr) << ". This probably means that a friend in under traffic re-routing attack." << std::endl;
        return false ;
    }

    {
        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        //update ip address list
        pqiIpAddress ipAddressTimed;
        ipAddressTimed.mAddr = localAddr;
        ipAddressTimed.mSeenTime = time(NULL);
        ipAddressTimed.mSrc = 0 ;
        mOwnState.ipAddrs.updateLocalAddrs(ipAddressTimed);

        mOwnState.localaddr = localAddr;
    }


    {
        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        //update ip address list
        pqiIpAddress ipAddressTimed;
        ipAddressTimed.mAddr = extAddr;
        ipAddressTimed.mSeenTime = time(NULL);
        ipAddressTimed.mSrc = 0 ;
        mOwnState.ipAddrs.updateExtAddrs(ipAddressTimed);

        /* Attempted Fix to MANUAL FORWARD Mode....
         * don't update the server address - if we are in this mode
         *
         * It is okay - if they get it wrong, as we put the address in the address list anyway.
         * This should keep people happy, and allow for misconfiguration!
         */

        if (mOwnState.netMode & RS_NET_MODE_TRY_EXT)
        {
            /**** THIS CASE SHOULD NOT BE TRIGGERED ****/
            std::cerr << "p3PeerMgrIMPL::UpdateOwnAddress() Disabling Update of Server Port ";
            std::cerr << " as MANUAL FORWARD Mode (ERROR - SHOULD NOT BE TRIGGERED: TRY_EXT_MODE)";
            std::cerr << std::endl;
            std::cerr << "Address is Now: ";
            std::cerr << sockaddr_storage_tostring(mOwnState.serveraddr);
            std::cerr << std::endl;
        }
        else if (mOwnState.netMode & RS_NET_MODE_EXT)
        {
            sockaddr_storage_copyip(mOwnState.serveraddr,extAddr);

            std::cerr << "p3PeerMgrIMPL::UpdateOwnAddress() Disabling Update of Server Port ";
            std::cerr << " as MANUAL FORWARD Mode";
            std::cerr << std::endl;
            std::cerr << "Address is Now: ";
            std::cerr << sockaddr_storage_tostring(mOwnState.serveraddr);
            std::cerr << std::endl;
        }
        else
        {
            mOwnState.serveraddr = extAddr;
        }
    }

    IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
    mLinkMgr->setLocalAddress(localAddr);

    return true;
}




bool    p3PeerMgrIMPL::setLocalAddress(const RsPeerId &id, const struct sockaddr_storage &addr)
{
	bool changed = false;

	if (id == AuthSSL::getAuthSSL()->OwnId())
	{
		{
			RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
			if (!sockaddr_storage_same(mOwnState.localaddr, addr))
			{
				mOwnState.localaddr = addr;
				changed = true;
			}
		}

		if (changed)
		{
			IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
			
			mNetMgr->setLocalAddress(addr);
			mLinkMgr->setLocalAddress(addr);
		}
		return changed;
	}

	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	/* check if it is a friend */
	std::map<RsPeerId, peerState>::iterator it;
	if (mFriendList.end() == (it = mFriendList.find(id)))
	{
		if (mOthersList.end() == (it = mOthersList.find(id)))
		{
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::setLocalAddress() cannot add addres info : peer id not found in friend list  id: " << id << std::endl;
#endif
			return false;
		}
	}

	/* "it" points to peer */
	if (!sockaddr_storage_same(it->second.localaddr, addr))
	{
		it->second.localaddr = addr;
		changed = true;
	}

#if 0
	//update ip address list
	IpAddressTimed ipAddressTimed;
	ipAddressTimed.ipAddr = addr;
	ipAddressTimed.seenTime = time(NULL);
	it->second.updateIpAddressList(ipAddressTimed);
#endif

	if (changed) {
		IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
	}

	return changed;
}

bool    p3PeerMgrIMPL::setExtAddress(const RsPeerId &id, const struct sockaddr_storage &addr)
{
    bool changed = false;
    uint32_t check_res = 0 ;

    if(!rsBanList->isAddressAccepted(addr,RSBANLIST_CHECKING_FLAGS_BLACKLIST,&check_res))
    {
        std::cerr << "(SS) trying to set external contact address for peer " << id << " to a banned address " << sockaddr_storage_iptostring(addr )<< std::endl;
        return false ;
    }

	if (id == AuthSSL::getAuthSSL()->OwnId())
	{
		{
			RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
			if (!sockaddr_storage_same(mOwnState.serveraddr, addr))
			{
				mOwnState.serveraddr = addr;
				changed = true;
			}
		}
		
		mNetMgr->setExtAddress(addr);
		
		return changed;
	}

	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	/* check if it is a friend */
	std::map<RsPeerId, peerState>::iterator it;
	if (mFriendList.end() == (it = mFriendList.find(id)))
	{
		if (mOthersList.end() == (it = mOthersList.find(id)))
		{
#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::setLocalAddress() cannot add addres info : peer id not found in friend list  id: " << id << std::endl;
#endif
			return false;
		}
	}

	/* "it" points to peer */
	if (!sockaddr_storage_same(it->second.serveraddr, addr))
    {
		it->second.serveraddr = addr;
		changed = true;
	}

#if 0
	//update ip address list
	IpAddressTimed ipAddressTimed;
	ipAddressTimed.ipAddr = addr;
	ipAddressTimed.seenTime = time(NULL);
	it->second.updateIpAddressList(ipAddressTimed);
#endif

	if (changed) {
		IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
	}

	return changed;
}


bool p3PeerMgrIMPL::setDynDNS(const RsPeerId &id, const std::string &dyndns)
{
    bool changed = false;

    if (id == AuthSSL::getAuthSSL()->OwnId())
    {
        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        if (mOwnState.dyndns.compare(dyndns) != 0) {
            mOwnState.dyndns = dyndns;
            IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
            changed = true;
        }
        return changed;
    }

    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
    /* check if it is a friend */
    std::map<RsPeerId, peerState>::iterator it;
    if (mFriendList.end() == (it = mFriendList.find(id)))
    {
            if (mOthersList.end() == (it = mOthersList.find(id)))
            {
#ifdef PEER_DEBUG
                                    std::cerr << "p3PeerMgrIMPL::setDynDNS() cannot add dyn dns info : peer id not found in friend list  id: " << id << std::endl;
#endif
                    return false;
            }
    }

    /* "it" points to peer */
    if (it->second.dyndns.compare(dyndns) != 0) {
        it->second.dyndns = dyndns;
        IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
        changed = true;
    }

    return changed;
}

struct ZeroedInt
{
    ZeroedInt() { n=0 ;}
    int n ;
};

bool p3PeerMgrIMPL::addCandidateForOwnExternalAddress(const RsPeerId &from, const sockaddr_storage &addr)
{
    // The algorithm is the following:
    // - collect for each friend the last external connection address that is reported
    // - everytime the list is changed, parse it entirely and
    //		* emit a warnign when the address is unknown
    // 		* if multiple peers report the same address => notify the LinkMgr that the external address had changed.

	sockaddr_storage addr_filtered ;
	sockaddr_storage_clear(addr_filtered) ;
	sockaddr_storage_copyip(addr_filtered,addr) ;

#ifdef PEER_DEBUG
	std::cerr << "Own external address is " << sockaddr_storage_iptostring(addr_filtered) << ", as reported by friend " << from << std::endl;
#endif

	if(!sockaddr_storage_isExternalNet(addr_filtered))
	{
#ifdef PEER_DEBUG
		std::cerr << "  address is not an external address. Returning false" << std::endl ;
#endif
		return false ;
	}

    // Update a list of own IPs:
    //	- remove old values for that same peer
    //	- remove values for non connected peers
    
    {
	    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
            
	    mReportedOwnAddresses[from] = addr_filtered ;

	    for(std::map<RsPeerId,sockaddr_storage>::iterator it(mReportedOwnAddresses.begin());it!=mReportedOwnAddresses.end();)
		    if(!mLinkMgr->isOnline(it->first))
		    {
			    std::map<RsPeerId,sockaddr_storage>::iterator tmp(it) ;
			    ++tmp ;
			    mReportedOwnAddresses.erase(it) ;
			    it=tmp ;
		    }
	    else
		    ++it ;

	    sockaddr_storage current_best_ext_address_guess ;
	    uint32_t count ;

	    locked_computeCurrentBestOwnExtAddressCandidate(current_best_ext_address_guess,count) ;

	    std::cerr << "p3PeerMgr::  Current external address is calculated to be: " << sockaddr_storage_iptostring(current_best_ext_address_guess) << " (simultaneously reported by " << count << " peers)." << std::endl;
    }
       
    // now current 

    sockaddr_storage own_addr ;

    if(!mNetMgr->getExtAddress(own_addr))
    {
#ifdef PEER_DEBUG
        std::cerr << "  cannot get current external address. Returning false" << std::endl;
#endif
        return false ;
    }
#ifdef PEER_DEBUG
    std::cerr << "  current external address is known to be " << sockaddr_storage_iptostring(own_addr) << std::endl;
#endif

    // Notify for every friend that has reported a wrong external address, except if that address is in the IP whitelist.

    if((!rsBanList->isAddressAccepted(addr_filtered,RSBANLIST_CHECKING_FLAGS_WHITELIST)) && (!sockaddr_storage_sameip(own_addr,addr_filtered)))
    {
        std::cerr << "  Peer " << from << " reports a connection address (" << sockaddr_storage_iptostring(addr_filtered) <<") that is not your current external address (" << sockaddr_storage_iptostring(own_addr) << "). This is weird." << std::endl;

        RsServer::notify()->AddFeedItem(RS_FEED_ITEM_SEC_IP_WRONG_EXTERNAL_IP_REPORTED, from.toStdString(), sockaddr_storage_iptostring(own_addr), sockaddr_storage_iptostring(addr));
    }
    
    // we could also sweep over all connected friends and see if some report a different address.

    return true ;
}

bool p3PeerMgrIMPL::locked_computeCurrentBestOwnExtAddressCandidate(sockaddr_storage& addr, uint32_t& count)
{
    std::map<sockaddr_storage,ZeroedInt> addr_counts ;
    
    for(std::map<RsPeerId,sockaddr_storage>::iterator it(mReportedOwnAddresses.begin());it!=mReportedOwnAddresses.end();++it)
	    ++addr_counts[it->second].n ;

#ifdef PEER_DEBUG
    std::cerr << "Current ext addr statistics:" << std::endl;
#endif
    
    count = 0 ;
    
    for(std::map<sockaddr_storage,ZeroedInt>::const_iterator it(addr_counts.begin());it!=addr_counts.end();++it)
    {
        if(uint32_t(it->second.n) > count)
        {
            addr = it->first ;
            count = it->second.n ;
        }
        
#ifdef PEER_DEBUG
        std::cerr << sockaddr_storage_iptostring(it->first) << " : " << it->second.n << std::endl;
#endif
    }
    
    return true ;
}
 
bool p3PeerMgrIMPL::getExtAddressReportedByFriends(sockaddr_storage &addr, uint8_t& /*isstable*/)
{
        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
        
        uint32_t count ;
        
        locked_computeCurrentBestOwnExtAddressCandidate(addr,count) ;
        
#ifdef PEER_DEBUG
        std::cerr << "Estimation count = " << count << ". Trusted? = " << (count>=2) << std::endl;
#endif
        
        return count >= 2 ;// 2 is not conservative enough. 3 should be probably better.	
}

static bool cleanIpList(std::list<pqiIpAddress>& lst,const RsPeerId& pid,p3LinkMgr *link_mgr)
{
    bool changed = false ;
    time_t now = time(NULL) ;

    for(std::list<pqiIpAddress>::iterator it2(lst.begin());it2 != lst.end();)
    {
#ifdef PEER_DEBUG
    std::cerr << "Checking IP address " << sockaddr_storage_iptostring( (*it2).mAddr) << " for peer " << pid << ", age = " << now - (*it2).mSeenTime << std::endl;
#else
    /* remove unused parameter warnings */
    (void) pid;
#endif
      if(!link_mgr->checkPotentialAddr( (*it2).mAddr,now - (*it2).mSeenTime))
      {
#ifdef PEER_DEBUG
        std::cerr << "  (SS) Removing Banned/old IP address " << sockaddr_storage_iptostring( (*it2).mAddr) << " from peer " << pid << ", age = " << now - (*it2).mSeenTime << std::endl;
#endif

            std::list<pqiIpAddress>::iterator ittmp = it2 ;
            ++ittmp ;
            lst.erase(it2) ;
            it2 = ittmp ;

            changed = true ;
        }
        else
            ++it2 ;
    }

    return changed ;
}

bool    p3PeerMgrIMPL::updateAddressList(const RsPeerId& id, const pqiIpAddrSet &addrs)
{
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::setAddressList() called for id : " << id << std::endl;
#endif
    // first clean the list from potentially banned IPs.

    pqiIpAddrSet clean_set = addrs ;

    cleanIpList(clean_set.mExt.mAddrs,id,mLinkMgr) ;
    cleanIpList(clean_set.mLocal.mAddrs,id,mLinkMgr) ;

	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	/* check if it is our own ip */
	if (id == getOwnId()) 
	{
        mOwnState.ipAddrs.updateAddrs(clean_set);
		return true;
	}

	/* check if it is a friend */
	std::map<RsPeerId, peerState>::iterator it;
	if (mFriendList.end() == (it = mFriendList.find(id)))
	{
            if (mOthersList.end() == (it = mOthersList.find(id)))
            {
#ifdef PEER_DEBUG
				std::cerr << "p3PeerMgrIMPL::setLocalAddress() cannot add addres info : peer id not found in friend list. id: " << id << std::endl;
#endif
                    return false;
            }
	}

	/* "it" points to peer */
    it->second.ipAddrs.updateAddrs(clean_set);
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::setLocalAddress() Updated Address for: " << id;
	std::cerr << std::endl;
	std::string addrstr;
	it->second.ipAddrs.printAddrs(addrstr);
	std::cerr << addrstr;
	std::cerr << std::endl;
#endif

	IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/

	return true;
}


bool    p3PeerMgrIMPL::updateCurrentAddress(const RsPeerId& id, const pqiIpAddress &addr)
{
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::updateCurrentAddress() called for id : " << id << std::endl;
#endif
	
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	
	/* cannot be own id */
	
	/* check if it is a friend */
	std::map<RsPeerId, peerState>::iterator it;
	if (mFriendList.end() == (it = mFriendList.find(id)))
	{
		if (mOthersList.end() == (it = mOthersList.find(id)))
		{
			std::cerr << "p3PeerMgrIMPL::updateCurrentAddress() ERROR peer id not found: " << id << std::endl;
			return false;
		}
	}

	if (sockaddr_storage_isPrivateNet(addr.mAddr))
	{
		it->second.ipAddrs.updateLocalAddrs(addr);
		it->second.localaddr = addr.mAddr;
	}
	else
	{
		it->second.ipAddrs.updateExtAddrs(addr);
		it->second.serveraddr = addr.mAddr;
	}

#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::updatedCurrentAddress() Updated Address for: " << id;
	std::cerr << std::endl;
	std::string addrstr;
	it->second.ipAddrs.printAddrs(addrstr);
	std::cerr << addrstr;
	std::cerr << std::endl;
#endif
	
	IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
	
	return true;
}
	

bool    p3PeerMgrIMPL::updateLastContact(const RsPeerId& id)
{
#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::updateLastContact() called for id : " << id << std::endl;
#endif
	
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	
	/* cannot be own id */
	
	/* check if it is a friend */
	std::map<RsPeerId, peerState>::iterator it;
	if (mFriendList.end() == (it = mFriendList.find(id)))
	{
		if (mOthersList.end() == (it = mOthersList.find(id)))
		{
			std::cerr << "p3PeerMgrIMPL::updateLastContact() ERROR peer id not found: " << id << std::endl;
			return false;
		}
	}

	it->second.lastcontact = time(NULL);

	IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
	
	return true;
}

bool    p3PeerMgrIMPL::setNetworkMode(const RsPeerId &id, uint32_t netMode)
{
	if (id == AuthSSL::getAuthSSL()->OwnId())
	{
		return setOwnNetworkMode(netMode);
	}

	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	/* check if it is a friend */
	std::map<RsPeerId, peerState>::iterator it;
	if (mFriendList.end() == (it = mFriendList.find(id)))
	{
		if (mOthersList.end() == (it = mOthersList.find(id)))
		{
			return false;
		}
	}

	bool changed = false;

	/* "it" points to peer */
	if (it->second.netMode != netMode)
	{
		it->second.netMode = netMode;
		IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
		changed = true;
	}

	return changed;
}

bool    p3PeerMgrIMPL::setLocation(const RsPeerId &id, const std::string &location)
{
        bool changed = false;

        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
        std::cerr << "p3PeerMgrIMPL::setLocation() called for id : " << id << "; with location " << location << std::endl;
#endif
        if (id == AuthSSL::getAuthSSL()->OwnId())
        {
            if (mOwnState.location.compare(location) != 0) {
                mOwnState.location = location;
                changed = true;
            }
            return changed;
        }

        /* check if it is a friend */
        std::map<RsPeerId, peerState>::iterator it;
        if (mFriendList.end() != (it = mFriendList.find(id))) {
            if (it->second.location.compare(location) != 0) {
                it->second.location = location;
                changed = true;
            }
        }
        return changed;
}

bool    p3PeerMgrIMPL::setVisState(const RsPeerId &id, uint16_t vs_disc, uint16_t vs_dht)
{
	{
		std::string out;
		rs_sprintf(out, "p3PeerMgr::setVisState(%s, %u, %u)", id.toStdString().c_str(), vs_disc, vs_dht);
		rslog(RSL_WARNING, p3peermgrzone, out);
	}

	if (id == AuthSSL::getAuthSSL()->OwnId())
	{
		return setOwnVisState(vs_disc, vs_dht);
	}

	bool isFriend = false;
	bool changed = false;
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		/* check if it is a friend */
		std::map<RsPeerId, peerState>::iterator it;
		if (mFriendList.end() == (it = mFriendList.find(id)))
		{
			if (mOthersList.end() == (it = mOthersList.find(id)))
			{
				return false;
			}
		}
		else
		{
			isFriend = true;
		}

		/* "it" points to peer */
		if ((it->second.vs_disc != vs_disc) || (it->second.vs_dht = vs_dht))
		{
			it->second.vs_disc = vs_disc;
			it->second.vs_dht = vs_dht;
			changed = true;

#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgrIMPL::setVisState(" << id << ", DISC: " << vs_disc << " DHT: " << vs_dht << ") ";
			std::cerr << " NAME: " << it->second.name;

			switch(it->second.vs_disc)
			{
				default:
				case RS_VS_DISC_OFF:
					std::cerr << " NO-DISC ";
					break;
				case RS_VS_DISC_MINIMAL:
					std::cerr << " MIN-DISC ";
					break;
				case RS_VS_DISC_FULL:
					std::cerr << " FULL-DISC ";
					break;
			}
			switch(it->second.vs_dht)
			{
				default:
				case RS_VS_DHT_OFF:
					std::cerr << " NO-DHT ";
					break;
				case RS_VS_DHT_PASSIVE:
					std::cerr << " PASSIVE-DHT ";
					break;
				case RS_VS_DHT_FULL:
					std::cerr << " FULL-DHT ";
					break;
			}
			std::cerr << std::endl;
#endif
		}
	}
	if(isFriend && changed)
	{
		mLinkMgr->setFriendVisibility(id, vs_dht != RS_VS_DHT_OFF);
	}

	if (changed) {
		IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
	}

	return changed;
}




/*******************************************************************/


/**********************************************************************
 **********************************************************************
 ******************** p3Config functions ******************************
 **********************************************************************
 **********************************************************************/

        /* Key Functions to be overloaded for Full Configuration */

RsSerialiser *p3PeerMgrIMPL::setupSerialiser()
{
	RsSerialiser *rss = new RsSerialiser();
	rss->addSerialType(new RsPeerConfigSerialiser());
	rss->addSerialType(new RsGeneralConfigSerialiser()) ;

	return rss;
}


bool p3PeerMgrIMPL::saveList(bool &cleanup, std::list<RsItem *>& saveData)
{
	/* create a list of current peers */
    cleanup = true;
	bool useExtAddrFinder = mNetMgr->getIPServersEnabled();

	/* gather these information before mPeerMtx is locked! */
	struct sockaddr_storage proxy_addr_tor, proxy_addr_i2p;
	getProxyServerAddress(RS_HIDDEN_TYPE_TOR, proxy_addr_tor);
	getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy_addr_i2p);

	mPeerMtx.lock(); /****** MUTEX LOCKED *******/ 

	RsPeerNetItem *item = new RsPeerNetItem();
	item->clear();

	item->peerId = getOwnId();
	item->pgpId = mOwnState.gpg_id;
	item->location = mOwnState.location;

#if 0
	if (mOwnState.netMode & RS_NET_MODE_TRY_EXT)
	{
		item->netMode = RS_NET_MODE_EXT;
	}
	else if (mOwnState.netMode & RS_NET_MODE_TRY_UPNP)
	{
		item->netMode = RS_NET_MODE_UPNP;
	}
	else
	{
		item->netMode = RS_NET_MODE_UDP;
	}
#endif
	item->netMode = mOwnState.netMode;

	item->vs_disc = mOwnState.vs_disc;
	item->vs_dht = mOwnState.vs_dht;
	
	item->lastContact = mOwnState.lastcontact;

	item->localAddrV4.addr = mOwnState.localaddr;
	item->extAddrV4.addr = mOwnState.serveraddr;
	sockaddr_storage_clear(item->localAddrV6.addr);
	sockaddr_storage_clear(item->extAddrV6.addr);
	
	item->dyndns = mOwnState.dyndns;
	mOwnState.ipAddrs.mLocal.loadTlv(item->localAddrList);
	mOwnState.ipAddrs.mExt.loadTlv(item->extAddrList);
	item->domain_addr = mOwnState.hiddenDomain;
	item->domain_port = mOwnState.hiddenPort;

#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgrIMPL::saveList() Own Config Item:" << std::endl;
	item->print(std::cerr, 10);
	std::cerr << std::endl;
#endif

	saveData.push_back(item);

	/* iterate through all friends and save */
        std::map<RsPeerId, peerState>::iterator it;
	for(it = mFriendList.begin(); it != mFriendList.end(); ++it)
	{
		item = new RsPeerNetItem();
		item->clear();

		item->peerId = it->first;
		item->pgpId = (it->second).gpg_id;
		item->location = (it->second).location;
		item->netMode = (it->second).netMode;
		item->vs_disc = (it->second).vs_disc;
		item->vs_dht = (it->second).vs_dht;

		item->lastContact = (it->second).lastcontact;
		
		item->localAddrV4.addr = (it->second).localaddr;
		item->extAddrV4.addr = (it->second).serveraddr;
		sockaddr_storage_clear(item->localAddrV6.addr);
		sockaddr_storage_clear(item->extAddrV6.addr);
		
		
		item->dyndns = (it->second).dyndns;
		(it->second).ipAddrs.mLocal.loadTlv(item->localAddrList);
		(it->second).ipAddrs.mExt.loadTlv(item->extAddrList);

		item->domain_addr = (it->second).hiddenDomain;
		item->domain_port = (it->second).hiddenPort;
        
		saveData.push_back(item);
#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgrIMPL::saveList() Peer Config Item:" << std::endl;
		item->print(std::cerr, 10);
		std::cerr << std::endl;
#endif
	}

	RsPeerBandwidthLimitsItem *pblitem = new RsPeerBandwidthLimitsItem ;
    	pblitem->peers = mPeerBandwidthLimits ;
	saveData.push_back(pblitem) ;
    
	RsPeerServicePermissionItem *sitem = new RsPeerServicePermissionItem ;

	for(std::map<RsPgpId,ServicePermissionFlags>::const_iterator it(mFriendsPermissionFlags.begin());it!=mFriendsPermissionFlags.end();++it)
	{
		sitem->pgp_ids.push_back(it->first) ;
		sitem->service_flags.push_back(it->second) ;
	}
    
	saveData.push_back(sitem) ;

	// Now save config for network digging strategies
	
	RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet ;

	RsTlvKeyValue kv;
	kv.key = kConfigKeyExtIpFinder;
	kv.value = (useExtAddrFinder)?"TRUE":"FALSE" ;
	vitem->tlvkvs.pairs.push_back(kv) ;


	// Store Proxy Server.
	// Tor
#ifdef PEER_DEBUG
	std::cerr << "Saving proxyServerAddress for Tor: " << sockaddr_storage_tostring(proxy_addr_tor);
	std::cerr << std::endl;
#endif

	kv.key = kConfigKeyProxyServerIpAddrTor;
	kv.value = sockaddr_storage_iptostring(proxy_addr_tor);
	vitem->tlvkvs.pairs.push_back(kv) ;

	kv.key = kConfigKeyProxyServerPortTor;
	kv.value = sockaddr_storage_porttostring(proxy_addr_tor);
	vitem->tlvkvs.pairs.push_back(kv) ;

	// I2P
#ifdef PEER_DEBUG
	std::cerr << "Saving proxyServerAddress for I2P: " << sockaddr_storage_tostring(proxy_addr_i2p);
	std::cerr << std::endl;
#endif

	kv.key = kConfigKeyProxyServerIpAddrI2P;
	kv.value = sockaddr_storage_iptostring(proxy_addr_i2p);
	vitem->tlvkvs.pairs.push_back(kv) ;

	kv.key = kConfigKeyProxyServerPortI2P;
	kv.value = sockaddr_storage_porttostring(proxy_addr_i2p);
	vitem->tlvkvs.pairs.push_back(kv) ;
	
	saveData.push_back(vitem);

	/* save groups */

    for ( std::map<RsNodeGroupId,RsGroupInfo>::iterator groupIt = groupList.begin(); groupIt != groupList.end(); ++groupIt)
    {
        RsNodeGroupItem *itm = new RsNodeGroupItem(groupIt->second);
        saveData.push_back(itm) ;
    }

	return true;
}

bool p3PeerMgrIMPL::getMaxRates(const RsPeerId& pid,uint32_t& maxUp,uint32_t& maxDn) 
{
    RsPgpId pgp_id ;
    
    {
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
    
   	std::map<RsPeerId, peerState>::const_iterator it = mFriendList.find(pid) ;
    
    	if(it == mFriendList.end())
            return false ;
        
        pgp_id = it->second.gpg_id ;
    }
    return getMaxRates(pgp_id,maxUp,maxDn) ;
}

bool p3PeerMgrIMPL::getMaxRates(const RsPgpId& pid,uint32_t& maxUp,uint32_t& maxDn) 
{
    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

    std::map<RsPgpId,PeerBandwidthLimits>::const_iterator it2 = mPeerBandwidthLimits.find(pid) ;

    if(it2 != mPeerBandwidthLimits.end())
    {
	    maxUp = it2->second.max_up_rate_kbs ;
	    maxDn = it2->second.max_dl_rate_kbs ;
	    return true ;
    }
    else
    {
	    maxUp = 0;
	    maxDn = 0;
	    return false ;
    }
}
bool p3PeerMgrIMPL::setMaxRates(const RsPgpId& pid,uint32_t maxUp,uint32_t maxDn) 
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        PeerBandwidthLimits& p(mPeerBandwidthLimits[pid]) ;
        
        if(maxUp == p.max_up_rate_kbs && maxDn == p.max_dl_rate_kbs)
            return true ;
        
        std::cerr << "Updating max rates for peer " << pid << " to " << maxUp << " kB/s (up), " << maxDn << " kB/s (dn)" << std::endl;
        
        p.max_up_rate_kbs = maxUp ;
        p.max_dl_rate_kbs = maxDn ;
        
        IndicateConfigChanged();
        
        return true ;
}
        
void    p3PeerMgrIMPL::saveDone()
{
	/* clean up the save List */
	std::list<RsItem *>::iterator it;
	for(it = saveCleanupList.begin(); it != saveCleanupList.end(); ++it)
	{
		delete (*it);
	}

	saveCleanupList.clear();

	/* unlock mutex */
	mPeerMtx.unlock(); /****** MUTEX UNLOCKED *******/
}

bool  p3PeerMgrIMPL::loadList(std::list<RsItem *>& load)
{

    // DEFAULTS.
    bool useExtAddrFinder = true;
    std::string proxyIpAddressTor = kConfigDefaultProxyServerIpAddr;
    uint16_t    proxyPortTor = kConfigDefaultProxyServerPortTor;
    std::string proxyIpAddressI2P = kConfigDefaultProxyServerIpAddr;
    uint16_t    proxyPortI2P = kConfigDefaultProxyServerPortI2P;

    if (load.empty()) {
	    std::cerr << "p3PeerMgrIMPL::loadList() list is empty, it may be a configuration problem."  << std::endl;
	    return false;
    }

#ifdef PEER_DEBUG
    std::cerr << "p3PeerMgrIMPL::loadList() Item Count: " << load.size() << std::endl;
#endif

    RsPeerId ownId = getOwnId();

    /* load the list of peers */
    std::list<RsItem *>::iterator it;
    for(it = load.begin(); it != load.end(); ++it)
    {
	    RsPeerNetItem *pitem = dynamic_cast<RsPeerNetItem *>(*it);
	    if (pitem)
	    {
		    RsPeerId peer_id = pitem->peerId ;
		    RsPgpId peer_pgp_id = pitem->pgpId ;

		    if (peer_id == ownId)
		    {
#ifdef PEER_DEBUG
			    std::cerr << "p3PeerMgrIMPL::loadList() Own Config Item:" << std::endl;
			    pitem->print(std::cerr, 10);
			    std::cerr << std::endl;
#endif
			    /* add ownConfig */
			    setOwnNetworkMode(pitem->netMode);
			    setOwnVisState(pitem->vs_disc, pitem->vs_dht);

			    mOwnState.gpg_id = AuthGPG::getAuthGPG()->getGPGOwnId();
			    mOwnState.location = AuthSSL::getAuthSSL()->getOwnLocation();
		    }
		    else
		    {
#ifdef PEER_DEBUG
			    std::cerr << "p3PeerMgrIMPL::loadList() Peer Config Item:" << std::endl;
			    pitem->print(std::cerr, 10);
			    std::cerr << std::endl;
#endif
			    /* ************* */
			    // permission flags is used as a mask for the existing perms, so we set it to 0xffff
			    addFriend(peer_id, peer_pgp_id, pitem->netMode, pitem->vs_disc, pitem->vs_dht, pitem->lastContact, RS_NODE_PERM_ALL);
			    setLocation(pitem->peerId, pitem->location);
		    }

		    if (pitem->netMode == RS_NET_MODE_HIDDEN)
		    {
			    /* set only the hidden stuff & localAddress */
			    setLocalAddress(peer_id, pitem->localAddrV4.addr);
			    setHiddenDomainPort(peer_id, pitem->domain_addr, pitem->domain_port);

		    }
		    else
		    {
			    setLocalAddress(peer_id, pitem->localAddrV4.addr);
			    setExtAddress(peer_id, pitem->extAddrV4.addr);
			    setDynDNS (peer_id, pitem->dyndns);

			    /* convert addresses */
			    pqiIpAddrSet addrs;
			    addrs.mLocal.extractFromTlv(pitem->localAddrList);
			    addrs.mExt.extractFromTlv(pitem->extAddrList);

			    updateAddressList(peer_id, addrs);
		    }

		    delete(*it);

		    continue;
	    }

	    RsConfigKeyValueSet *vitem = dynamic_cast<RsConfigKeyValueSet *>(*it) ;
	    if (vitem)
	    {
		    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
		    std::cerr << "p3PeerMgrIMPL::loadList() General Variable Config Item:" << std::endl;
		    vitem->print(std::cerr, 10);
		    std::cerr << std::endl;
#endif
		    std::list<RsTlvKeyValue>::iterator kit;
		    for(kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit)
		    {
			    if (kit->key == kConfigKeyExtIpFinder)
			    {
				    useExtAddrFinder = (kit->value == "TRUE");
#ifdef PEER_DEBUG
				    std::cerr << "setting use_extr_addr_finder to " << useExtAddrFinder << std::endl ;
#endif
			    } 
			    // Tor
			    else if (kit->key == kConfigKeyProxyServerIpAddrTor)
			    {
				    proxyIpAddressTor = kit->value;
#ifdef PEER_DEBUG
				    std::cerr << "Loaded proxyIpAddress for Tor: " << proxyIpAddressTor;
				    std::cerr << std::endl ;
#endif

			    }
			    else if (kit->key == kConfigKeyProxyServerPortTor)
			    {
                    uint16_t p = atoi(kit->value.c_str());

                    if(p >= 1024)
						proxyPortTor = p;
#ifdef PEER_DEBUG
				    std::cerr << "Loaded proxyPort for Tor: " << proxyPortTor;
				    std::cerr << std::endl ;
#endif
			    }
			    // I2p
			    else if (kit->key == kConfigKeyProxyServerIpAddrI2P)
			    {
				    proxyIpAddressI2P = kit->value;
#ifdef PEER_DEBUG
				    std::cerr << "Loaded proxyIpAddress for I2P: " << proxyIpAddressI2P;
				    std::cerr << std::endl ;
#endif
			    }
			    else if (kit->key == kConfigKeyProxyServerPortI2P)
			    {
                    uint16_t p = atoi(kit->value.c_str());

                    if(p >= 1024)
						proxyPortI2P = p;
#ifdef PEER_DEBUG
				    std::cerr << "Loaded proxyPort for I2P: " << proxyPortI2P;
				    std::cerr << std::endl ;
#endif
			    }
		    }

		    delete(*it);

		    continue;
	    }

        RsNodeGroupItem *gitem2 = dynamic_cast<RsNodeGroupItem*>(*it) ;

        if (gitem2)
        {
            RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
            std::cerr << "p3PeerMgrIMPL::loadList() Peer group item:" << std::endl;
            gitem->print(std::cerr, 10);
            std::cerr << std::endl;
#endif
            RsGroupInfo info ;
            info.peerIds = gitem2->pgpList.ids ;
            info.id      = gitem2->id ;
            info.name    = gitem2->name ;
            info.flag    = gitem2->flag ;

            std::cerr << "(II) Loaded group in new format. ID = " << info.id << std::endl;
            groupList[info.id] = info ;

			delete *it ;
            continue;
        }
	    RsPeerBandwidthLimitsItem *pblitem = dynamic_cast<RsPeerBandwidthLimitsItem*>(*it) ;

	    if(pblitem)
	    {
		    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
		    std::cerr << "Loaded service permission item: " << std::endl;
#endif
	    		mPeerBandwidthLimits = pblitem->peers ;
	    }
	    RsPeerServicePermissionItem *sitem = dynamic_cast<RsPeerServicePermissionItem*>(*it) ;

	    if(sitem)
	    {
		    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
		    std::cerr << "Loaded service permission item: " << std::endl;
#endif

		    for(uint32_t i=0;i<sitem->pgp_ids.size();++i)
			    if(AuthGPG::getAuthGPG()->isGPGAccepted(sitem->pgp_ids[i]) || sitem->pgp_ids[i] == AuthGPG::getAuthGPG()->getGPGOwnId())
			    {
				    mFriendsPermissionFlags[sitem->pgp_ids[i]] = sitem->service_flags[i] ;
#ifdef PEER_DEBUG
				    std::cerr << "   " << sitem->pgp_ids[i] << " - " << sitem->service_flags[i] << std::endl;
#endif
			    }
#ifdef PEER_DEBUG
			    else
				    std::cerr << "   " << sitem->pgp_ids[i] << " - Not a friend!" << std::endl;
#endif
	    }

	    delete (*it);
    }

    {
	    /* set missing groupIds */

	    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	    /* Standard groups */
	    const int standardGroupCount = 5;
        const RsNodeGroupId   standardGroupIds  [standardGroupCount] = { RS_GROUP_ID_FRIENDS,           RS_GROUP_ID_FAMILY,           RS_GROUP_ID_COWORKERS,           RS_GROUP_ID_OTHERS,           RS_GROUP_ID_FAVORITES };
        const char           *standardGroupNames[standardGroupCount] = { RS_GROUP_DEFAULT_NAME_FRIENDS, RS_GROUP_DEFAULT_NAME_FAMILY, RS_GROUP_DEFAULT_NAME_COWORKERS, RS_GROUP_DEFAULT_NAME_OTHERS, RS_GROUP_DEFAULT_NAME_FAVORITES };

        for(uint32_t k=0;k<standardGroupCount;++k)
            if(groupList.find(standardGroupIds[k]) == groupList.end())
            {
                RsGroupInfo info ;
                info.id = standardGroupIds[k];
                info.name = standardGroupNames[k];
                info.flag |= RS_GROUP_FLAG_STANDARD;

                groupList[info.id] = info;
		    }
    }

    // If we are hidden - don't want ExtAddrFinder - ever!
    if (isHidden())
    {
	    useExtAddrFinder = false;
    }

    mNetMgr->setIPServersEnabled(useExtAddrFinder);

    // Configure Proxy Server.
    struct sockaddr_storage proxy_addr;
    // Tor
    sockaddr_storage_clear(proxy_addr);
    sockaddr_storage_ipv4_aton(proxy_addr, proxyIpAddressTor.c_str());
    sockaddr_storage_ipv4_setport(proxy_addr, proxyPortTor);

    if (sockaddr_storage_isValidNet(proxy_addr))
    {
	    setProxyServerAddress(RS_HIDDEN_TYPE_TOR, proxy_addr);
    }

    // I2P
    sockaddr_storage_clear(proxy_addr);
    sockaddr_storage_ipv4_aton(proxy_addr, proxyIpAddressI2P.c_str());
    sockaddr_storage_ipv4_setport(proxy_addr, proxyPortI2P);

    if (sockaddr_storage_isValidNet(proxy_addr))
    {
	    setProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy_addr);
    }

    load.clear() ;
    return true;
}


#if 0

void  printConnectState(std::ostream &out, peerState &peer)
{

	out << "Friend: " << peer.name << " Id: " << peer.id << " State: " << peer.state;
	if (peer.state & RS_PEER_S_FRIEND)
		out << " S:RS_PEER_S_FRIEND";
	if (peer.state & RS_PEER_S_ONLINE)
		out << " S:RS_PEER_S_ONLINE";
	if (peer.state & RS_PEER_S_CONNECTED)
		out << " S:RS_PEER_S_CONNECTED";
	out << " Actions: " << peer.actions;
	if (peer.actions & RS_PEER_NEW)
		out << " A:RS_PEER_NEW";
	if (peer.actions & RS_PEER_MOVED)
		out << " A:RS_PEER_MOVED";
	if (peer.actions & RS_PEER_CONNECTED)
		out << " A:RS_PEER_CONNECTED";
	if (peer.actions & RS_PEER_DISCONNECTED)
		out << " A:RS_PEER_DISCONNECTED";
	if (peer.actions & RS_PEER_CONNECT_REQ)
		out << " A:RS_PEER_CONNECT_REQ";

	out << std::endl;
	return;
}

#endif


/**********************************************************************
 **********************************************************************
 ************************** Groups ************************************
 **********************************************************************
 **********************************************************************/

bool p3PeerMgrIMPL::addGroup(RsGroupInfo &groupInfo)
{
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        do { groupInfo.id = RsNodeGroupId::random(); } while(groupList.find(groupInfo.id) != groupList.end()) ;

        RsGroupInfo groupItem(groupInfo) ;

		// remove standard flag

        groupItem.flag &= ~RS_GROUP_FLAG_STANDARD;
        groupList[groupInfo.id] = groupItem;

        std::cerr << "(II) Added new group with ID " << groupInfo.id << ", name=\"" << groupInfo.name << "\"" << std::endl;
	}

	RsServer::notify()->notifyListChange(NOTIFY_LIST_GROUPLIST, NOTIFY_TYPE_ADD);

	IndicateConfigChanged();

	return true;
}

bool p3PeerMgrIMPL::editGroup(const RsNodeGroupId& groupId, RsGroupInfo &groupInfo)
{
    if (groupId.isNull())
		return false;

	bool changed = false;

    {
        RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        std::map<RsNodeGroupId,RsGroupInfo>::iterator it = groupList.find(groupId) ;

        if(it == groupList.end())
        {
            std::cerr << "(EE) cannot find local node group with ID " << groupId << std::endl;
            return false ;
        }

        if (it->second.flag & RS_GROUP_FLAG_STANDARD)
        {
            // can't edit standard groups
            std::cerr << "(EE) cannot edit standard group with ID " << groupId << std::endl;
            return false ;
        }
        else
        {
            changed = true;
            it->second = groupInfo;
        }
    }

    if (changed)
    {
		RsServer::notify()->notifyListChange(NOTIFY_LIST_GROUPLIST, NOTIFY_TYPE_MOD);

		IndicateConfigChanged();
	}

	return changed;
}

bool p3PeerMgrIMPL::removeGroup(const RsNodeGroupId& groupId)
{
	bool changed = false;

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        std::map<RsNodeGroupId,RsGroupInfo>::iterator it = groupList.find(groupId) ;

        if (it != groupList.end()) {
            if (it->second.flag & RS_GROUP_FLAG_STANDARD)
            {
				// can't remove standard groups
                std::cerr << "(EE) cannot remove standard group with ID " << groupId << std::endl;
                return false ;
            }
#warning csoler: we need to check that the local group is not used. Otherwise deleting it is going to cause problems!
//            else if(!it->second.used_gxs_groups.empty())
//            {
//                std::cerr << "(EE) cannot remove standard group with ID " << groupId << " because it is used in the following groups: " << std::endl;
//                for(std::set<RsGxsGroupId>::const_iterator it2(it->second.used_gxs_groups.begin());it2!=it->second.used_gxs_groups.end();++it2)
//                    std::cerr << "  " << *it2 << std::endl;
//
//                return false ;
//            }
            else
            {
				changed = true;
                groupList.erase(it);
			}
		}
	}

	if (changed) {
		RsServer::notify()->notifyListChange(NOTIFY_LIST_GROUPLIST, NOTIFY_TYPE_DEL);

		IndicateConfigChanged();
	}

	return changed;
}

bool p3PeerMgrIMPL::getGroupInfoByName(const std::string& groupName, RsGroupInfo &groupInfo)
{
    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

    for(std::map<RsNodeGroupId,RsGroupInfo>::iterator it = groupList.begin();it!=groupList.end();++it)
        if(it->second.name == groupName)
        {
            groupInfo = it->second ;
            return true ;
        }

    std::cerr << "(EE) getGroupInfoByName: no known group for name " << groupName << std::endl;
    return false ;
}
bool p3PeerMgrIMPL::getGroupInfo(const RsNodeGroupId& groupId, RsGroupInfo &groupInfo)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

    std::map<RsNodeGroupId,RsGroupInfo>::iterator it = groupList.find(groupId) ;

    if(it == groupList.end())
        return false ;

    groupInfo =  it->second;

    return true;
}

bool p3PeerMgrIMPL::getGroupInfoList(std::list<RsGroupInfo>& groupInfoList)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

	for(std::map<RsNodeGroupId,RsGroupInfo> ::const_iterator groupIt = groupList.begin(); groupIt != groupList.end(); ++groupIt)
		groupInfoList.push_back(groupIt->second) ;

	return true;
}

// groupId.isNull() && assign == false -> remove from all groups

bool p3PeerMgrIMPL::assignPeersToGroup(const RsNodeGroupId &groupId, const std::list<RsPgpId> &peerIds, bool assign)
{
    if (groupId.isNull() && assign == true)
		return false;

    if (peerIds.empty())
		return false;

	bool changed = false;

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

        for (std::map<RsNodeGroupId,RsGroupInfo>::iterator groupIt = groupList.begin(); groupIt != groupList.end(); ++groupIt)
            if (groupId.isNull() || groupIt->first == groupId)
            {
                RsGroupInfo& groupItem = groupIt->second;

                for (std::list<RsPgpId>::const_iterator peerIt = peerIds.begin(); peerIt != peerIds.end(); ++peerIt)
                {
                    //std::set<RsPgpId>::iterator peerIt1 = groupItem.peerIds.find(*peerIt);

                    if (assign)
                    {
                        groupItem.peerIds.insert(*peerIt);
                        changed = true;
                    }
                    else
                    {
                        groupItem.peerIds.erase(*peerIt);
                        changed = true;
					}
				}

                if (!groupId.isNull())
					break;
			}
	}

	if (changed) {
		RsServer::notify()->notifyListChange(NOTIFY_LIST_GROUPLIST, NOTIFY_TYPE_MOD);

		IndicateConfigChanged();
	}

	return changed;
}


/**********************************************************************
 **********************************************************************
 ******************** Service permission stuff ************************
 **********************************************************************
 **********************************************************************/

ServicePermissionFlags p3PeerMgrIMPL::servicePermissionFlags(const RsPeerId& ssl_id)
{
	RsPgpId gpg_id ;

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		std::map<RsPeerId, peerState>::const_iterator it = mFriendList.find(ssl_id);

		if(it == mFriendList.end())
            return RS_NODE_PERM_DEFAULT ;

		gpg_id = it->second.gpg_id ;
	}

	return servicePermissionFlags(gpg_id) ;
}


ServicePermissionFlags p3PeerMgrIMPL::servicePermissionFlags(const RsPgpId& pgp_id)
{
	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		std::map<RsPgpId,ServicePermissionFlags>::const_iterator it = mFriendsPermissionFlags.find( pgp_id ) ;

		if(it == mFriendsPermissionFlags.end())
            return RS_NODE_PERM_DEFAULT ;
		else
			return it->second ;
	}
}
void p3PeerMgrIMPL::setServicePermissionFlags(const RsPgpId& pgp_id, const ServicePermissionFlags& flags)
{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

		// Check that we have a PGP id. This should not be necessary, but because
		// we use std::string, anything can get passed down here.
		//

		mFriendsPermissionFlags[pgp_id] = flags ;
		IndicateConfigChanged(); /**** INDICATE MSG CONFIG CHANGED! *****/
}

/**********************************************************************
 **********************************************************************
 ******************** Stuff moved from p3peers ************************
 **********************************************************************
 **********************************************************************/

bool p3PeerMgrIMPL::removeAllFriendLocations(const RsPgpId &gpgid)
{
	std::list<RsPeerId> sslIds;
	if (!getAssociatedPeers(gpgid, sslIds))
	{
		return false;
	}
	
	std::list<RsPeerId>::iterator it;
	for(it = sslIds.begin(); it != sslIds.end(); ++it)
	{
		removeFriend(*it, true);
	}
	
	return true;
}


bool	p3PeerMgrIMPL::getAssociatedPeers(const RsPgpId &gpg_id, std::list<RsPeerId> &ids)
{
	RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
	std::cerr << "p3PeerMgr::getAssociatedPeers() for id : " << gpg_id << std::endl;
#endif
	
	int count = 0;
	std::map<RsPeerId, peerState>::iterator it;
	for(it = mFriendList.begin(); it != mFriendList.end(); ++it)
	{
		if (it->second.gpg_id == gpg_id)
		{
			count++;
			ids.push_back(it->first);

#ifdef PEER_DEBUG
			std::cerr << "p3PeerMgr::getAssociatedPeers() found ssl id :  " << it->first << std::endl;
#endif
			
		}
	}
	
	return (count > 0);
}

// goes through the list of known friend IPs and remove the ones that are banned by p3LinkMgr.


bool p3PeerMgrIMPL::removeBannedIps()
{
    RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/

#ifdef PEER_DEBUG
    std::cerr << "Cleaning known IPs for all peers." << std::endl;
#endif

    bool changed = false ;
    for( std::map<RsPeerId, peerState>::iterator it = mFriendList.begin(); it != mFriendList.end(); ++it)
    {
        if(cleanIpList(it->second.ipAddrs.mExt.mAddrs,it->first,mLinkMgr)) changed = true ;
        if(cleanIpList(it->second.ipAddrs.mLocal.mAddrs,it->first,mLinkMgr)) changed = true ;

        if(!rsBanList->isAddressAccepted(it->second.serveraddr,RSBANLIST_CHECKING_FLAGS_BLACKLIST))
        {
            sockaddr_storage_clear(it->second.serveraddr) ;
            std::cerr << "(SS) Peer " << it->first << " has a banned server address. Wiping it out." << std::endl;
        }
    }

    if(cleanIpList(mOwnState.ipAddrs.mExt.mAddrs,mOwnState.id,mLinkMgr) ) changed = true ;
    if(cleanIpList(mOwnState.ipAddrs.mLocal.mAddrs,mOwnState.id,mLinkMgr) )  changed = true ;

    if(changed)
        IndicateConfigChanged();

    return true ;
}

// /* This only removes SSL certs, that are old... Can end up with no Certs per GPG Id 
//  * We are removing the concept of a "DummyId" - There is no need for it.
//  */
// 
// bool isDummyFriend(RsPeerId id)
// {
// 	bool ret = (id.substr(0,5) == "dummy");
// 	return ret;
// }

/**
 * @brief p3PeerMgrIMPL::removeUnusedLocations Removes all location offline for RS_PEER_OFFLINE_DELETE seconds or more. Keeps the most recent location per PGP id.
 * @return true on success
 *
 * This function removes all location that are offline for too long defined by RS_PEER_OFFLINE_DELETE.
 * It also makes sure that at least one location (the most recent) is kept.
 *
 * The idea of the function is the following:
 *  - keep track if there is at least one location per PGP id that is not offline for too long
 *      -> hasRecentLocation
 *  - keep track of most recent location per PGP id that is offline for too long (and its time stamp)
 *      -> mostRecentLocation
 *      -> mostRecentTime
 *
 * When a location is found that is offline for too long the following points are checked from the top to the bottom:
 * 1) remove it when the PGP id has a location that is not offline for too long
 * 2) remove it when the PGP id has a more recent location
 * 3) keep it when it is the most recent location
 *      This location will possibly be removed when a more recent (but still offline for too long) is found
 */
bool p3PeerMgrIMPL::removeUnusedLocations()
{
	std::list<RsPeerId> toRemove;
	std::map<RsPgpId, time_t>   mostRecentTime;

	const time_t now = time(NULL);

	std::list<RsPgpId> pgpList ;

	if (!rsPeers->getGPGAcceptedList(pgpList))
		return false ;

	{
		RsStackMutex stack(mPeerMtx); /****** STACK LOCK MUTEX *******/
	
        // First put a sensible number in all PGP ids

        for(std::list<RsPgpId>::const_iterator it = pgpList.begin(); it != pgpList.end(); ++it)
            mostRecentTime[*it] = (time_t)0;

#ifdef PEER_DEBUG
		std::cerr << "p3PeerMgr::removeUnusedLocations()" << std::endl;
#endif
        // Then compute the most recently used location for all PGP ids
		
        for( std::map<RsPeerId, peerState>::iterator it = mFriendList.begin(); it != mFriendList.end(); ++it)
        {
            time_t& bst(mostRecentTime[it->second.gpg_id]) ;
            bst = std::max(bst,it->second.lastcontact) ;
        }

        // And remove all locations that are too old and also older than the most recent location. Doing this we're sure to always keep at least one location per PGP id.

        for( std::map<RsPeerId, peerState>::iterator it = mFriendList.begin(); it != mFriendList.end(); ++it)
        {
            if (now > it->second.lastcontact + RS_PEER_OFFLINE_DELETE && it->second.lastcontact < mostRecentTime[it->second.gpg_id])
                toRemove.push_back(it->first);
#ifdef PEER_DEBUG
            std::cerr << "Location " << it->first << " PGP id " << it->second.gpg_id << " last contact " << it->second.lastcontact << " remove: " << (now > it->second.lastcontact + RS_PEER_OFFLINE_DELETE)  << " most recent: " << mostRecentTime[it->second.gpg_id]
                      << ". Final result remove: " << (it->second.lastcontact < mostRecentTime[it->second.gpg_id] && now > it->second.lastcontact + RS_PEER_OFFLINE_DELETE )<< std::endl;
#endif
        }
	}

	for (std::list<RsPeerId>::iterator it = toRemove.begin(); it != toRemove.end(); ++it)
		removeFriend(*it, false) ;

	return true;
}

	
