/*
Plugin of Miranda IM for communicating with users of the AIM protocol.
Copyright (c) 2008-2011 Boris Krasnovskiy
Copyright (C) 2005-2006 Aaron Myles Landwehr

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "aim.h"
#include "version.h"

int CAimProto::aim_send_connection_packet(HANDLE hServerConn,unsigned short &seqno,char *buf)
{
	return aim_sendflap(hServerConn,0x01,4,buf,seqno);
}

int CAimProto::aim_authkey_request(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE*3+strlen(username));
	aim_writesnac(0x17,0x06,offset,buf);
	aim_writetlv(0x01,(unsigned short)strlen(username),username,offset,buf);
	aim_writetlv(0x4B,0,0,offset,buf);
	aim_writetlv(0x5A,0,0,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_auth_request(HANDLE hServerConn,unsigned short &seqno,const char* key,const char* language,
								const char* country, const char* username, const char* password)
{
	unsigned short offset=0;
	mir_md5_byte_t pass_hash[16];
	mir_md5_byte_t auth_hash[16];
	mir_md5_state_t state;
	
	mir_md5_init(&state);
	mir_md5_append(&state,(const mir_md5_byte_t *)password, (int)strlen(password));
	mir_md5_finish(&state,pass_hash);
	mir_md5_init(&state);
	mir_md5_append(&state,(mir_md5_byte_t*)key, (int)strlen(key));
	mir_md5_append(&state,(mir_md5_byte_t*)pass_hash,MD5_HASH_LENGTH);
	mir_md5_append(&state,(mir_md5_byte_t*)AIM_MD5_STRING, sizeof(AIM_MD5_STRING)-1);
	mir_md5_finish(&state,auth_hash);

	char client_id[64];
	int client_id_len = mir_snprintf(client_id, sizeof(client_id), "Miranda AIM, version %s", __VERSION_STRING);

	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE*14+MD5_HASH_LENGTH+strlen(username)+client_id_len+30+strlen(language)+strlen(country));

	aim_writesnac(0x17,0x02,offset,buf);
	aim_writetlv(0x01,(unsigned short)strlen(username),username,offset,buf);
	aim_writetlv(0x25,MD5_HASH_LENGTH,(char*)auth_hash,offset,buf);
	aim_writetlv(0x4C,0,0,offset,buf);//signifies new password hash instead of old method
	aim_writetlv(0x03,(unsigned short)client_id_len,client_id,offset,buf);
	aim_writetlvshort(0x17,AIM_CLIENT_MAJOR_VERSION,offset,buf);
	aim_writetlvshort(0x18,AIM_CLIENT_MINOR_VERSION,offset,buf);
	aim_writetlvshort(0x19,AIM_CLIENT_LESSER_VERSION,offset,buf);
	aim_writetlvshort(0x1A,AIM_CLIENT_BUILD_NUMBER,offset,buf);
	aim_writetlvshort(0x16,AIM_CLIENT_ID_NUMBER,offset,buf);
	aim_writetlvlong(0x14,AIM_CLIENT_DISTRIBUTION_NUMBER,offset,buf);
	aim_writetlv(0x0F,(unsigned short)strlen(language),language,offset,buf);
	aim_writetlv(0x0E,(unsigned short)strlen(country),country,offset,buf);
	aim_writetlvchar(0x4A,getByte(AIM_KEY_FSC, 0) ? 3 : 1,offset,buf);
//    aim_writetlvchar(0x94,0,offset,buf);
	if (!getByte(AIM_KEY_DSSL, 0))
		aim_writetlv(0x8c,0,NULL,offset,buf);                       // Request SSL connection
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_send_cookie(HANDLE hServerConn,unsigned short &seqno,int cookie_size,char * cookie)
{
	unsigned short offset=0;
	char* buf=(char*)alloca(TLV_HEADER_SIZE*2+cookie_size);
	aim_writelong(0x01,offset,buf);//protocol version number
	aim_writetlv(0x06,(unsigned short)cookie_size,cookie,offset,buf);
	return aim_sendflap(hServerConn,0x01,offset,buf,seqno);
}

int CAimProto::aim_send_service_request(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*12];
	aim_writesnac(0x01,0x17,offset,buf);
	aim_writefamily(AIM_SERVICE_GENERIC,offset,buf);
	aim_writefamily(AIM_SERVICE_SSI,offset,buf);
	aim_writefamily(AIM_SERVICE_LOCATION,offset,buf);
	aim_writefamily(AIM_SERVICE_BUDDYLIST,offset,buf);
	aim_writefamily(AIM_SERVICE_MESSAGING,offset,buf);
	aim_writefamily(AIM_SERVICE_ICQ,offset,buf);
	aim_writefamily(AIM_SERVICE_INVITATION,offset,buf);
	aim_writefamily(AIM_SERVICE_POPUP,offset,buf);
	aim_writefamily(AIM_SERVICE_BOS,offset,buf);
	aim_writefamily(AIM_SERVICE_USERLOOKUP,offset,buf);
	aim_writefamily(AIM_SERVICE_STATS,offset,buf);
	aim_writefamily(AIM_SERVICE_UNKNOWN,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_new_service_request(HANDLE hServerConn,unsigned short &seqno,unsigned short service)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+2+TLV_HEADER_SIZE];
	aim_writesnac(0x01,0x04,offset,buf);
	aim_writeshort(service,offset,buf);
	if (!getByte(AIM_KEY_DSSL, 0))
		aim_writetlv(0x8c,0,NULL,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_request_rates(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE];
	aim_writesnac(0x01,0x06,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_accept_rates(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE*2];
	aim_writesnac(0x01,0x08,offset,buf);
	aim_writegeneric(10,AIM_SERVICE_RATES,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_request_icbm(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE];
	aim_writesnac(0x04,0x04,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_set_icbm(HANDLE hServerConn,unsigned short &seqno)
{
	const unsigned icbm_flags = ICBM_CHANNEL_MSGS_ALLOWED | ICBM_MISSED_CALLS_ENABLED | 
		ICBM_EVENTS_ALLOWED | ICBM_SMS_SUPPORTED | ICBM_OFFLINE_MSGS_ALLOWED;
//	const unsigned icbm_flags = 0x3db;

	unsigned short offset=0;
	char buf[SNAC_SIZE+16];
	aim_writesnac(0x04,0x02,offset,buf);
	aim_writeshort(0,offset,buf);           //channel
	aim_writelong(icbm_flags,offset,buf);   //flags
	aim_writeshort(0x1f40,offset,buf);      //max snac size              8000
	aim_writeshort(0x03e7,offset,buf);      //max sender warning level   999 (0-1000) WinAim default
	aim_writeshort(0x03e7,offset,buf);      //max receiver warning level 999 (0-1000) WinAim default
	aim_writelong(0,offset,buf);            //min message interval, ms   0
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_request_offline_msgs(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE];
	aim_writesnac(0x04,0x10,offset,buf); // Subtype for offline messages 0x10
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_request_list(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE];
	aim_writesnac(0x13,0x04,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_activate_list(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE];
	aim_writesnac(0x13,0x07,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

/*
0000   00 05 00 02 00 17 00 06 00 03 00 00 00 00 07 00  ................
0010   01 00 00 08 00 01 01 00 0a 00 14 00 02 00 08 66  ...............f
0020   61 63 65 62 6f 6f 6b 00 06 67 6f 6f 67 6c 65     acebook..google

int CAimProto::aim_request_rights(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+50];
	aim_writesnac(0x03,0x02,offset,buf);
	aim_writetlvshort(0x05,0x17,offset,buf);
	aim_writetlv(0x06,3,"\x0\x0",offset,buf);
	aim_writetlvchar(0x07,0x01,offset,buf);
	aim_writetlvshort(0x05,0x17,offset,buf);
	aim_writetlvshort(0x05,0x17,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}
*/
int CAimProto::aim_set_caps(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	int i=1;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*3+AIM_CAPS_LENGTH*50+sizeof(AIM_MSG_TYPE)];
	char temp[AIM_CAPS_LENGTH*20];
	memcpy(temp,AIM_CAP_SHORT_CAPS,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_HOST_STATUS_TEXT_AWARE,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_SMART_CAPS,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_FILE_TRANSFER,AIM_CAPS_LENGTH);
//	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_HAS_MICROPHONE,AIM_CAPS_LENGTH);
//	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_RTCAUDIO,AIM_CAPS_LENGTH);
//	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_HAS_CAMERA,AIM_CAPS_LENGTH);
//	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_RTCVIDEO,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_BUDDY_ICON,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_CHAT,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_SUPPORT_ICQ,AIM_CAPS_LENGTH);
//	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_ICQ_SERVER_RELAY,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_UTF8,AIM_CAPS_LENGTH);
	memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_MIRANDA,AIM_CAPS_LENGTH);
	if(getByte(AIM_KEY_HF, 0))
		memcpy(&temp[AIM_CAPS_LENGTH*i++],AIM_CAP_HIPTOP,AIM_CAPS_LENGTH);
	aim_writesnac(0x02,0x04,offset,buf);
	aim_writetlv(0x05,(unsigned short)(AIM_CAPS_LENGTH*i),temp,offset,buf);

	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_set_profile(HANDLE hServerConn,unsigned short &seqno, char* amsg)//user info
{
	aimString str(amsg);
	const char *charset = str.isUnicode() ? AIM_MSG_TYPE_UNICODE : AIM_MSG_TYPE;
	const unsigned short charset_len = (unsigned short)strlen(charset);

	const char* msg = str.getBuf();
	const unsigned short msg_len = str.getSize();

	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE*3+1+charset_len+msg_len);
	unsigned short offset=0;

	aim_writesnac(0x02,0x04,offset,buf);
	aim_writetlvchar(0x0c,1,offset,buf);
	aim_writetlv(0x01,charset_len,charset,offset,buf);
	aim_writetlv(0x02,msg_len,msg,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_client_ready(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	NETLIBBIND nlb = {0};
	nlb.cbSize = sizeof(nlb);
	nlb.pfnNewConnectionV2 = (NETLIBNEWCONNECTIONPROC_V2)aim_direct_connection_initiated;
	nlb.pExtra = this;
	hDirectBoundPort = (HANDLE)CallService(MS_NETLIB_BINDPORT, (WPARAM)hNetlibPeer, (LPARAM)&nlb);
	if (hDirectBoundPort == NULL)
	{
		ShowPopup(LPGEN("Aim was unable to bind to a port. File transfers may not succeed in some cases."), ERROR_POPUP);
	}
	else
	{
		local_port = nlb.wPort;
		internal_ip = nlb.dwInternalIP;
	}

	char buf[SNAC_SIZE+TLV_HEADER_SIZE*22];
	aim_writesnac(0x01,0x02,offset,buf);
	aim_writefamily(AIM_SERVICE_GENERIC,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_SSI,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_LOCATION,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_BUDDYLIST,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_MESSAGING,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_ICQ,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_INVITATION,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	//removed extra generic server 
	aim_writefamily(AIM_SERVICE_POPUP,offset,buf);
	aim_writegeneric(4,"\x01\x04\0\x01",offset,buf);//different version number like trillian 3.1
	aim_writefamily(AIM_SERVICE_BOS,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_USERLOOKUP,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_STATS,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_mail_ready(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*4];
	aim_writesnac(0x01,0x02,offset,buf);
	aim_writefamily(AIM_SERVICE_GENERIC,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_MAIL,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_avatar_ready(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*4];
	aim_writesnac(0x01,0x02,offset,buf);
	aim_writefamily(AIM_SERVICE_GENERIC,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_AVATAR,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_chatnav_ready(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*4];
	aim_writesnac(0x01,0x02,offset,buf);
	aim_writefamily(AIM_SERVICE_GENERIC,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_CHATNAV,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_chat_ready(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*4];
	aim_writesnac(0x01,0x02,offset,buf);
	aim_writefamily(AIM_SERVICE_GENERIC,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_CHAT,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_send_message(HANDLE hServerConn,unsigned short &seqno,const char* sn,char* amsg,bool auto_response, bool blast)
{	
	aimString str(amsg);

	const char* msg = str.getBuf();
	const unsigned short msg_len = str.getSize();

	unsigned short tlv_offset=0;
	char* tlv_buf=(char*)alloca(5+msg_len+8);

	char icbm_cookie[8];
	CallService(MS_UTILS_GETRANDOM, 8, (LPARAM)icbm_cookie);
 
	aim_writegeneric(5,"\x05\x01\x00\x01\x01",tlv_offset,tlv_buf);   // icbm im capabilities
	aim_writeshort(0x0101,tlv_offset,tlv_buf);                       // icbm im text tag
	aim_writeshort(msg_len+4,tlv_offset,tlv_buf);                    // icbm im text tag length
	aim_writeshort(str.isUnicode()?2:0,tlv_offset,tlv_buf);          // character set
	aim_writeshort(0,tlv_offset,tlv_buf);                            // language

	aim_writegeneric(msg_len,msg,tlv_offset,tlv_buf);                // message text
	
	unsigned short offset=0;
	unsigned short sn_length=(unsigned short)strlen(sn);
	char* buf= (char*)alloca(SNAC_SIZE+8+3+sn_length+TLV_HEADER_SIZE*3+tlv_offset);
	
	aim_writesnac(0x04,0x06,offset,buf,get_random());
	aim_writegeneric(8,icbm_cookie,offset,buf);                      // icbm cookie
	aim_writeshort(0x01,offset,buf);                                 // channel
	aim_writechar((unsigned char)sn_length,offset,buf);              // screen name len
	aim_writegeneric(sn_length,sn,offset,buf);                       // screen name

	aim_writetlv(0x02,tlv_offset,tlv_buf,offset,buf);

	if (!blast)
	{
		if(auto_response)
			aim_writetlv(0x04,0,0,offset,buf);                       // auto-response message
		else
		{
			aim_writetlv(0x03,0,0,offset,buf);                       // message ack request
			aim_writetlv(0x06,0,0,offset,buf);                       // offline message storage
		}
	}
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno) ? 0 : *(int*)icbm_cookie & 0x7fffffff;
}

int CAimProto::aim_query_profile(HANDLE hServerConn,unsigned short &seqno,char* sn)
{
	unsigned short offset=0;
	unsigned short sn_length=(unsigned short)strlen(sn);
	char* buf=(char*)alloca(SNAC_SIZE+5+sn_length);
	aim_writesnac(0x02,0x15,offset,buf);
	aim_writelong(0x01,offset,buf);
	aim_writechar((unsigned char)sn_length,offset,buf);
	aim_writegeneric(sn_length,sn,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno)==0;
}

int CAimProto::aim_delete_contact(HANDLE hServerConn, unsigned short &seqno, char* sn, unsigned short item_id,
								  unsigned short group_id, unsigned short list, bool nil)
{
	unsigned short offset=0;
	unsigned short sn_length=(unsigned short)strlen(sn);
	char* buf=(char*)alloca(SNAC_SIZE+sn_length+10);
	aim_writesnac(0x13,0x0a,offset,buf, get_random());              // SSI Delete
	aim_writeshort(sn_length,offset,buf);                           // screen name length
	aim_writegeneric(sn_length,sn,offset,buf);                      // screen name
	aim_writeshort(group_id,offset,buf);                            // group id
	aim_writeshort(item_id,offset,buf);                             // buddy id
	aim_writeshort(list,offset,buf);                                // buddy type
	aim_writeshort(nil?4:0,offset,buf);                             // length of extra data
	if (nil) aim_writetlv(0x6a,0,NULL,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_add_contact(HANDLE hServerConn, unsigned short &seqno, const char* sn, unsigned short item_id,
							   unsigned short group_id, unsigned short list, char* nick, char* note)
{
	unsigned short offset=0;
	unsigned short sn_length=(unsigned short)strlen(sn);
	unsigned short nick_length = (unsigned short)_strlens(nick);
	unsigned short note_length = (unsigned short)_strlens(note);
	unsigned short tlv_len = nick || note ? TLV_HEADER_SIZE * 2 + nick_length + note_length : 0;

	char* buf=(char*)alloca(SNAC_SIZE + sn_length + 10 + tlv_len);
	aim_writesnac(0x13,0x08,offset,buf, get_random());               // SSI Add
	aim_writeshort(sn_length,offset,buf);                            // screen name length
	aim_writegeneric(sn_length,sn,offset,buf);                       // screen name
	aim_writeshort(group_id,offset,buf);                             // group id
	aim_writeshort(item_id,offset,buf);                              // buddy id
	aim_writeshort(list,offset, buf);                                // buddy type
	aim_writeshort(tlv_len,offset,buf);                              // length of extra data

	if (nick || note)
	{
		aim_writetlv(0x13c,note_length,note,offset,buf);
		aim_writetlv(0x131,nick_length,nick,offset,buf);
	}
	return aim_sendflap(hServerConn, 0x02, offset, buf, seqno);
}

int CAimProto::aim_mod_group(HANDLE hServerConn, unsigned short &seqno, const char* name, unsigned short group_id,
							 char* members, unsigned short members_length)
{
	unsigned short offset=0;
	unsigned short name_length=(unsigned short)strlen(name);
	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE+name_length+members_length+10);
	aim_writesnac(0x13,0x09,offset,buf, get_random());              // SSI Edit
	aim_writeshort(name_length,offset,buf);                         // group name length
	aim_writegeneric(name_length,name,offset,buf);                  // group name 
	aim_writeshort(group_id,offset,buf);                            // group id
	aim_writeshort(0,offset,buf);                                   // buddy id
	aim_writeshort(1,offset,buf);                                   // buddy type: Group
	aim_writeshort(TLV_HEADER_SIZE+members_length,offset,buf);      // length of extra data 
	aim_writetlv(0xc8,members_length,members,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_mod_buddy(HANDLE hServerConn, unsigned short &seqno, const char* sn, 
							 unsigned short buddy_id, unsigned short group_id,
							 char* nick, char* note)
{
	unsigned short offset=0;
	unsigned short sn_length = (unsigned short)strlen(sn);
	unsigned short nick_length = (unsigned short)_strlens(nick);
	unsigned short note_length = (unsigned short)_strlens(note);
	unsigned short tlv_len = TLV_HEADER_SIZE * 2 + nick_length + note_length;
	
	char* buf=(char*)alloca(SNAC_SIZE+sn_length+10+tlv_len);
	aim_writesnac(0x13,0x09,offset,buf, get_random());              // SSI Edit
	aim_writeshort(sn_length,offset,buf);                           // screen name length
	aim_writegeneric(sn_length,sn,offset,buf);                      // screen name 
	aim_writeshort(buddy_id,offset,buf);                            // buddy id
	aim_writeshort(group_id,offset,buf);                            // group id
	aim_writeshort(0,offset,buf);                                   // buddy type: Buddy
	aim_writeshort(tlv_len,offset,buf);                             // length of extra data 

	aim_writetlv(0x13c,note_length,note,offset,buf);
	aim_writetlv(0x131,nick_length,nick,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_set_pd_info(HANDLE hServerConn, unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*3+20];
	unsigned short req = 0x09;
	if (pd_info_id == 0)
	{
		pd_info_id = get_random();
		req = 0x08;
	}
	aim_writesnac(0x13,req,offset,buf, get_random());               // SSI Edit/Add
	aim_writeshort(0,offset,buf);                                   // name length
	aim_writeshort(0,offset,buf);                                   // group id (root)
	aim_writeshort(pd_info_id,offset,buf);                          // buddy id
	aim_writeshort(0x4,offset,buf);                                 // pd info id
	aim_writeshort(0x15,offset,buf);                                // size
	aim_writetlvchar(0xca,pd_mode,offset,buf);                      // pd mode
	aim_writetlvlong(0xcb,0xffffffff,offset,buf);                   // pd mask
	aim_writetlvlong(0xcc,pd_flags,offset,buf);                     // pd flags
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_ssi_update_preferences(HANDLE hServerConn, unsigned short &seqno)
{
	unsigned short offset = 0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*4+100];
	unsigned short req = 0x09;
	if (pref1_id == 0)
	{
		pref1_id = get_random();
		req = 0x08;
	}
	aim_writesnac(0x13,req,offset,buf, get_random());               // SSI Edit/Add
	aim_writeshort(0,offset,buf);									// group name length
	aim_writeshort(0,offset,buf);									// group id (root)
	aim_writeshort(pref1_id,offset,buf);                            // buddy id
	aim_writeshort(5,offset,buf);                                   // buddy type: Presence

	unsigned short tlv_len = TLV_HEADER_SIZE * 2 + 8;
	if (pref2_len) tlv_len += TLV_HEADER_SIZE + pref2_len;
	if (pref2_set_len) tlv_len += TLV_HEADER_SIZE + pref2_set_len;

	aim_writeshort(tlv_len,offset,buf);								// length of extra data
	aim_writetlvlong(0xc9,pref1_flags,offset,buf);					// Update Buddy preferences 1
	aim_writetlvlong(0xd6,pref1_set_flags,offset,buf);				// Update Buddy preferences 1
	if (pref2_len) 
		aim_writetlv(0xd7,pref2_len,pref2_flags,offset,buf);		// Update Buddy preferences 2
	if (pref2_set_len) 
		aim_writetlv(0xd8,pref2_set_len,pref2_set_flags,offset,buf);// Update Buddy preferences 2
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_ssi_update(HANDLE hServerConn, unsigned short &seqno, bool start)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE];
	aim_writesnac(0x13,start?0x11:0x12,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno)==0;
}

int CAimProto::aim_keepalive(HANDLE hServerConn,unsigned short &seqno)
{
	return aim_sendflap(hServerConn,0x05,4,"\x0\x0\x0\xEE",seqno);
}

// used when requesting a regular file transfer
int CAimProto::aim_send_file(HANDLE hServerConn, unsigned short &seqno,
							 unsigned long ip, unsigned short port, 
							 bool force_proxy, file_transfer *ft)
{	
	char msg_frag[2048];
	unsigned short frag_offset=0;

	aim_writeshort(0,frag_offset,msg_frag);                             // request type
	aim_writegeneric(8,ft->icbm_cookie,frag_offset,msg_frag);           // icbm cookie
	aim_writegeneric(AIM_CAPS_LENGTH, AIM_CAP_FILE_TRANSFER,
		frag_offset, msg_frag);                                         // uuid
	aim_writetlvshort(0x0a,++ft->req_num,frag_offset,msg_frag);         // request number
//    aim_writetlvshort(0x12,2,frag_offset,msg_frag);                     // max protocol version

	aim_writetlvlong(0x02,ip,frag_offset,msg_frag);                     // ip
	aim_writetlvlong(0x16,~ip,frag_offset,msg_frag);                    // ip check

	aim_writetlvshort(0x05,port,frag_offset,msg_frag);                  // port
	aim_writetlvshort(0x17,~port,frag_offset,msg_frag);                 // port ip check
	
	if (force_proxy)
		aim_writetlv(0x10,0,0,frag_offset,msg_frag);                    // request proxy transfer
	else
		aim_writetlvlong(0x03,internal_ip,frag_offset,msg_frag);        // ip

	if (ft->req_num == 1)
	{
		if (ft->message)
		{
			aimString dscr(ft->message);

			const char* charset = dscr.isUnicode() ? "unicode-2-0" : "us-ascii";
			const unsigned short charset_len = (unsigned short)strlen(charset);

			const char* desc_msg = dscr.getBuf();
			const unsigned short desc_len = dscr.getSize();

			aim_writetlv(0x0e,2,"en",frag_offset,msg_frag);             // language used by the data
			aim_writetlv(0x0d,charset_len,charset,frag_offset,msg_frag);// charset used by the data
			aim_writetlv(0x0c,desc_len,desc_msg,frag_offset,msg_frag);  // invitaion text
		}

		aim_writetlv(0x0f,0,0,frag_offset,msg_frag);                    // request host check

		const char* fname = get_fname(ft->file);
		const unsigned short fnlen = (unsigned short)strlen(fname);

		char* fblock = (char*)alloca(9 + fnlen);
		*(unsigned short*)&fblock[0] = _htons(ft->pfts.totalFiles > 1 ? 2 : 1);     // single file transfer
		*(unsigned short*)&fblock[2] = _htons(ft->pfts.totalFiles);     // number of files
		*(unsigned long*) &fblock[4] = _htonl(ft->pfts.totalBytes);     // total bytes in files
		memcpy(&fblock[8], fname, fnlen + 1);

		const char* enc = is_utf(fname) ? "utf-8" : "us-ascii";
		aim_writetlv(0x2711,9+fnlen,fblock,frag_offset,msg_frag);               // extra data, file names, size
		aim_writetlv(0x2712,8,enc,frag_offset,msg_frag);                        // character set used by data
//        aim_writetlvlong64(0x2713,ft->pfts.totalBytes,frag_offset,msg_frag);    // file length

		LOG("Attempting to Send a file to a buddy.");
	}
	else
	{
		aim_writetlvshort(0x14,0x0a,frag_offset,msg_frag);              // Counter proposal reason
	}

	unsigned short offset=0;
	unsigned short sn_length=(unsigned short)strlen(ft->sn);
	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE*2+12+frag_offset+sn_length);
	aim_writesnac(0x04,0x06,offset,buf);                                // msg to host
	aim_writegeneric(8,ft->icbm_cookie,offset,buf);                     // icbm cookie
	aim_writeshort(2,offset,buf);                                       // icbm channel
	aim_writechar((unsigned char)sn_length,offset,buf);                 // screen name length
	aim_writegeneric(sn_length,ft->sn,offset,buf);                      // screen name
	aim_writetlv(0x05,frag_offset,msg_frag,offset,buf);                 // icbm tags
	aim_writetlv(0x03,0,0,offset,buf);                                  // request ack

	char cip[20];
	long_ip_to_char_ip(ip, cip);
	LOG("IP for Buddy to connect to: %s:%u", cip, port);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno)==0;
}


int CAimProto::aim_file_ad(HANDLE hServerConn,unsigned short &seqno,char* sn, char* icbm_cookie, bool deny, unsigned short max_ver)
{	
	unsigned short frag_offset=0;
	char msg_frag[10+AIM_CAPS_LENGTH+TLV_HEADER_SIZE*2+6];
	aim_writeshort(deny ? 1 : 2,frag_offset,msg_frag);              // icbm accept / deny
	aim_writegeneric(8,icbm_cookie,frag_offset,msg_frag);           // icbm cookie
	aim_writegeneric(AIM_CAPS_LENGTH,
		AIM_CAP_FILE_TRANSFER,frag_offset,msg_frag);                // uuid

//    if (max_ver > 1)
//        aim_writetlvshort(0x12,2,frag_offset,msg_frag);             // max protocol version

	unsigned short sn_length=(unsigned short)strlen(sn);
	unsigned short offset=0;
	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE+21+frag_offset+sn_length);
	aim_writesnac(0x04,0x06,offset,buf);                            // msg to host
	aim_writegeneric(8,icbm_cookie,offset,buf);                     // icbm cookie
	aim_writeshort(2,offset,buf);                                   // icbm channel
	aim_writechar((unsigned char)sn_length,offset,buf);             // screen name length
	aim_writegeneric(sn_length,sn,offset,buf);                      // screen name
	aim_writetlv(0x05,frag_offset,msg_frag,offset,buf);             // icbm tags

	LOG("%s a file transfer.", deny ? "Denying" : "Accepting");
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno)==0;
}

int CAimProto::aim_typing_notification(HANDLE hServerConn,unsigned short &seqno,char* sn,unsigned short type)
{
	unsigned short offset=0;
	unsigned short sn_length=(unsigned short)strlen(sn);
	char* buf= (char*)alloca(SNAC_SIZE+sn_length+13);
	aim_writesnac(0x04,0x14,offset,buf);
	aim_writegeneric(8,"\0\0\0\0\0\0\0\0",offset,buf);              // icbm cookie
	aim_writeshort(1,offset,buf);                                   // icbm channel
	aim_writechar((unsigned char)sn_length,offset,buf);             // screen name length
	aim_writegeneric(sn_length,sn,offset,buf);                      // screen name
	aim_writeshort(type,offset,buf);                                // typing event
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_set_idle(HANDLE hServerConn,unsigned short &seqno,unsigned long seconds)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+4];
	aim_writesnac(0x01,0x11,offset,buf);
	aim_writelong(seconds,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_request_mail(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+34];
	aim_writesnac(0x18,0x06,offset,buf);
	aim_writegeneric(34,
		"\x00\x02"
		"\xb3\x80\x9a\xd8\x0d\xba\x11\xd5\x9f\x8a\x00\x60\xb0\xee\x06\x31"
		"\x5d\x5e\x17\x08\x55\xaa\x11\xd3\xb1\x43\x00\x60\xb0\xfb\x1e\xcb",
		offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_activate_mail(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+17];
	aim_writesnac(0x18,0x16,offset,buf);
	aim_writegeneric(17,"\x02\x04\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_request_avatar(HANDLE hServerConn,unsigned short &seqno, const char* sn, unsigned short bart_type, const char* hash, unsigned short hash_size)
{
	unsigned short offset=0;
	unsigned char sn_length=(unsigned char)strlen(sn);
	char* buf= (char*)alloca(SNAC_SIZE+sn_length+hash_size+12);
	aim_writesnac(0x10,0x06,offset,buf);
	aim_writechar(sn_length,offset,buf);                            // screen name length
	aim_writegeneric(sn_length,sn,offset,buf);                      // screen name
	aim_writechar(1,offset,buf);                                    // number of BART ID
	aim_writebartid(bart_type,0,hash_size,hash,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_set_avatar_hash(HANDLE hServerConn, unsigned short &seqno, char flags, unsigned short bart_type, unsigned short &id, char size, const char* hash)
{
	unsigned short offset=0;
	
	char bart_type_txt[8];
	ultoa(bart_type, bart_type_txt, 10);
	unsigned short bart_type_len = (unsigned short)strlen(bart_type_txt);

	unsigned short req = 0x09;
	if (id == 0)
	{
		id = get_random();
		req = 0x08;
	}

	char* buf = (char*)alloca(SNAC_SIZE + TLV_HEADER_SIZE * 2 + 20 + size + bart_type_len);
	aim_writesnac(0x13,req,offset,buf, get_random());               // SSI Edit/Add
	aim_writeshort(bart_type_len,offset,buf);                       // name length
	aim_writegeneric(bart_type_len,bart_type_txt,offset,buf);       // name 
	aim_writeshort(0,offset,buf);                                   // group id
	aim_writeshort(id,offset,buf);                                  // buddy id
	aim_writeshort(0x14,offset,buf);                                // buddy type: Buddy Icon
	aim_writeshort(2+size+TLV_HEADER_SIZE,offset,buf);              // length of extra data

	char* buf2 = (char*)alloca(2+size);
	buf2[0] = flags;
	buf2[1] = (char)size;
	memcpy(&buf2[2], hash, size);
	aim_writetlv(0xd5, 2+size, buf2, offset, buf);                  // BART

	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_delete_avatar_hash(HANDLE hServerConn, unsigned short &seqno, char flags, unsigned short bart_type, unsigned short & id)
{
	unsigned short offset=0;

	if (id == 0) return -1;
	id = 0;
	
	char bart_type_txt[8];
	ultoa(bart_type, bart_type_txt, 10);
	unsigned short bart_type_len = (unsigned short)strlen(bart_type_txt);

	char* buf = (char*)alloca(SNAC_SIZE + 20 + bart_type_len);
	aim_writesnac(0x13,0x0a,offset,buf, get_random());              // SSI Delete
	aim_writeshort(bart_type_len,offset,buf);                       // name length
	aim_writegeneric(bart_type_len,bart_type_txt,offset,buf);       // name 
	aim_writeshort(0,offset,buf);                                   // group id
	aim_writeshort(id,offset,buf);                                  // buddy id
	aim_writeshort(0x14,offset,buf);                                // buddy type: Buddy Icon
	aim_writeshort(0,offset,buf);                                   // length of extra data

	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_upload_avatar(HANDLE hServerConn, unsigned short &seqno, unsigned short bart_type, const char* avatar, unsigned short avatar_size)
{
	unsigned short offset=0;
	char* buf=(char*)alloca(SNAC_SIZE+22+avatar_size);
	aim_writesnac(0x10,0x02,offset,buf);
	aim_writeshort(bart_type,offset,buf);                                   // BART id
	aim_writeshort(avatar_size,offset,buf);
	aim_writegeneric(avatar_size,avatar,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_search_by_email(HANDLE hServerConn,unsigned short &seqno, const char* email)
{
	unsigned short offset=0;
	char em_length=(char)strlen(email);
	char* buf= (char*)alloca(SNAC_SIZE+em_length);
	aim_writesnac(0x0a,0x02,offset,buf);	                        // Email search
	aim_writegeneric(em_length,email,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_chatnav_request_limits(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE];
	aim_writesnac(0x0d,0x02,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno) ? -1 : 0;
}

int CAimProto::aim_chatnav_create(HANDLE hServerConn,unsigned short &seqno, char* room, unsigned short exchage)
{
	//* Join Pseudo Room (Get's the info we need for the real connection)
	unsigned short room_len = (unsigned short)strlen(room);

	unsigned short offset=0;
	char* buf=(char*)alloca(SNAC_SIZE+10+room_len+26);
	aim_writesnac(0x0d,0x08,offset,buf);
	aim_writeshort(exchage,offset,buf);           				    // Exchange
	aim_writechar(6,offset,buf);		        				    // Command Length
	aim_writegeneric(6,"create",offset,buf);					    // Command
	aim_writeshort(0xffff,offset,buf);      					    // Last Instance
	aim_writechar(1,offset,buf);        						    // Detail
	aim_writeshort(3,offset,buf);          				            // Number of TLVs
	aim_writetlv(0xd3,room_len,room,offset,buf);                    // Room Name
	aim_writetlv(0xd6,8,"us-ascii",offset,buf);		                // Character Set
	aim_writetlv(0xd7,2,"en",offset,buf);			                // Language Encoding

	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_chatnav_room_info(HANDLE hServerConn,unsigned short &seqno, char* chat_cookie, unsigned short exchange, unsigned short instance) 
{ 
	unsigned short offset=0; 
	unsigned short chat_cookie_len = (unsigned short)strlen(chat_cookie); 
	char* buf=(char*)alloca(SNAC_SIZE+7+chat_cookie_len); 
	aim_writesnac(0x0d,0x04,offset,buf); 
	aim_writeshort(exchange,offset,buf);                            // Exchange 
	aim_writechar((unsigned char)chat_cookie_len,offset,buf);       // Chat Cookie Length 
	aim_writegeneric(chat_cookie_len,chat_cookie,offset,buf);       // Chat Cookie 
	aim_writeshort(instance,offset,buf);                            // Last Instance 
	aim_writechar(1,offset,buf);                                    // Detail 
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno); 
}  

int CAimProto::aim_chat_join_room(HANDLE hServerConn,unsigned short &seqno, char* chat_cookie, 
								  unsigned short exchange, unsigned short instance, unsigned short id)
{
	unsigned short offset=0;
	unsigned short cookie_len = (unsigned short)strlen(chat_cookie);
	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE*2+cookie_len+8);
	aim_writesnac(0x01,0x04,offset,buf,id);
	aim_writeshort(0x0e,offset,buf);	        			        // Service request for Chat

	aim_writeshort(0x01,offset,buf);						        // Tag
	aim_writeshort(cookie_len+5,offset,buf);				        // Length
	aim_writeshort(exchange,offset,buf);					        // Value - Exchange
	aim_writechar((unsigned char)cookie_len,offset,buf);	        // Value - Cookie Length
	aim_writegeneric(cookie_len,chat_cookie,offset,buf);	        // Value - Cookie
	aim_writeshort(instance,offset,buf);					        // Value - Instance

	if (!getByte(AIM_KEY_DSSL, 0))
		aim_writetlv(0x8c,0,NULL,offset,buf);                       // Request SSL connection

	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_chat_send_message(HANDLE hServerConn, unsigned short &seqno, char *amsg)
{
	aimString str(amsg);

	const char* charset = str.isUnicode() ? "unicode-2-0" : "us-ascii";
	const unsigned short chrset_len = (unsigned short)strlen(charset);

	const char* msg = str.getBuf();
	const unsigned short msg_len = str.getSize();

	unsigned short tlv_offset=0;
	char* tlv_buf=(char*)alloca(TLV_HEADER_SIZE*4+chrset_len+msg_len+20);
	aim_writetlv(0x04,13,"text/x-aolrtf",tlv_offset,tlv_buf);   // Format
	aim_writetlv(0x02,chrset_len,charset,tlv_offset,tlv_buf);   // Character Set
	aim_writetlv(0x03,2,"en",tlv_offset,tlv_buf);			    // Language Encoding
	aim_writetlv(0x01,msg_len,msg,tlv_offset,tlv_buf);			// Message

	unsigned short offset=0;
	char* buf=(char*)alloca(SNAC_SIZE+8+2+TLV_HEADER_SIZE*3+tlv_offset);
	aim_writesnac(0x0e,0x05,offset,buf);
	aim_writegeneric(8,"\0\0\0\0\0\0\0\0",offset,buf);			// Message Cookie (can be random)
	aim_writeshort(0x03,offset,buf);							// Message Channel (Always 3 for chat)
	aim_writetlv(0x01,0,NULL,offset,buf);						// Public/Whisper flag
	aim_writetlv(0x06,0,NULL,offset,buf);						// Enable Reflection flag
	aim_writetlv(0x05,tlv_offset,tlv_buf,offset,buf);			// Message Information TLV

	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_chat_invite(HANDLE hServerConn,unsigned short &seqno, char* chat_cookie, unsigned short exchange, unsigned short instance, char* sn, char* msg)
{
	unsigned short offset=0;
	unsigned short chat_cookie_len = (unsigned short)strlen(chat_cookie);
	unsigned short sn_len = (unsigned short)strlen(sn);
	unsigned short msg_len = (unsigned short)strlen(msg);
	char* buf=(char*)alloca(SNAC_SIZE+64+chat_cookie_len+sn_len+msg_len);
	aim_writesnac(0x04,0x06,offset,buf);
	aim_writegeneric(8,"\0\0\0\0\0\0\0\0",offset,buf);		    // ICBM Cookie
	aim_writeshort(2,offset,buf);				                // ICBM Channel
	aim_writechar((unsigned char)sn_len,offset,buf);		    // Screen Name Length
	aim_writegeneric(sn_len,sn,offset,buf);					    // Screen Name

	aim_writeshort(0x05,offset,buf);						    // Rendezvous Message Data TLV
	aim_writeshort(49+msg_len+chat_cookie_len,offset,buf);	    // TLV size
	
	aim_writeshort(0,offset,buf);							    // Message Type (0) - Request
	aim_writegeneric(8,"\0\0\0\0\0\0\0\0",offset,buf);		    // ICBM Cookie (same as above)
	aim_writegeneric(16,AIM_CAP_CHAT,offset,buf);			    // Capability

	aim_writetlvshort(0x0a,1,offset,buf);				        // Sequence Number TLV
	aim_writetlv(0x0f,0,NULL,offset,buf);					    // Request Host Caps Check TLV
	aim_writetlv(0x0c,msg_len,msg,offset,buf);				    // Invitation Message TLV

	aim_writeshort(0x2711,offset,buf);							// Capability TLV
	aim_writeshort(chat_cookie_len+5,offset,buf);				// Length
	aim_writeshort(exchange,offset,buf);						// Value - Exchange
	aim_writechar((unsigned char)chat_cookie_len,offset,buf);	// Value - Cookie Length
	aim_writegeneric(chat_cookie_len,chat_cookie,offset,buf);	// Value - Cookie
	aim_writeshort(instance,offset,buf);						// Value - Instance

	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_chat_deny(HANDLE hServerConn,unsigned short &seqno,char* sn,char* icbm_cookie)
{	
	unsigned short offset=0;
	unsigned short sn_length=(unsigned short)strlen(sn);
	char* buf=(char*)alloca(SNAC_SIZE+20+sn_length);
	aim_writesnac(0x04,0x0b,offset,buf);
	aim_writegeneric(8,icbm_cookie,offset,buf);             // ICBM Cookie
	aim_writeshort(2,offset,buf);                           // Channel
	aim_writechar((unsigned char)sn_length,offset,buf);     // Screen Name length
	aim_writegeneric(sn_length,sn,offset,buf);              // Screen Name
	aim_writeshort(3,offset,buf);                           // Error code
	aim_writeshort(2,offset,buf);                           // Error code
	aim_writeshort(1,offset,buf);                           // Error code
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno)==0;
}

int CAimProto::aim_admin_ready(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE*4];
	aim_writesnac(0x01,0x02,offset,buf);
	aim_writefamily(AIM_SERVICE_GENERIC,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	aim_writefamily(AIM_SERVICE_ADMIN,offset,buf);
	aim_writegeneric(4,AIM_TOOL_VERSION,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_admin_format_name(HANDLE hServerConn,unsigned short &seqno, const char* sn)
{
	unsigned short offset=0;
	unsigned short sn_len = (unsigned short)strlen(sn);
	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE+sn_len);
	aim_writesnac(0x07,0x04,offset,buf);
	aim_writetlv(0x01,sn_len,sn,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_admin_change_email(HANDLE hServerConn,unsigned short &seqno, const char* email)
{
	unsigned short offset=0;
	unsigned short email_len = (unsigned short)strlen(email);
	char* buf=(char*)alloca(SNAC_SIZE+TLV_HEADER_SIZE+email_len);
	aim_writesnac(0x07,0x04,offset,buf);
	aim_writetlv(0x11,email_len,email,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_admin_change_password(HANDLE hServerConn,unsigned short &seqno, const char* cur_pw, const char* new_pw)
{
	unsigned short offset=0;
	unsigned short cur_pw_len = (unsigned short)strlen(cur_pw);
	unsigned short new_pw_len = (unsigned short)strlen(new_pw);
	char* buf=(char*)alloca(SNAC_SIZE+2*TLV_HEADER_SIZE+cur_pw_len+new_pw_len);
	aim_writesnac(0x07,0x04,offset,buf);
	aim_writetlv(0x02,new_pw_len,new_pw,offset,buf);
	aim_writetlv(0x12,cur_pw_len,cur_pw,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_admin_request_info(HANDLE hServerConn,unsigned short &seqno, const unsigned short &type)
{
	// types: 0x01 - nickname, 0x11 - email info, 0x13 - registration status
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE];
	aim_writesnac(0x07,0x02,offset,buf);
	aim_writetlv(type,0,NULL,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}

int CAimProto::aim_admin_account_confirm(HANDLE hServerConn,unsigned short &seqno)
{
	unsigned short offset=0;
	char buf[SNAC_SIZE+TLV_HEADER_SIZE];
	aim_writesnac(0x07,0x06,offset,buf);
	return aim_sendflap(hServerConn,0x02,offset,buf,seqno);
}
