#include "stdafx.h"
#include "aplSocketTransport.h"
#include <StepData.h>
#include <aplguiex.h>
#include <apl_gui.h>
#include <aplBaseLib.h>

#include "commands.h"
#include <Aclapi.h>
#include "atlbase.h"
#include <mlang.h>
#include <wincrypt.h>

#include <string>
#include <iostream>

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


/*******************************************************************************/
/* Функция диагностические сообщения                                          */
/******************************************************************************/

CaplLog * MainModuleLog=0;
CRITICAL_SECTION _DEBUG_LOG_MESSAGE_CS;


// void SetModuleLogFileName(const CString &file_name)
// {
// 	_DEBUG_LOG_FILENAME=file_name;
// }

void aplGetOSUser(CString &user, CString &domain)
{
#ifdef __linux__
	//	char username[LOGIN_NAME_MAX];
	//	getlogin_r(username, LOGIN_NAME_MAX);
	//	m_sOsUser=username;

	user.GetEnvironmentVariable(_T("USER"));
	domain.GetEnvironmentVariable(_T("ALD_DOMAIN"));

	if(domain.Left(1) == _T("."))
		domain.Delete(0, 1);

#else

	user.GetEnvironmentVariable(_T("USERNAME"));
	domain.GetEnvironmentVariable(_T("USERDNSDOMAIN"));

#endif
}



void SetModuleLog(CaplLog *ModuleLog)
{
	InterlockedExchange((LONG*)&MainModuleLog,(LONG)ModuleLog);
//	MainModuleLog=ModuleLog;
}

bool LogDebugMessageReal(CString& ErrDescr, LPCTSTR funct, LPCTSTR source , LPCTSTR file, LPCTSTR date, int line, bool carry)
{
	return LogDebugMessageReal((LPCTSTR)ErrDescr, funct ,source, file, date, line, carry);
}

inline bool LogDebugMessageReal(LPCTSTR ErrDescr, LPCTSTR funct, LPCTSTR source, LPCTSTR file, LPCTSTR date, int line, bool carry)
{
	bool res=true;
	CString str = _T("");
	if(source!=0){str = _T(" cls "); str+= source;}
	
	if(MainModuleLog!=0)
	{
		MainModuleLog->LogMessage(ErrDescr,true,true,true,true,funct,str,0,file,date,line);
	}
	return res;
}


// Если is_win == true то IP записан в обратном порядке байт (windows, sin_addr.s_addr)
// Если is_win == false то IP записан в прямом порядке байт (Qt)
CString Ip2String(DWORD ip_num, bool is_win)
{
	CString ip_str;
	DWORD num1, num2, num3, num4;
	if(is_win)
	{
		num1=ip_num&0x0000000FF;
		num2=ip_num&0x00000FF00;num2>>=8;
		num3=ip_num&0x000FF0000;num3>>=16;
		num4=ip_num&0x0FF000000;num4>>=24;
	}
	else
	{
		num4=ip_num&0x0000000FF;
		num3=ip_num&0x00000FF00;num3>>=8;
		num2=ip_num&0x000FF0000;num2>>=16;
		num1=ip_num&0x0FF000000;num1>>=24;
	}
	ip_str.Format(_T("%i.%i.%i.%i"), num1, num2, num3, num4);
	return ip_str;
}

// Формат строки: диапазоны ip адресов, разделенные точкой с запятой
// Формат диапазона: <начальный адрес>-<конечный адрес>
// Если надо задать один Ip. он задается как диапазон с одинаковым начальным и конечным адресом
// Например "127.0.0.1-127.255.255.255;195.14.35.195-195.14.35.195"
bool ParseListIpDiapazon(CString &pattern, CaplMap &out_list)
{
	CaplStackLogger stack_logger(_T("ParseListIpDiapazon"));

	DWORD ip_b[4]={0,0,0,0};
	DWORD ip[2]={0,0};
	CString word;
	TCHAR ch;
	int i,pos_b,pos_ip;
	word.Empty();
	pos_b=0;
	pos_ip=0;
	int len=pattern.GetLength()+1;
	for(i=0;i<len;i++)
	{
		if(i<len-1)
			ch = pattern[i];
		else
			ch=0;

		if(ch==_T(' ') || ch==_T('\t'))continue;
		if(iswdigit(ch) != 0)
		{
			word += ch;
		}
		else if(ch==_T('.') || ch==_T('-') || ch==_T(';') || ch==0)
		{
			if(word == _T(""))
			{
				// служебный символ в начале строки или после предыдущей точки. Ошибка
				return false;
			}
			ip_b[pos_b] = _atoi(word);
			ip_b[pos_b] <<= 8*pos_b;
			word.Empty();
			pos_b++;
			if(ch == _T('.') && pos_b > 3)
			{
				// количество точек больше трех подряд. Ошибка
				return false;
			}
			else if(ch==_T('-') && pos_ip!=0)
			{
				// тире должно идти после первого адреса в паре. И точка.
				return false;
			}
			else if(ch==_T(';') && pos_ip!=1)
			{
				// точка с запятой должна идти после второго адреса в паре. И точка.
				return false;
			}
			if(ch==_T('-') || ch==_T(';') || ch==0)
			{
				// собираем полный IP адрес
				ip[pos_ip]=ip_b[3]+ip_b[2]+ip_b[1]+ip_b[0];
				// заносим значение в Map
				if(pos_ip==1)
				{
					out_list.Add(ip[0],ip[1]);
				}
				// устанавливаем переменные
				pos_ip=1-pos_ip;
				pos_b=0;
			}
			
		}
		else
		{
			// какой-то неправильный символ. Ошибка
			return false;
		}
	};
	return true;
}

bool MakeListIpDiapazon(CaplMap &in_list, CString &pattern)
{
	int i;
	pattern.Empty();
	CString ip1,ip2;
	for(i=0;i<in_list.Size;i++)
	{
		ip1 = Ip2String(in_list.Data[i].in);
		ip2 = Ip2String(in_list.Data[i].out);
		if(pattern != _T(""))
			pattern += _T(";");

		pattern+= ip1 + _T("-") + ip2;
	}
	return true;
}

#define ERR_2_TEXT_T(par,text) case par : buffer= text; buffer+= APL_T("\n( Код ошибки "); buffer+=_T(#par) _T("  )"); break;
#define ERR_2_TEXT_T_HT(par,text) case par : buffer= text ; buffer+= APL_T("\n( Код ответа HTTP ");buffer+= _T(#par) _T(")");break;
#define ERR_2_TEXT_S(par,text) case par : GetDescriptionSystemError(LastWsaError,tmp); buffer=text; buffer+=_T(" (") _T(#par) _T("):\n"); buffer+=tmp; break;
#define ERR_2_TEXT_SM(par,text) case par : GetDescriptionSystemError(LastWsaError,tmp);buffer=text; buffer+=_T(" :\n"); buffer+=tmp; break;


void GetDescriptionTransportError(ASC_ERROR_CODES error,int LastWsaError,CString &buffer)
{
	buffer=_T("");
	CString tmp;
	switch(error){
	ERR_2_TEXT_T(ASC_NO_ERROR, APL_T("Нет ошибки"))
	ERR_2_TEXT_T(ASC_ERROR_PROCESSED, APL_T("Ошибка обработки"))
	ERR_2_TEXT_T(ASC_MISSED_PARAM, APL_T("Неверные параметры"))
	ERR_2_TEXT_S(ASC_TIMEOUT, APL_T("Сервер не ответил в течение заданного времени. Возможно, он перегружен."))
	ERR_2_TEXT_S(ASC_MISSED_RECEIVE_SIZE, APL_T("Неверный размер при приеме"))
	ERR_2_TEXT_S(ASC_MISSED_SENT_SIZE, APL_T("Неверный размер при передаче"))
	ERR_2_TEXT_T(ASC_BREAK_CALL, APL_T("Операция прервана пользователем"))
	ERR_2_TEXT_S(ASC_ALREADY_CONNECTED, APL_T(" Сокет уже присоединен"))
	ERR_2_TEXT_T(ASC_TOO_MANY_CLIENTS_ON_SERVER, APL_T("Сервер приложений не может установить соединение. Возможно, он перегружен.\nПопробуйте повторить через некоторое время"))
	ERR_2_TEXT_T(ASC_ERROR_INIT_SERVER, APL_T("Ошибка инициализации сервера"))
	ERR_2_TEXT_S(ASC_ERROR_IN_SOCKET, APL_T("Ошибка механизма сокетов"))
	ERR_2_TEXT_S(ASC_ERROR_SOCKET_CLOSE, APL_T("Сокет закрыт сервером"))
	ERR_2_TEXT_S(ASC_ERROR_IN_CONNECT, APL_T("Невозможно установить соединение с сервером"))
	ERR_2_TEXT_T(ASC_ERROR_MISSED_VERSION, APL_T("Разная версия транспорта у клиента и сервера"))
	ERR_2_TEXT_T(ASC_OPERATION_IN_PROGRESS, APL_T("Предыдущий вызов еще не завершен"))
	ERR_2_TEXT_T(ASC_ERROR_PROCESSED_ON_SERVER, APL_T("При обработке запроса на сервере возникла неизвестная ошибка"))
	ERR_2_TEXT_T(ASC_MISSED_RECEIVE_SIZE_ON_SERVER, APL_T("При приеме данных сервер принял неверное количество данных"))
	ERR_2_TEXT_S(ASC_HOST_INACCESSIBLE, APL_T("Сервер недоступен или выключен"))
	ERR_2_TEXT_S(ASC_MISSED_SERVER, APL_T("Сервер не является сервером приложений PSS"))
	ERR_2_TEXT_SM(ASC_ERROR_IN_WININET, APL_T("Ошибка при работе по протоколу HTTP"))
	//UUU надо поменять имя сервреа и клиента
	ERR_2_TEXT_T(ASC_ERROR_SERVER_UNICODE, ERR_MESS_SERVER_U_CLIENT_A )
	ERR_2_TEXT_T(ASC_UNKNOWN_PROTOCOL, APL_T("Неизвестный протокол соединения") )
	ERR_2_TEXT_T(ASC_DNS_ERROR, APL_T("Ошибка DNS запроса") )
	ERR_2_TEXT_T(ASC_SERVER_STOPPED, APL_T("Сервер завершает работу") )

	case ASC_ERROR_IN_HTTP_REQ : GetDescriptionHTTPAnswer(LastWsaError,buffer);break;
	default:buffer= _T("unknown           \n");break;
	}
}


#define ADD_CODE_IF(par) case par : buf+= _T(" (") _T(#par) _T(")");break;

void AddCodeSocketError(int error, CString &buf)
{
	switch(error)
	{
		ADD_CODE_IF(WSAEINTR)
		ADD_CODE_IF(WSAEBADF)
		ADD_CODE_IF(WSAEACCES)
		ADD_CODE_IF(WSAEFAULT)
		ADD_CODE_IF(WSAEINVAL)
		ADD_CODE_IF(WSAEMFILE)
		ADD_CODE_IF(WSAEWOULDBLOCK)
		ADD_CODE_IF(WSAEINPROGRESS)
		ADD_CODE_IF(WSAEALREADY)
		ADD_CODE_IF(WSAENOTSOCK)
		ADD_CODE_IF(WSAEDESTADDRREQ)
		ADD_CODE_IF(WSAEMSGSIZE)
		ADD_CODE_IF(WSAEPROTOTYPE)
		ADD_CODE_IF(WSAENOPROTOOPT)
		ADD_CODE_IF(WSAEPROTONOSUPPORT)
		ADD_CODE_IF(WSAESOCKTNOSUPPORT)
		ADD_CODE_IF(WSAEOPNOTSUPP)
		ADD_CODE_IF(WSAEPFNOSUPPORT)
		ADD_CODE_IF(WSAEAFNOSUPPORT)
		ADD_CODE_IF(WSAEADDRINUSE)
		ADD_CODE_IF(WSAEADDRNOTAVAIL)
		ADD_CODE_IF(WSAENETDOWN)
		ADD_CODE_IF(WSAENETUNREACH)
		ADD_CODE_IF(WSAENETRESET)
		ADD_CODE_IF(WSAECONNABORTED)
		ADD_CODE_IF(WSAECONNRESET)
		ADD_CODE_IF(WSAENOBUFS)
		ADD_CODE_IF(WSAEISCONN)
		ADD_CODE_IF(WSAENOTCONN)
		ADD_CODE_IF(WSAESHUTDOWN)
		ADD_CODE_IF(WSAETOOMANYREFS)
		ADD_CODE_IF(WSAETIMEDOUT)
		ADD_CODE_IF(WSAECONNREFUSED)
		ADD_CODE_IF(WSAELOOP)
		ADD_CODE_IF(WSAENAMETOOLONG)
		ADD_CODE_IF(WSAEHOSTDOWN)
		ADD_CODE_IF(WSAEHOSTUNREACH)
		ADD_CODE_IF(WSAENOTEMPTY)
		ADD_CODE_IF(WSAEPROCLIM)
		ADD_CODE_IF(WSAEUSERS)
		ADD_CODE_IF(WSAEDQUOT)
		ADD_CODE_IF(WSAESTALE)
		ADD_CODE_IF(WSAEREMOTE)
		ADD_CODE_IF(WSAEDISCON)
		ADD_CODE_IF(WSASYSNOTREADY)
		ADD_CODE_IF(WSAVERNOTSUPPORTED)
		ADD_CODE_IF(WSANOTINITIALISED)
		ADD_CODE_IF(WSAHOST_NOT_FOUND)
		ADD_CODE_IF(WSATRY_AGAIN)
		ADD_CODE_IF(WSANO_RECOVERY)
		ADD_CODE_IF(WSANO_DATA)
	}
};//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void GetDescriptionSystemError(int error_id, CString &buf)
{
	if(error_id>INTERNET_ERROR_BASE && error_id<INTERNET_ERROR_LAST)
	{
		GetDescriptionWininetError(error_id,buf);
	}
	else if(error_id != 0)
	{
		aplGetDescriptionSystemError(error_id,buf);
		AddCodeSocketError(error_id,buf);
	}
	else
	{
		buf.Format(_T("code: %i"), error_id);
	}

}

void GetDescription_FILE_Error(int &error_id, CString &buffer)
{
	errno_t err;
	buffer=_T("");
	CString tmp;
	_get_errno( &err );
	error_id = err;
	switch(error_id)
	{
		ERR_2_TEXT_T(EPERM, APL_T("Operation not permitted"));
		ERR_2_TEXT_T(ENOENT, APL_T("No such file or directory"));
		ERR_2_TEXT_T(ESRCH, APL_T("No such process"));
		ERR_2_TEXT_T(EINTR, APL_T("Interrupted function"));
		ERR_2_TEXT_T(EIO, APL_T("I/O error"));
		ERR_2_TEXT_T(ENXIO, APL_T("No such device or address"));
		ERR_2_TEXT_T(E2BIG, APL_T("Argument list too long"));
		ERR_2_TEXT_T(ENOEXEC, APL_T("Exec format error"));
		ERR_2_TEXT_T(EBADF, APL_T("Bad file number"));
		ERR_2_TEXT_T(ECHILD, APL_T("No spawned processes"));
		ERR_2_TEXT_T(EAGAIN, APL_T("No more processes or not enough memory or maximum nesting level reached"));
		ERR_2_TEXT_T(ENOMEM, APL_T("Not enough memory"));
		ERR_2_TEXT_T(EACCES, APL_T("Permission denied"));
		ERR_2_TEXT_T(EFAULT, APL_T("Bad address"));
		ERR_2_TEXT_T(EBUSY, APL_T("Device or resource busy"));
		ERR_2_TEXT_T(EEXIST, APL_T("File exists"));
		ERR_2_TEXT_T(EXDEV, APL_T("Cross-device link"));
		ERR_2_TEXT_T(ENODEV, APL_T("No such device"));
		ERR_2_TEXT_T(ENOTDIR, APL_T("Not a directory"));
		ERR_2_TEXT_T(EISDIR,	APL_T("Is a directory"));
		ERR_2_TEXT_T(EINVAL,	APL_T("Invalid argument"));
		ERR_2_TEXT_T(ENFILE,	APL_T("Too many files open in system"));
		ERR_2_TEXT_T(EMFILE,	APL_T("Too many open files"));			
		ERR_2_TEXT_T(ENOTTY,	APL_T("Inappropriate I/O control operation"));
		ERR_2_TEXT_T(EFBIG,		APL_T("File too large"));
		ERR_2_TEXT_T(ENOSPC,	APL_T("No space left on device"));
		ERR_2_TEXT_T(ESPIPE,	APL_T("Invalid seek"));			
		ERR_2_TEXT_T(EROFS,		APL_T("Read-only file system"));
		ERR_2_TEXT_T(EMLINK,	APL_T("Too many links"));
		ERR_2_TEXT_T(EPIPE	,	APL_T("Broken pipe"));
		ERR_2_TEXT_T(EDOM	,	APL_T("Math argument"));			
		ERR_2_TEXT_T(ERANGE	,	APL_T("Result too large"));
		ERR_2_TEXT_T(EDEADLK,	APL_T("Resource deadlock would occur"));			
		//ERR_2_TEXT_T(EDEADLOCK,		APL_T("Same as EDEADLK for compatibility with older Microsoft C versions")); 
		ERR_2_TEXT_T(ENAMETOOLONG,	APL_T("Filename too long"));			
		ERR_2_TEXT_T(ENOLCK	,	APL_T("No locks available"));
		ERR_2_TEXT_T(ENOSYS	,	APL_T("Function not supported"));			
		ERR_2_TEXT_T(ENOTEMPTY,	APL_T("Directory not empty"));
		ERR_2_TEXT_T(EILSEQ	,	APL_T("Illegal BYTE sequence"));			
		ERR_2_TEXT_T(STRUNCATE,	APL_T("String was truncated"));
		default:buffer= _T("unknown           \n");break;
	}
}

void GetDescriptionHTTPAnswer(int answer, CString &buffer)
{
	buffer = _T("");
	switch(answer)
	{
		ERR_2_TEXT_T_HT(400, APL_T("Bad Request (Плохой запрос)\nСервер обнаружил в запросе клиента синтаксическую ошибку."));
		ERR_2_TEXT_T_HT(401, APL_T("Unauthorized (Не авторизован)\nЗапрос требует идентификации пользователя."));
		ERR_2_TEXT_T_HT(402, APL_T("Payment Required (Необходима оплата)\n"));
		ERR_2_TEXT_T_HT(403, APL_T("Forbidden (Запрещено)\nОбращение к ресурсу запрещено установками сервера"));
		ERR_2_TEXT_T_HT(404, APL_T("Not Found (Не найдено)\n"));
		ERR_2_TEXT_T_HT(405, APL_T("Method Not Allowed (Метод не применим)\n"));
		ERR_2_TEXT_T_HT(406, APL_T("Not Acceptable (Не приемлемо)\nЗапрошенный URI не может удовлетворить переданным в заголовке характеристикам."));
		ERR_2_TEXT_T_HT(407, APL_T("Proxy Authentication Required (Необходима авторизация прокси)\n"));
		ERR_2_TEXT_T_HT(408, APL_T("Request Timeout (Время ожидания истекло)\nВремя ожидания сервером передачи от клиента истекло."));
		ERR_2_TEXT_T_HT(409, APL_T("Conflict (Конфликт)\nЗапрос не может быть выполнен из-за конфликтного обращения к ресурсу."));
		ERR_2_TEXT_T_HT(410, APL_T("Gone (Удалён)\nТакой ответ сервер посылает, когда ресурс раньше был по указанному URI, но был удалён и теперь недоступен."));
		ERR_2_TEXT_T_HT(411, APL_T("Length Required (Необходима длина)\n"));
		ERR_2_TEXT_T_HT(412, APL_T("Precondition Failed (Условие «ложно»)\nВозвращается, если ни одно из условных полей заголовка запроса не было выполнено."));
		ERR_2_TEXT_T_HT(413, APL_T("Request Entity Too Large (Размер запроса слишком велик)\nВозвращается в случае, когда сервер отказывается обработать запрос по причине слишком большого размера тела запроса."));
		ERR_2_TEXT_T_HT(414, APL_T("Request-URL Too Long (Запрашиваемый URL слишком длинный)\nСервер не может обработать запрос из-за слишком длинного указанного URL."));
		ERR_2_TEXT_T_HT(415, APL_T("Unsupported Media Type (Неподдерживаемый тип данных)\n"));
		ERR_2_TEXT_T_HT(500, APL_T("Internal Server Error (Внутренняя ошибка сервера)\n"));
		ERR_2_TEXT_T_HT(501, APL_T("Not Implemented (Не реализовано)\nСервер не поддерживает возможностей, необходимых для обработки запроса."));
		ERR_2_TEXT_T_HT(502, APL_T("Bad Gateway (Плохой шлюз)\nПрокси-сервер получил сообщение о неудачном выполнении промежуточной операции."));
		ERR_2_TEXT_T_HT(503, APL_T("Service Unavailable (Сервис недоступен)\nСервер временно не имеет возможности обрабатывать запросы по техническим причинам;\nВозможно, прокси-сервер не может связаться с сервером приложений."));
		ERR_2_TEXT_T_HT(504, APL_T("Gateway Timeout (Шлюз не отвечает)\nПроверьте, что соединение с интернет работает"));
	}

}

void GetDescriptionWininetError(int error, CString &buffer)
{
	buffer = _T("");
	switch(error)
	{
		ERR_2_TEXT_T(ERROR_INTERNET_OUT_OF_HANDLES, APL_T("Ошибка при создании указателя. Необходимо перезапустить программу!"));
		ERR_2_TEXT_T(ERROR_INTERNET_TIMEOUT, APL_T("Время ожидания ответа от сервера истекло."));
		ERR_2_TEXT_T(ERROR_INTERNET_EXTENDED_ERROR,_T("An extended error was returned from the server. This is typically a string or buffer containing a verbose error message. Call InternetGetLastResponseInfo to retrieve the error text."));
		ERR_2_TEXT_T(ERROR_INTERNET_INTERNAL_ERROR, APL_T("Внутренняя ошибка системы. Необходимо перезапустить программу."));
		ERR_2_TEXT_T(ERROR_INTERNET_INVALID_URL, APL_T("Указан неверный интернет-адрес."));
		ERR_2_TEXT_T(ERROR_INTERNET_UNRECOGNIZED_SCHEME,_T("The URL scheme could not be recognized or is not supported."));
		ERR_2_TEXT_T(ERROR_INTERNET_NAME_NOT_RESOLVED, APL_T("Сервер с таким именем не найден."));
		ERR_2_TEXT_T(ERROR_INTERNET_PROTOCOL_NOT_FOUND, APL_T("Не удалось найти протокол, используемый для обмена данными."));
		ERR_2_TEXT_T(ERROR_INTERNET_INVALID_OPTION,_T("A request to InternetQueryOption or InternetSetOption specified an invalid option value."));
		ERR_2_TEXT_T(ERROR_INTERNET_BAD_OPTION_LENGTH,_T("The length of an option supplied to InternetQueryOption or InternetSetOption is incorrect for the type of option specified."));
		ERR_2_TEXT_T(ERROR_INTERNET_OPTION_NOT_SETTABLE,_T("The request option cannot be set, only queried."));
		ERR_2_TEXT_T(ERROR_INTERNET_SHUTDOWN, APL_T("Функции работы с интернетом отключены"));
		ERR_2_TEXT_T(ERROR_INTERNET_INCORRECT_USER_NAME, APL_T("Соединение с FTP сервером невозможно - неверно имя пользователя"));
		ERR_2_TEXT_T(ERROR_INTERNET_INCORRECT_PASSWORD, APL_T("Соединение с FTP сервером невозможно - неверный пароль"));
		ERR_2_TEXT_T(ERROR_INTERNET_LOGIN_FAILURE, APL_T("Соединение с FTP сервером невозможно - ошибка при проверке имени пользователя"));
		ERR_2_TEXT_T(ERROR_INTERNET_INVALID_OPERATION, APL_T("Неверна запрашиваемая операция"));
		ERR_2_TEXT_T(ERROR_INTERNET_OPERATION_CANCELLED, APL_T("Операция была прервана системой"));
		ERR_2_TEXT_T(ERROR_INTERNET_INCORRECT_HANDLE_TYPE, APL_T("Тип указателя не подходит для операции"));
		ERR_2_TEXT_T(ERROR_INTERNET_INCORRECT_HANDLE_STATE, APL_T("Состояние указателя не подходит для операции"));
		ERR_2_TEXT_T(ERROR_INTERNET_NOT_PROXY_REQUEST, APL_T("Запрос не может быть выполнен через прокси-сервер."));
		ERR_2_TEXT_T(ERROR_INTERNET_REGISTRY_VALUE_NOT_FOUND, APL_T("В реестре нет параметра, необходимого для выполнения операции"));
		ERR_2_TEXT_T(ERROR_INTERNET_BAD_REGISTRY_PARAMETER, APL_T("Параметр реестра, необходимый для выполнения операции, имеет неверный тип или неподходящее значение"));
		ERR_2_TEXT_T(ERROR_INTERNET_NO_DIRECT_ACCESS, APL_T("Прямое подключение к сети в настоящий момент невозможно"));
		ERR_2_TEXT_T(ERROR_INTERNET_NO_CONTEXT, APL_T("Подключение к сети невозможно из-за неверного контекста соединения"));
		ERR_2_TEXT_T(ERROR_INTERNET_NO_CALLBACK, APL_T("Подключение к сети невозможно - не задана функция обработки событий"));
		ERR_2_TEXT_T(ERROR_INTERNET_REQUEST_PENDING, APL_T("Операция не может быть выполнена, так как система ожидает выполнения предыдущей операции"));
		ERR_2_TEXT_T(ERROR_INTERNET_INCORRECT_FORMAT, APL_T("Неверный формат запроса."));
		ERR_2_TEXT_T(ERROR_INTERNET_ITEM_NOT_FOUND, APL_T("Запрашиваемый объект не найден"));
		ERR_2_TEXT_T(ERROR_INTERNET_CANNOT_CONNECT, APL_T("Не удалось соединиться с сервером. Убедитесь, что подключение к\nИнтернету установлено, а также проверьте настройки прокси-сервера."));
		ERR_2_TEXT_T(ERROR_INTERNET_CONNECTION_ABORTED, APL_T("Соединение с сервером было прервано"));
		ERR_2_TEXT_T(ERROR_INTERNET_CONNECTION_RESET,_T("The connection with the server has been reset."));
		ERR_2_TEXT_T(ERROR_INTERNET_FORCE_RETRY,_T("Calls for the Win32 Internet function to redo the request."));
		ERR_2_TEXT_T(ERROR_INTERNET_INVALID_PROXY_REQUEST, APL_T("Прокси сервер не может обработать данный запрос"));
		ERR_2_TEXT_T(ERROR_INTERNET_HANDLE_EXISTS,_T("The request failed because the handle already exists."));
		ERR_2_TEXT_T(ERROR_INTERNET_SEC_CERT_DATE_INVALID,_T("SSL certificate date that was received from the server is bad. The certificate is expired."));
		ERR_2_TEXT_T(ERROR_INTERNET_SEC_CERT_CN_INVALID,_T("SSL certificate common name (host name field) is incorrect. For example, if you entered www.server.com and the common name on the certificate says www.different.com."));
		ERR_2_TEXT_T(ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR,_T("The application is moving from a non-SSL to an SSL connection because of a redirect."));
		ERR_2_TEXT_T(ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR,_T("The application is moving from an SSL to an non-SSL connection because of a redirect."));
		ERR_2_TEXT_T(ERROR_INTERNET_MIXED_SECURITY,_T("Indicates that the content is not entirely secure. Some of the content being viewed may have come from unsecured servers."));
		ERR_2_TEXT_T(ERROR_INTERNET_CHG_POST_IS_NON_SECURE,_T("The application is posting and attempting to change multiple lines of text on a server that is not secure."));
		ERR_2_TEXT_T(ERROR_INTERNET_POST_IS_NON_SECURE,_T("The application is posting data to a sever that is not secure."));
		ERR_2_TEXT_T(ERROR_FTP_TRANSFER_IN_PROGRESS,_T("The requested operation cannot be made on the FTP session handle because an operation is already in progress."));
		ERR_2_TEXT_T(ERROR_FTP_DROPPED,_T("The FTP operation was not completed because the session was aborted."));
		ERR_2_TEXT_T(ERROR_GOPHER_PROTOCOL_ERROR,_T("An error was detected while parsing data returned from the gopher server."));
		ERR_2_TEXT_T(ERROR_GOPHER_NOT_FILE,_T("The request must be made for a file locator."));
		ERR_2_TEXT_T(ERROR_GOPHER_DATA_ERROR,_T("An error was detected while receiving data from the gopher server."));
		ERR_2_TEXT_T(ERROR_GOPHER_END_OF_DATA,_T("The end of the data has been reached."));
		ERR_2_TEXT_T(ERROR_GOPHER_INVALID_LOCATOR,_T("The supplied locator is not valid."));
		ERR_2_TEXT_T(ERROR_GOPHER_INCORRECT_LOCATOR_TYPE,_T("The type of the locator is not correct for this operation."));
		ERR_2_TEXT_T(ERROR_GOPHER_NOT_GOPHER_PLUS,_T("The requested operation can only be made against a Gopher+ server or with a locator that specifies a Gopher+ operation."));
		ERR_2_TEXT_T(ERROR_GOPHER_ATTRIBUTE_NOT_FOUND,_T("The requested attribute could not be located."));
		ERR_2_TEXT_T(ERROR_GOPHER_UNKNOWN_LOCATOR,_T("The locator type is unknown."));
		ERR_2_TEXT_T(ERROR_HTTP_HEADER_NOT_FOUND, APL_T("Не найден заголовок HTTP-запроса, необходимый для выполнения операции"));
		ERR_2_TEXT_T(ERROR_HTTP_DOWNLEVEL_SERVER, APL_T("Сервер не возвращает ни одного HTTP-заголовка ответа"));
		ERR_2_TEXT_T(ERROR_HTTP_INVALID_SERVER_RESPONSE, APL_T("Ответ сервера не поддается декодированию.\nВозможно, сервер не запущен или нет соединения с интернет"));
		ERR_2_TEXT_T(ERROR_HTTP_INVALID_HEADER, APL_T("Заголовок ответа имеет неизвестный формат"));
		ERR_2_TEXT_T(ERROR_HTTP_INVALID_QUERY_REQUEST, APL_T("Неверный запрос"));
		ERR_2_TEXT_T(ERROR_HTTP_HEADER_ALREADY_EXISTS, APL_T("Добавляемый HTTP-заголовок уже есть в запросе"));
		ERR_2_TEXT_T(ERROR_HTTP_REDIRECT_FAILED,_T("The redirection failed because either the scheme changed (for example, HTTP to FTP) or all attempts made to redirect failed (default is five attempts)."));
		default: buffer.Format(APL_T("\n( Код ошибки winninet: %i "), error); break;
	}
}


/*
TCHAR* GetDescriptionSocketError(int error)
{
	switch(error)
	{
		case WSAEINTR            :return _T("WSAEINTR          \n"
									  "Работа функции отменена при помощи функции WSACancelBlokingCall");break;
		case WSAEBADF            :return _T("WSAEBADF          \n...");break;
		case WSAEACCES           :return _T("WSAEACCES         \n"
									  "Указанный адрес-широковещательный, но перед вызовом не был уст. соотв. флаг");break;
		case WSAEFAULT           :return _T("WSAEFAULT         \n"
									  "Значение параметра namelen меньше\n размера структуры sockaddr\n"
									  "а для send: параметр buf указывает на \n чужое аресное пространство");break;
		case WSAEINVAL           :return _T("WSAEINVAL         \n"
									  "В WSAStartup: несоотв. версии DLL запрошенной;\nВ bind: сокет уже привязан к адресу;\n"
									  "В listen: ошибка привязки сокета\nВ accept: не была вызвана listen\n"
									  "В recv и send: сокет не подключен фнц-ей bind");break;
		case WSAEMFILE           :return _T("WSAEMFILE         \n"
									  "Израсходован весь запас свободных дескрипторов");break;
		case WSAEWOULDBLOCK      :return _T("WSAEWOULDBLOCK    \n"
									  "Сокет отмечен как неблокирующий \nно запрошенная операция приведет к блокировке ");break;
		case WSAEINPROGRESS      :return _T("WSAEINPROGRESS    \n"
									  "Выполняется блокирующая функция интерфейса WinSockets");break;
		case WSAEALREADY         :return _T("WSAEALREADY       \n...");break;
		case WSAENOTSOCK         :return _T("WSAENOTSOCK       \n"
									  "Указанный дескриптор не является сокетом");break;
		case WSAEDESTADDRREQ     :return _T("WSAEDESTADDRREQ   \n"
									  "Надо указать адрес получателя пакета");break;
		case WSAEMSGSIZE         :return _T("WSAEMSGSIZE       \n"
									  "При передаче в режиме датаграмм размер \nпакета превысил максимально допустимый\n"
									  "при приеме размер пакета превысил размер \nбуфера и был обрезан");break;
		case WSAEPROTOTYPE       :return _T("WSAEPROTOTYPE     \n"
									  "Указанный протокол несовместим с данным типом сокета");break;
		case WSAENOPROTOOPT      :return _T("WSAENOPROTOOPT    \n...");break;
		case WSAEPROTONOSUPPORT  :return _T("WSAEPROTONOSUPPORT\n"
									  "Указан неправильный протокол");break;
		case WSAESOCKTNOSUPPORT  :return _T("WSAESOCKTNOSUPPORT\n"
									  "Указанный тип сокета несовместим с указанным типом адреса");break;
		case WSAEOPNOTSUPP       :return _T("WSAEOPNOTSUPP     \n"
									  "Указанный сокет нельзя использовать в функциях,\n ориентированных на канал связи");break;
		case WSAEPFNOSUPPORT     :return _T("WSAEPFNOSUPPORT   \n...");break;
		case WSAEAFNOSUPPORT     :return _T("WSAEAFNOSUPPORT   \n"
									  "Указан неправильный тип адреса");break;
		case WSAEADDRINUSE       :return _T("WSAEADDRINUSE     \n"
									  "Указанный адреc уже используется");break;
		case WSAEADDRNOTAVAIL    :return _T("WSAEADDRNOTAVAIL  \n"
									  "Указанный адрес недоступен");break;
		case WSAENETDOWN         :return _T("WSAENETDOWN       \n"
									  "Сбой в сетевом П/О");break;
		case WSAENETUNREACH      :return _T("WSAENETUNREACH    \n"
									  "Из данного узла в данное время нет доступа к сети");break;
		case WSAENETRESET        :return _T("WSAENETRESET      \n"
									  "Необходимо сбросить соединение");break;
		case WSAECONNABORTED     :return _T("WSAECONNABORTED   \n"
									  "Сбой из-за слишком большой задержки\n или по другой причине");break;
		case WSAECONNRESET       :return _T("WSAECONNRESET     \n"
									  "Сброс соединения удаленным узлом");break;
		case WSAENOBUFS          :return _T("WSAENOBUFS        \n"
									  "Нет памяти для создания буфера");break;
		case WSAEISCONN          :return _T("WSAEISCONN        \n"
									  "Сокет уже находится в подключенном состоянии");break;
		case WSAENOTCONN         :return _T("WSAENOTCONN       \n"
									  "Сокет не подсоединен");break;
		case WSAESHUTDOWN        :return _T("WSAESHUTDOWN      \n"
									  "Сокет был закрыт функцией ShutDown");break;
		case WSAETOOMANYREFS     :return _T("WSAETOOMANYREFS   \n...");break;
		case WSAETIMEDOUT        :return _T("WSAETIMEDOUT      \n"
									  "При попытке устан. связи возникла задержка во времени");break;
		case WSAECONNREFUSED     :return _T("WSAECONNREFUSED   \n"
									  "Попытка установления связи была отвергнута");break;
		case WSAELOOP            :return _T("WSAELOOP          \n...");break;
		case WSAENAMETOOLONG     :return _T("WSAENAMETOOLONG   \n...");break;
		case WSAEHOSTDOWN        :return _T("WSAEHOSTDOWN      \n...");break;
		case WSAEHOSTUNREACH     :return _T("WSAEHOSTUNREACH   \n...");break;
		case WSAENOTEMPTY        :return _T("WSAENOTEMPTY      \n...");break;
		case WSAEPROCLIM         :return _T("WSAEPROCLIM       \n...");break;
		case WSAEUSERS           :return _T("WSAEUSERS         \n...");break;
		case WSAEDQUOT           :return _T("WSAEDQUOT         \n...");break;
		case WSAESTALE           :return _T("WSAESTALE         \n..");break;
		case WSAEREMOTE          :return _T("WSAEREMOTE        \n...");break;
		case WSAEDISCON          :return _T("WSAEDISCON        \n...");break;
		case WSASYSNOTREADY      :return _T("WSASYSNOTREADY    \n"
									  "Сетевое П/О не готово к работе");break;
		case WSAVERNOTSUPPORTED  :return _T("WSAVERNOTSUPPORTED\n"
									  "Функция не поддерживается данной реализацией WinSockets");break;
		case WSANOTINITIALISED   :return _T("WSANOTINITIALISED \n"
									  "Механизм надо инициировать функцией WSAStartup");break;
		// Authoritative Answer: Host not found
		case WSAHOST_NOT_FOUND   :return _T("WSAHOST_NOT_FOUND \n...");break;
		// Non-Authoritative: Host not found, or SERVERFAIL 
		case WSATRY_AGAIN        :return _T("WSATRY_AGAIN      \n...");break;

		// Non recoverable errors, FORMERR, REFUSED, NOTIMP 
		case WSANO_RECOVERY      :return _T("WSANO_RECOVERY    \n...");break;
		// Valid name, no data record of requested type 
		case WSANO_DATA          :return _T("WSANO_DATA        \n...");break;
		default					  	 :
			_sprintf(buf_for_default,"unknown WSA error %i\n",error);
			return buf_for_default;break;
	}
	return _T("");
};//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/


bool CreateSecurityAttributes (ACCESS_MASK amAccessRights,PSECURITY_ATTRIBUTES* ppSecurityAttributes)
{
	CaplStackLogger stack_logger(__APL_FUNC__);

	LPCTSTR m_class_info = _T("global CreateSecurityAttributes");

	bool r = false;
	PSECURITY_DESCRIPTOR pSecDesc = NULL;
	PSECURITY_ATTRIBUTES pSecAttr = NULL;
	PACL pDacl = NULL;
	PSID pSidEveryone = NULL;
	PSID pSidSystem = NULL;
	DWORD dwSidSize = SECURITY_MAX_SID_SIZE;
	EXPLICIT_ACCESS ExplicitAccess [2];
	DWORD dwResult = ERROR_SUCCESS;

	//
	// Check input parameters
	//

	if (ppSecurityAttributes==0) return false;
	*ppSecurityAttributes=0;

	try
	{
		//
		// Allocate security attributes
		//

		pSecAttr = (PSECURITY_ATTRIBUTES) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof (SECURITY_ATTRIBUTES) * 2);
		if (pSecAttr==0) APL_TRANSPORT_THROW_O(GetLastError());
		memset(pSecAttr,0,sizeof (SECURITY_ATTRIBUTES) * 2);

		//
		// Allocate security descriptor
		//

		pSecDesc = (PSECURITY_DESCRIPTOR) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, SECURITY_DESCRIPTOR_MIN_LENGTH * 2);
		if (pSecDesc==0) APL_TRANSPORT_THROW_O(GetLastError());
		memset(pSecDesc,0,SECURITY_DESCRIPTOR_MIN_LENGTH * 2);

		//
		// Initialize security descriptor
		//

		if (InitializeSecurityDescriptor (pSecDesc,SECURITY_DESCRIPTOR_REVISION)==0)APL_TRANSPORT_THROW_O((int)GetLastError);

		//
		// Allocate storage for SIDs
		//

		pSidEveryone = (PSID) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, dwSidSize);
		if (pSidEveryone==0) APL_TRANSPORT_THROW_O(GetLastError());
		memset(pSidEveryone,0,dwSidSize);

		pSidSystem = (PSID) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, dwSidSize);
		if (pSidSystem==0) APL_TRANSPORT_THROW_O(GetLastError());
		memset(pSidSystem,0,dwSidSize);

		//
		// Get SID for any user
		//
		if (CreateWellKnownSid (WinWorldSid,NULL,pSidEveryone,&dwSidSize)==FALSE) APL_TRANSPORT_THROW_O(GetLastError());

		//
		// Get SID for 'SYSTEM' account
		//
		if (CreateWellKnownSid (WinLocalSystemSid,NULL,pSidSystem,&dwSidSize)==FALSE) APL_TRANSPORT_THROW_O(GetLastError());

		memset(ExplicitAccess,0,sizeof (EXPLICIT_ACCESS) * 2);
		//
		// 'Everyone' account
		//

		ExplicitAccess [0].grfAccessPermissions = amAccessRights;
		ExplicitAccess [0].grfAccessMode = GRANT_ACCESS;
		ExplicitAccess [0].grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE | SUB_CONTAINERS_AND_OBJECTS_INHERIT;
		ExplicitAccess [0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
		ExplicitAccess [0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
		ExplicitAccess [0].Trustee.ptstrName  = (LPTSTR) pSidEveryone;

		//
		// 'SYSTEM' account
		//

		ExplicitAccess [1].grfAccessPermissions = amAccessRights;
		ExplicitAccess [1].grfAccessMode = GRANT_ACCESS;
		ExplicitAccess [1].grfInheritance = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE | SUB_CONTAINERS_AND_OBJECTS_INHERIT;
		ExplicitAccess [1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
		ExplicitAccess [1].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
		ExplicitAccess [1].Trustee.ptstrName  = (LPTSTR) pSidSystem;

		//
		// Set up ACL object
		//

		dwResult = SetEntriesInAcl (sizeof (ExplicitAccess) / sizeof (EXPLICIT_ACCESS),ExplicitAccess,NULL,&pDacl);
		if (dwResult != ERROR_SUCCESS) APL_TRANSPORT_THROW_O((int)dwResult);

		//
		// Update security descriptor
		//

		if (SetSecurityDescriptorDacl (pSecDesc, TRUE, pDacl, TRUE)==FALSE) APL_TRANSPORT_THROW_O(GetLastError());

		//
		// Compose security attributes
		//

		pSecAttr -> bInheritHandle = TRUE;
		pSecAttr -> nLength = sizeof (SECURITY_ATTRIBUTES);
		pSecAttr -> lpSecurityDescriptor = pSecDesc;

		//
		// Return security security
		//

		*ppSecurityAttributes = pSecAttr;

		//
		// Success ;)
		//
	}
	catch(SaplErrorDescription err)
	{
		CString str1,str2;
		GetDescriptionSystemError(err.m_err_code,str1);
		str2 = stack_logger.GetCurrStackList();
		LOG_ERROR(str1+str2+err.Print());
		if(MainModuleLog!=0){MainModuleLog->PrintMyEmergencyLog(0,_T("CreateSecurityAttributes - catch(SaplErrorDescription err)"));}

		if(pSecAttr!=0){HeapFree (GetProcessHeap (), 0, pSecAttr);pSecAttr=0;}
		if(pSecDesc!=0){HeapFree (GetProcessHeap (), 0, pSecDesc);pSecDesc=0;}
		if(pSidEveryone!=0){HeapFree (GetProcessHeap (), 0, pSidEveryone);pSidEveryone=0;}
		if(pSidSystem!=0){HeapFree (GetProcessHeap (), 0, pSidSystem);pSidSystem=0;}
		if(pDacl!=0){LocalFree (pDacl);pDacl=0;}
		return false;
	}

	return true;
}


static const std::string base64_chars = 
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";


static inline bool is_base64(unsigned char c)
{
	return (isalnum(c) || (c == '+') || (c == '/'));
}

// при изменении - в линуховом варианте тоже поменять!
CString EncodeToBase64( CaplDataBuf &dbuf_in)
{
	unsigned int in_len = dbuf_in.m_Size;
	unsigned char const* BYTEs_to_encode = (unsigned char const*)dbuf_in.m_data;
	return EncodeToBase64( BYTEs_to_encode, in_len);
}

// при изменении - в линуховом варианте тоже поменять!
//UUUU проверить работу с юникодными строками
CString EncodeToBase64( const BYTE* BYTEs_to_encode, unsigned int in_len)
{
	CString cs_ret;
	std::string c_ret = "";
	int i = 0;
	int j = 0;
	unsigned char char_array_3[3];
	unsigned char char_array_4[4];


	while (in_len--) 
	{
		char_array_3[i++] = *(BYTEs_to_encode++);
		if (i == 3) 
		{
			char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
			char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
			char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
			char_array_4[3] = char_array_3[2] & 0x3f;

			for(i = 0; (i <4) ; i++)
				c_ret += base64_chars[char_array_4[i]];
			i = 0;
		}
	}

	if (i)
	{
		for(j = i; j < 3; j++)
			char_array_3[j] = '\0';

		char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
		char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
		char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
		char_array_4[3] = char_array_3[2] & 0x3f;

		for (j = 0; (j < i + 1); j++)
			c_ret += base64_chars[char_array_4[j]];

		while((i++ < 3))
			c_ret += '=';

	}

#ifdef _UNICODE
	CaplStringAdapter str_ad(c_ret.c_str());
	cs_ret = (LPCTSTR)str_ad;
#else // #ifdef _UNICODE
	cs_ret = c_ret.c_str();
#endif // #ifdef _UNICODE

	return cs_ret;
}

// при изменении - в линуховом варианте тоже поменять!
//UUUU проверить работу с юникодными строками
bool DecodeFromBase64( const char* encoded_string, DWORD size_string, CaplDataBuf &dbuf_out)
{
	int in_len = size_string;
	int i = 0;
	int j = 0;
	int in_ = 0;
	unsigned char char_array_4[4], char_array_3[3];
	//UUUU опасное место
	if(in_len == 0) in_len = strlen(encoded_string);
	
	dbuf_out.Clear();

	while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) 
	{
		char_array_4[i++] = encoded_string[in_]; in_++;
		if (i ==4) 
		{
			for (i = 0; i <4; i++)
				char_array_4[i] = base64_chars.find(char_array_4[i]);

			char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
			char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
			char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

			for (i = 0; (i < 3); i++)
			{
				dbuf_out.Add(&(char_array_3[i]),1);
			}
			i = 0;
		}
	}

	if (i) 
	{
		for (j = i; j <4; j++)
			char_array_4[j] = 0;

		for (j = 0; j <4; j++)
			char_array_4[j] = base64_chars.find(char_array_4[j]);

		char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
		char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
		char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

		for (j = 0; (j < i - 1); j++)
		{
			dbuf_out.Add(&(char_array_3[j]),1);
		}
	}

	return true;
}

CString GetDescriptionTransportCommand(int command)
{
	CString description = _T("???");

	switch(command)
	{
	case CMDGetServerStatus:
		description = APL_T("CMDGetServerStatus (0) (K->S клиент запрашивает у сервера статус)");
		break;
	case CMDConnectionOK:
		description = APL_T("CMDConnectionOK (1) (S->K соединение установлено успешно)");
		break;
	case CMDGetVersionBuffer:
		description = APL_T("CMDGetVersionBuffer (3) (К->S Ожидается буфер с идентификацией версии)");
		break;
	case CMDTooManyClients:
		description = APL_T("CMDTooManyClients (5) (S->K сервер приложений достиг максимума подключений)");
		break;
	case CMDErrorServerProcessed:
		description = APL_T("CMDErrorServerProcessed (10) (S->K ошибка на сервере при обработке данных)");
		break;
	case CMDErrorReceiveData:
		description = APL_T("CMDErrorReceiveData (11) (S->K ошибка при приеме данных)");
		break;
	case CMDErrorInitServer:
		description = APL_T("CMDErrorInitServer (12) (S->K ошибка инициализации сервера)");
		break;
	case CMDErrorMissedSocketType:
		description = APL_T("CMDErrorMissedSocketType (13) (S->K неверный тип сокета (для слушания или для данных))");
		break;
	case CMDErrorMissedData:
		description = APL_T("CMDErrorMissedData (14) (S->K неверные входные данные)");
		break;
	case CMDDisconnect:
		description = APL_T("CMDDisconnect (15) (К->S разрываем связь)");
		break;
	case CMDBreakOperation:
		description = APL_T("CMDBreakOperation (20) (К->S надо прервать операцию нити, указатель на которую идет в следующих 4 байтах)");
		break;
	case CMDSetProgress:
		description = APL_T("CMDSetProgress (21) (S->K в следующих 4 байтах идет процент выполненной сервером работы)");
		break;
	case CMDDeadTime:
		description = APL_T("CMDDeadTime (25) (S->K сервер ожидает выключения)");
		break;
	case CMDRenewClientState:
		description = APL_T("CMDRenewClientState (26) (S->K сервер запрашивает, жив ли клиент)");
		break;
	case CMDBreakOk:
		description = APL_T("CMDBreakOk (27) (S->K) Прерывание операции выполнено успешно");
		break; 
	case CMDSetProgressText:
		description = APL_T("CMDSetProgressText (28) (S->K) в следующих 4 байтах идет длинна строчки, потом строчка текста");
		break;
	case CMDNeedReconnect:
		description = APL_T("CMDNeedReconnect (40) (S->K) команда клиенту переконнектиться");
		break;
	case CMDNeedRelogin:
		description = APL_T("CMDNeedReconnect (40) (S->K) команда клиенту перелогиниться");
		break;
	case CMDConnectionOKNew:
		description = APL_T("CMDConnectionOKNew (101) (S->K) соединение установлено успешно (новый вариант)");
		break;
	case CMDCriticalError:
		description = APL_T("CMDCriticalError (102) (S->K) критическая ошибка, дошедшая до функции prossed транспорта");
		break;
	default:
		description.Format(APL_T("Команда (%i) - описание не задано"));
	}
	return description;
}

DWORD aplReadServUILang(bool only_read)
{
	CRegKey key;
	DWORD dval;
	DWORD curUILang = 0;

	// читаем из раздела HKLM
	if(key.Open(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\CALS Centre \"Applied Logistic\"\\Server"), KEY_QUERY_VALUE) == ERROR_SUCCESS)
	{
		if(key.QueryDWORDValue(_T("DefLangCode"), dval) == ERROR_SUCCESS)
		{
			curUILang = dval;
		}
	}

	if(only_read)
		return curUILang;

	if (curUILang == 0)
		curUILang = CaplTranslate::GetGlobalUILang();

	if(curUILang == 0)
	{
		//При первом запуске установим тек. язык равный тек. локали, если это русский или английский
		LCID cur_locale = GetThreadLocale (); 
		curUILang = APL_ALP_LANG_CODE_ENGLISH; //для неизвестной локали тек. язык - английский
		if(cur_locale == 0x0419)// Текущий язык - русский
		{
			curUILang = APL_ALP_LANG_CODE_RUSSIAN;
		}
	}

	OSVERSIONINFO os;
	os.dwOSVersionInfoSize = sizeof(os);
	GetVersionEx(&os);
	if(os.dwMajorVersion>=6)
	{
		SetThreadLocale(MAKELCID(MAKELANGID(curUILang, SUBLANG_DEFAULT), SORT_DEFAULT));
		SetThreadUILanguage(MAKELANGID(curUILang, SUBLANG_NEUTRAL));
	}
	else
	{
		SetThreadLocale(MAKELCID(MAKELANGID(curUILang, SUBLANG_DEFAULT), SORT_DEFAULT));
	}
	return curUILang;
}

bool aplSaveServUILang(DWORD curUILang)
{
	CRegKey key;

	// читаем из раздела HKLM
	if(key.Create(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\CALS Centre \"Applied Logistic\"\\Server"), REG_NONE, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS)
		== ERROR_SUCCESS)
	{
		key.SetDWORDValue(_T("DefLangCode"), curUILang);
		// если HKLM доступен - очищаем локальный профиль чтобы не было путаницы
		CWinApp *app=AfxGetApp(); // При вызове из ActiveX возвращает 0
		if(0!=app)app->WriteProfileString(_T("Settings"), _T("DefLangCode"), NULL);
		// удаляем ключ из HKCU 
		if(key.Open(HKEY_CURRENT_USER, _T("SOFTWARE\\CALS Centre \"Applied Logistic\"\\Server"), KEY_ALL_ACCESS) == ERROR_SUCCESS)
			key.DeleteValue(_T("DefLangCode"));

		return true;
	}
	return false;
}


void SUriElement::Set(SUriElement* prev)
{
	Clear();
	if(prev==0) return;
	m_base = prev->m_base;
	m_source = prev->m_source;
	int i;
//	for(i=0;i<prev->m_vals.GetCount();i++)m_vals.Add(prev->m_vals[i]);
	for(i=0;i<prev->m_params.GetSize();i++)m_params.Add(prev->m_params.GetAt(i)->str, prev->m_params.GetAt(i)->val);
}

void SUriElement::Clear()
{
	m_base = _T("");
//	m_vals.RemoveAll();
	m_params.Clear();
	m_source = _T("");
}

// void SUriData::Set(SUriData *prev)
// {
// 	m_type=_T("");
// 	if(prev==0) return;
// 	m_type = prev->m_type;
// 	SUriElement* el,  *prev_el;
// 	for(int i=0;i<prev->m_params.GetSize();i++)
// 	{
// 		prev_el = prev->m_params.GetAt(i);
// 		el = new SUriElement(prev_el);
// 		m_params.Add(el);
// 	}
// }

#define CODEPAGE_Win_Cyrillic 1251
#define CODEPAGE_UTF_8 65001

CString ConvertFromUTF8toWIN1251(CString sSource)
{
	IMultiLanguage *pMultiLanguage;
	IMLangConvertCharset* convertCharset;
	UINT sizeT = 0; 
	UINT sizeS = sSource.GetLength();
	CString sTarget;

	CoInitialize(NULL);
	HRESULT hr = CoCreateInstance(CLSID_CMultiLanguage, NULL,  CLSCTX_INPROC_SERVER, IID_IMultiLanguage, (void**)&pMultiLanguage);
	if (hr == S_OK)
	{
		hr = pMultiLanguage->CreateConvertCharset(CODEPAGE_UTF_8, CODEPAGE_Win_Cyrillic, MLCONVCHARF_NCR_ENTITIZE, &convertCharset);
		if (hr == S_OK)
		{
			// вычисление размера результата
			convertCharset->DoConversion((BYTE*)sSource.GetBuffer(), &sizeS, NULL, &sizeT); 
			sTarget.GetBufferSetLength(sizeT);

			// сама перекодировка
			convertCharset->DoConversion((BYTE*)sSource.GetBuffer(), &sizeS, (BYTE*)sTarget.GetBuffer(), &sizeT); 
		}
	}

	CoUninitialize();
	return sTarget;
}

CString ConvertFromWIN1251toUTF8(CString sSource)
{
	IMultiLanguage *pMultiLanguage;
	IMLangConvertCharset* convertCharset;
	UINT sizeT = 0; 
	UINT sizeS = sSource.GetLength();
	CString sTarget;

	CoInitialize(NULL);
	HRESULT hr = CoCreateInstance(CLSID_CMultiLanguage, NULL,  CLSCTX_INPROC_SERVER, IID_IMultiLanguage, (void**)&pMultiLanguage);
	if (hr == S_OK)
	{
		hr = pMultiLanguage->CreateConvertCharset(CODEPAGE_Win_Cyrillic, CODEPAGE_UTF_8, MLCONVCHARF_NCR_ENTITIZE, &convertCharset);
		if (hr == S_OK)
		{
			// вычисление размера результата
			convertCharset->DoConversion((BYTE*)sSource.GetBuffer(), &sizeS, NULL, &sizeT); 
			sTarget.GetBufferSetLength(sizeT);

			// сама перекодировка
			convertCharset->DoConversion((BYTE*)sSource.GetBuffer(), &sizeS, (BYTE*)sTarget.GetBuffer(), &sizeT); 
		}
	}

	CoUninitialize();
	return sTarget;
}

void aplReplaceLog2User(CString &strInternalCons)
{
	strInternalCons.Replace(_T("\t\t"), _T("\t"));
	strInternalCons.Replace(_T("\tDescr: Core: "),	APL_T("\tМодуль: "));
	strInternalCons.Replace(_T("\tDescr: "),			APL_T("\t"));
	strInternalCons.Replace(_T("\tDescr:"),			APL_T("\t"));
	strInternalCons.Replace(_T("\tAdd: "),			APL_T("\t"));
	strInternalCons.Replace(_T("\tAdd:"),			APL_T("\t"));
	strInternalCons.Replace(_T("\tSrc:"),			APL_T("\tИсходный код:"));
	strInternalCons.Replace(_T("\tFun:"),			APL_T("\tФункция:"));
	strInternalCons.Replace(_T("\tDictionary file:"), APL_T("\tФайл словаря:"));
	strInternalCons.Replace(_T("Core: "),			APL_T("\tМодуль: "));
	strInternalCons.Replace(_T(" Err: "),			APL_T(" :"));
}


///////////////////////////////////
/// Собственные методы (только хеширование)
bool builtInGetHash(LPCTSTR lpMethod, CaplDataBuf &data_in, CString &csHashPrev, CString &errMess);

bool internalHashGetMethod(LPCTSTR lpHash, CString &csMethod)
{
	CString csHash = lpHash;
	csMethod = _T("Unknown");

	if(csHash.GetLength() < 8) return false; // минимальный заголовок ^^SHA1^^
	if(csHash.Left(2) != _T("^^")) return false;
	int pos2 = csHash.Find(_T("^^"), 2);
	if(pos2==-1) return false;
	csMethod = csHash.Mid(2, pos2-2);
	if(csMethod != _T("SHA1") && csMethod != _T("GOST256") && csMethod != _T("GOST512"))
	{
		csMethod = _T("Unknown");
		return false;
	}
	return true;
}


// Вычисление хэша
bool internalCryptGetHash(LPCTSTR lpMethod, LPCTSTR lpStrIn, CString &csHash, CString &errMess)
{
	CString csMethod = lpMethod;
	CaplDataBuf dbuf;
	errMess = _T("");
	csHash = _T("");

	// Временное решение, чтобы не поломать сохранение паролей
	if(csMethod == _T("APL"))
	{
		csHash = lpStrIn;
		return true;
	}
	if(csMethod != _T("SHA1") && csMethod != _T("GOST256") && csMethod != _T("GOST512"))
	{
		errMess.Format(APL_T("Метод '%s' не поддерживается!"), (LPCTSTR) lpMethod);
		return false;
	}

	// Хэш считаем по строкам в кодировке UTF8
#ifdef _UNICODE
	CaplStringAdapter ad2((LPCWSTR)lpStrIn);
#else
	CaplStringAdapter ad1 = lpStrIn;
	CaplStringAdapter ad2((LPCWSTR)ad1);
	ad2.m_bIsAnsiUtf8 = true;
#endif
	const char* ccData = (const char*)ad2;
	dbuf.Add(ccData, strlen(ccData));

	CString csHashPrev;
	if(!builtInGetHash(lpMethod, dbuf, csHashPrev, errMess))
		return false;

	csHash.Format(_T("^^%s^^%s"), lpMethod, (LPCTSTR)csHashPrev);

	return true;
}


// Встроенное вычисление хешей
bool builtInGetHash(LPCTSTR lpMethod, CaplDataBuf &data_in, CString &csHashPrev, CString &errMess)
{
	CString csMethod = lpMethod, str2;
	errMess = _T("");
	csHashPrev = _T("");

	if(csMethod == _T("SHA1"))
	{
		HCRYPTPROV hProv;
		HCRYPTHASH hHash;
		if(CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT|CRYPT_SILENT)!=TRUE)
		{
			GetDescriptionSystemError(GetLastError(), errMess);
			return false;
		}
		if(CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash)!=TRUE)
		{
			GetDescriptionSystemError(GetLastError(), errMess);
			return false;
		}
		if(CryptHashData(hHash, (LPBYTE)data_in.GetBuffer(),data_in.m_Size, 0)!=TRUE)
		{
			GetDescriptionSystemError(GetLastError(), errMess);
			return false;
		}
		/* Get Size of Hash*/
		BYTE dBufOut[64];
		DWORD dwSize=64;
		if(CryptGetHashParam(hHash, HP_HASHVAL, dBufOut, &dwSize, 0)!=TRUE)
		{
			GetDescriptionSystemError(GetLastError(), errMess);
			return false;
		}
		for(DWORD i = 0; i < dwSize; i++)
		{
			str2.Format(_T("%02x"), dBufOut[i]);
			csHashPrev+=str2;
		}

		CryptDestroyHash(hHash);
		CryptReleaseContext(hProv, 0);

// 		QCryptographicHash hash(QCryptographicHash::Sha1);
// 		hash.addData((const char *)data_in.data(),data_in.size());
// 		QByteArray baHash=hash.result();
// 		csHashPrev = QS2CS(QString(baHash.toHex()).toUpper());

	}
	else if(csMethod == _T("GOST256"))
	{
		csHashPrev = GetHashString_Stribog((const char *)data_in.GetBuffer(),data_in.m_Size, 256);
	}
	else if(csMethod == _T("GOST512"))
	{
		csHashPrev = GetHashString_Stribog((const char *)data_in.GetBuffer(),data_in.m_Size, 512);
	}
	else
	{
		errMess.Format(APL_T("Метод '%s' не поддерживается"), (LPCTSTR) lpMethod);
		return false;
	}

	csHashPrev.MakeUpper();

	return true;
}

CString DeleteNon1251Symbols(LPCSTR str_in)
{
	CString str;
	int len = strlen(str_in);

	for(int i=0; i<len; i++)
	{
		BYTE c=str_in[i];
		if(((c>=32 && c<127) || (c>=0x91 &&c<=0x94) || c==0xa7 || c==0xa8 || 
												c==0xa9 || c==0xab || c==0xb1 || c==0xb8 || c==0xb9  || c==0xbb || (c>=0xc0 && c<=0xff)))
			str+=c;
	}
	return str;
}


/**
	@brief aplCheckPassword Проверяет закодированный пароль на корректность
	@param pwdClient закодированный пароль, пришедший от клиента.
	@param pwdDb закодированный пароль, прочитанный из БД.
	@return true если пароли совпадают и false если не совпадают
*/
bool aplCheckPassword(LPCTSTR _pwdClient, LPCTSTR _pwdDb)
{
	//Заметки.
	// Для старых клиентов pwdClient может быть закодирован старым методом.
	// pwdDb может быть не закодирован (учтено) или закодирован старым методом (надо учесть).

	CString pwdDb(_pwdDb), pwdDbEnc;

	// Если оба пароля незакодированы, то кодировать их незачем
	if(pwdDb == _pwdClient)
		return true;

	if(pwdDb.GetLength() == 0 || (pwdDb.GetLength()>0 && pwdDb[0]!=_T('^')))
	{
		// Если пароль в базе не зашифрован - шифруем
		aplTranslateK(pwdDb, pwdDbEnc);
	}
	else pwdDbEnc = pwdDb;

	if(pwdDbEnc == _pwdClient)
		return true;
	
	CString pwdClient(_pwdClient), pwdClientEnc;
	// Юникодный клиент MFC временно присылает незакодированные пароли
	// QTшный клиент (Прагматика и т.п.) тоже
	if(pwdClient[0] != _T('^'))
	{
		aplTranslateK(pwdClient, pwdClientEnc);
		return pwdClientEnc == pwdDbEnc;
	}
	return false;
}

int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
	switch (uMsg)
	{
	case BFFM_INITIALIZED:
		SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)lpData);
	case BFFM_SELCHANGED:
		{
			TCHAR *sBuffer = (TCHAR*)new TCHAR[MAX_PATH];
			if( SHGetPathFromIDList((LPCITEMIDLIST)lParam, sBuffer) )
				SendMessage(hwnd, BFFM_SETSTATUSTEXT, FALSE, (LPARAM)sBuffer);

			delete [] sBuffer;
		}
	}
	return 0;
}

CString aplSelectFolder(HWND hWnd, LPCTSTR start_dir, LPCTSTR title)
{
	CString strRet(_T(""));
	CString titleConst(APL_T("Выберите папку"));
	LPITEMIDLIST selected_d = 0;
	CoInitialize(0);
	char			szPath[MAX_PATH];
	TCHAR			szTPath[MAX_PATH];
	OLECHAR			olePath[MAX_PATH];
	LPSHELLFOLDER	pDesktopFolder;
	ULONG			chEaten;
	ULONG			dwAttributes;
	HRESULT			hr;
	sprintf(szPath,"::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"); // корневой узел компьютера (рабочий стол)
	if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
	{
		MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1, olePath, MAX_PATH);
		hr = pDesktopFolder->ParseDisplayName(NULL, NULL, olePath, &chEaten, &selected_d, &dwAttributes);
		if (!FAILED(hr))
		{

			BROWSEINFO lpbi;
			lpbi.hwndOwner = hWnd; 
			lpbi.pidlRoot = selected_d; 
			lpbi.pszDisplayName = new TCHAR[MAX_PATH]; 
			if(CString(title).IsEmpty())
				lpbi.lpszTitle= titleConst; 
			else
				lpbi.lpszTitle= title; 
			lpbi.ulFlags = BIF_DONTGOBELOWDOMAIN | BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT ; //| BIF_NEWDIALOGSTYLE; 
			lpbi.lpfn=BrowseCallbackProc; 
			lpbi.lParam=(LPARAM)start_dir; 

			selected_d = SHBrowseForFolder(&lpbi); 
			if(selected_d != 0)
			{
				BOOL bres = SHGetPathFromIDList(selected_d, szTPath);
				strRet = szTPath;
			}

			LPMALLOC ppMalloc;
			hr = SHGetMalloc(&ppMalloc);
			ppMalloc->Free(selected_d);
			pDesktopFolder->Release();
			delete lpbi.pszDisplayName;

			//AfxMessageBox(szPath, MB_OK);
		}
	}
	CoUninitialize();

	return strRet;
}

CString aplSelectComputer(HWND hWnd, LPCTSTR title)
{
	CString strRet(_T(""));
	CString titleConst(APL_T("Выбор сервера в сети"));
	LPITEMIDLIST selected_d = 0;
	CoInitialize(0);
	char			szPath[MAX_PATH];
//	TCHAR			szTPath[MAX_PATH];
	OLECHAR			olePath[MAX_PATH];
	LPSHELLFOLDER	pDesktopFolder;
	ULONG			chEaten;
	ULONG			dwAttributes;
	HRESULT			hr;
	sprintf(szPath,"::{208D2C60-3AEA-1069-A2D7-08002B30309D}"); // корневой узел сетевого окружения
	if (SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder)))
	{
		MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szPath, -1, olePath, MAX_PATH);
		hr = pDesktopFolder->ParseDisplayName(NULL, NULL, olePath, &chEaten, &selected_d, &dwAttributes);
		if (!FAILED(hr))
		{

			BROWSEINFO lpbi;
			lpbi.hwndOwner = hWnd; 
			lpbi.pidlRoot = selected_d; 
			lpbi.pszDisplayName = new TCHAR[MAX_PATH]; 
			if(CString(title).IsEmpty())
				lpbi.lpszTitle= titleConst; 
			else
				lpbi.lpszTitle= title; 
			lpbi.ulFlags = BIF_BROWSEFORCOMPUTER; 
			lpbi.lpfn=0; 
			lpbi.lParam=0; 

			selected_d = SHBrowseForFolder(&lpbi); 
			if(selected_d != 0)
			{
				//BOOL bres = SHGetPathFromIDList(selected_d, szTPath);
				//strRet = szTPath;
				strRet = lpbi.pszDisplayName;

				// windows 7 выдает здесь не только имя компьютера, но и описание компьютера
				// а нам нужно только имя. Между именем и комментарием идет пробел
				int pos = strRet.Find(_T(" "));
				if(pos!=-1)
				{
					strRet.Delete(pos,1000);
				}
			}

			LPMALLOC ppMalloc;
			hr = SHGetMalloc(&ppMalloc);
			ppMalloc->Free(selected_d);
			pDesktopFolder->Release();
			delete lpbi.pszDisplayName;

			//AfxMessageBox(szPath, MB_OK);
		}
	}
	CoUninitialize();

	return strRet;

}

