/**
 **	File ......... MinderHandler.cpp
 **	Published ....  2004-04-17
 **	Author ....... grymse@alhem.net
**/
/*
Copyright (C) 2004  Anders Hedstrom

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#if (defined(__unix__) || defined(unix)) && !defined(USG)
#include <sys/param.h>
#endif

#ifdef _WIN32
#pragma warning(disable:4786)
#endif
#ifdef HAVE_OPENSSL

#include <stdlib.h>
//#include <time.h>
//#include "socket_include.h"
#ifdef _WIN32
#define random rand
#define srandom srand
#define strcasecmp stricmp
#endif
//#include "Parse.h"
#include "Uid.h"
#include "MinderSocket.h"
#include "Utility.h"
#include "MinderHandler.h"


#ifdef _DEBUG
#define DEB(x) x
#else
#define DEB(x)
#endif




MinderHandler::MinderHandler()
:SocketHandler()
,m_id("")
,m_external_ip("")
,m_message_id(0)
,m_host_id(0)
,m_bDebug(false)
,m_tMinder(0)
{
	GenerateID();
}


MinderHandler::~MinderHandler()
{
	for (seen_v::iterator it = m_seen.begin(); it != m_seen.end(); it++)
	{
		SEEN *p = *it;
		delete p;
	}
	{
		for (store_v::iterator it = m_store.begin(); it != m_store.end(); it++)
		{
			STORE *p = *it;
			delete p;
		}
	}
	{
		for (hosts_v::iterator it = m_hosts.begin(); it != m_hosts.end(); it++)
		{
			HOSTS *p = *it;
			delete p;
		}
	}
}


void MinderHandler::GenerateID()
{
	Uid t;
	m_id = t.GetUid();
}


const std::string& MinderHandler::GetID()
{
	return m_id;
}


void MinderHandler::SendMessage(const std::string& msg_in,short ttl)
{
	std::string msg;
	long message_id = m_message_id++;

	msg = "Message_" + Utility::l2string(m_host_id);
	msg += ":" + Utility::l2string(message_id);
	msg += ":" + Utility::l2string(ttl);
	msg += ":" + msg_in;

DEB(	printf("Message:\n%s\n",msg.c_str());)

	if (msg.size() > 255) // try is good even here, because of possible bandwidth differences between nodes
	{
		Store(m_host_id, message_id, msg);
		msg = "Try_" + Utility::l2string(m_host_id) + ":" + Utility::l2string(message_id);
		for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
		{
			MinionSocket *p = dynamic_cast<MinionSocket *>((*it).second);
			if (p && p -> Ready() )
			{
				Uid ruid(p -> GetRemoteId());
				memcpy(GetKey_m2minion() + 8,ruid.GetBuf(),16);
				p -> Send( p -> encrypt(GetKey_m2minion(),msg) + "\n" );
			}
		}
	}
	else
	{
		for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
		{
			MinionSocket *p = dynamic_cast<MinionSocket *>((*it).second);
			if (p && p -> Ready() )
			{
				Uid ruid(p -> GetRemoteId());
				memcpy(GetKey_m2minion() + 8,ruid.GetBuf(),16);
//				p -> Send( p -> Utility::base64(msg) + "\n" );
				p -> Send( p -> encrypt(GetKey_m2minion(),msg) + "\n" );
			}
		}
	}
}


void MinderHandler::SendMessage(const std::string& hid,const std::string& mid,short ttl,const std::string& msg_in,std::list<std::string>& clist,ulong_v& hosts)
{
	std::string msg;
	long host_id = atol(hid.c_str());
	long message_id = atol(mid.c_str());

	msg = "Message_" + hid;
	msg += ":" + mid;
	msg += ":" + Utility::l2string(ttl); // + ":" + ttlstr;
	msg += ":" + msg_in;

	if (msg.size() > 255)
	{
		Store(host_id, message_id, msg);
		msg = "Try_" + Utility::l2string(host_id) + ":" + Utility::l2string(message_id);
		for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
		{
			MinionSocket *p = dynamic_cast<MinionSocket *>((*it).second);
			if (p && p -> Ready() )
			{
				bool ok = true;
				for (std::list<std::string>::iterator it = clist.begin(); it != clist.end() && ok; it++)
				{
					std::string id = *it;
					if (!strcmp(p -> GetRemoteId().c_str(),id.c_str()))
					{
						ok = false;
					}
				}
				for (ulong_v::iterator i2 = hosts.begin(); i2 != hosts.end() && ok; i2++)
				{
					if (*i2 == static_cast<unsigned long>(p -> GetRemoteHostId()))
					{
						ok = false;
					}
				}
				if (ok)
				{
					Uid ruid(p -> GetRemoteId());
					memcpy(GetKey_m2minion() + 8,ruid.GetBuf(),16);
					p -> Send( p -> encrypt(GetKey_m2minion(), msg) + "\n" );
				}
			}
		}
	}
	else
	{
		for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
		{
			MinionSocket *p = dynamic_cast<MinionSocket *>((*it).second);
			if (p && p -> Ready() )
			{
				bool ok = true;
				for (std::list<std::string>::iterator it = clist.begin(); it != clist.end(); it++)
				{
					std::string id = *it;
					if (!strcmp(p -> GetRemoteId().c_str(),id.c_str()))
					{
						ok = false;
						break;
					}
				}
				if (ok)
				{
					Uid ruid(p -> GetRemoteId());
					memcpy(GetKey_m2minion() + 8,ruid.GetBuf(),16);
//					p -> Send( p -> Utility::base64(msg) + "\n" );
					p -> Send( p -> encrypt(GetKey_m2minion(), msg) + "\n" );
				}
			}
		}
	}
}


void MinderHandler::KeepAlive()
{
	std::string msg;
	std::string tmp;
	msg = "KeepAlive";
	m_b.encode(msg, tmp, false);
	SendMessage(tmp);
}


void MinderHandler::SendConnectList()
{
	std::string msg;
	std::string tmp = "_";
	msg = "ConnectList";
	for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
	{
		MinionSocket *p = dynamic_cast<MinionSocket*>((*it).second);
		if (p && p -> Ready() )
		{
			msg += tmp + p -> GetRemoteId();
			tmp = ":";
		}
	}
	m_b.encode(msg, tmp, false);
//printf("ConnectList:\n%s\n",msg.c_str());
	SendMessage(tmp, 0);
}


bool MinderHandler::MinderSockets()
{
	for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
	{
		MinderSocket *p = dynamic_cast<MinderSocket *>((*it).second);
		if (p)
			return true;
	}
	return false;
}


bool MinderHandler::FindMinion(const std::string& id)
{
	for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
	{
		MinionSocket *p = dynamic_cast<MinionSocket*>((*it).second);
		if (p && p -> GetRemoteId() == id) //!strcasecmp(p -> GetRemoteId().c_str(),id.c_str()))
		{
			return true;
		}
	}
	for (socket_m::iterator i2 = m_add.begin(); i2 != m_add.end(); i2++)
	{
		MinionSocket *p = dynamic_cast<MinionSocket*>((*i2).second);
		if (p && p -> GetRemoteId() == id) //!strcasecmp(p -> GetRemoteId().c_str(),id.c_str()))
		{
			return true;
		}
	}
	return false;
}


int MinderHandler::Count()
{
	int q = 0;
	for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
	{
		MinionSocket *p = dynamic_cast<MinionSocket*>((*it).second);
		if (p)
		{
			q++;
		}
	}
	for (socket_m::iterator i2 = m_add.begin(); i2 != m_add.end(); i2++)
	{
		MinionSocket *p = dynamic_cast<MinionSocket*>((*i2).second);
		if (p)
		{
			q++;
		}
	}
	return q;
}


bool MinderHandler::Seen(unsigned long host_id,unsigned long message_id,bool temp)
{
	if (host_id == m_host_id)
		return true;
	for (seen_v::iterator it = m_seen.begin(); it != m_seen.end(); it++)
	{
		SEEN *p = *it;
		if (p -> Eq(host_id,message_id))
		{
			if (p -> m_temp && !temp)
			{
				p -> m_temp = temp;
				return false;
			}
			return true;
		}
	}
	// %! while time - erase
	time_t t = time(NULL);
	while (m_seen.size())
	{
		SEEN *p = m_seen[0];
		if (t - p -> m_t > 120)
		{
			delete p;
			m_seen.erase(m_seen.begin());
		}
		else
		{
			break;
		}
	}
	SEEN *p = new SEEN(host_id,message_id);
	p -> m_temp = temp;
	m_seen.push_back(p);
	return false;
}


void MinderHandler::Store(long hid,long mid,const std::string& msg)
{
	// %! while time - erase
	time_t t = time(NULL);
	while (m_store.size())
	{
		STORE *p = m_store[0];
		if (t - p -> m_t > 10)
		{
			delete p;
			m_store.erase(m_store.begin());
		}
		else
		{
			break;
		}
	}
	STORE *p = new STORE(hid,mid,msg);
	m_store.push_back(p);
}


bool MinderHandler::StoreGet(long hid,long mid,std::string& msg)
{
	for (store_v::iterator it = m_store.begin(); it != m_store.end(); it++)
	{
		STORE *p = *it;
		if (p -> m_hid == hid && p -> m_mid == mid)
		{
			msg = p -> m_message;
			return true;
		}
	}
	return false;
}


void MinderHandler::AddHost(ipaddr_t a,port_t p,const std::string& k,long r)
{
DEB(	printf("AddHost: size %d\n",m_hosts.size());)
	if (m_hosts.size() >= 20)
		return;
	for (hosts_v::iterator it = m_hosts.begin(); it != m_hosts.end(); it++)
	{
		HOSTS *p2 = *it;
		if (p2 -> ip == a && p2 -> port == p)
		{
			p2 -> key == k;
			return;
		}
	}
	HOSTS *p2 = new HOSTS(a,p,k,r);
	m_hosts.push_back(p2);
}


bool MinderHandler::GetHost(ipaddr_t& a,port_t& p,std::string& k,long& r)
{
	if (m_hosts.size())
	{
		hosts_v::iterator it = m_hosts.begin();
		HOSTS *p2 = *it;
		a = p2 -> ip;
		p = p2 -> port;
		k = p2 -> key;
		r = p2 -> remote_host_id;
		delete p2;
		m_hosts.erase(it);
		return true;
	}
	return false;
}


void MinderHandler::SendTop(const std::string& hid)
{
	std::string msg;
	std::string tmp;
	msg = "Top_" + hid;
	msg += ":" + Utility::l2string(GetHostId());
	m_b.encode(GetVersion(), tmp, false);
	msg += ":" + tmp;
#ifdef _WIN32
	msg += ":Win32";
#elif defined __FreeBSD__
        msg += ":FreeBSD";
#else
	msg += ":Linux";
#endif
	for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
	{
		MinionSocket *p = dynamic_cast<MinionSocket*>((*it).second);
//		TelnetSocket *p2 = dynamic_cast<TelnetSocket *>((*it).second);
		if (p && p -> Ready() )
		{
			msg += ":" + Utility::l2string(p -> GetRemoteHostId() );
		}
	}
//printf(" Top reply: %s\n",msg.c_str());
	m_b.encode(msg, tmp, false);
	SendMessage(tmp);
}


void MinderHandler::Tops(FILE *fil)
{
	for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
	{
		MinionSocket *p = dynamic_cast<MinionSocket*>((*it).second);
		if (p && p -> Ready() )
		{
			fprintf(fil,"\t\"%ld\" -> \"%ld\"\n",
				GetHostId(),
				p -> GetRemoteHostId());
		}
	}
}




#endif // HAVE_OPENSSL


syntax highlighted by Code2HTML, v. 0.9.1