#if !defined(AFX_APLSOCKETTRANSPORT_H__0867BCA7_426B_401B_BB45_CB4DF4675759__INCLUDED_)
#define AFX_APLSOCKETTRANSPORT_H__0867BCA7_426B_401B_BB45_CB4DF4675759__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <afxsock.h>		// MFC socket extensions
#include <StepData.h>

#ifndef _WININET_
# ifndef _WINDOWS_
#  include <windows.h>
# endif
# ifndef INTERNET_DEFAULT_PORT
#  include <wininet.h>
# endif
#endif

#define POINTER_FORMAT  _T("0x%08X")
#define THREAD_FORMAT   _T("t0x%04X")
#define LOGSERV_THREAD_FORMAT _T("s%05i:") THREAD_FORMAT _T(" ")
#define LOGSERV_FORMAT _T("s%05i ")

// Неправильный разделитель путей из другой ОС
#ifdef __linux__
	#define aplMissedDirRazd _T('\\')
#else
	#define aplMissedDirRazd _T('/')
#endif

typedef void (__cdecl *_PostProcessing)(LPVOID);

#ifndef _UNICODE
	// #define OUTPUT_MY_MESS
#endif // #ifndef _UNICODE

#ifdef _DEBUG
	#ifdef OUTPUT_MY_MESS

		#undef OutputDebugStringWithSrs
		#undef EnterCriticalSectionODS
		#undef LeaveCriticalSectionODS
		#undef DeleteCriticalSectionODS

		#ifdef this
			#define OutputDebugStringWithSrs( MESSAGE, ...) {CString dmd;SYSTEMTIME SystemTime;GetLocalTime(&SystemTime); dmd.Format( _T(" %2i:%02i:%02i.%03i " ## MESSAGE ## " Thread: " THREAD_FORMAT ", this: " POINTER_FORMAT ", Src: %s от %s str %i\n"), SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond,SystemTime.wMilliseconds, ##__VA_ARGS__, GetCurrentThreadId(), this, _T(__FILE__), _T(__DATE__), __LINE__); OutputDebugString(dmd);}
		#else
			#define OutputDebugStringWithSrs( MESSAGE, ...) {CString dmd;SYSTEMTIME SystemTime;GetLocalTime(&SystemTime); dmd.Format( _T(" %2i:%02i:%02i.%03i ") ## MESSAGE ## _T(" Thread: ") THREAD_FORMAT _T(", Src: %s от %s str %i\n"), SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond,SystemTime.wMilliseconds, ##__VA_ARGS__, GetCurrentThreadId(), _T(__FILE__), _T(__DATE__), __LINE__); OutputDebugString(dmd);}
		#endif

		#define OutputDebugStringWithSrs2( MESSAGE, ... ) {CString dmd;SYSTEMTIME SystemTime;GetLocalTime(&SystemTime); dmd.Format( _T(" %2i:%02i:%02i.%03i ") ## MESSAGE ## _T(" Thread: ") THREAD_FORMAT _T(", this: ") POINTER_FORMAT _T(", Src: %s от %s str %i\n"), SystemTime.wHour,SystemTime.wMinute,SystemTime.wSecond,SystemTime.wMilliseconds, ##__VA_ARGS__, GetCurrentThreadId(), this, file, date, line); OutputDebugString(dmd);}

		#define EnterCriticalSectionODS( param ) EnterCriticalSection( param ); OutputDebugStringWithSrs(_T("EnterCriticalSection" # param ))
		#define LeaveCriticalSectionODS( param ) OutputDebugStringWithSrs(_T("LeaveCriticalSection" # param ) ); LeaveCriticalSection( param )
		#define DeleteCriticalSectionODS( param ) OutputDebugStringWithSrs(_T("DeleteCriticalSection" # param ) );  DeleteCriticalSection( param )

	#else //#ifdef OUTPUT_MY_MESS

		#define OutputDebugStringWithSrs( MESSAGE, ... ) 
		#define OutputDebugStringWithSrs2( MESSAGE, ... )

		#define EnterCriticalSectionODS( param ) EnterCriticalSection( param )
		#define LeaveCriticalSectionODS( param ) LeaveCriticalSection( param )
		#define DeleteCriticalSectionODS( param )  DeleteCriticalSection( param )

	#endif //#ifdef OUTPUT_MY_MESS
#else // #ifdef _DEBUG

	#define OutputDebugStringWithSrs( MESSAGE, ... ) 
	#define OutputDebugStringWithSrs2( MESSAGE, ... ) 

	#define EnterCriticalSectionODS( param ) EnterCriticalSection( param )
	#define LeaveCriticalSectionODS( param ) LeaveCriticalSection( param )
	#define DeleteCriticalSectionODS( param )  DeleteCriticalSection( param )

#endif // #ifdef _DEBUG


// aplSocketTransport.h : header file
//
#ifdef SOCKET_TRANSPORT_EXPORTS
#define SOCKET_TRANSPORT_API __declspec(dllexport)
#else
#define SOCKET_TRANSPORT_API __declspec(dllimport)
#endif

#ifndef APL_SOCKETTRANSPORT_NOAUTOLIB
#ifdef _DEBUG
#ifdef UNICODE
#pragma comment (lib,"aplSocketTransport_ud.lib")
#else
#pragma comment (lib,"aplSocketTransportD.lib")
#endif
#else
#ifdef UNICODE
#pragma comment (lib,"aplSocketTransport_u.lib")
#else
#pragma comment (lib,"aplSocketTransport.lib")
#endif
#endif
#endif

// Служебные странички. Начинаются или заканчиваются уникальной строкой
#define URI_SERVICE_ROOT "xservice"

// таймер прокачки данных через заблокированный сокет
#define ID_TIMER_BLOCK  1002
#define ID_TIMER_RENEW  1003
#define ID_TIMER_PROCESS_CLIENT_OVER_HTTP  1005

#define ID_TIMER_START_CLIENT_ID  1015

#define WWW_PORTION_SIZE 1024*1024

#define TIMEOUT_RECEIVE 15000
//#define TIMEOUT_WAIT_NODIALOG 600000
#define TIMEOUT_WAIT_NODIALOG 1000*60*60*24
#define TIMEOUT_BEFORE_WAITDIALOG 2000
#define TIMEOUT_LIFE_NOACTIVE_HTTP 15*60*1000

// Ошибки уровня apl socket transport
enum ASC_ERROR_CODES{
	ASC_NO_ERROR				=0,
	ASC_ERROR_PROCESSED			=1,
	ASC_MISSED_PARAM			=2,	   
	ASC_TIMEOUT					=3,
	ASC_MISSED_RECEIVE_SIZE		=4,
	ASC_MISSED_SENT_SIZE		=5,
	ASC_BREAK_CALL				=6,
	ASC_ALREADY_CONNECTED		=7,
	ASC_TOO_MANY_CLIENTS_ON_SERVER	=8,
	ASC_ERROR_INIT_SERVER		=9,
	ASC_ERROR_IN_SOCKET			=10,
	ASC_ERROR_SOCKET_CLOSE		=11,
	ASC_ERROR_IN_CONNECT		=12,
	ASC_ERROR_MISSED_VERSION	=13,
	ASC_OPERATION_IN_PROGRESS	=14,
	ASC_ERROR_PROCESSED_ON_SERVER	=15,
	ASC_MISSED_RECEIVE_SIZE_ON_SERVER =16,
	ASC_HOST_INACCESSIBLE		=17,
	ASC_MISSED_SERVER			=18,
	ASC_ERROR_IN_WININET		=19,
	ASC_ERROR_IN_HTTP_REQ		=20,
	ASC_ERROR_SERVER_UNICODE	=21,
	ASC_UNKNOWN_PROTOCOL		=22,
	ASC_DNS_ERROR				=23,
	ASC_SERVER_STOPPED			=24
};

// код ошибки при пересылке исключения по памяти наверх по стеку
#define SAD_MEMORY_EXEPTION 9999

#define ADD_PVAR_2_STR(pVar) \
	if( pVar !=0){ \
		str1.Format(_T(" ") _T(#pVar) _T(" %i;"), \
			*pVar); str+=str1;}

// поимка исключения по памяти и пересылка наверх. ВАЖНО! Функция с этим кодом ДОЛЖНА быть вызвана только из функций сокета OnTimer или Processed !!!
#define RETRANSLATE_CMEMORY_EXEPTION catch(CMemoryException* e){\
			TCHAR   szCause[255];e->GetErrorMessage(szCause,255);\
			throw(SaplErrorDescription(SAD_MEMORY_EXEPTION,_T(__FILE__),_T(__DATE__),__LINE__,szCause,SaplErrorDescriptionLevel_Server,__APL_FUNC__,m_class_info));}

#define APL_CATCH_ALL_EXEPTION( ext_code_prev , LOG_FUNCTION_NAME, ext_code_post ) \
	catch(CMemoryException* e){ \
		TCHAR   szCause[255];  szCause[0]=_T('\0'); e->GetErrorMessage(szCause,255);CString str,str1;str.Format(APL_T("CMemoryException!!! Srs %s от %s str %i "),_T(__FILE__),_T(__DATE__),__LINE__);str+=szCause; \
		aplGetDescriptionSystemError(GetLastError(),str1);str+=APL_T("\nПоследняя системная ошибка: ")+str1; \
		ext_code_prev \
		LOG_FUNCTION_NAME (str); \
		ext_code_post \
	}catch (CException* e){ \
		TCHAR   szCause[255]; szCause[0]=_T('\0'); e->GetErrorMessage(szCause,255);CString str,str1;str.Format(APL_T("CException!!! Srs %s от %s str %i "),_T(__FILE__),_T(__DATE__),__LINE__);str+=szCause; \
		aplGetDescriptionSystemError(GetLastError(),str1);str+=APL_T("\nПоследняя системная ошибка: ")+str1; \
		ext_code_prev \
		LOG_FUNCTION_NAME (str); \
		ext_code_post \
	}catch(...){ \
		CString str,str1;str.Format(APL_T(" Unknown Exception!!! Srs %s от %s str %i "),_T(__FILE__),_T(__DATE__),__LINE__); \
		aplGetDescriptionSystemError(GetLastError(),str1);str+=APL_T("\nПоследняя системная ошибка: ")+str1; \
		ext_code_prev \
		LOG_FUNCTION_NAME (str); \
		ext_code_post \
	}



// Режимы работы клиентского сокета при ожиджании ответа сервера.
// Не показывает ничего, тихо варится внутри цикла ожидания
#define MANAGE_ABSENT 0
//#define MANAGE_TIMEOUT 1
// через полсекунды ожидания показывает диалог с бегущими бочками. В диалоге обрабатываются таймеры!
#define MANAGE_WAITDLG 2

// определяет служебные команды транспорта
// !!! При изменении необходимо синхронизировать виндовую и линуксовую часть !
enum aplTransportCommands{
	CMDGetServerStatus	=	0,	// K->S сразу после установки канала связи клиент запрашивает у сервера статус. В ответ должна придти CMDConnectionOKNew
	CMDConnectionOK		=	1,	// S->K соединение установлено успешно. Вторым байтом идет указатель на класс сервера
	CMDGetVersionBuffer	=	3,	// К->S Ожидается буфер с идентификацией версии
	CMDTooManyClients	=	5,	// S->K сервер приложений уже имеет максимум подключений
	CMDErrorServerProcessed	=10,	// S->K ошибка на сервере при обработке данных
	CMDErrorReceiveData	=11,		// S->K ошибка при приеме данных
	CMDErrorInitServer		=12,	// S->K ошибка инициализации сервера
	CMDErrorMissedSocketType		=13,		// S->K неверный тип сокета (для слушания или для данных)
	CMDErrorMissedData		=14,	// S->K неверные входные данные
	CMDDisconnect		=15,		// К->S разрываем связь
	CMDBreakOperation		=20,		// К->S надо прервать операцию нити, указатель на которую идет в следующих 4 байтах
	CMDSetProgress		=21,		// S->K в следующих 4 байтах идет процент выполненной сервером работы
	CMDDeadTime			=25,		// S->K сервер ожидает выключения; в следующих 4 байтах идет ожидаемое время выключения (в секундах)
	CMDRenewClientState		=26,		// S->K сервер запрашивает, жив ли клиент
	CMDBreakOk				=27,		// S->K сервер запрашивает, жив ли клиент
	CMDSetProgressText		=28,		// S->K в следующих 4 байтах идет длинна строчки, потом строчка текста
	CMDNeedReconnect		=40,		// S->K команда клиенту переконнектиться, так как текущий рабочий процесс сервера должен исчерпал время и ресурсы и должен умереть
	CMDNeedRelogin			=41,		// S->K (Qt) команда клиенту перелогиниться, так как время бездействия превысило установленный лимит
	CMDConnectionOKNew		=	101,	// S->K соединение установлено успешно (новый вариант). Вторым байтом идет номер версии, третьим байтом - указатель на класс сервера
	CMDCriticalError		=	102,	// S->K критическая ошибка (неизвестное исключение или исключение по памяти), дошедшее до функции prossed транспорта. Вторым байтом идет номер ошибки APLAPIERR_... из StepData.h
};

//
// Уровень детализации информации для лога. Указывает, на каком уровне будет выводиться то или иное сообщение
//
typedef enum {
	LL_undefine = -1, // не выводить ничего
	LL_error = 0, // выводить только ошибки
	LL_warning = 1, // + предупреждения
	LL_info = 2, // + общая информацию
	LL_debug = 3, // + отладочная информация
} aplLogLevel;

// Старые названия уровней - чтобы не переписывать все сообщения с их упоминанием
#define LogLevelOnlyCriticalError	LL_error
#define LogLevelAllError			LL_warning
#define LogLevelFunctionInfo		LL_info
#define LogLevelAllMessage			LL_debug


// имя файла лога по умолчанию
#define DEF_ERROR_LOG_FILE _T("errors.log")

// на номер версии отводится только один байт, так что важно не перестараться
// заголовок буфера VERSION_INFO идет в сокет в однобайтном виде! Макрос _T тут не нужен!
// !!! При изменении необходимо синхронизировать виндовую и линуксовую часть !
#define VERSION_INFO "Apl SocketTransport v1.06"
#define VERSION_INFO_U "Apl SocketTransport u1.06"
#define VERSION_NUMBER 106
// !!! При изменении необходимо синхронизировать виндовую и линуксовую часть !
// старое название VERSION_PSS_OVER_HTTP: #define VERSION_NUMBER_HTTP 100
#define VERSION_PSS_OVER_HTTP 100

/////////////////////////////////////////////////////////////////////////////
// Для частных объектов синхронизации серверов 
// имена объектов синхронизации начинаются с
#define GLOBAL_SERVER_ID_FORMAT _T("Global\\PSS_SERV_0x%04X_")
#define LOCAL_SERVER_ID_FORMAT _T("Local\\PSS_SERV_0x%04X_")

// имена объектов синхронизации аварийного лога
#define EMERGENCY_LOG_MUTEX _T("ins_log_mutex")
#define EMERGENCY_LOG_MAIN_SERV_MUTEX _T("ins_log_main_serv_mutex")
#define EMERGENCY_LOG_SHARED_MEMORY_NAME _T("PssServ4Memory4InsLog")
// Размер расшаренного буфера для аварийного лога
#define EMERGENCY_LOG_BUFFER_SIZE 4000000

//#define ERR_MESS_SERVER_A_CLIENT_U APL_T("Клиент для unicode не может работать с сервером под однобайтную кодировку. Используйте сервер под Unicode.")
#define ERR_MESS_SERVER_A_CLIENT_U_DEBUG APL_T("Клиент unicode, сервер ansi. При сохранении возможны потери данных.")

#define ERR_MESS_SERVER_U_CLIENT_A APL_T("Вы пытаетесь соединиться ansi-клиентом с unicode сервером. С unicode сервером может работать только unicode клиент.")
#define ERR_MESS_SERVER_U_CLIENT_A_DEBUG APL_T("Вы пытаетесь соединиться ansi-клиентом с unicode сервером. При чтении возможны потери данных.")

///////////////////////////////////////////////////////////////////
// заголовки и структуры для работы с клиентами поверх http и для rest клиентов
// !!! При изменении необходимо синхронизировать виндовую и линуксовую часть !
//
#define HTTP_NAME_REST_URI _T("rest")
#define HTTP_NAME_APPLICATION_BASE _T("PSS_CLIENT_OVER_HTTP")
#define HTTP_NAME_APPLICATION_VER _T(" 0.1")
#define HTTP_CLIENT_ID_HEADER _T("PSS_Client_Id")
#define HTTP_USER_AGENT _T("User-Agent")
#define HTTP_CONTENT_LENGTH _T("Content-length")
#define HTTP_TRANSFER_ENCODING _T("Transfer-Encoding")
#define HTTP_CLIENT_SESSION_KEY _T("X-Apl-SessionKey")
#define HTTP_SPEC_REQUEST_INDEX _T("SPEC_REQUEST_INDEX")
#define HTTP_SPEC_REQUEST_DATA_SIZE _T("SPEC_REQUEST_DATA_SIZE")
#define HTTP_SPEC_ORIGINAL_DATA_SIZE _T("SPEC_ORIGINAL_DATA_SIZE")
#define HTTP_SPEC_ANSWER_INDEX _T("SPEC_ANSWER_INDEX")
#define HTTP_SPEC_ANSWER_DATASIZE _T("SPEC_ANSWER_DATASIZE")
#define HTTP_SPEC_ANSWER_CRC_ORIGINAL _T("SPEC_ANSWER_CRC_ORIGINAL")
#define HTTP_SPEC_ANSWER_CRC_ENCODED _T("SPEC_ANSWER_CRC_ENCODED")

// Какой тип клиента присоединен к текущему сокет-серверу
enum EClientType
{
	ECT_Unknown = -1,		// - неизвестный тип клиента.
	ECT_Standart = 0,		// - стандартный в постоянном режиме
	ECT_ClientOverHttp = 1,	// - стандарный поверх http
	ECT_Rest = 2,			// - rest клиент, при разрыве начального сокета ждет следующших подключений
	ECT_WwwMail = 3,			// - обычный www, при разрыве сокета удаляет контекст сеанса, если он есть.
};

inline CString EClientType2Str(EClientType type)
{
	CString ret = _T("Unknown");
	switch(type)
	{
	case ECT_Unknown: ret = _T("ECT_Unknown");break;
	case ECT_Standart: ret = _T("ECT_Standart");break;
	case ECT_ClientOverHttp: ret = _T("ECT_ClientOverHttp");break;
	case ECT_Rest: ret = _T("ECT_Rest");break;
	case ECT_WwwMail: ret = _T("ECT_WwwMail");break;
	}
	return ret;
}


// элемент URI для rest запроса
struct SOCKET_TRANSPORT_API SUriElement
{
	SUriElement(){m_base=_T("");m_source=_T("");}
	SUriElement(SUriElement* prev){Set(prev);}
	void Set(SUriElement* prev);
	void Clear();

	CString m_base;
	CString m_source;
	//	CStringArray m_vals;
	CaplStrStrMap m_params;
};

// надор элементов URI для rest запроса
struct SOCKET_TRANSPORT_API SUriData
{
	SUriData(){Clear();}
	void Clear(){m_http_ver = _T(""); m_type = _T(""); m_uri_path = _T(""); m_uri_full = _T(""); m_elements.Clear(); m_num_base_elements = -1; m_is_xservice=false;}

	int GetSize(){return m_elements.GetSize();}

	inline CString GetStringAt(int index) const
	{if(index<0) return CString(_T("")); if(index>=m_elements.Size) return CString(_T(""));return m_elements.Data[index]->m_source;}

	inline CString GetBaseStringAt(int index) const
	{if(index<0) return CString(_T("")); if(index>=m_elements.Size) return CString(_T(""));return m_elements.Data[index]->m_base;}

	CString m_http_ver; // версия http
	CString m_type; // тип HTTP запроса

	// Элементы пути файла вместе с параметрами после ? разбитые по "папкам"
	CaplTAggr<SUriElement*, SUriElement*, APLAGGR_LIST|APLAGGR_AUTOKILLREF> m_elements;

	// Количество элементов до символа '?'. Если символа '?' не было, то -1
	int m_num_base_elements;
	// Адрес файла или ресурса (до символа '?')
	CString m_uri_path;
	// Адрес файла или ресурса c параметрами (с символом '?' и всем что после)
	CString m_uri_full;

	// Параметры (после символа '?')
	// CString m_uri_params; - устарело. Для разделения параметров до '?' и после используется m_num_base_elements
	bool m_is_xservice; // запрос веб интерфейса
};

//
// конец: заголовки и структуры для работы с клиентами поверх http и для rest клиентов
///////////////////////////////////////////////////////////////////


class CaplTimeMeter;

// Класс стандартного лога
class SOCKET_TRANSPORT_API CaplLog  
{
public:
	CaplLog();
	virtual ~CaplLog();
	bool SetFileName(const CString &LogFileName, const CString &first_info = _T(""), bool is_single_log=true, void (__cdecl *prevGetAddDescription)(CString &descr)=0);
	bool AddFirstInfo(const CString &first_info);
	// Сообщения в файл лога
	virtual bool LogMessage(LPCTSTR ErrDescr, bool is_warning=true, bool is_time=true, bool is_write=true, bool is_line=false,
					LPCTSTR funct=0, LPCTSTR class_info=0, LPCTSTR ExtDescr=0,
					LPCTSTR srs_file=0, LPCTSTR srs_date=0, DWORD srs_line=0);

	bool SetCVSHeader(LPCTSTR header);
	bool LogCSV(CStringArray &data, bool is_date_time=true, bool is_proc_thread=false);

	static CString GetLogLevelName(aplLogLevel level);	

	static void DeleteOldestFiles(LPCTSTR fileMask, size_t numSaved);

	// если стоит флаг "вывести в лог позже" - сюда складываются сообщения
	CString m_stored_info;
	// Информация для записи в каждый сменяемый лог  - время начала работы, основная нить, название приложения  и т.п.
	CString m_const_info;
	
	// Уровень количества информации в лог
	aplLogLevel m_LogInfoLevel;
	
	bool m_IsPrintThread;
	bool m_isLogTick;
	// если true - при запуске процесса записывать информацию о сервере в лог сразу. 
	// если false - то только при появлении первого сообщения
	bool m_isAlwaysLogStart;
	// при смене лога вставлять в имя дату смены
	bool m_isChangeLogWithDate;

	// если не равно -1 - то при записи в лог в течение 2 часов после наступления указанного времени - старый лог будет переименован, 
	// а запись будет вестить в новый лог
	int m_TimeForChangeLog;

	DWORD m_MaxLogSize;
	DWORD m_NumCopies;
	
	// имя файла лога
	CString m_LogFileName;

	void ClearFirst();

	static CString CorrectLinefeed(LPCTSTR message);

	// защита доступа к структурам лога
	CRITICAL_SECTION m_HandlesProtect;

	// аварийный лог - переменные
	HANDLE m_hFileEmergencyLogMapping;
	PVOID m_EmergencyLogPregion;
	HANDLE m_Emergency_log_mutex;

	// указатель на первые 4 байта буфера - в которых лежит смещение на последнюю запись
	long *m_Emergency_log_last_active;
	// указатель на начало данных
	BYTE *m_Emergency_log_data;
	DWORD m_Emergency_log_size;

	CString m_class_info;

	// аварийный лог - инициализация переменных
	bool InitEmergencyLog(bool global);
	// аварийный лог - деинициализация переменных
	void ClearEmergencyHandles();
	// записать строчку в аварийный лог
	bool LogMess2EmergencyLog(LPCTSTR Mess);
	// разобрать аварийный лог по строчкам
	void ParseEmergencyLog(PVOID EmergencyLogPregion, DWORD Emergency_log_size, CStringArray &records);
	// Вывести в собственный лог сервера его собсвенный аварийный лог
	void PrintMyEmergencyLog(CStringArray* records = 0, LPCTSTR description=0);


protected:
	
	bool m_need_change_log4time;
	bool m_is_csv;

	CRITICAL_SECTION m_logManager;
	HANDLE m_log_global_mutex;

	FILE *m_ErrLogFile;

	bool m_is_single_log;

	void (__cdecl *GetAddDescription)(CString &descr);
	bool Write2FileWithCheck(LPCTSTR line, bool force_change = false);
	
};

class CManageThread;
class CTransportWaitDlg;
class CServerStopDlg;

/////////////////////////////////////////////////////////////////////////////
// Типовой класс клиентского сокета
class SOCKET_TRANSPORT_API CaplSocketClient : public CSocket
{
friend CTransportWaitDlg;
// Attributes
protected:
	bool m_operation_in_progress;
	ASC_ERROR_CODES m_LastAscError;
	int m_LastWsaError;
	CServerStopDlg* m_server_stop_dlg;
	AFX_THREADPROC m_renew_function;
	LPVOID m_parameter_renew_function;
	CString m_class_info;

	bool (__cdecl *m_ApplicationLevelLogMessage)(LPCTSTR message);

	bool m_isCall;
public:
	bool m_NeedAutoDelete;
	bool m_ServerStopping;
	bool m_hide_mode;

	// с сервера пришла команда с требованием заново залогиниться
	bool m_need_relogin;

	bool m_ServerAnsi;
#ifndef _UNICODE
//	bool m_DenyServerUnicode;
#endif

	DWORD m_LastReadyData;
	CWnd* m_parent_wnd;
	// указатель на контейнер - CTransportSocketClient - из которого вызываются функции при разрыве соединения, переконнекте и т.п.
	LPVOID m_upper_container;

	DWORD m_thread_id;
	
// Operations
public:
	CaplSocketClient();
	CaplSocketClient(LPCTSTR ip, UINT port);
	virtual ~CaplSocketClient();

	// При установке параметра is_process в false - во внутренних циклах и т.п. перестает обрабатывать
	// сообщения WM_PAINT. Флаг добавлен для ILS. Вызывается с уровня NetStepData.
	static bool SetGlobalProcessWmPaint(bool is_process);

	bool GetHostName(CString &name);
	// Если ip соединение с сервером есть - возвращает ip свой адрес, упакованный в 4 байта. иначе 0
	UINT32 GetCompIp();
	// возвращает имя сервера, которое было передано при коннекте
	CString GetCompName(){return m_server_name;};

	void SetBreak(bool state);
	bool IsBreak();
	bool IsBreakWaitData();

	void SetApplicationLevelLogMessage(bool (__cdecl *_ApplicationLevelLogMessageDlg)(LPCTSTR message));

// Overrides
public:
	
	// Работа над ошибками
	CString	GetDescriptionLastError();
	ASC_ERROR_CODES GetLastAscError();
	int GetLastWsaError();	

	// установка времен'ных параметров
	bool SetTimeOut(DWORD timeout_waitdlg, DWORD timeout_my_receive=TIMEOUT_RECEIVE);
	
	// Соедниение с сервером по адресу ip и порту port
	// если prev_ping==true то перед соединением сервер пингуется и при отсутствии ответа соединение
	// не устанавливается (чтобы не тормозить с выключенными серверами)
	// параметр reserved нужен для совместимости по параметрам с функцией CaplSocketClientH::Connect; там он задает прокси
	virtual bool Connect(LPCTSTR ip, UINT port, bool prev_ping=true, LPCTSTR reserved=0, int num_prev_ping=3); // При изменении параметров менять параметры CaplSocketClientH!!!
	virtual bool Connect(UINT ip, UINT port, bool prev_ping=true, LPCTSTR reserved=0, int num_prev_ping=3); // При изменении параметров менять параметры CaplSocketClientH!!!
	virtual void Disconnect();

	virtual bool Call(void** data, DWORD &length, void* lpBuf2=0, DWORD nBufLen2=0, bool can_break=true, bool pause_wait_dlg=true, bool wait_answer=true, bool detach_buffer = false); // При изменении параметров менять параметры CaplSocketClientH!!!
	void ClearInternalData();
	void SetErrProtocolMess(const CString &err_protocol_mess){m_err_protocol_mess=err_protocol_mess;}

	void SetRenewFunction(AFX_THREADPROC renew_function,LPVOID parameter_renew_function);
	// Получение состояний
	bool IsConnected();
	bool IsHalted();
//	bool IsTimeOutDlg();
	bool IsWaitDlg();
	bool IsActiveWaitDlg();
	bool IsOperated();
	// установка режима отображения управляющих диалогов
	void SetManageMode(long manage_mode);
	long GetManageMode(){return InterlockedExchangeAdd(&m_manage_mode,0);}
	void SetWaitDlgText(LPCTSTR waitdlg_text);
	bool IsReadyData();

	void SetHalt(){InterlockedIncrement(&m_halt);}
	
	// При вызове OnClose будет вызываться эта внешняя функция (если она определена)
	void (__cdecl *external_close_funct)(LPVOID param);
	// внешняя функция выполняет автоматический реконнект для перераспределения клиентов по рабочим серверам
	void (__cdecl *external_reconnect_funct)(LPVOID param);
	
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CaplSocketClient)
public:
	virtual void OnReceive(int nErrorCode);
	virtual void OnSend(int nErrorCode);
	virtual void OnClose(int nErrorCode);
	//}}AFX_VIRTUAL

	// Generated message map functions
	//{{AFX_MSG(CaplSocketClient)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG

// Implementation
protected:
	// номер обращения к серверу
	DWORD m_num_send;
	int MyReceive(void* data, DWORD length);
	int MySend(const void* lpBuf1, DWORD nBufLen1, const void* lpBuf2=0, DWORD nBufLen2=0, const void* lpBuf3=0, DWORD nBufLen3=0, bool is_throw=true);
	// в зависимости от length читает из канала данные (letgth>0) или команду (==0)
	// возвращает true при обработке данных и false в случае команды
	// !!! На все ошибки вызывает исключения типа ASC_ERROR_CODES
	bool FullRead(DWORD length);

	// указатель на класс транспорта у сервера
	DWORD m_server_class;
	// время до появления диалога "выполняется операция.Ждите"
	DWORD m_waitdlg_time;
	// текст на диалоге "ждите"
	CString m_waitdlg_text;
	// максимальное время для выполнения операции сервером (0-не ограничено)
	DWORD m_timeout_call;
	// максимальное время для ожидания приема (receive) (0-по умолчанию- 50 000 милисекунд)
	DWORD m_timeout_my_receive;
	// есть ли связь с сервером
	bool m_IsConnected;
	// срочно прерываем текущую операцию сервера
	bool m_flag_break;
	// операция чтения/ожидания данных прервана.
	bool m_is_buffer_full;

	//
	bool ProcessedCommand(DWORD command);
	
	CString m_err_protocol_mess;
	
	// активный буфер, в который помещаются данные из канала
	// указатель на этот буфер передается NetStepData напрямую, и тамошние функции чтения радотают именно с этим буфером!
	BYTE* m_InternalData;
	DWORD m_size_InternalData;
	CRITICAL_SECTION m_IsBlockedInternalBuf;

	// режим отображения управляющих диалогов
	long m_manage_mode;

	// Параметры, по которым мы соединялись с сервером
	CString m_server_name;
	UINT m_server_port;

	CTransportWaitDlg* m_wt_dlg;
	// если >0 сокет уже разорван. Любая передача данных отменяется
	long m_halt;

	CRITICAL_SECTION m_ManageOperate;
private:
	void InitVar();
};


/////////////////////////////////////////////////////////////////////////////
// Класс для работы клиента поверх http
// В винде использует механизм wininet.h
class SOCKET_TRANSPORT_API CHTTPReader
{
public:
	CHTTPReader();
	CHTTPReader(LPCTSTR lpszServerName, INTERNET_PORT port=0, bool bUseSSL=false, LPCTSTR proxy =0);
	~CHTTPReader();

	bool SetServer(LPCTSTR lpszServerName, INTERNET_PORT port=0, bool bUseSSL=false, LPCTSTR proxy =0);

	bool  OpenInternet     (LPCTSTR lpszAgent);
	void  CloseInternet    ();
	bool  OpenConnection   (LPCTSTR lpszServerName=NULL);
	void  CloseConnection  ();

	bool  Get              (LPCTSTR lpszAction, LPCTSTR lpszReferer=NULL);
	bool  Post             (LPCTSTR lpszAction, LPVOID lpszData, DWORD DataLength, LPCTSTR lpszReferer=NULL);
	void  CloseRequest     ();

	BYTE *GetData          (BYTE *lpszBuffer, DWORD dwSize, DWORD *lpdwBytesRead=NULL);
	BYTE *GetData          (DWORD *lpdwBytesRead=NULL);
	DWORD GetDataSize      ();
	void  SetDataBuffer    (DWORD dwBufferSize);

	void  SetDefaultHeader (LPCTSTR lpszDefaultHeader);
	DWORD GetError         () const { return m_dwLastError; }
	bool QueryAnswerHeaders();

	ASC_ERROR_CODES GetAscErrorCode(){return m_asc_code;};

	DWORD	  m_ClientId;

	CString m_hdr_server;
	DWORD m_hdr_version_num;
	DWORD m_dwStatus;
	CString m_StatusText;
	DWORD m_hdr_spec_answer_index;
	DWORD m_hdr_spec_answer_datasize;
	long m_hdr_spec_answer_crc_original;
	long m_hdr_spec_answer_crc_encoded;
	long m_hdr_spec_original_data_size;

	ASC_ERROR_CODES m_asc_code;

	CString m_hdr_all;

	//CaplLog m_log;

protected:
	bool CheckError (bool bTest);
	void StrDup     (LPTSTR& lpszDest, LPCTSTR lpszSource);
	bool SendRequest(LPCTSTR lpszVerb, LPCTSTR lpszAction, LPVOID lpszData=0, DWORD DataLength=0, LPCTSTR lpszReferer=0);

	HINTERNET m_hInternet;
	HINTERNET m_hConnection;
	HINTERNET m_hRequest;

	DWORD     m_dwLastError;
	bool      m_bUseSSL;
	CString   m_lpszServerName;
	INTERNET_PORT m_port;
	CString   m_lpszDefaultHeader;
	BYTE     *m_lpszDataBuffer;
	DWORD     m_dwBufferSize;

	CString m_ProxyName;
	CString m_ProxyLogin;
	CString m_ProxyPassword;

	bool m_first;

	long m_HTTP_SPEC_REQUEST_INDEX;

	CString m_class_info;

};

/////////////////////////////////////////////////////////////////////////////
// Класс клиента для работы поверх http
//
class SOCKET_TRANSPORT_API CaplSocketClientH : public CaplSocketClient
{
public:
	CaplSocketClientH();
	~CaplSocketClientH();

	// Соединение с сервером по адресу ip и порту port
	// если prev_ping==true то перед соединением сервер пингуется и при отсутствии ответа соединение
	// не устанавливается (чтобы не тормозить с выключенными серверами)
	bool Connect(LPCTSTR ip, UINT port, bool prev_ping=true, LPCTSTR proxy = 0, int num_prev_ping=3 );
	bool Connect(UINT ip, UINT port, bool prev_ping=true, LPCTSTR proxy = 0, int num_prev_ping=3 );
	void Disconnect();
	bool Call(void** data, DWORD &length, void* lpBuf2=0, DWORD nBufLen2=0, bool can_break=true, bool pause_wait_dlg=true, bool wait_answer=true, bool detach_buffer = false);


	CHTTPReader *m_http_reader;

};

//////////////////////////////////////////////////////////////
// Коллекция строк с приведением ключа к нижнему регистру и поиском по нижнему регистру
//
class SOCKET_TRANSPORT_API CaplStrStrMapLo :protected CaplStrMap
{
public:
	struct SStrMapLoItem
	{
		SStrMapLoItem(LPCTSTR _key_orig, LPCTSTR _val){key_orig=_key_orig; val=_val;}
		CString key_orig;
		CString val;
	};
	~CaplStrStrMapLo(){Clear();}
	void Clear();

	int Add(LPCTSTR str, LPCTSTR val);
	int Add(LPCTSTR _str, int ival);
	int Replace(LPCTSTR str, LPCTSTR val);
	int FindByIn(LPCTSTR str) const;	/**< Поиск индекса по строке*/
	CString Get(LPCTSTR str) const;		/**< Чтение значения по строке*/
	CString Get(int indx) const;		/**< Чтение значения по индексу*/
	CString GetKey(int indx) const;		/**< Чтение ключа по индексу*/

	CString PrintList(bool html_table=false);

protected:
	int GetSize() const { return items.GetSize();} /**< Размер */
};


/////////////////////////////////////////////////////////////////////////////

// Оконный класс сервера
class CPssServWnd;
class CSocketThread;
/////////////////////////////////////////////////////////////////////////////
// CaplSocketServ command target

class SOCKET_TRANSPORT_API CaplSocketServ : public CSocket
{
	friend CSocketThread;
	friend CPssServWnd;
// Attributes
protected:
	CString m_client_ip_s;
	DWORD m_client_ip;
	UINT m_client_port;
	CString m_server_ip_str;
	UINT m_server_ip;
	UINT m_server_port;
	CString m_class_info;
	// класс-наследник при обработке данных с клиента может писать сюда инфо
	// если происходит исключение - обработчик запишет инфо в лог.
	CString m_funct_info;
	// время начала работы текущего сокета
	COleDateTime m_start_time;  //?

	//	true означает что это соединение с pss-нфм клиентом, но работающим через http- протокол
	// bool m_client_over_http; - использование заменено на m_client_type

// Operations
public:
	CaplSocketServ();
	virtual ~CaplSocketServ();

	// функция формирует описание класса для вывода в лог
	virtual void MakeClassInfo(LPCTSTR additional = _T(""));
	
	DWORD GetServerIp(){return m_server_ip;}
	UINT GetServerPort();
	void GetServerIpPortString(CString &str);
	DWORD GetClientIp();
	void GetClientIpPortString(CString &str);
	UINT GetClientPort(){return m_client_port;}

// Overrides
public:
	int m_LastWsaError;

	SOCKET m_temp_socket;
	// 
	bool m_detach_after_send;
	//bool m_close_after_send;
	bool StartListen(UINT port, int &error, bool show_err_mess=false, bool mode_www=false, int count_hide_attempt = 0, bool mode_mail=false);
	bool SendData(BYTE *data1, DWORD length1, BYTE *data2, DWORD length2);
	bool SendCommand(int command, INT32 ext_data=-1, INT32 ext_data_2=-1, LPCTSTR mess=0);

	// устанавливает максимальное количество клиентов, которое может обслуживать сервер
	void SetMaxClient(DWORD size);
	// передает клиенту положение бегунка в диалоге ожидания
	bool SendPercentage(UINT32 percentage);
	bool SendPercentageText(LPCTSTR mess);
		
	// Объекту лога приложения передаются сообщения об ошибках транспорта. 
	// Предварительно приложение должно задать объект лога
//	inline void LogItemC(SaplLogItem &log_item);
//	inline void LogItemA(SaplLogItem &log_item);

	inline bool IsLogAll();
	
	// Функция для перегрузки - приложение само должно прервать работу нити, связанной с объектом obj
	virtual bool BreakOperation(CaplSocketServ* obj){return false;};
	// Функция для перегрузки - потомок с ее помощью будет обрабатывать пришедший буфер
	virtual bool Processed(CaplDataBuf &dbuf_in, CaplDataBuf &dbuf_head, CaplDataBuf &dbuf_out){return true;};
	// Функция для перегрузки - потомок с ее помощью будет выполнять дополнительный действия после того, как ответ уйдет клиенту
	virtual bool PostProcessed(){return true;};
	// Функция для перегрузки - эта функция рабочего сервера при необходимости допринимает данные и обрабатывает запрос
	//	клиента, работающего поверх http или через rest.
	virtual bool ProcessExternalHTTPClient(){return true;};
	// Функция для перегрузки - потомок с ее помощью будет диспетчировать (определять рабочие процессы и нити) соединения
	//	с клиента, работающего поверх http или через rest.
	virtual bool DispatchHTTPClient(){return true;};
	// Функция для перегрузки - конструирует потомок этого класса для передачи данных
	virtual CaplSocketServ* NewDataSocket(){return new CaplSocketServ();};
	// Функция для перегрузки - при создании канала вызывается для инициализации 
	// экземпляра. В случае возврата false канал разрывается, экземпляр удаляется
	virtual bool Init(){return true;};
	// определение параметров сокета в читабельную форму
	bool PrevInit();
	// Функция для перегрузки - при закрытии сокета вызывается для корректной 
	// очистки данных, созданных в нити данного экземпляра
	virtual void Exit(){return;};
	// Функция для отработки чистого HTTP запроса (управление получает уже после проверок на 
	//	почту, клиента поверх http и на rest).
	// Должна сливать в канал некий HTML с описанием сервера или выдавать файлы с диска и пр
	virtual bool ProcessedHTTP(){m_terminate_after=1; return true;};

	// функция для отработки MAIL запроса на стадии соединения. 
	virtual bool ProcessedMail(){m_terminate_after=1; return true;};

	// выделяет из буфера http: строку URI; http заголовки; разбирает строку на подпараметры для REST.
	// возвращает позицию начала блока данных в буфере http
	// если буфер нет http (не get и не post) - очищает переменные и возвращает 0
	virtual bool ParseHttpHeaders();
	void CompletionUriData(SUriData &m_uri_data);

	// Сокет главной нити, ведущий список активных сокетов. Для главной нити равен 0
	CaplSocketServ* m_pool;
	// Прерывает выполнение нити
	virtual bool ProcessBreakThread(int param);
	// возвращает общее количество соединений, подключенных к данному серверу
	DWORD GetNumAllConnected();
	// возвращает количество соединений НЕ HTTP
	DWORD GetNumDataConnected();

	
	// посылает клиентам время (всекундах), через которое сервер прервет все соединения 
	// с клиентами и выключится. Если ==0 то выключаться немедленно
	// если этот экземпляр - клиентский сокет, то посылает текущему
	// если это слушающий сокет, то вызывает аналогичную функцию у всех, кто есть в списке
	void SetStopTime(long stop_time);
	long GetStopTime();
	
	// время ожидаемого выключения
	CAplAtomicLong m_stop_time;

	bool IsReadyData();
	DWORD m_LastReadyData;

	bool m_connect_status;
	// окно для отработки таймера
	CPssServWnd* m_wnd;
	// объект нити сокета
	CSocketThread* m_thread;

	// номер подключения, уникальный в рамках всех рабочих серверов запущенного сервера приложений
	// после перезапуска - начинается опять с нуля
	UINT32 m_serv_seans_id;

	// Принимает данные из сокета, по первым байтам решает, что это за запрос (клиент, HTTP, мыло) и перенаправляет на дальнейшую обработку
	void ProcessedReceive(int nErrorCode);

	// при http запросе дочитывает данные из сокета до конца заголовка (два переноса строки)
	bool ReadNextHttpFromSocket();

	// запускает посылку на клиента команды "откликнись если живой". Может запускаться из другой нити!
	void RenewClientState();

	BOOL Create(UINT nSocketPort, bool show_err_mess, int count_hide_attempt = 0);

	// прерывает все соединения, тормозит нити, удаляет сокеты и т.п.
	BOOL DisconnectAll();

	// функция обязательно должна вызываться внутри критической секции m_OperateList!
	// иначе во время исполнения этой функции нить может завершиться и самоудалиться - с непредсказуемыми последствиями!
	BOOL KillThread(int pos, bool forsed=false, LPCTSTR text=0);

	// возвращает значение глобального флага - если dll-ка используется в WWW сервере
	bool IsModeWWW();
	// возвращает значение глобального флага - если dll-ка используется в почтовом сервере
	bool IsModeMail();

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CaplSocketServ)
//public:
protected:
	virtual void OnClose(int nErrorCode);
	virtual void OnAccept(int nErrorCode);
	virtual void OnReceive(int nErrorCode);

	// этот флаг запрещает посылку команд клиенту
	long m_supress_command;
	// клиенту была послана команда на переконнект
	bool m_wait_reconnect;

	bool TestErrOnHalt();
	//}}AFX_VIRTUAL

	// Generated message map functions
	//{{AFX_MSG(CaplSocketServ)
	//}}AFX_MSG

// Implementation
public:
	
	// возвращает объект клиентского сокета по индексу в пуле
	CaplSocketServ* GetSocketFromList(DWORD pos);

	virtual CaplSocketServ* GetSocketFromListByParam(long param){return 0;};

	DWORD m_thread_id;

	// У слушающего сокета - управлением списком активных сокетов (in)/нитей(out)
	int PoolAddSocketThread(CSocketThread* thread);
	bool PoolRemoveSocketThread(CSocketThread* thread);
	inline void PoolOperateListEnter();
	inline void PoolOperateListLeave();

	// принимает и привязывает сокет, полученный с другого сервера
	bool AttachTempSocket();

	// Устанавливает функцию, которая будет вызываться по таймеру каждые elapse микросекунд  для каждого сервера связанного с NetStepData
	UINT SetUserTimer(DWORD elapse, AFX_THREADPROC lpfnTimer);
	// убивает таймер
	bool KillUserTimer(UINT timer_id);
	// список пользовательских таймеров (in идентификатор таймера, out указатель на функцию таймера
	CaplMap m_clients_timers;
	// вызов пользовательского таймера
	bool ProcessUserTimer(UINT timer_id);

	bool GetCookie(LPCTSTR, CString &value);

protected:
	// У слушающего сокета - список активных сокетов (in)/нитей(out)
	CArray<CSocketThread*,CSocketThread*> m_list;

	// не дает посылать клиенту слишком часто команды об процентах выполнения
	CaplTimeMeter m_LastPercentageTimer;
	// Количество клиентских сокетов, у которых прошел коннект. Учитывается при умирании уставших серверов
	DWORD m_num_data_connects;

	// true - это http-запрос от браузера. Нить в таблице подключенных клиентов не светится, после ответа убивается
	//bool m_web_query;

	// интервал вызова таймера ID_TIMER_BLOCK
	DWORD m_ClearedTimeInterval;

	// У слушающего сокета - максимальное число активных клиентов. У сокетов для передачи данных - 0
	DWORD m_max_client;
	// Event. Находится в сигнальном (установленном) состоянии, когда с сокетом не проводится операций (на обработку 
	//    данных с клиента, обновление состояния и т.п.). Если сокет не в сигнальном - он кем то занят, ыдалять его нельзя
	HANDLE m_free;

	// Даннные, принятые из сокета. Возможно, не полностью (при REST запросе с передачей файла - догрузка идет уже в нити окончательной обработке)
	CaplDataBuf m_session_data;
	// количество данных, которые еще надо прочитать из сокета
	int m_size_waited;
	// если true сокет уже разорван. Любая передача данных отменяется
	//long m_halt;

	// флаг входа в OnClose. Используется для предотвращения одновременного выполнения закрывающих процедур из основной нити и из пула
	CAplAtomicLong m_onclose_enter;

	// Флаг прерывания после выполненния запроса. Если 1, то после отправки данных клиенту сокет будет удален
	CAplAtomicLong m_terminate_after;

	/////////////////////////////////////////
	// блок переменных для клиентов поверх http-запросов
	// полная строка m_URI - см m_uri_data.m_uri_path
	//
	// массив http -заголовков
	CaplStrStrMapLo m_http_headers_in;
	// куки
	CaplStrStrMapLo m_cookies;
	// параметры URI разобранные на составные части
	SUriData m_uri_data;
	// позиция блока данных во входном массиве
	DWORD m_indx_start_data;
	// тип клиента поверх http
	EClientType m_client_type;
	// идентификатор клиенткого кеша в рамках сервера (только для REST и PSS поверх HTTP)
	DWORD m_ClientId;
	CString m_session_key;

	//////////////////////////////////////////
	// Переменные для формирования http ответа
	// - код ответа
	int m_http_code; //--
	// - ответ в виде html
	CString m_csHtmlAnswer; //--
	// - ответ в виде буфера. Используется при отдаче файлов. Если буфер пустой, то
	//	сервер отправляет клиенту содержимое m_csHtmlAnswer
	CaplDataBuf m_dbufFileBody; //--
	// - http-заголовки ответа
	CaplStrStrMapLo m_smHttpHeadersOut; //--
#ifdef _DEBUG
	// Отладочный заголовок в ответе браузеру со служебной информацией
	//CString m_csXDebugHeader;
#endif

	// Проверка на наличие обязательных заголовков и при их отсутствии - формирование
	virtual void CheckCompliteHttpHeaders();

	// Сброс переменных ответа на http-запрос в начальное состояние с установкой кода http
	void ClearAnswerData(int http_code=0);

public:
	inline void AddHttpHeader(LPCTSTR header, LPCTSTR val){m_smHttpHeadersOut.Add(header, val);}
	inline void AddHttpHeader(LPCTSTR header, int val){m_smHttpHeadersOut.Add(header, val);}
	void AddHttpHeaderExpires(int sec);

	int SetHttpCode(int http_code);

	// Собирает из разных источников ответ и записывает его в m_cdbAnswer
	// Если данные ответа записаны в сокет внутри функции, то m_cdbAnswer обнуляется
	ULONGLONG PrepareHttpAnswer(ULONGLONG ullExtSize=-1);

	CString MakeHttpCodeWithHeaders();

protected:

	// Буфер с ответом. Если после возврата из функций обработки размер буфера 0,
	//	то считается что данные были отправлены клиенту внутри функции обработки
	CaplDataBuf m_cdbAnswer;
	//
	// блок переменных для клиентов поверх http-запросов
	/////////////////////////////////////////


	// защита переменных, используемых другими нитями
	CRITICAL_SECTION m_MultithreadVarProtect;

	CRITICAL_SECTION m_OperateList;

	// Функция для очистки БД, памяти и т.п.
	// Если она задана и задан параметр m_PostProcessingObj, то она будет однократно вызвана после
	//  отправки ответа клиенту с параметром m_PostProcessingObj.
	_PostProcessing m_PostProcessing;
	LPVOID m_PostProcessingObj;
};




class SOCKET_TRANSPORT_API CSocketThread :public CWinThread{
public:
	CSocketThread(CaplSocketServ *pool,CaplSocketServ* pSocket);
	virtual ~CSocketThread();
	BOOL InitInstance();
	int ExitInstance();

	// при принудительном завершени нити сюда записывается тик, после которого нить должна быть убита terminate-ом
	CaplTimeMeter m_tick_kill_timer;
	long m_state_kill;

// 	HANDLE m_event;
	CaplSocketServ* m_pSocket;
	
	CString m_class_info;

protected:
	CaplSocketServ *m_pool;

};

class SOCKET_TRANSPORT_API CPSSServiceDescriptor 
{
public:
	CPSSServiceDescriptor();
	CPSSServiceDescriptor(const CString &_service_name, int _process_id, int _service_port);
	void Set(CPSSServiceDescriptor &source);
	CString m_service_name;
	int m_process_id;
	int m_service_port;
};



class SOCKET_TRANSPORT_API CPSSServicesList : public CObject
{
public:
	CPSSServicesList(const CString &_service_name, int _service_port);
	virtual ~CPSSServicesList();
	
	void GetHtmlTable(const CString &curr_server_name, CString &html_table);
	
protected:
	CString m_class_info;

	void Clear();
	void Reload(bool add=true);

	CPSSServiceDescriptor m_i_m;

	HANDLE m_hFileServicesList;
	PVOID m_GlobalServicesListData;

	long *m_pListSize;
	PVOID m_pRegionData;
	HANDLE m_MutexServicesListProcessed;

#ifdef _UNICODE

	HANDLE m_hFileServicesListU;
	PVOID m_GlobalServicesListDataU;

	long *m_pListSizeU;
	PVOID m_pRegionDataU;

	HANDLE m_MutexServicesListProcessedU;
#endif

	CaplTAggr<CPSSServiceDescriptor*,CPSSServiceDescriptor*,APLAGGR_AUTOKILLREF> m_local_list;
	CRITICAL_SECTION m_local_list_protect;
	CaplTimeMeter m_last_local_list_reload_timer;
};



AFX_EXT_API void GetDescriptionTransportError(ASC_ERROR_CODES error, int LastWsaError, CString &buffer);
AFX_EXT_API void GetDescription_FILE_Error(int &error_id, CString &buffer);
AFX_EXT_API void GetDescriptionSystemError(int error_id, CString &buf);
//AFX_EXT_API TCHAR *GetDescriptionSocketError(int error);
AFX_EXT_API void GetDescriptionWininetError(int error, CString &buffer);

AFX_EXT_API bool HttpCode2Text(int http_code, CString &text, bool forMan, LPCTSTR prevText = 0);
AFX_EXT_API void GetDescriptionHTTPAnswer(int answer, CString &buffer);
AFX_EXT_API CString GetDescriptionTransportCommand(int command);

// Выводит IPv4 адрес, заданный числом, в строку
// Если is_win == true то IP записан в обратном порядке байт (windows, sin_addr.s_addr)
// Если is_win == false то IP записан в прямом порядке байт (Qt)
AFX_EXT_API CString Ip2String(DWORD ip_num, bool is_win = true);
// Выводит IPv4 адрес, заданный числом, в строку
// Если is_win == true то IP записан в обратном порядке байт (windows, sin_addr.s_addr)
// Если is_win == false то IP записан в прямом порядке байт (Qt)
AFX_EXT_API inline void Ip2String(DWORD ip_num, CString &ip_str, bool is_win = true){ip_str = Ip2String(ip_num, is_win);}

// Ищет IP-адрес в кеше и если находит, возвращает имя компьютера.
// Если не находит, возвращает строковое представление IP и запускает фоновую 
//	нить с обратным запросом к DNS. При получении ответа от DNS заносит ответ в кеш.
// Считает что IP записан в обратном порядке байт (windows, sin_addr.s_addr)
AFX_EXT_API CString Ip2Name(DWORD ip_num);

AFX_EXT_API bool ParseListIpDiapazon(CString &pattern, CaplMap &out_list);
AFX_EXT_API bool MakeListIpDiapazon(CaplMap &in_list, CString &pattern);

// проверяет, запущен ли процесс с указанным id процесса
AFX_EXT_API bool PSSCheckProcessDead(int process_id);

AFX_EXT_API long StartTransportWaitDlg(LPCTSTR text=0);
AFX_EXT_API bool IsBreakTransportWaitDlg(long dialog);
AFX_EXT_API bool SetTransportWaitDlgText(long thread, LPCTSTR text);
AFX_EXT_API void EndTransportWaitDlg(long dialog, bool foreground_main_wnd=true);

AFX_EXT_API bool LogDebugMessageReal(LPCTSTR ErrDescr, LPCTSTR funct, LPCTSTR source, LPCTSTR file, LPCTSTR date, int line, bool carry=true);
AFX_EXT_API bool LogDebugMessageReal(CString& ErrDescr, LPCTSTR funct, LPCTSTR source, LPCTSTR file, LPCTSTR date, int line, bool carry=true);
//AFX_EXT_API void SetModuleLogFileName(const CString &file_mane);
// Устанавливает объект лога для вывода предупреждений и ошибок
AFX_EXT_API void SetModuleLog(CaplLog *ModuleLog);

AFX_EXT_API BOOL TransportPing(LPCTSTR strHost);

AFX_EXT_API bool aplIsUserAdmin();
AFX_EXT_API void aplGetOSUser(CString &user, CString &domain);

AFX_EXT_API bool CreateSecurityAttributes (ACCESS_MASK amAccessRights,PSECURITY_ATTRIBUTES* ppSecurityAttributes);

AFX_EXT_API bool DecodeFromBase64( const char* encoded_string, DWORD size_string, CaplDataBuf &dbuf_out);
AFX_EXT_API CString EncodeToBase64( CaplDataBuf &dbuf_in);
AFX_EXT_API CString EncodeToBase64( const BYTE* BYTEs_to_encode, unsigned int in_len);

// Перекодировка из UTF8 в 1251 и обратно
AFX_EXT_API CString ConvertFromUTF8toWIN1251(CString sSource);
AFX_EXT_API CString ConvertFromWIN1251toUTF8(CString sSource);

// Проверяет кодировку исходной строки (UTF8 или ANSI) и раскодирует в текущую системную кодировку
// В случае ошибок декодирования бросает исключение типа SaplErrorDescription
AFX_EXT_API CString DecodeURLString(CStringA &sSource);

AFX_EXT_API void SetFunctionGlobalExit(void (__cdecl *prevSetGlobalExit)());
AFX_EXT_API void SetApplicationGlobalExit(bool _is_set_global_exit);

enum enumEnvProtect
{
	EnvProtectEnter = 1,
	EnvProtectLeave = 2
};

AFX_EXT_API void SetFunctionGlobalEnvProtect(void (__cdecl *prevOracleEnvProtect)(enumEnvProtect param));
AFX_EXT_API void SetFunctionProcessAplExeption(void (__cdecl *prevProcessAplExeption)(SaplErrorDescription &error));
AFX_EXT_API long GetSocketInitErrors();

AFX_EXT_API long GetNumErrdataSocket();

// чтение и сохранение текущего языка для серверных приложений
AFX_EXT_API DWORD aplReadServUILang(bool only_read = false);
AFX_EXT_API bool aplSaveServUILang(DWORD curUILang);

// Вычисление хэша по заданному методу. Внутри определяет, вычислить самой или вызвать из aplCryptQt
AFX_EXT_API bool internalCryptGetHash(LPCTSTR lpMethod, LPCTSTR str_in, CString &csHash, CString &errMess);

// По хешу возвращает строчку с названием метода хэширования
AFX_EXT_API bool internalHashGetMethod(LPCTSTR lpHash, CString &csMethod);

/**
	@brief aplCheckPassword Проверяет закодированный пароль на корректность
	@param pwdClient закодированный пароль, пришедший от клиента. 
	@param pwdDb закодированный пароль, прочитанный из БД.
	@return true если пароли совпадают и false если не совпадают.
*/
AFX_EXT_API bool aplCheckPassword(LPCTSTR pwdClient, LPCTSTR pwdDb);

/** @brief aplSelectFolder Показывает диалог выбора папки.
	@param hWnd родительское окно
	@param start_dir папка, которая будет выбран при старте диалога
	@param title пояснительный текст в диалоге.
	@return путь к выбранной папке если папка была вбырана, иначе пустая строка.
	*/
AFX_EXT_API CString aplSelectFolder(HWND hWnd, LPCTSTR start_dir, LPCTSTR title);

/** @brief aplSelectComputer Показывает диалог выбора компьютера.
	@param hWnd родительское окно
	@param title пояснительный текст в диалоге.
	@return имя выбранного компьютера если он был вбыран, иначе пустая строка.
	*/
AFX_EXT_API CString aplSelectComputer(HWND hWnd, LPCTSTR title);

//////////////////////////////////////////////////
// Служебные функции для формирования http-ответа


// Выделяет из полного имени файла имя и расширение
inline void ParseFilename(CString csFilePath, CString &csName, CString &csExt)
{
	csExt.Empty();
	csName.Empty();
	int pos;

	pos=csFilePath.ReverseFind('.');
	if(pos!=-1)
		csExt=csFilePath.Mid(pos+1);

	pos=csFilePath.ReverseFind(aplDirRazd);
	if(pos!=-1)
		csName=csFilePath.Mid(pos+1);

}

// по расширению определяем тип
inline CString GetContentType(CString csExt, bool &is_text)
{
	CString csContentType=_T("");
	is_text = false;

	if(csExt==_T("css")){csContentType=_T("text/css;");}
	else if(csExt==_T("gif")){csContentType=_T("image/gif");}
	else if(csExt==_T("gz")){csContentType=_T("application/gzip");}
	else if(csExt==_T("htm") || csExt==_T("html")){csContentType=_T("text/html;");is_text=true;}
//	else if(csExt==_T("htm") || csExt==_T("html")){csContentType=_T("text/html; charset=UTF-8;");is_text=true;}
	else if(csExt==_T("mpeg") || csExt==_T("mpg") || csExt==_T("mpe")){csContentType=_T("video/mpeg");}
	else if(csExt==_T("jpg") || csExt==_T("jpeg") || csExt==_T("jpe")){csContentType=_T("image/jpeg");}
	else if(csExt==_T("js")){csContentType=_T("application/javascript;");}
	else if(csExt==_T("pdf")){csContentType = _T("application/pdf");}
	else if(csExt==_T("png")){csContentType=_T("image/png");}
	else if(csExt==_T("rtx") || csExt==_T("rtf")){csContentType=_T("text/richtext");}
	else if(csExt==_T("soap")){csContentType=_T("application/soap+xml;");}
	else if(csExt==_T("sgml")){csContentType=_T("text/sgml;");}
	else if(csExt==_T("tiff") || csExt==_T("tif")){csContentType=_T("image/tiff");}
	else if(csExt==_T("txt")){csContentType=_T("text/plain;");is_text=true;}
	else if(csExt==_T("wav")){csContentType=_T("application/x-wav");}
	else if(csExt==_T("xhtml")){csContentType=_T("application/xhtml+xml");}
	else if(csExt==_T("xml")){csContentType=_T("text/xml");}
	else if(csExt==_T("zip")){csContentType=_T("application/zip");}
	else if(csExt==_T("")){
		/*if(is_strong)
		{
			// такой тип мы не знаем
			//APL_THROW_EX(APL_NET_CLI_MISSED_DATA,"Сервер не поддерживает запрошенный тип данных: "+ext);
			csContentType=_T("");
		}
		else*/
		{
			csContentType=_T("application/force-download");
		}
	}
	else
	{
		csContentType=_T("application/force-download");
	}
	return csContentType;
}

inline CString ReplaseServiceSymbols(CString &str)
{
	str.Replace(_T("\n"), _T(""));
	str.Replace(_T("\r"), _T(""));
	str.Replace(_T("\t"), _T(""));
	return str;
}

// Оставляет в строке только алфавитно-цифровые символы win-1251 и знаки препинания
AFX_EXT_API CString DeleteNon1251Symbols(LPCSTR str_in);

// Определяет, является ли IP адрес локальным адресом
// bool is_win - если true, то считается что адрес в обратном порядке, из sin_addr.s_addr
//	если false, то в прямом, полученном из Qt
AFX_EXT_API inline bool IsIpLocal(UINT32 ip_num, bool is_win)
{
	if(is_win)
		return (ip_num&0x0FF) == 0x7f; // Старшие 0x7f в последнем байте

	return ((ip_num>>24)&0x0FF) == 0x7f;// Старшие 0x7f в первом байте, надо сначала сдвинуть
}


//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_APLSOCKETTRANSPORT_H__0867BCA7_426B_401B_BB45_CB4DF4675759__INCLUDED_)
