/***************************************************************************
 *   Copyright (C) 2004 by yunfan                                          *
 *   yunfan_zg@163.com                                                     *
 *                                                                         *
 *   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, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "evaimreceive.h"
#include "evadefines.h"
#include "evautil.h"
#include <string.h>
#ifdef _WIN32
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif


ReceiveIMPacket::ReceiveIMPacket(unsigned char *buf, const int len)
	: InPacket(buf, len),
	bodyOffset(0)
{
}   

ReceiveIMPacket::ReceiveIMPacket(const ReceiveIMPacket &rhs)
	: InPacket(rhs)
{
	memcpy(replyKey, rhs.getReplyKey(), REPLY_KEY_LENGTH);
	sender = rhs.getSender();
	receiver = rhs.getReceiver();
	sequence = rhs.getSequence();
	senderIP = rhs.getSenderIP();
	senderPort = rhs.getSenderPort();
	type = rhs.getIMType();
	
	bodyOffset = rhs.getLength() - rhs.getBodyLength();
}

std::string ReceiveIMPacket::convertToShow(const std::string &src)
{
	std::string converted = "";
	for(uint i=0; i<src.length(); i++){
		if(src[i]==0x14){
			converted+=EvaUtil::smileyToText(src[++i]);
			converted+=' ';
		}else if(src[i]==0x15){
				int t=0;
				char *tmp = new char[src.length()+1];
				strcpy(tmp, src.c_str());
				converted+=EvaUtil::customSmileyToText(tmp + i, &t);
				i+=(t-1); // get rid of 0x15
				delete tmp;
			} else
				converted+=src[i];
	}
	return converted;
}

ReceiveIMPacket &ReceiveIMPacket::operator=(const ReceiveIMPacket &rhs)
{
	*((InPacket *)this) = (InPacket)rhs;
	memcpy(replyKey, rhs.getReplyKey(), REPLY_KEY_LENGTH);
	sender = rhs.getSender();
	receiver = rhs.getReceiver();
	sequence = rhs.getSequence();
	senderIP = rhs.getSenderIP();
	senderPort = rhs.getSenderPort();
	type = rhs.getIMType();
	
	bodyOffset = rhs.getLength() - rhs.getBodyLength();
        return *this;
}

void ReceiveIMPacket::parseBody()
{
	if(bodyLength < 16){
		printf("message too short, just chuck it\n");  
		bodyOffset = -1; // means error occurred
		return;
	}
	memcpy(replyKey, decryptedBuf, REPLY_KEY_LENGTH);
	bodyOffset = readHeader(decryptedBuf);
}

int ReceiveIMPacket::readHeader(const unsigned char *buf)
{
	int pos=0;	
	int tmp4;
	memcpy(&tmp4, buf, 4);
	sender = ntohl(tmp4);
	pos+=4;
	
	memcpy(&tmp4, buf+pos, 4);
	receiver = ntohl(tmp4);
	pos+=4;
		
	memcpy(&tmp4, buf+pos, 4);
	intSequence = ntohl(tmp4);
	pos+=4;

	memcpy(&tmp4, buf+pos, 4);
	senderIP = ntohl(tmp4);
	pos+=4;
		
	short tmp2;
	memcpy(&tmp2, buf+pos, 2);
	senderPort = ntohs(tmp2);
	pos+=2;
		
	memcpy(&tmp2, buf+pos, 2);
	type = ntohs(tmp2);
	pos+=2;	
	return pos;
}

/* =========================================================== */

NormalIMBase::NormalIMBase(const unsigned char *buf, const int len)
{
	bodyBuf = (unsigned char *)malloc(len * sizeof(unsigned char));
	bodyLength = len;
	memcpy(bodyBuf, buf, len);
}

NormalIMBase::NormalIMBase(const NormalIMBase &rhs)
{
	*this = rhs;
}

NormalIMBase::~NormalIMBase()
{
	free(bodyBuf);
}

void NormalIMBase::setNormalIMBase(const NormalIMBase *base)
{
	*this = *base;	
}

void NormalIMBase::parseData()
{
	int len = readHeader(bodyBuf);
	parseContents(bodyBuf + len, bodyLength - len);
}

NormalIMBase &NormalIMBase::operator=(const NormalIMBase &rhs)
{
	bodyLength = rhs.getBodyLength();
	bodyBuf = (unsigned char *)malloc(bodyLength * sizeof(unsigned char));
	memcpy(bodyBuf, rhs.getBodyData(), bodyLength);
	
	senderVersion = rhs.getSenderVersion();
	sender = rhs.getSender();
	receiver = rhs.getReceiver();
	memcpy(fileSessionKey, rhs.getBuddyFileSessionKey(), 16);
	type = rhs.getNormalIMType();
	sequence = rhs.getSequence();
	sendTime = rhs.getSendTime();
	unknown1 = rhs.getUnknown1();
	senderFace = rhs.getSenderFace();
        return *this;
}

void NormalIMBase::parseContents(const unsigned char *, const int )
{
	//fprintf(stderr, "In NormalIMBase: buf[0]=0x%2x, len=%d\n",buf[0], len); 
}

int NormalIMBase::readHeader(const unsigned char *buf)
{
	int pos=0;
	short tmp2;
	memcpy(&tmp2, buf, 2);	
	senderVersion = ntohs(tmp2);
	pos+=2;
	
	int tmp4;
	memcpy(&tmp4, buf+pos, 4);
	sender = ntohl(tmp4);
	pos+=4;
	memcpy(&tmp4, buf+pos, 4);
	receiver = ntohl(tmp4);
	pos+=4;
	
	memcpy(fileSessionKey, buf+pos, 16);
	pos+=16;
	
	memcpy(&tmp2, buf+pos, 2);
	type = ntohs(tmp2);
	pos+=2;
	
	memcpy(&tmp2, buf+pos, 2);
	sequence = ntohs(tmp2);
	pos+=2;
	
	memcpy(&tmp4, buf+pos, 4);
	//sendTime = ntohl(tmpTime) * 1000L;
	sendTime = ntohl(tmp4);
	pos+=4;
	
	memcpy(&tmp2, buf+pos, 2);
	senderFace = ntohs(tmp2);	
	pos+=2;
	
	return pos;
}

/* =========================================================== */

ReceivedNormalIM::ReceivedNormalIM(const unsigned char *buf, const int len)
	: NormalIMBase(buf, len)
{
}

ReceivedNormalIM::ReceivedNormalIM(const ReceivedNormalIM &rhs)
	: NormalIMBase(rhs)
{
	memcpy(unknown2, rhs.getUnknown2(), 3);
	memcpy(unknown3, rhs.getUnknown3(), 4);
	replyType = rhs.getReplyType();
	message = rhs.getMessage();
	mHasFontAttribute = rhs.hasFontAttribute();
	encoding = rhs.getEncoding();
	red = rhs.getRed();
	green = rhs.getGreen();
	blue = rhs.getBlue();
	fontSize = rhs.getFontSize();
	fontName = rhs.getFontName();
	bold = rhs.isBold();
	italic = rhs.isItalic();
	underline = rhs.isUnderline();
}

const bool ReceivedNormalIM::isNormalReply() const
{
	return replyType == QQ_IM_NORMAL_REPLY;
}

ReceivedNormalIM &ReceivedNormalIM::operator=(const ReceivedNormalIM &rhs)
{
	*((NormalIMBase *)this) = (NormalIMBase)rhs;
	memcpy(unknown2, rhs.getUnknown2(), 3);
	memcpy(unknown3, rhs.getUnknown3(), 4);
	replyType = rhs.getReplyType();
	message = rhs.getMessage();
	mHasFontAttribute = rhs.hasFontAttribute();
	encoding = rhs.getEncoding();
	red = rhs.getRed();
	green = rhs.getGreen();
	blue = rhs.getBlue();
	fontSize = rhs.getFontSize();
	fontName = rhs.getFontName();
	bold = rhs.isBold();
	italic = rhs.isItalic();
	underline = rhs.isUnderline();
        return *this;
}

void ReceivedNormalIM::parseContents(const unsigned char *buf, const int len)
{
	int pos=0;
	memcpy(unknown2, buf, 3);
        pos+=3;
	        
        mHasFontAttribute = (buf[pos++] != 0);
	memcpy(unknown3, buf+pos, 4);
	pos+=4;

        replyType = buf[pos++];
			
	int i=0;
	while( (buf[pos++]) != 0x00){
		i++;
	}
#ifdef WIN32
	char* str; str=(char*)_alloca(i+2);
#else
	char str[i+2];
#endif
	memcpy(str, buf+(pos-i-1), i+1);
	str[i+1]= 0x00;
	message = ReceiveIMPacket::convertToShow(str);
	
        if(mHasFontAttribute) {
		if(pos<len) {
#ifdef WIN32
			char* fa; fa=(char*)_alloca(len-pos);
#else
			char fa[len-pos];
#endif
			memcpy(fa, buf+pos, len-pos);
			fontSize = fa[0] & 0x1F;
			bold = ((fa[0] & 0x20) != 0);
			italic = ((fa[0] & 0x40) != 0);
			underline = ((fa[0] & 0x80) != 0);
			red = fa[1] & 0xFF;
			green = fa[2] & 0xFF;
			blue = fa[3] & 0xFF;
			short tmp2;
			memcpy(&tmp2, fa+5, 2);
			encoding = ntohs(tmp2);
#ifdef WIN32
			char* fn; fn=(char*)_alloca(len-pos-7);
#else
			char fn[len-pos-7];
#endif
			memcpy(fn, fa+7, len-pos-7-1);
			fn[len-pos-7-1]=0x00;
			//fn[len-pos-7]=0x00;
			fontName.assign(fn);
		} else
			mHasFontAttribute = false;
        }
}

/* =========================================================== */

ReceivedSystemIM::ReceivedSystemIM(const unsigned char *buf, const int len)
{
	parseData(buf, len);
}

ReceivedSystemIM::ReceivedSystemIM( const ReceivedSystemIM &rhs)
{
	*this = rhs;
}

ReceivedSystemIM &ReceivedSystemIM::operator=(const ReceivedSystemIM &rhs)
{
	systemIMType = rhs.getSystemIMType();
	message = rhs.getMessage();
        return *this;
}

void ReceivedSystemIM::parseData(const unsigned char *buf, const int len)
{
	int pos = 0;
	systemIMType = buf[pos++];
	
	char msgLen = buf[pos++];
#ifdef WIN32
	char* msg; msg=(char*)_alloca(msgLen+1);
#else
	char msg[msgLen+1];
#endif
	memcpy(msg, buf+pos, msgLen);
	msg[msgLen]=0x00;
	message.assign(msg);
}

/* =========================================================== */

ReceiveIMReplyPacket::ReceiveIMReplyPacket(const char *key) 
	: OutPacket(QQ_CMD_RECV_IM, false)
{
        memcpy(replyKey, key, 16);
}

ReceiveIMReplyPacket::ReceiveIMReplyPacket(const ReceiveIMReplyPacket &rhs)
	: OutPacket(rhs)
{
	memcpy(replyKey, rhs.getReplyKey(), REPLY_KEY_LENGTH);
}

ReceiveIMReplyPacket &ReceiveIMReplyPacket::operator=(const ReceiveIMReplyPacket &rhs)
{
	*((OutPacket *)this) = (OutPacket)rhs;
	memcpy(replyKey, rhs.getReplyKey(), REPLY_KEY_LENGTH);
        return *this;
}

int ReceiveIMReplyPacket::putBody(unsigned char *buf) {

        memcpy(buf, replyKey, REPLY_KEY_LENGTH);
	return REPLY_KEY_LENGTH;
}

/* =========================================================== */

ReceivedQunIM::ReceivedQunIM(const short src,  const unsigned char * buf, const int len )
	: source(src)
{
	parseData(buf, len);
}

ReceivedQunIM::ReceivedQunIM( const ReceivedQunIM & rhs )
{
	(*this) = rhs;
}

ReceivedQunIM & ReceivedQunIM::operator =( const ReceivedQunIM & rhs )
{
	source = rhs.getSource(); // this is to mention,  permenent, temporary qun 
	externalID = rhs.getExtID();
	qunID = rhs.getQunID();
	type = rhs.getType();
	sender = rhs.getSenderQQ();
	unknown1 = rhs.getUnknown1();
	sequence = rhs.getSequence();
	sentTime = rhs.getSentTime();
	versionID = rhs.getVersionID();
	message = rhs.getMessage();
	
	mHasFontAttribute = rhs.hasFontAttribute();
	encoding = rhs.getEncoding();
	red = rhs.getRed();
	green = rhs.getGreen();
	blue = rhs.getBlue();
	fontSize = rhs.getFontSize();
	fontName = rhs.getFontName();
	bold = rhs.isBold();
	italic = rhs.isItalic();
	underline = rhs.isUnderline();
	return *this;
}

void ReceivedQunIM::parseData( const unsigned char * buf, const int len )
{
	int pos=0;
	
	int tmp4;
	memcpy(&tmp4, buf, 4);
	externalID = ntohl(tmp4); // the external Qun ID
	pos+=4;
	
	type = buf[pos++];  // type of the Qun

	if(source == QQ_RECV_IM_TEMP_QUN_IM){ // internal ID of temporary Qun
		memcpy(&tmp4, buf+pos, 4);
		qunID = ntohl(tmp4);
		pos+=4;
	}
	
	memcpy(&tmp4, buf+pos, 4);
	sender = ntohl(tmp4);       // the sender
	pos+=4;
	
	short tmp2;
	memcpy(&unknown1, buf+pos, 2);
	pos+=2;

	memcpy(&tmp2, buf+pos, 2);
	sequence = ntohs(tmp2);     // message sequence number
	pos+=2;
	
	memcpy(&tmp4, buf+pos, 4);
	sentTime = ntohl(tmp4);    // the sending time
	pos+=4;
	
	memcpy(&tmp4, buf+pos, 4);
	versionID = ntohl(tmp4);   // version ID is to tell if something like memebers, or rules of this Qun were changed
        pos+=4;

        pos+=2; // ignore 2 bytes;  Luma QQ says it's the length for the following data.

	// 10 unknown bytes
	if(source != QQ_RECV_IM_UNKNOWN_QUN_IM) {
		pos+=10;
	}
	
	int i=0;
	while( (buf[pos++]) != 0x00){
		i++;
	}
#ifdef WIN32
	char* str; str=(char*)_alloca(i+2);
#else
	char str[i+2];
#endif
	memcpy(str, buf+(pos-i-1), i+1);
	str[i+1]= 0x00;
	message = ReceiveIMPacket::convertToShow(str);
	
	mHasFontAttribute = (len > pos);
        if(mHasFontAttribute) {
#ifdef WIN32
		char* fa; fa=(char*)_alloca(len-pos);
#else
		char fa[len-pos];
#endif
		memcpy(fa, buf+pos, len-pos);
		fontSize = fa[0] & 0x1F;
		bold = ((fa[0] & 0x20) != 0);
		italic = ((fa[0] & 0x40) != 0);
		underline = ((fa[0] & 0x80) != 0);
		red = fa[1] & 0xFF;
		green = fa[2] & 0xFF;
		blue = fa[3] & 0xFF;
		short tmp2;
		memcpy(&tmp2, fa+5, 2);
		encoding = ntohs(tmp2);
#ifdef WIN32
		char* fn; fn=(char*)_alloca(len-pos-7);
#else
		char fn[len-pos-7];
#endif
		memcpy(fn, fa+7, len-pos-7-1);
		fn[len-pos-7-1]=0x00;
		//fn[len-pos-7]=0x00;
		fontName.assign(fn);
        } 
}



/* =========================================================== */

ReceivedQunIMJoinRequest::ReceivedQunIMJoinRequest( const unsigned char * buf, const int len)
{
	int pos=0;
	int tmp4;
	memcpy(&tmp4, buf, 4);
	externalID = ntohl(tmp4);
	pos+=4;
	
	type = buf[pos++];

	memcpy(&tmp4, buf + pos, 4);
	sender = ntohl(tmp4);
	pos+=4;
	
	if(len == pos) {
		message = "";
		return;
	}
	unsigned length = (unsigned int)buf[pos++];
	char *msg = (char *)malloc((length+1) * sizeof(char));
	memcpy(msg, buf + pos, length);
	msg[length] = 0x00;
	message.assign(msg);
	free(msg);
}

ReceivedQunIMJoinRequest::ReceivedQunIMJoinRequest( const ReceivedQunIMJoinRequest & rhs )
{
	*this = rhs;
}

ReceivedQunIMJoinRequest & ReceivedQunIMJoinRequest::operator =( const ReceivedQunIMJoinRequest & rhs )
{
	externalID = rhs.getExtID();
	sender = rhs.getSender();
	type = rhs.getType();
	message = rhs.getMessage();
	return *this;
}



/* =========================================================== */

SignatureChangedPacket::SignatureChangedPacket( const unsigned char * buf, const int len )
{
	parseData(buf, len);
}

SignatureChangedPacket::SignatureChangedPacket( const SignatureChangedPacket & rhs )
{
	*this = rhs;
}

SignatureChangedPacket & SignatureChangedPacket::operator =( const SignatureChangedPacket & rhs )
{
	qq = rhs.getQQ();
	time = rhs.getTime();
	signature = rhs.getSignature();
	return *this;
}

void SignatureChangedPacket::parseData( const unsigned char * buf, const int len )
{
	int pos=0;
	int tmp4;
	memcpy(&tmp4, buf, 4);    // your buddy qq number
	qq = ntohl(tmp4); pos+=4;
	
	memcpy(&tmp4, buf+pos, 4);  // the time he/she changed his/her signature
	time = ntohl(tmp4);  pos+=4;
	
	tmp4 = buf[pos++]; // the length of signature
	
	char *str = new char[tmp4+1];
	memcpy(str, buf+pos, tmp4);   // we get the new signature
	str[tmp4] = 0x00;
	signature.assign(str);
}
