#include "stdafx.h"
#include "sl_struct.h"
#include <StepData.h>
#include "..\CaplData_Client\MiddleWare.h"
#include "commands.h"

// для защиты от переполнения
#define MAX_NUM_TYPES_LIC 1000


inline DWORD GetRandNotMin(DWORD min_val)
{
	DWORD retval = rand()+rand()*0xFFFF;
	if(retval < min_val) retval += min_val*2;
	return retval;
}

bool EnMaskSL_PS_DATA(apl_SL_PS_DATA *p_sl_data, const char* mask_data, int mask_size)
{
	if(p_sl_data == 0) return false;
	if(mask_data == 0) return false;
	if(mask_size == 0) return false;

	char* data = (char*)(&p_sl_data->server_ip);
	int pos_m = 0, pos_d = 0;
	do{
		data[pos_d] ^= mask_data[pos_m];
		pos_d++;
		pos_m++;
		if(pos_m >= mask_size) pos_m = 0;
	}while(pos_d < 40);

	return true;
}

// Проверка отзыва ключей.
LPCTSTR gl_Revokes[] = {_T("Отозванный ключ"), _T("#9995")};

bool apl_SL_PS_DATA::ReadFromIterator(CaplDataBufEx &dbuf, eSLBufGeneration mode)
{
	DWORD d;
	if(!dbuf.ReadFromIterator(&d, 4))return false;
	command=(EaplSLCommand)d;

	if(!dbuf.ReadFromIterator(&server_ip, 4))return false;
	if(!dbuf.ReadFromIterator(&server_port, 4))return false;
	if(!dbuf.ReadFromIterator(&client_id_m, 4))return false;
	if(mode == SLBufGen_2)
	{
		if(!dbuf.ReadFromIterator(&client_id_s1, 4))return false;
		if(!dbuf.ReadFromIterator(&client_id_s2, 4))return false;
	}
	if(!dbuf.ReadFromIterator(&client_ip, 4))return false;
	if(!dbuf.ReadFromIterator(&type_license, 4))return false;
	if(!dbuf.ReadFromIterator(&data_1, 4))return false;
	if(!dbuf.ReadFromIterator(&data_2, 4))return false;
	if(!dbuf.ReadFromIterator(&data_3, 4))return false;
	if(!dbuf.ReadFromIterator(&ver, 4))return false;
	return true;
}

bool apl_SL_PS_DATA::Add2Iterator(CaplDataBufEx &dbuf, eSLBufGeneration mode)
{
	DWORD d=(DWORD)command;
	if(!dbuf.Add2Iterator(&d, 4))return false;
	if(!dbuf.Add2Iterator(&server_ip, 4))return false;
	if(!dbuf.Add2Iterator(&server_port, 4))return false;
	if(!dbuf.Add2Iterator(&client_id_m, 4))return false;
	if(mode == SLBufGen_2)
	{
		if(!dbuf.Add2Iterator(&client_id_s1, 4))return false;
		if(!dbuf.Add2Iterator(&client_id_s2, 4))return false;
	}
	if(!dbuf.Add2Iterator(&client_ip, 4))return false;
	if(!dbuf.Add2Iterator(&type_license, 4))return false;
	if(!dbuf.Add2Iterator(&data_1, 4))return false;
	if(!dbuf.Add2Iterator(&data_2, 4))return false;
	if(!dbuf.Add2Iterator(&data_3, 4))return false;
	if(!dbuf.Add2Iterator(&ver, 4))return false;
	return true;
}

bool apl_SL_PS_DATA::Compare(apl_SL_PS_DATA *p_sl_data)
{
	if(p_sl_data == 0) return false;
	return (
		command == p_sl_data->command && 
		server_ip == p_sl_data->server_ip && 
		server_port == p_sl_data->server_port &&
		client_id_m == p_sl_data->client_id_m && 
		client_id_s1 == p_sl_data->client_id_s1 && 
		client_id_s2 == p_sl_data->client_id_s2 &&
		client_ip == p_sl_data->client_ip && 
		ver == p_sl_data->ver && 
			(p_sl_data->command == apl_SL_GET_DATE || type_license == p_sl_data->type_license) 
		);
}

SLTransportData::SLTransportData()
{
	SL_ip = 0;
	SL_port = 0;
	command = apl_SL_GET_VER; // самая нейтральная команда
	Client_ip = 0;
	client_id_m =0;
	client_id_s1 =0;
	client_id_s2 =0;
	server_ip = 0;
	server_port = 0;
	Type_license = _T("");
	is_use_by_date = 0;
	key_error = 0;
	dt = 0;
	aplError = 0;
	SL_status = _T("");
}

SLTransportData::SLTransportData(SLTransportData *src)
{
	Set(src, true);
}

void SLTransportData::Set(SLTransportData *src, bool bAllData)
{
	if(src==0) return;
	if(bAllData)
	{
		SL_ip = src->SL_ip;
		SL_port = src->SL_port;
		command = src->command;
		Client_ip = src->Client_ip;
		client_id_m = src->client_id_m;
		client_id_s1 = src->client_id_s1;
		client_id_s2 = src->client_id_s2;
		server_ip = src->server_ip;
		server_port = src->server_port;
		Type_license = src->Type_license;
	}
	is_use_by_date = src->is_use_by_date;
	key_error = src->key_error;
	dt = src->dt;
	aplError = src->aplError;
	SL_status = src->SL_status;
}

void SLTransportData::Get(apl_SL_PS_DATA *dest, CStringArray &types, CaplStrMap &indexes)
{
	if(dest==0)return;
	dest->Set(VER_SL_DATA);
	dest->command = command;
	dest->server_ip = server_ip;
	dest->server_port = server_port;
	dest->client_id_m = client_id_m;
	dest->client_id_s1 = client_id_s1;
	dest->client_id_s2 = client_id_s2;
	dest->client_ip = Client_ip;
	int indx = indexes.Find(Type_license);
	if(indx != -1)
		dest->type_license = indexes.GetAt(indx)->val;
	else
	{
		dest->type_license = types.Add(Type_license);
		indexes.Add(Type_license, dest->type_license);
	}
}

bool SLTransportData::Compare(SLTransportData *src)
{
	if(src==0) return false;
	return (SL_ip == src->SL_ip &&
		SL_port == src->SL_port &&
		command == src->command &&
		Client_ip == src->Client_ip &&
		client_id_m == src->client_id_m &&
		client_id_s1 == src->client_id_s1 &&
		client_id_s2 == src->client_id_s2 &&
		server_ip == src->server_ip &&
		server_port == src->server_port &&
		Type_license == src->Type_license);
}

bool SLTransportData::Read(CaplDataBufEx& dbuf)
{
	DWORD d;
	if(!dbuf.Read(&SL_ip, 4))return false;
	if(!dbuf.Read(&SL_port, 4))return false;
	if(!dbuf.Read(&d, 4))return false;
	command=(EaplSLCommand)d;
	if(!dbuf.Read(&Client_ip, 4))return false;
	if(!dbuf.Read(&client_id_m, 4))return false;
	if(!dbuf.Read(&client_id_s1, 4))return false;
	if(!dbuf.Read(&client_id_s2, 4))return false;
	if(!dbuf.ReadStrBuf_TCHAR_(Type_license))return false;
	if(!dbuf.Read(&is_use_by_date, 4))return false;
	if(!dbuf.Read(&key_error, 4))return false;
	if(!dbuf.Read(&dt, 8))return false;
	if(!dbuf.Read(&aplError, 4))return false;
	if(!dbuf.ReadStrBuf_TCHAR_(SL_status))return false;
	return true;
}

// В большинстве случаев dbuf будет с внешним буфером, поэтому используем Add2Iterator
bool SLTransportData::Write2Iterator(CaplDataBufEx& dbuf)
{
	if(!dbuf.Add2Iterator(&SL_ip, 4))return false;
	if(!dbuf.Add2Iterator(&SL_port, 4))return false;
	DWORD d=(DWORD)command;
	if(!dbuf.Add2Iterator(&d, 4))return false;
	if(!dbuf.Add2Iterator(&Client_ip, 4))return false;
	if(!dbuf.Add2Iterator(&client_id_m, 4))return false;
	if(!dbuf.Add2Iterator(&client_id_s1, 4))return false;
	if(!dbuf.Add2Iterator(&client_id_s2, 4))return false;
	if(!dbuf.AddStrBuf_TCHAR_2Iterator(Type_license))return false;
	if(!dbuf.Add2Iterator(&is_use_by_date, 4))return false;
	if(!dbuf.Add2Iterator(&key_error, 4))return false;
	if(!dbuf.Add2Iterator(&dt, 8))return false;
	if(!dbuf.Add2Iterator(&aplError, 4))return false;
	if(!dbuf.AddStrBuf_TCHAR_2Iterator(SL_status))return false;
	return true;
}


CAplAtomicLong gl_TimeLogStatLicense = 0;

void CServLicDescription::SetTimeLogStatLicense(int _TimeLogStatLicense)
{
	gl_TimeLogStatLicense.Set(_TimeLogStatLicense);
}


CServLicDescription::CServLicDescription(UINT32 sl_ip, UINT32 sl_port)
{
	CaplStackLogger stack_logger(__APL_FUNC__);

	m_TimeLogStatLicense = gl_TimeLogStatLicense.Get();

	m_sl_data.SL_ip = sl_ip;
	m_sl_data.SL_port = sl_port;
	m_sl_data.SL_status = ERR_MESSAGE_SL_CONNECTING;
	m_sl_data.aplError = APL_NET_SRV_NOT_CONNECT;

	m_last_bad_tick = 0;
	m_old_ver = false;

	m_list_clients_users.bAutoSort = true;

	m_live_counter = SL_DESCR_DEL_COUNTER;
	m_SL_ver = SL_VER_UNKNOWN;

	m_messagin_err = false;
	m_after_halt = false;

	m_time_request_max = 0;
	m_time_request_all = 0;
	m_num_pakets_max = 0;
	m_num_pakets_all = 0;
	m_num_request = 0;
	m_odt_stat_print = COleDateTime::GetCurrentTime();

#ifndef _DEBUG
	// Перезапускаем генератор случайных числе
	srand(GetTickCount());
#endif

	m_class_info.Format(_T("CServLicDescription ") POINTER_FORMAT _T(" "), this);
	m_ErrDescr = _T("");
}

CServLicDescription::~CServLicDescription()
{
	// Чтобы вывелась статистика
	if(m_TimeLogStatLicense != 0)
	{
		m_odt_stat_print = COleDateTime(1970,01,01,0,0,0);
		m_TimeLogStatLicense = 1;
		LogStatisticB();
	}

	CaplStackLogger stack_logger(__APL_FUNC__);
}

void CServLicDescription::LogStatisticB()
{
	if(MainModuleLog!=0)
	{
		CString str = _T("");
		COleDateTime odt = COleDateTime::GetCurrentTime();
		CaplEnterCriticalSection ecs(&m_data_protect);
		COleDateTimeSpan odts = odt - m_odt_stat_print;
		if(odts.GetTotalMinutes() > m_TimeLogStatLicense && m_num_request > 0)
		{
			m_odt_stat_print = odt;
			DWORD d1 = (DWORD)(m_time_request_all / m_num_request);
			DWORD d2 = (DWORD)(m_num_pakets_all / m_num_request);
			str.Format(APL_T("Ожидание сервера лицензии: mid: %i, max %i; пакетов mid %i max %i; запросов %i"), d1, m_time_request_max, d2, m_num_pakets_max, m_num_request);

			m_time_request_all =0;
			m_num_request = 0;
			m_time_request_max = 0;
			m_num_pakets_max = 0;
			m_num_pakets_all = 0;

		}
		ecs.Leave();
		if(str!=_T(""))
			MainModuleLog->LogMessage(str, true, true, true, true, __APL_FUNC__, _T(""), _T(""), _T(__FILE__), _T(__DATE__), __LINE__);	

	}
}

bool CServLicDescription::RemoveClient(int client_id)
{
	CaplEnterCriticalSection ecs(&m_data_protect);
	int pos = m_list_clients_users.FindByIn(client_id);
	if(pos!=-1)
	{
		return false;
	}

	// указанный клиент обслуживался найденным сервером лицензий
	// удаляем идентификатор клиента из списка найденного СЛ
	m_list_clients_users.Remove(pos);

	return true;
}


bool CServLicDescription::ProcessRequest(CaplTAggr<SLTransportData*, SLTransportData*, APLAGGR_AUTOKILLREF> &arr_transport_data)
{
	// Надо ли немедленно посылать запрос на СЛ или можно поставить его в очередь ожидания
	bool need_reload = false;
	SLTransportData* sl_data;
	int i;

	LogMessageConditionFC(LogLevelFunctionInfo, _T("Begin"));

	LogStatistic();

	// Защита от многопоточного доступа для простоты выполнена одним куском
	CaplEnterCriticalSection ecs(&m_data_protect);

	// Первый запрос из текущего блока во внутреннем списке
	int offset = m_list_sl_queries.GetSize();

	for(i=0; i< arr_transport_data.GetSize(); i++)
	{
		sl_data = arr_transport_data.GetAt(i);
		if(sl_data == 0)
		{
			m_ErrDescr = APL_T("Неверные входные данные: нулевой указатель!");
			LOG_WARN(m_ErrDescr);
			return false;
		}

		if((sl_data->command == apl_SL_ADD) || (sl_data->command == apl_SL_DELETE))
		{
			// Ставим флаг ошибки, чтобы при неполном выполнении клиенту не ушло APL_NET_OK
			sl_data->aplError = APL_NET_SRV_LIMIT_CONNECT_EXESS;
			need_reload = true;
		}
		m_list_sl_queries.Add(new SLTransportData(*sl_data));
		if(!need_reload)
		{
			if(i==0)
				sl_data->SL_status = m_sl_data.SL_status;

			sl_data->is_use_by_date = m_sl_data.is_use_by_date;
			sl_data->dt = m_sl_data.dt;
			sl_data->key_error = m_sl_data.key_error;
		}
	}

	if(!need_reload)
		need_reload = (m_LastTick.GetTickDelta() > SL_REQUEST_TIME );

	// Надо немедленно запрашивать сервер лицензий
	bool retval = true;
	while(need_reload)
	{
		m_LastTick.Start();

		if(!TestConnect())
			break;

		if(m_SL_ver == SL_VER_UNKNOWN)
		{
			// Проверяем версию сервера лицензий
			retval = ProcessRequestVer();
		}
		if(!retval)
		{
			// Мы умеем работать только с новым сервером лицензий
			m_sl_data.SL_status = ERR_MESSAGE_SL_MISSED_VERSION;
			// При этом флаге при следующем обращении сокет разорвет соединение и попробует заново соединиться - вдруг запустили правильный сервер лицензий.
			m_sl_data.aplError = APL_NET_SRV_MISSED_VERSION_SRV_LIC; 

			m_ErrDescr = APL_T("Сервер лицензий устарел. Работа с этим сервером невозможна. Обновите сервер лицензий.");
			LOG_WARN(m_ErrDescr);
			break;
		}
		retval  = ProcessRequestData();
		break;
	}
	if(need_reload)
	{
		// Пищем код результата всем входным запросам
		for(int i=0; i< arr_transport_data.GetSize(); i++)
		{
			SLTransportData *sl_data = arr_transport_data.GetAt(i); // из запроса
			if(sl_data == 0) continue;
			// Строчку статуса записываем только в первый запрос для экономии места
			if(i==0)
				sl_data->SL_status = m_sl_data.SL_status;

			sl_data->is_use_by_date = m_sl_data.is_use_by_date;
			sl_data->dt = m_sl_data.dt;
			sl_data->key_error = m_sl_data.key_error;

			if(retval)
			{
				// Ищем среди внутренних запросов наши запросы ADD и заносим им индивидуальные коды возврата
				if((sl_data->command == apl_SL_ADD) || (sl_data->command == apl_SL_DELETE))
				{
					SLTransportData *sld_i(0); // из внутреннего списка
					if(i+offset < m_list_sl_queries.GetSize())
						sld_i = m_list_sl_queries.GetAt(i+offset);
					if(!sl_data->Compare(sld_i))
					{
						// по смещению не нашли, придется перебором
						sld_i = 0;
						for(int j=0; j<m_list_sl_queries.GetSize(); j++)
						{
							if(sl_data->Compare(m_list_sl_queries.GetAt(j)))
							{
								sld_i = m_list_sl_queries.GetAt(j);
								offset = j-i;
								break;
							}
						}
					}
					if(sld_i == 0)
					{
						m_ErrDescr = APL_T("Ошибка выполнения: во внутреннем массиве не найден запрашиваемый объект операции с лицензией!");
						LOG_WARN(m_ErrDescr);
						return false;
					}
					sl_data->aplError = sld_i->aplError;
				}
				else
					sl_data->aplError = APL_NET_OK;

			}
			else
			{
				// Пишем всем общий код ошибки
				sl_data->aplError = m_sl_data.aplError;
			}
		}
		// Очищаем внутренний список запросов
		m_list_sl_queries.Clear();
	}
	LogMessageConditionFC(LogLevelFunctionInfo, _T("End"));

	return true;
}


bool CServLicDescription::TestConnect()
{
	CaplStackLogger stack_logger(__APL_FUNC__);

	// защита от многопоточного доступа нужна только как защита "от дурака" и выполнена одним куском
	CaplEnterCriticalSection ecs(&m_data_protect);

	if(m_slSocket.IsConnected())
	{
		if(m_sl_data.aplError == APL_NET_SRV_NOT_CONNECT || m_sl_data.aplError == APL_NET_SRV_MISSED_VERSION_SRV_LIC)
		{
			m_after_halt = true;
			m_slSocket.Disconnect();
			m_sl_data.SL_status = ERR_MESSAGE_SL_NOT_CONNECT;
		}
		else if(m_slSocket.IsHalted())
		{
			m_after_halt = true;
			m_slSocket.Disconnect();
			m_sl_data.SL_status = ERR_MESSAGE_SL_NOT_CONNECT;
			m_sl_data.aplError = APL_NET_SRV_NOT_CONNECT;
		}
		else
		{
			// устанавливаем флаг - выводить сообщения об ошибках
			m_messagin_err = true;
			return true;
		}
	}

	// если пытаться соединиться слишком часто - все зависнет нафиг
	m_SL_ver = SL_VER_UNKNOWN;
	if(m_last_bad_tick!=0)
	{
		DWORD curr_tick = GetTickCount();
		// если не было перехода тика через границу DWORD и прошло менее SL_CONNECT_ATTEMPT_TICK минисекунд - ждем
		if((m_last_bad_tick < curr_tick) && (m_last_bad_tick + SL_CONNECT_ATTEMPT_TICK > curr_tick))
		{
			if(m_old_ver){m_sl_data.aplError = APL_NET_SRV_MISSED_VERSION_SRV_LIC;}
			else{m_sl_data.aplError = APL_NET_SRV_NOT_CONNECT;}
			
			m_ErrDescr = APL_T("Нет соединения с сервером лицензий!");
			return false;
		}
	}
	m_old_ver = false;
#ifndef _UNICODE
//	m_slSocket.m_DenyServerUnicode=false;
#endif
	m_slSocket.SetManageMode(MANAGE_ABSENT);

	if(!m_slSocket.Connect(m_sl_data.SL_ip, m_sl_data.SL_port))
	{
		m_last_bad_tick = GetTickCount();
		if(m_slSocket.GetLastAscError() == ASC_ERROR_MISSED_VERSION)
		{
			m_sl_data.aplError = APL_NET_SRV_MISSED_VERSION_SRV_LIC;
			m_sl_data.SL_status =  APL_T("Сервер лицензий устарел; его необходимо обновить");
			m_ErrDescr = m_sl_data.SL_status;
			LOG_WARN(m_sl_data.SL_status);
			m_old_ver = true;
		}
		else if(m_slSocket.GetLastAscError() == ASC_ALREADY_CONNECTED)
		{
			// все в порядке, соединение уже есть
			m_sl_data.aplError = APL_NET_OK;
			return true;
		}
		else
		{
			m_sl_data.aplError = APL_NET_SRV_NOT_CONNECT;
			if(m_after_halt)
			{
				m_sl_data.SL_status = APL_T("Соединение с сервером лицензий было нарушено. При восстановлении соединения возникла ошибка: ") + m_slSocket.GetDescriptionLastError();
			}
			else if(m_messagin_err)
			{
				m_sl_data.SL_status = APL_T("Ошибка при работе с сервером лицензий: ") + m_slSocket.GetDescriptionLastError();
				m_messagin_err = false;
			}
			m_ErrDescr = m_sl_data.SL_status;
			LOG_WARN(m_sl_data.SL_status);

		}
		return false;
	}
	else if(m_after_halt)
	{
		// LogMessage1(APL_T("Соединение с сервером лицензий успешно восстановлено"));
		m_after_halt = false;
	}
	m_last_bad_tick = 0;
	m_sl_data.aplError = APL_NET_OK;

	return true;
}

// Запрашиваем версию сервера лицензий
bool CServLicDescription::ProcessRequestVer()
{
	int size_arr_data;
	int i;
	CaplDataBufEx dbuf;
	char* header = 0;
	bool error_read = false;


	apl_SL_PS_DATA data_test_in, data_test_out;
	data_test_in.command = apl_SL_GET_VER;
	data_test_in.Set(VER_SL_DATA);
	// для усложнения понимания потока и для дальнейшей обработки на сервере лицензий
	data_test_in.data_1 = rand()+rand()*0xFFFF;
	data_test_in.data_2 = rand()+rand()*0xFFFF;
	data_test_in.data_3 = rand()+rand()*0xFFFF;

	// заполняем буфер
	dbuf.Add2Iterator(std_License_header, strlen(std_License_header)+1);
	size_arr_data = 1;
	dbuf.Add2Iterator(&size_arr_data, 4);
	data_test_in.Add2Iterator(dbuf, SLBufGen_2);

	DWORD size = dbuf.m_Size;
	void *buf = dbuf.m_data;
	DWORD t2;
	CaplTimeMeter tm;

#ifndef _DEBUG
	// если сервер лицензий не ответит за 10 секунд - значит, он уже не ответит
	m_slSocket.SetTimeOut(1000*10);
#endif

	LogMessageConditionFC(LogLevelFunctionInfo, _T("2-z"));

	// Посылаем данные о себе
	error_read = !m_slSocket.Call(&buf, size, 0, 0, false, false);

	LogMessageConditionFC(LogLevelFunctionInfo, _T("3-z"));

	t2 = tm.GetTickDelta();

	m_time_request_all += t2;
	if(t2 > m_time_request_max) m_time_request_max = t2;
	m_num_request++;

	if(!error_read)
	{
		dbuf.SetExternalData(buf, size, size);

		// читаем из буфера заголовок
		i = strlen(std_License_header)+1;
		header = new char[i];
		if(!dbuf.Read(header, i))
			error_read = true;
		else
			error_read = (CStringA(std_License_header) != header);

		delete[] header; header = 0;
	}
	// прочитан правильный заголовок?
	if(!error_read)
		error_read = !dbuf.Read(&size_arr_data); // читаем размер ответного массива

	if(!error_read)
		// в ответ должна прийти одна структура
		error_read = (size_arr_data !=1 || (dbuf.GetCurPos() + sizeof(apl_SL_PS_DATA) > size));
			
	if(!error_read)
		error_read = !data_test_out.ReadFromIterator(dbuf, SLBufGen_2);

	if(!error_read)
	{
		// и читаем из этого массива
		if(data_test_out.command == apl_SL_GET_VER)
		{
			// декодируем номер версии
			m_SL_ver = data_test_out.data_1 ^ data_test_in.data_2 ^ data_test_in.data_3;
			// защита от дурака и от ошибки передачи
			if(m_SL_ver > 100)
				m_SL_ver = SL_VER_OBSOLETE;
		}
	}
	m_slSocket.ClearInternalData();

	if(error_read)
	{
		// ошибка общения с сервером. Ставим флаги "сервер неизвестен" и "надо разорвать tcp и заново соединиться"
		m_SL_ver = SL_VER_UNKNOWN;
		m_sl_data.SL_status = ERR_MESSAGE_SL_MISSED_VERSION;
		m_sl_data.aplError = APL_NET_SRV_MISSED_VERSION_SRV_LIC;

		m_ErrDescr = m_sl_data.SL_status;
		LogMessageConditionFC(LogLevelFunctionInfo, _T("End error"));
		return false;
	}

	return m_SL_ver != SL_VER_OBSOLETE;
}

bool CServLicDescription::ProcessRequestData()
{
	CString tmp;

	// Массив объектов с запросами лицензий, дат и т.п.
	CaplTAggr <apl_SL_PS_DATA*, apl_SL_PS_DATA*, APLAGGR_LIST_OR_AUTOKILLREF> arr_lic_data_in, arr_lic_data_out;
	apl_SL_PS_DATA* p_sl_data;

	int i;
	CaplDataBufEx dbuf;
	char* header = 0;

// 	DWORD res1A = 0, res2A = 0;
// 	DWORD par1A = 0, par2A = 0;
// 	DWORD res1D = 0, res2D = 0, res3D = 0, res4D = 0;
// 	DWORD par1D = 0, par2D = 0, par3D = 0, par4D = 0;

	DWORD use;
	DWORD lic;

	UINT32 my_ip = m_slSocket.GetCompIp();

	// типы лицениий для запроса
	CStringArray types;
	CaplStrMap indexes;

	LogMessageConditionFC(LogLevelFunctionInfo, _T("1"));

	// Количество отозванных ключей/файлов
	int size_rev = sizeof(gl_Revokes)/sizeof(PVOID);

	// Количество запросов на СЛ  равно кол-ву запросов с клиентов + запрос даты действия ключа
	//	+ количество отозванных деленных на 10
	// Запрашиваем по новой схеме, со строковыми типами лицензий

	// Параллельно заполняем буфер
	dbuf.Clear();
	dbuf.Add2Iterator(std_License_header, strlen(std_License_header)+1);
	dbuf.BeginPosSizer(); dbuf.m_Pos_Sizer--; // костылик - m_Pos_Sizer считается от m_Size, а тут надо от m_Iterator.

	// первым всегда идет запрос даты действия ключа
	p_sl_data = new apl_SL_PS_DATA();
	arr_lic_data_in.Add(p_sl_data);
	p_sl_data->command = apl_SL_GET_DATE;
	p_sl_data->server_ip = my_ip;
	p_sl_data->Set(VER_SL_DATA);
	/*res1D = */ p_sl_data->data_1 = rand()+rand()*0xFFFF;
	/*res2D = */ p_sl_data->data_2 = rand()+rand()*0xFFFF;
	/*res3D = */ p_sl_data->data_3 = rand()+rand()*0xFFFF;
	/*res4D = */ p_sl_data->type_license = rand()+rand()*0xFFFF;

	p_sl_data->Add2Iterator(dbuf, SLBufGen_2);

	// Данные для маскировки строк. 4 случайных числа по 4 байта
	const char* mask_data = (const char*)(&p_sl_data->type_license);
	int mask_size = 16;

	int offset = arr_lic_data_in.GetSize();

	// По списку запросов на обработку
	for(i = 0; i<m_list_sl_queries.GetSize(); i++)
	{
		if(m_list_sl_queries.GetAt(i) == 0)
		{
			m_ErrDescr = APL_T("Пустой входной объект!");
			LOG_WARN(m_ErrDescr);
			return false;
		}
		p_sl_data = new apl_SL_PS_DATA();
		arr_lic_data_in.Add(p_sl_data);

		m_list_sl_queries.GetAt(i)->Get(p_sl_data, types, indexes);
		// ставим правильный IP
		p_sl_data->server_ip = my_ip;
		if(p_sl_data->command == apl_SL_ADD)
		{
			/*res1A = par1A = */ p_sl_data->data_1 = rand()+rand()*0xFFFF;
			/*res2A = par2A = */ p_sl_data->data_2 = rand()+rand()*0xFFFF;
		}
		else
		{
			// хоть они и не используются - но для усложнения понимания потока
			p_sl_data->data_1 = rand()+rand()*0xFFFF;
			p_sl_data->data_2 = rand()+rand()*0xFFFF;
		}
		// хоть они и не используются - но для усложнения понимания потока
		p_sl_data->data_3 = rand()+rand()*0xFFFF;
		p_sl_data->Add2Iterator(dbuf, SLBufGen_2);
	}
	m_num_pakets_all += m_list_sl_queries.GetSize();
	if(m_list_sl_queries.GetSize() > (long)m_num_pakets_max)m_num_pakets_max = m_list_sl_queries.GetSize();

	// Записываем отозванные ключи
	if(size_rev > 0)
	{
		int pos_r = 0, pos_s = -1;
		p_sl_data = new apl_SL_PS_DATA();
		arr_lic_data_in.Add(p_sl_data);
		p_sl_data->command = apl_SL_REVOKES;

		for(i = 0; i<size_rev; i++)
		{
			tmp = gl_Revokes[i];
			pos_s = types.Add(tmp);
			if(pos_r==0) p_sl_data->type_license = pos_s;
			else if(pos_r==1) p_sl_data->data_1 = pos_s;
			else if(pos_r==2) p_sl_data->data_2 = pos_s;
			else if(pos_r==3) p_sl_data->data_3 = pos_s;
			else if(pos_r==4) p_sl_data->server_ip = pos_s;
			else if(pos_r==5) p_sl_data->server_port = pos_s;
			else if(pos_r==6) p_sl_data->client_id_m = pos_s;
			else if(pos_r==7) p_sl_data->client_id_s1 = pos_s;
			else if(pos_r==8) p_sl_data->client_id_s2 = pos_s;
			else if(pos_r==9) p_sl_data->client_ip = pos_s;

			pos_r++;
			if(pos_r >=10 && i< size_rev-1)
			{
				pos_r = 0;	
				EnMaskSL_PS_DATA(p_sl_data, mask_data, mask_size);
				p_sl_data->Add2Iterator(dbuf, SLBufGen_2);

				p_sl_data = new apl_SL_PS_DATA();
				arr_lic_data_in.Add(p_sl_data);
				p_sl_data->command = apl_SL_REVOKES;
			}
		}
		// Забиваем остатки случайными значениями
		int types_size = types.GetSize();
		if(pos_r < 10) p_sl_data->client_ip = GetRandNotMin(types_size);
		if(pos_r < 9) p_sl_data->client_id_s2 = GetRandNotMin(types_size);
		if(pos_r < 8) p_sl_data->client_id_s1 = GetRandNotMin(types_size);
		if(pos_r < 7) p_sl_data->client_id_m = GetRandNotMin(types_size);
		if(pos_r < 6) p_sl_data->server_port = GetRandNotMin(types_size);
		if(pos_r < 5) p_sl_data->server_ip = GetRandNotMin(types_size);
		if(pos_r < 4) p_sl_data->data_3 = GetRandNotMin(types_size);
		if(pos_r < 3) p_sl_data->data_2 = GetRandNotMin(types_size);
		if(pos_r < 2) p_sl_data->data_1 = GetRandNotMin(types_size);

		EnMaskSL_PS_DATA(p_sl_data, mask_data, mask_size);
		p_sl_data->Add2Iterator(dbuf, SLBufGen_2);
	}

	dbuf.EndSiezer(arr_lic_data_in.GetSize());

	// Добавляем в буфер строчки с запрашиваемыми типами лицензий в utf8
	int pos_start = dbuf.GetCurPos();
	int size_arr_data = types.GetCount();
	dbuf.Add2Iterator(&size_arr_data, 4);
	for(i=0; i<types.GetCount(); i++)
	{
		dbuf.Utf8StringAdd2Iterator(types.GetAt(i));
	}
	// маскируем строчки
	dbuf.EnMaskTail(pos_start, mask_data, mask_size);

	DWORD size = dbuf.m_Size;
	void *buf = dbuf.m_data;
	DWORD t2;
	CaplTimeMeter tm;

	// если сервер лицензий не ответит за 10 секунд - значит, он уже не ответит
#ifdef _DEBUG
	m_slSocket.SetTimeOut(1000*120); // для отладки подождем подольше
#else
	m_slSocket.SetTimeOut(1000*10);
#endif

	// Посылаем буфер с запросами
	bool is_error = !m_slSocket.Call(&buf, size, 0, 0, false, false);

	t2 = tm.GetTickDelta();

	m_time_request_all += t2;
	if(t2 > m_time_request_max) m_time_request_max = t2;
	m_num_request++;

	if(is_error)
	{
		// ошибка общения с сервером. Ставим флаги "сервер неизвестен" и "надо разорвать tcp и заново соединиться"
		m_sl_data.SL_status = ERR_MESSAGE_SL_NOT_CONNECT;
		m_sl_data.aplError = APL_NET_SRV_NOT_CONNECT;

		m_ErrDescr = APL_T("Ошибка выполнения запроса к серверу лицензий!");
		LOG_WARN(m_ErrDescr);

		LogMessageConditionFC(LogLevelFunctionInfo, _T("End error"));
		m_slSocket.ClearInternalData();
		return false;
	}

	// На случай если проверки не пройдут
	// (новые приложения работают только с новым сервером лицензий)
	m_sl_data.SL_status = ERR_MESSAGE_SL_MISSED_VERSION;
	m_sl_data.aplError = APL_NET_SRV_MISSED_VERSION_SRV_LIC;
	
	dbuf.SetExternalData(buf, size, size);

	// читаем из буфера заголовок
	i = strlen(std_License_header)+1;
	header = new char[i];
	if(dbuf.Read(header, i))
	{
		if(CStringA(std_License_header_001) == header)
		{
			// При этом флаге при следующем обращении сокет разорвет соединение и попробует заново соединиться - вдруг запустили правильный сервер лицензий.
			m_sl_data.aplError = APL_NET_SRV_MISSED_VERSION_SRV_LIC; 
		}
		else if((CStringA(std_License_header) == header))
			m_sl_data.aplError = APL_NET_OK;
	}
	delete[] header; header = 0;
	mask_data = 0;

	if(m_sl_data.aplError != APL_NET_OK)
	{
		LogMessageConditionFC(LogLevelFunctionInfo, _T("End obsolete"));
		m_slSocket.ClearInternalData();

		m_ErrDescr = APL_T("Невозможно работать с устаревшей версией сервера лицензий!");
		LOG_WARN(m_ErrDescr);

		return false;
	}

	// читаем размер ответного массива
	// если прочитано нормально - проверяем, что размер буфера вместит такой массив структур
	if(dbuf.Read(&size_arr_data) && size_arr_data > 0 && (dbuf.GetCurPos() + size_arr_data*sizeof(apl_SL_PS_DATA) <= size))
	{
		m_sl_data.aplError = APL_NET_OK;
		// Коллекция для строк с занятыми лицензиями 
		std::map<int, CString> map_int2Lic;
		//Максимальный номер типа лицензии
		DWORD size_types = 0;

		// и читаем из этого массива
		for(i = 0; i<size_arr_data; i++)
		{
			p_sl_data = new apl_SL_PS_DATA();
			arr_lic_data_out.Add(p_sl_data);
			if(!p_sl_data->ReadFromIterator(dbuf, SLBufGen_2))
			{
				m_ErrDescr = APL_T("Ошибка при чтении объектов ответа из буфера!");
				LOG_WARN(m_ErrDescr);
				m_slSocket.ClearInternalData();

				return false;
			}
			if(mask_data == 0)
				mask_data = (const char*)(&p_sl_data->type_license);
			mask_size = 16;
			
			if(p_sl_data->command == apl_SL_ADD)
			{
				if(i >= arr_lic_data_in.GetSize() || !p_sl_data->Compare(arr_lic_data_in[i]))
				{
					m_ErrDescr = APL_T("Не совпадают объекты запроса на СЛ и его ответа (add)!");
					LOG_WARN(m_ErrDescr);
					m_slSocket.ClearInternalData();
					return false;
				}
				SLTransportData* sld = m_list_sl_queries.GetAt(i-offset);
				apl_SL_PS_DATA* p_sl_data_in = arr_lic_data_in[i];
				if(sld!=0)
				{
					DWORD dd = p_sl_data_in->data_1 - p_sl_data_in->data_2; // res1A-res2A;
					//if(arr_data[i].data_1 == (res1A&dd) && arr_data[i].data_2 == (res2A&dd))
					if(p_sl_data->data_1 == (p_sl_data_in->data_1&dd) && p_sl_data->data_2 == (p_sl_data_in->data_2&dd))
						sld->aplError = APL_NET_OK;
					else
						sld->aplError = APL_NET_SRV_LIMIT_CONNECT_EXESS;
				}

			}
			else if(p_sl_data->command == apl_SL_DELETE)
			{
				apl_SL_PS_DATA* p_sl_data_in = arr_lic_data_in[i];
				if(!p_sl_data->Compare(p_sl_data_in))
				{
					m_ErrDescr = APL_T("Не совпадают объекты запроса на СЛ и его ответа (del)!");
					LOG_WARN(m_ErrDescr);
					m_slSocket.ClearInternalData();
					return false;
				}
				SLTransportData* sld = m_list_sl_queries.GetAt(i-offset);

				if(p_sl_data->data_1 == 1 && p_sl_data->data_2 == 1 && p_sl_data->data_3 ==1)
					sld->aplError = APL_NET_OK;
				else
					sld->aplError = APL_NET_SRV_ERROR_PROCESSED;
		
			}
			else if(p_sl_data->command == apl_SL_GET_DATE)
			{
				if(i >= arr_lic_data_in.GetSize() || !p_sl_data->Compare(arr_lic_data_in[i]))
				{
					m_ErrDescr = APL_T("Не совпадают объекты запроса на СЛ и его ответа (date)!");
					LOG_WARN(m_ErrDescr);
					m_slSocket.ClearInternalData();
					return false;
				}
				apl_SL_PS_DATA* p_sl_data_in = arr_lic_data_in[i];
				DATE dt;
				// проверяем.
				DWORD par1D = p_sl_data_in->data_1^p_sl_data_in->data_2; // res1D^res2D;
				DWORD par2D = p_sl_data_in->data_2^p_sl_data_in->data_3; // res2D^res3D;
				DWORD par3D;// = p_sl_data_in->data_3^p_sl_data_in->type_license; // res3D^res4D;
				DWORD par4D;// = p_sl_data_in->type_license^p_sl_data_in->data_1; // res4D^res1D;

				// if(par1D == arr_data[i].data_1 && par2D == arr_data[i].data_2)
				if(par1D == p_sl_data->data_1 && par2D == p_sl_data->data_2)
				{
					// ограничений по дате у ключа нет
					m_sl_data.is_use_by_date = 0;
				}
				else
				{
					// ограничения по дате есть. Теперь вопрос - просрочен ли ключ?
					m_sl_data.is_use_by_date = 1;
					par1D = p_sl_data->data_1^ p_sl_data_in->data_1; // arr_data[i].data_1^res1D;
					par2D = p_sl_data->data_2^ p_sl_data_in->data_2; // arr_data[i].data_2^res2D;
					par3D = p_sl_data->data_3^ p_sl_data_in->data_3; // arr_data[i].data_3^res3D;
					par4D = p_sl_data->type_license^ p_sl_data_in->type_license;// arr_data[i].type_license^res4D;
					if(par1D == par2D && par3D == par4D)
					{
						//просрочен
						m_sl_data.key_error = 1;
						memcpy(&dt,&par1D, 4);
						memcpy((BYTE*)(&dt)+4,&par3D, 4);
						m_sl_data.dt = dt;
						m_sl_data.aplError = APL_NET_SRV_EXPIRE_KEY_IN_SRV_LIC_BREAK; //
						break;
					}
					else if(par1D == par3D && par2D == par4D)
					{
						// пока еще можно работать
						m_sl_data.key_error = 0;
						memcpy(&dt,&par1D, 4);
						memcpy((BYTE*)(&dt)+4,&par2D, 4);
						m_sl_data.dt = dt;
					}
				}
			}
			else if(p_sl_data->command == apl_SL_GET_LIC)
			{
				if(p_sl_data->data_1 == 0xFFFF0000 || p_sl_data->data_1 > MAX_NUM_TYPES_LIC ||
					p_sl_data->type_license > MAX_NUM_TYPES_LIC)
				{
					// Сервер лицензий не смог прочитать ни ключ, ни файлы
					// Или явно некорректный индекс типа лицензии
					m_sl_data.key_error = 1;
					m_sl_data.aplError = APL_NET_SRV_MISSED_KEY_IN_SRV_LIC;
					// При такой ошибке запрос "выдай мне лицензию" всегда вернет ошибку
					break;
				}
				else 
				{
					size_types = p_sl_data->data_1;
					use = p_sl_data->data_2;
					lic = p_sl_data->data_3;
					tmp.Format(_T(" %i (%i)"), use, lic);
					map_int2Lic[p_sl_data->type_license] = tmp;
				}
			}
		}
		if(m_sl_data.aplError != APL_NET_OK)
		{
			m_sl_data.SL_status = APL_T("Ошибка чтения ключей.");
			m_ErrDescr = m_sl_data.SL_status;
			LOG_WARN(m_ErrDescr);
			m_slSocket.ClearInternalData();
			LogMessageConditionFC(LogLevelFunctionInfo, _T("End: key error"));
			return false;
		}

		// демаскируем строчки
		dbuf.EnMaskTail(dbuf.GetCurPos(), mask_data, mask_size);

		CString str;
		m_sl_data.SL_status.Empty();
		dbuf.Read(&size, 4);
		if(size < size_types) LOG_WARN_C(APL_T("Некорректный блок данных с сервера лицензий. В блоке данных присутствует %i типов лицензий, и %i строк с типами."), size_types, size);
		if(size > MAX_NUM_TYPES_LIC)
		{
			LOG_WARN_C(APL_T("Некорректный блок данных с сервера лицензий. В блоке указано некорректное количество строк с типами %i."), size);
			m_sl_data.aplError = APL_NET_SRV_NOT_CONNECT;
			m_sl_data.SL_status = APL_T("Некорректный блок данных с сервера лицензий.");
			m_ErrDescr = m_sl_data.SL_status;
			m_slSocket.ClearInternalData();
			LogMessageConditionFC(LogLevelFunctionInfo, _T("End: data from SL error"));
			return false;
		}
		else
		{
			CaplStrStrMap map;
			for(int i=0; i<(int)size; i++)
			{
				// читаем названия типов лицензий
				if(!dbuf.Utf8StringReadFromIterator(str))
				{	
					str.Format(_T("unknown %i"), i);			
					map.Add(str, map_int2Lic[i]);
					break;
				}
				else
					map.Add(str, map_int2Lic[i]);
			}
			// В строке типы будут отсортированы.
			for(int i=0; i<(int)map.GetSize(); i++)
			{
				if(i > 0)m_sl_data.SL_status += _T(", ");
				if(map.GetAt(i)->str == SPEC_LIC_TYPE)
					continue; // m_sl_data.SL_status += CString(SPEC_LIC_MESS) + _T(": ") + map.GetAt(i)->val; 
				else
					m_sl_data.SL_status += map.GetAt(i)->str + _T(": ") + map.GetAt(i)->val; 
			}

		}
	}
	else
	{
		m_sl_data.aplError = APL_NET_SRV_NOT_CONNECT;
		m_sl_data.SL_status = APL_T("Некорректный размер блока данных с сервера лицензий.");
		m_ErrDescr = m_sl_data.SL_status;
		m_slSocket.ClearInternalData();
		LogMessageConditionFC(LogLevelFunctionInfo, _T("End: data from SL error"));
		return false;
	}
	LogMessageConditionFC(LogLevelFunctionInfo, _T("End"));
	m_slSocket.ClearInternalData();
	return true;
}

