#include "stdafx.h"
#include <aplguiex.h>
#include "apl_api.h"
#include "dictionary.h"

#include "MessageDlg.h"
#include "DispMessageDlg.h"
#include "SelectPecipientsDlg.h"
#include "AlertDlg.h"
#include "ItemSubscribeDlg.h"
#include "AlertPropertiesDlg.h"
#include "ShowAlertsDlg.h"
#include "aplMPC.h"
#include "aplSocketTransport.h"

const TCHAR CaplMessageManager::m_alert_state_active[] =   APL_NO_T("активно");
const TCHAR CaplMessageManager::m_alert_state_completed[] =   APL_NO_T("выключено");

bool CaplMessageManager::Attach(CaplAPI *api)
{
	if(api==0) {Detach();return false;}
	CaplStepManager::Attach(api);

	bool bRes= false;
	
	e_message= m_data->GetEntityBN(S::apl_message);
	a_message_sender= m_data->GetAttrDefinition(e_message, S::sender);
	a_message_recipient= m_data->GetAttrDefinition(e_message, S::recipient);
	a_message_recipient_in_copy= m_data->GetAttrDefinition(e_message, _T("recipient_in_copy"));
	a_message_subject= m_data->GetAttrDefinition(e_message, S::subject);
	a_message_content_lenght= m_data->GetAttrDefinition(e_message, _T("subject_lenght"));
	a_message_content_source= m_data->GetAttrDefinition(e_message, _T("subject_source"));
	a_message_content= m_data->GetAttrDefinition(e_message, S::content);
	a_message_content_format= m_data->GetAttrDefinition(e_message, _T("content_format"));
	a_message_items= m_data->GetAttrDefinition(e_message, S::items);
	a_message_priority= m_data->GetAttrDefinition(e_message, S::priority);
	a_message_ask_notify= m_data->GetAttrDefinition(e_message, S::ask_notify);
	a_message_status= m_data->GetAttrDefinition(e_message, S::status);
	a_message_date_send= m_data->GetAttrDefinition(e_message, S::date_send_s);
	a_message_date_received= m_data->GetAttrDefinition(e_message, S::date_received_s);
	a_message_prev_message= m_data->GetAttrDefinition(e_message, S::prev_message);
	a_message_forward= m_data->GetAttrDefinition(e_message, S::forward);
	a_message_sender_kill= m_data->GetAttrDefinition(e_message, S::sender_kill);
	a_message_recipient_kill= m_data->GetAttrDefinition(e_message, S::recipient_kill);
	a_message_copy= m_data->GetAttrDefinition(e_message, _T("copy"));
	a_message_sender_folder= m_data->GetAttrDefinition(e_message, _T("sender_folder"));
	a_message_recipient_folder= m_data->GetAttrDefinition(e_message, _T("recipient_folder"));
	a_message_complete_date= m_data->GetAttrDefinition(e_message, _T("complete_date"));
	a_message_required_complete_date=m_data->GetAttrDefinition(e_message, _T("required_complete_date"));
	a_message_message_type= m_data->GetAttrDefinition(e_message, _T("message_type"));	
	a_message_rec_nick= m_data->GetAttrDefinition(e_message, _T("recipient_nick"));
	a_message_send_nick= m_data->GetAttrDefinition(e_message, _T("sender_nick"));
	
	a_message_copies= m_data->GetAttrDefinition(e_message, _T("copies"));

	a_message_rec_offisial= m_data->GetAttrDefinition(e_message, _T("recipient_official"));
	a_message_code= m_data->GetAttrDefinition(e_message, _T("code"));
	a_message_last_reply= m_data->GetAttrDefinition(e_message, _T("last_reply"));
	a_message_note= m_data->GetAttrDefinition(e_message, _T("note"));
	a_message_kurator= m_data->GetAttrDefinition(e_message, _T("kurator"));
	a_message_complete_date2= m_data->GetAttrDefinition(e_message, _T("complete_date2"));
	a_message_required_complete_date2=m_data->GetAttrDefinition(e_message, _T("required_complete_date2"));
	
	a_message_counter=m_data->GetAttrDefinition(e_message, _T("counter"));

	a_message_parent=m_data->GetAttrDefinition(e_message, _T("parent"));

	a_message_sender_color=m_data->GetAttrDefinition(e_message, _T("sender_color"));
	a_message_recipient_color=m_data->GetAttrDefinition(e_message, _T("recipient_color"));

	e_subscr= m_data->GetEntityBN(_T("apl_subscription"));
	a_subscr_subscriber= m_data->GetAttrDefinition(e_subscr, _T("subscriber"));
	a_subscr_item= m_data->GetAttrDefinition(e_subscr, _T("item"));

	e_alert= m_data->GetEntityBN(_T("apl_alert"));
	a_alert_person= m_data->GetAttrDefinition(e_alert, _T("person"));
	a_alert_item= m_data->GetAttrDefinition(e_alert, _T("item"));
	a_alert_code= m_data->GetAttrDefinition(e_alert, _T("code"));
	a_alert_date= m_data->GetAttrDefinition(e_alert, _T("performance_date"));
	a_alert_next_date= m_data->GetAttrDefinition(e_alert, _T("next_date"));
	a_alert_span= m_data->GetAttrDefinition(e_alert, _T("span"));
	a_alert_text= m_data->GetAttrDefinition(e_alert, _T("alert_text"));
	a_alert_module= m_data->GetAttrDefinition(e_alert, _T("alert_module"));
	a_alert_function= m_data->GetAttrDefinition(e_alert, _T("alert_function"));
	a_alert_author=m_data->GetAttrDefinition(e_alert, _T("author"));
	a_alert_state=m_data->GetAttrDefinition(e_alert, _T("state"));
	a_alert_delete_completed=m_data->GetAttrDefinition(e_alert, _T("delete_completed"));
	a_alert_notify_author=m_data->GetAttrDefinition(e_alert, _T("notify_author"));
	
	bRes= true;
	return bRes;
}

void CaplMessageManager::Detach()
{
	e_message= 0;
	a_message_sender= 0;
	a_message_recipient= 0;
	a_message_recipient_in_copy= 0;
	a_message_subject= 0;
	a_message_content_lenght= 0;
	a_message_content_source= 0;
	a_message_content= 0;
	a_message_content_format= 0;
	a_message_items= 0;
	a_message_priority= 0;
	a_message_ask_notify= 0;
	a_message_status= 0;
	a_message_date_send= 0;
	a_message_date_received= 0;
	a_message_prev_message= 0;
	a_message_forward= 0;
	a_message_sender_kill= 0;
	a_message_recipient_kill= 0;	
	a_message_copy= 0;
	a_message_copies= 0;
	a_message_sender_folder= 0;
	a_message_recipient_folder= 0;
	a_message_complete_date= 0;
	a_message_required_complete_date=0;
	a_message_message_type= 0;
	a_message_rec_offisial= 0;
	a_message_code= 0;
	a_message_last_reply=0;
	a_message_note=0;

	e_subscr= 0;
	a_subscr_subscriber= 0;
	a_subscr_item= 0;
	
	e_alert= 0;
	a_alert_person= 0;
	a_alert_item= 0;
	a_alert_code= 0;
	a_alert_date= 0;
	a_alert_next_date= 0;
	a_alert_text= 0;
	a_alert_span= 0;
	a_alert_module= 0;
	a_alert_function= 0;
	a_alert_author=0;
	a_alert_state=0;
	a_alert_delete_completed=0;
	a_alert_notify_author=0;

	a_message_kurator=0;
	a_message_complete_date2=0;
	a_message_required_complete_date2=0;
    a_message_counter=0;
	a_message_parent=0;
	a_message_sender_color=0;
	a_message_recipient_color=0;

}
//**************************************************************************************************************
CaplInstance* CaplMessageManager::CreateMessage(CaplInstance* sender, CaplInstance *recipient, 
							const TCHAR *subject, const TCHAR *content, aplExtent& ext, 
							int priority, const TCHAR* status, bool ask_notify, bool silent,
							CaplInstance* copy, aplExtent* possibleRecipient, CStringArray *sArrayFiles)
{
	if(!m_data) return 0;
	if(!m_data->IsConnected()) return 0;
	
	CaplInstance *inst0= NULL;
	CaplInstance *user= NULL;
	if(sender==NULL)
	{
		sender= m_api->m_message_mgr.GetCurrentPerson();
		if(!sender)
		{
			AfxMessageBox( APL_T("Для текущего пользователя не указан сотрудник !"),MB_ICONSTOP|MB_OK);
			return 0;
		}
	}
	if(sender==(CaplInstance*)(-1)) sender=0;

	CString sContent=content;

	if((0!=subject && 0!=recipient && content!=_T("") && silent))
	{

		//Создание сообщения
		CaplInstance* message= NULL;
		message= m_data->CreateInstance(e_message);

		m_data->PutAttr(message, a_message_sender, sender);


		if(recipient && m_data->IsKindOf(recipient, m_api->m_appr_mgr.e_person))
			m_data->PutAttr(message, a_message_recipient, recipient);
		else if(recipient && m_data->IsKindOf(recipient, m_api->m_appr_mgr.e_official))
			m_data->PutAttr(message, a_message_rec_offisial, recipient);					
		// ЯАИ: убрал, т.к. раньше этот кусок использовался при отправке через диалог
		// а сейчас только без диалога.
		// else if(!recipient && !recs.GetSize())
		//	m_data->PutAttr(message, a_message_rec_nick, sEMailAddress);					

		if(!copy)
		{
			int contentSize= sContent.GetLength()+1;

			long num= 0;
			bool res;
			LPTSTR szBuf= NULL;
			CString sBlobId;

			if(contentSize<1000)
			{
				m_data->PutAttr(message, a_message_content, sContent);
				sBlobId= _T("");
			}
			else
			{
				szBuf= sContent.GetBuffer(contentSize);
				res=m_data->NET_SaveFileFromMemory(message, a_message_content_source, 
					(BYTE*)szBuf, sContent.GetLength()+1, num,  APL_T("Сохранение тела сообщения"));
				sContent.ReleaseBuffer();
				m_data->GetAttr(message, a_message_content_source, sBlobId);
				sContent= _T("");
			}

			m_data->PutAttr(message, a_message_content_lenght, contentSize);
			m_data->PutAttr(message, a_message_subject, subject);
			m_data->PutAttr(message, a_message_priority, priority);
			m_data->PutAttr(message, a_message_ask_notify, ask_notify);
			m_data->PutAttr(message, a_message_items, ext);
			m_data->PutAttr(message, a_message_copy, copy);
		}
		else
		{
			CString sBlobId,sFormat;
			int contentSize;

			m_data->GetAttr(copy, a_message_content_format, sFormat);
			m_data->PutAttr(message, a_message_content_format, sFormat);
			m_data->GetAttr(copy, a_message_content, sContent);
			m_data->PutAttr(message, a_message_content, sContent);
			m_data->GetAttr(copy, a_message_content_lenght, contentSize);
			m_data->PutAttr(message, a_message_content_lenght, contentSize);
			m_data->GetAttr(copy, a_message_content_source, sBlobId);
			m_data->PutAttr(message, a_message_content_source, sBlobId);
		}

		COleDateTime odt;
		CString sDate;
		m_api->m_data.NET_GetServerDateTime(odt);
		aplDate2String(odt, sDate);	
		m_data->PutAttr(message, a_message_date_send, sDate);
		m_data->PutAttr(message, a_message_date_received, _T("")); 

		CString sStatus=status;
		m_data->PutAttr(message, a_message_status, sStatus);
		m_data->PutAttr(message, a_message_sender_kill, false);		
		if(sStatus==_T("draft") || !recipient)
			m_data->PutAttr(message, a_message_recipient_kill, true);
		else
			m_data->PutAttr(message, a_message_recipient_kill, false);

		bool b=SendMessageToEmlServer(message);

		return message;
	}
	

	// Отправляем через диалог
	int i;
	CMessageDlg dlg;
	//инициализация
	dlg.m_api= m_api;
	dlg.m_message= 0;
	dlg.m_sender= sender;
	dlg.m_recipient= recipient;
	for(i=0; i<ext.GetSize(); i++)
	{
		dlg.m_items_in_list.Add(ext[i]);
	}

	dlg.m_iPriority= priority;
	dlg.m_sSubject= subject;
	dlg.m_sContent= content;
	dlg.m_possibleRecips= possibleRecipient;

	if(0!=sArrayFiles)
	{
		for(int i=0;i<sArrayFiles->GetSize();i++) dlg.m_sArrayFiles4NewMessage.Add(sArrayFiles->GetAt(i));
	}

	AfxInitRichEdit2( );
	CaplSetResourceHandle setres(module_inst);

	if(IDOK!=dlg.DoModal()) return 0;

	CaplInstance *message=dlg.m_message;
	if(0==message) {AfxMessageBox(APL_T("Ошибка при сохранении сообщения!"),MB_ICONSTOP);return 0;} // Такого быть не должно.

	if(MESSAGE_DLG_SEND==dlg.m_iAction) SendDraftMessage(message,false);


/*	if(true==m_AutoUsurpire)
	{
			//обработки доступа для сообщений нет
//			CaplEntity*		et	=NULL;
//			CaplInstance*	cit	=NULL;
//			aplExtent		aet;
//			CString			csAccPat;
//
//			et=e_message;
//
//			if(NULL!=message)
//				if(aplOWN==message->GetAccessmode())
//					aet.Add(message);
//			for(i=0;i<copies.GetSize();i++)
//			{
//				cit=copies.GetAt(i);
//				if(NULL!=cit)
//					if(aplOWN==cit->GetAccessmode())
//						aet.Add(cit);
//			}
//			if(TRUE==m_api->m_options_mgr.GetDefPatern(et,csAccPat))
//				apidata.NET_SetAccessFromPattern(&aet,csAccPat);
		}
	}
	*/

	return message;
}
//**************************************************************************************************************
bool CaplMessageManager::DeleteMessage(CaplInstance *message, bool *end_operation)
{
	if(!message) return false;
	if(message->GetId()==0) return false;
	if(message->GetType()==0) return false;
	if(!m_data) return false;

	CaplInstance *inst= NULL, 
		*inst1= NULL, *copy, *sender, *recip, *person;
	int i;
	i= message->GetId();

	person=GetCurrentPerson();

	inst1= NULL;
	bool bRecipKill= false,bSenderKill= false, bAdminJambaHack= false;
	m_data->GetAttr(message,  a_message_recipient_kill, bRecipKill);
	m_data->GetAttr(message,  a_message_sender_kill, bSenderKill);
	m_data->GetAttr(message, a_message_sender, sender);
	m_data->GetAttr(message, a_message_recipient, recip);
	
	/*Админ может удалить любое сообщение*/
	if(m_api->m_data.IsAdmin())
	{
		static bool bYestoAll=false;
		// Через минуту сбрасываем флаг
		static DWORD t0=0;
		DWORD t1=GetTickCount();
		if((t1-t0)>60*1000) bYestoAll=false;
		t0=t1;
		UINT rez=IDYES;

		if(bYestoAll) bAdminJambaHack=true;
		else
		{
			if(person!=sender && person!=recip)
			{
				//rez=AfxMessageBox( APL_T("Удалить сообщение у получателя и отправителя (удаление из БД)?"), MB_YESNO|MB_YES_TO_ALL|MB_ICONWARNING);
				CaplMessageBox dlg(AfxGetMainWnd(), APL_T("Вы не являетесь ни отправителем ни получателем. \n\nУдалить сообщение из БД?\n\n"),APL_T("Удаление сообщения"),
					MB_YESNO|MB_YES_TO_ALL|MB_ICONWARNING);
				rez=dlg.DoModal();
				if(IDNO==rez)
				{
					if(0!=end_operation) *end_operation=true;
					return false;
				}
				bAdminJambaHack= true;
				if(IDYESTOALL==rez)bYestoAll=true;	
			}
			else
			{
				CaplMessageBox dlg(AfxGetMainWnd(), APL_T("Удалить у всех (удаление из БД)?\n\n(при выборе \"Нет\", сообщение будет удалено только для текущего пользователя)"),APL_T("Удаление сообщения"),
					MB_YESNO|MB_ICONWARNING);
				rez=dlg.DoModal(); 
				if(IDYES==rez) {bAdminJambaHack= true; bYestoAll=true;	}
			}
		}
	}
	else if(!bSenderKill || !bRecipKill)
	{
		if(person!=sender && person!=recip)
		{
			if(m_api->m_ModeInteractive)
				AfxMessageBox( APL_T("Вы не не можете удалить сообщение, т.к. не являетесь ни отправителем ни получателем!"), MB_OK|MB_ICONERROR);
			return false;
		}
	}
	
/*	if(IDNO==MessageBox(NULL, APL_T("Вы действительно хотите удалить сообщение?"), 
		APL_T("Удаление сообщения"), MB_YESNO|MB_ICONWARNING)) return false;*/

	aplExtent ext;//массив рассылки
	m_data->GetAttr(message, a_message_copy, copy);
	
	CaplInstance* ciNewMain=message;
	if(0!=copy)ciNewMain=copy;

	CString sQuery,buf;
	sQuery.Format(_T("SELECT Ext_1 FROM Ext_1{apl_message.copy= #%i} END_SELECT"), ciNewMain->GetId());
	m_api->m_data.NET_QueryEditParse(sQuery, false);
	m_api->m_data.NET_QueryExecute(ext);
	ext.Add(ciNewMain);
	LoadMessageInfo(ext);

	aplExtent aggr;//рабочие объекты
	m_data->GetAttr(message, a_message_items, aggr);
	

	//ЯАИ Переписал старую логику, т.к. работало неправильно, а где было неправильно найти не смог.

	//Этап 1 - отмечаем как удаленное
	int iCount4del=0;
	bool bSave=false;

	for(i= 0; i<ext.GetSize(); i++)
	{
		CaplInstance *msg=ext[i];
		if(ext[i]->GetAccessmode()>aplRO && !m_api->m_data.IsAdmin()) continue;
		m_data->GetAttr(msg, a_message_sender, sender);
		m_data->GetAttr(msg, a_message_recipient, recip);
		if(!recip)
			m_data->GetAttr(msg, a_message_rec_offisial, recip);

		if(person==sender || bAdminJambaHack) {m_data->PutAttr(msg,  a_message_sender_kill, true); bSave=true;}
		if(person==recip  || bAdminJambaHack) {m_data->PutAttr(msg,  a_message_recipient_kill, true);bSave=true;}

		// Можно ли удалять это сообщение
		bool bSenderDel=false, bRecipDel=false;
		m_data->GetAttr(msg,  a_message_sender_kill, bSenderDel);
		m_data->GetAttr(msg,  a_message_recipient_kill, bRecipDel);

		CaplInstance *copy;
		m_data->GetAttr(msg,  a_message_copy, copy);
		if(0!=copy) //Это копия
		{
			if( bRecipDel) iCount4del++; // Можно удалять
		}
		else
		{
			if(0==recip) //Это начальное письмо множ. рассылки
			{
				if( bSenderDel) iCount4del++; // Можно удалять
			}
			else //Это не рассылка
			{
				if( bSenderDel && bRecipDel) iCount4del++; // Можно удалять
			}
		}
	}

	if(iCount4del!=ext.GetSize() && !bAdminJambaHack) // Удалять все копии еще рано, т.к. есть неудаленные
	{
		if(bSave)  m_api->SaveChanges(); 	
		return true; 
	}

	// Можно удалять все копии
	{
		CaplAdminModeProvider adminmode(&(m_api->m_data),-1, -1,DEF_SOURCE);
		for(i= 0; i<ext.GetSize(); i++)
		{
			CaplInstance *msg=ext[i];
			m_data->GetAttr(msg, a_message_content_source, buf);
			if(buf!=_T("")) m_data->NET_DeleteBlob(msg, a_message_content_source);
			m_data->DeleteInstance(msg);		
		}
	}

	m_api->SaveChanges(); // Иначе послед. проверка для e_apl_stored_document будет работать неправильно
	
	// Удаляем файлы
	for(i=0; i<aggr.GetSize(); i++)
	{
		if(!(m_data->IsKindOf(aggr[i], m_api->m_doc_mgr.e_apl_stored_document) ||
			m_data->IsKindOf(aggr[i], m_api->m_doc_mgr.e_apl_assigned_document))) continue;

		//ЯАИ т.к. теперь при пересылке отправляется не копия файла (apl_stored_document)
		// а тот-же самый, то перед удалением проверяем есть ли он в других сообщениях
		CString sSql;
		aplExtent aeItems;
		sSql.Format(_T("SELECT Ext_1 FROM Ext_1 { apl_message.items LIKE #%d } END_SELECT"), aggr[i]->GetId());
		m_api->m_data.NET_QueryEditParse(sSql);
		m_api->m_data.NET_QueryExecute(aeItems);

		if(0==aeItems.GetSize())
		{
			if(m_data->IsKindOf(aggr[i], m_api->m_doc_mgr.e_apl_stored_document))
			{
				m_data->NET_DeleteBlob(aggr[i], m_api->m_doc_mgr.a_apl_stored_document_source);
			}
			m_data->DeleteInstance(aggr[i]);
		}
	}
	
	if(m_AutoSave) m_api->SaveChanges();
	return true;
}
//**************************************************************************************************************
int CaplMessageManager::SendDraftMessage(CaplInstance *message, bool bShowDlg)
{
	if(!m_api->m_data.IsConnected()) return 0;
	if(!message) return 0;
	if(!message->GetId()) return 0;
	if(!message->GetType()) return 0;
	
	bool bCheck= true;  // Можно ли отправлять
	CaplInstance* inst= NULL;
	CString buf;

	m_api->m_data.GetAttr(message, a_message_sender, inst);
	if(!inst) bCheck= false;
	m_api->m_data.GetAttr(message, a_message_rec_offisial, inst);
	if(!inst) m_api->m_data.GetAttr(message, a_message_recipient, inst);
	
	aplExtent copyMessages;
	m_api->m_data.GetAttr(message, a_message_copies, copyMessages);
	
	if(!inst && !copyMessages.GetSize())//пользователь не указан
	{
		m_api->m_data.GetAttr(message, a_message_rec_nick, buf);
		if(buf.IsEmpty())
		{
			bCheck= false;
			AfxMessageBox(APL_T("Отсутстует получатель! Отправка невозможна."),MB_ICONSTOP|MB_OK);
		}

	}
	
	// ЯАИ убрано по просьбе ГРПЗ
	//m_api->m_data.GetAttr(message, a_message_subject, buf);
	//if(buf.IsEmpty()) bCheck= false;
	
	if(bCheck) // Можно отправлять
	{
		CString sDate;
		COleDateTime odt;
		m_api->m_data.NET_GetServerDateTime(odt);
		aplDate2String(odt, sDate);	
		
		if(copyMessages.GetSize())//рассылка
		{
			for(int i= 0; i<copyMessages.GetSize(); i++)
			{
				m_api->m_data.PutAttr(copyMessages[i], a_message_status, _T("sendet"));
				m_api->m_data.PutAttr(copyMessages[i], a_message_recipient_kill, false);
				m_api->m_data.PutAttr(copyMessages[i], a_message_date_send, sDate);
			}
		}
		else
		{
			m_api->m_data.PutAttr(message, a_message_recipient_kill, false);					
		}

		m_api->m_data.PutAttr(message, a_message_status, _T("sendet"));
		m_api->m_data.PutAttr(message, a_message_date_send, sDate);
		
		m_api->m_data.GetAttr(message, a_message_prev_message, inst);
		if(inst)
		{
			CaplAdminModeProvider adminmode; // таким способом делаем переходим только, если надо

			if(inst->GetAccessmode()>aplRW)
			{
				adminmode.SetData(&(m_api->m_data));
				adminmode.StartAdminMode(-1, -1,DEF_SOURCE);
			}
			m_api->m_data.PutAttr(inst, a_message_status, _T("replyed"));
		}
		
		if(m_api->m_AutoSave)m_api->SaveChanges();

		// Отправка копии по обычной почте
		CString sEmalErr;
		bool b=SendMessageToEmlServer(message,&sEmalErr);
		
	
		return 3;
	}
	else if(bShowDlg) return ShowMessageProperty(message);
	
	return 0;
}
//**************************************************************************************************************
bool CaplMessageManager::GetAllLostMessages(aplExtent &out_extent, 
		CString sStartDate, CString sEndDate)
{
	bool bRes= false;
	
	out_extent.Clear();

	if(!m_data) return bRes;
	
	CaplInstance* person= m_api->m_appr_mgr.GetCurrentPerson();

	if(!person) return bRes;
	if(person->GetType()==0) return bRes;
	if(person->GetId()!=0)
	{
		aplExtent offisials;
		m_api->m_appr_mgr.GetPersonOfficials(person, offisials, true);
		
		CaplLoadData ld(m_data,DEF_SOURCE);
		bool bLoad= false;

		for(int j= 0; j<offisials.GetSize(); j++)
		{
			//
			CaplAttrValue val[3];
			aplExtent ext;
			
			val[0].value.Set(offisials[j]);
			val[0].attr= a_message_rec_offisial;

			m_data->NET_FindInstancesWithAttrValues(e_message, 1, &val[0], ext, false);
			
			for(int n= 0; n<ext.GetSize(); n++)
			{
				ld.AddQuery(0, ext[n], true); bLoad= true;
			}
		}
		if(bLoad)
		{
			if(!ld.LoadEx()){
				if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
					if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
				}
			}
		}
		
		CaplInstance *recipient= NULL, *recip_off= NULL;
		aplExtent ext;
		CString buf;
		bRes= m_data->GetEntityExtent(e_message, ext);
		for(int i= 0; i<ext.GetSize(); i++)
		{
			if(ext[i]->GetAccessmode()>aplRO) continue;

			m_data->GetAttr(ext[i], a_message_rec_offisial, recip_off);
			if(recip_off)
			{
				for(int j= 0; j<offisials.GetSize(); j++)
				{
					if(recip_off==offisials[j])
					{
						CString str;
						m_data->GetAttr(ext[i], a_message_subject, str);
						m_data->GetAttr(ext[i], a_message_recipient, recipient);
						if(recipient!=NULL && recipient!=person) 
						{
							out_extent.Add(ext[i]);
						}
						break;
					}
				}
			}
		}
	}

	return bRes;
}
//**************************************************************************************************************
bool CaplMessageManager::GetPersonDraftedMessages(aplExtent &out_extent, CaplInstance* person, bool bWithFolder)
{
	out_extent.Clear();
	if(!m_api->m_data.IsConnected()) return false;

	bool bKill;
	if(!person) person= m_api->m_message_mgr.GetCurrentPerson();
	if(!person) return false;

	CaplInstance* sender= NULL, *inst;
	aplExtent ext;
	CString buf;
	CString sSql,sSender,sCondition=_T("");
	sSender.Format(_T(".sender=#%i"),person->GetId());

	if(!bWithFolder  )sCondition+=_T(" AND .sender_folder=#0");

	sSql.Format(_T("SELECT Ext_1 FROM Ext_1 { apl_message(%s %s AND .status='draft') } END_SELECT"), LPCTSTR(sSender),LPCTSTR(sCondition));
	if(m_api->m_data.NET_QueryEditParse(sSql))
	{
		m_api->m_data.NET_QueryExecute(ext);	
		LoadMessageInfo(ext);
	}

	bool bNeedSave= false;

	for(int i= 0; i<ext.GetSize(); i++)
	{
		if(ext[i]->GetAccessmode()>aplRO) continue;

		m_api->m_data.GetAttr(ext[i], a_message_status, buf);
		if(buf!=_T("draft")) continue;
		
		m_api->m_data.GetAttr(ext[i], a_message_sender, sender);
		if(sender!=person) continue;

		m_data->GetAttr(ext[i], a_message_sender_kill, bKill);
		if(bKill) continue;
		
		m_data->GetAttr(ext[i], a_message_sender_folder, inst);
		if(inst && !bWithFolder) continue;					
		
		m_data->GetAttr(ext[i], a_message_copy, inst);
		if(inst) continue;
		
		out_extent.Add(ext[i]);
	}

	return true;
}
//**************************************************************************************************************
bool CaplMessageManager::GetPersonSendedMessages(aplExtent &out_extent, CaplInstance* person, bool bWithDeleted, bool bWithFolder)
{
	out_extent.Clear();

	if(!m_api->m_data.IsConnected()) return false;
	CaplInstance* sender= NULL, *inst;
	aplExtent ext;
	CString buf;
	bool bKill;

	if(!person) person= m_api->m_message_mgr.GetCurrentPerson();
	if(!person) return false;

	CString sSql,sSender,sCondition=_T("");
	sSender.Format(_T(".sender=#%i"),person->GetId());

	if(!bWithDeleted) sCondition+=_T(" AND .sender_kill!=true");
	if(!bWithFolder  )sCondition+=_T(" AND .sender_folder=#0");

	sSql.Format(_T("SELECT Ext_1 FROM Ext_1 { apl_message(%s %s) } END_SELECT"), LPCTSTR(sSender),LPCTSTR(sCondition));
	if(m_api->m_data.NET_QueryEditParse(sSql))
	{
		m_api->m_data.NET_QueryExecute(ext);	
		LoadMessageInfo(ext);
	}
	
	bool bNeedSave= false;

	for(int i= 0; i<ext.GetSize(); i++)
	{
		if(ext[i]->GetAccessmode()>aplRO) continue;

		m_api->m_data.GetAttr(ext[i], a_message_sender, sender);
		if(sender!=person) continue;
		
		m_api->m_data.GetAttr(ext[i], a_message_status, buf);
		if(buf==_T("draft")) continue;

		m_data->GetAttr(ext[i], a_message_sender_kill, bKill);
		if(bKill && !bWithDeleted) continue;
		
		m_data->GetAttr(ext[i], a_message_sender_folder, inst);
		if(inst && !bWithFolder) continue;					
		
		m_data->GetAttr(ext[i], a_message_copy, inst);
		if(inst) continue;
		
		out_extent.Add(ext[i]);
	}

	return true;
}
//**************************************************************************************************************
bool CaplMessageManager::GetPersonIncomingMessages(aplExtent &out_extent, CaplInstance* person, bool bWithDeleted, bool bWithLosted, bool bWithFolder)
{
	int i;
	CString buf;

	out_extent.Clear();

	if(!m_api->m_data.IsConnected()) return false;

	if(!person) person= m_api->m_message_mgr.GetCurrentPerson();
	if(!person) return false;

	CString sSql,sRecipient,sCondition=_T("");
	aplExtent ext;

	sRecipient.Format(_T(".recipient=#%i"),person->GetId());
	aplExtent offisials;
	m_api->m_appr_mgr.GetPersonOfficials(person, offisials, bWithLosted);
	for(i= 0; i<offisials.GetSize(); i++)
	{
	buf.Format(_T(" OR .recipient_official=#%i"),offisials[i]->GetId());
	sRecipient+=buf;
	}

	if(!bWithDeleted) sCondition+=_T(" AND .recipient_kill!=true");
	if(!bWithFolder  )sCondition+=_T(" AND .recipient_folder=#0");

	sSql.Format(_T("SELECT Ext_1 FROM Ext_1 { apl_message((%s)%s) } END_SELECT"), LPCTSTR(sRecipient),LPCTSTR(sCondition));
	if(m_api->m_data.NET_QueryEditParse(sSql))
	{
		m_api->m_data.NET_QueryExecute(ext);	
		LoadMessageInfo(ext);
	}

	CaplInstance* recipient= NULL, *inst;
	bool bKill;
	
	CString sDate;	
	COleDateTime odt;

	if(!m_api->m_data.NET_GetServerDateTime(odt))
	{
		if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS)
		{
			if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
		}
		return false;
	}

	aplDate2String(odt, sDate);	
	
	bool bNeedSave= false;
	bool bNewMes= false;

	aplExtent myMessagesExt;
	aplExtent myNewMessages;

	for(i= 0; i<ext.GetSize(); i++)
	{
		if(ext[i]->GetAccessmode()>aplRO) continue;

		//сначала разбираемся кому послали
		m_api->m_data.GetAttr(ext[i], a_message_recipient, recipient);
		if(!recipient)
		{
			m_api->m_data.GetAttr(ext[i], a_message_rec_offisial, inst);
			if(!inst) continue;
			
			if(-1!=offisials.Find(inst))
			{
				myMessagesExt.Add(ext[i]);
				bNeedSave= true;
			}
			else continue;

		}
		else if(recipient!=person)
		{
			if(bWithLosted)
			{
				m_api->m_data.GetAttr(ext[i], a_message_rec_offisial, inst);
				if(-1==offisials.Find(inst))
					continue;
			}
			else continue;
		}

		m_api->m_data.GetAttr(ext[i], a_message_status, buf);
		if(buf==_T("draft")) continue;

		m_api->m_data.GetAttr(ext[i], a_message_recipient_kill, bKill);
		if(bKill && !bWithDeleted) continue;

		m_api->m_data.GetAttr(ext[i], a_message_recipient_folder, inst);
		if(inst && !bWithFolder) continue;

		m_api->m_data.GetAttr(ext[i], a_message_date_received, buf);
		if(buf.IsEmpty())
		{
			myNewMessages.Add(ext[i]);
			bNewMes= true;
		}
		
		out_extent.Add(ext[i]);
	}
	
	if(bNeedSave || bNewMes)
	{
		CaplAdminModeProvider adminmode(&(m_api->m_data),-1, -1,DEF_SOURCE);

		for(i= 0; i<myMessagesExt.GetSize(); i++)
			m_api->m_data.PutAttr(myMessagesExt[i], a_message_recipient, person);
		for(i= 0; i<myNewMessages.GetSize(); i++)
			m_api->m_data.PutAttr(myNewMessages[i], a_message_date_received, sDate);
		if(m_AutoSave)
			m_api->SaveChanges();
	}

	return bNewMes;
}
//**************************************************************************************************************
bool CaplMessageManager::GetAllMessages(aplExtent &out_extent)
{
	bool bRes= false;
	
	out_extent.Clear();

	if(!m_data) return bRes;
	
	CaplInstance* inst;
	CaplInstance* person= m_api->m_message_mgr.GetCurrentPerson();
	if(!person) return bRes;
	if(person->GetType()==0) return bRes;
	if(person->GetId()!=0)
	{
		CaplLoadData ld(m_data,DEF_SOURCE);

		ld.AddQuery(_T('b'), person->GetId(), e_message, a_message_recipient, true, true);
		if(!ld.LoadEx()){
			if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
				if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
			}
		}
	
		ld.ClearQuery();
		ld.AddQuery(_T('b'), person->GetId(), e_message, a_message_sender, true, true);
		if(!ld.LoadEx()){
			if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
				if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
			}
		}
		
		ld.ClearQuery();

		aplExtent offisials;
		m_api->m_appr_mgr.GetPersonOfficials(person, offisials);
		bool bLoad= false;

		for(int j= 0; j<offisials.GetSize(); j++)
		{
			//
			CaplAttrValue val[3];
			aplExtent ext;
			
			val[0].value.Set(offisials[j]);
			val[0].attr= a_message_rec_offisial;
			val[1].value.Set(_T("sendet"));
			val[1].attr= a_message_status;
			CaplInstance* prs= NULL;
			val[2].value.Set(prs);
			val[2].attr= a_message_recipient;

			m_data->NET_FindInstancesWithAttrValues(e_message, 3, &val[0], ext, false);
			
			if(0!=ext.GetSize())
			{
				bLoad= true;
				for(int m= 0; m<ext.GetSize(); m++)
					ld.AddQuery(0, ext[m], true);
			}
		}
		if(bLoad)
		{
			if(!ld.LoadEx()){
				if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
					if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
				}
			}
		}
		
		CaplInstance* sender= NULL, *recipient= NULL;
		aplExtent ext;
		CString buf;
		bool bKill;
		bRes= m_data->GetEntityExtent(e_message, ext);
		for(int i= 0; i<ext.GetSize(); i++)
		{
			if(ext[i]->GetAccessmode()>aplRO) continue;

			m_data->GetAttr(ext[i], a_message_recipient, recipient);
			m_data->GetAttr(ext[i], a_message_sender, sender);
			if(recipient==NULL)//возможно, сообщение отправлено на должность сотрудника
			{
				m_data->GetAttr(ext[i], a_message_rec_offisial, inst);

				for(int j= 0; j<offisials.GetSize(); j++)
				{
					if(inst==offisials[j])
					{
						m_data->PutAttr(ext[i], a_message_recipient, person);
						if(m_AutoSave)m_data->NET_SaveChanges();
						recipient= person;
						break;
					}
				}
			}
			if(recipient==person && sender==person)
			{
				m_data->GetAttr(ext[i], a_message_recipient_folder, inst);
				if(inst)
				{
					m_data->GetAttr(ext[i], a_message_sender_folder, inst);
					if(inst) continue; //он и как получатель и как отправитель зафигачил в папку
				}
				m_data->GetAttr(ext[i], a_message_copy, inst);
				if(inst)
				{
//					m_data->GetAttr(ext[i], a_message_rec_offisial, inst);
//					if(!inst) continue; //это копия сообщения
				}
			}
			else if(sender==person)
			{
				m_data->GetAttr(ext[i], a_message_sender_kill, bKill);
				if(bKill) continue;
				m_data->GetAttr(ext[i], a_message_sender_folder, inst);
				if(inst) continue;					
				m_data->GetAttr(ext[i], a_message_copy, inst);
				if(inst) continue;
			}
			else if(recipient==person)
			{
				m_data->GetAttr(ext[i], a_message_recipient_kill, bKill);
				if(bKill) continue;
				m_data->GetAttr(ext[i], a_message_recipient_folder, inst);
				if(inst) continue;
				m_data->GetAttr(ext[i], a_message_status, buf);
				if(buf==_T("draft")) continue;
			}
			
			out_extent.Add(ext[i]);
		}
	}
	
	return bRes;
}
//**************************************************************************************************************
bool CaplMessageManager::LoadMessageInfo(aplExtent &messages)
{
	bool bRes= true;
	if(m_data==0) return false;
	if(e_message==0) return false;
	if(messages.GetSize()==0) return true;// если ничего нет - должно быть true. иначе сыплется в CaplAPI::LoadExtentInfo!
	
	CaplLoadData ld(m_data,DEF_SOURCE);
	int n=0;
	for(int i= 0; i<messages.GetSize(); i++)
	{
		if(!messages[i]->GetId()) continue;
		if(!messages[i]->GetType()) continue;

		ld.AddQuery(0, messages[i],true);
		n++;

		if(n>=m_MaxItemsLoad  || i==(messages.GetSize()-1))
		{
			n=0;
			int k=ld.AddQuery(_T('r'), 0, e_message, a_message_copy, true, true);

			if(!m_api->m_bLoadedOrgstruct)
			{
				ld.AddQuery(_T('d'), 0, 0, a_message_sender, true, true);
				ld.AddQuery(_T('d'), 0, 0, a_message_recipient, true, true);
				ld.AddQuery(_T('d'), k, 0, a_message_sender, true, true);
				ld.AddQuery(_T('d'), k, 0, a_message_recipient, true, true);


			}

			if(!ld.LoadEx())
			{
				if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS)
				{
					if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
					bRes=false;
				}
			}
		}
	}

	return bRes;
}
//**************************************************************************************************************
bool CaplMessageManager::LoadMessageInfo(CaplInstance* message)
{
	bool bRes= false;
	if(m_data==0) return bRes;
	if(message==0) return bRes;
	if(message->GetId()==0) return bRes;
	if(message->GetType()==0) return bRes;
	if(e_message==0) return bRes;

	aplExtent messages;
	messages.Add(message);
	return LoadMessageInfo(messages);
}
//**************************************************************************************************************
bool CaplMessageManager::LoadAlertsInfo(aplExtent &alerts)
{
	bool bRes= false;
	if(m_data==0) return bRes;
	if(e_alert==0) return bRes;
	if(!alerts.GetSize()) return bRes;
	
	CaplLoadData ld(m_data,DEF_SOURCE);

	bool bLoad= false;
	for(int i= 0; i<alerts.GetSize(); i++)
	{
		if(!alerts[i]->GetId()) continue;
		if(!alerts[i]->GetType()) continue;

		ld.AddQuery(0, alerts[i], true);
		bLoad= true;
	}
	
	if(bLoad)
	{
		if(!ld.LoadEx()){
			if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
				if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
			}
			else
				bRes= true;
		}
		else
			bRes= true;
	}
	
	return bRes;
}
//**************************************************************************************************************
bool CaplMessageManager::LoadAlertInfo(CaplInstance* alert)
{
	bool bRes= false;
	if(m_data==0) return bRes;
	if(alert==0) return bRes;
	if(alert->GetId()==0) return bRes;
	if(alert->GetType()==0) return bRes;
	if(e_alert==0) return bRes;

	aplExtent alerts;
	alerts.Add(alert);
	return LoadAlertsInfo(alerts);
}
//**************************************************************************************************************
bool CaplMessageManager::ShowDispMessageState(CaplInstance *message)
{
	if(m_data==0) return false;
	if(message==0) return false;
	if(message->GetId()==0) return false;
	if(message->GetType()==0) return false;
	if(e_message==0) return false;

	CDispMessageDlg dlg;

	dlg.m_api= m_api;
	dlg.m_message= message;

	CaplSetResourceHandle setres(module_inst);

	if(IDOK==dlg.DoModal())
	{
		
	}
	return true;
}
//**************************************************************************************************************
bool CaplMessageManager::IsUserSenderOrRecipient(CaplInstance *message)
{
	if(!m_data) return false;
	if(!m_data->IsConnected()) return false;
	if(!message) return false;

	CaplInstance *inst,*curUser= m_api->m_appr_mgr.GetCurrentPerson();
	
	// Отправитель - ?
	m_data->GetAttr(message, a_message_sender, inst);
	if(inst==curUser) return true;

	// Получатель - ?
	CaplInstance *recipient,*recip_official,*offic_pers=0;
	m_data->GetAttr(message, a_message_recipient, recipient);
	if(recipient==curUser)  return true;

	// по абоненту
	m_data->GetAttr(message, a_message_rec_offisial, recip_official);
	if(recip_official!=0)
	{
		m_data->GetAttr(recip_official, m_api->m_appr_mgr.a_official_act_person, offic_pers);
		if(curUser==offic_pers)return true;

		m_data->GetAttr(recip_official, m_api->m_appr_mgr.a_official_owner, offic_pers);
		if(curUser==offic_pers)return true; //-V649
	}

	// копии при множетвенной рассылке
	aplExtent ext;
	m_data->GetAttr(message, a_message_copies, ext);
	int i;
	for(i=0;i<ext.GetSize();i++)
	{
		CaplInstance *msg=ext[i];
		m_data->GetAttr(msg, a_message_recipient, recipient);
		if(recipient==curUser)  return true;

		m_data->GetAttr(msg, a_message_rec_offisial, recip_official);
		if(recip_official!=0)
		{
			m_data->GetAttr(recip_official, m_api->m_appr_mgr.a_official_act_person, offic_pers);
			if(curUser==offic_pers)return true;

			m_data->GetAttr(recip_official, m_api->m_appr_mgr.a_official_owner, offic_pers);
			if(curUser==offic_pers)return true; //-V649
		}
	}
	return false;
}

//**************************************************************************************************************
bool CaplMessageManager::RunExternalCmd(LPCTSTR cmd)
{
	if(!m_api->m_data.IsConnected()) return false;

	// Запуск во внешнем exe
	CString buf,psw,sCmdLine=_T("* ");
	sCmdLine+=_T("\""); sCmdLine+=_T("/user="); sCmdLine+=m_api->m_data.GetNameCurrUser(); sCmdLine+=_T("\"");
	m_api->m_data.NET_GetString4DupSession(psw);
	sCmdLine+=_T(" \""); sCmdLine+=_T("/password="); sCmdLine+=psw; sCmdLine+=_T("\"");
	sCmdLine+=_T(" \""); sCmdLine+=_T("/db="); sCmdLine+=m_api->m_data.GetNameCurrConnection(); sCmdLine+=_T("\"");

	if (0!=cmd) {sCmdLine+=_T(" "); sCmdLine+=cmd; }


	DWORD PID_extcmd=CaplMPC::StartProcess(_T("extcmd.exe"),sCmdLine);
	if(0==PID_extcmd||-1==PID_extcmd )
	{
		AfxMessageBox( APL_T("Ошибка запуска extcmd.exe!"),MB_ICONSTOP|MB_OK);
		return false;
	}
	return true;
}
//**************************************************************************************************************
bool CaplMessageManager::OpenMessageNoModal(CaplInstance *message)
{
	if(0==message) return true;

	/*CMessageDlg *dlg=new CMessageDlg;
	//инициализация
	dlg->m_api= m_api;
	dlg->m_message= message;
	dlg->Create(IDD_MESSAGE,0);
	int nResult = dlg->ShowWindow(SW_SHOW);*/

	// Запуск во внешнем exe

	CString buf;
	buf.Format(_T("/showitem=#%i"),message->GetId());
	return RunExternalCmd(buf);
}


bool CaplMessageManager::CreateMessageExternal(CStringArray *sArrayFiles)
{
	CString cmd=_T("/cmd=CreareMessage");

	if(0!=sArrayFiles)
	{
		CString buf;
		int i;
		for(i=0;i<sArrayFiles->GetSize();i++)
		{
			if(i>0)buf+=_T("|");
			buf+=sArrayFiles->GetAt(i);
		}
		if(buf!=_T("")) {cmd+=_T(" \"/files4add="); cmd+=buf; cmd+=_T("\"");}

	}
	return RunExternalCmd(cmd);
}


bool CaplMessageManager::ReplyExternal(CaplInstance *message)
{
	if(0==message) return false;
	if(message->IsDeleted()) return false;
	if(!m_api->m_data.IsKindOf(message,e_message))return false;

	CString buf;
	buf.Format(_T("/cmd=ReplyMessage  /item=#%i"),message->GetId());
	return RunExternalCmd(buf);
}

bool CaplMessageManager::ForwardExternal(CaplInstance *message)
{
	if(0==message) return false;
	if(message->IsDeleted()) return false;
	if(!m_api->m_data.IsKindOf(message,e_message))return false;

	CString buf;
	buf.Format(_T("/cmd=ForwardMessage /item=#%i"),message->GetId());
	return RunExternalCmd(buf);

	return true;
}

#ifdef _UNICODE
#pragma message ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Юникодный вариант CRTF_HTMLConverter::ConvertRTF2HTML сохраняет тело письма в кривой RTF c 1251 !!!!!!!!!!!!!!")
#endif
//**************************************************************************************************************
int CaplMessageManager::ShowMessageProperty(CaplInstance *message, bool bNoActionMode)
{
//	return 0 -> при выходе
//	return 1 -> при удалении
//	return 2 -> при пересылке или ответе
//	return 3 -> при отправке из черновиков

	int bRes= -1;
	
	if(!m_data) return bRes;
	if(!m_data->IsConnected()) return bRes;
	if(!message) return bRes;
	
	//2010.10.01 ayatsk - проверяем является ли текущий пользователь получателем или отправителем.
	// если нет - не открываем
	if(!IsUserSenderOrRecipient(message))
	{
		if(m_data->IsAdmin())
		{
			if(IDYES!=AfxMessageBox(APL_NO_T("Вы не являетесь ни получателем, ни отправителем (но являетесь администратором).\nВы действительно хотите открыть сообщение?!"),MB_ICONSTOP|MB_YESNO))
			return 0;
		}
		else
		{
			AfxMessageBox(APL_NO_T("Вы не являетесь ни получателем, ни отправителем.\nПоэтому не можете открыть сообщение!"),MB_ICONSTOP|MB_OK);
			return 0;
		}
	}

	CString buf;
	m_data->GetAttrBN(message, S::status, buf);

	if(buf==_T("sendet"))  // Омечаем как открытое
	{
		CaplInstance *curUser= m_api->m_appr_mgr.GetCurrentPerson();
		CaplInstance *inst= NULL;
		m_data->GetAttr(message, a_message_recipient, inst);

		// Проверяем на абонента
		CaplInstance *recipient,*recip_official,*offic_pers=0;
		m_data->GetAttr(message, a_message_recipient, recipient);
		m_data->GetAttr(message, a_message_rec_offisial, recip_official);
		if(recip_official!=0) m_data->GetAttr(recip_official, m_api->m_appr_mgr.a_official_act_person, offic_pers);

		if( curUser && ( (curUser==inst) || (curUser==offic_pers) ))
		{
			CaplAdminModeProvider adminmode(&(m_api->m_data),-1, -1,DEF_SOURCE);
			m_data->PutAttr(message, a_message_status, _T("readed"));
			adminmode.ExitAdminMode();
			if(m_AutoSave) m_data->NET_SaveChanges();
		}
	}

	CaplSetResourceHandle setres(module_inst);

	AfxInitRichEdit2();
	
	aplExtent ext;
	m_data->GetAttr(message, a_message_items, ext);
	LoadExtentInfo(ext);
	
	CMessageDlg dlg;
	//инициализация
	dlg.m_api= m_api;
	dlg.m_message= message;
	dlg.m_bNoAction=bNoActionMode;
	
	int nResult = dlg.DoModal();
	setres.RestoreHandle();
	
	if(IDOK==nResult)
	{
		switch(dlg.m_iAction)
		{
			case 0:
				bRes= 0;
				break;
			case MESSAGE_DLG_REPLY: //Ответить
				if(Reply(message))
					bRes= 2;
				break;
			case MESSAGE_DLG_REPLY_ALL: //Ответить всем
				if(Reply(message,true))
					bRes= 2;
				break;
			case 2: //Ответить на исходное
				bRes= 0;
				break;
			case MESSAGE_DLG_FORWARD: //Переправили
				if(Forward(message))
					bRes= 2;
				break;
			case MESSAGE_DLG_DELETE: //Удалили
				if(DeleteMessage(message))
					bRes= 1;
				break;
			case MESSAGE_DLG_SEND: //Отправили из черновиков

				SendDraftMessage(message,false);
				bRes= 3;
				break;
			case MESSAGE_DLG_SAVE: //Сохранили в черновиках
				bRes= 0;
				break;
		}
	}
	else //Отмена
	{
		if(MESSAGE_DLG_OPEN_NOMODAL==dlg.m_iAction)
			OpenMessageNoModal(message);
	}
	return bRes;
}
//************************************************************************************************************** 
CaplInstance* CaplMessageManager::Reply(CaplInstance *message, bool bForAll)
{
	CaplInstance* mes= NULL;
	
	if(0==m_data) return 0;
	if(0==message) return 0;
	if(0==message->GetType()) return 0;
	
	LoadMessageInfo(message);

	aplExtent ext;
	m_data->GetAttr(message, a_message_items, ext);
	LoadExtentInfo(ext);
	
	AfxInitRichEdit2();
	CMessageDlg dlg;
	//инициализация
	dlg.m_api= m_api;
	dlg.m_message= 0;

	CString sRecipName,sRecipCopyName, sSenderName, sDate,sBuf;
	CaplInstance* inst= NULL;
	int i;

	m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_date_send, sBuf);
	sDate= sBuf.Mid(6,2)+_T(".")+sBuf.Mid(4,2)+_T(".")+sBuf.Mid(0,4)+_T(" ")+sBuf.Mid(8,2)+_T(":")+sBuf.Mid(10,2)+_T(":")+sBuf.Mid(12,2);

	CaplInstance* person= m_api->m_appr_mgr.GetCurrentPerson();
	dlg.m_sender= person;

	m_data->GetAttr(message, a_message_sender, inst);
	if(inst) m_api->m_appr_mgr.GetPersonName(inst, sSenderName);
	dlg.m_recipient= inst;


	// Определяем всех получателей
	CaplInstance *parent_msg;
	m_data->GetAttr(message, a_message_copy, parent_msg);
	if(0==parent_msg) parent_msg=message;
	if(parent_msg!=message) LoadMessageInfo(parent_msg);

	aplExtent ext_copies;
	m_data->GetAttr(parent_msg, a_message_copies, ext_copies);

	if(0==ext_copies.GetSize())
	{
		m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_recipient, inst);
		if(inst) m_api->m_appr_mgr.GetPersonName(inst, sRecipName);
	}
	else
	{
		LoadMessageInfo(ext_copies);
		aplExtent ext_prev_recepients, ext_prev_recepients_copy;
		int i;
		CaplInstance *inst1;
		for(i=0;i<ext_copies.GetSize();i++)
		{
			CaplInstance *msg=ext_copies[i];
			apidata.GetAttr(msg, m_api->m_message_mgr.a_message_rec_offisial, inst1);
			if(!inst1)//отправляли именно этому person'у
				apidata.GetAttr(msg, m_api->m_message_mgr.a_message_recipient, inst1);

			if(0==inst1) continue;

			bool b_rec_copy;
			apidata.GetAttr(msg, m_api->m_message_mgr.a_message_recipient_in_copy, b_rec_copy);
			if(b_rec_copy)  ext_prev_recepients_copy.Add(inst1);
			else  ext_prev_recepients.Add(inst1);
		}

		for(i=0;i<ext_prev_recepients.GetSize();i++)
		{
			CaplInstance *pers=ext_prev_recepients[i];
			m_api->m_appr_mgr.GetPersonName(pers, sBuf);
			if(sRecipName!=_T("")) sRecipName+=_T("; ");
			sRecipName+=sBuf;

			if(bForAll && pers!=person)  dlg.m_recipients.Add(pers);
		}

		for(i=0;i<ext_prev_recepients_copy.GetSize();i++)
		{
			CaplInstance *pers=ext_prev_recepients_copy[i];
			m_api->m_appr_mgr.GetPersonName(pers, sBuf);
			if(sRecipCopyName!=_T("")) sRecipCopyName+=_T("; ");
			sRecipCopyName+=sBuf;

			if(bForAll && pers!=person)  dlg.m_recipients_copy.Add(pers);
		}

		if(bForAll && dlg.m_recipients.GetSize()>0 && 0!=dlg.m_recipient) 
		{
			dlg.m_recipients.Insert(0,dlg.m_recipient);
			dlg.m_recipient=0;
		}
	}


	CString sSubject;
	m_data->GetAttr(message, a_message_subject, sSubject);

	sBuf=sSubject;

	if(-1!=sBuf.Find(_T("Fw: ")) || -1!=sBuf.Find(_T("Re: ")))
		sBuf.Delete(0, 4);

	dlg.m_sSubject= _T("Re: ")+sBuf;

	GetMessageContent(message, dlg.m_sContent);
	/*int iConLenght; 

	apidata.GetAttr(message, m_api->m_message_mgr.a_message_content_lenght, iConLenght);
	CString sBlobId;
	apidata.GetAttr(message, m_api->m_message_mgr.a_message_content_source, sBlobId);
	//if(iConLenght<1000)
	if(sBlobId==_T(""))
	{
		apidata.GetAttr(message, m_api->m_message_mgr.a_message_content, dlg.m_sContent);
	}
	else//зафигачили недетское сообщение...
	{
		LPTSTR szBuf= dlg.m_sContent.GetBuffer(iConLenght); 
		long size= iConLenght;
		apidata.NET_LoadBlob2Memory(message, m_api->m_message_mgr.a_message_content_source, (BYTE*)szBuf, size);
		dlg.m_sContent.ReleaseBuffer();
	}*/

	int iConLenght= dlg.m_sContent.GetLength();

	apidata.GetAttr(message, m_api->m_message_mgr.a_message_content_format, dlg.m_sContentFormat);

	// Так извращенчески сделано, чтобы проще было выделять жирным
	dlg.m_sTextArray4Insert.Add(_T("\n"));
	dlg.m_sTextArray4Insert.Add(APL_T("|-------- Исходное сообщение --------\n"));
	dlg.m_sTextArray4Insert.Add(APL_T("|От: "));
	dlg.m_sTextArray4Insert.Add(sSenderName);
	dlg.m_sTextArray4Insert.Add(APL_T("\n"));
	dlg.m_sTextArray4Insert.Add(APL_T("|Кому: "));
	dlg.m_sTextArray4Insert.Add(sRecipName);
	dlg.m_sTextArray4Insert.Add(APL_T("\n"));	
	if(sRecipCopyName!=_T(""))
	{
		dlg.m_sTextArray4Insert.Add(APL_T("|Копия: "));
		dlg.m_sTextArray4Insert.Add(sRecipCopyName);
		dlg.m_sTextArray4Insert.Add(_T("\n"));	
	}
	dlg.m_sTextArray4Insert.Add(APL_T("|Отослано: "));
	dlg.m_sTextArray4Insert.Add(sDate);
	dlg.m_sTextArray4Insert.Add(_T("\n"));	
	dlg.m_sTextArray4Insert.Add(APL_T("|Тема: "));
	dlg.m_sTextArray4Insert.Add(sSubject);
	dlg.m_sTextArray4Insert.Add(_T("\n\n"));

	dlg.m_bInsetQuote=true;

	CaplSetResourceHandle setres(module_inst);
	if(IDOK==dlg.DoModal())
	{
		CaplInstance *mes=dlg.m_message;
		if(0==mes) {AfxMessageBox(APL_T("Ошибка при сохранении в диалоге!"),MB_ICONSTOP);return 0;} // По идее такого быть не должно
		
		if(MESSAGE_DLG_SEND==dlg.m_iAction  || MESSAGE_DLG_SAVE==dlg.m_iAction)
		{
			mes=dlg.m_message;
			m_data->PutAttr(mes, a_message_prev_message, message);
			aplExtent ext_copy;
			m_data->GetAttr(mes, a_message_copies, ext_copy);

			for(i= 0; i<ext_copy.GetSize(); i++)//груповая рассылка
			{
				CaplInstance* mes1= ext_copy[i];
				if(!mes1) continue;
				m_data->PutAttr(mes1, a_message_prev_message, message);
			}
			if(m_AutoSave) m_data->NET_SaveChanges();

			if(MESSAGE_DLG_SEND==dlg.m_iAction) SendDraftMessage(mes);
		}
	}
	return mes;
}


//**************************************************************************************************************
CaplInstance* CaplMessageManager::Forward(CaplInstance *message)
{
	CaplInstance* mes= NULL;
	
	int i;
	if(!m_data) return 0;
	if(!message) return 0;
	
	AfxInitRichEdit2();

	LoadMessageInfo(message);
	
	CMessageDlg dlg;
	//инициализация
	dlg.m_api= m_api;
	dlg.m_message= 0;
	dlg.m_recipient= 0;

	CaplInstance *curUser= m_api->m_appr_mgr.GetCurrentPerson();
	CaplInstance *inst= NULL;
	m_data->GetAttr(message, a_message_recipient, inst);

	dlg.m_sender= curUser;
	CString sBuf;
	CString sSubject;
	m_data->GetAttr(message, a_message_subject, sSubject);
	sBuf= sSubject;
	if(-1!=sBuf.Find(_T("Fw: ")) || -1!=sBuf.Find(_T("Re: ")))
		sBuf.Delete(0, 4);

	dlg.m_sSubject= _T("Fw: ")+sBuf;
	
	GetMessageContent(message,dlg.m_sContent);
	
	/*
	m_data->GetAttr(message, a_message_content, sBuf);
	dlg.m_sContent= sBuf;

	int iConLenght;
	apidata.GetAttr(message, m_api->m_message_mgr.a_message_content_lenght, iConLenght);
	CString sBlobId;
	apidata.GetAttr(message, m_api->m_message_mgr.a_message_content_source, sBlobId);
	//if(iConLenght<1000)
	if(sBlobId==_T(""))
	{
		apidata.GetAttr(message, m_api->m_message_mgr.a_message_content, dlg.m_sContent);
		#ifdef _UNICODE
		ConvertUCS2ToMsgSting(dlg.m_sContent,false);
		#endif
	}
	else//зафигачили недетское сообщение...
	{
		//LPTSTR szBuf= dlg.m_sContent.GetBuffer(iConLenght);
		//long size= iConLenght;
		//apidata.NET_LoadBlob2Memory(message, a_message_content_source, (BYTE*)szBuf, size);
		//dlg.m_sContent.ReleaseBuffer();

		if(iConLenght>0)
		{
			unsigned char *tmpBuf=new  unsigned char[iConLenght+2];  
			long size=iConLenght;// приведение типов - в будущем GetAttr-ы надо тоже на long переделать
			apidata.NET_LoadBlob2Memory(message, a_message_content_source, (BYTE*)tmpBuf, size);
			if(size>2) // Меньше 2х не бывае - считаем пустое
			{
				tmpBuf[size]='\0'; tmpBuf[size+1]='\0';

				#ifdef _UNICODE
					//так не работает !!! 
					//CStringW ucsStr((LPCSTR)tmpBuf);
					//m_sContent=ucsStr;
					// надо так	
					ConvertMultiByteToMsgSting(tmpBuf,size, dlg.m_sContent);
				#else
					 dlg.m_sContent=tmpBuf;
				#endif
			}
			delete tmpBuf;
		}
	}*/

	apidata.GetAttr(message, a_message_content_format, dlg.m_sContentFormat);

	CaplInstance* person;
	CString sRecipName,sRecipCopyName;
	CString sSenderName;
	CString sDate;
	m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_sender, person);
	if(person) m_api->m_appr_mgr.GetPersonName(person, sSenderName);

	if(sSenderName==_T(""))
	{
		m_api->m_data.GetAttr(message, a_message_send_nick, sSenderName);
		if(sSenderName==_T("")) sSenderName=  APL_T("Система");
	}

	// Если пользователь пересылает свое сообщение, то записывать информацию о пересылке нельзя, 
	// т.к. ломается логика информации о получении
	bool bNoForwardInfo=false; 
	if(dlg.m_sender==person) bNoForwardInfo=true; 

	// Определяем всех получателей исх. письма
	CaplInstance *parent_msg;
	m_data->GetAttr(message, a_message_copy, parent_msg);
	if(0==parent_msg) parent_msg=message;
	if(parent_msg!=message) LoadMessageInfo(parent_msg);

	aplExtent ext_copies;
	m_data->GetAttr(parent_msg, a_message_copies, ext_copies);

	if(0==ext_copies.GetSize())
	{
		m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_recipient, person);
		if(person) m_api->m_appr_mgr.GetPersonName(person, sRecipName);
	}
	else
	{
		LoadMessageInfo(ext_copies);
		aplExtent ext_prev_recepients, ext_prev_recepients_copy;
		int i;
		CaplInstance *inst1;
		for(i=0;i<ext_copies.GetSize();i++)
		{
			CaplInstance *msg=ext_copies[i];
			apidata.GetAttr(msg, m_api->m_message_mgr.a_message_rec_offisial, inst1);
			if(!inst1)//отправляли именно этому person'у
				apidata.GetAttr(msg, m_api->m_message_mgr.a_message_recipient, inst1);

			if(0==inst1) continue;

			bool b_rec_copy;
			apidata.GetAttr(msg, m_api->m_message_mgr.a_message_recipient_in_copy, b_rec_copy);
			if(b_rec_copy)  ext_prev_recepients_copy.Add(inst1);
			else  ext_prev_recepients.Add(inst1);
		}

		for(i=0;i<ext_prev_recepients.GetSize();i++)
		{
			CaplInstance *pers=ext_prev_recepients[i];
			m_api->m_appr_mgr.GetPersonName(pers, sBuf);
			if(sRecipName!=_T("")) sRecipName+=_T("; ");
			sRecipName+=sBuf;
		}

		for(i=0;i<ext_prev_recepients_copy.GetSize();i++)
		{
			CaplInstance *pers=ext_prev_recepients_copy[i];
			m_api->m_appr_mgr.GetPersonName(pers, sBuf);
			if(sRecipCopyName!=_T("")) sRecipCopyName+=_T("; ");
			sRecipCopyName+=sBuf;
		}
	}

	m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_date_send, sBuf);
	sDate= sBuf.Mid(6,2)+_T(".")+sBuf.Mid(4,2)+_T(".")+sBuf.Mid(0,4)+_T(" ")+sBuf.Mid(8,2)+_T(":")+sBuf.Mid(10,2)+_T(":")+sBuf.Mid(12,2);
	
	// Так извращенчески сделано, чтобы проще было выделять жирным
	dlg.m_sTextArray4Insert.Add(_T("\n"));
	//dlg.m_sTextArray4Insert.Add(APL_T("-------- Исходное сообщение --------\n"));
	dlg.m_sTextArray4Insert.Add(APL_T("-------- Перенаправленное сообщение --------\n"));
	dlg.m_sTextArray4Insert.Add(APL_T("|От: "));
	dlg.m_sTextArray4Insert.Add(sSenderName);
	dlg.m_sTextArray4Insert.Add(_T("\n"));
	dlg.m_sTextArray4Insert.Add(APL_T("|Кому: "));
	dlg.m_sTextArray4Insert.Add(sRecipName);
	dlg.m_sTextArray4Insert.Add(_T("\n"));	
	if(sRecipCopyName!=_T(""))
	{
		dlg.m_sTextArray4Insert.Add(APL_T("|Копия: "));
		dlg.m_sTextArray4Insert.Add(sRecipCopyName);
		dlg.m_sTextArray4Insert.Add(_T("\n"));	
	}
	dlg.m_sTextArray4Insert.Add(APL_T("|Отослано: "));
	dlg.m_sTextArray4Insert.Add(sDate);
	dlg.m_sTextArray4Insert.Add(_T("\n"));	
	dlg.m_sTextArray4Insert.Add(APL_T("|Тема: "));
	dlg.m_sTextArray4Insert.Add(sSubject);
	dlg.m_sTextArray4Insert.Add(_T("\n\n"));

	aplExtent aggr;
	m_data->GetAttr(message, a_message_items, aggr);
	LoadExtentInfo(aggr);
	
	for(i= 0; i<aggr.GetSize(); i++) 
	{
		inst= aggr[i];
		dlg.m_items_in_list.Add(inst);
	}


	CaplSetResourceHandle setres(module_inst);
	if(IDOK==dlg.DoModal())
	{
		CaplInstance *mes=dlg.m_message;
		if(0==mes) {AfxMessageBox(APL_T("Ошибка при сохранении в диалоге!"),MB_ICONSTOP);return 0;} // По идее такого быть не должно

		if(MESSAGE_DLG_SEND==dlg.m_iAction  || MESSAGE_DLG_SAVE==dlg.m_iAction)
		{
			mes=dlg.m_message;
			if(!bNoForwardInfo)
			{
				m_data->PutAttr(mes, a_message_prev_message, message); 
				m_data->PutAttr(message, a_message_forward, mes);
			}
			aplExtent ext_copy;
			m_data->GetAttr(mes, a_message_copies, ext_copy);

			for(i= 0; i<ext_copy.GetSize(); i++)//груповая рассылка
			{
				CaplInstance* mes1= ext_copy[i];
				if(!mes1) continue;
				if(!bNoForwardInfo) m_data->PutAttr(mes1, a_message_prev_message, message);
			}
			if(m_AutoSave) m_data->NET_SaveChanges();

			if(MESSAGE_DLG_SEND==dlg.m_iAction) SendDraftMessage(mes);
		}
	}
	return mes;
}
//**************************************************************************************************************
CaplInstance* CaplMessageManager::SendNotifyMessage(CaplInstance *sender, aplExtent &recipients, 
				const TCHAR *subject, const TCHAR *content, aplExtent &items, bool bSubscribe)
{
	int i;
	if(m_data==NULL || !m_data->IsConnected()) return NULL;
	if(e_message==NULL) return NULL;

	if(subject==NULL || subject[0]==0) return NULL;
	
	CaplAggr aggr;
	for(i= 0; i<items.GetSize(); i++) aggr.Add(items[i]);
	
	CaplInstance* rootMessage= NULL;
	aplExtent copies;
	int iLenght= _strlen(content);

	CString sDate;
	COleDateTime odt;
	m_api->m_data.NET_GetServerDateTime(odt);
	aplDate2String(odt, sDate);

	bool bNeedToSave = false;
	
	if(recipients.GetSize()>1 && bSubscribe)
	{
		rootMessage= m_data->CreateInstance(e_message);
		if(rootMessage)
		{
			m_data->PutAttr(rootMessage, a_message_sender, sender);
			m_data->PutAttr(rootMessage, a_message_subject, subject);
			m_data->PutAttr(rootMessage, a_message_content, content);
			m_data->PutAttr(rootMessage, a_message_content_lenght, iLenght);
			m_data->PutAttr(rootMessage, a_message_priority, 1);
			m_data->PutAttr(rootMessage, a_message_ask_notify, false);
			m_data->PutAttr(rootMessage, a_message_items, items);
			m_data->PutAttr(rootMessage, a_message_date_send, sDate);
			m_data->PutAttr(rootMessage, a_message_status, _T("sendet"));

			bNeedToSave = true;
		}
	}

	for(i= 0; i<recipients.GetSize(); i++)
	{
		CaplInstance* message= m_data->CreateInstance(e_message);
		if(message)
		{
			m_data->PutAttr(message, a_message_sender, sender);
			if(m_data->IsKindOf(recipients[i],m_api->m_appr_mgr.e_person))
				m_data->PutAttr(message, a_message_recipient, recipients[i]);
			else if(m_data->IsKindOf(recipients[i],m_api->m_appr_mgr.e_official))
				m_data->PutAttr(message, a_message_rec_offisial, recipients[i]);	
			m_data->PutAttr(message, a_message_subject, subject);
			m_data->PutAttr(message, a_message_content, content);
			m_data->PutAttr(message, a_message_content_lenght, iLenght);
			
			m_data->PutAttr(message, a_message_priority, 1);
			m_data->PutAttr(message, a_message_ask_notify, false);
			m_data->PutAttr(message, a_message_items, aggr);

			m_data->PutAttr(message, a_message_date_send, sDate);
			m_data->PutAttr(message, a_message_status, _T("sendet"));
			m_data->PutAttr(message, a_message_recipient_kill, false);

			if(sender==NULL)
				m_data->PutAttr(message, a_message_sender_kill, true);
			else
				m_data->PutAttr(message, a_message_sender_kill, false);
			
			if(rootMessage)
				m_data->PutAttr(message, a_message_copy, rootMessage);
			
			copies.Add(message);

			bNeedToSave = true;
		}
	}
	
	if(recipients.GetSize()>1 && bSubscribe && rootMessage)
	{
		m_data->PutAttr(rootMessage, a_message_copies, copies);

		bNeedToSave = true;
	}

	if (m_AutoSave && bNeedToSave) m_data->NET_SaveChanges();
	if (m_AutoUsurpire)
	{
		int AutoUzurpire;
		m_api->m_options_mgr.GetOptionValueBN( APL_NO_T("Автоматически становиться владельцем\\Автоматически становиться владельцем создаваемых почтовых сообщений"),AutoUzurpire,0);
		if(AutoUzurpire==1) 
		{
			if(!m_AutoSave) m_data->NET_SaveChanges();
			aplExtent ext; if(rootMessage) ext.Add(rootMessage);ext.Append(copies);
			m_data->NET_BecomeInstancesOwner(&ext);
		}
	}

	return rootMessage;
}

bool CaplMessageManager::SendGroupMessage(CaplInstance *sender, aplExtent &possible_recipients, aplExtent &recipients,const TCHAR *subject, const TCHAR *content, aplExtent &items)
{
	return false;
}


CaplInstance* CaplMessageManager::CreateSubscribe(CaplInstance* item, CaplInstance* person, bool bSave)
{
	if(m_data==NULL || !m_data->IsConnected()) return NULL;
	if(e_subscr==NULL) return NULL;
	if(item==NULL) return NULL;
	if(item->GetId()==0) return NULL;
	if(item->GetType()==0) return NULL;

	CaplInstance* inst= NULL, *sbscr= NULL;
	
	if(person==NULL)
	{
		person= m_api->m_appr_mgr.GetCurrentPerson();
		if(!person)
		{
			MessageBox(NULL,  APL_T("Для текущего пользователя не указан сотрудник !"),
				 APL_T("Ошибка"), MB_ICONSTOP|MB_OK);
			return NULL;
		}
	}

	aplExtent sbscr_list;
	bool bNew= true;
	GetPersonSubscribeList(person, sbscr_list);
	for(int i= 0; i<sbscr_list.GetSize(); i++)
	{
		inst= NULL;
		m_data->GetAttr(sbscr_list[i], a_subscr_item, inst);
		if(inst==item) //пользователь на память слаб...
		{
			sbscr= sbscr_list[i];
			bNew= false;
			break;
		}
	}

	if(bNew)
	{
		sbscr= m_data->CreateInstance(e_subscr);
		if(sbscr)
		{
			m_data->PutAttr(sbscr, a_subscr_item, item);
			m_data->PutAttr(sbscr, a_subscr_subscriber, person);
			if(bSave) m_data->NET_SaveChanges();
		}
	}

	return sbscr;
}

CaplInstance* CaplMessageManager::CreateSubscribe(CaplInstance* item, aplExtent &persons, bool bSave)
{
	if(m_data==NULL || !m_data->IsConnected()) return NULL;
	if(e_subscr==NULL) return NULL;
	if(item==NULL) return NULL;
	if(item->GetId()==0) return NULL;
	if(item->GetType()==0) return NULL;

	CaplInstance* inst= NULL, *sbscr= NULL;
	
	if(persons.Size==0) return 0;

	for(int p=0; p<persons.Size;p++)
	{
		aplExtent sbscr_list;
		bool bNew= true;
		GetPersonSubscribeList(persons[p], sbscr_list);
		for(int i= 0; i<sbscr_list.GetSize(); i++)
		{
			inst= NULL;
			m_data->GetAttr(sbscr_list[i], a_subscr_item, inst);
			if(inst==item) //пользователь на память слаб...
			{
				sbscr= sbscr_list[i];
				bNew= false;
				break;
			}
		}

		if(bNew)
		{
			sbscr= m_data->CreateInstance(e_subscr);
			if(sbscr)
			{
				m_data->PutAttr(sbscr, a_subscr_item, item);
				m_data->PutAttr(sbscr, a_subscr_subscriber, persons[p]);
			}
		}

	}
	if(bSave) m_data->NET_SaveChanges();
	return sbscr;
}

bool CaplMessageManager::DeleteSubscribe(CaplInstance* sbscr, bool bSave)
{
	if(m_data==NULL || !m_data->IsConnected()) return false;
	if(e_subscr==NULL) return false;
	if(sbscr==NULL) return false;
	if(sbscr->GetId()==0) return false;
	if(sbscr->GetType()==0) return false;
	
/*	if(IDYES==MessageBox(NULL, APL_T("Вы действительно хотите удалить подписку на элемент?") , 
		APL_T("Удаление подписки"),
		MB_ICONQUESTION|MB_YESNO))
*/	{
		m_data->DeleteInstance(sbscr);
		if(bSave) m_data->NET_SaveChanges();
	}

	return true;
}

bool CaplMessageManager::EditItemSubscribeList(CaplInstance* item)
{
	if(m_data==NULL || !m_data->IsConnected()) return false;
	if(e_subscr==NULL) return false;

	CaplSetResourceHandle setres(module_inst);
	
	CItemSubscribeDlg dlg;
	dlg.m_api= m_api;
	dlg.m_item= item;
	
	dlg.DoModal();

	return true;
}

bool CaplMessageManager::GetPersonSubscribeList(CaplInstance* person, aplExtent &ext)
{
	if(m_data==NULL || !m_data->IsConnected()) return false;
	if(e_subscr==NULL) return false;
	
	CaplInstance* inst= NULL;
	
	if(person==NULL)
	{
		person= m_api->m_appr_mgr.GetCurrentPerson();
		if(!person)
		{
			MessageBox(NULL,  APL_T("Для текущего пользователя не указан сотрудник !"),
				 APL_T("Ошибка"), MB_ICONSTOP|MB_OK);
			return false;
		}
	}

	CaplLoadData ld(m_data,DEF_SOURCE);
	int n;
n=	ld.AddQuery(_T('b'), person->GetId(), e_subscr, a_subscr_subscriber, true);
	ld.AddQuery(_T('d'), n, 0, a_subscr_subscriber, true);
	ld.AddQuery(_T('d'), n, 0, a_subscr_item, true);
	if(!ld.LoadEx()){
		if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
			if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
		}
	}
	
	aplExtent ext0;
	m_data->GetEntityExtent(e_subscr, ext0);
	for(int i= 0; i<ext0.GetSize(); i++)
	{
		inst= NULL;
		m_data->GetAttr(ext0[i], a_subscr_subscriber, inst);
		if(inst==person)
			ext.Add(ext0[i]);
	}

	return true;
}

bool CaplMessageManager::GetItemsSubscribers(CaplInstance* item, aplExtent &ext)
{
	if(m_data==NULL || !m_data->IsConnected()) return false;
	if(e_subscr==NULL) return false;
	if(item==NULL) return false;
	if(item->GetId()==0) return false;
	if(item->GetType()==0) return false;
	
	CaplInstance* inst= NULL;

	CaplLoadData ld(m_data,DEF_SOURCE);
	int n;
n=	ld.AddQuery(_T('b'), item->GetId(), e_subscr, a_subscr_item, true);
	ld.AddQuery(_T('d'), n, 0, a_subscr_subscriber, true);
	ld.AddQuery(_T('d'), n, 0, a_subscr_item, true);
	if(!ld.LoadEx()){
		if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
			if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
		}
	}
	
	aplExtent ext0;
	ext.Clear();
	
	m_data->GetEntityExtent(e_subscr, ext0);
	for(int i= 0; i<ext0.GetSize(); i++)
	{
		inst= NULL;
		m_data->GetAttr(ext0[i], a_subscr_item, inst);
		if(inst==item)
		{
			inst= NULL;
			m_data->GetAttr(ext0[i], a_subscr_subscriber, inst);
			if(inst)
				ext.Add(inst);
		}
	}

	return true;
}

CaplInstance* CaplMessageManager::CreateAlert(CaplInstance* person, CaplInstance* item, const TCHAR* text, const TCHAR* code, 
	COleDateTime alert_date, COleDateTime next_date, COleDateTimeSpan alert_span, 
	CString sModule, CString sFunction,CaplInstance* author,bool delete_completed,bool notify_author)
{
	CaplInstance* alert= NULL;
	
	if(!person) return alert;
	if(m_data==NULL || !m_data->IsConnected()) return alert;
//	if(sModule.IsEmpty() || sFunction.IsEmpty()) return alert;

	alert= m_data->CreateInstance(e_alert);
	if(alert)
	{
		CString buf;
		m_data->PutAttr(alert, a_alert_person, person);
		if(item) m_data->PutAttr(alert, a_alert_item, item);
		if(text!=NULL) m_data->PutAttr(alert, a_alert_text, text);
		if(code!=NULL) m_data->PutAttr(alert, a_alert_code, code);
		if(author) m_data->PutAttr(alert, a_alert_author, author);
		m_data->PutAttr(alert, a_alert_state, CString(m_api->m_message_mgr.m_alert_state_active));
		m_data->PutAttr(alert, a_alert_delete_completed, delete_completed);
		m_data->PutAttr(alert, a_alert_notify_author, notify_author);
				
		aplDate2String(alert_date, buf);
		m_data->PutAttr(alert, a_alert_date, buf);
		aplDate2String(next_date, buf);
		m_data->PutAttr(alert, a_alert_next_date, buf);
		m_data->PutAttr(alert, a_alert_span, alert_span.m_span);
		
		if(!sModule.IsEmpty()) m_data->PutAttr(alert, a_alert_module, sModule);
		if(!sFunction.IsEmpty())m_data->PutAttr(alert, a_alert_function, sFunction);

		if(m_AutoSave)
		{
			m_data->NET_SaveChanges();
		}
	}

	return alert;
}
bool CaplMessageManager::LoadPersonAlerts(CaplInstance* person,  bool bAsAuthor, bool bAsPerson, aplExtent &ext_alerts)
{
	ext_alerts.Clear();
	if(false==bAsPerson && false==bAsAuthor) return false;
	
	if(0==person) person=m_api->m_appr_mgr.GetCurrentPerson();
	if(0==person) return false;

	CString buf, sQuery;
	sQuery=_T("SELECT_LOAD_ALL Ext_ FROM Ext_{apl_alert(@)} END_SELECT");
	
	if(bAsAuthor) buf=_T(".author=#%i");
	if(bAsPerson)
	{
		if(buf!=_T("")) buf+=_T(" OR ");
		buf+=_T(".person=#%i  OR .person->apl_official.actual_person=#%i");
	}
	sQuery.Replace(_T("@"),buf);
	
	buf.Format(_T("%i"),person->GetId());
	sQuery.Replace(_T("%i"),buf);
	if(m_data->NET_QueryEditParse(sQuery))
	{
		return m_data->NET_QueryExecute(ext_alerts);
	}
	return false;
}


bool CaplMessageManager::FindAlerts(aplExtent &alerts, 
	CaplInstance* person, CaplInstance* item, const TCHAR* code)
{
	alerts.Clear();
 
	if(m_data==NULL || !m_data->IsConnected()) return false;
	if(!e_alert) return false;
	
	if(person && (person->GetId()==0 || person->GetType()==0)) person= NULL; 
	if(item && (item->GetId()==0 || item->GetType()==0)) item= NULL;
	if(!person && !item) return false;//для простоты

	aplExtent ext_alerts;

	//если указан чувак
	if(person) LoadPersonAlerts(person, false, true, ext_alerts);
	//если указан объект
	else if(item)
	{
		CaplLoadData ld(m_data,DEF_SOURCE);
		ld.AddQuery(_T('b'), item->GetId(), e_alert, a_alert_item, true, true);
		if(!ld.LoadEx())
		{
			if(apidata.GetLastAplError()!=APL_SOCK_OPERATION_IN_PROGRESS){
				if(m_api->m_ModeInteractive)AfxMessageBox(S::ErrorLoadDataMgr(),MB_OK|MB_ICONSTOP);
			}
		}
	}
	
	//что-то добавилось точно...
	aplExtent ext;
	CaplInstance* pers_inst, *item_inst;
	CString sCode(code), buf;
	
	m_data->GetEntityExtent(e_alert, ext);
	
	for(int i= 0; i<ext.GetSize(); i++)
	{
		pers_inst= NULL;
		item_inst= NULL;
		buf= _T("");

		if(person) m_data->GetAttr(ext[i], a_alert_person, pers_inst);
		if(m_data->IsKindOf(pers_inst,m_api->m_appr_mgr.e_official))
			m_data->GetAttr(pers_inst, m_api->m_appr_mgr.a_official_act_person, pers_inst);

		if(item) m_data->GetAttr(ext[i], a_alert_item, item_inst);
		if(!sCode.IsEmpty()) m_data->GetAttr(ext[i], a_alert_code, buf);

		if(person==pers_inst && item==item_inst && !sCode.Compare(buf))
			alerts.Add(ext[i]);
	}

	return alerts.GetSize()?true:false;
}

bool CaplMessageManager::DeleteAlert(CaplInstance* alert)
{
	if(m_data==NULL || !m_data->IsConnected()) return false;
	if(!alert) return false;
	if(0==alert->GetType()) return false;

	if(!m_data->IsKindOf(alert,e_alert)) return false;
	
	// проверка автора


	CaplInstance *autor;
	m_data->GetAttr(alert, a_alert_author, autor);

	CaplInstance *person=m_api->m_message_mgr.GetCurrentPerson();


	if(autor!=person)
	{
		if(m_data->IsAdmin())
		{
			if(IDYES!=AfxMessageBox( APL_T("Вы не являетесь автором напоминания! \n\nВсе равно удалить?"),MB_YESNO|MB_ICONSTOP))
			return false;
		}
		else
		{
			AfxMessageBox( APL_T("Вы не являетесь автором напоминания и не можете его удалить!"),MB_OK|MB_ICONSTOP);
			return false;
		}
	}


	
	m_data->DeleteInstance(alert);
	if(m_AutoSave)
	{
		m_data->NET_SaveChanges();
	}
	return true;
}

bool CaplMessageManager::SetAlertState(CaplInstance* alert,const TCHAR *state)
{
	if(m_data==NULL || !m_data->IsConnected()) return false;
	if(!alert) return false;
	if(alert->GetAccessmode()>aplRW)
	{
		aplErrorMessage(S::NoAccessRight());
		return false;
	}
	if(!state) return false;
	
	CaplInstance *cur_person=m_api->m_message_mgr.GetCurrentPerson();
	if(!cur_person) return false;
	CString buf=_T(""),s_state=state;
	if(CString(state)==CString(m_alert_state_active)) state=m_alert_state_active;
	else if(CString(state)==CString(m_alert_state_completed)) state=m_alert_state_completed;
	else return false; 

	CaplInstance *author=0;
	CaplInstance *person=0;
	m_data->GetAttr(alert, a_alert_author,author);
	m_data->GetAttr(alert, a_alert_person,person);
	if(m_data->IsKindOf(person,m_api->m_appr_mgr.e_official))
		m_data->GetAttr(person, m_api->m_appr_mgr.a_official_act_person, person);
	
	if(cur_person!=author && cur_person!=person)
	{
		if(!m_api->m_message_mgr.IsAdmin()) return false;
	}

	m_data->GetAttr(alert, a_alert_state,buf);
	if(buf==s_state) return true;
	if(state==m_alert_state_completed)
	{
		bool bDelete=false;
		bool bNotify=false;
		CString alert_text;
		m_data->GetAttr(alert, a_alert_delete_completed,bDelete);
		m_data->GetAttr(alert, a_alert_delete_completed,bDelete);
		m_data->GetAttr(alert, a_alert_notify_author,bNotify);
		m_data->GetAttr(alert, a_alert_text,alert_text);
		if(bNotify && author)
		{
			CString sSubject= APL_T("Напоминание");
			CString sContent= APL_T("Напоминание: '")+alert_text+_T("'");
			if(person)
			{
				m_api->GetItemName(person,buf);
				sSubject+= APL_T(" для ")+buf;
				sContent+= APL_T(" для ")+buf;
			}
			sSubject+= APL_T(" выполнено.");
			sContent+= APL_T(" выполнено.Напоминание было");
			if(bDelete)
			{
				sContent+= APL_T(" автоматически удалено");
			}
			else
			{
				sContent+= APL_T(" помечено как выполненное, удалите его вручную при необходимости.");
			}

			aplExtent recipientes;
			recipientes.Add(author);
			aplExtent wo;
			m_api->m_message_mgr.SendNotifyMessage(cur_person, recipientes,	sSubject, sContent, wo);
		}
		if(bDelete) 
			m_data->DeleteInstance(alert);
		else
			m_data->PutAttr(alert, a_alert_state,state);
	}
	else
	{
		COleDateTime dt=dt.GetCurrentTime();
		aplDate2String(dt, buf);
		m_data->PutAttr(alert, a_alert_next_date, buf);
		m_data->PutAttr(alert, a_alert_state,state);
	}
	
	if(m_AutoSave)
	{
		m_data->NET_SaveChanges();
	}
	return true;
}


bool CaplMessageManager::ShowAlert(CaplInstance* alert)
{
	bool bRes= false;

	CaplSetResourceHandle setres(module_inst);
	
	CAlertDlg dlg;
	dlg.m_api= m_api;
	dlg.m_alert= alert;

	if(IDOK==dlg.DoModal())
	{
		bRes= true;
	}

	return bRes;
}

bool CaplMessageManager::ShowAlert(aplExtent &alert)
{
	return false;
}

bool CaplMessageManager::ShowAlertProperties(CaplInstance *alert)
{
	if(!alert) return false;
	if(alert->GetType()==0) return false;

	bool bRes= false;

	CaplSetResourceHandle setres(module_inst);
	
	CAlertPropertiesDlg dlg;
	dlg.m_api= m_api;
	dlg.m_alert= alert;

	if(IDOK==dlg.DoModal())
	{
		bRes= true;
	}
	
	if(m_AutoSave)
	{
		m_data->NET_SaveChanges();
	}
	return bRes;
}

bool CaplMessageManager::ShowPersonAlerts(CaplInstance *person)
{
	if(!person) return false;
	if(person->GetType()==0) return false;

	CaplSetResourceHandle setres(module_inst);
	
	CShowAlertsDlg dlg;
	dlg.m_api= m_api;
		
	dlg.DoModal();
	return true;
}

CaplInstance *CaplMessageManager::CreateAlert(CaplInstance *recipient)
{
	CaplInstance *new_alert= 0;
	CaplSetResourceHandle setres(module_inst);
	
	CAlertPropertiesDlg dlg;
	dlg.m_api= m_api;
	dlg.m_recipient=recipient;

	if(IDOK==dlg.DoModal())
	{
		new_alert= dlg.m_alert;
	}
	return new_alert;
}

bool CaplMessageManager::AddItemToAlert(CaplInstance *alert,CaplInstance *item)
{
	if(!alert) return false;
	if(alert->GetType()==0) return false;
	if(!item) return false;
	if(item->GetType()==0) return false;
	if(alert->GetAccessmode()>aplRW)
	{
		aplErrorMessage(S::NoAccessRight());
		return false;
	}
	CaplInstance *cur_person = m_api->m_appr_mgr.GetCurrentPerson();
	bool m_bIsAdmin=m_api->m_appr_mgr.IsAdmin();
	CaplInstance *author=0,*recipient=0;
	m_api->m_data.GetAttr(alert,a_alert_author,author);
	if(!m_bIsAdmin && author!=cur_person) 
	{
		if(m_api->m_ModeInteractive)
			AfxMessageBox( APL_T("У Вас нет прав на добавление/замену ассоциированных с напоминанием объектов!"));
		return false;
	}
	
	CaplInstance *old_item=0;
	m_api->m_data.GetAttr(alert,a_alert_item,old_item);
	if(old_item)
	{
		if(old_item==item) 
			return true;
		else
		{
			if(m_api->m_ModeInteractive)
			{
				if(IDNO==AfxMessageBox( APL_T("С напоминанием уже ассоциирован объект. Заменить ?"),MB_YESNO|MB_ICONQUESTION)) 
					return false;
			}
		}
	}
	m_api->m_data.PutAttr(alert,a_alert_item,item);
	if(m_AutoSave)
	{
		m_data->NET_SaveChanges();
	}
	return true;

}

bool CaplMessageManager::DeleteItemFromAlert(CaplInstance *alert)
{
	if(!alert) return false;
	if(alert->GetType()==0) return false;
	if(alert->GetAccessmode()>aplRW)
	{
		aplErrorMessage(S::NoAccessRight());
		return false;
	}
	CaplInstance *cur_person = m_api->m_appr_mgr.GetCurrentPerson();
	bool m_bIsAdmin=m_api->m_appr_mgr.IsAdmin();
	CaplInstance *author=0,*recipient=0;
	m_api->m_data.GetAttr(alert,a_alert_author,author);
	if(!m_bIsAdmin && author!=cur_person) 
	{
		if(m_api->m_ModeInteractive)
			AfxMessageBox( APL_T("У Вас нет прав на удаление ассоциированных с напоминанием объектов!"));
		return false;
	}
	
	CaplInstance *item=0;
	m_api->m_data.GetAttr(alert,a_alert_item,item);
	if(!item)
	{
		if(m_api->m_ModeInteractive)
			AfxMessageBox( APL_T("Нет ассоциированных объектов!"));
		return false;
	}
	m_api->m_data.PutAttr(alert,a_alert_item,(CaplInstance*)0);
	if(m_AutoSave)
	{
		m_data->NET_SaveChanges();
	}
	return true;

}

bool CaplMessageManager::GetMessageContent(CaplInstance *message,CString &content)
{
	/* Заменено на код далее
	content=_T("");
	if(!message) return false;
	if(message->GetAccessmode()>aplRO)return false;
	//чтение тела сообщения
	CString cnt_src;
	m_api->m_data.GetAttr(message,m_api->m_message_mgr.a_message_content_source,cnt_src);
	if(cnt_src!=_T(""))
	{
		int cnt_length;
		m_api->m_data.GetAttr(message,m_api->m_message_mgr.a_message_content_lenght,cnt_length);
		BYTE* cnt_buf=new BYTE[cnt_length];
		m_api->m_data.NET_LoadFileFromSrv2Memory(cnt_src,cnt_buf,(long&)cnt_length, APL_T("Загрузка содержания сообщения..."));
		content=cnt_buf;
		delete [] cnt_buf;
	}
	else
	{
		m_api->m_data.GetAttr(message,m_api->m_message_mgr.a_message_content,content);
	}
	return true;*/

	content=_T("");
	if(0==message) return false;
	if(message->IsDeleted()) return false;
	if(message->GetAccessmode()>aplRO) return false;

	m_data->GetAttr(message, a_message_content, content);

	int iConLenght;
	apidata.GetAttr(message, a_message_content_lenght, iConLenght);
	CString sBlobId;
	apidata.GetAttr(message, a_message_content_source, sBlobId);
	//if(iConLenght<1000)
	if(sBlobId==_T(""))
	{
		apidata.GetAttr(message, a_message_content, content);
		#ifdef _UNICODE
		ConvertUCS2ToMsgSting(content, false);
		#endif
	}
	else//зафигачили недетское сообщение...
	{
		//LPTSTR szBuf= dlg.m_sContent.GetBuffer(iConLenght);
		//long size= iConLenght;
		//apidata.NET_LoadBlob2Memory(message, a_message_content_source, (BYTE*)szBuf, size);
		//dlg.m_sContent.ReleaseBuffer();

		if(iConLenght>0)
		{
			unsigned char *tmpBuf=new  unsigned char[iConLenght+2];  
			long size=iConLenght;// приведение типов - в будущем GetAttr-ы надо тоже на long переделать
			apidata.NET_LoadBlob2Memory(message, a_message_content_source, (BYTE*)tmpBuf, size);
			if(size>2) // Меньше 2х не бывае - считаем пустое
			{
				tmpBuf[size]='\0'; tmpBuf[size+1]='\0';

				#ifdef _UNICODE
					//так не работает !!! 
					//CStringW ucsStr((LPCSTR)tmpBuf);
					//m_sContent=ucsStr;
					// надо так	
					ConvertMultiByteToMsgSting(tmpBuf,size, content);
				#else
					 content=tmpBuf;
				#endif
			}
			delete tmpBuf;
		}
	}
	return true;
}

bool CaplMessageManager::SetMessageContent(CaplInstance *message,CString &content)
{
	if(!message) return false;
	if(message->GetAccessmode()>aplRO)return false;
	
	int contentSize= content.GetLength()+1;
	long num= 0;
	bool res;
	LPTSTR szBuf= NULL;
		
	if(contentSize<1000)
	{
		m_data->PutAttr(message, a_message_content, content);
	}
	else
	{
		szBuf= content.GetBuffer(contentSize);
		res=m_data->NET_SaveFileFromMemory(message, a_message_content_source, 
			(BYTE*)szBuf, content.GetLength()+1, num,  APL_T("Сохранение тела сообщения"));
		content.ReleaseBuffer();
		m_data->PutAttr(message, a_message_content_lenght, contentSize);
	}
	return true;
}

bool CaplMessageManager::Add2AddressBook(CaplInstance *item)
{
	if(item == 0) return false;
	aplExtent items;
	items.Add(item);
	return Add2AddressBook(items);
}

bool CaplMessageManager::Add2AddressBook(aplExtent &items)
{
	int i;
	if(items.GetSize() == 0) return false;

	CaplInstance* address_book = GetAddressBook(true);
	if(address_book == 0)
	{
		AfxMessageBox(APL_T("Ошибка при созданиии объекта адресной книги"),MB_ICONERROR);
		return false;
	}
	for(i=0;i<items.GetSize();i++)
	{
		if(!m_data->IsKindOf(items[i],m_api->m_appr_mgr.e_person) && !m_data->IsKindOf(items[i],m_api->m_appr_mgr.e_official))
		{
			if(items.GetSize()==1)AfxMessageBox(APL_T("Объект не подходит для занесения в адресную книгу"),MB_ICONERROR);
			else AfxMessageBox(APL_T("Один из объектов не подходит для занесения в адресную книгу"),MB_ICONERROR);
			return false;
		}
	}

	aplExtent ext;
	ext.Add(address_book);
	CaplInstance* address_book_object = SelectInstance(APL_MODE_SELECT_FOLDER,APL_T("Выберите папку"),&ext);
	if(address_book_object == 0) return false;
	
	m_api->m_folder_mgr.AddToFolder(address_book_object,items);
	
	return true;
}

CaplInstance* CaplMessageManager::GetAddressBook(bool create)
{
	aplExtent adressbooks;
	CaplInstance* curr_user = m_api->m_data.GetCurrUser();
	CaplInstance* address_book = 0;

	CaplAttrValue find_values[2];
	find_values[0].attr = m_api->m_folder_mgr.a_folder_parent;
	find_values[0].value.Set(curr_user);
	find_values[1].attr = m_api->m_folder_mgr.a_folder_name;
	find_values[1].value.Set(APL_NO_T("адресная книга"));
	
	m_api->m_data.NET_FindInstancesWithAttrValues(m_api->m_folder_mgr.e_folder, 2, &find_values[0], adressbooks, false);

	if(adressbooks.GetSize()>0)
	{
		address_book = adressbooks[0];
	}
	if(address_book != 0)
	{
		//m_api->m_folder_mgr.LoadFolderInfo(address_book);
		CaplLoadData ld(m_data,DEF_SOURCE);

		if(address_book->GetType()==0)
		{
			address_book = 0;
		}
		else if(!m_data->IsKindOf(address_book,m_api->m_folder_mgr.e_folder))
		{
			address_book = 0;
		}
		else
		{
			ld.AddQuery(0,address_book,true);
			ld.LoadEx();
		}
	}
	if(address_book==0 && create)
	{
		address_book = m_api->m_folder_mgr.CreateFolder(curr_user,APL_NO_T("Адресная книга"),0);
	}

	return address_book;
}

bool CaplMessageManager::GetMessageRecipients(CaplInstance *item,aplExtent &recipients)
{
	if(item == 0) return false;

	CaplInstance* inst;
	aplExtent copyes;
	int j;

	// сначала поле "получатель"
	apidata.GetAttr(item, a_message_recipient, inst);
	// если не оно - то поле "абонент"
	if(inst==0)apidata.GetAttr(item, a_message_rec_offisial, inst);
	// если и оно пустое - ищем в копиях
	if(inst!=0)
	{
		recipients.Add(inst);
	}
	else
	{
		apidata.GetAttr(item, a_message_copies, copyes);
		for(j=0;j<copyes.GetSize();j++)
		{
			// сначала поле "получатель"
			apidata.GetAttr(copyes[j], a_message_recipient, inst);
			// если не оно - то поле "абонент"
			if(inst==0)apidata.GetAttr(copyes[j], a_message_rec_offisial, inst);
			if(inst!=0) recipients.Add(inst);
		}
	}
	return true;
}

bool CaplMessageManager::MoveMessages2Folder(aplExtent &messages, CaplInstance *folder)
{
	CaplInstance* inst= NULL, *message, *sender, *recipient;
	CaplInstance* rec_folder, *send_folder;
	CaplInstance* person=m_api->m_appr_mgr.GetCurrentPerson();
	bool bRet= false;

	if(person==0) return false;

	if(folder == 0)
	{
		folder = m_api->m_folder_mgr.SelectInstance(APL_MODE_SELECT_FOLDER,APL_T("Выберите папку"));
	}
	if(folder == 0) return false;

	bool prevAutoSave = m_api->m_folder_mgr.m_AutoSave;
	m_api->m_folder_mgr.m_AutoSave = false;

	DWORD wait_dlg = 0;
	wait_dlg = aplStartWaitDlg(APL_T("Перенос сообщений в папку"));

	for(int i=0;i<messages.GetSize();i++)
	{
		message = messages.GetAt(i);

		m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_sender, sender);
		m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_recipient, recipient);

		m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_recipient_folder, rec_folder);
		m_api->m_data.GetAttr(message, m_api->m_message_mgr.a_message_sender_folder, send_folder);

		if(sender==person)//отправитель
		{
			m_api->m_folder_mgr.AddToFolder(folder, message);
			m_api->m_data.PutAttr(message, m_api->m_message_mgr.a_message_sender_folder, folder);		
		}
		if(recipient==person)//получатель
		{
			m_api->m_folder_mgr.AddToFolder(folder, message);
			m_api->m_data.PutAttr(message,m_api->m_message_mgr.a_message_recipient_folder, folder);
		}
		bRet = true;
	}

	if(wait_dlg!=0) aplEndWaitDlg(wait_dlg);

	m_api->m_folder_mgr.m_AutoSave = prevAutoSave;
	if(prevAutoSave) m_data->NET_SaveChanges();

	return bRet;
}

bool CaplMessageManager::RemoveMessagesFromFolder(aplExtent &messages)
{
	CaplInstance* inst= NULL, *message, *sender, *recipient, *folder, *folder_prev=0;
	CaplInstance* person=m_api->m_appr_mgr.GetCurrentPerson();
	bool bRet= false;

	if(person==0) return false;
	if(messages.GetSize()==0) return false;

	bool prevAutoSave = m_api->m_folder_mgr.m_AutoSave;
	m_api->m_folder_mgr.m_AutoSave = false;

	DWORD wait_dlg = 0;
	wait_dlg = aplStartWaitDlg(APL_T("Удаление сообщений из папки"));

	for(int i=0;i<messages.GetSize();i++)
	{
		message = messages.GetAt(i);

		m_api->m_data.GetAttr(message, a_message_sender, sender);
		m_api->m_data.GetAttr(message, a_message_recipient, recipient);

		if(person==sender)
		{
			m_api->m_data.GetAttr(message,a_message_sender_folder, folder);
			m_api->m_data.PutAttr(message,a_message_sender_folder, inst);
			if(folder!=0)
			{
				if(folder != folder_prev)
					m_api->m_folder_mgr.LoadFolderInfo(folder);
				folder_prev = folder;
				if(folder->GetAccessmode()>aplRW) 
				{
					if(wait_dlg!=0) aplEndWaitDlg(wait_dlg);
					aplErrorMessage(S::NoAccessRight());
					return false;
				}
				int j;
				CaplInstance *inst; 
				CaplAggr aggr;
				m_data->GetAttr(folder,m_api->m_folder_mgr.a_folder_content,aggr);

				for(j=0;j<aggr.GetSize();j++)
				{
					aggr.GetByIndex(j,inst);
					if((inst==message)||(inst==0)) {aggr.Remove(j);j--;}
				}
				m_data->PutAttr(folder,m_api->m_folder_mgr.a_folder_content,aggr);
			}
		}
		if(person==recipient)
		{
			m_api->m_data.GetAttr(message,a_message_recipient_folder, folder);
			m_api->m_data.PutAttr(message,a_message_recipient_folder, inst);
			if(folder!=0)
			{
				if(folder != folder_prev)
					m_api->m_folder_mgr.LoadFolderInfo(folder);
				folder_prev = folder;
				if(folder->GetAccessmode()>aplRW) 
				{
					if(wait_dlg!=0) aplEndWaitDlg(wait_dlg);
					aplErrorMessage(S::NoAccessRight());
					return false;
				}
				int j;
				CaplInstance *inst; 
				CaplAggr aggr;
				m_data->GetAttr(folder,m_api->m_folder_mgr.a_folder_content,aggr);

				for(j=0;j<aggr.GetSize();j++)
				{
					aggr.GetByIndex(j,inst);
					if((inst==message)||(inst==0)) {aggr.Remove(j);j--;}
				}
				m_data->PutAttr(folder,m_api->m_folder_mgr.a_folder_content,aggr);
			}
		}
	}
	if(wait_dlg!=0) aplEndWaitDlg(wait_dlg);

	m_api->m_folder_mgr.m_AutoSave = prevAutoSave;
	if(prevAutoSave) m_data->NET_SaveChanges();

	return true;
}

/** Сохраняет указанное сообщение в формат eml
	@param message сообщение
	@body_eml буфер с телом сообщения
	@eml_file полное имя файла в которое будет сохранено сообщение
	@return true<\b> при удачном выполнении и false<\b> в случае ошибки.*/
bool CaplMessageManager::SaveMessageToEml(CaplInstance *message, LPCTSTR eml_file, int _max_size_mail)
{
	if(!message) return false;
	if(message->GetAccessmode()>aplRO)return false;
	if(!m_data) return false;
	if(0 == eml_file) return false;

	CaplDataBuf body_eml;
	if(!SaveMessageToEml(message, body_eml, _max_size_mail))
		return false;


	CFile file;
	CFileException pError;
	if(!file.Open(eml_file, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary, &pError))
	{
		if(pError.m_cause==CFileException::fileNotFound){
			//The file could not be located."
			AfxMessageBox( APL_T("Не найден файл ") + CString(eml_file), MB_ICONSTOP|MB_OK);
		}
		else if(pError.m_cause==CFileException::badPath )
		{
			//All or part of the path is invalid.
			AfxMessageBox( APL_T("Не верный путь к файлу !") + CString(eml_file), MB_ICONSTOP|MB_OK);
		}
		else if(pError.m_cause==CFileException::accessDenied)
		{
			//The file could not be accessed.
			AfxMessageBox( APL_T("Нет доступа к файлу!") + CString(eml_file), MB_ICONSTOP|MB_OK);
		}
		return false;
	}
	file.Write(body_eml.GetBuffer(), body_eml.m_Size);
	file.Close();

	return true;

}

CString EncodeBuf2EmlBase64(BYTE* buf, int buf_length, bool for_header)
{
	CString str_base64;
	CString ret = _T(""); 
	int i=0, i1;
	if(for_header)i1 = 40;
	else i1 = 60;

	do{
		if(i+i1 > buf_length) i1 = buf_length-i;
	
		str_base64 = ::EncodeToBase64(buf + i, i1);

		if(i > 0)
		{
			ret += _T("\r\n");
			if(for_header)ret += _T("\t");
		}
		if(for_header)ret += _T("=?UTF-8?B?");
		//if(for_header)ret += _T("=?CP1251?B?"); -- если делать разные кодировки, так выглядит win1251
		ret += str_base64;
		if(for_header)ret += _T("?=");
		i += i1;

	}while(i < buf_length);

	return ret;
}

CString EncodeText2EmlUtf8Base64(LPCTSTR encoded_text, bool for_header)
{
	CString ret = _T(""); 
	const wchar_t* wszTemp;
#ifdef _UNICODE
	wszTemp = encoded_text;
#else
	CaplStringAdapter str_ad(encoded_text);
	wszTemp = (const wchar_t*)str_ad;
#endif 
	int utf8_length;
	BYTE *utf8_buf;
	utf8_length  = ::WideCharToMultiByte(CP_UTF8, 0, wszTemp, -1, NULL,  0,              NULL, NULL); 
	utf8_buf = new BYTE[utf8_length + 1];

	utf8_length  = ::WideCharToMultiByte(CP_UTF8, 0, wszTemp, -1, (LPSTR)utf8_buf, utf8_length, NULL, NULL); 
	utf8_buf[utf8_length] = 0; 

	ret = EncodeBuf2EmlBase64(utf8_buf ,utf8_length-1, for_header);

	delete utf8_buf;

	return ret;
}

bool CaplMessageManager::SaveMessageToEml(CaplInstance *message, CaplDataBuf &body_eml, int _max_size_mail)
{
	body_eml.Clear();
	if(!message) return false;
	if(message->GetAccessmode()>aplRO)
	{
		if(m_api->m_ModeInteractive)aplErrorMessage(S::NoAccessRight(),message);
		return false;
	}
	if(!m_data) return false;

	LoadMessageInfo(message);

	CaplInstance *inst;
	m_data->GetAttr(message, m_api->m_message_mgr.a_message_copy, inst);
	if(0!=inst && inst!=message)  {message=inst; LoadMessageInfo(message); } // Это была копия письма. Берем корневое.
	
	COleDateTime odt;
	CString body_str, attachments_str, str_tmp, email, date_mess;
	CaplInstance *person;
	aplExtent recipients;
	int i;

	body_str = _T("");
	attachments_str = _T("");

	GetMessageRecipients(message, recipients);
	if(recipients.GetSize() == 0) return false;

	// Дата отправки письма
	body_str += _T("Date: ");
	m_data->GetAttr(message, a_message_date_send, date_mess);
	aplString2Date(date_mess, odt);
	// для вставки временной зоны приходщится извращаться
	TIME_ZONE_INFORMATION TimeZoneInformation;
	DWORD status = GetTimeZoneInformation(&TimeZoneInformation);
	LONG Bias = TimeZoneInformation.Bias;
	int zone_h = 0 - Bias/60;
	int zone_m = 0 - (Bias - (Bias/60) * 60);	
	str_tmp.Format(_T("%c%02i%02i\r\n"),zone_h>=0?_T('+'):_T('-'),zone_h,zone_m);
	body_str += odt.Format(_T("%a, %b %d %Y %H:%M:%S ")) + str_tmp;

	// идентификатор сообщения - из instance_id, времени сообщения и названия БД
	str_tmp.Format(_T("<%i."),message->GetId());
	body_str += _T("Message-ID: ") + str_tmp + date_mess + _T("@") + m_data->GetNameCurrBase() + _T(">\r\n");

	// Кому
	body_str += _T("To: ");
	for(i=0; i<recipients.GetSize(); i++)
	{
		person = recipients.GetAt(i);
		m_data->GetAttr(person, m_api->m_appr_mgr.a_pers_e_mail, email);
		m_api->m_appr_mgr.GetPersonName(person, str_tmp);
		body_str += EncodeText2EmlUtf8Base64(str_tmp, true);
		if(email != _T(""))body_str += _T(" <") + email + _T(">");
		if(i < recipients.GetSize()-1)body_str += _T(",\r\n\t");
	}
	body_str += _T("\r\n");

	// От кого
	body_str += _T("From: ");
	m_data->GetAttr(message, a_message_sender, person);
	if(0==person )
	{
		CaplInstance *parent_msg;
		m_data->GetAttr(message, a_message_copy, parent_msg);
		if(parent_msg!=message)
		{
			LoadMessageInfo(parent_msg);
			m_data->GetAttr(message, a_message_sender, person);
		}
	}
	if(0!=person)
	{
		m_data->GetAttr(person, m_api->m_appr_mgr.a_pers_e_mail, email);
		m_api->m_appr_mgr.GetPersonName(person, str_tmp);
	}
	else {email=""; str_tmp=""; }

	CString service_email, service_domain(_T("")), sender_domain(_T("")), return_email(_T("")), prefix(_T("")), suffix(_T(""));
	m_api->m_options_mgr.GetOptionValueBN(APL_NO_T("Email\\Login"), service_email, _T(""));
	i = service_email.Find(_T("@"));
	if(i != -1)
	{
		service_domain = service_email.Mid(i+1);
		if(email.IsEmpty())
		{
			email = service_email;
		}
		else
		{
			return_email = email;
			i = email.Find(_T("@"));
			if(i != -1)
			{
				sender_domain = email.Mid(i+1);
				sender_domain.MakeLower();
				service_domain.MakeLower();
				if(sender_domain != service_domain)
				{
					prefix = APL_T("--технический адрес. ");
					suffix = _T("-- Нажмите \"Ответить\" чтобы увидеть адрес отправителя. ");
					email = service_email;
				}
			}
		}
	}

	body_str += EncodeText2EmlUtf8Base64(prefix + str_tmp + suffix, true);
	if(email != _T(""))body_str += _T(" <") + email + _T(">");
	body_str += _T("\r\n");
	if(return_email != _T(""))
	{
		body_str += _T("Reply-to: ");
		body_str += EncodeText2EmlUtf8Base64(str_tmp, true);
		body_str += _T(" <") + return_email + _T(">");
		body_str += _T("\r\n");
	}
	
	// Приоритет
	body_str += _T("X-Priority: ");
	m_data->GetAttr(message, a_message_priority, i);
	switch(i)
	{
	case 1:
		body_str += _T("3 (Normal)");
		break;
	case 2:
		body_str += _T("5 (Low)");
		break;
	case 3:
		body_str += _T("1 (Highest)");
		break;
	}
	body_str += _T("\r\n");

	// Тема
	body_str += _T("Subject: ");
	m_data->GetAttr(message, a_message_subject, str_tmp);
	body_str += EncodeText2EmlUtf8Base64(str_tmp, true);
	body_str += _T("\r\n");

	//Разное
	body_str += _T("X-Mailer: PDM Step Suite mail gate \r\n");
	body_str += _T("MIME-Version: 1.0\r\n");

	// Формат тела письма и разделительи секций
	CString boundary_top;
	CString boundary_body;
	CString body_html;
	CString guid;
	bool body_multipart = false;
	CaplAPI::CreateGuid(guid);
	boundary_top = _T("----------top--") + guid;

	body_str += _T("Content-Type: multipart/mixed;\r\n");
	body_str += _T("\tboundary=\"") + boundary_top + _T("\"\r\n");

	//тело
	body_str += _T("\r\n");
	//блок с самим письмом
	body_str += _T("--") + boundary_top + _T("\r\n");



	m_data->GetAttr(message, a_message_content_format, str_tmp);
	if(str_tmp == _T("html"))
	{
		body_str += _T("Content-Type: text/html;");
		// теперь всё сложнее m_data->GetAttr(message, a_message_content, body_html);
		GetMessageContent(message,body_html);
	}
	else
	{
		body_multipart = true;
		CaplAPI::CreateGuid(guid);
		boundary_body = _T("----------body-") + guid;

		body_str += _T("Content-Type: multipart/alternative;\r\n");
		body_str += _T("\tboundary=\"") + boundary_body + _T("\"\r\n");

		m_data->GetAttr(message, a_message_content, str_tmp);
		CString header=_T("");
		aplConvertRTF2HTML(str_tmp, body_html, header, false);

		//блок с html-частью
		body_str += _T("\r\n");
		body_str += _T("--") + boundary_body + _T("\r\n");
		body_str += _T("Content-Type: text/html;");

	}
	body_str += _T(" charset=UTF-8\r\n");
	body_str += _T("Content-Transfer-Encoding: base64\r\n");
	body_str += _T("\r\n");

	body_str += EncodeText2EmlUtf8Base64(body_html, false);

	body_str += _T("\r\n");
	if(body_multipart)
	{
		body_str += _T("\r\n");
		body_str += _T("--") + boundary_body +  _T("\r\n");
		body_str += _T("Content-Type: application/rtf;");

		body_str += _T(" charset=UTF-8\r\n");
		body_str += _T("Content-Transfer-Encoding: base64\r\n");
		body_str += _T("\r\n");

		body_str += EncodeText2EmlUtf8Base64(str_tmp, false);

		body_str += _T("\r\n");
		body_str += _T("--") + boundary_body + _T("--");
		body_str += _T("\r\n");
	}

	aplExtent items, items_a;
	CaplInstance *item;
	CaplInstance *rev= NULL, *stor= NULL, *doc=0;
	m_data->GetAttr(message, a_message_items, items);
	CString file_name, buf, doc_name;
	CString all_attaches = _T(""), current_attach;
	CString attach_body;
	int max_size_attached = 10 * 1024 * 1024;
	
	// если задан максимальный размер письма - чуток уменьшаем его для запаса
	int max_size_mail = _max_size_mail;
	if(max_size_mail != -1)
	{
		max_size_mail = (int)(_max_size_mail * 0.95);
	}

	m_api->LoadExtentInfo(items);

	for(i=0; i<items.GetSize(); i++)
	{
		bool isFile=false;
		item = items.GetAt(i);
		if(item==0) continue;
		if(item==(CaplInstance*)-1) continue;
		if(item->GetType()==0) continue;
		
		current_attach = APL_T("Тип: ") + m_api->GetItemTypeName(item) + APL_T("; Имя: ") + m_api->GetItemName(item);

		if(m_data->IsKindOf(item,m_api->m_doc_mgr.e_doc))
		{
			m_data->GetAttr(item,m_api->m_doc_mgr.a_apl_doc_active, rev);
			doc=item;
		}
		else if(m_data->IsKindOf(item,m_api->m_doc_mgr.e_apl_doc_rev))
		{
			rev=item;
			m_data->GetAttr(item,m_api->m_doc_mgr.a_apl_doc_rev_doc, doc);
		}
		else if(m_data->IsKindOf(item,m_api->m_doc_mgr.e_apl_stored_document)) isFile=true;
		else if(m_data->IsKindOf(item,m_api->m_doc_mgr.e_apl_assigned_document)) isFile=true;
		else
		{
			all_attaches += current_attach + _T(";\r\n");
			continue;	
		}
		if(doc)
		{
			if(doc->GetAccessmode()>aplRO) continue;
		}
		if(rev)
		{
			if(rev->GetAccessmode()>aplRO) continue;
		}
		m_api->GetItemName(item, doc_name);

		if(!isFile)
		{
			if(rev==0){continue;}
			if(rev->GetType()==0){continue;}
			m_api->m_doc_mgr.GetFileName(rev, file_name);
		}
		else 
			m_api->m_doc_mgr.GetFileName(item, file_name);
		
		current_attach += APL_T("; Имя файла \"") + file_name + _T("\"");

		if(isFile) 
			stor=item;
		else if(rev->GetAccessmode()<aplNO) 
			m_data->GetAttr(rev,m_api->m_doc_mgr.a_apl_doc_rev_access_form,stor);
		
		if(stor==0)
		{
			current_attach += APL_T("; Не найден объект с телом документа");
			all_attaches += current_attach + _T(";\r\n");
			continue;
		}
		long blob_size;
		__int64 _i64_blob_size;
		bool is_export = true;

		if(m_data->IsKindOf(stor,m_api->m_doc_mgr.e_apl_stored_document))
		{
			if (!m_data->NET_GetBlobSize(stor, m_api->m_doc_mgr.a_apl_stored_document_source, _i64_blob_size))
			{
				current_attach += APL_T("; Ошибка при определении размера;");
				is_export = false;
			}
			if(is_export)
			{
				str_tmp.Format(APL_T("; Размер документа %I64i байт"), _i64_blob_size );
				current_attach += str_tmp;

				if(_i64_blob_size > max_size_attached)
				{
					str_tmp.Format(APL_T(" - больше ограничения в %i байт;"), max_size_attached );
					current_attach += str_tmp;
					current_attach += APL_T(" не экспортирован.");
					is_export = false;
				}
			}
			if(is_export)
			{
				bool bOldHM = m_data->SetHideMode(true);
				blob_size = (int)_i64_blob_size;
				BYTE *membuf=new BYTE[blob_size];

				if (m_data->NET_LoadBlob2Memory(stor, m_api->m_doc_mgr.a_apl_stored_document_source, membuf, blob_size ))
				{
					// сначала получаем закодированное тело аттача
					attach_body = EncodeBuf2EmlBase64(membuf, blob_size, false);
					// если задан максимальный размер письма - проверяем, влезет ли
					if(max_size_mail != -1)
					{
						if( (body_str.GetLength() + attachments_str.GetLength() +  attach_body.GetLength()) > max_size_mail )
						{
							str_tmp.Format(APL_T("; с этим документом размер письма превысит ограничение сервера %i байт. Документ не экспортирован."), _max_size_mail);
							current_attach += str_tmp;
							is_export = false;
						}
					}

					if(is_export)
					{
						// перед каждым аттачем - пустая строка
						attachments_str += _T("\r\n");
						attachments_str += _T("--") + boundary_top +  _T("\r\n");
						// определение подтипа пока не делаем, пишем что нечто общедвоичное
						attachments_str += _T("Content-Type: application/octet-stream;\r\n");
						/*
						if()
						attachments_str += _T("Content-Type: application/rtf;\r\n");
						attachments_str += _T("Content-Type: application/vnd.ms-excel;\r\n");
						attachments_str += _T("Content-Type: application/pdf;\r\n");
						attachments_str += _T("Content-Type: application/zip;\r\n");
						attachments_str += _T("Content-Type: application/x-zip-compressed;\r\n");
						attachments_str += _T("Content-Type: application/xml;\r\n");
						attachments_str += _T("Content-Type: image/jpeg;\r\n");
						*/
						// после подтипа - имя документа
						attachments_str += _T("\tname=\"") + EncodeText2EmlUtf8Base64(m_api->GetItemName(item), true) + _T("\"\r\n");
						attachments_str += _T("Content-Transfer-Encoding: base64\r\n");
						// уточнение типа - аттачмент, и сразу имя файла
						attachments_str += _T("Content-Disposition: attachment;\r\n");
						attachments_str += _T("\tfilename=\"")  + EncodeText2EmlUtf8Base64(file_name, true) + _T("\"\r\n");
						// перед телом - перевод строки
						attachments_str += _T("\r\n");							

						attachments_str += attach_body;
						// после -тоже
						attachments_str += _T("\r\n");

						current_attach += APL_T("; успешно экспортирован.");
					}
				}
				else
				{
					current_attach += APL_T(". Ошибка при получении тела документа;");
					current_attach += APL_T(" не экспортирован.");
				}
				delete membuf;
				m_data->SetHideMode(bOldHM);

			}
		}
		else if(m_data->IsKindOf(stor,m_api->m_doc_mgr.e_apl_assigned_document))
		{
			m_data->GetAttr(stor, m_api->m_doc_mgr.a_apl_assigned_document_location_path, buf);

			CFileFind finder;
			if(!finder.FindFile(buf))
			{
				current_attach += APL_T("Не найдено тело документа \"") + doc_name + APL_T("\", хранимого вне БД !");
			}
			else
			{
				finder.FindNextFile();
				_i64_blob_size = finder.GetLength();

				str_tmp.Format(APL_T("; Размер документа %I64i байт"), _i64_blob_size );
				current_attach += str_tmp;

				if(max_size_mail != -1 && (body_str.GetLength() + attachments_str.GetLength() +  attach_body.GetLength()) > max_size_mail )
				{
					str_tmp.Format(APL_T("; с этим документом размер письма превысит ограничение сервера %i байт. Документ не экспортирован."), _max_size_mail);
					current_attach += str_tmp;
				}
				else if(_i64_blob_size > max_size_attached)
				{
					str_tmp.Format(APL_T(" - больше ограничения в %i байт;"),max_size_attached );
					current_attach += str_tmp;
					current_attach += APL_T(" не экспортирован.");
				}
				else
				{
					bool bOldHM = m_data->SetHideMode(true);
					blob_size = (int)_i64_blob_size;
					BYTE *membuf=new BYTE[blob_size];

					CFile file;
					if(!file.Open(finder.GetFilePath(), CFile::modeRead||CFile::typeBinary))
					{
						current_attach += APL_T(". Ошибка при получении тела документа;");
						current_attach += APL_T(" не экспортирован.");
					}
					else
					{
						file.Read(membuf, blob_size);

						// перед каждым аттачем - пустая строка
						attachments_str += _T("\r\n");
						attachments_str += _T("--") + boundary_top +  _T("\r\n");
						// определение подтипа пока не делаем, пишем что нечто общедвоичное
						attachments_str += _T("Content-Type: application/octet-stream;\r\n");
						/*
						if()
						attachments_str += _T("Content-Type: application/rtf;\r\n");
						attachments_str += _T("Content-Type: application/vnd.ms-excel;\r\n");
						attachments_str += _T("Content-Type: application/pdf;\r\n");
						attachments_str += _T("Content-Type: application/zip;\r\n");
						attachments_str += _T("Content-Type: application/x-zip-compressed;\r\n");
						attachments_str += _T("Content-Type: application/xml;\r\n");
						attachments_str += _T("Content-Type: image/jpeg;\r\n");
						*/
						// после подтипа - имя документа
						attachments_str += _T("\tname=\"") + EncodeText2EmlUtf8Base64(m_api->GetItemName(item), true) + _T("\"\r\n");
						attachments_str += _T("Content-Transfer-Encoding: base64\r\n");
						// уточнение типа - аттачмент, и сразу имя файла
						attachments_str += _T("Content-Disposition: attachment;\r\n");
						attachments_str += _T("\tfilename=\"")  + EncodeText2EmlUtf8Base64(file_name, true) + _T("\"\r\n");
						// перед телом - перевод строки
						attachments_str += _T("\r\n");							

						attachments_str += EncodeBuf2EmlBase64(membuf, blob_size, false);
						// после -тоже
						attachments_str += _T("\r\n");

						current_attach += APL_T("; успешно экспортирован.");

					}

					delete membuf;
					m_data->SetHideMode(bOldHM);
				}
			}
		}


		all_attaches += current_attach + _T("\r\n");

		/*
Ассоциированные пока не обрабатываем

		// Проверка наличия вложенных документов
		bool bHasSubDoc=false;
		if(m_api->m_ModeInteractive) bHasSubDoc=CheckSubDocument(rev);


		if(bHasSubDoc)
		{
			if(0==iUseSubDoc)
			{
				if(!m_api->m_ModeInteractive){
					iUseSubDoc=-1;
				}else{
					BOOL b=AfxMessageBox( APL_T("Документ содержит вложенные. Обрабатывать их ?"),
						MB_YESNO|MB_ICONQUESTION)==IDYES;

					if (b) iUseSubDoc=1; else iUseSubDoc=-1;
				}
			}
		}

		FindAssociatedDocuments(doc,ext0,ext2);
		FindAssociatedDocuments(rev,ext1,ext2);
		items_a.Append(ext0);
		items_a.Append(ext1);
		*/
	}

	if(all_attaches != _T(""))
	{
		body_str += _T("--") + boundary_top +  _T("\r\n");
		// определение подтипа пока не делаем, пишем что нечто общедвоичное
		body_str += _T("Content-Type: text/plain;\r\n");
		body_str += _T(" charset=UTF-8\r\n");

		// после подтипа - имя документа
		body_str += _T("\tname=\"") + EncodeText2EmlUtf8Base64( APL_T("Вложенные элементы"), true) + _T("\"\r\n");
		body_str += _T("Content-Transfer-Encoding: base64\r\n");
		// уточнение типа - аттачмент, и сразу имя файла
		body_str += _T("Content-Disposition: attachment;\r\n");
		body_str += _T("\tfilename=\"")  + EncodeText2EmlUtf8Base64(APL_T("Вложенные элементы.txt"), true) + _T("\"\r\n");
		// перед телом - перевод строки
		body_str += _T("\r\n");		

		// тело смписка кодируем в UTF-8
		const wchar_t* wszTemp;
#ifdef _UNICODE
		wszTemp = all_attaches;
#else
		CaplStringAdapter str_ad(all_attaches);
		wszTemp = (const wchar_t*)str_ad;
#endif 
		int utf8_length;
		BYTE *utf8_buf;
		utf8_length  = ::WideCharToMultiByte(CP_UTF8, 0, wszTemp, -1, NULL,  0,              NULL, NULL); 
		utf8_buf = new BYTE[utf8_length + 1];

		utf8_length  = ::WideCharToMultiByte(CP_UTF8, 0, wszTemp, -1, (LPSTR)utf8_buf, utf8_length, NULL, NULL); 
		utf8_buf[utf8_length] = 0; 

		// файлик должен сождержать маркер последовательности байтов
		int blob_size = utf8_length + 3;
		BYTE *membuf = new BYTE[blob_size];
		BYTE header_utf8[3] = {0x0EF, 0x0BB, 0x0BF};

		memcpy(membuf, header_utf8, 3);
		memcpy(membuf+3, utf8_buf, utf8_length);

		body_str += EncodeBuf2EmlBase64(membuf, blob_size, false);

		delete utf8_buf;
		delete membuf;
		body_str += _T("\r\n");
	}

	// все приаттаченные вложения - после списка
	body_str += attachments_str;


	body_str += _T("--") + boundary_top + _T("--");
	body_str += _T("\r\n");



	// Запись в буфер для сохранение или отправки по сети
#ifdef _UNICODE
	CaplStringAdapter str_ad(body_str);
	body_eml.Add((void*)(const char*)str_ad, body_str.GetLength());
#else
	body_eml.Add((void*)(const char*)body_str, body_str.GetLength());
#endif 

	return true;
}

int ReadFromSocket(CSocket &socket, CaplDataBuf &dbuf, CString *err_mess)
{
	int nRead;
	dbuf.Reset();
#ifdef _DEBUG
	memset(dbuf.m_data, 0, dbuf.m_Size);
#endif
	nRead = socket.Receive(dbuf.m_data, dbuf.m_Size);
	if(nRead == SOCKET_ERROR)
	{
		if(err_mess!=0)*err_mess = APL_T("Ошибка при приеме данных с почтового сервера");
		socket.Close();
		return SOCKET_ERROR;
	}
	char *buff = dbuf.m_data;
	buff[nRead] = 0;
	return nRead;
}	



/** Отправляет указанное сообщение на почтовый сервер, указанный в группе опций Email. 
	@param message сообщение
	@return true<\b> при удачном выполнении и false<\b> в случае ошибки.*/
bool CaplMessageManager::SendMessageToEmlServer(CaplInstance *message, CString *err_mess)
{
	if(0==message) return false;
	if(message->GetAccessmode()>aplRO)return false;
	if(0==m_data) return false;

	CString err_mess_const;
	if(err_mess == NULL) err_mess = &err_mess_const;

	CString server, login, password;
	CString str_tmp;
	int port=0, ival=0, i;

	m_api->m_options_mgr.GetOptionValueBN(APL_NO_T("Email\\Server"), server, _T(""));
	if(server == _T("")) return false;

	m_api->m_options_mgr.GetOptionValueBN(APL_NO_T("Email\\Port"), port, 25);
	m_api->m_options_mgr.GetOptionValueBN(APL_NO_T("Email\\Login"), login, _T(""));
	m_api->m_options_mgr.GetOptionValueBN(APL_NO_T("Email\\Password"), password, _T(""));
	m_api->m_options_mgr.GetOptionValueBN(APL_NO_T("Email\\IsProtocoling"), ival, 0);
	bool IsProtocoling = ival!=0;


	// читаем список получателей и ищем адреса электронной почты
	aplExtent recipients;
	CStringArray emails_of_rec;
	GetMessageRecipients(message, recipients);
	bool bSecdMsg=false;
	for(i=0; i<recipients.GetSize(); i++)
	{
		CaplInstance *recip_i=recipients.GetAt(i);
		if(0==recip_i) continue;
		if(m_api->m_data.IsKindOf(recip_i,m_api->m_appr_mgr.e_official))
			m_data->GetAttr(recip_i, m_api->m_appr_mgr.a_official_act_person, recip_i);
		if(0==recip_i) continue;

		m_data->GetAttr(recip_i, m_api->m_appr_mgr.a_pers_send_copy_message_to_email, bSecdMsg);
		if(!bSecdMsg) continue;
		m_data->GetAttr(recip_i, m_api->m_appr_mgr.a_pers_e_mail, str_tmp);
		if(str_tmp != _T(""))
		{
			bool bFound=false;
			for(int j=0; j<emails_of_rec.GetSize(); j++) if(str_tmp==emails_of_rec[j]) {bFound=true; break;}
			if(!bFound)emails_of_rec.Add(str_tmp);
		}
	}
	// если нет ни одного адреса почты - делать тут больше нечего
	if(emails_of_rec.GetSize() == 0) return false;


	CSocket socket;
	socket.Create();
	if(socket.Connect(server, port) == FALSE)
	{
		GetDescriptionTransportError(ASC_ERROR_IN_SOCKET, WSAGetLastError(), str_tmp);
		*err_mess = CString(APL_T("Ошибка при подключении к почтовому серверу: ")) + str_tmp;
		m_data->WriteLocalLog(*err_mess);
		return false;
	}

	CString readed_string;
	CStringA command_string;
	int max_size_buff = WWW_PORTION_SIZE;

	int sended;
	CaplDataBuf dbuf;
	dbuf.m_ansi_string = true;
	dbuf.SetSize(max_size_buff);
	dbuf.m_Size = max_size_buff;
	
	// ждем ответ сервера с кодом 220 и именем сервера
	if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
	{
		//MM надо наверно подождать и еще попробовать? Но пока сразу нафиг
		if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
		return false;
	}
	dbuf.ReadString(readed_string);
	if(readed_string.Left(3) != _T("220"))
	{
		*err_mess = APL_T("Ошибка соединения с почтовым сервером: вместо кода 220 сервер вернул ") + readed_string;
		if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
		return false;
	}
	// посылаем серверу команду ehlo со своим именем
	m_data->GetCompName(str_tmp);
	CaplStringAdapter str_ad(str_tmp);
	command_string = "EHLO ";
	command_string += (const char*)str_ad;
	command_string += "\r\n";
	sended = socket.Send((LPCSTR)command_string, command_string.GetLength());

	// ждем список параметров сервера и выбираем нужные
	if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
	{ 
		if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
		return false;
	}

	if(IsProtocoling)
	{
		str_tmp = (const char*)dbuf.GetBuffer();
		m_data->WriteLocalLog(APL_T("Ответ почтового сервера:\n") + str_tmp);
	}
	
	int pos1;
	int _max_size_mail = -1;
	bool is_auth_login = false;
	bool is_auth_plain = false;
	while(dbuf.ReadString(readed_string))
	{
		if(readed_string == _T("")) break;
		if(readed_string.Left(3) != _T("250"))
		{
			*err_mess = APL_T("Ошибка чтения параметров почтового сервера: вместо кода 250 сервер вернул: ") + readed_string;
			if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
			return false;
		}
		pos1 = readed_string.Find(_T("SIZE"));
		if(pos1 != -1)
		{
			_max_size_mail = _atoi(readed_string.Mid(pos1+5));
		}
		pos1 = readed_string.Find(_T("AUTH"));
		if(pos1 != -1)
		{
			int pos2 = readed_string.Find(_T("LOGIN"), pos1);
			//int pos3 = readed_string.Find(_T("PLAIN"), pos1);
			if(pos2 != -1)
			{
				is_auth_login = true;
			}
// 			else if(pos3 != -1)
// 			{
// 				is_auth_plain = true;
// 			}
			else
			{
				*err_mess = APL_T("Сервер требует неподдерживаемый вид авторизации: ") + readed_string;
				if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
			}
		}
	}

	if(is_auth_login || is_auth_plain)
	{
		CaplDataBuf body_eml;
		CStringA str_tmp;
#ifdef _UNICODE
#pragma message ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Юникодный вариант криво конвертит RTF с 1251 в HTML UTF-8 !!!!!!!!!!!!!!")
#endif
		if(SaveMessageToEml(message, body_eml, _max_size_mail))
		{
			CStringA login_b64, password_b64;
			//login += _T("@cals.ru");
			str_tmp = login;
			login_b64 = EncodeBuf2EmlBase64((BYTE*)(const char*)str_tmp, str_tmp.GetLength(), false);
			login_b64 += "\r\n";
			str_tmp = password;
			password_b64 = EncodeBuf2EmlBase64((BYTE*)(const char*)str_tmp, str_tmp.GetLength(), false); // + _T(" \r\n");
			password_b64 += "\r\n";

			CString server_334_Username = _T("334 VXNlcm5hbWU6"); //"334 " + EncodeBuf2EmlBase64((BYTE*)"Username:", 9, false);
			CString server_334_Password = _T("334 UGFzc3dvcmQ6"); //"334 " + EncodeBuf2EmlBase64((BYTE*)"Password:", 9, false);

			if(is_auth_login)
			{
				command_string = "AUTH LOGIN\r\n";
				sended = socket.Send((LPCSTR)command_string, command_string.GetLength());

				// ждем приглашение на ввод логина
				if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
				{ 
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
					return false;
				}

				dbuf.ReadString(readed_string);
				if(readed_string != server_334_Username)
				{
					*err_mess = APL_T("Ошибка авторизации на почтовом сервере: вместо кода 334 с запросом логина сервер вернул: ") + readed_string;
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
					socket.Close();
					return false;
				}
				// посылаем логин
				sended = socket.Send((LPCSTR)login_b64, login_b64.GetLength());

				// ждем приглашение на ввод пароля
				if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
				{ 
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
					return false;
				}

				dbuf.ReadString(readed_string);
				if(readed_string != server_334_Password)
				{
					*err_mess = APL_T("Ошибка авторизации на почтовом сервере: вместо кода 334 с запросом пароля сервер вернул: ") + readed_string;
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
					socket.Close();
					return false;
				}
				// послыаем пароль
				sended = socket.Send((LPCSTR)password_b64, password_b64.GetLength());

				// ждем сообщение об успехе
				if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
				{ 
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
					return false;
				}

				dbuf.ReadString(readed_string);
				if(readed_string.Left(4) != _T("235 "))
				{
					*err_mess = APL_T("Ошибка авторизации. Ответ сервера: ") + readed_string;
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
					socket.Close();
					return false;
				}
			}
// 			else if(is_auth_plain)
// 			{
// 			}

			// И собственно передаем письмо
			// От кого
			command_string = "MAIL FROM:";
			CaplInstance* sender;
			CString mail_from = _T(""), domain_from(_T("")), domain_login(_T(""));
			m_data->GetAttr(message, a_message_sender, sender);
			if(sender != 0) m_data->GetAttr(sender, m_api->m_appr_mgr.a_pers_e_mail, mail_from);
			if(mail_from != _T(""))
			{
				str_ad = (LPCTSTR)mail_from;
				i = mail_from.Find(_T("@"));
				if(i != -1)
				{
					domain_from = mail_from.Mid(i+1);
					i = login.Find(_T("@"));
					if(i != -1)
					{
						domain_login = login.Mid(i+1);
						domain_login.MakeLower();
						domain_from.MakeLower();
						if(domain_login != domain_from)
							str_ad = (LPCTSTR)login;

					}
				}
			}
			else
			{
				str_ad = (LPCTSTR)login;
			}
			command_string += (const char*)str_ad;
			command_string += "\r\n";
			sended = socket.Send((LPCSTR)command_string, command_string.GetLength());

			// ждем приглашение на дальнейший ввод
			if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
			{ 
				if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
				return false;
			}

			dbuf.ReadString(readed_string);
			if(readed_string.Left(4) != _T("250 "))
			{
				*err_mess = APL_T("Ошибка отправки почты: вместо кода 250 с запросом продолжения сервер вернул: ") + readed_string;
				if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
				socket.Close();
				return false;
			}	

			// Кому
			for(i=0; i<emails_of_rec.GetSize(); i++)
			{
				command_string = "rcpt to: ";
				str_ad = (LPCTSTR)emails_of_rec.GetAt(i);
				command_string += (const char*)str_ad;
				command_string += "\r\n";
				sended = socket.Send((LPCSTR)command_string, command_string.GetLength());

				// ждем приглашение на дальнейший ввод
				if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
				{
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
					return false;
				}

				dbuf.ReadString(readed_string);
				if(readed_string.Left(4) != _T("250 "))
				{
					*err_mess = APL_T("Ошибка отправки почты: вместо кода 250 с запросом продолжения сервер вернул: ") + readed_string;
					if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
					socket.Close();
					return false;
				}				
			}

			// Команда на тело письма
			command_string = "data \r\n";
			sended = socket.Send((LPCSTR)command_string, command_string.GetLength());

			// ждем приглашение на дальнейший ввод
			if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
			{
				if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
				return false;
			}

			dbuf.ReadString(readed_string);
			if(readed_string.Left(4) != _T("354 "))
			{
				*err_mess = APL_T("Ошибка отправки почты: вместо кода 354 с запросом тела сообщения сервер вернул: ") + readed_string;
				if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
				socket.Close();
				return false;
			}				

			// Тело письма
			sended = socket.Send((LPCSTR)body_eml.GetBuffer(), body_eml.m_Size);
			// и точка
			command_string = ".\r\n";
			sended = socket.Send((LPCSTR)command_string, command_string.GetLength());

			// ждем сообщения что все Ок
			if(ReadFromSocket(socket, dbuf, err_mess) == SOCKET_ERROR)
			{
				if(IsProtocoling)m_data->WriteLocalLog(*err_mess); 
				return false;
			}

			dbuf.ReadString(readed_string);
			if(readed_string.Left(4) != _T("250 "))
			{
				*err_mess = APL_T("Ошибка отправки почты: вместо кода 250 с указанием успеха отправки сервер вернул: ") + readed_string;
				if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
				socket.Close();
				return false;
			}				
			// завершаем сеанс
			command_string = "quit\r\n";
			sended = socket.Send((LPCSTR)command_string, command_string.GetLength());
		}
		else
		{
			CString str;
			str.Format(_T("#%i"), message->GetId());
			*err_mess = APL_T("Ошибка при сохранении в буфер сообщения ") + str;
			if(IsProtocoling)m_data->WriteLocalLog(*err_mess);
		}

	}

	socket.Close();

	return true;
}


// Сохраянет html содержание во временный файл с необходимыми преобразованиями.
// В sResultTempFile возвращает имя созданного временного фафла
bool CaplMessageManager::SaveHtmlContentToTempFile(const CString &sContent, CString &sResultTempFile)
{
	sResultTempFile.Empty();
	if(sContent.IsEmpty()) return false;

	CString tmp_fname;
	aplGetTempFileName(_T("pssmail"),_T("htm"),tmp_fname);
	CFile file;
	if(!file.Open(tmp_fname,CFile::modeCreate|CFile::modeWrite)) return false;

	int iLen=sContent.GetLength();
	bool bNeedDeleteWriteBuf=false;
	#ifdef _UNICODE  // В sContent будет UTF-8 + BOM, но в UCS2 (все четные нули)
		// Такая перекодировка не работает CStringA sAnsiStr(m_sContent);
		LPSTR sWriteBuf=new char[iLen];
		bNeedDeleteWriteBuf=true;
		int k1,k2;
		// Копируем только нечетные символы
		for(k1=0,k2=0; k1<iLen; k1++,k2+=2) sWriteBuf[k1]=((const char*)(LPCWSTR(sContent)))[k2];
	#else
		LPCSTR sWriteBuf=(LPCSTR)sContent;
	#endif
	 
	 file.Write(sWriteBuf,iLen);
	 file.Close();

	 if(bNeedDeleteWriteBuf) delete sWriteBuf;

	 sResultTempFile=tmp_fname;
	 return true;
}



