/*
 * libretroshare/src/pqi: p3linkmgr.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 "pqi/p3linkmgr.h"

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

#include "rsserver/p3face.h"
#include "pqi/authssl.h"
#include "pqi/p3dhtmgr.h" // Only need it for constants.
#include "tcponudp/tou.h"
#include "util/extaddrfinder.h"
#include "util/dnsresolver.h"
#include "util/rsnet.h"
#include "pqi/authgpg.h"


#include "util/rsprint.h"
#include "util/rsdebug.h"
#include "util/rsstring.h"
const int p3connectzone = 3431;

#include "serialiser/rsconfigitems.h"

#include "retroshare/rsiface.h"
#include "retroshare/rspeers.h"

/* Network setup States */


/****
 * #define LINKMGR_DEBUG 1
 * #define LINKMGR_DEBUG_CONNFAIL 1
 * #define LINKMGR_DEBUG_ACTIONS  1
 * #define LINKMGR_DEBUG_LINKTYPE	1
 ***/

/****
 * #define DISABLE_UDP_CONNECTIONS	1		
 ***/

/****
 * #define P3CONNMGR_NO_TCP_CONNECTIONS 1
 ***/
/****
 * #define P3CONNMGR_NO_AUTO_CONNECTION 1
 ***/

//#define P3CONNMGR_NO_TCP_CONNECTIONS 1

const uint32_t P3CONNMGR_TCP_DEFAULT_DELAY = 3; /* 2 Seconds? is it be enough! */
const uint32_t P3CONNMGR_UDP_DEFAULT_DELAY = 3; /* 2 Seconds? is it be enough! */

const uint32_t P3CONNMGR_TCP_DEFAULT_PERIOD = 10;
const uint32_t P3CONNMGR_UDP_DEFAULT_PERIOD = 30;  // this represents how long it stays at the default TTL (4), before rising.

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

#define MAX_RANDOM_ATTEMPT_OFFSET	6 // seconds.

void  printConnectState(std::ostream &out, peerConnectState &peer);

peerConnectAddress::peerConnectAddress()
	:delay(0), period(0), type(0), flags(0), ts(0)
{
	sockaddr_clear(&addr);
}


peerAddrInfo::peerAddrInfo()
	:found(false), type(0), ts(0)
{
}

peerConnectState::peerConnectState()
	:id("unknown"), 
	 connecttype(0),
	 lastavailable(0),
         lastattempt(0),
         name(""),
         state(0), actions(0),
	 source(0), 
	 inConnAttempt(0), 
	 wasDeniedConnection(false), deniedTS(false), deniedInConnAttempt(false)
{
	//sockaddr_clear(&currentlocaladdr);
	//sockaddr_clear(&currentserveraddr);

	return;
}

std::string textPeerConnectState(peerConnectState &state)
{
    return "Id: " + state.id + "\n";
}

/*********
 * NOTES:
 *
 * p3LinkMgr doesn't store anything. All configuration is handled by p3PeerMgr.
 * 
 * p3LinkMgr recvs the Discovery / Dht / Status updates.... tries the address.
 * at success the address is pushed to p3PeerMgr for storage.
 *
 */


p3LinkMgrIMPL::p3LinkMgrIMPL(p3PeerMgrIMPL *peerMgr, p3NetMgrIMPL *netMgr)
	:mPeerMgr(peerMgr), mNetMgr(netMgr), mLinkMtx("p3LinkMgr"),mStatusChanged(false)
{

	{
		RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	
		mAllowTunnelConnection = false;
		mDNSResolver = new DNSResolver();
		mRetryPeriod = MIN_RETRY_PERIOD;
	
		lastGroupId = 1;

		/* setup Banned Ip Address - static for now 
		 */

		struct in_addr bip;
		memset(&bip, 0, sizeof(bip));
		bip.s_addr = 1;

		mBannedIpList.push_back(bip);
	}
	
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgr() Startup" << std::endl;
#endif

	return;
}

void p3LinkMgrIMPL::setTunnelConnection(bool b)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	mAllowTunnelConnection = b;
}

bool p3LinkMgrIMPL::getTunnelConnection()
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	return mAllowTunnelConnection;
}

bool    p3LinkMgrIMPL::setLocalAddress(struct sockaddr_in addr)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	mLocalAddress = addr;

	return true ;
}

struct sockaddr_in p3LinkMgrIMPL::getLocalAddress()
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	return mLocalAddress;
}


bool    p3LinkMgrIMPL::isOnline(const std::string &ssl_id)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

        std::map<std::string, peerConnectState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
		return false;
	}

	if (it->second.state & RS_PEER_S_CONNECTED)
	{
		return true;
	}
	return false;
}



uint32_t p3LinkMgrIMPL::getLinkType(const std::string &ssl_id)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

        std::map<std::string, peerConnectState>::iterator it;
	it = mFriendList.find(ssl_id);
	if (it == mFriendList.end())
	{
		return 0;
	}

	if (it->second.state & RS_PEER_S_CONNECTED)
	{
		return it->second.linkType;
	}
	return 0;
}



void    p3LinkMgrIMPL::getOnlineList(std::list<std::string> &ssl_peers)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

        std::map<std::string, peerConnectState>::iterator it;
	for(it = mFriendList.begin(); it != mFriendList.end(); it++)
	{
		if (it->second.state & RS_PEER_S_CONNECTED)
		{
			ssl_peers.push_back(it->first);
		}
	}
	return;
}

void    p3LinkMgrIMPL::getFriendList(std::list<std::string> &ssl_peers)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

	std::map<std::string, peerConnectState>::iterator it;
	for(it = mFriendList.begin(); it != mFriendList.end(); it++)
	{
		ssl_peers.push_back(it->first);
	}
	return;
}

bool    p3LinkMgrIMPL::getPeerName(const std::string &ssl_id, std::string &name)
{
	return mPeerMgr->getPeerName(ssl_id, name);
}


bool    p3LinkMgrIMPL::getFriendNetStatus(const std::string &id, peerConnectState &state)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

        std::map<std::string, peerConnectState>::iterator it;
	it = mFriendList.find(id);
	if (it == mFriendList.end())
	{
		return false;
	}

	state = it->second;

	return true;
}



void    p3LinkMgrIMPL::setFriendVisibility(const std::string &id, bool isVisible)
{
	/* set visibility */
	{
		RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	
		std::map<std::string, peerConnectState>::iterator it;
		it = mFriendList.find(id);
		if (it == mFriendList.end())
		{
			/* */
			std::cerr << "p3LinkMgrIMPL::setFriendVisibility() ERROR peer unknown: " << id;
			std::cerr << std::endl;
			return;
		}

		if (it->second.dhtVisible == isVisible)
		{
			/* no change in state */
			return;
		}
	
		it->second.dhtVisible = isVisible;
	
		if (it->second.state & RS_PEER_S_CONNECTED)
		{
			/* dont worry about it */
			return;
		}
	}

	/* switch the NetAssistOn/Off */
	mNetMgr->netAssistFriend(id, isVisible);
}
	

void p3LinkMgrIMPL::tick()
{
	statusTick();
	tickMonitors();
}


void    p3LinkMgrIMPL::statusTick()
{
	/* iterate through peers ... 
	 * 	if been available for long time ... remove flag
	 * 	if last attempt a while - retryConnect. 
	 * 	etc.
	 */

#ifdef LINKMGR_DEBUG_TICK
	std::cerr << "p3LinkMgrIMPL::statusTick()" << std::endl;
#endif
	std::list<std::string> retryIds;
	std::list<std::string>::iterator it2;
        //std::list<std::string> dummyToRemove;

      {
	time_t now = time(NULL);
	time_t oldavail = now - MAX_AVAIL_PERIOD;
        time_t retry = now - mRetryPeriod;

      	RsStackMutex stack(mLinkMtx);  /******   LOCK MUTEX ******/
        std::map<std::string, peerConnectState>::iterator it;
	for(it = mFriendList.begin(); it != mFriendList.end(); it++)
	{
		if (it->second.state & RS_PEER_S_CONNECTED)
		{
			continue;
		}

		if ((it->second.state & RS_PEER_S_ONLINE) &&
			(it->second.lastavailable < oldavail))
		{
#ifdef LINKMGR_DEBUG_TICK
			std::cerr << "p3LinkMgrIMPL::statusTick() ONLINE TIMEOUT for: ";
			std::cerr << it->first;
			std::cerr << std::endl;
#endif
			it->second.state &= (~RS_PEER_S_ONLINE);
		}

		if (it->second.lastattempt < retry)
		{
			retryIds.push_back(it->first);
		}
	}
      }

#ifndef P3CONNMGR_NO_AUTO_CONNECTION 

        for(it2 = retryIds.begin(); it2 != retryIds.end(); it2++)
	{
#ifdef LINKMGR_DEBUG_TICK
		std::cerr << "p3LinkMgrIMPL::statusTick() RETRY TIMEOUT for: ";
		std::cerr << *it2;
		std::cerr << std::endl;
#endif
		/* retry it! */
		retryConnect(*it2);
	}

#endif

}


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

void p3LinkMgrIMPL::addMonitor(pqiMonitor *mon)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

	std::list<pqiMonitor *>::iterator it;
	it = std::find(clients.begin(), clients.end(), mon);
	if (it != clients.end())
	{
		return;
	}

	mon->setLinkMgr(this);
	clients.push_back(mon);
	return;
}

void p3LinkMgrIMPL::removeMonitor(pqiMonitor *mon)
{
	RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

	std::list<pqiMonitor *>::iterator it;
	it = std::find(clients.begin(), clients.end(), mon);
	if (it == clients.end())
	{
		return;
	}
	(*it)->setLinkMgr(NULL);
	clients.erase(it);

	return;
}

void p3LinkMgrIMPL::tickMonitors()
{
	bool doStatusChange = false;
	std::list<pqipeer> actionList;
        std::map<std::string, peerConnectState>::iterator it;

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

	if (mStatusChanged)
	{
#ifdef LINKMGR_DEBUG_ACTIONS
		std::cerr << "p3LinkMgrIMPL::tickMonitors() StatusChanged! List:" << std::endl;
#endif
		/* assemble list */
		for(it = mFriendList.begin(); it != mFriendList.end(); it++)
		{
			if (it->second.actions)
			{
				/* add in */
				pqipeer peer;
				peer.id = it->second.id;
				peer.name = it->second.name;
				peer.state = it->second.state;
				peer.actions = it->second.actions;

				/* reset action */
				it->second.actions = 0;

				actionList.push_back(peer);

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

				std::cerr << std::endl;
#endif

				/* notify GUI */
				if (peer.actions & RS_PEER_CONNECTED)
				{
					p3Notify *notify = RsServer::notify();
					if (notify)
					{
						notify->AddPopupMessage(RS_POPUP_CONNECT, peer.id,"", "Online: ");
						notify->AddFeedItem(RS_FEED_ITEM_PEER_CONNECT, peer.id, "", "");
					}
				}
			}
		}

		/* do the Others as well! */
		for(it = mOthersList.begin(); it != mOthersList.end(); it++)
		{
			if (it->second.actions)
			{
				/* add in */
				pqipeer peer;
				peer.id = it->second.id;
				peer.name = it->second.name;
				peer.state = it->second.state;
				peer.actions = it->second.actions;

				/* reset action */
				it->second.actions = 0;

#ifdef LINKMGR_DEBUG_ACTIONS
				std::cerr << "Other: " << peer.name << " Id: " << peer.id << " State: " << peer.state;
				if (peer.state & RS_PEER_S_FRIEND)
					std::cerr << " S:RS_PEER_S_FRIEND";
				if (peer.state & RS_PEER_S_ONLINE)
					std::cerr << " S:RS_PEER_S_ONLINE";
				if (peer.state & RS_PEER_S_CONNECTED)
					std::cerr << " S:RS_PEER_S_CONNECTED";
				std::cerr << " Actions: " << peer.actions;
				if (peer.actions & RS_PEER_NEW)
					std::cerr << " A:RS_PEER_NEW";
				if (peer.actions & RS_PEER_MOVED)
					std::cerr << " A:RS_PEER_MOVED";
				if (peer.actions & RS_PEER_CONNECTED)
					std::cerr << " A:RS_PEER_CONNECTED";
				if (peer.actions & RS_PEER_DISCONNECTED)
					std::cerr << " A:RS_PEER_DISCONNECTED";
				if (peer.actions & RS_PEER_CONNECT_REQ)
					std::cerr << " A:RS_PEER_CONNECT_REQ";

				std::cerr << std::endl;
#endif

				actionList.push_back(peer);
			}
		}
		mStatusChanged = false;
		doStatusChange = true;
	
	}
      } /****** UNLOCK STACK MUTEX ******/




	/* NOTE - clients is accessed without mutex protection!!!!
	 * At the moment this is okay - as they are only added at the start.
	 * IF this changes ---- must fix with second Mutex.
	 */

	if (doStatusChange)
	{
#ifdef LINKMGR_DEBUG_ACTIONS
		std::cerr << "Sending to " << clients.size() << " monitorClients" << std::endl;
#endif
	
		/* send to all monitors */
		std::list<pqiMonitor *>::iterator mit;
		for(mit = clients.begin(); mit != clients.end(); mit++)
		{
			(*mit)->statusChange(actionList);
		}
	}

#ifdef WINDOWS_SYS
///////////////////////////////////////////////////////////
// hack for too many connections

	/* notify all monitors */
	std::list<pqiMonitor *>::iterator mit;
	for(mit = clients.begin(); mit != clients.end(); mit++) {
		(*mit)->statusChanged();
	}

///////////////////////////////////////////////////////////
#endif

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

		/* Now Cleanup OthersList (served its purpose (MOVE Action)) */
		mOthersList.clear();
	}

}


const std::string p3LinkMgrIMPL::getOwnId()
{
                return AuthSSL::getAuthSSL()->OwnId();
}


bool p3LinkMgrIMPL::connectAttempt(const std::string &id, struct sockaddr_in &raddr,
								   struct sockaddr_in &proxyaddr,
								   struct sockaddr_in &srcaddr,
								   uint32_t &delay, uint32_t &period, uint32_t &type, uint32_t &flags, uint32_t &bandwidth)

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

	/* check for existing */
	std::map<std::string, peerConnectState>::iterator it;
	it = mFriendList.find(id);
	if (it == mFriendList.end())
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::connectAttempt() FAILED Not in FriendList! id: " << id << std::endl;
#endif

		return false;
	}

	if (it->second.connAddrs.size() < 1)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::connectAttempt() FAILED No ConnectAddresses id: " << id << std::endl;
#endif
		return false;
        }


        if (it->second.state & RS_PEER_S_CONNECTED)
	{
#ifdef LINKMGR_DEBUG
                std::cerr << "p3LinkMgrIMPL::connectAttempt() Already FLAGGED as connected!!!!"  << std::endl;
                std::cerr << "p3LinkMgrIMPL::connectAttempt() But allowing anyway!!!"  << std::endl;
#endif

		rslog(RSL_WARNING, p3connectzone, "p3LinkMgrIMPL::connectAttempt() ERROR ALREADY CONNECTED");

         }

	rslog(RSL_WARNING, p3connectzone, "p3LinkMgrIMPL::connectAttempt() called id: " + id);

        it->second.lastattempt = time(NULL); 
        it->second.inConnAttempt = true;
        it->second.currentConnAddrAttempt = it->second.connAddrs.front();
	it->second.connAddrs.pop_front();

	raddr = it->second.currentConnAddrAttempt.addr;
	delay = it->second.currentConnAddrAttempt.delay;
	period = it->second.currentConnAddrAttempt.period;
	type = it->second.currentConnAddrAttempt.type;
	flags = it->second.currentConnAddrAttempt.flags;

	proxyaddr = it->second.currentConnAddrAttempt.proxyaddr;
	srcaddr = it->second.currentConnAddrAttempt.srcaddr;
	bandwidth = it->second.currentConnAddrAttempt.bandwidth;

	/********* Setup LinkType parameters **********/

#define TRICKLE_LIMIT		2001		// 2kb
#define LOW_BANDWIDTH_LIMIT	5001		// 5kb

#ifdef LINKMGR_DEBUG_LINKTYPE
	std::cerr << "p3LinkMgrIMPL::connectAttempt() Setting up LinkType" << std::endl;
	std::cerr << "p3LinkMgrIMPL::connectAttempt() type = " << type << std::endl;
#endif

	it->second.linkType = 0;
	if (type & RS_NET_CONN_TCP_ALL)
	{
#ifdef LINKMGR_DEBUG_LINKTYPE
		std::cerr << "p3LinkMgrIMPL::connectAttempt() type & TCP_ALL => TCP_UNKNOWN" << std::endl;
#endif
		it->second.linkType |= RS_NET_CONN_TRANS_TCP_UNKNOWN;
	}
	else if (type & RS_NET_CONN_UDP_ALL)
	{
		if (flags & RS_CB_FLAG_MODE_UDP_DIRECT)
		{
#ifdef LINKMGR_DEBUG_LINKTYPE
			std::cerr << "p3LinkMgrIMPL::connectAttempt() type & UDP_ALL && flags & DIRECT => UDP_DIRECT" << std::endl;
#endif
			it->second.linkType |= RS_NET_CONN_TRANS_UDP_DIRECT;
		}
		else if (flags & RS_CB_FLAG_MODE_UDP_PROXY)
		{
#ifdef LINKMGR_DEBUG_LINKTYPE
			std::cerr << "p3LinkMgrIMPL::connectAttempt() type & UDP_ALL && flags & PROXY => UDP_PROXY" << std::endl;
#endif
			it->second.linkType |= RS_NET_CONN_TRANS_UDP_PROXY;
		}
		else if (flags & RS_CB_FLAG_MODE_UDP_RELAY)
		{
#ifdef LINKMGR_DEBUG_LINKTYPE
			std::cerr << "p3LinkMgrIMPL::connectAttempt() type & UDP_ALL && flags & RELAY => UDP_RELAY" << std::endl;
#endif
			it->second.linkType |= RS_NET_CONN_TRANS_UDP_RELAY;
		}
		else 
		{
#ifdef LINKMGR_DEBUG_LINKTYPE
			std::cerr << "p3LinkMgrIMPL::connectAttempt() type & UDP_ALL && else => UDP_UNKNOWN" << std::endl;
#endif
			it->second.linkType |= RS_NET_CONN_TRANS_UDP_UNKNOWN;
		}
	}
	else if (type & RS_NET_CONN_TUNNEL)
	{
#ifdef LINKMGR_DEBUG_LINKTYPE
		std::cerr << "p3LinkMgrIMPL::connectAttempt() type & TUNNEL => TUNNEL" << std::endl;
#endif
		it->second.linkType |= RS_NET_CONN_TRANS_TUNNEL;
	}
	else
	{
#ifdef LINKMGR_DEBUG_LINKTYPE
		std::cerr << "p3LinkMgrIMPL::connectAttempt() else => TRANS_UNKNOWN" << std::endl;
#endif
		it->second.linkType |= RS_NET_CONN_TRANS_UNKNOWN;
	}

	if ((type & RS_NET_CONN_UDP_ALL) && (flags & RS_CB_FLAG_MODE_UDP_RELAY))
	{
		if (bandwidth < TRICKLE_LIMIT)
		{
#ifdef LINKMGR_DEBUG_LINKTYPE
			std::cerr << "p3LinkMgrIMPL::connectAttempt() flags & RELAY && band < TRICKLE => SPEED_TRICKLE" << std::endl;
#endif
			it->second.linkType |= RS_NET_CONN_SPEED_TRICKLE;
		}
		else if (bandwidth < LOW_BANDWIDTH_LIMIT)
		{
#ifdef LINKMGR_DEBUG_LINKTYPE
			std::cerr << "p3LinkMgrIMPL::connectAttempt() flags & RELAY && band < LOW => SPEED_LOW" << std::endl;
#endif
			it->second.linkType |= RS_NET_CONN_SPEED_LOW;
		}
		else
		{
#ifdef LINKMGR_DEBUG_LINKTYPE
			std::cerr << "p3LinkMgrIMPL::connectAttempt() flags & RELAY && else => SPEED_NORMAL" << std::endl;
#endif
			it->second.linkType |= RS_NET_CONN_SPEED_NORMAL;
		}
	}
	else
	{ 
#ifdef LINKMGR_DEBUG_LINKTYPE
		std::cerr << "p3LinkMgrIMPL::connectAttempt() else => SPEED_NORMAL" << std::endl;
#endif
		it->second.linkType |= RS_NET_CONN_SPEED_NORMAL;
	}

	uint32_t connType = mPeerMgr->getConnectionType(id);
	it->second.linkType |= connType;

#ifdef LINKMGR_DEBUG_LINKTYPE
	std::cerr << "p3LinkMgrIMPL::connectAttempt() connType: " << connType << std::endl;
	std::cerr << "p3LinkMgrIMPL::connectAttempt() final LinkType: " << it->second.linkType << std::endl;


        std::cerr << "p3LinkMgrIMPL::connectAttempt() found an address: id: " << id << std::endl;
	std::cerr << " laddr: " << rs_inet_ntoa(raddr.sin_addr) << " lport: " << ntohs(raddr.sin_port) << " delay: " << delay << " period: " << period;
	std::cerr << " type: " << type << std::endl;
	std::cerr << "p3LinkMgrIMPL::connectAttempt() set LinkType to: " << it->second.linkType << std::endl;
#endif


	/********* Setup LinkType parameters **********/

#ifdef LINKMGR_DEBUG
        	std::cerr << "p3LinkMgrIMPL::connectAttempt() found an address: id: " << id << std::endl;
        std::cerr << " laddr: " << rs_inet_ntoa(addr.sin_addr) << " lport: " << ntohs(addr.sin_port) << " delay: " << delay << " period: " << period;
		std::cerr << " type: " << type << std::endl;
        	std::cerr << "p3LinkMgrIMPL::connectAttempt() set LinkType to: " << it->second.linkType << std::endl;
#endif
        if (raddr.sin_addr.s_addr == 0 || raddr.sin_port == 0) {
#ifdef LINKMGR_DEBUG
        	std::cerr << "p3LinkMgrIMPL::connectAttempt() WARNING: address or port is null" << std::endl;
        	std::cerr << " type: " << type << std::endl;
#endif
        }

	return true;
}


/****************************
 * Update state,
 * trigger retry if necessary,
 *
 * remove from DHT?
 *
 */

bool p3LinkMgrIMPL::connectResult(const std::string &id, bool success, uint32_t flags, struct sockaddr_in remote_peer_address)
{
	bool doDhtAssist = false ;
	bool updatePeerAddr = false;
	bool updateLastContact = false;

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


		if (id == getOwnId()) 
		{
			rslog(RSL_ALERT, p3connectzone, "p3LinkMgrIMPL::connectResult() ERROR Trying to Connect to OwnId: " + id);

			return false;
		}
		/* check for existing */
		std::map<std::string, peerConnectState>::iterator it;
		it = mFriendList.find(id);
		if (it == mFriendList.end())
		{
			rslog(RSL_ALERT, p3connectzone, "p3LinkMgrIMPL::connectResult() ERROR Missing Friend: " + id);

#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::connectResult() ERROR, missing Friend " << " id: " << id << std::endl;
#endif
			return false;
		}

		/* now we can tell if we think we were connected - proper point to log */

		{
			std::string out = "p3LinkMgrIMPL::connectResult() id: " + id;
			if (success) 
			{
				out += " SUCCESS ";
				if (it->second.state & RS_PEER_S_CONNECTED)
				{
					out += " WARNING: State says: Already Connected";
				}
			} 
			else 
			{
				if (it->second.state & RS_PEER_S_CONNECTED)
				{
					out += " FAILURE OF THE CONNECTION (Was Connected)";
				}
				else
				{
					out += " FAILED ATTEMPT (Not Connected)";
				}
			}
			rslog(RSL_WARNING, p3connectzone, out);
		}



		if (success)
		{
			/* update address (should also come through from DISC) */
#ifdef LINKMGR_DEBUG_CONNFAIL
			std::cerr << "p3LinkMgrIMPL::connectResult() Connect!: id: " << id << std::endl;
			std::cerr << " Success: " << success << " flags: " << flags << std::endl;
#endif

#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::connectResult() Connect!: id: " << id << std::endl;
			std::cerr << " Success: " << success << " flags: " << flags << std::endl;

			rslog(RSL_WARNING, p3connectzone, "p3LinkMgrIMPL::connectResult() Success");
#endif

			/* change state */
			it->second.state |= RS_PEER_S_CONNECTED;
			it->second.actions |= RS_PEER_CONNECTED;
			it->second.connecttype = flags;
			it->second.connectaddr = remote_peer_address;

			updateLastContact = true; /* time of connect */

			/* only update the peer's address if we were in a connect attempt.
			 * Otherwise, they connected to us, and the address will be a
			 * random port of their outgoing TCP socket
			 *
			 * NB even if we received the connection, the IP address is likely to okay.
			 */

			//used to send back to the peer it's own ext address
			//it->second.currentserveraddr = remote_peer_address;

			// THIS TEST IS A Bit BAD XXX, we should update their address anyway...
			// This means we only update connections that we've made.. so maybe not too bad?
			
			if ((it->second.inConnAttempt) &&
					(it->second.currentConnAddrAttempt.addr.sin_addr.s_addr 
					 == remote_peer_address.sin_addr.s_addr) &&
					(it->second.currentConnAddrAttempt.addr.sin_port 
					 == remote_peer_address.sin_port))
			{				
				updatePeerAddr = true;
#ifdef LINKMGR_DEBUG
				std::cerr << "p3LinkMgrIMPL::connectResult() adding current peer address in list." << std::endl;
#endif
			}

			/* remove other attempts */
			it->second.inConnAttempt = false;
			it->second.connAddrs.clear();
			mStatusChanged = true;
		}
		else
		{
#ifdef LINKMGR_DEBUG_CONNFAIL
			std::cerr << "p3LinkMgrIMPL::connectResult() Disconnect/Fail: flags: " << flags << " id: " << id;
			std::cerr << std::endl;

			if (it->second.inConnAttempt)
			{
				std::cerr << "p3LinkMgrIMPL::connectResult() Likely Connect Fail, as inConnAttempt Flag is set";
				std::cerr << std::endl;
			}
			if (it->second.state & RS_PEER_S_CONNECTED)
			{
				std::cerr << "p3LinkMgrIMPL::connectResult() Likely DISCONNECT, as state set to Connected";
				std::cerr << std::endl;
			}
#endif

			it->second.inConnAttempt = false;

#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::connectResult() Disconnect/Fail: id: " << id << std::endl;
			std::cerr << " Success: " << success << " flags: " << flags << std::endl;
#endif

			/* if currently connected -> flag as failed */
			if (it->second.state & RS_PEER_S_CONNECTED)
			{
				it->second.state &= (~RS_PEER_S_CONNECTED);
				it->second.actions |= RS_PEER_DISCONNECTED;
				mStatusChanged = true;

				updateLastContact = true; /* time of disconnect */
			}

			if (it->second.connAddrs.size() >= 1)
			{
				it->second.actions |= RS_PEER_CONNECT_REQ;
				mStatusChanged = true;
			}

			if (it->second.dhtVisible)
			{
				doDhtAssist = true;
			}
		}
	}
	
	if (updatePeerAddr)
	{
		pqiIpAddress raddr;
		raddr.mAddr = remote_peer_address;
		raddr.mSeenTime = time(NULL);
		raddr.mSrc = 0;

#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::connectResult() Success and we initiated connection... Updating Address";
		std::cerr << std::endl;
#endif
		mPeerMgr->updateCurrentAddress(id, raddr);
	}

	if (updateLastContact)
	{
		mPeerMgr->updateLastContact(id);
	}


	/* inform NetAssist of result. This is slightly duplicating below, as we switch it on/off
	 * in a second anyway. However, the FAILURE of UDP connection, must be informed.
 	 * 	
	 * actually the way the DHT works at the moment, both forms of feedback are required.
	 * this handles connection requests, the other searches. As they are independent... do both.
	 */

	if (flags == RS_NET_CONN_UDP_ALL)
	{
#ifdef LINKMGR_DEBUG
#endif
		std::cerr << "p3LinkMgrIMPL::connectResult() Sending Feedback for UDP connection";
		std::cerr << std::endl;
		if (success)
		{
#ifdef LINKMGR_DEBUG
#endif
			std::cerr << "p3LinkMgrIMPL::connectResult() UDP Update CONNECTED to: " << id;
			std::cerr << std::endl;

			mNetMgr->netAssistStatusUpdate(id, NETMGR_DHT_FEEDBACK_CONNECTED);
		}
		else
		{
#ifdef LINKMGR_DEBUG
#endif
			std::cerr << "p3LinkMgrIMPL::connectResult() UDP Update FAILED to: " << id;
			std::cerr << std::endl;

			/* have no differentiation between failure and closed? */
			mNetMgr->netAssistStatusUpdate(id, NETMGR_DHT_FEEDBACK_CONN_FAILED);
		}
	}


	if (success)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::connectResult() Success switching off DhtAssist for friend: " << id;
		std::cerr << std::endl;
#endif
		/* always switch it off now */
		mNetMgr->netAssistFriend(id,false);

		/* inform NetMgr that we know this peers address: but only if external address */
		if (isExternalNet(&(remote_peer_address.sin_addr)))
		{
			mNetMgr->netAssistKnownPeer(id,remote_peer_address, 
				NETASSIST_KNOWN_PEER_FRIEND | NETASSIST_KNOWN_PEER_ONLINE);
		}
	}
	else
	{
		if (doDhtAssist)
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::connectResult() Fail, Enabling DhtAssist for: " << id;
			std::cerr << std::endl;
#endif
			mNetMgr->netAssistFriend(id,true) ;
		}
		else
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::connectResult() Fail, No DhtAssist, as No DHT visibility for: " << id;
			std::cerr << std::endl;
#endif
		}

		/* inform NetMgr that this peer is offline */
		mNetMgr->netAssistKnownPeer(id,remote_peer_address, NETASSIST_KNOWN_PEER_FRIEND | NETASSIST_KNOWN_PEER_OFFLINE);
	}

	return success;
}

/******************************** Feedback ......  *********************************
 * From various sources
 */

// from pqissl, when a connection failed due to security
void 	p3LinkMgrIMPL::notifyDeniedConnection(const std::string& gpgid,const std::string& sslid,const std::string& sslcn,const struct sockaddr_in &addr, bool incoming)
{
	std::cerr << "p3LinkMgrIMPL::notifyDeniedConnection()";
	std::cerr << " pgpid: " << gpgid;
	std::cerr << " sslid: " << sslid;
	std::cerr << " sslcn: " << sslcn;
	std::cerr << std::endl;

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

        std::map<std::string, peerConnectState>::iterator it;
	it = mFriendList.find(sslid);
	if (it == mFriendList.end())
	{
		std::cerr << "p3LinkMgrIMPL::notifyDeniedConnection() of NON-FRIEND: " << sslid;
		std::cerr << std::endl;
		return;
	}

	it->second.wasDeniedConnection = true;
	it->second.deniedTS = time(NULL);

	if ((!incoming) && it->second.inConnAttempt)
	{
		it->second.deniedInConnAttempt = true;
		it->second.deniedConnectionAttempt = it->second.currentConnAddrAttempt;

		std::cerr << "p3LinkMgrIMPL::notifyDeniedConnection() Denied In Connection Attempt";
		std::cerr << std::endl;
	}
	else
	{
		it->second.deniedInConnAttempt = false;
		std::cerr << "p3LinkMgrIMPL::notifyDeniedConnection() Denied NOT In Connection Attempt";
		std::cerr << std::endl;
	}
	return;
}
	

void    p3LinkMgrIMPL::peerStatus(std::string id, const pqiIpAddrSet &addrs,
                       uint32_t type, uint32_t flags, uint32_t source)
{
	/* HACKED UP FIX ****/

        std::map<std::string, peerConnectState>::iterator it;
	bool isFriend = true;

	time_t now = time(NULL);

	peerAddrInfo details;
	details.type    = type;
	details.found   = true;
	details.addrs = addrs;
	details.ts      = now;
	
	bool updateNetConfig = (source == RS_CB_PERSON);	
	uint32_t peerVisibility = 0;
	uint32_t peerNetMode = 0;

	uint32_t ownNetMode = mNetMgr->getNetworkMode();
	
	{
		RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

	{
		/* Log */
		std::string out = "p3LinkMgrIMPL::peerStatus() id: " + id;
		rs_sprintf_append(out, " type: %lu flags: %lu source: %lu\n", type, flags, source);
		addrs.printAddrs(out);
		
		rslog(RSL_WARNING, p3connectzone, out);
#ifdef LINKMGR_DEBUG
		std::cerr << out << std::endl;
#endif
	}

	/* look up the id */
	it = mFriendList.find(id);
	if (it == mFriendList.end())
	{
		/* check Others list */
		isFriend = false;
		it = mOthersList.find(id);
		if (it == mOthersList.end())
		{
			/* not found - ignore */
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::peerStatus() Peer Not Found - Ignore" << std::endl;
#endif
			return;
		}
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::peerStatus() Peer is in mOthersList" << std::endl;
#endif
	}

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::peerStatus() Current Peer State:" << std::endl;
	printConnectState(std::cerr, it->second);
	std::cerr << std::endl;
#endif

	/* update the status */

	/* if source is DHT */
	if (source == RS_CB_DHT)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::peerStatus() Update From DHT:";
		std::cerr << std::endl;
#endif
		/* DHT can tell us about
		 * 1) connect type (UDP/TCP/etc)
		 * 2) local/external address
		 */
		it->second.source = RS_CB_DHT;
		it->second.dht = details;

		/* If we get a info -> then they are online */
		it->second.state |= RS_PEER_S_ONLINE;
		it->second.lastavailable = now;

	}
	else if (source == RS_CB_DISC)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::peerStatus() Update From DISC:";
		std::cerr << std::endl;
#endif
		/* DISC can tell us about
		 * 1) connect type (UDP/TCP/etc)
		 * 2) local/external addresses
		 */
		it->second.source = RS_CB_DISC;
		it->second.disc = details;


		if (flags & RS_NET_FLAGS_ONLINE)
		{
			it->second.actions |= RS_PEER_ONLINE;
			it->second.state |= RS_PEER_S_ONLINE;
			it->second.lastavailable = now;
			mStatusChanged = true;
		}

	}
	else if (source == RS_CB_PERSON)
	{
		/* PERSON can tell us about
		 * 1) online / offline
		 * 2) connect address
		 * -> update all!
		 */
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::peerStatus() Update From PERSON:";
		std::cerr << std::endl;
#endif

		it->second.source = RS_CB_PERSON;
		it->second.peer = details;

		it->second.state |= RS_PEER_S_ONLINE;
		it->second.lastavailable = now;

		/* must be online to recv info (should be connected too!) 
		 * but no need for action as should be connected already 
		 * 
		 * One problem with these states... is that we will never find them via DHT
		 * if these flags are switched off here... If we get a connection attempt via DHT
		 * we should switch the DHT search back on.
		 */

		peerNetMode = 0; //it->second.netMode &= (~RS_NET_MODE_ACTUAL); /* clear actual flags */
		if (flags & RS_NET_FLAGS_EXTERNAL_ADDR)
		{
			peerNetMode = RS_NET_MODE_EXT;
		}
		else if (flags & RS_NET_FLAGS_STABLE_UDP)
		{
			peerNetMode = RS_NET_MODE_UDP;
		}
		else
		{
			peerNetMode = RS_NET_MODE_UNREACHABLE;
		}


		/* always update VIS status */
		if (flags & RS_NET_FLAGS_USE_DISC)
		{
			peerVisibility &= (~RS_VIS_STATE_NODISC);
		}
		else
		{
			peerVisibility |= RS_VIS_STATE_NODISC;
		}

		if (flags & RS_NET_FLAGS_USE_DHT)
		{
			peerVisibility &= (~RS_VIS_STATE_NODHT);
		}
		else
		{
			peerVisibility |= RS_VIS_STATE_NODHT;
		}

		
	}

	/* Determine Reachability (only advisory) */
	if (ownNetMode & RS_NET_MODE_UDP)
	{
                if ((details.type & RS_NET_CONN_UDP_DHT_SYNC) ||
		    (details.type & RS_NET_CONN_TCP_EXTERNAL)) 
		{
			/* reachable! */
			it->second.state &= (~RS_PEER_S_UNREACHABLE);
		}
		else 
		{
			/* unreachable */
			it->second.state |= RS_PEER_S_UNREACHABLE;
		}
	}
	else if (ownNetMode & RS_NET_MODE_UNREACHABLE)
	{
		if (details.type & RS_NET_CONN_TCP_EXTERNAL) 
		{
			/* reachable! */
			it->second.state &= (~RS_PEER_S_UNREACHABLE);
		}
		else 
		{
			/* unreachable */
			it->second.state |= RS_PEER_S_UNREACHABLE;
		}
	}
	else
	{
		it->second.state &= (~RS_PEER_S_UNREACHABLE);
	}

	if (!isFriend)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::peerStatus() NOT FRIEND " << " id: " << id << std::endl;
#endif

		{
			rslog(RSL_WARNING, p3connectzone, "p3LinkMgrIMPL::peerStatus() NO CONNECT (not friend)");
		}
		return;
	}

	/* if already connected -> done */
	if (it->second.state & RS_PEER_S_CONNECTED)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::peerStatus() PEER ONLINE ALREADY " << " id: " << id << std::endl;
#endif
		{
			/* Log */
			rslog(RSL_WARNING, p3connectzone, "p3LinkMgrIMPL::peerStatus() NO CONNECT (already connected!)");
		}

		return;
	}


      } /****** STACK UNLOCK MUTEX *******/
	
	bool newAddrs = mPeerMgr->updateAddressList(id, addrs);
	if (updateNetConfig)
	{
		mPeerMgr -> setVisState(id, peerVisibility);
		mPeerMgr -> setNetworkMode(id, peerNetMode);
	}

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::peerStatus()" << " id: " << id;
	std::cerr << " type: " << type << " flags: " << flags;
	std::cerr << " source: " << source << std::endl;
	std::cerr << " addrs: " << std::endl;
	std::string out;
	addrs.printAddrs(out);
	std::cerr << out << std::endl;

#endif

#ifndef P3CONNMGR_NO_AUTO_CONNECTION 

#ifndef P3CONNMGR_NO_TCP_CONNECTIONS
	if (newAddrs)
	{
		retryConnectTCP(id);
	}
    
#endif  // P3CONNMGR_NO_TCP_CONNECTIONS


#else 
#endif  // P3CONNMGR_NO_AUTO_CONNECTION 

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::peerStatus() Resulting Peer State:" << std::endl;
	printConnectState(std::cerr, it->second);
	std::cerr << std::endl;
#endif

}

/* This has become very unwieldy - as extra arguments are required for UDP connections */
void    p3LinkMgrIMPL::peerConnectRequest(std::string id, struct sockaddr_in raddr, struct sockaddr_in proxyaddr, struct sockaddr_in srcaddr,
						uint32_t source, uint32_t flags, uint32_t delay, uint32_t bandwidth)
{
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::peerConnectRequest() id: " << id;
	std::cerr << " raddr: " << rs_inet_ntoa(raddr.sin_addr) << ":" << ntohs(raddr.sin_port);
	std::cerr << " proxyaddr: " << rs_inet_ntoa(proxyaddr.sin_addr) << ":" << ntohs(proxyaddr.sin_port);
	std::cerr << " srcaddr: " << rs_inet_ntoa(srcaddr.sin_addr) << ":" << ntohs(srcaddr.sin_port);
	std::cerr << " source: " << source;
	std::cerr << " flags: " << flags;
	std::cerr << " delay: " << delay;
	std::cerr << " bandwidth: " << bandwidth;
	std::cerr << std::endl;
#endif
	{
		/* Log */
		std::string out = "p3LinkMgrIMPL::peerConnectRequest() id: " + id;
		rs_sprintf_append(out, " raddr: %s:%u", rs_inet_ntoa(raddr.sin_addr).c_str(), ntohs(raddr.sin_port));
		rs_sprintf_append(out, " proxyaddr: %s:%u", rs_inet_ntoa(proxyaddr.sin_addr).c_str(), ntohs(proxyaddr.sin_port));
		rs_sprintf_append(out, " srcaddr: %s:%u", rs_inet_ntoa(srcaddr.sin_addr).c_str(), ntohs(srcaddr.sin_port));
		rs_sprintf_append(out, " source: %lu", source);
		rs_sprintf_append(out, " flags: %lu", flags);
		rs_sprintf_append(out, " delay: %lu", delay);
		rs_sprintf_append(out, " bandwidth: %lu", bandwidth);
		
		rslog(RSL_WARNING, p3connectzone, out);
	}

	/******************** TCP PART *****************************/

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::peerConnectRequest() (From DHT Only)" << std::endl;
#endif

	if (source == RS_CB_DHT)
	{

		if (flags & RS_CB_FLAG_MODE_TCP)
		{

#ifndef P3CONNMGR_NO_TCP_CONNECTIONS

#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::peerConnectRequest() DHT says Online ==> so try TCP";
			std::cerr << std::endl;
#endif
			{
				RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

	        		std::map<std::string, peerConnectState>::iterator it;
				if (mFriendList.end() == (it = mFriendList.find(id)))
				{
#ifdef LINKMGR_DEBUG
					std::cerr << "p3LinkMgrIMPL::peerConnectRequest() ERROR Peer is not Friend" << std::endl;
#endif
					return;
				}
	
				/* if already connected -> done */
				if (it->second.state & RS_PEER_S_CONNECTED)
				{
#ifdef LINKMGR_DEBUG
					std::cerr << "p3LinkMgrIMPL::peerConnectRequest() ERROR Peer Already Connected" << std::endl;
#endif
					return;
				} 
				/* setup specific attempt for DHT found address. */
				locked_ConnectAttempt_SpecificAddress(&(it->second), &raddr);
			}

			retryConnect(id);

#endif

		}
		else
		{
			/* UDP Attempt! */
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::peerConnectRequest() DHT says CONNECT ==> tryConnectUDP()";
			std::cerr << std::endl;
#endif
			tryConnectUDP(id, raddr, proxyaddr, srcaddr, flags, delay, bandwidth);

		}
		return;

	}
	else
	{	// IS THIS USED???
		std::cerr << "p3LinkMgrIMPL::peerConnectRequest() ERROR source OTHER ==> NOOP" << std::endl;
		std::cerr << std::endl;

		return;
	}
}


/*******************************************************************/
/*******************************************************************/
       /*************** External Control ****************/
bool   p3LinkMgrIMPL::retryConnect(const std::string &id)
{
	/* push all available addresses onto the connect addr stack */
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::retryConnect() id: " << id << std::endl;
#endif

#ifndef P3CONNMGR_NO_TCP_CONNECTIONS

	retryConnectTCP(id);

#endif  // P3CONNMGR_NO_TCP_CONNECTIONS

	return true;
}



bool   p3LinkMgrIMPL::tryConnectUDP(const std::string &id, struct sockaddr_in &rUdpAddr, 
									struct sockaddr_in &proxyaddr, struct sockaddr_in &srcaddr,
									uint32_t flags, uint32_t delay, uint32_t bandwidth)

{

#ifdef DISABLE_UDP_CONNECTIONS

	std::cerr << "p3LinkMgrIMPL::tryConnectUDP() CONNECTIONS DISABLED FOR NOW... id: " << id << std::endl;

	std::cerr << "p3LinkMgrIMPL::tryConnectUDP() PARAMS id: " << id;
	std::cerr << " raddr: " << rs_inet_ntoa(rUdpAddr.sin_addr) << ":" << ntohs(rUdpAddr.sin_port);
	std::cerr << " proxyaddr: " << rs_inet_ntoa(proxyaddr.sin_addr) << ":" << ntohs(proxyaddr.sin_port);
	std::cerr << " srcaddr: " << rs_inet_ntoa(srcaddr.sin_addr) << ":" << ntohs(srcaddr.sin_port);
	std::cerr << " flags: " << flags;
	std::cerr << " delay: " << delay;
	std::cerr << " bandwidth: " << bandwidth;
	std::cerr << std::endl;

	mNetMgr->netAssistStatusUpdate(id, NETMGR_DHT_FEEDBACK_CONN_FAILED);

	return false;	
#endif


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

	/* push all available addresses onto the connect addr stack */
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::tryConnectUDP() id: " << id << std::endl;
#endif

        if (id == getOwnId()) {
            #ifdef LINKMGR_DEBUG
            rslog(RSL_WARNING, p3connectzone, "p3LinkMgrIMPL::retryConnectUDP() Failed, connecting to own id: ");
            #endif
            return false;
        }

        /* look up the id */
        std::map<std::string, peerConnectState>::iterator it;
	if (mFriendList.end() == (it = mFriendList.find(id)))
	{
#ifdef LINKMGR_DEBUG
               std::cerr << "p3LinkMgrIMPL::retryConnectUDP() Peer is not Friend" << std::endl;
#endif
		return false;
	}

	/* if already connected -> done */
        if (it->second.state & RS_PEER_S_CONNECTED)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::retryConnectUDP() Peer Already Connected" << std::endl;
#endif
                if (it->second.connecttype & RS_NET_CONN_TUNNEL) {
#ifdef LINKMGR_DEBUG
                    std::cerr << "p3LinkMgrIMPL::retryConnectUDP() Peer Connected through a tunnel connection, let's try a normal connection." << std::endl;
#endif
                } else {
#ifdef LINKMGR_DEBUG
                    	std::cerr << "p3LinkMgrIMPL::retryConnectUDP() Peer Connected no more connection attempts" << std::endl;
#endif
                    return false;
                }
	}

	/* Explicit Request to start the UDP connection */
	if (isValidNet(&(rUdpAddr.sin_addr)))
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "Adding udp connection attempt: ";
		std::cerr << "Addr: " << rs_inet_ntoa(rUdpAddr.sin_addr);
		std::cerr << ":" << ntohs(rUdpAddr.sin_port);
		std::cerr << std::endl;
#endif
		peerConnectAddress pca;
		pca.addr = rUdpAddr;
		pca.type = RS_NET_CONN_UDP_PEER_SYNC;
		pca.delay = delay; 
		pca.ts = time(NULL);
		pca.period = P3CONNMGR_UDP_DEFAULT_PERIOD;
		pca.flags = flags;
		
		pca.proxyaddr = proxyaddr;
		pca.srcaddr = srcaddr;
		pca.bandwidth = bandwidth;

		// Push address to the front... so it happens quickly (before any timings are lost).
		addAddressIfUnique(it->second.connAddrs, pca, true);
	}

	/* finish it off */
	return locked_ConnectAttempt_Complete(&(it->second));
}





bool   p3LinkMgrIMPL::retryConnectTCP(const std::string &id)
{
	/* Check if we should retry first */
	{
		RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	
		/* push all available addresses onto the connect addr stack...
		 * with the following exceptions:
	   	 *   - check local address, see if it is the same network as us
		     - check address age. don't add old ones
		 */
	
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::retryConnectTCP() id: " << id << std::endl;
#endif
	
		if (id == getOwnId()) 
		{
#ifdef LINKMGR_DEBUG
			rslog(RSL_WARNING, p3connectzone, "p3LinkMgrIMPL::retryConnectTCP() Failed, connecting to own id: ");
#endif
			return false;
		}
	
	        /* look up the id */
	        std::map<std::string, peerConnectState>::iterator it;
		if (mFriendList.end() == (it = mFriendList.find(id)))
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::retryConnectTCP() Peer is not Friend" << std::endl;
#endif
			return false;
		}
	
		/* if already connected -> done */
		if (it->second.state & RS_PEER_S_CONNECTED)
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::retryConnectTCP() Peer Already Connected" << std::endl;
#endif
			if (it->second.connecttype & RS_NET_CONN_TUNNEL) 
			{
#ifdef LINKMGR_DEBUG
				std::cerr << "p3LinkMgrIMPL::retryConnectTCP() Peer Connected through a tunnel connection, let's try a normal connection." << std::endl;
#endif
			} 
			else 
			{
#ifdef LINKMGR_DEBUG
				std::cerr << "p3LinkMgrIMPL::retryConnectTCP() Peer Connected no more connection attempts" << std::endl;
#endif
				return false;
			}
		}
	} /****** END of LOCKED ******/

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::retryConnectTCP() Getting Address from PeerMgr for : " << id;
	std::cerr << std::endl;
#endif
	/* If we reach here, must retry .... extract the required info from p3PeerMgr */

	struct sockaddr_in lAddr;
	struct sockaddr_in eAddr;
	pqiIpAddrSet histAddrs;
	std::string dyndns;

	if (mPeerMgr->getConnectAddresses(id, lAddr, eAddr, histAddrs, dyndns))
	{
		RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/

	        std::map<std::string, peerConnectState>::iterator it;
		if (mFriendList.end() != (it = mFriendList.find(id)))
		{
			locked_ConnectAttempt_CurrentAddresses(&(it->second), &lAddr, &eAddr);

			uint16_t dynPort = ntohs(eAddr.sin_port);
			if (!dynPort)
				dynPort = ntohs(lAddr.sin_port);
			if (dynPort)
			{
				locked_ConnectAttempt_AddDynDNS(&(it->second), dyndns, dynPort);
			}

			locked_ConnectAttempt_HistoricalAddresses(&(it->second), histAddrs);

			//locked_ConnectAttempt_AddTunnel(&(it->second));
	
			/* finish it off */
			return locked_ConnectAttempt_Complete(&(it->second));
		}
		else
		{
			std::cerr << "p3LinkMgrIMPL::retryConnectTCP() ERROR failed to find friend data : " << id;
			std::cerr << std::endl;
		}
	}
	else
	{
		std::cerr << "p3LinkMgrIMPL::retryConnectTCP() ERROR failed to addresses from PeerMgr for: " << id;
		std::cerr << std::endl;
	}

	return false;

}


#define MAX_TCP_ADDR_AGE	(3600 * 24 * 14) // two weeks in seconds.

bool  p3LinkMgrIMPL::locked_CheckPotentialAddr(const struct sockaddr_in *addr, time_t age)
{
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr("; 
	std::cerr << rs_inet_ntoa(addr->sin_addr);
	std::cerr << ":" << ntohs(addr->sin_port);
	std::cerr << ", " << age << ")";
	std::cerr << std::endl;
#endif

	/*
	 * if it is old - quick rejection 
	 */
	if (age > MAX_TCP_ADDR_AGE)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr() REJECTING - TOO OLD"; 
		std::cerr << std::endl;
#endif
		return false;
	}

	bool isValid = isValidNet(&(addr->sin_addr));
	//	bool isLoopback = isLoopbackNet(&(addr->sin_addr));
	//	bool isPrivate = isPrivateNet(&(addr->sin_addr));
	bool isExternal = isExternalNet(&(addr->sin_addr));

	/* if invalid - quick rejection */
	if (!isValid)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr() REJECTING - INVALID";
		std::cerr << std::endl;
#endif
		return false;
	}

	/* if it is on the ban list - ignore */
	/* checks - is it the dreaded 1.0.0.0 */

	std::list<struct in_addr>::const_iterator it;
	for(it = mBannedIpList.begin(); it != mBannedIpList.end(); it++)
	{
		if (it->s_addr == addr->sin_addr.s_addr)
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr() REJECTING - ON BANNED IPLIST"; 
			std::cerr << std::endl;
#endif
			return false;
		}
	}


	/* if it is an external address, we'll accept it.
	 * - even it is meant to be a local address.
	 */
	if (isExternal)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr() ACCEPTING - EXTERNAL"; 
		std::cerr << std::endl;
#endif
		return true;
	}


	/* get here, it is private or loopback 
	 *  - can only connect to these addresses if we are on the same subnet.
	    - check net against our local address.
	 */

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr() Checking sameNet against: "; 
	std::cerr << rs_inet_ntoa(mLocalAddress.sin_addr);
	std::cerr << ")";
	std::cerr << std::endl;
#endif

	if (sameNet(&(mLocalAddress.sin_addr), &(addr->sin_addr)))
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr() ACCEPTING - PRIVATE & sameNET"; 
		std::cerr << std::endl;
#endif
		return true;
	}

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::locked_CheckPotentialAddr() REJECTING - PRIVATE & !sameNET"; 
	std::cerr << std::endl;
#endif

	/* else it fails */
	return false;

}


void  p3LinkMgrIMPL::locked_ConnectAttempt_SpecificAddress(peerConnectState *peer, struct sockaddr_in *remoteAddr)
{
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_SpecificAddresses()";
	std::cerr << std::endl;
#endif
	if ((remoteAddr) && (locked_CheckPotentialAddr(remoteAddr, 0)))
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_SpecificAddresses() ";
		std::cerr << "Adding tcp connection attempt: ";
		std::cerr << "Addr: " << rs_inet_ntoa(remoteAddr->sin_addr);
		std::cerr << ":" << ntohs(remoteAddr->sin_port);
		std::cerr << std::endl;
#endif
		peerConnectAddress pca;
		pca.addr = *remoteAddr;
		pca.type = RS_NET_CONN_TCP_EXTERNAL;
		pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
		pca.ts = time(NULL);
		pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
		sockaddr_clear(&(pca.proxyaddr));
		sockaddr_clear(&(pca.srcaddr));
		pca.bandwidth = 0;
		
		addAddressIfUnique(peer->connAddrs, pca, false);
	}
}


void  p3LinkMgrIMPL::locked_ConnectAttempt_CurrentAddresses(peerConnectState *peer, struct sockaddr_in *localAddr, struct sockaddr_in *serverAddr)
{
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_CurrentAddresses()";
	std::cerr << std::endl;
#endif
	// Just push all the addresses onto the stack.
	/* try "current addresses" first */
	if ((localAddr) && (locked_CheckPotentialAddr(localAddr, 0)))
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_CurrentAddresses() ";
		std::cerr << "Adding tcp connection attempt: ";
		std::cerr << "Current Local Addr: " << rs_inet_ntoa(localAddr->sin_addr);
		std::cerr << ":" << ntohs(localAddr->sin_port);
		std::cerr << std::endl;
#endif
		peerConnectAddress pca;
		pca.addr = *localAddr;
		pca.type = RS_NET_CONN_TCP_LOCAL;
		pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
		pca.ts = time(NULL);
		pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
		sockaddr_clear(&(pca.proxyaddr));
		sockaddr_clear(&(pca.srcaddr));
		pca.bandwidth = 0;
		
		addAddressIfUnique(peer->connAddrs, pca, false);
	}

	if ((serverAddr) && (locked_CheckPotentialAddr(serverAddr, 0)))
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_CurrentAddresses() ";
		std::cerr << "Adding tcp connection attempt: ";
		std::cerr << "Current Ext Addr: " << rs_inet_ntoa(serverAddr->sin_addr);
		std::cerr << ":" << ntohs(serverAddr->sin_port);
		std::cerr << std::endl;
#endif
		peerConnectAddress pca;
		pca.addr = *serverAddr;
		pca.type = RS_NET_CONN_TCP_EXTERNAL;
		pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
		pca.ts = time(NULL);
		pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
		sockaddr_clear(&(pca.proxyaddr));
		sockaddr_clear(&(pca.srcaddr));
		pca.bandwidth = 0;
		
		addAddressIfUnique(peer->connAddrs, pca, false);
	}
}


void  p3LinkMgrIMPL::locked_ConnectAttempt_HistoricalAddresses(peerConnectState *peer, const pqiIpAddrSet &ipAddrs)
{
	/* now try historical addresses */
	/* try local addresses first */
	std::list<pqiIpAddress>::const_iterator ait;
	time_t now = time(NULL);

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_HistoricalAddresses()";
	std::cerr << std::endl;
#endif
	for(ait = ipAddrs.mLocal.mAddrs.begin(); 
		ait != ipAddrs.mLocal.mAddrs.end(); ait++)
	{
		if (locked_CheckPotentialAddr(&(ait->mAddr), now - ait->mSeenTime))
		{

#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_HistoricalAddresses() ";
			std::cerr << "Adding tcp connection attempt: ";
			std::cerr << "Local Addr: " << rs_inet_ntoa(ait->mAddr.sin_addr);
			std::cerr << ":" << ntohs(ait->mAddr.sin_port);
			std::cerr << std::endl;
#endif

			peerConnectAddress pca;
			pca.addr = ait->mAddr;
			pca.type = RS_NET_CONN_TCP_LOCAL;
			pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
			pca.ts = time(NULL);
			pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
			sockaddr_clear(&(pca.proxyaddr));
			sockaddr_clear(&(pca.srcaddr));
			pca.bandwidth = 0;
			
			addAddressIfUnique(peer->connAddrs, pca, false);
		}
	}

	for(ait = ipAddrs.mExt.mAddrs.begin(); 
		ait != ipAddrs.mExt.mAddrs.end(); ait++)
	{
		if (locked_CheckPotentialAddr(&(ait->mAddr), now - ait->mSeenTime))
		{
	
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_HistoricalAddresses() ";
			std::cerr << "Adding tcp connection attempt: ";
			std::cerr << "Ext Addr: " << rs_inet_ntoa(ait->mAddr.sin_addr);
			std::cerr << ":" << ntohs(ait->mAddr.sin_port);
			std::cerr << std::endl;
#endif
			peerConnectAddress pca;
			pca.addr = ait->mAddr;
			pca.type = RS_NET_CONN_TCP_EXTERNAL;
			pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
			pca.ts = time(NULL);
			pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;
			sockaddr_clear(&(pca.proxyaddr));
			sockaddr_clear(&(pca.srcaddr));
			pca.bandwidth = 0;
			
			addAddressIfUnique(peer->connAddrs, pca, false);
		}
	}
}


void  p3LinkMgrIMPL::locked_ConnectAttempt_AddDynDNS(peerConnectState *peer, std::string dyndns, uint16_t port)
{
	/* try dyndns address too */
	struct in_addr addr;
	if (!dyndns.empty() && port) 
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_AddDynDNS() Looking up DynDNS address: " << dyndns << std::endl;
#endif
		if(mDNSResolver->getIPAddressFromString(dyndns, addr))
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_AddDynDNS() ";
			std::cerr << "Adding tcp connection attempt: ";
			std::cerr << "DynDNS Addr: " << rs_inet_ntoa(addr);
			std::cerr << ":" << ntohs(port);
			std::cerr << std::endl;
#endif
			peerConnectAddress pca;
			pca.addr.sin_family = AF_INET;
			pca.addr.sin_addr.s_addr = addr.s_addr;
			pca.addr.sin_port = htons(port);
			pca.type = RS_NET_CONN_TCP_EXTERNAL;
			//for the delay, we add a random time and some more time when the friend list is big
			pca.delay = P3CONNMGR_TCP_DEFAULT_DELAY;
			pca.ts = time(NULL);
			pca.period = P3CONNMGR_TCP_DEFAULT_PERIOD;

			sockaddr_clear(&(pca.proxyaddr));
			sockaddr_clear(&(pca.srcaddr));
			pca.bandwidth = 0;
			
			/* check address validity */
			if (locked_CheckPotentialAddr(&(pca.addr), 0))
			{
				addAddressIfUnique(peer->connAddrs, pca, true);
			}
		}
		else
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_AddDynDNS() DNSResolver hasn't found addr yet";
			std::cerr << std::endl;
#endif
		}
	}
	else
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_AddDynDNS() Address(" << dyndns << ") or Port(" << port << ") NULL ignoring";
		std::cerr << std::endl;
#endif
	}
}


void  p3LinkMgrIMPL::locked_ConnectAttempt_AddTunnel(peerConnectState *peer)
{
	if (!(peer->state & RS_PEER_S_CONNECTED) && mAllowTunnelConnection)
	{
#ifdef LINKMGR_DEBUG
		std::cerr << "Adding TUNNEL Connection Attempt";
		std::cerr << std::endl;
#endif
		peerConnectAddress pca;
		pca.type = RS_NET_CONN_TUNNEL;
		pca.ts = time(NULL);
		pca.period = 0;

		sockaddr_clear(&pca.addr);

		sockaddr_clear(&(pca.proxyaddr));
		sockaddr_clear(&(pca.srcaddr));
		pca.bandwidth = 0;
		
		
		addAddressIfUnique(peer->connAddrs, pca, false);
	}
}


bool  p3LinkMgrIMPL::addAddressIfUnique(std::list<peerConnectAddress> &addrList, peerConnectAddress &pca, bool pushFront)
{
	/* iterate through the list, and make sure it isn't already 
	 * in the list 
	 */
#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::addAddressIfUnique() Checking Address: " << rs_inet_ntoa(pca.addr.sin_addr);
	std::cerr << std::endl;
#endif

	std::list<peerConnectAddress>::iterator it;
	for(it = addrList.begin(); it != addrList.end(); it++)
	{
		if ((pca.addr.sin_addr.s_addr == it->addr.sin_addr.s_addr)	&&
			(pca.addr.sin_port == it->addr.sin_port)	&&
			(pca.type == it->type))
		{
#ifdef LINKMGR_DEBUG
			std::cerr << "p3LinkMgrIMPL::addAddressIfUnique() Discarding Duplicate Address";
			std::cerr << std::endl;
#endif
			/* already */
			return false;
		}
	}

#ifdef LINKMGR_DEBUG
	std::cerr << "p3LinkMgrIMPL::addAddressIfUnique() Adding New Address";
	std::cerr << std::endl;
#endif

	if (pushFront)
	{
		addrList.push_front(pca);
	}
	else
	{
		addrList.push_back(pca);
	}

	return true;
}



bool  p3LinkMgrIMPL::locked_ConnectAttempt_Complete(peerConnectState *peer)
{

	/* flag as last attempt to prevent loop */
	//add a random perturbation between 0 and 2 sec.
	peer->lastattempt = time(NULL) + rand() % MAX_RANDOM_ATTEMPT_OFFSET; 

	if (peer->inConnAttempt) 
	{
                /*  -> it'll automatically use the addresses we added */
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_Complete() Already in CONNECT ATTEMPT";
		std::cerr << std::endl;
		std::cerr << "p3LinkMgrIMPL::locked_ConnectAttempt_Complete() Remaining ConnAddr Count: " << peer->connAddrs.size();
		std::cerr << std::endl;
#endif
		return true;
	}

	/* start a connection attempt */
	if (peer->connAddrs.size() > 0) 
	{
#ifdef LINKMGR_DEBUG
		std::string out = "p3LinkMgrIMPL::locked_ConnectAttempt_Complete() Started CONNECT ATTEMPT!\n" ;
		rs_sprintf_append(out, "p3LinkMgrIMPL::locked_ConnectAttempt_Complete() ConnAddr Count: %u", peer->connAddrs.size());
		rslog(RSL_DEBUG_ALERT, p3connectzone, out);
		std::cerr << out << std::endl;
#endif

		peer->actions |= RS_PEER_CONNECT_REQ;
		mStatusChanged = true;
	    return true; 
	} 
	else 
	{
#ifdef LINKMGR_DEBUG
		std::string out = "p3LinkMgrIMPL::locked_ConnectAttempt_Complete() No addr in the connect attempt list. Not suitable for CONNECT ATTEMPT!";
		rslog(RSL_DEBUG_ALERT, p3connectzone, out);
		std::cerr << out << std::endl;
#endif
	    return false;
	}
	return false;
}


/***********************************************************************************************************
 ************************************* Handling of Friends *************************************************
 ***********************************************************************************************************/

int p3LinkMgrIMPL::addFriend(const std::string &id, bool isVisible)
{
	rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::addFriend() id: " + id);

	{
		RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
	
#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::addFriend(" << id << "," << isVisible << ")";
		std::cerr << std::endl;
#endif

	        std::map<std::string, peerConnectState>::iterator it;
		it = mFriendList.find(id);
	
		if (it != mFriendList.end())
		{
			std::cerr << "p3LinkMgrIMPL::addFriend() ERROR, friend already exists : " << id;
			std::cerr << std::endl;
			return 0;
		}
	
		peerConnectState pcs;
		pcs.dhtVisible = isVisible;
		pcs.id = id;
		pcs.name = "NoName";
		pcs.state = RS_PEER_S_FRIEND;
		pcs.actions = RS_PEER_NEW;
		pcs.linkType = RS_NET_CONN_SPEED_UNKNOWN ;
	
		mFriendList[id] = pcs;

		mStatusChanged = true;
	}
	
	mNetMgr->netAssistFriend(id, isVisible);

	return 1;
}


int p3LinkMgrIMPL::removeFriend(const std::string &id)
{
	rslog(RSL_WARNING, p3connectzone, "p3LinkMgr::removeFriend() id: " + id);

	{
		RsStackMutex stack(mLinkMtx); /****** STACK LOCK MUTEX *******/
		
		#ifdef LINKMGR_DEBUG
		std::cerr << "p3LinkMgrIMPL::removeFriend(" << id << ")";
		std::cerr << std::endl;
		#endif
		
		std::map<std::string, peerConnectState>::iterator it;
		it = mFriendList.find(id);
		
		if (it == mFriendList.end())
		{
			std::cerr << "p3LinkMgrIMPL::removeFriend() ERROR, friend not there : " << id;
			std::cerr << std::endl;
			return 0;
		}
		
		/* Move to OthersList (so remove can be handled via the action) */
		peerConnectState peer = it->second;
		
		peer.state &= (~RS_PEER_S_FRIEND);
		peer.state &= (~RS_PEER_S_CONNECTED);
		peer.state &= (~RS_PEER_S_ONLINE);
		peer.actions = RS_PEER_MOVED;
		peer.inConnAttempt = false;
		mOthersList[id] = peer;

		mStatusChanged = true;
		
		mFriendList.erase(it);
	}
		
	mNetMgr->netAssistFriend(id, false);

	return 1;
}


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

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


                std::map<std::string, peerConnectState>::iterator it;
                for(it = mFriendList.begin(); it != mFriendList.end(); it++) 
                {
                        out << "\t SSL ID: " << it->second.id;
                        out << "\t State: " << it->second.state;
                        out << std::endl;
                }

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

        return;
}


void  printConnectState(std::ostream &out, peerConnectState &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;
}



