/****************************************************************/
/*																*/
/*  DBServer.cpp												*/
/*																*/
/*  Implementation of the CtheDBServer class.						*/
/*	This class is a part of the Date Server Application			*/
/*																*/
/*  Programmed by LYFZ van der Meer							*/
/*  Copyright LYFZ Software Solutions 2002						*/
/*	http://www.LYFZvandermeer.nl								*/
/*																*/
/*  Last updated: 10 july 2002									*/
/*																*/
/****************************************************************/


#include "stdafx.h"
#include "theDBServer.h"
#include "MyLock.h"
//extern int g_port;
CtheDBServer *g_pWndServer=NULL;
extern void WriteTextLog(CString str);
CtheDBServer::CtheDBServer()
{
	m_nPort = g_dwCSPort;
	m_nMaxUsers = 200;
	m_strWelcomeMessage = "Welcome to LYFZ Date Server";
	m_strGoodbyeMessage = "Bye";
	m_nTimeout = 30;
	m_bRunning = FALSE;
	m_hWnd = NULL;
	m_nConnectionCount = 0;

	// intialize statistics
	m_dwTotalSentBytes = 0;
	m_dwTotalReceivedBytes = 0;
	m_nTotalConnections = 0;
	m_nFilesDownloaded = 0;
	m_nFilesUploaded = 0;
	m_nFailedDownloads = 0;
	m_nFailedUploads = 0;

	m_nSecurityMode = 0;

	m_nStatisticsInterval = 0;

	// load users
	m_UserManager.Serialize(FALSE);

	// load security
	m_SecurityManager.Serialize(FALSE);
}

CtheDBServer::~CtheDBServer()
{
	Stop();
}

BEGIN_MESSAGE_MAP(CtheDBServer, CWnd)
	//{{AFX_MSG_MAP(CtheDBServer)
	ON_WM_TIMER()
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_THREADSTART, OnThreadStart)
	ON_MESSAGE(WM_THREADCLOSE, OnThreadClose)
	ON_MESSAGE(WM_THREADMSG, OnThreadMessage)
END_MESSAGE_MAP()



/********************************************************************/
/*																	*/
/* Function name : Start											*/
/* Description   : Start listining on port 21 and accept new		*/
/*				   connections.										*/
/*																	*/
/********************************************************************/
//��21�Ŷ˿�����������׼�������µ�����
BOOL CtheDBServer::Start()
{
	if (m_bRunning)
	{
		WriteTextLog("����������");
		return FALSE;
	}

	// Ϊ��Ϣ���ʹ���һ���µĴ���
    if (!CWnd::CreateEx(0, AfxRegisterWndClass(0), "Date Server Notification Sink", WS_POPUP, 0,0,0,0, NULL, 0))
	{
		AddTraceLine(0, "Failed to create notification window.");
		WriteTextLog("������Ӧ����ʧ��");
		return FALSE;
	}
	// ����һ���µ��׽���
	if (m_ListenSocket.Create(g_dwCSPort)) // g_port==5678;
	{
		// ��������
		if (m_ListenSocket.Listen())
		{
            g_pWndServer=this;
		//	m_ListenSocket.m_pWndServer = this;
			m_bRunning = TRUE;	
			
			// Jeff.��ʱ��1 ��������������ʾ���շ��ص���ֵ��Ϣ;
			SetTimer(1, m_nStatisticsInterval, NULL);	// ��ʱ��1��

			AddTraceLine(0, "Date Server started on port %d.", g_dwCSPort);
			return TRUE;
		}
	}
	DWORD dwError = GetLastError();
	ErrorExit(CString("����5678�˿�ʧ��"), dwError);
	AddTraceLine(0, "Date Server failed to listen on port %d.", g_dwCSPort);

	// ����ʧ��,����֪ͨ����;
	if (IsWindow(m_hWnd))
		DestroyWindow();
	m_hWnd = NULL;

	return FALSE;
}


/********************************************************************/
/*																	*/
/* Function name : Stop												*/
/* Description   : Stop Date Server.									*/
/*																	*/
/********************************************************************/
//ֹͣ���ݷ�����
extern CConnectThread *g_pThreadPt[200];
void CtheDBServer::Stop()
{
	if (!m_bRunning)
		return;

	// ֹͣͳ�Ƽ�ʱ��
	KillTimer(1);

	m_bRunning = FALSE;	
	m_ListenSocket.Close();

	CConnectThread* pThread = NULL;

	//�ر������߳�
	do
	{
		m_CriticalSection.Lock();

		POSITION pos = m_ThreadList.GetHeadPosition();
		if (pos != NULL)
		{
			pThread = (CConnectThread *)m_ThreadList.GetAt(pos);
		
			m_CriticalSection.Unlock();

			// �����߳�
			int nThreadID = pThread->m_nThreadID;
			HANDLE hThread = pThread->m_hThread;

			AddTraceLine(0, "[%d] Shutting down thread...", nThreadID);

			// ֪ͨ�߳�ֹͣ
			pThread->SetThreadPriority(THREAD_PRIORITY_HIGHEST);
			pThread->PostThreadMessage(WM_QUIT,0,0);

			//�ȴ��߳���ֹ
			if (WaitWithMessageLoop(hThread, 5000) == FALSE)
			{
				// �̲߳�����ֹ
				AddTraceLine(0, "[%d] Problem while killing thread.", nThreadID);
				//����߳�
				m_CriticalSection.Lock();
				POSITION rmPos = m_ThreadList.Find(pThread);
				if (rmPos != NULL)
					m_ThreadList.RemoveAt(rmPos);
				m_CriticalSection.Unlock();
			}
			else
			{
				AddTraceLine(0, "[%d] Thread successfully stopped.", nThreadID);
			}
		}
		else
		{
			m_CriticalSection.Unlock();	
			pThread = NULL;
		}
	}
	while (pThread != NULL);
    //���·�����״̬
	AddTraceLine(0, "Date Server stopped.");

	for(int i=0; i<200; i++)
	{
		g_pThreadPt[i]=NULL;
	}

	if (IsWindow(m_hWnd))
		DestroyWindow();

	m_hWnd = NULL;
}


/********************************************************************/
/*																	*/
/* Function name : IsActive											*/
/* Description   : Is Date Server active?							*/
/*																	*/
/********************************************************************/
BOOL CtheDBServer::IsActive()
{
	return m_bRunning;
}


/********************************************************************/
/*																	*/
/* Function name : SetMaxUsers										*/
/* Description   : Set maximum number of users						*/
/*																	*/
/********************************************************************/
void CtheDBServer::SetMaxUsers(int nValue)
{
//	m_nMaxUsers = nValue;
}


/********************************************************************/
/*																	*/
/* Function name : SetPort											*/
/* Description   : Set listening port for new connections			*/
/*																	*/
/********************************************************************/
void CtheDBServer::SetPort(int nValue)
{
//	m_nPort = nValue;
}


/********************************************************************/
/*																	*/
/* Function name : SetTimeout										*/
/* Description   : Set connection timeout							*/
/*																	*/
/********************************************************************/
void CtheDBServer::SetTimeout(int nValue)
{
//	m_nTimeout = nValue;
}


/********************************************************************/
/*																	*/
/* Function name : SetTimeout										*/
/* Description   : Set connection timeout							*/
/*																	*/
/********************************************************************/
void CtheDBServer::SetStatisticsInterval(int nValue)
{
	m_nStatisticsInterval = nValue;
	if (m_nStatisticsInterval != 0)
	{
		KillTimer(1);
		SetTimer(1, nValue, NULL);
	}
	else
	{
		KillTimer(1);
	}
}


/********************************************************************/
/*																	*/
/* Function name : SetWelcomeMessage								*/
/* Description   : Set welcome message								*/
/*																	*/
/********************************************************************/
void CtheDBServer::SetWelcomeMessage(LPCTSTR lpszText)
{
	m_strWelcomeMessage = lpszText;
}


/********************************************************************/
/*																	*/
/* Function name : SetGoodbyeMessage								*/
/* Description   : Set goodbye message								*/
/*																	*/
/********************************************************************/
void CtheDBServer::SetGoodbyeMessage(LPCTSTR lpszText)
{
	m_strGoodbyeMessage = lpszText;
}


/********************************************************************/
/*																	*/
/* Function name : Initialize										*/
/* Description   : Initialize event sink.							*/
/*																	*/
/********************************************************************/
void CtheDBServer::Initialize(CFTPEventSink *pEventSink)
{
	m_pEventSink = pEventSink;
}


/********************************************************************/
/*																	*/
/* Function name : AddTraceLine										*/
/* Description   : FTP status change.								*/
/*																	*/
/********************************************************************/
void CtheDBServer::AddTraceLine(int nType, LPCTSTR pstrFormat, ...)
{
	CString str;

	// format and write the data we were given
	va_list args;
	va_start(args, pstrFormat);
	str.FormatV(pstrFormat, args);
	m_pEventSink->OnFTPStatusChange(nType, str);
}


/********************************************************************/
/*																	*/
/* Function name : OnThreadStart									*/
/* Description   : Called when a new thread has started.			*/
/*																	*/
/********************************************************************/
LRESULT CtheDBServer::OnThreadStart(WPARAM wParam, LPARAM)
{
	m_nConnectionCount++;
	m_nTotalConnections++;
	CConnectThread *pThread = (CConnectThread *)wParam;
 
//	pThread->m_ConnectSocket.GetPeerName(pThread->m_strRemoteHost, port);
	AddTraceLine(0, "[%d] Client connected from %s.", pThread->m_nThreadID, pThread->m_strRemoteHost);

	return TRUE;
}


/********************************************************************/
/*																	*/
/* Function name : OnThreadClose									*/
/* Description   : Called when a thread is about to stop.			*/
/*																	*/
/********************************************************************/
LRESULT CtheDBServer::OnThreadClose(WPARAM wParam, LPARAM lParam)
{
	m_nConnectionCount--;
	CConnectThread *pThread = (CConnectThread *)wParam;

	AddTraceLine(0, "[%d] Client disconnected from %s.", pThread->m_nThreadID, pThread->m_strRemoteHost);
	
	m_pEventSink->OnFTPUserDisconnected(pThread->m_nThreadID, pThread->m_ConnectSocket.m_strUserName);
	return TRUE;
}


/********************************************************************/
/*																	*/
/* Function name : OnThreadMessage									*/
/* Description   : Message sent from connect connection.			*/
/*																	*/
/********************************************************************/
LRESULT CtheDBServer::OnThreadMessage(WPARAM wParam, LPARAM lParam)
{
	switch(wParam)
	{
		case 0:
			m_dwTotalSentBytes += (int)lParam;
			break;
		case 1:
			m_dwTotalReceivedBytes += (int)lParam;
			break;
		case 2:
			switch(lParam)
			{
				case FTPSTAT_DOWNLOADSUCCEEDED:
					m_nFilesDownloaded++;
					break;
				case FTPSTAT_UPLOADSUCCEEDED:
					m_nFilesUploaded++;
					break;
				case FTPSTAT_DOWNLOADFAILED:
					m_nFailedDownloads++;
					break;
				case FTPSTAT_UPLOADFAILED:
					m_nFailedUploads++;
					break;
			}
			break;
		default:
			break;
	}
	return TRUE;
}


/********************************************************************/
/*																	*/
/* Function name : CheckMaxUsers									*/
/* Description   : Reached maximum number of connections?			*/
/*																	*/
/********************************************************************/
BOOL CtheDBServer::CheckMaxUsers()
{
	if (m_nConnectionCount > m_nMaxUsers)
		return TRUE;
	else
		return FALSE;
}


/********************************************************************/
/*																	*/
/* Function name : OnTimer											*/
/* Description   : Update statictics.								*/
/*																	*/
/********************************************************************/
void CtheDBServer::OnTimer(UINT nIDEvent) 
{
	// update statictics ?
	if (nIDEvent == 1)
	{
		m_pEventSink->OnFTPSentBytesChange(m_dwTotalSentBytes);
		m_pEventSink->OnFTPReceivedBytesChange(m_dwTotalReceivedBytes);
		m_pEventSink->OnFTPStatisticChange(0, m_nTotalConnections);
		m_pEventSink->OnFTPStatisticChange(1, m_nConnectionCount);
		m_pEventSink->OnFTPStatisticChange(2, m_nFilesDownloaded);
		m_pEventSink->OnFTPStatisticChange(3, m_nFilesUploaded);
		m_pEventSink->OnFTPStatisticChange(4, m_nFailedDownloads);
		m_pEventSink->OnFTPStatisticChange(5, m_nFailedUploads);
	}
	CWnd::OnTimer(nIDEvent);
}


/********************************************************************/
/*																	*/
/* Function name : SetSecurityMode									*/
/* Description   : Set security mode.								*/
/*																	*/
/********************************************************************/
void CtheDBServer::SetSecurityMode(BOOL bBlockSpecific)
{
	m_nSecurityMode = bBlockSpecific ? 0 : 1;
}

int FindArray2(CStringArray *pArray, CString Str)
{
	for(int i=0; i<pArray->GetSize (); i++)
	{
		if(pArray->ElementAt (i)==Str)
			return i;
	}
	return -1;
}

//extern CStringArray g_ipnoallowarray;
/********************************************************************/
/*																	*/
/* Function name : IsIPAddressAllowed								*/
/* Description   : Check (based on blockmode) if IP is allowed.		*/
/*																	*/
/********************************************************************/
BOOL CtheDBServer::IsIPAddressAllowed(LPCTSTR lpszIPAddress)
{
//	MyLock lock("IsIPAddressAllowed");
//	if(::FindArray2 (&g_ipnoallowarray, lpszIPAddress)!=-1)return 0;
	return 1;
/*	if (m_nSecurityMode == 0)
	{
		return !m_SecurityManager.IsIPAddressBlocked(lpszIPAddress);
	}
	else
	{
		return m_SecurityManager.IsIPAddressNonBlocked(lpszIPAddress);
	}*/
}