// AutoUpd.cpp

#include "stdafx.h"
#include <direct.h>
#include <ERRNO.H>
#include <process.h>

#include <atlbase.h>

#include "AutoUpdate.h"
#include "OptionsDlg.h"
#include "UpdateDlg.h"
#include "UpdateMesDlg.h"
#include "ChekUpdatesDlg.h"
#include <aplNetStepData.h>
#include <apl_gui.h>

LPCTSTR global_name_param_file = _T("param.upd");
LPCTSTR global_name_del_file = _T("del_files.upd");
LPCTSTR global_name_start_file = _T("start.upd");

#ifdef _UNICODE
aplTextEncoding newFileCoding = aplUTF8;
#else
aplTextEncoding newFileCoding = aplANSI;
#endif

CString GetExePath()
{
	CString strExePath;
	HMODULE hModule = GetModuleHandle(NULL);
	TCHAR szModuleName[2048];	

	if (hModule != NULL)
	{
		if (GetModuleFileName(hModule, szModuleName, 2048) > 0)
		{
			CString strBuffer;

			strBuffer = szModuleName;
			strExePath = strBuffer.Left(strBuffer.ReverseFind(_T('\\')) + 1);
		}
	}

	return strExePath;
}

inline void CheckExeDll(CString &name, bool &has_exe)
{
	if(has_exe)return;
	if(name.Right(4).CompareNoCase(_T(".exe"))==0){has_exe=true;return;}
	if(name.Right(4).CompareNoCase(_T(".dll"))==0){has_exe=true;}
}
//*********************************************************************************************
// Копируем дату с одного файла на другой
bool CopyFileTime(LPCTSTR sFileSrc,LPCTSTR sFileDst)
{
	if(0==sFileSrc || 0== sFileDst) return false;
	if(_T('\0')==sFileSrc[0] || _T('\0')== sFileDst[0]) return false;

	FILETIME ctime={0,0}, mtime={0,0}, atime={0,0}; 

	HANDLE hSrc=CreateFile(sFileSrc,0,0,NULL,OPEN_EXISTING,0,NULL);
	if(0==hSrc) return false;
	BOOL b=GetFileTime(hSrc, &ctime, &atime, &mtime);
	CloseHandle(hSrc);
	if(!b) return false;

	if(0==ctime.dwHighDateTime && 0==ctime.dwLowDateTime ) ctime=mtime;
	if(0==atime.dwHighDateTime && 0==atime.dwLowDateTime ) atime=mtime;


	HANDLE hDst=CreateFile(sFileDst,FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ|FILE_SHARE_WRITE,
		NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	if(0==hDst) return false;
	b=SetFileTime(hDst, &ctime,&atime, &mtime);
	CloseHandle(hDst);
	if(!b) return false;

	return true;
}
//*********************************************************************************************

#ifndef TRACE_TO_FILE
#define OPEN_FILE {CStdioFile TRACE_TO_FILE_file;\
	CString TRACE_TO_FILE_dict_path = _T("");\
	TRACE_TO_FILE_dict_path = GetExePath();\
	if(TRACE_TO_FILE_file.Open(TRACE_TO_FILE_dict_path+_T("AutoUpdate.log"), CFile::modeCreate|CFile::modeWrite))\
{COleDateTime TRACE_TO_FILE_time = COleDateTime::GetCurrentTime(); CString TRACE_TO_FILE_str; aplGetFormatedDate(TRACE_TO_FILE_time, TRACE_TO_FILE_str);\
	CString MGR_INIT_str; MGR_INIT_str.LoadString(IDS_MGR_INITIALIZING);\
	TRACE_TO_FILE_file.SeekToEnd(); TRACE_TO_FILE_file.WriteString(MGR_INIT_str);TRACE_TO_FILE_file.WriteString(TRACE_TO_FILE_str);TRACE_TO_FILE_file.WriteString(_T("\n"));TRACE_TO_FILE_file.Close();}}
#define TRACE_TO_FILE(TRACE_TO_FILE_str)	{CStdioFile TRACE_TO_FILE_file;\
	CString TRACE_TO_FILE_dict_path = _T("");\
	TRACE_TO_FILE_dict_path = GetExePath();\
	if(TRACE_TO_FILE_file.Open(TRACE_TO_FILE_dict_path+_T("AutoUpdate.log"),CFile::modeWrite))\
{TRACE_TO_FILE_file.SeekToEnd(); TRACE_TO_FILE_file.WriteString(COleDateTime::GetCurrentTime().Format(_T("%d.%m.%Y %H:%M:%S \t")));\
	TRACE_TO_FILE_file.WriteString(TRACE_TO_FILE_str);TRACE_TO_FILE_file.WriteString(_T("\n"));TRACE_TO_FILE_file.Close();}\
	else if(TRACE_TO_FILE_file.Open(TRACE_TO_FILE_dict_path+_T("AutoUpdate.log"),CFile::modeCreate|CFile::modeWrite))\
{TRACE_TO_FILE_file.SeekToEnd(); TRACE_TO_FILE_file.WriteString(TRACE_TO_FILE_str);TRACE_TO_FILE_file.WriteString(_T("\n"));TRACE_TO_FILE_file.Flush();TRACE_TO_FILE_file.Close();}}
#endif


#define REPAINT {MSG msg; while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE|PM_NOYIELD)) DispatchMessage(&msg);while (::PeekMessage(&msg, NULL, WM_SHOWWINDOW, WM_SHOWWINDOW, PM_REMOVE|PM_NOYIELD)) DispatchMessage(&msg);}
DWORD prev_tik=0;
#define REPAINT_WITH_INTERVAL {DWORD tick=GetTickCount();if(prev_tik!=0 && ((tick-prev_tik)>1000) ) REPAINT;prev_tik=tick;}
//#define REPAINT_WITH_INTERVAL


extern HINSTANCE module_inst;

//TCHAR def_ext[]=_T("*.exe;*.dll;*.bin;*.dict");

//TCHAR param0[_MAX_FNAME];
//TCHAR param1[_MAX_FNAME];

#define TMP_BUF_SIZE 1024

TCHAR tmp[TMP_BUF_SIZE];
TCHAR base[]=_T("SOFTWARE\\CALS Centre \"Applied Logistic\"\\AutoUpdate\\");

class CAutoSwitchResources
{
public:
	CAutoSwitchResources();
	~CAutoSwitchResources();
	
	HINSTANCE	hModCur;
};

CAutoSwitchResources::CAutoSwitchResources()
{
	hModCur = AfxGetResourceHandle();
	AfxSetResourceHandle(module_inst);
}

CAutoSwitchResources::~CAutoSwitchResources()
{
	AfxSetResourceHandle(hModCur);
}


class UpdaeFileInfo
{
public:
	CString name;
	CString src_path;
	CString dst_path;
	CFile *src_file;
	CFile *dst_file;
};

//*******************************************************************
bool GetValue(HKEY key, LPCTSTR var, CString &buf)
{
	buf=_T("");
	DWORD size=TMP_BUF_SIZE * sizeof(TCHAR);
	tmp[0]=_T('\0');
	DWORD type;
	if(RegQueryValueEx(key,var,0,&type,(BYTE*)tmp,&size)!=ERROR_SUCCESS) return false;
	if(type==REG_SZ)buf=tmp; else return false;
	return true;
}
//*******************************************************************
bool GetValue(HKEY key, LPCTSTR var, long *buf)
{
	*buf=0;
	DWORD size=4;
	DWORD type,ret;
	if(RegQueryValueEx(key,var,0,&type,(BYTE*)(&ret),&size)!=ERROR_SUCCESS) return false;
	if(type==REG_DWORD) *buf=ret; else return false;
	return true;
}
//*******************************************************************
bool SetValue(HKEY key, LPCTSTR var, CString &buf)
{
	if(RegSetValueEx(key,var,0,REG_SZ,(BYTE*)LPCTSTR(buf),buf.GetLength()*sizeof(TCHAR))!=ERROR_SUCCESS) return false;
	return true;
}
//*******************************************************************
bool SetValue(HKEY key, LPCTSTR var, unsigned long buf)
{
	if(RegSetValueEx(key,var,0,REG_DWORD,(BYTE*)(&buf),sizeof(buf))!=ERROR_SUCCESS) return false;
	return true;
}
//*******************************************************************
//*******************************************************************
//*******************************************************************
CaplAutoUpdate::CaplAutoUpdate (LPCTSTR module)
{
	m_module=module;
	ASSERT(m_module!=_T(""));
	m_update_interval=0;
	m_AutoUpdate=0;
	m_source=0;
	m_data=0;
	m_curpath=GetExePath();

#ifdef _DEBUG
//	m_curpath=_T("C:\\_TEST_UPDATE\\");
#endif 

	m_temp_dir=_T("");
	m_bForceUpdate=FALSE;
}

bool CaplAutoUpdate::CheckCreateTemp()
{
	if(m_temp_dir==_T(""))
	{
		// Создаем временный каталог
		CString temp_dir;temp_dir.GetEnvironmentVariable(_T("TEMP"));
		temp_dir+=_T("\\_update_"); temp_dir+=m_module; temp_dir+=_T("\\");

		if(__mkdir(temp_dir)!=0 )
		{
			if(errno==EEXIST)
			{
				m_temp_dir = temp_dir;
			}
		}
		else
		{
			m_temp_dir = temp_dir;
		}
	}
	return true;
}


bool CaplAutoUpdate::CheckNeedUpdate()
{
	// Проверка необходимости обновления
	CString str=base; str+=m_module;
	
	// Читаем из файла
	CString last_update_file_name = m_curpath;
	last_update_file_name+=_T("lastupdate");
	CFile upd_file;
	if(upd_file.Open(last_update_file_name,CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary))
	{
		double last_upd=0;
		if(upd_file.Read(&last_upd,sizeof(last_upd)))
		{
			COleDateTime t_cur=COleDateTime::GetCurrentTime();
			COleDateTime  t_upd;
			t_upd.m_dt=last_upd;
			COleDateTimeSpan  span= t_cur-t_upd;
			double d=span.GetTotalHours();
			if(d<m_update_interval)
			{
				return false;
			}
		}
	}

	/*CRegKey key;
	if(key.Open(HKEY_LOCAL_MACHINE, str, KEY_QUERY_VALUE)==ERROR_SUCCESS)
	{
		DWORD dwSize=0;
		double last_upd=0;
		key.QueryValue(NULL, _T("last_update"), &dwSize);
		key.QueryValue(buf.GetBuffer(dwSize), _T("last_update"), &dwSize);
		buf.ReleaseBuffer();
		key.Close();
		last_upd=atof(buf);
		COleDateTime t_cur=COleDateTime::GetCurrentTime(), t_upd;
		t_upd.m_dt=last_upd;
		COleDateTimeSpan  span= t_cur-t_upd;
		double d=span.GetTotalHours();
		if(d<m_update_interval)
		{
		if(bShowAllMessage) AfxMessageBox(CaplTranslate::Translate(_T("Автообновление:\n\n   Не истек период обновления.")
				_T("\n   Обновление не требуется.")));
			return false;
		}
	}*/
	return true;
}

bool CaplAutoUpdate::SetOptions()
{
	CAutoSwitchResources Resources;

	COptionsDlg dlg;
	dlg.m_source=m_source;
	dlg.m_module=m_module;
	dlg.m_fold=m_folder;
	dlg.m_hours=m_update_interval;
	dlg.m_autoupdate=m_AutoUpdate;

	if(dlg.DoModal()==IDOK)
	{
		m_source=dlg.m_source;
		m_folder=dlg.m_fold;
		if(m_folder.Right(1)!=_T('\\'))m_folder+=_T('\\');
		m_AutoUpdate=dlg.m_autoupdate;
		m_update_interval=dlg.m_hours;
		SaveOptions();
	}
	return true;
}


bool CaplAutoUpdate::SaveOptions()
{
	CString str;
	
	//читаем из файла
	CString fname = m_curpath;
	fname+=_T("autoupdate.ini");
	
	CaplStringFile file;

	unsigned int nOpenFlags = CFile::modeCreate|CFile::modeWrite|CFile::typeText;
	if(!file.Open(fname,nOpenFlags, newFileCoding))
	{
		str=APL_T("Ошибка записи в файл \"")+fname+_T("\"!");
		AfxMessageBox(str,MB_OK|MB_ICONSTOP);
		return false;
	}

	file.WriteString(_T("[autoupdate]\n"));

	str.Format(_T("source=%i\n"),m_source);
	file.WriteString(str);

	str=_T("folder=")+m_folder+_T("\n");
	file.WriteString(str);

	str.Format(_T("AutoUpdate=%i\n"),m_AutoUpdate);
	file.WriteString(str);

	str.Format(_T("interval=%i\n"),m_update_interval);
	file.WriteString(str);

	str.Format(_T("force_update=%i\n"),m_bForceUpdate);
	file.WriteString(str);

	file.Flush();
	file.Close();



	/*CString str=base; str+=m_module;
	HKEY key;
	if(RegCreateKey(HKEY_LOCAL_MACHINE,str,&key)!=ERROR_SUCCESS) return false;

	SetValue(key,_T("folder"),m_folder);
	SetValue(key,_T("AutoUpdate"), m_AutoUpdate);
	SetValue(key,_T("interval"), m_update_interval);

	RegCloseKey(key);*/
	return true;
}


bool CaplAutoUpdate::LoadOptions()
{
	CaplStackLogger stack_logger(_T(__FUNCTION__));

	CString str;
	
	//читаем из файла
	CString fname = m_curpath;
	fname+=_T("autoupdate.ini");

	CaplStringFile file;

	unsigned int nOpenFlags = CFile::modeRead|CFile::shareDenyWrite|CFile::typeText;
	if(file.Open(fname,nOpenFlags))
	{
		file.ReadString(str);
		if(str==_T("[autoupdate]"))
		{
			while(file.ReadString(str))
			{
				int i=str.Find(_T('='));
				if(i>0)
				{
					CString name=str.Left(i);
					CString value=str.Right(str.GetLength()-i-1);
					name.TrimLeft();name.TrimRight();
					value.TrimLeft();value.TrimRight();
					name.MakeLower();

					if(name==_T("source")) m_source=_atoi(value);
					else if(name==_T("folder")) m_folder=value;
					else if(name==_T("autoupdate")) m_AutoUpdate=_atoi(value);
					else if(name==_T("interval")) m_update_interval=_atoi(value);
					else if(name==_T("force_update")) m_bForceUpdate=_atoi(value);
				}
			}
		}
		file.Close();
		if(m_source>1)m_source=0;
		if(m_folder.Right(1)!=_T('\\'))m_folder+=_T('\\');
		return true;
	}

	
	// Читаем из реестра
	str=base; str+=m_module;
	HKEY key;
	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, str,0, KEY_QUERY_VALUE, &key)!=ERROR_SUCCESS) return false;

	GetValue(key,_T("source"), (long*)(&m_source));
	GetValue(key,_T("folder"),m_folder);
	if(m_folder.Right(1)!=_T('\\'))m_folder+=_T('\\');
	GetValue(key,_T("AutoUpdate"), (long*)(&m_AutoUpdate));
	GetValue(key,_T("interval"), (long*)(&m_update_interval));

	RegCloseKey(key);
	return true;
}

struct SMutexWrapper
{
	SMutexWrapper(LPCTSTR module)
	{
		// Имя мутекса привязано к имени системы - чтобы LSS и PSS могли обновляться одновременно
		CString name_mutex =  _T("Global\\APL_AUTOUPDATE_");
		name_mutex += module;
		name_mutex += _T("00001000000");

		m_hMutex = ::CreateMutex(NULL,FALSE,name_mutex);
		DWORD err=::GetLastError();
		m_not_single = ( err == ERROR_ALREADY_EXISTS || err == ERROR_ACCESS_DENIED);
	}
	~SMutexWrapper()
	{
		CloseHandle(m_hMutex);
	}

	HANDLE m_hMutex; // хэндл семафора для проверки множественного запуска
	bool m_not_single;
};

bool CaplAutoUpdate::Update(bool bChekTimeInterval, bool bShowAllMessage, bool bInteractive /*= true*/)
{
	// Проверяем, не запущено ли уже кем-то обновление
	SMutexWrapper mw(m_module);
	if( mw.m_not_single)
	{
		// обновление уже кем-то запущено
		return false;
	}


	// Создаем временный каталог
	CheckCreateTemp();
	if(m_temp_dir==_T(""))
	{
		CString buf=APL_T("   Ошибка создания временного каталога\n\n");
		if (bInteractive)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);

		return false;
	}

	if(m_source==1)
	{
		return UpdateServer(bChekTimeInterval, bShowAllMessage, bInteractive);
	}
	return UpdateFiles(bChekTimeInterval, bShowAllMessage, bInteractive);
}

bool CaplAutoUpdate::CheckUpdate()
{
	bool bChekTimeInterval = true;
	bool bShowAllMessage = false;
	bool bInteractive = false;
	CStringArray modified_files, deleted_files;
	bool has_exe;

	if (m_source==1)
	{
		return CheckFilesServer(bChekTimeInterval, bShowAllMessage, bInteractive, modified_files, deleted_files, has_exe);
	}
	return CheckFiles(bChekTimeInterval, bShowAllMessage, bInteractive, modified_files, deleted_files, has_exe);
}

bool CaplAutoUpdate::CheckFiles(bool bChekTimeInterval, bool bShowAllMessage, bool bInteractive,
								CStringArray &modified_files, CStringArray &deleted_files, bool &has_exe)
{
	int i;
	bool b=true;
	CString ext,buf,buf1;

	modified_files.RemoveAll();
	deleted_files.RemoveAll();
	has_exe = false;

	if(m_folder.IsEmpty())
	{
		if(bShowAllMessage) 
			AfxMessageBox(APL_T("Не задан путь к эталонным файлам. Обновление невозможно !!!"));
		else 
			TRACE_TO_FILE(APL_T("Не задан путь к эталонным файлам"));
		return false; 
	}
	if(m_folder.CompareNoCase(m_curpath)==0)
	{
		if(bShowAllMessage) 
			AfxMessageBox(APL_T("Путь к эталонным файлам совпадает с текущим. Обновление не требуется"));
		else 
			TRACE_TO_FILE(APL_T("Путь к эталонным файлам совпадает с текущим"));
		return false; 
	}

	if(bChekTimeInterval)
	{
		if(!CheckNeedUpdate())return false;
	}


	// Получение из эталонной папки списка файлов согласно имеющимся в ней *.luf
	CString err_mes;
	CStringArray a_upd_files;

	if(!aplReadFiles4AutoUpdate(m_folder,m_folder,a_upd_files,bInteractive,err_mes))
	{
		// в интерактивном режиме сообщения выводятся внутри функции
		if (!bInteractive)
			TRACE_TO_FILE(err_mes);
		return false;
	}

	if(a_upd_files.GetSize()<1)
	{
		if (bInteractive)
			AfxMessageBox(APL_T("Автообновление:\n\n  Список проверяемых файлов пуст !!!"));
		else
			TRACE_TO_FILE(APL_T("Список проверяемых файлов пуст"));
		return false;
	}

	// список файлов в эталонном каталоге - для быстрого поиска файлов на удаление
	CaplStrMap list_ethalon_files(false);

	// меняем ресурсы
	CAutoSwitchResources Resources;

	CChekUpdatesDlg chkdlg;
	chkdlg.Create(IDD_CHEK_UPDATES);
	chkdlg.ShowWindow(SW_SHOW);

	CFileStatus fstatus0, fstatus1;

	// Определение изменившихся файлов
	for(i=0;i<a_upd_files.GetSize();i++)
	{
		if(a_upd_files[i].CompareNoCase(_T("aplupdate.exe"))==0) continue;

		list_ethalon_files.Add(a_upd_files[i],(long)0);

		CString sDtCrTime1, sDtModTime1, sDtCrTime0, sDtModTime0;
		CString fname_cur=m_curpath+a_upd_files[i];
		CString fname_new=m_folder+a_upd_files[i];
		BOOL b_cur=CFile::GetStatus(fname_cur,fstatus0);
		BOOL b_new=CFile::GetStatus(fname_new,fstatus1);
		if(!b_new) continue;
		if(!b_cur) 
		{
			modified_files.Add(a_upd_files[i]);
			CheckExeDll(a_upd_files[i],has_exe);
		}
		else
		{
			if(fstatus0.m_size!=fstatus1.m_size)
			{
				modified_files.Add(a_upd_files[i]);
				CheckExeDll(a_upd_files[i],has_exe);
			}
			else
			{
// 				if(GetFileCRC32(fname0)!=GetFileCRC32(fname1))
// 				{	modified_files.Add(a_upd_files[i]);CheckExeDll(a_upd_files[i],has_exe);}

				CTimeSpan ts;
				LONGLONG ds;
				// сперва сравниваем дату создания двух файлов
				ts = fstatus0.m_ctime - fstatus1.m_ctime;
				ds= ts.GetTotalSeconds();
				if(ds<0) ds=-ds;
				// допустимая разница во времени - 4 сек. 
				// Должна быть не менее 2 сек для отработки ntfs-fat
				if(ds<4) 
				{
					// если даты создания совпадают с точностью до 4 секунд - сравниваем дату изменения
					ts = fstatus0.m_mtime - fstatus1.m_mtime;
					ds= ts.GetTotalSeconds();
					if(ds<0) ds=-ds;
				}
				if(ds>4) 
				{
					modified_files.Add(a_upd_files[i]);
					CheckExeDll(a_upd_files[i],has_exe);
				}
			}
		}
		REPAINT_WITH_INTERVAL;
	}

	// Определение удаляемых файлов по шаблону из *.luf из каталога с эталонными файлами
	if(aplReadFiles4AutoUpdate(m_curpath,m_folder,a_upd_files,bInteractive,err_mes))
	{
		list_ethalon_files.Add(_T("aplupdate.exe"),(long)0);
		for(i=0;i<a_upd_files.GetSize();i++)
		{
			if(list_ethalon_files.Find(a_upd_files[i])==-1)
			{
				deleted_files.Add(a_upd_files[i]);
				CheckExeDll(a_upd_files[i],has_exe);
			}
		}
	}

	chkdlg.ShowWindow(SW_HIDE);

	if(modified_files.GetSize()==0)
	{
		if(bShowAllMessage)
			AfxMessageBox(APL_T("Автообновление:\n\n   Обновление не требуется.  Файлы не изменились."));
		else
			TRACE_TO_FILE(APL_T("Ни один файл не изменился"));
		return false;
	}
	TRACE_TO_FILE(_T("OK"));
	return true;
}

bool CaplAutoUpdate::CheckFilesServer(bool bChekTimeInterval, bool bShowAllMessage, bool bInteractive,
									  CStringArray &modified_files, CStringArray &deleted_files, bool &has_exe)
{
	bool b=true;
	CString ext,buf,buf1;
	CString err_mes;

	modified_files.RemoveAll();
	deleted_files.RemoveAll();
	has_exe=false;

	if(m_data==0)
	{
		buf = APL_T("   Не задан класс для связи с сервером!!!");
		if (bShowAllMessage)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);
		return false; 
	}
	if(!m_data->IsConnected())
	{
		buf = APL_T("   Соединение с сервером не установлено.");
		if (bShowAllMessage)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);
		return false; 
	}

	if(bChekTimeInterval)
	{
		if(!CheckNeedUpdate()) return false;
	}
	// Создаем временный каталог
	CheckCreateTemp();
	if(m_temp_dir==_T(""))
	{
		buf=APL_T("   Ошибка создания временного каталога\n\n");
		if (bInteractive)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);
		return false;
	}

	// Получение списка проверяемых файлов
	CStringArray a_luf_files;
	CStringArray a_files_mask;

	// список файлов в эталонном каталоге - для быстрого поиска файлов на удаление
	CaplStrMap list_ethalon_files(false);

	a_files_mask.Add(_T("*.luf"));
	int indx_luf;
	int i;

	CaplTAggr<SaplFileDescr*,SaplFileDescr*,APLAGGR_LIST_OR_AUTOKILLREF> list_file_descr;

	if(!m_data->NET_Autoupdate_LoadListNamesFiles(m_module,a_files_mask,list_file_descr))return false;
	
	// сервер вернул список файлов luf. Запрашиваем их содержимое 
	if(list_file_descr.GetSize()==0) 
	{
		buf = APL_T("   Файлы со списком обновляемых файлов (*.luf) на сервере не найдены !");
		if (bInteractive)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);
		return false;
	}

	for(indx_luf=0;indx_luf<list_file_descr.GetSize();indx_luf++)
	{
		a_luf_files.Add(list_file_descr.GetAt(indx_luf)->name);
	}

	// загружаем luf во временный каталог
	if(!m_data->NET_Autoupdate_LoadListFiles(m_module,m_temp_dir,a_luf_files))return false;
	
	// читаем содержимое luf для отсылки на сервер
	BOOL bNextFile=TRUE;
	a_files_mask.RemoveAll();

	for(indx_luf=0;indx_luf<a_luf_files.GetSize();indx_luf++)
	{
		buf=m_temp_dir+a_luf_files.GetAt(indx_luf);

		CaplStringFile file;
		unsigned int nOpenFlags = CFile::modeRead|CFile::shareDenyWrite|CFile::typeText;

		if(!file.Open(buf, nOpenFlags))
		{
			continue;
		}
		while(file.ReadString(buf))
		{
			buf.TrimLeft();buf.TrimRight(); buf.MakeLower();
			if(buf!=_T("")) a_files_mask.Add(buf);
		}
		file.Close();
	}
	
	// запрашиваем список эталонных файлов, соответствующих маскам
	if(!m_data->NET_Autoupdate_LoadListNamesFiles(m_module,a_files_mask,list_file_descr))
	{
		buf = APL_T("   Ошибка получения списка эталонных файлов !");
		if (bInteractive)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);

		return false;
	}

//	Поиск дубликатов
//	CString fname;
// 	for(indx_found=0;indx_found<list_file_descr.GetSize();indx_found++)
// 	{
// 		fname=list_file_descr.GetAt(indx_found)->name;
// 		bool bFound=false;
// 		for(int i=0;i<list_file_descr.GetSize();i++)
// 		{
// 			if(list_file_descr[i]->name==fname && i!=indx_found) {bFound=true; break;}
// 		}
// 		if(bFound) 
// 		{
// 			list_file_descr.Remove(indx_found);
// 			indx_found--;
// 		}
// 	}

	if(list_file_descr.GetSize()==0)
	{
		buf = APL_T("  Ошибка получения списка эталонных файлов (список пуст)!");
		if (bInteractive)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);

		return false;
	}
	
	// Определение изменившихся файлов
	CAutoSwitchResources Resources;
	CStringArray a_upd_files;

	CChekUpdatesDlg chkdlg;
	chkdlg.Create(IDD_CHEK_UPDATES);
	chkdlg.ShowWindow(SW_SHOW);

	CFileStatus fstatus0, fstatus1;
	long crc32_c;

	for(i=0;i<list_file_descr.GetSize();i++)
	{
		if(list_file_descr[i]->name.CompareNoCase(_T("aplupdate.exe"))==0) continue;

		CString sDtCrTime1, sDtModTime1, sDtCrTime0, sDtModTime0;
		CString fname0=m_curpath+list_file_descr[i]->name;

		// формируем мап для быстрого поиска файлов-претендентов на удаление
		list_ethalon_files.Add(list_file_descr[i]->name,(long)0);

		BOOL b0=CFile::GetStatus(fname0,fstatus0);
		
		fstatus1=list_file_descr[i]->fstatus;
		//if(!b1) continue;
		if(!b0) 
		{
			modified_files.Add(list_file_descr[i]->name);
			CheckExeDll(list_file_descr[i]->name,has_exe);
		}
		else
		{
			if(fstatus0.m_size!=fstatus1.m_size)
			{
				modified_files.Add(list_file_descr[i]->name);
				CheckExeDll(list_file_descr[i]->name,has_exe);
			}
			else
			{
				crc32_c=GetFileCRC32(fname0,false);
				if(crc32_c!=list_file_descr[i]->crc32)
				{
					modified_files.Add(list_file_descr[i]->name);
					CheckExeDll(list_file_descr[i]->name,has_exe);
				}
			}
		}
		REPAINT_WITH_INTERVAL
	}

	// Определение файлов из текущего каталога по шаблону из *.luf (на удаление)
	// luf берется из временного каталога
	if(aplReadFiles4AutoUpdate(m_curpath,m_temp_dir,a_upd_files,bInteractive,buf))
	{
		list_ethalon_files.Add(_T("aplupdate.exe"),(long)0);
		for(i=0;i<a_upd_files.GetSize();i++)
		{
			if(list_ethalon_files.Find(a_upd_files[i])==-1)
			{
				deleted_files.Add(a_upd_files[i]);
				CheckExeDll(a_upd_files[i],has_exe);
			}
		}
	}
	else
	{
		if (!bShowAllMessage)TRACE_TO_FILE(buf);
	}

	chkdlg.ShowWindow(SW_HIDE);

	if(modified_files.GetSize()==0)
	{
		buf = APL_T("   Обновление не требуется.  Файлы не изменились.");
		if (bShowAllMessage)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf);
		else
			TRACE_TO_FILE(buf);
		return false;
	}
	return true;
}


bool CaplAutoUpdate::UpdateFiles(bool bChekTimeInterval, bool bShowAllMessage, bool bInteractive /*= true*/)
{
	int i;
	bool b=true;
	CString ext,buf,buf1;
	bool has_exe;

	CStringArray modified_files, deleted_files;

	if(!CheckFiles(bChekTimeInterval, bShowAllMessage, bInteractive, modified_files, deleted_files, has_exe))
	{
		return false;
	}
	// в функции несколько диалогов, ресурсы переназначаем заранее
	CAutoSwitchResources Resources;

	// Подтверждение обновления пользователем
	//if(bInteractive)
	if(bInteractive && (!m_bForceUpdate)) // ЯАИ: По просьбе Сизова запускается сразу
	{

		CUpdateMesDlg dlg0;
		buf.Format(APL_T("Найдено %i файлов требующих обновления !!!"), modified_files.GetSize());
		dlg0.m_text=buf;
		dlg0.m_bNoCancel=m_bForceUpdate;
		if(dlg0.DoModal()!=IDOK)
		{
			return false;
		}

		if(dlg0.m_sShowPodrob)
		{
			CUpdateDlg dlg;
			dlg.m_etalon_path=m_folder;
			dlg.m_update_path=m_curpath;
			dlg.m_files=&modified_files;
			dlg.m_del_files=&deleted_files;
			dlg.m_bNoCancel=m_bForceUpdate;
			if(dlg.DoModal()!=IDOK)
			{
				return false;
			}
		}
	}
	//
	// Создание файлов с параметрами копирования-удаления-перезапуска
	if(!CreateParamFiles(bInteractive,modified_files,deleted_files,has_exe))
		return false;

	// Копирование файлов из эталонной папки во временную
	CString fname0;
	CString fname1;

	bool res_copy=true;
	CArray<UpdaeFileInfo, UpdaeFileInfo&> updated_files;

	CChekUpdatesDlg chkdlg;
	chkdlg.m_Text = APL_T("Копирование файлов во временную папку");
	chkdlg.Create(IDD_CHEK_UPDATES);
	chkdlg.ShowWindow(SW_SHOW);


	try
	{
		chkdlg.SetText2(APL_T("Создание новых файлов"));
		// этот экзешник копируем всегда
		modified_files.Add(_T("aplUpdate.exe"));

		for(i=0; i<modified_files.GetSize(); i++)
		{
			fname0 = m_temp_dir + modified_files[i];
			fname1 = m_folder + modified_files[i];

			buf.Format(_T("Создание файла: %i / %i "),i,modified_files.GetSize());
			chkdlg.SetText2(buf);
			//Sleep(30);

			CFile *f0=new CFile;
			CFile *f1=new CFile;

			// обработка каталогов
			if(!CheckAndCreateFolder(fname0))
			{
				buf=APL_T("Ошибка при создании пути к файлу\n\n"); buf+=fname0;
				buf+=APL_T("\n\nОбновление невозможно.");
				if(bInteractive)
					AfxMessageBox(buf,MB_OK);
				else
					TRACE_TO_FILE(buf);
				throw(2);
			}

			// обработка файлов
			b=true;
			while(b)
			{
				SetFileAttributes(fname0,FILE_ATTRIBUTE_NORMAL);
				if(TRUE==f0->Open(fname0,CFile::modeWrite|CFile::modeCreate|CFile::modeNoTruncate|CFile::typeBinary))
					b=false;
				else
				{
					buf.Format(APL_T("Ошибка открытия временного файла\n\n%s\n\nВозможно закрыты не все приложения.\n"), fname0);
					buf+=APL_T("\n\nВозможно закрыты не все приложения.");
					if(!bInteractive||AfxMessageBox(buf,MB_RETRYCANCEL)!=IDRETRY) throw(2);
				}
			}

			b=true;
			while(b)
			{
				if(TRUE==f1->Open(fname1,CFile::modeRead|CFile::shareDenyWrite|CFile::typeBinary))
					b=false;
				else
				{
					buf.Format(APL_T("Ошибка открытия исходного файла:\n%s."), fname1);
					if(!bInteractive||AfxMessageBox(buf, MB_RETRYCANCEL)!=IDRETRY)
					{
						f0->Close();
						throw(3);
					}
				}
			}
			UpdaeFileInfo ufi;
			ufi.name=modified_files[i];
			ufi.dst_file=f0;
			ufi.dst_path=fname0;
			ufi.src_file=f1;
			ufi.src_path=fname1;
			
			updated_files.Add(ufi);

			REPAINT_WITH_INTERVAL
		}

		chkdlg.SetText2(APL_T("Копирование содержимого ..."));

		//Непосредственно копирование во временную папку
		for(i=0;i<updated_files.GetSize();i++)
		{
			UpdaeFileInfo &fi=updated_files[i];
			
#if _MSC_VER >= 1400
			ULONGLONG len=fi.src_file->GetLength();
#else
			DWORD len=fi.src_file->GetLength();
#endif
			fi.dst_file->SetLength(0);
			fi.dst_file->SeekToBegin();

			int ilen=(int)len;
			buf.Format(_T("Запись содержимого файла: %i / %i ( %i BYTEs)"),i,modified_files.GetSize(),ilen);
			chkdlg.SetText2(buf);
			//Sleep(100);

			if(len!=0)
			{
				BYTE *tmp=new BYTE[(UINT)len+1];
#if _MSC_VER >= 1400
				fi.src_file->Read(tmp,(UINT)len);
				fi.dst_file->Write(tmp,(UINT)len);
#else
				fi.src_file->ReadHuge(tmp,len);
				fi.dst_file->WriteHuge(tmp,len);
#endif
				delete []tmp;
			}
			fi.dst_file->Flush();
			fi.src_file->Close();
			fi.dst_file->Close();

			CopyFileTime(updated_files[i].src_path,updated_files[i].dst_path);

			REPAINT_WITH_INTERVAL
		}
	}
	catch(int err)
	{
		if(err==2||err==3)
		{
			buf=APL_T("Обновление не выполнено\n\n");
			if(err==2) buf+=APL_T("(Ошибка открытия файла в источнике обновления)");
			else if(err==3) buf+=APL_T("(Нарушение прав совместного доступа к файлу)");
		}
		else
		{
			buf=CaplTranslate::Translate(_T("Не все файлы были обновлены. \n\n")
				_T("Если приложение будет работать некорректно\n")
				_T("выполните обновление снова."));
			buf=_T("(???)");
		}
		res_copy=false;
	}
	catch(...)
	{
		for(i=0;i<updated_files.GetSize();i++)
		{
			if(updated_files[i].dst_file!=0) updated_files[i].dst_file->Close();
			if(updated_files[i].src_file!=0) updated_files[i].src_file->Close();
		}

		res_copy= false;
		buf = APL_T("Во время подготовки исходных данный произошла неизвестная ошибка. Обновление модуля невозможно!");
	}

	chkdlg.SetText2(APL_T("Очистка памяти ..."));

	// Очистка памяти
	for(i=0;i<updated_files.GetSize();i++)
	{
		if(updated_files[i].dst_file!=0) delete updated_files[i].dst_file;
		if(updated_files[i].src_file!=0) delete updated_files[i].src_file;
		updated_files[i].dst_file=0;
		updated_files[i].src_file=0;
	}
	updated_files.RemoveAll();

	chkdlg.ShowWindow(SW_HIDE);

	if(!res_copy)
	{
		if (bInteractive)
			AfxMessageBox(buf,MB_OK|MB_ICONSTOP);
		else
			TRACE_TO_FILE(buf);
		return false;
	}

	//
	// Запуск обновления

	//
	if(StartUpdate(bInteractive, has_exe))
		return has_exe;
	else
		return true;
}

bool CaplAutoUpdate::UpdateServer(bool bChekTimeInterval, bool bShowAllMessage, bool bInteractive /*= true*/)
{
	bool b=true;
	CString ext,buf,buf1;
	CStringArray modified_files, deleted_files,a_upd_files;
	bool has_exe;

	// в функции несколько диалогов, ресурсы переназначаем заранее
	CAutoSwitchResources Resources;

	if(!CheckFilesServer(bChekTimeInterval, bShowAllMessage, bInteractive, modified_files, deleted_files, has_exe))
	{
		return false;
	}
	// Подтверждение обновления пользователем
	//if (bInteractive)
	if (bInteractive && (!m_bForceUpdate)) // ЯАИ: По просьбе Сизова запускается сразу
	{
		CUpdateMesDlg dlg0;
		buf.Format(APL_T("Найдено %i файлов требующих обновления !!!"), modified_files.GetSize());
		dlg0.m_text=buf;
		dlg0.m_bNoCancel=m_bForceUpdate;
		if(dlg0.DoModal()!=IDOK)
		{
			return false;
		}

		if(dlg0.m_sShowPodrob)
		{
			CUpdateDlg dlg;
			dlg.m_etalon_path=APL_T("Сервер приложений");
			dlg.m_update_path=m_curpath;
			dlg.m_files=&modified_files;
			dlg.m_del_files=&deleted_files;
			dlg.m_bNoCancel=m_bForceUpdate;

			if(dlg.DoModal()!=IDOK)
			{
				return false;
			}
		}
	}
	//
	// Создание файлов с параметрами копирования-удаления-перезапуска
	if(!CreateParamFiles(bInteractive,modified_files,deleted_files, has_exe))
		return false;
	
	// обязательно грузим с сервера эти файлы
	modified_files.Add(_T("aplUpdate.exe"));

	//Загружаем файлы с сервера во временный каталог
	if(!m_data->NET_Autoupdate_LoadListFiles(m_module,m_temp_dir,modified_files))
	{
		buf = APL_T("   Ошибка загрузки файлов с сервера во временный каталог!!!");
		if (bShowAllMessage)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);
		return false; 
	}

	//
	// Запуск обновления
	//
	if(StartUpdate(bInteractive, has_exe))
		return has_exe;
	else
		return true;
}

void ClearFolder(LPCTSTR _folder_name)
{
	CFileFind ff;
	CString buf,buf1;
	CString folder_name = _folder_name;
	if(folder_name.Right(1)!=_T('\\'))folder_name+=_T('\\');
	buf= folder_name + _T("*.*");

	if(!ff.FindFile(buf)) 
	{
		return;
	}

	BOOL bNextFile=TRUE;
	while(bNextFile)
	{
		bNextFile=ff.FindNextFile();
		buf1 = ff.GetFileName();
		if(buf1==_T(".") || buf1==_T("..")) continue;
		buf = folder_name + buf1;
		if(ff.IsDirectory( ))
		{
			// пробуем очистить его как папку, а потом удалить как папку
			ClearFolder(buf);
			::RemoveDirectory(buf);
		}
		else
		{
			::DeleteFile(buf);
		}
	}
}

//
// Создание файлов с параметрами копирования-удаления-перезапуска
//
bool CaplAutoUpdate::CreateParamFiles(bool bInteractive, CStringArray &modified_files, CStringArray &deleted_files, bool has_exe)
{
	CString upd_file_name,buf;
	CaplStringFile upd_file;

	// Очищаем временную папку
	ClearFolder(m_temp_dir);

	//Формируем файл с параметрами для утилиты копирования и списком доступных для обновления файлов
	upd_file_name=m_temp_dir + global_name_param_file;

	int i;
	unsigned int nOpenFlags = CFile::modeCreate|CFile::modeWrite|CFile::typeText;
	if(!upd_file.Open(upd_file_name,nOpenFlags,newFileCoding))
	{
		buf = APL_T("   Ошибка создания файла параметров \n\n");buf+=upd_file_name;
		if (bInteractive)
			AfxMessageBox(APL_T("Автообновление:\n\n")+buf+APL_T("\n\n   Обновление невозможно !!!"));
		else
			TRACE_TO_FILE(buf);
		return false; 
	}

	upd_file.WriteString(m_module);		upd_file.WriteString(_T("\n"));
	upd_file.WriteString(m_curpath);	upd_file.WriteString(_T("\n"));
	upd_file.WriteString(m_temp_dir);	
	//	upd_file.WriteString(m_folder); - по архитектуре 2014 эта строчка не нужна. По старой архитектуре - тут был путь к шаре
	// перевод строки нужен, он показывает новому aplUpdate.exe что обновление идет новым клиентом
	// если же строчка не пустая - обновление запущено старым клиентом и aplUpdate.exe должен сам копировать файло с шары
	upd_file.WriteString(_T("\n")); 

	for(i=0;i<modified_files.GetSize();i++)
	{
		upd_file.WriteString(_T("\n"));
		upd_file.WriteString(modified_files[i]);
	}
	upd_file.Flush();
	upd_file.Close();

	//Формируем файл со списком файлов, которые надо удалить
	CString del_file_name;
	CaplStringFile del_file;

	del_file_name=m_temp_dir + global_name_del_file;

	if(del_file.Open(del_file_name,nOpenFlags,newFileCoding))
	{
		for(i=0;i<deleted_files.GetSize();i++)
		{
			if(i>0)del_file.WriteString(_T("\n"));
			del_file.WriteString(deleted_files[i]);
		}
		del_file.Flush();
		del_file.Close();
	}
	else
	{
		buf=APL_T("Ошибка создания файла \n\n"); buf+=del_file_name;
		TRACE_TO_FILE(buf);
		// return false; если файл для удаляемых не удалось создать - то и пусть, это не критично
	}


	// файл с путем и параметрами запуска приложения, из которого было запущено обновление
	CString start_file_name=m_temp_dir; start_file_name+=global_name_start_file;
	CaplStringFile start_file;

	if(start_file.Open(start_file_name,nOpenFlags,newFileCoding))
	{
		CString app=_T("");
		if(has_exe)
		{
			CWinApp *appication=AfxGetApp(); // При вызове из ActiveX возвращает 0
			if(0==appication) return false;

			CString buf=appication->m_pszHelpFilePath;
			buf.MakeLower();
			buf.Replace(_T(".hlp"),_T(".exe"));

			app=_T("\"");
			app+=buf;
			app+=_T("\" ");
			app+=appication->m_lpCmdLine;
		}
		// если среди изменившихся нет exe и dll - то в этот файл пишем пустую строку, при этом утилита копирования не будет ничего прерывать и потом запускать

		start_file.WriteString(app);
		start_file.Close();
	}
	else
	{
		buf=APL_T("Ошибка создания файла \n\n"); buf+=start_file_name;
		TRACE_TO_FILE(buf);
		// return false; если файл для параметров запуска не удалось создать - то и пусть, это не критично
	}

	return true;
}

//
// Запуск обновления
//
bool CaplAutoUpdate::StartUpdate(bool bInteractive, bool has_exe)
{
	CString fname0 = m_temp_dir + _T("aplUpdate.exe");
	CString upd_file_name = m_temp_dir + global_name_param_file;
	CString buf;

	buf.Format(_T("\"%s\" \"%s\" "),(LPCTSTR)fname0,(LPCTSTR)upd_file_name);

	if (!bInteractive || !has_exe)
	{
		long wd_id=0;
		if(!has_exe)wd_id = aplStartWaitDlg(APL_T("Обновление изменившихся файлов..."));
		SHELLEXECUTEINFO info;
		memset(&info, 0, sizeof(info));
		info.cbSize = sizeof(info);
		info.lpVerb = _T("open");
		info.hwnd = NULL;
		info.lpFile = fname0;
		info.nShow = SW_HIDE;
		info.fMask = SEE_MASK_NOCLOSEPROCESS|SEE_MASK_FLAG_NO_UI; 
		if (!bInteractive)
			upd_file_name+=_T(" /hide=true");
		info.lpParameters = upd_file_name;
		if(ShellExecuteEx(&info)) 
		{	
			if(!has_exe)WaitForSingleObject(info.hProcess, INFINITE);
		}
		if(wd_id!=0)aplEndWaitDlg(wd_id);
	}
	else
	{
		if(aplCreateProcess(0, buf, false)==-1)
		{
			AfxMessageBox(APL_T("Ошибка запуска обновления"));
			return false;
		}
	}

	//	curpath+=_T("aplUpdate.exe");
	//	buf=_T("\""); buf+=; buf+=_T("\"");
	//	buf1=_T("\""); buf1+=; buf1+=_T("\"");
	//	int ret=_spawnl(_P_NOWAIT ,curpath, buf1, buf, 0);
	//	if(!errno) AfxMessageBox(APL_T("Ошибка запуска обновления"));
	return true;
}