/***************************************************************************
 *   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 "evalogin.h"
#ifdef _WIN32
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
#include <string.h>
#include <stdlib.h>
#include "evacrypt.h"
#include "evautil.h"
 
LoginPacket::LoginPacket(unsigned char loginMode)
	: OutPacket(QQ_CMD_LOGIN, true),
	mLoginMode(loginMode)
{
}

LoginPacket::LoginPacket(const LoginPacket &rhs)
	:OutPacket(rhs)
{
	mLoginMode = rhs.getLoginMode();
}

LoginPacket::~LoginPacket()
{
}

LoginPacket &LoginPacket::operator=(const LoginPacket &rhs)
{
	*((OutPacket *)this) = (OutPacket)rhs;
	mLoginMode = rhs.getLoginMode();
        return *this;
}

int LoginPacket::putBody(unsigned char *buf) 
{
	memcpy(buf, iniKey, QQ_KEY_LENGTH);

	int pos = 16;
	
	if(!getPasswordKey()){
		fprintf(stderr, "LoginPacket->putBody: no password key avalible\n");
		return 0;
	}
	
	unsigned char *login = new unsigned char[QQ_LOGIN_DATA_LENGTH];
	EvaCrypt::encrypt(NULL, 0, getPasswordKey(), login, &pos);
	
	memcpy(login+pos, login_16_51, 36);
	pos+=36;
	
	login[pos++] = mLoginMode;
	
	memcpy(login+pos, login_53_68, 16);
	pos+=16;
	
	login[pos++] = getLoginTokenLength();
	memcpy(login+pos, getLoginToken(), getLoginTokenLength());
	pos += getLoginTokenLength();

	memcpy(login+pos, login_94_193, 100);
	pos+= 100;
	
	memset(login+pos, 0, QQ_LOGIN_DATA_LENGTH - pos);
	
	unsigned char *encrypted = new unsigned char[MAX_PACKET_SIZE];
	int enLen=200;
	EvaCrypt::encrypt(login, QQ_LOGIN_DATA_LENGTH, iniKey, encrypted, &enLen);
	
	memcpy(buf+QQ_KEY_LENGTH, encrypted, enLen);
	
	delete login;
	delete encrypted;
	return QQ_KEY_LENGTH + enLen;
}

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

LoginReplyPacket::LoginReplyPacket(unsigned char *buf, int len)
	: InPacket(buf, len)
{ 
}   

LoginReplyPacket::LoginReplyPacket(const LoginReplyPacket &rhs)
	: InPacket(rhs)
{ 	
	replyCode = rhs.getReplyCode();
	redirectedIP = rhs.getRedirectedIP();
	redirectedPort = rhs.getRedirectedPort();
	
	qqNum = rhs.getQQ();
	IP = rhs.getMyIP();
	port = rhs.getMyPort();
	loginTime = rhs.getLoginTime();
	serverIP = rhs.getServerIP();
	serverPort = rhs.getServerPort();
	lastLoginIP = rhs.getLastLoginIP();
	lastLoginTime = rhs.getLastLoginTime();
	replyMessage = rhs.getReplyMessage();
} 

LoginReplyPacket &LoginReplyPacket::operator=(const LoginReplyPacket &rhs)
{
	*((InPacket *)this) = (InPacket)rhs;
	replyCode = rhs.getReplyCode();
	redirectedIP = rhs.getRedirectedIP();
	redirectedPort = rhs.getRedirectedPort();
	
	qqNum = rhs.getQQ();
	IP = rhs.getMyIP();
	port = rhs.getMyPort();
	loginTime = rhs.getLoginTime();
	serverIP = rhs.getServerIP();
	serverPort = rhs.getServerPort();
	lastLoginIP = rhs.getLastLoginIP();
	lastLoginTime = rhs.getLastLoginTime();
	replyMessage = rhs.getReplyMessage();
        return *this;
}

void LoginReplyPacket::parseBody() 
{
	replyCode = decryptedBuf[0];
	switch(replyCode) {     
	case QQ_LOGIN_REPLY_OK:
		{
			short tmp2;
			unsigned int tmp4;
			setSessionKey(decryptedBuf+1);
			
			// now we can set the file session key
			char *fsbuf = new char[QQ_KEY_LENGTH + 4];
			tmp4 = htonl(getQQ());
			memcpy(fsbuf, &tmp4, 4);
			memcpy(fsbuf+4, getSessionKey(), QQ_KEY_LENGTH);
			setFileSessionKey((unsigned char *)EvaUtil::doMd5(fsbuf, 4 + QQ_KEY_LENGTH));
			delete fsbuf;
			
			memcpy(&tmp4, decryptedBuf + QQ_KEY_LENGTH + 1, 4);
			qqNum = ntohl(tmp4);
			// 021-024  user IP
			memcpy(&tmp4, decryptedBuf + 21, 4); 
			IP = ntohl(tmp4);
			// 025-026 user port
			memcpy(&tmp2, decryptedBuf+25, 2);
			port = ntohs(tmp2);
			// 027-030 server IP
			memcpy(&tmp4, decryptedBuf + 27, 4);
			serverIP = ntohl(tmp4);
			// 031-032 server port
			memcpy(&tmp2, decryptedBuf+31, 2);
			serverPort = ntohs(tmp2);
			// 033-036  login time    
			memcpy(&tmp4, decryptedBuf+33, 4);
			loginTime = ntohl(tmp4);
			
			// 037-038 2 unknown bytes
			
			// 039-062 file share token used in Qun disk share file access
			setFileShareToken(decryptedBuf+39);
			
			// 79-110  client key for QQ Home
			setClientKey(decryptedBuf+79, 32); // always 32 bytes long
			
			// 123-126  user IP when last login
			memcpy(&tmp4, decryptedBuf+123, 4);
			lastLoginIP = ntohl(lastLoginIP);
			// 127-130  last login time             
			memcpy(&tmp4, decryptedBuf+127, 4);
			lastLoginTime = ntohl(tmp4);
			// 131-138 unknow bytes
			break;
		}
	case QQ_LOGIN_REPLY_PWD_ERROR:
		// message from server when password is wrong. the messege is encoded by "GB18030"
		{
			char *tmpMsg = new char[bodyLength];
			memcpy(tmpMsg, decryptedBuf+1, bodyLength-1);
			tmpMsg[bodyLength-1]=0; 
			replyMessage.assign(tmpMsg);
			delete tmpMsg;
			break;
		}
	case QQ_LOGIN_REPLY_REDIRECT:
		// redirect user to another server for balance reason
		{
			short tmp2;
			int tmp4;
			// 001-004 user qq number
			memcpy(&tmp4, decryptedBuf + 1, 4);
			qqNum = ntohl(tmp4);
			// 005-008  the new server IP address
			memcpy(&tmp4, decryptedBuf+5, 4);
			redirectedIP = ntohl(tmp4);
			// 009-010  the new server's Port number              
			memcpy(&tmp2, decryptedBuf+9, 2);
			redirectedPort = ntohs(tmp2);
			break;
		}
	default:
		replyCode = QQ_LOGIN_REPLY_MISC_ERROR;
		{
			printf("login unknown reply(first byte is reply code)\n");
			for(int i=0; i<bodyLength; i++){
				if(!(i%8)) printf("\n%d: ",i);
				char t = decryptedBuf[i];
				printf("%2x ", (uint8_t)t);
			}
			printf("\n");
		}
		break;
	}
}

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

LogoutPacket::LogoutPacket()
	: OutPacket(QQ_CMD_LOGOUT, false)
{
 	sequence = 0xFFFF;
}

LogoutPacket::LogoutPacket(const LogoutPacket &rhs)
	: OutPacket(rhs)
{
}

LogoutPacket &LogoutPacket::operator=(const LogoutPacket &rhs)
{
	*((OutPacket *)this) = (OutPacket)rhs;
        return *this;
}

int LogoutPacket::putBody(unsigned char *buf) 
{    
	unsigned char *pwKey = Packet::getPasswordKey();
	if(pwKey==NULL){
		fprintf(stderr, "LogoutPacket->putBody: no password key avalible\n");
		return 0;
	}
	memcpy(buf, pwKey, QQ_KEY_LENGTH);
	return QQ_KEY_LENGTH;
}

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

KeepAlivePacket::KeepAlivePacket()
	: OutPacket(QQ_CMD_KEEP_ALIVE, true) 
{
}

KeepAlivePacket::KeepAlivePacket(const KeepAlivePacket &rhs)
	: OutPacket(rhs) 
{
}

KeepAlivePacket &KeepAlivePacket::operator=(const KeepAlivePacket &rhs )
{
	*((OutPacket *)this) = (OutPacket)rhs;
        return *this;
}

int KeepAlivePacket::putBody(unsigned char *buf) 
{
        int qq = htonl(getQQ());
	memcpy(buf, &qq, 4);
        return 4; 
}

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

int KeepAliveReplyPacket::onlineUsers=0;

KeepAliveReplyPacket::KeepAliveReplyPacket(unsigned char *buf, int len) 
	: InPacket(buf, len) 
{
}   

KeepAliveReplyPacket::KeepAliveReplyPacket(const KeepAliveReplyPacket &rhs)
	: InPacket(rhs) 
{
	onlineUsers = rhs.numOnlineUsers();
	myIP = rhs.getMyIP();
	myPort = rhs.getMyPort();
}

KeepAliveReplyPacket &KeepAliveReplyPacket::operator=(const KeepAliveReplyPacket &rhs)
{
	*((InPacket *)this) = (InPacket)rhs;
	onlineUsers = rhs.numOnlineUsers();
	myIP = rhs.getMyIP();
	myPort = rhs.getMyPort();
        return *this;	
}
 
void KeepAliveReplyPacket::parseBody()
{
	int sec = 0, offset = 0;
	char section[10][256];
	for(int i=0; i<bodyLength; i++){
		if(sec>5) printf("Keep Alive -- number of fields greater than 5\n");
		if(decryptedBuf[i]==KA_DIVIDER){
			section[sec][offset] = 0;
			sec++;
			offset=0;
			continue;
		}
		section[sec][offset++]=decryptedBuf[i]; 
	}
	section[sec][offset]=0;
	
	if(sec != 5){
		printf("Keep Alive -- number of fields wrong!\n");
		return;
	}

	onlineUsers = atoi(section[2]);
	if(onlineUsers == 0 ){
		printf("KeepAliveReplyPacket -- parse online users error!\n");
		return;
	}
       
	myIP.assign( section[3]);
	myPort = atoi(section[4]);
}


