/*

WinPopup Protocol plugin for Miranda IM.

Copyright (C) 2004-2010 Nikolay Raspopov

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.
*/

#include "stdafx.h"

#define MAX_MESSAGE_SIZE		424 // ������ ������ ������ (����) ����������/�����������
									// ����� ��������

mailslot pluginMailslot;			// �������� ��� ������ ���������

/*const struct {					// ������ ������������� ���������
	LPCTSTR name;
} blacklist [] = {
	_T("winpopup"),
	_T("vikpopup"),
	_T("netter"),
	_T("realpopup"),
	NULL
};*/

////////////////////////////////////////////////////////////////////////
// Class mailslot

mailslot::mailslot () :
	m_hMailslot (INVALID_HANDLE_VALUE),
	m_MonitorTerm (NULL),
	m_Monitor (NULL)
{
}

mailslot::~mailslot ()
{
	Destroy ();
}

bool mailslot::Create (LPCTSTR Name)
{
	CLock oLock( m_cs );

	m_sName = Name;

	bool ret = true;
	if ( ! IsValid() )
	{		
		// �������� ���������
		CString sAddr;
		sAddr.Format( _T("\\\\.\\mailslot\\%s"), (LPCTSTR)m_sName );

		m_hMailslot = CreateMailslot( sAddr, 0, 2000, NULL );
		if ( ! IsValid() )
		{
			ret = false;
			DWORD err = GetLastError ();
			if (err == ERROR_ALREADY_EXISTS)
				WarningBox (NULL, 0, _T("%s\r\n%s"), T_CREATE_ERROR,
					TranslateT ("Please shutdown any other IM applications and/or Messenger service"));
			else
				WarningBox (NULL, err, T_CREATE_ERROR);
		}
	}
	if (ret)
	{
		if (m_MonitorTerm)
			ResetEvent (m_MonitorTerm);
		else
			m_MonitorTerm = CreateEvent (NULL, TRUE, FALSE, NULL);
		m_Monitor = (HANDLE)mir_forkthread( MonitorThread, this );
	}

	return ret;
}

void mailslot::AskForDestroy()
{
	if (m_MonitorTerm)
		SetEvent (m_MonitorTerm);
}

void mailslot::Destroy ()
{
	CLock oLock( m_cs );
	
	// ������ �������� ����������� ���������
	AskForDestroy();
	
	// �������� ���������
	if ( IsValid() )
	{
		CloseHandle (m_hMailslot);
		m_hMailslot = INVALID_HANDLE_VALUE;
	}

	// �������� �������� �����������
	if (m_Monitor)
	{
		if (WaitForSingleObject (m_Monitor, ALMOST_INFINITE) == WAIT_TIMEOUT)
		{
			LOG("Terminate mailslot monitor!");
			TerminateThread (m_Monitor, 0);
		}
		m_Monitor = NULL;
	}
	if (m_MonitorTerm)
	{
		CloseHandle (m_MonitorTerm);
		m_MonitorTerm = NULL;
	}
}

bool mailslot::IsValid() const
{
	return ( m_hMailslot != INVALID_HANDLE_VALUE );
}

bool mailslot::SendMailslotMessage(HANDLE hContact, LPCTSTR msg, DWORD& err)
{
	// ��������� ��������
	CString sTo = GetNick( hContact );
	if ( sTo.IsEmpty() )
	{
		err = ERROR_BAD_NETPATH;
		return false;
	}

	// ��������� ������ �����
	CString sFrom = GetNick( NULL );
	if ( sFrom.IsEmpty() )
	{
		err = ERROR_BAD_NETPATH;
		return false;
	}

	// ��� ������� ������ ��� ��� ���������
	// bool bGroup = IsGroup( hContact );

	// ������ ������ ���������: FROM<00>TO<00>MESSAGE<00>
	COemString sOemMessage = msg;
	COemString sOemTo = (LPCTSTR)sTo;
	COemString sOemFrom = (LPCTSTR)sFrom;

	// ������ ��������� ������
	int fixed_size = sOemFrom.GetLength() + 1 + sOemTo.GetLength() + 1 + 1;
	if ( fixed_size >= MAX_MESSAGE_SIZE )
	{
		err = ERROR_BAD_LENGTH;
		return false;
	}

	// �������� ��������� ��� �������� ���������
	CString sAddr;
	sAddr.Format( _T("\\\\%s\\mailslot\\%s"), sTo, (LPCTSTR)m_sName );
	HANDLE hFile = CreateFile( sAddr, GENERIC_WRITE, FILE_SHARE_READ, NULL,
		OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); 
	if ( hFile == INVALID_HANDLE_VALUE )
	{
		err = GetLastError();
		return false;
	}

	int max_message_size = MAX_MESSAGE_SIZE - fixed_size;
	char buf[ MAX_MESSAGE_SIZE ] = {};
	lstrcpynA( buf, sOemFrom, sOemFrom.GetLength() + 1 );
	lstrcpynA( buf + sOemFrom.GetLength() + 1, sOemTo, sOemTo.GetLength() + 1 );
	do
	{
		int message_size = ( sOemMessage.GetLength() < max_message_size ) ? 
			sOemMessage.GetLength() : max_message_size;
		lstrcpynA( buf + fixed_size - 1, sOemMessage, message_size + 1 );

		// ������� ������
		DWORD written = 0;
		if ( ! WriteFile( hFile, buf, (DWORD)fixed_size + message_size, &written, NULL ) ||
			( written < (DWORD)fixed_size ) )
		{
			err = GetLastError();
			CloseHandle( hFile );
			return false;
		}
		Sleep( 100 );

		// ������������ �� ���������� �����
		sOemMessage.CutFromStart( message_size );
	}
	while ( sOemMessage.GetLength() );

	err = ERROR_SUCCESS;
	CloseHandle( hFile );
	return true;
}

bool mailslot::Receive(unsigned char* buf /* OEM */, DWORD size)
{
	// �������� ��������� <FROM><00><TO><00><MESSAGE><00> (��������� <00> ������������)
	if (size)
	{
		char* from = (char*) buf;
		char* to = lstrnchr (from, 0, (int)size);
		if (to)
		{
			DWORD from_len = (DWORD)( to - from + 1 );
			if ( from_len < size )
			{
				to++;
				size -= from_len;
				char* msg = lstrnchr (to, 0, (int)size);
				if (msg)
				{
					DWORD to_len = (DWORD)( msg - to + 1 );
					if (to_len < size)
					{
						msg++;
						size -= to_len;
						char* eof = lstrnchr (msg, 0, (int)size);
						DWORD msg_len = eof ? (DWORD)( eof - msg + 1 ) : size;
						if (msg_len == size)
						{
							CAnsiString sFrom (from);
							CAnsiString sTo (to);							
							CAnsiString sMessage (msg);
							ReceiveContactMessage(sFrom, sTo, sMessage, sMessage.GetLength ());
							return true;
						}
					}
				}
			}
		}
	}
	return false;
}

void mailslot::MonitorThread(void* param)
{
	if ( mailslot* pMailslot = (mailslot*)param )
	{
		pMailslot->Monitor();	
		pMailslot->m_Monitor = NULL;
	}
}

void mailslot::Monitor ()
{
	// �������� ���������� 500 �� ��� �������� ���������, ����� 50 ��
	while ( WaitForSingleObject( m_MonitorTerm, IsValid() ? 50u : 500u ) == WAIT_TIMEOUT )
	{
		// �������� �������� ���������
		LPSTR buf = NULL;
		for ( DWORD buf_size = MAX_MESSAGE_SIZE; IsValid(); buf_size += 1024 )
		{
			if ( WaitForSingleObject( m_MonitorTerm, 0 ) != WAIT_TIMEOUT )
				break;

			if ( buf ) mir_free( buf );
			buf = (LPSTR)mir_alloc( buf_size );

			DWORD readed = 0;
			DWORD err = ReadFile (m_hMailslot, buf, buf_size,
				&readed, NULL) ? ERROR_SUCCESS : GetLastError ();
			if (err == ERROR_ACCESS_DENIED || err == ERROR_SEM_TIMEOUT)
			{
				// ����-��� ���������
				break;
			}
			else if (err == ERROR_SUCCESS)
			{
				// ������ �������
				if (readed)
					if (!Receive((LPBYTE)buf, readed))
						LOG("Receive error (bad format?)");
				break;
			}
			else if (err == ERROR_INSUFFICIENT_BUFFER)
			{
				// �������� ������� ������
				continue;
			}
			else 
			{
				// ������ ������
				// ERROR_HANDLE_EOF - ����� ��������� ������
				LOG("ReadFile form mailslot error: %d", err);
				break;
			}
		}
		if ( buf ) mir_free( buf );
	}
}