
#include "protocol.h"

#include "bookmarks.h"
#include "vCard.h"

SO_PUBLIC WCHAR        	DevilsDir[256];
XMPPACCOUNT_T			xmpp_account;
UINT8 					xmpp_state;   	// Состояние подключения
char					xmpp_msg_from[256];
BOOL					xmpp_connected;
XMPP_CALL_BACK_F *		handleAppNtf;
ldrElf *				jabber_elf;
LIST					xstatus_list;

const char* presences[13] = {
							 "none",
							 "online",
                             "chat",
							 "away",
							 "dnd",
							 "xa",
							 "invisible",
							 "unavailable",
							 "error",
							 "subscribe",
							 "subscribed",
							 "unsubscribe",
							 "unsubscribed"
};

int XMPPInit( ldrElf * elf, XMPP_CALL_BACK_F * xmppCallBack ) 
{
	
	dbgf("XMPPInit: elf = 0x%p, xmppCallBack = 0x%p", elf, xmppCallBack);
	
	if ( !elf || !xmppCallBack ) return RESULT_FAIL;

	
	if ( init_socket_api(elf->app->port) < 0 )
		return RESULT_FAIL;
	if ( read_ws_params() < 0 )
		return RESULT_FAIL;
	
		
	jabber_elf = elf;	
	xmpp_connected = false;
	handleAppNtf = xmppCallBack;
	xmpp_state = XMPPL_NOT_CONNECTED;
	
	XstatusInit();
	
	dbg("XMPPInit: done");
	
}
int XMPPUninit() 
{
	dbg("XMPPUnint: enter");
	
	if( xmpp_connected ) XMPPDisconnect();
	
	XstatusUninit();

	return RESULT_OK;
}

int XMPPHandleEvent( EVENT_STACK_T * ev_st, APPLICATION_T * app )
{
	if( AFW_GetEvCode(ev_st) == ldrGetConstVal( EV_TIMER_EXPIRED ) ) {
	
		if( GET_TIMER_ID(ev_st) == XMPP_PING_TIMER_ID ){
			if( XMPPIsReady() )  
				XMPPReportPING("_ping", xmpp_account.server );
		}
	}
	
	return socket_HandleEvent(ev_st, app);
}
int XMPPConnect( SETTING_T * setting, ACCOUNT_T * acnt ) 
{
	int 	i;
	dbgf("XMPPConnect: setting = 0x%p, acnt = 0x%p", setting, acnt );
	
	if ( !setting || !acnt ) return;
	
	xmpp_account.port = acnt->port;			// Порт
	xmpp_account.priority = acnt->priority;	// Приоритет
	xmpp_account.status = setting->status;		// Обычный статус
	xmpp_account.xstatus = setting->xstatus;	// X-статус
	xmpp_account.muc_ajoin = setting->muc_ajoin;// Автовход в конференции
	xmpp_account.zlib_enable = setting->zlib_enable;
	
	CP1251toUTF8( acnt->login, xmpp_account.login );
	CP1251toUTF8( acnt->server, xmpp_account.server );
	CP1251toUTF8( acnt->resource, xmpp_account.resource );
	CP1251toUTF8( acnt->password, xmpp_account.password );
	CP1251toUTF8( setting->time_zone, xmpp_account.time_zone );
	
	for ( i = 0; i < XMPPS_OFFLINE; i++ )
		CP1251toUTF8( setting->status_msg[i], xmpp_account.status_msg[i] );
	
	
	sprintf(xmpp_account.full_jid, "%s@%s/%s", 
									xmpp_account.login,
									xmpp_account.server,
									xmpp_account.resource
									);
									
	ContactListInit();
	return SockCreatSocket();
}

// Дисконнект
int XMPPDisconnect() 
{
	dbgf("XMPPDisconnect" );

    char *xmpp_logout_req =
					"<presence type='unavailable'>"
							"<status>Good-bye</status>"
						"</presence>";

	if(XMPPIsReady())	SockSend(xmpp_logout_req, strlen(xmpp_logout_req));
	
	xmpp_connected = false;
	xmpp_state = XMPPL_NOT_CONNECTED;
	
	if ( xmpp_account.ping_timer ) StopTimer( &xmpp_account.ping_timer, jabber_elf->app );

 /*	if( xmpp_account.zlib_on ) {
		inflateEnd(&d_stream);
		deflateEnd(&c_stream);
	} */
	ContactListUninit();
	return SockClose();
}

BOOL XMPPIsReady( void )
{
	return xmpp_connected;
}

/*
  Послать приветствие, на него сервер высылает ответный stream.
  После этого можно общаться с сервером
*/

void XMPPSendWelcomePacket(void)
{
  char 			buf[256];
  
		sprintf(buf, "<?xml version='1.0' encoding='UTF-8'?>"
							"<stream:stream to='%s' xmlns='jabber:client' "
								"xmlns:stream='http://etherx.jabber.org/streams' "
						"version='1.0'>",		
						xmpp_account.server);
				
  
  SockSend(buf, strlen(buf));
}

// Запрос компрессии у сервера
void XMPPCompressionAsk()
{
	char zlib_ask[]="<compress xmlns='http://jabber.org/protocol/compress'>"
							"<method>zlib</method>"
						"</compress>";
				  
  xmpp_state = XMPPL_ZLIB_INIT_ACK;
  handleAppNtf(XMPP_NTF__ZLIB_INIT_ACK, NULL, NULL, 0);
  
  SockSend(zlib_ask, strlen(zlib_ask));
}

// Сообщить серверу об использовании аунтитификации PLAIN
void XMPPUsePlainAuthReport(void)
{
	xmpp_state = XMPPL_LOGIN; 
	handleAppNtf(XMPP_NTF__LOGIN, NULL, NULL, 0);
  
	char reqdata[768];
						
	char credentials[128*3];
	char *credentials64;

	sprintf(credentials, "%s@%s%c%s%c%s", xmpp_account.login,
										  xmpp_account.server, '\0',
										  xmpp_account.login, '\0',
										  xmpp_account.password);
	
	base64_encode(credentials,
				strlen(xmpp_account.login)+strlen(xmpp_account.server)+strlen(xmpp_account.login)
				+strlen(xmpp_account.password)+3,
				&credentials64);
			
	sprintf(reqdata, "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='PLAIN'>%s</auth>",
						credentials64);
  
  SockSend(reqdata, strlen(reqdata));
}

// Выполняем Resource Binding
void XMPPBindResource(void)
{
	xmpp_state = XMPPL_BIND; 
	handleAppNtf(XMPP_NTF__BIND, NULL, NULL, 0);
  
	char bind_tpl[256];
	sprintf(bind_tpl, "<iq type='set' id='bind_req'>"
						"<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
							"<resource>%s</resource>"
						"</bind>"
					"</iq>",
					xmpp_account.resource);
				  
  xmpp_state = XMPPL_BIND;
  SockSend(bind_tpl, strlen(bind_tpl));
}

// Инициализация сессии
void XMPPInitSession(void)
{
	xmpp_state = XMPPL_SESSION; 
	handleAppNtf(XMPP_NTF__SESSION, NULL, NULL, 0);

	char sess_init_tpl[256];
	
	sprintf(sess_init_tpl, "<iq to='%s' type='set' id='sess_req'>"
								"<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
							"</iq>",
							xmpp_account.server );
				  

  SockSend(sess_init_tpl, strlen(sess_init_tpl));
}


// Послать запрос ростера
void XMPPSendRosterQuery(void)
{
 	xmpp_state = XMPPL_ROSTER; 
	handleAppNtf(XMPP_NTF__ROSTER, NULL, NULL, 0);
  
	char roster_query_tpl[256] =  "<iq type='get' id='rost_req'>"
										"<query xmlns='jabber:iq:roster'/>"
								   "</iq>";
				  
  
  SockSend(roster_query_tpl, strlen(roster_query_tpl));
}

void XMPPSendBookmarksRequest()
{
	xmpp_state = XMPPL_BOOKMARKS; 
	handleAppNtf(XMPP_NTF__BOOKMARKS, NULL, NULL, 0);
	
	char bookmarks_tpl[256];

	sprintf(bookmarks_tpl, "<iq type='get' id='priv_req'>"
									"<query xmlns='jabber:iq:private'>"
										"<storage xmlns='storage:bookmarks'>"
											"</storage>"
									"</query>"
								"</iq>"
								);
																						
	SockSend(bookmarks_tpl, strlen(bookmarks_tpl));
}

// Сгенерировать ver= исходя из текущих возможностей клиента
// Возвращаемую строку необходимо освобождать!
char *XMPPGenerateCaps(void)
{
	char answer[1024] = "client/mobile//mJabber " ELF_VER
						"<http://jabber.org/protocol/caps"
						"<http://jabber.org/protocol/activity"
						"<http://jabber.org/protocol/activity+notify"
						"<http://jabber.org/protocol/chatstates"
						"<http://jabber.org/protocol/disco#info"
						"<http://jabber.org/protocol/mood"
						"<http://jabber.org/protocol/mood+notify"
						"<http://jabber.org/protocol/muc"
						"<http://jabber.org/protocol/pubsub#event"
						"<http://qip.ru/x-status"
						"<jabber:iq:last"
						"<jabber:iq:time"
						"<jabber:iq:version"
						"<urn:xmpp:ping"
						"<urn:xmpp:receipts"
						"<urn:xmpp:attention:0";
      
	SHA1Context  ctx;
	char 	hash2[256];

	SHA1Init(&ctx);
	SHA1Update(&ctx, answer, strlen(answer));
	SHA1Final(&ctx, hash2 );


	char 	*result;

	result = malloc( 256 );
	memclr(result, 256);

	base64_encode(hash2, strlen(hash2), &result);

	return result;
}

/*
  Послать своё присутствие (в частности, после этого на нас вываливаются
  присутствия остальных, а мы появляемся в ресурсах своего контакта)

*/
void XMPPSendPresence()
{
	char 	presence_tpl[1152];
    xmpp_state = XMPPL_PRESENSE; 
	handleAppNtf(XMPP_NTF__PRESENSE, NULL, NULL, 0);
	
	if(xmpp_account.status != XMPPS_INVISIBLE) {
		sprintf(presence_tpl, "<presence>"
									"<show>%s</show>"
									"<status>%s</status>"
									"<priority>%i</priority>"
									"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='%s' ver='%s'/>"
									"<x xmlns='http://qip.ru/x-status' id='%d'>"
										"<title>?</title>"
									"</x>"													
							  "</presence>",
								presences[xmpp_account.status],
								xmpp_account.status_msg[xmpp_account.status],
								xmpp_account.priority,
								VERSION_NODE,
								XMPPGenerateCaps(),
								xmpp_account.xstatus
								);
								
		if( UtilGetIcqTransport() ) {	// Находим в списке контактов icq-транспорт и отправляем ему xstatus
			
			char	xstatus_tpl[768];
			
			sprintf(xstatus_tpl, "<iq type='set' id='xstatus' to='%s'>"
										"<command xmlns='http://jabber.org/protocol/commands' node='setxstatus' action='complete'>"
											"<x xmlns='jabber:x:data' type='submit'>"
												"<field var='xstatus_desc'>"
													"<value>%s</value>"
												"</field>"
												"<field var='xstatus_name'>"
													"<value>%s</value>"
												"</field>"
											"</x>"
										"</command>"
								 "</iq>",
								UtilGetIcqTransport(),
								XstatusGetItemStr(xmpp_account.xstatus),
								xmpp_pyicq_t_xstatuses[xmpp_account.xstatus]
								);
			SockSend(xstatus_tpl, strlen(xstatus_tpl));					
		}
	
	} else {
		sprintf(presence_tpl, "<presence>"
									"<status>%s</status>"
									"<priority>%i</priority>"	
									"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='%s' ver='%s'/>"
							  "</presence>",
								xmpp_account.status_msg[xmpp_account.status],
								xmpp_account.priority,
								VERSION_NODE,
								XMPPGenerateCaps()
								);
	}
  
    xmpp_connected = true;
	
	SockSend(presence_tpl, strlen(presence_tpl));
 
    xmpp_state = XMPPL_READY; 
	handleAppNtf(XMPP_NTF__READY, NULL, NULL, 0);
}

void XMPPProcessXMLPacket( char* xmlbuf, int size )
{
	dbgf("XMPPProcessXMLPacket: enter, size = %d", size);
	// Сюда попадаем, если от транслятора принят указатель на порцию данных
	XMLNode *data = XMLDecode(xmlbuf, size);
	
	if(data) {
	
		XMPPProcessDecodedXML(data);
		DestroyTree(data);
	}
	dbg("XMPPProcessXMLPacket: done");
}

// Рекурсивная функция декодирования XML-потока
void XMPPProcessDecodedXML(XMLNode* node)
{
	XMLNode* nodeEx = node;
	dbgf("XMPPProcessDecodedXML: nodeEx->name = %s", nodeEx->name);
	
	while(nodeEx)
	{
		//----------------
		if (!strcmp(nodeEx->name, "stream:features")) {
		
			if( xmpp_state == XMPPL_SEND_WELCOME ) {

				if ( xmpp_account.zlib_enable ) {
					XMPPCompressionAsk();
				} else {
					XMPPUsePlainAuthReport();
				}
				
			} else if ( xmpp_state == XMPPL_LOGGED ) {
				XMPPBindResource();
			}	
		}
		else if(!strcmp(nodeEx->name,"compressed") && xmpp_state == XMPPL_ZLIB_INIT_ACK)
		{
			InitZlib();
		}
		//----------------
		else if(!strcmp(nodeEx->name,"success"))
		{
			XMPPSendWelcomePacket();
			xmpp_state = XMPPL_LOGGED;
			handleAppNtf(XMPP_NTF__LOGGED, NULL, NULL, 0);
		}
		//----------------
		else if(!strcmp(nodeEx->name, "message"))
		{
			XMPPProcessIncomingMessage(nodeEx);
		}
		//----------------
		else if(!strcmp(nodeEx->name, "iq"))
		{
			XMPPProcessIqRequest(nodeEx);
		}
		 //----------------
		else if(!strcmp(nodeEx->name, "stream:stream"))
		{
			if(nodeEx->subnode)
			{
			  XMPPProcessDecodedXML(nodeEx->subnode);
			  return;
			}
		}
		//----------------
		else if(!strcmp(nodeEx->name, "stream:error"))
		{
			xmpp_state = XMPPL_ERROR;
			cprintf("\x84 Error: bad xml-packet\n");
			XMPPDisconnect();
		}
		//----------------
		else if(!strcmp(nodeEx->name, "presence"))
		{
			XMPPProcessPresenceChange(nodeEx);
		}
		 //----------------
		else if(!strcmp(nodeEx->name, "failure"))
		{
			xmpp_state = XMPPL_ERROR;
			handleAppNtf(XMPP_NTF__JID_PW_ERROR, NULL, NULL, 0);
			XMPPDisconnect();
		}
		//----------------
		nodeEx = nodeEx->next;
	}
}

// Обработка входящих Iq-запросов
void XMPPProcessIqRequest(XMLNode* nodeEx)
{	
	XMLNode* query;
	char  *iqtype = XML_Get_Attr_Value("type",nodeEx->attr);
	char  *id = XML_Get_Attr_Value("id",nodeEx->attr);
	char  *from = XML_Get_Attr_Value("from",nodeEx->attr);
  
	dbgf("XMPPProcessIqRequest: type = %s", iqtype);
	dbgf("XMPPProcessIqRequest: from = %s", from);
	dbgf("XMPPProcessIqRequest: id = %s", id);
  

	// Проверяем наличие обязательных атрибутов
	if( !iqtype ) return;

	if(!strcmp(iqtype, "get")) // Iq type = get
	{
		char* q_type;
		if(query = XML_Get_Child_Node_By_Name(nodeEx, "query"))
		  
			if(q_type = XML_Get_Attr_Value("xmlns", query->attr))
			{
				// Тут мы знаем XMLNS поступившего запроса
				if(!strcmp(q_type, "jabber:iq:version"))
				{
				// jabber:iq:version
					if(from)
					{
						XMPPReportVersionInfo(id, from);
						return;
					}
				} //end version
				
				if(!strcmp(q_type, "jabber:iq:time"))
				{
					// jabber:iq:time
					if(from)
					{
						XMPPReportTimeInfo(id, from);
						return;
					}
				}
				if(!strcmp(q_type, "http://jabber.org/protocol/disco#info"))
				{
					if(from)
					{
						XMPPReportDiscoInfo(id, from);
						return;
					}
				}
			} //end "query" 
			
			query = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "urn:xmpp:ping");
			
				if( query ) {
				
					if(from) {
					
						XMPPReportPING(id, from);
						return;
					}
				} //end ping
		// Ни один обработчик не подошёл, отправляем ошибку.
		XMPPSendFeatureNotImplemented(from, id);
	}
	
	// Обработка  Iq type = result
	if( !strcmp(iqtype, "result") )
	{
		if(!strcmp(id, "bind_req"))
		{
			xmpp_state = XMPPL_SESSION;
			XMPPInitSession();
			return;
		}
		if(!strcmp(id, "sess_req"))   // Конец инициализации сессии
		{
			xmpp_state = XMPPL_ROSTER;
			XMPPSendRosterQuery();
			return;
		}
		if(!strcmp(id, "rost_req"))   // Запрос ростера
		{
			query = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "jabber:iq:roster");
			if( query )
			{
			  // jabber:iq:roster
			 ///////////////////////////////////////////////////////////////////
			    ContactListFillRoster(query->subnode);
				//XMPPSendBookmarksRequest();
				XMPPSendPresence();
				xmpp_account.ping_timer = StartTimer(XMPP_PING_TIMER_PERIOD, XMPP_PING_TIMER_ID, ttCyclical, jabber_elf->app);
				xmpp_account.cl_save = true;
			///////////////////////////////////////////////////////////////////
				return;
			}
		}
		if(!strcmp(id, "ver_req"))   // Запрос версии (ответ)
		{
			query = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "jabber:iq:version" );
			if( query )
			{
				char* vers_os_str;
				XMLNode *cl_name = XML_Get_Child_Node_By_Name(query, "name");
				XMLNode *cl_version = XML_Get_Child_Node_By_Name(query, "version");
				XMLNode *cl_os = XML_Get_Child_Node_By_Name(query, "os");
				if(cl_os)
					vers_os_str = cl_os->value;
				else
					vers_os_str = "(no data)";
				//Формируем сообщение
				char	buf[512];		
				strcpy(buf, xmpp_lang(LG_CLIENT_INFO_));
				strcat(buf, xmpp_lang(LG_NAME));
				strcat(buf, cl_name->value);
				strcat(buf, "\n");
				
				strcat(buf, xmpp_lang(LG_VERSION));
				strcat(buf, cl_version->value);
				strcat(buf, "\n");
				
				strcat(buf, "OS: ");
				strcat(buf, vers_os_str);
				strcat(buf, "\n");
				handleAppNtf( XMPP_NTF__CLIENT_INFO, from, buf, strlen(buf) );
				return;
			}
		}
		
		if(!strcmp(id, "vcard_req"))   // Запрос vcard (ответ)
		{
			XMLNode* vcard = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "vCard", "xmlns", "vcard-temp");
			
			if( vcard )
				Process_vCard(from, vcard);
			return;
		}
		
		if(!strcmp(id, "priv_req"))		// Запрос bookmarks (ответ)
		{		
			query = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "jabber:iq:private");
		
			if( query ) {
				XMLNode *bm = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "storage:bookmarks");
		
				if( bm )
					Process_Bookmarks_Storage(bm);	
			}
			XMPPSendPresence();
			return;
		}
		
		if(!strcmp(id, "reg_req1"))
		{		
			query = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "jabber:iq:register" );
			if( query )
			{
				char			buf[512];
				RESISTER_INFO_T reg_info;
				
				memclr( &reg_info, sizeof(RESISTER_INFO_T) );
	
	
				XMLNode *email = XML_Get_Child_Node_By_Name(query, "email");
				
				if( email ) {
				
					if( email->value )
						UTF8toCP1251( email->value, reg_info.email );
					
					reg_info.edit_email = true;
				}
				
				XMLNode *username = XML_Get_Child_Node_By_Name(query, "username");
				
				if( username ) {
				
					if( username->value )
						UTF8toCP1251( username->value, reg_info.username );
						
					reg_info.edit_username = true;
				}
				
				XMLNode *instructions = XML_Get_Child_Node_By_Name(query, "instructions");
				
				if( instructions ) {
				
					UTF8toCP1251( instructions->value, buf );
					handleAppNtf( XMPP_NTF__REGISTRATION_INSTRUCTIONS, from, buf, strlen(buf) );
					handleAppNtf( XMPP_NTF__REGISTRATION_DATA, from, &reg_info, sizeof(RESISTER_INFO_T) );
				}
				return;
			}
		}
		// Сообщение об успешной регистрации в транспорте
		if(!strcmp(id, "reg_req2"))
		{		
			query = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "jabber:iq:register" );
			if( query )
			{
				handleAppNtf( XMPP_NTF__REGISTRATION_SUCCESSFUL, from, NULL, 0 );
			}
			return;
		}
	}	

	if(!strcmp(iqtype, "set"))
	{
		query = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "query", "xmlns", "jabber:iq:roster");
	
		if( query ) {
			ContactListFillRoster(query->subnode);
			return;
		}
	
		XMPPSendServiceUnavailable(from, id);
	}

	if(!strcmp(iqtype, "error")) // Iq type = error
	{

		// Анализируем ошибку
		XMLNode* error = XML_Get_Child_Node_By_Name(nodeEx, "error");
		if( !error ) return;
		long errcode = strtol( XML_Get_Attr_Value("code", error->attr) , '\0', 10);
		switch (errcode)
		{
			case 302: cprint("\x8C Error: Redirect\n"); break;
			case 400: cprint("\x8C Error: Bad request\n"); break;
			case 401: cprint("\x8C Error: Unauthorized\n"); break;
			case 402: cprint("\x8C Error: Payment required\n"); break;
			case 403: cprint("\x8C Error: Forbidden\n"); break;
			case 404: cprint("\x8C Error: Not found\n"); break;
			case 405: cprint("\x8C Error: Not allowed\n"); break;
			case 406: cprint("\x8C Error: Not acceptable\n"); break;
			case 407: cprint("\x8C Error: Registration required\n"); break;
			case 408: cprint("\x8C Error: Request timeout\n"); break;
			case 409: cprint("\x8C Error: Conflict\n"); break;
			case 500: cprint("\x8C Error: Internal server error\n"); break;
			case 501: cprint("\x8C Error: Not mplemented\n"); break;
			case 502: cprint("\x8C Error: Remote server error\n"); break;
			case 503: cprint("\x8C Error: Service unavailable\n"); break;
			case 504: cprint("\x8C Error: Remote server timeout\n"); break;
			default:  cprintf("\x8C Error: #%s\n", errcode);
		}
	}
}


// Презенсы :)
void XMPPProcessPresenceChange(XMLNode* node)
{
	XMPP_STATUS	status;
	int 		i=0;
	CONTACT_T   *contact;
	char		*from = XML_Get_Attr_Value("from",node->attr); if(!from) return;
	char 		*pr_type = XML_Get_Attr_Value("type",node->attr);
	char 		*resource = UtilGetResourceByFullJID(from);
	XMLNode		*status_msg_node = XML_Get_Child_Node_By_Name(node, "status");
	
	dbg("XMPPProcessPresenceChange: enter");
	
	UtilGetJIDByFullJID(from, xmpp_msg_from);
	contact = ContactListFindContact( xmpp_msg_from );
	
	dbgf("XMPPProcessPresenceChange: msg_from = %s", xmpp_msg_from);
	
	if( !contact && XMPPIsReady() ) {
		dbgf("XMPPProcessPresenceChange: contact = NULL, ContactListAddContact = %s", xmpp_msg_from);
		UINT8   gr_id;
		
				if( !ContactListGetGroupID( NOT_IN_LIST ) ) 
				gr_id = ContactListAddGroup( NOT_IN_LIST );
				else
				gr_id = ContactListGetGroupID( NOT_IN_LIST );
				
		ContactListAddContact(xmpp_msg_from, xmpp_msg_from, SUB_NONE, gr_id);
		contact = ContactListFindContact( xmpp_msg_from );
	}
	
	dbgf("XMPPProcessPresenceChange: contact = 0x%p", contact);
	if(!contact) return;
	

	if( contact && resource ) {
		contact->resource = malloc(strlen(resource)+1);
		UTF8toCP1251(resource, contact->resource);
		dbgf("XMPPProcessPresenceChange: resource = %s", resource);
	}

	if(pr_type) {
	
		dbgf("XMPPProcessPresenceChange: type = %s", pr_type);
		status = UtilGetPresenceIndex(pr_type);
		if(status == XMPPS_UNAVAILABLE) status = XMPPS_ONLINE;
		
		ContactListChangeStatus( xmpp_msg_from, status );
		ContactListAddSystemMessage(xmpp_msg_from, status, "");
		
	} else {
	
			XMLNode* status_node = XML_Get_Child_Node_By_Name(node, "show");

			if( !status_node ) {
			
				ContactListChangeStatus( xmpp_msg_from, XMPPS_ONLINE );
			
			} else {
			
				dbgf("XMPPProcessPresenceChange: show = %s", status_node->value);
				status = UtilGetPresenceIndex(status_node->value);
				
				ContactListChangeStatus( xmpp_msg_from, status );
			}
			
			if( status_msg_node ) {
			
				contact->status_msg = malloc(strlen(status_msg_node->value)+1);
				UTF8toCP1251(status_msg_node->value, contact->status_msg);
			}
	
		XMLNode* prio_node = XML_Get_Child_Node_By_Name(node, "priority");
		
		if(prio_node) contact->priority = strtol( prio_node->value, '\0', 10 ); 
	
	}
	
	
	XMLNode		*client_node = XML_Get_Child_Node_By_Name(node, "c"); if(!client_node) goto _xstatus_node;
	char		*client_nodeStr = XML_Get_Attr_Value("node", client_node->attr); 
	
	dbgf("XMPPProcessPresenceChange: client_nodeStr = %s", client_nodeStr);
	
	if(client_nodeStr) {
		 
		for(i=0; i < CLIENT_MAX; i++){
	
			if(strstr( client_nodeStr, xmpp_clients[i].description )){
				contact->client_id = i;
			}
		}
	}	
_xstatus_node:;
	XMLNode		*xstatus_node = XML_Get_Child_Node_By_Name(node, "x"); if(!xstatus_node) goto end;
	char		*xstatus_id = XML_Get_Attr_Value("id", xstatus_node->attr);
	
	if(xstatus_id) contact->xstatus = strtol( xstatus_id, '\0', 10 ); 
	
	XMLNode		*xstatus_msg = XML_Get_Child_Node_By_Name( xstatus_node, "title" );
	
	if( xstatus_msg )
	if( xstatus_msg->value ) {
			
		contact->xstatus_msg = malloc(strlen(xstatus_msg->value)+1);
		UTF8toCP1251(xstatus_msg->value, contact->xstatus_msg);
	}
			
   // Предусматриваем случай, что послано нам что-то от конференции. Это важно.
   XMLNode *x_node;

    if(x_node = XML_Get_Child_Node_By_Name_And_Attr(node, "x", "xmlns", "http://jabber.org/protocol/muc")) // Послано от конференции
    {
      // Получаем дочерний узел error (ибо нацелены на обработку именно ошибок)
      XMLNode* err_node = XML_Get_Child_Node_By_Name(node, "error");
      if(err_node)  // Есть ошибка!
      {
        // Хочу текст ошибки
        XMLNode* err_desc = XML_Get_Child_Node_By_Name(err_node, "text");
        if(err_desc)
        {
          cprintf("\x8C Error: %s", err_desc->value);
		  ContactListChangeStatus( xmpp_msg_from, XMPPS_OFFLINE );
        }
        else
        {
          char *code = XML_Get_Attr_Value("code",err_node->attr);
          cprintf("\x8C Error: #%s", err_desc->value);
        }
      }
    }
	
	/*static char r[512];       // Статик, чтобы не убило её при завершении процедуры
	
	if(x_node = XML_Get_Child_Node_By_Name_And_Attr(node, "x", "xmlns", "http://jabber.org/protocol/muc")) // Послано от конференции в пользователя
    {
	
     // if (!CList_FindMUCByJID(Conference->JID)) return; //нету такой конференции, значит мы ёё несоздавали

      // Тут можно обрабатывать события входа/выхода в конфу
      // Ибо сообщается, кто вошёл (модер ли, админ...)
      XMLNode* item = XML_Get_Child_Node_By_Name(x_node,"item");
	  
	char* real_jid;
	char* affiliation = XML_Get_Attr_Value("affiliation", item->attr);
	char* role =  XML_Get_Attr_Value("role", item->attr);
		
	UINT8 		Req_Set_Role=0;
	CONF_DATA 	priv;
	
      if(status != XMPPS_OFFLINE) // Вход с любым статусом
      {
        real_jid = XML_Get_Attr_Value("jid", item->attr);
        priv.aff = UtilGetAffRoleIndex(affiliation);
        priv.role = UtilGetAffRoleIndex(role);
		
		
        if(contact->status != XMPPS_OFFLINE)
        {
          if(!(contact->muc_privs.aff == priv.aff && contact->muc_privs.role == priv.role))
          {
            sprintf(r, "%s is now %s and %s [%d->%d, %d->%d]", xmpp_msg_from, affiliation, role, contact->muc_privs.aff, priv.aff, contact->muc_privs.role, priv.role);
            Req_Set_Role = 1;
          }
          else
          {
            if(status_msg_node->value)
            {
              snprintf(r, 128, "%s changed status to %s (%s)", xmpp_msg_from, presences[status], status_msg_node->value);
            }
            else
            {
              sprintf(r, "%s changed status to %s", xmpp_msg_from, presences[status]);
            }
          }
        }
        else if(real_jid)  //если знаем реальный jid, выводим его
        {
          sprintf(r, "%s (%s) joined as %s and %s", resource, real_jid, affiliation, role);
          Req_Set_Role = 1;
        }
        else
          sprintf(r, "%s joined as %s and %s", resource, affiliation, role);
          Req_Set_Role = 1;

        }
        else 
        {
        if(real_jid)
        {
          sprintf(r, "%s (%s) joined as %s and %s", resource, real_jid, affiliation, role);
          Req_Set_Role = 1;
        }
        else
        {
          sprintf(r, "%s joined as %s and %s", resource, affiliation, role);
          Req_Set_Role = 1;
        }
        };

        char* my_nick = UtilGetResourceByFullJID(contact->jid);
        if ((!strcmp(resource, my_nick))&&(contact->status == XMPPS_OFFLINE)) //если ето мы, входим в нее.
        {
          contact->status = XMPPS_ONLINE;
          //ShowMSG(1,(int)LG_MUCCROK);
        };
        ContactListAddSystemMessage(contact->jid, XMPPS_ONLINE, r);
	
	  
	  if(status == XMPPS_OFFLINE) // Выход
      {
        XMLNode* statusmsg_node = XML_Get_Child_Node_By_Name(node, "status");
        if (!statusmsg_node)
          sprintf(r, "%s left us", resource);
        else 
        {
         if(statusmsg_node->value) sprintf(r, "%s left us (%s)", resource, statusmsg_node->value);
         else sprintf(r, "%s left us", resource);
        }

        char* my_nick = UtilGetResourceByFullJID(contact->jid);
        if (!strcmp(resource, my_nick)) //если ето мы, удаляем конфу.
        {
          XMPPLeaveConference(contact->jid);
          //CList_MakeAllResourcesOFFLINE(Conference);
        }; 
		ContactListChangeStatus( contact->jid, XMPPS_OFFLINE );
        priv.role = ROLE_NONE;
        priv.aff  = AFFILIATION_NONE;
        Req_Set_Role = 1;
      }
    }*/
	
end:;
	
	dbgf("XMPPProcessPresenceChange: done");
} 

// Входящие сообщения
void XMPPProcessIncomingMessage(XMLNode* nodeEx)
{
	int			i = 0, j = 0;
	char		*from = XML_Get_Attr_Value("from", nodeEx->attr); if(!from) return;
	CONTACT_T	*contact;
	char 		buffer[2048];
	WCHAR 		jid[128];
	
	dbg("XMPPProcessIncomingMessage: enter");
	
	
	UtilGetJIDByFullJID(from, xmpp_msg_from);
	contact = ContactListFindContact( xmpp_msg_from );
   
   	if(!contact && XMPPIsReady()){
		dbgf("XMPPProcessIncomingMessage: contact = NULL, ContactListAddContact : %s", xmpp_msg_from);
		UINT8   gr_id;
		
				if( !ContactListGetGroupID(NOT_IN_LIST) ) 
				gr_id = ContactListAddGroup(NOT_IN_LIST);
				else
				gr_id = ContactListGetGroupID(NOT_IN_LIST);
				
		ContactListAddContact(xmpp_msg_from, xmpp_msg_from, SUB_NONE, gr_id);
		contact = ContactListFindContact( xmpp_msg_from );
	}
	
	dbgf("XMPPProcessIncomingMessage: contact = 0x%p", contact);
	if( !contact ) return;
	
    // Если включено обслуживание запросов о получении...
    XMLNode* xnode = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "x", "xmlns", "http://jabber.org/protocol/muc"); //обработка invite
    if( xnode ) 
	{   
		XMLNode *invite =  XML_Get_Child_Node_By_Name(xnode, "invite");
		if(invite)
		{
			if(!contact) //если еще нет такой конфы то добавим в список muctop, а вдруг зайдем
				ContactListAddContact(from, from, SUB_BOTH, 0);
		}
    }
	
	//обработка event
	xnode =  XML_Get_Child_Node_By_Name_And_Attr( nodeEx, "event", "xmlns", "http://jabber.org/protocol/pubsub#event" );
	
	if( xnode ) // Обработка настроений. ужасный код :O 
	{
		XMLNode* items = XML_Get_Child_Node_By_Name_And_Attr( xnode, "items", "node", "http://jabber.org/protocol/mood" );

		if( items ) {
			XMLNode *item = XML_Get_Child_Node_By_Name(items, "item");
			
			if( item ) { // XEP-0107 // Настроения
				XMLNode *mood = XML_Get_Child_Node_By_Name_And_Attr( item, "mood", "xmlns", "http://jabber.org/protocol/mood" ); 
	
				if( mood ) {
					XMLNode *mood_type = NULL;

					for( j = 0; j < 36; j++ ) {
						mood_type = XML_Get_Child_Node_By_Name(mood, (char*)xmpp_mood[j] );
						if( mood_type ) {
							contact->xstatus = j;
							handleAppNtf( XMPP_NTF__USER_XSTATUS, from, &j, 1 );
							return;
						}
					}
				}
			}
			
		} else if ( items = XML_Get_Child_Node_By_Name_And_Attr( xnode, "items", "node", "http://jabber.org/protocol/activity" ) ) {
		
			if( items ) {
				XMLNode *item = XML_Get_Child_Node_By_Name(items, "item");
				
				if( item ) { // XEP-0108
					XMLNode *activity = XML_Get_Child_Node_By_Name_And_Attr( item, "activity", "xmlns", "http://jabber.org/protocol/activity" ); 
		
					if( activity ) {
						XMLNode* activity_type = NULL;
						XMLNode* activity_subtype = NULL;
					
						for( i = 0; i < 10; i++ ) {
							activity_type = XML_Get_Child_Node_By_Name(activity, (char*)xmpp_activity_types[i] );


							if( activity_type )	{

								switch( i ) {
									case 1: //	activity:eating
										j = 7;  // Кушаю
										goto set_xstatus;
										break;

									case 3: //	activity:having_appointment
										j = 12; // Дела
										goto set_xstatus;
										break;

									case 6: //  activity:working
										j = 23; // Инженеринг
										goto set_xstatus;
										break;

									default:
										for( j = 0; j < 36; j++ ) {
											activity_subtype = XML_Get_Child_Node_By_Name(activity_type, (char*)xmpp_activity[j] );
											
											if( activity_subtype ) {
set_xstatus:									contact->xstatus = j;
												handleAppNtf( XMPP_NTF__USER_XSTATUS, from, &j, 1 );
												return;
											}
										}
										break;
								}
							}
						}
					}
				}
			}
		} 
	}
	
    // Сообшение о наборе
	xnode = XML_Get_Child_Node_By_Name_And_Attr( nodeEx, "composing", "xmlns", "http://jabber.org/protocol/chatstates" );
    if( xnode ) 
	{
		ContactListChangeComposingStatus(xmpp_msg_from, true);
    }
	
	xnode = XML_Get_Child_Node_By_Name_And_Attr( nodeEx, "active", "xmlns", "http://jabber.org/protocol/chatstates" );
    if( xnode )
    {
        ContactListChangeComposingStatus(xmpp_msg_from, false);
    }
 
	//обработка attention xep-0224
	xnode = XML_Get_Child_Node_By_Name_And_Attr( nodeEx, "attention", "xmlns", "urn:xmpp:attention:0" );
	if( xnode )
	{
		handleAppNtf( XMPP_NTF__INCOMING_MESSAGE_SYSTEM, xmpp_msg_from, "Wake You up!", 12 );
	}
   
   // delivery
    xnode = XML_Get_Child_Node_By_Name_And_Attr( nodeEx, "request", "xmlns", "urn:xmpp:receipts" );
    if( xnode )
    {
         char *id = XML_Get_Attr_Value("id", nodeEx->attr);
         XMPPReportDelivery(id, XML_Get_Attr_Value("from", nodeEx->attr));
    }
	
   /*// delay
    xnode = XML_Get_Child_Node_By_Name_And_Attr( nodeEx, "request", "xmlns", "urn:xmpp:delay" );
    if( xnode )
    {
        char *stamp = XML_Get_Attr_Value("stamp", nodeEx->attr);
		 
		contact->time = malloc(strlen(stamp)+1);
		 
		UTF8toCP1251(stamp, contact->time);
		dbgf("XMPPProcessIncomingMessage: delay = %s", time);
    }
	
	*/
	XMLNode* msgnode = XML_Get_Child_Node_By_Name(nodeEx, "body");
	XMLNode* msgsubject = XML_Get_Child_Node_By_Name(nodeEx, "subject");
	XMLNode* msgerror = XML_Get_Child_Node_By_Name(nodeEx, "error");
	
	xnode = XML_Get_Child_Node_By_Name_And_Attr(nodeEx, "x", "xmlns", "jabber:x:oob"); 
	XMLNode* url = NULL; if(xnode) url = XML_Get_Child_Node_By_Name(xnode, "url");
	if( !msgnode )
	{
		if( msgsubject )
			msgnode = msgsubject;
	}
	if( msgerror )
	{
		XMLNode* texterror = XML_Get_Child_Node_By_Name(msgerror, "text");
		if(texterror)
		  if(texterror->value)
			if(strlen(texterror->value))
			  msgnode = texterror;
	}
	if( msgnode )
		if(msgnode->value)
		{
			if ( msgsubject ){
				if(msgsubject->value){  //если есть тема, обработаем...
					strcpy(buffer, xmpp_lang(LG_SUBJECT));
					UTF8toCP1251(msgsubject->value, buffer+strlen(buffer));
					strcat(buffer, "\n");
					UTF8toCP1251(msgnode->value, buffer+strlen(buffer));
				}
			} else {
				UTF8toCP1251(msgnode->value, buffer);
			}
			
			if(url)
			if(url->value)
			{
				strcat(buffer, "\nURL:\n");
				strcat(buffer, url->value);
				strcat(buffer, "\n");
			}
			
			dbgf("XMPPProcessIncomingMessage: msg_from = %s, msg_text len = %d", xmpp_msg_from, strlen(buffer));
			contact->new_msg = true;
			handleAppNtf( XMPP_NTF__INCOMING_MESSAGE, xmpp_msg_from, buffer, strlen(buffer) );
		}
	  
	dbg("XMPPProcessIncomingMessage: done");
}

void XMPPReportVersionInfo(char* id, char *to)
{
	char version_info_tpl[512];
	char version[32];
	char os[32];
  
	sprintf(version, "%s (%s)", ELF_VER, __DATE__);
	sprintf(os, "Motorola-%s/%s", ldrGetPhoneName(), ldrGetFirmwareVersion());
  
	sprintf(version_info_tpl, "<iq type='res' id='%s' to='%s' >"
									"<query xmlns='jabber:iq:version'>"
										"<name>%s</name>"
										"<version>%s</version>"
										"<os>%s</os>"
									"</query>"
								"</iq>",
							id,
							to,
							ELF_NAME,
							version,
							os);
  
  SockSend(version_info_tpl, strlen(version_info_tpl));
}

void XMPPReportTimeInfo(char* id, char *to)
{
	CLK_DATE_T date;
	CLK_TIME_T time;
  
	DL_ClkGetDate( &date );
	DL_ClkGetTime( &time );
	
	
	char time_info[512];
	
	sprintf(time_info, "<iq from='%s' to='%s' id='%s' type='result' >"
							"<utc>%04d%02d%02dT%02d:%02d:%02d</utc>"
								"<tz>%s</tz>"
								"<display>%04d%02d%02dT%02d:%02d:%02d</display>"
						 "</iq>",
							xmpp_account.full_jid,
							to,
							id,
							date.year,
							date.month,
							date.day,
							time.hour,
							time.minute,
							time.second,
							xmpp_account.time_zone,
							date.year,
							date.month,
							date.day,
							time.hour,
							time.minute,
							time.second
							);
							
	SockSend(time_info, strlen(time_info));
	
};

void XMPPReportDiscoInfo(char* id, char *to)
{
   char disco_info[1152];

   sprintf(disco_info, "<iq from='%s' to='%s' id='%s' type='result' >"
							"<identity category='client' name='%s %s' type='phone'>"
								"<feature var='http://jabber.org/protocol/caps'/>"
								"<feature var='http://jabber.org/protocol/activity'/>"
								"<feature var='http://jabber.org/protocol/activity+notify'/>"
								"<feature var='http://jabber.org/protocol/chatstates'/>"
								"<feature var='http://jabber.org/protocol/disco#info'/>"
								"<feature var='http://jabber.org/protocol/mood'/>"
								"<feature var='http://jabber.org/protocol/mood+notify'/>"
								"<feature var='http://jabber.org/protocol/muc'/>"
								"<feature var='http://jabber.org/protocol/pubsub#event'/>"
								"<feature var='http://qip.ru/x-status'/>"
								"<feature var='jabber:iq:last'/>"
								"<feature var='jabber:iq:time'/>"
								"<feature var='jabber:iq:version'/>"
								"<feature var='urn:xmpp:ping'/>"
								"<feature var='urn:xmpp:receipts'/>"
								"<feature var='urn:xmpp:attention:0'/>"
							"</identity>"
						 "</iq>",
							xmpp_account.full_jid,
							to,
							id,
							ELF_NAME,
							ELF_VER
							);
							

  SockSend(disco_info, strlen(disco_info));
}

void XMPPReportDelivery(char *mess_id, char *to)
{
  char delivery_tpl[312];

   sprintf(delivery_tpl, "<message from='%s' id='%d' to='%s'>"
								"<received xmlns='urn:xmpp:receipts'/>"
							"</message>",
							xmpp_account.full_jid,
							mess_id,
							to);
							
    SockSend(delivery_tpl, strlen(delivery_tpl));
}

void XMPPReportPING(char* id, char *to)
{
	if( !id || !to ) return;
	char ping_tpl[256];

   sprintf(ping_tpl, "<iq from='%s' to='%s' id='%s' type='result'/>",
							xmpp_account.full_jid,
							to,
							id);
  
  SockSend(ping_tpl, strlen(ping_tpl));

}

void XMPPSendVersionRequest(const char *to)
{
	if( !to ) return;
	char version_request_tpl[256];
	
	sprintf(version_request_tpl, "<iq to='%s' type='get' id='ver_req'>"
											"<query xmlns='jabber:iq:version'>"
										"</query>"
									"</iq>",
									to);
																
	SockSend(version_request_tpl, strlen(version_request_tpl));
}

// Послать запрос о версии vcard
void XMPPSendVcardRequest(const char *to)
{
	if( !to ) return;
	char vcard_request_tpl[256];
	
	sprintf(vcard_request_tpl, "<iq to='%s' type='get' id='vcard_req'>"
											"<vCard xmlns='vcard-temp'>"
											"</vCard>"
									"</iq>",
									to);
																
	SockSend(vcard_request_tpl, strlen(vcard_request_tpl));
}

void XMPPSendTranportRegisterRequest(const char *to)
{
	if( !to ) return;
	char reg_request_tpl[256];
	
	sprintf(reg_request_tpl, "<iq type='get' to='%s' id='reg_req1'>"
								"<query xmlns='jabber:iq:register'/>"
							"</iq>",
							to);
																
	SockSend(reg_request_tpl, strlen(reg_request_tpl));
}

void XMPPSendTranportUnregisterRequest(const char *to)
{
	if( !to ) return;
	char unreg_request_tpl[256];
	
	sprintf(unreg_request_tpl, "<iq type='set' to='%s' id='unreg_req1'>"
									"<query xmlns='jabber:iq:register'>"
										"<remove/>"
									"</query>"
								"</iq>",
								to);
																
	SockSend(unreg_request_tpl, strlen(unreg_request_tpl));
}

void XMPPSendTranportRegisterLogin(const char *to, RESISTER_INFO_T * reg_info)
{
	if( !to || !reg_info ) return;
	
	char reg_request_tpl[256];
	
	sprintf( reg_request_tpl, "<iq type='set' id='reg_req2' to='%s' >"
								"<query xmlns='jabber:iq:register'>",
								to );
								
	if( reg_info->edit_username && strlen(reg_info->username) ) {	
	
		strcat( reg_request_tpl,	"<username>" );
		strcat( reg_request_tpl,	reg_info->username );
		strcat( reg_request_tpl,	"</username>" );
	}
	
	if( reg_info->edit_email && strlen(reg_info->email) ) {	
	
		strcat( reg_request_tpl,	"<email>" );
		strcat( reg_request_tpl,	reg_info->email );
		strcat( reg_request_tpl,	"</email>" );
	}
	
	
	strcat( reg_request_tpl,	"<password>" );
	strcat( reg_request_tpl,	reg_info->password );
	strcat( reg_request_tpl,	"</password>"
								"</query>"
							"</iq>" );
				
																
	SockSend(reg_request_tpl, strlen(reg_request_tpl));
}

void XMPPSendServiceUnavailable(const char *to, const char *id)
{
  if( !to || !id ) return;
  char service_unavailable_tpl[256];
  
	sprintf(service_unavailable_tpl, "<iq to='%s' type='error'>"
											"<error type='cancel'>"
												"<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
											"</error>"
										"</iq>",
										to);
										
	SockSend(service_unavailable_tpl, strlen(service_unavailable_tpl));									
}

void XMPPSendFeatureNotImplemented(const char *id, const char *to)
{
  if(!to || !id) return;
  char feature_not_implemented_tpl[256];
										
	sprintf(feature_not_implemented_tpl, "<iq to='%s' type='error'>"
											"<error type='cancel'>"
												"<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>"
											"</error>"
										"</iq>",
										to);
										
							
  SockSend(feature_not_implemented_tpl, strlen(feature_not_implemented_tpl));
}

void XMPPCheckStatus(const char *to)
{
	if(!to) return;
    char subs_tpl[256];
										
	sprintf(subs_tpl, "<presence type='probe' to='%s'/>",
						to);
																
  SockSend(subs_tpl, strlen(subs_tpl));
}


void XMPPSubscribe(const char *to)
{
	if(!to) return;
    char subs_tpl[256];
										
	sprintf(subs_tpl, "<presence to='%s' from='%s' type='subscribe'/>",
							to,
							xmpp_account.full_jid);
																
  SockSend(subs_tpl, strlen(subs_tpl));
}

void XMPPSubscribed(const char *to)
{
	if(!to) return;
    char subs_tpl[256];
										
	sprintf(subs_tpl, "<presence to='%s' from='%s' type='subscribed'/>",
							to,
							xmpp_account.full_jid);
																
  SockSend(subs_tpl, strlen(subs_tpl));
}
void XMPPUnsubscribed(const char *to)
{
	if(!to) return;
    char subs_tpl[256];
										
	sprintf(subs_tpl, "<presence to='%s' from='%s' type='unsubscribed'/>",
							to,
							xmpp_account.full_jid);
																
  SockSend(subs_tpl, strlen(subs_tpl));
}


void XMPPSetStatus(UINT8 status, const char *message) 
{

	//char bufmsg[256];
	char 	buffer[256];
	char	utf8_message[64] = "";
	
	if( message ) 
		CP1251toUTF8( message, utf8_message );
	
	sprintf(buffer, "<presence>"
							"<show>%s</show>"
								"<status>%s</status>"
					"</presence>",
					presences[status], 
					utf8_message); 

	SockSend(buffer, strlen(buffer));
}

void XMPPSetXStatus(UINT8 xstatus, const char *message) 
{
	char 	buffer[1024];
	char	utf8_message[128] = "";
	
	if( message ) 
		CP1251toUTF8( message, utf8_message );
	
		sprintf(buffer,	"<presence>"
							"<x xmlns='http://qip.ru/x-status' id='%d'>"
								"<title>%s</title>"
							"</x>"
						"</presence>",
						xstatus,
						utf8_message);
	
	if( UtilGetIcqTransport() ) {
		
		char	xstatus_tpl[768];
		sprintf(xstatus_tpl, "<iq type='set' id='xstatus' to='%s'>"
									"<command xmlns='http://jabber.org/protocol/commands' node='setxstatus' action='complete'>"
										"<x xmlns='jabber:x:data' type='submit'>"
											"<field var='xstatus_desc'>"
												"<value>%s</value>"
											"</field>"
											"<field var='xstatus_name'>"
												"<value>%s</value>"
											"</field>"
										"</x>"
									"</command>"
								"</iq>",
								UtilGetIcqTransport(),
								utf8_message,
								xmpp_pyicq_t_xstatuses[xmpp_account.xstatus] );
								
		SockSend(xstatus_tpl, strlen(xstatus_tpl));					
	}

	SockSend(buffer, strlen(buffer));
		
}

void XMPPAddContact(const char *jid, const char *group) 
{
	if( !jid || !group ) return;
	
	char	utf8_group[64];
	CP1251toUTF8( group, utf8_group );
	
	dbgf("XMPPAddContact: jid = %s, group = %s", jid, utf8_group);
	
	char buffer[256];
	sprintf(buffer, "<iq type='set' id='rost_req'>"
							"<query xmlns='jabber:iq:roster'>"
								"<item jid='%s'>"
									"<group>%s</group>"
								"</item>"
							"</query>"
						"</iq>",
						jid, 
						utf8_group); 
	
	
	SockSend(buffer, strlen(buffer));
}

void XMPPRenameContact(const char *jid,const char *new_name) 
{
	if( !jid || !new_name ) return;
	
	char	utf8_group[64], utf8_new_name[64];
	
	CP1251toUTF8( ContactListGetGroupNameByID( ContactListFindContact(jid)->group_id ), utf8_group );
	CP1251toUTF8( new_name, utf8_new_name );
	
	dbgf("XMPPRenameContact: jid = %s, group = %s, new_name = %s", jid, utf8_group, utf8_new_name);
	
	char buffer[256];
	sprintf(buffer,  "<iq type='set' id='rost_req'>"
							"<query xmlns='jabber:iq:roster'>"
								"<item jid='%s' name='%s'>"
									"<group>%s</group>"
								"</item>"
							"</query>"
						"</iq>",
						jid, 
						utf8_new_name,
						utf8_group); 
	
	SockSend(buffer, strlen(buffer));
}

void XMPPMoveContact(const char *jid, const char *new_group)
{
	if( !jid || !new_group ) return;
	
	char	utf8_name[65], utf8_new_group[64];
	
	CP1251toUTF8( ContactListFindContact(jid)->nick, utf8_name );
	CP1251toUTF8( new_group, utf8_new_group );
	
	dbgf("XMPPMoveContact: jid = %s, name = %s, new_group = %s", jid, utf8_name, utf8_new_group);
	
	char buffer[256];
	sprintf(buffer,  "<iq type='set' id='rost_req'>"
							"<query xmlns='jabber:iq:roster'>"
								"<item jid='%s' name='%s'>"
									"<group>%s</group>"
								"</item>"
							"</query>"
						"</iq>",
						jid,
						utf8_name,
						utf8_new_group); 
	
	
	SockSend(buffer, strlen(buffer));
}

void XMPPRemoveContact(const char *jid) 
{
	if( !jid ) return;
	
	dbgf("XMPPRemoveContact: jid = %s", jid);
	
	char buffer[512];
	int len;
		sprintf(buffer, "<presence type='unsubscribed' to='%s'/>"
							"<iq type='set'>"
								"<query xmlns='jabber:iq:roster'>"
									"<item jid='%s' subscription='remove'/>"
								"</query>"
							"</iq>",
							jid,
							jid);
	
	len = SockSend(buffer, strlen(buffer));
	
}

void XMPPSendMsg(const char *jid, const char *message) 
{
	if(!jid || !message) return;
	//char bufmsg[256];
	char  *buffer, *utf8_message;
	
	utf8_message = (char*)malloc( strlen(message)*2 );
	
	CP1251toUTF8( message, utf8_message );
	
	buffer = (char*)malloc( strlen(utf8_message) + 256 );
	
	sprintf(buffer,  "<message type='chat' to='%s'>"
							"<active xmlns='http://jabber.org/protocol/chatstates'/>"
								"<body>%s</body>"
							"<request xmlns='urn:xmpp:receipts'/>"
						"</message>",
						jid,
						utf8_message
						);

	//sprintf(buffer, xmpp_msg_req, jid, FXMLEncode(message, bufmsg, XMPP_MSG_SIZE));
	
	SockSend(buffer, strlen(buffer));
	
	mfree(utf8_message);
	mfree(buffer);
}

void XMPPSendAttention(const char *jid) 
{
	if( !jid ) return;
	char buffer[512];
	
	
	sprintf(buffer,  "<message type='chat' to='%s'>"
							"<attention xmlns='urn:xmpp:attention:0'/>"
						"</message>",
						jid
						);


	SockSend(buffer, strlen(buffer));
}


void XMPPSendUrl(const char *jid, const char *url) 
{
	char buffer[512];
	
	if( !jid || !url ) return;
	
	sprintf(buffer,	"<message type='chat' to='%s'>"
						"<x xmlns='jabber:x:oob'>"
								"<url>%s</url>"
							"</x>"
						"</message>",
						jid,
						url
						);


	SockSend(buffer, strlen(buffer));
}

void XMPPSendComposing( const char* to )
{
	char buffer[512];
	
	if( !to ) return;
	
	sprintf(buffer,	"<message from='%s' to='%s' type='chat'>"
						"<composing xmlns='http://jabber.org/protocol/chatstates'/>"
					"</message>",
						xmpp_account.full_jid,
						to
						);

	SockSend(buffer, strlen(buffer));
	
}

void XMPPCancelComposing( const char* to )
{
	char buffer[512];
	
	if( !to ) return;
	
	sprintf(buffer,	"<message "
							"from='%s' "
							"to='%s' "
							"type='chat'>"
						"<thread>act2scene2chat1</thread>"
					"<active xmlns='http://jabber.org/protocol/chatstates'/>"
					"</message>",
						xmpp_account.full_jid,
						to
						);

	SockSend(buffer, strlen(buffer));
}

/*
// Входит в конференцию
void XMPPEnterConference(char *room, char *roomnick, char *roompass, UINT8 message_num)
{
  CONTACT_T *		contact = ContactListFindContact(room);

  // Добавляем контакт конференции в ростер
  if( !contact ) ContactListAddContact(room, room, SUB_BOTH, ContactListGetGroupID("Conferences"));

  if(contact->status == XMPPS_ONLINE) return; //Уже там, и нечего перезаходить


  char buffer[1024];
	
		sprintf(buffer, "<presence type='unsubscribed' to='%s'>"
							"<iq type='set'>"
								"<history maxstanzas='%d'/>"
								"<show>%s</show>"
								"<status>%s</status>"
								"<password>%s</password>"
								"<priority>%d</priority>"
								"<c xmlns='http://jabber.org/protocol/caps' node='%s' ver='%s'/>"
								"<x xmlns='http://jabber.org/protocol/muc' />"
								"<x xmlns='http://qip.ru/x-status' id='%d'><title>?</title></x>"
							"</iq>"							
						"</presence>",
						room,
						message_num,
						presences[xmpp_account.status],
						xmpp_account.status_msg[xmpp_account.status],
						roompass,
						xmpp_account.priority,
						VERSION_NODE,
						ELF_VER,
						xmpp_account.xstatus);
							
	contact->status = XMPPS_ONLINE;
	
	SockSend(buffer, strlen(buffer));
}

// Выходит из конференции
void XMPPLeaveConference(char* room)
{

    char buffer[256];
	sprintf(buffer, "<presence from='%s' to='%s' type='unavailable'>"
							"<status></status>"
						"</presence>",
						xmpp_account.full_jid,
						room);
						
	SockSend(buffer, strlen(buffer));
}
*/

// В прошивке zcalloc & zcfree не работают,
// не выделяют память (смотрел в 44R), это чтобы небыло ошибок

void * zcalloc_( void * opaque, UINT32 items, UINT32 size )
{
	INT32	err;
	void *	ptr;
	UINT32	sz = items * size;
	ptr = suAllocMem(sz, &err);
	if ( err == 0 )	// OK
		return ptr;
	return NULL; // ERR
}

void zcfree_( void * opaque, void * ptr )
{
	suFreeMem(ptr);
}

void InitZlib()
{ 
/*  int err;
  
  //Заполняем служебные структуры ДО инициализации

  c_stream.zalloc_func = d_stream.zalloc_func = (alloc_func)zcalloc_;
  c_stream.zfree_func = d_stream.zfree_func = (free_func)zcfree_;
  c_stream.opaque_func = d_stream.opaque_func = (voidpf)0;
  

  err = inflateInit2(&d_stream, MAX_WBITS);
  if (err) {
	cprintf("\x8C Error: Zlib inflate init: %d\n", err);
    XMPPDisconnect();
  }
  
  err = deflateInit(&c_stream, Z_BEST_SPEED);
  
  if (err) {
	cprintf("\x8C Error: Zlib deflate init: %d\n", err);
	XMPPDisconnect();
  }
  
  xmpp_state = XMPPL_ZLIB_INIT_ACK;
  cprintf("\x82ZLib enable...\n");
  xmpp_account.zlib_on = true;
  
  XMPPUsePlainAuthReport();*/
}


void XstatusInit( void )
{
	FILE_HANDLE_T	list_file = FILE_INVALID;
	WCHAR			list_uri[256];
	int				i = 0;
	
	u_mkpath(list_uri, jabber_elf->dir, L"xstatus.list", 0);
	list_file = DL_FsOpenFile( list_uri, FILE_READ_MODE, 0);

	List_Init(&xstatus_list, XSTATUS_T );
	
	if( List_ReadFile( &xstatus_list, list_file ) < 0 ) {
	
		XSTATUS_T 	xstatus;
		memclr( &xstatus, sizeof(XSTATUS_T) );
		
		for( i = 0; i < 36; i++ ) {
			strcpy( xstatus.str, xmpp_pyicq_t_xstatuses[i] );
			List_AppendItem( &xstatus_list, &xstatus );
		}
	}
	
	DL_FsCloseFile( list_file );

}
void XstatusUninit( void )
{
	FILE_HANDLE_T    list_file = FILE_INVALID;
	WCHAR   		 list_uri[256];
	
	 u_mkpath(list_uri, jabber_elf->dir, L"xstatus.list", 0);
	 list_file = DL_FsOpenFile( list_uri, FILE_WRITE_MODE, 0);
	
	 List_WriteFile( &xstatus_list, list_file );
	 DL_FsCloseFile( list_file );
	 
	 List_Clear(&xstatus_list);
}

char * XstatusGetItemStr( int index )
{
	XSTATUS_T * xstatus;
	
	xstatus = (XSTATUS_T *)List_GetItem(&xstatus_list, index);
	
	if ( xstatus == NULL )
		return "N/A";
		
	if ( xstatus->str == NULL )
		return "N/A";
	
	return xstatus->str;
}

void XstatusReplaceItemStr( int index, char *str )
{
	XSTATUS_T * xstatus;
	char 		utf8_str[128];
	
	if( !str ) return;
	
	xstatus = (XSTATUS_T *)List_GetItem(&xstatus_list, index);
	
	if ( xstatus == NULL ) return;
		
	CP1251toUTF8( str, utf8_str );
	
	strncpy(xstatus->str, utf8_str, 128);
}

char * CP1251toUTF8(const char * src, char * dst)
{
	WCHAR	*trg = (WCHAR*)malloc((strlen(src)+1)*sizeof(WCHAR));
	
	DL_Char_convCP1251toUCS2String( src,
									strlen(src),
									trg,
									(strlen(src)+1)*sizeof(WCHAR));
									
	UCS2toUTF8(trg, dst);								
	
	mfree( trg );
	
	return dst;
}
