#include "Client.h"


Client::Client(string h, int p){

	this->is_running=true;
	this->host=h;
	this->port=p;
	this->rx="";
	this->tx="";
	this->parameters=new BruteForcePassParam();

	cerr << "- CONNECTING TO " << host << ":" << port << endl;

	this->RETRIEVE_MODULE();
	this->RETRIEVE_PARAMS();

	while (this->is_running==true){
		this->RETRIEVE_JOBS(); // recuperation des jobs (nb job=10)
		this->COMPUTE_JOBS(); // lance le calcul
		this->REPORT_RESULTS(); // lance le report des resultats
	}

}

Client::~Client(void){
	this->socket_client->close();
	delete this;
}


void Client::RETRIEVE_MODULE(void){
	this->CONNECT();
	this->MODULE_WAIT();
}


void Client::MODULE_WAIT(){
	cerr << "- MODULE_WAIT()" << endl;
	socket_client->send(static_cast<string>("REQUEST MODULE ")+OS_TYPE);
	ofstream vFile (MODULE_NAME, ios::out|ios::binary|ios::ate); // ouverture du fichier en mode binary
	if (vFile.is_open()) { // verification du flux avant utilisation
		char c;
		int i=0;
		while (socket_client->recv(c)>0){
			vFile << c;
			i++;
		}
		vFile.close(); // on ferme le flux
		cerr << "\t" << i << " bytes recieved" << endl;
	} else {
		cerr << "ERROR : unable to open file : " << MODULE_NAME << endl;
	}
}



void Client::RETRIEVE_PARAMS(void){

	this->CONNECT();
	this->PARAM_WAIT();
	this->PARAM_ACK();
	sleep(1);
	this->QUIT();
}

void Client::PARAM_WAIT(){
	cerr << "- PARAM_WAIT()" << endl;
	socket_client->send(static_cast<string>("REQUEST PARAMS"));
	while (this->rx.substr(0,5)!="PARAM") {
		this->rx=socket_client->recv(this->rx);
		sleep(1);
	}
}

void Client::PARAM_ACK(){
	cerr << "- PARAM_ACK()" <<  endl;
	char** param=explode((char*)this->rx.substr(5).c_str(),'|');

	this->parameters->min_size=atoi(param[0]);
	this->parameters->max_size=atoi(param[1]);
	this->parameters->chars_allowed=param[2];
	this->parameters->chars_allowed_length=this->parameters->chars_allowed.length();
	this->parameters->hash_to_hack=param[4];
	this->salt=param[3];

	cerr << "\tmin_size                =  " << this->parameters->min_size << endl;
	cerr << "\tmax_size                =  " << this->parameters->max_size << endl;
	cerr << "\tchars_allowed           =  " << this->parameters->chars_allowed << endl;
	cerr << "\thash_to_hack            =  " << this->parameters->hash_to_hack << endl;
	cerr << "\tsalt                    =  " << this->salt << endl;
	cerr << "\tmodule_name             =  " << MODULE_NAME << endl;

	socket_client->send(static_cast<string>("PARAM_ACK"));
}




void Client::RETRIEVE_JOBS(void){
	this->CONNECT();
	this->JOBS_WAIT();
	this->JOBS_ACK();
	sleep(1);
	this->QUIT();
}

void Client::JOBS_WAIT(){
	cerr << "- JOBS_WAIT()" << endl;
	socket_client->send(static_cast<string>("REQUEST JOBS"));
	this->rx=socket_client->recv(this->rx);
	while (this->rx.substr(0,4)!="JOBS") {
		this->rx=socket_client->recv(this->rx);
		sleep(1);
	}
}



void Client::JOBS_ACK(){
	cerr << "- JOBS_ACK()" << endl;
	if (this->rx.length()>5){
		char** jobs=explode((char*)this->rx.substr(5).c_str(),'|');
		for (int idx=0;jobs[idx]!=NULL;idx++){
			string tmp=jobs[idx];
			BruteForcePass* bfp=new BruteForcePass(parameters);
			bfp->job=new Job(tmp);
			bfp->loadSharedModule();
			bfp->build_hash->salt=this->salt;
			tab_bruteforce.push_front(bfp);
		}
	}
	socket_client->send(static_cast<string>("JOBS_ACK"));
	this->tx="";
	this->rx="";
}



void Client::COMPUTE_JOBS(void){
	list<BruteForcePass*>::reverse_iterator j = tab_bruteforce.rbegin();
	while(j != tab_bruteforce.rend()){
		BruteForcePass* bfp=*j;
		bfp->initThread();
		cerr << "- COMPUTE_JOBS(" << bfp->job->begin << ")" << endl;
		j++;
	}

	j = tab_bruteforce.rbegin();
	while(j != tab_bruteforce.rend()){
		BruteForcePass* bfp=*j;
		bfp->waitThread();
		bfp->job->done=true;
		if (bfp->found==true) {
			bfp->job->found=bfp->result;
			this->is_running=false;
			break;
		} else {
			bfp->job->found=" ";
		}
		j++;
	}
}



void Client::REPORT_RESULTS(void){
	cerr << "- REPORT_RESULTS()" << endl;
	socket_client=new ClientSocket (host, port );
	this->CONNECT();
	this->tx="REPORT RESULTS|";
	list<BruteForcePass*>::reverse_iterator j = tab_bruteforce.rbegin();
	while(j != tab_bruteforce.rend()){
		BruteForcePass* bfp=*j;
		if (bfp->job->done==true){
			this->tx+=bfp->job->begin+"|"+bfp->job->found+"|";
		}
		
		j++;
	}
	
	socket_client->send(this->tx);
	sleep(1);
	this->tab_bruteforce.clear();
	this->QUIT();
	
}


void Client::CONNECT(){
	cerr << "- CONNECT()" << endl;
	try {
		socket_client=new ClientSocket (host, port );
		socket_client->send(static_cast<string>("CONNECT"));
		this->rx=socket_client->recv(this->rx); // attente de CONNECT_ACK
	} catch (SocketException &e) {
		cerr << "ERROR(" << errno << ") in socket : " << strerror(errno) << endl;
		exit(errno);
	}

	this->tx="";
	this->rx="";
}


void Client::QUIT(){
	cerr << "- QUIT()" << endl << endl;
    	socket_client->send(static_cast<string>("QUIT"));
	this->rx=socket_client->recv(this->rx);
	this->socket_client->close();
}




char** Client::explode(char* str,char separator){
	int  nbstr = 1;
	int  from = 0;
	char** res = (char **) malloc(sizeof (char *));
	int len = strlen(str);
	for (int i = 0; i <= len; ++i){
		if ((i == len) || (str[i] == separator)){
			res = (char **) realloc(res, ++nbstr * sizeof (char *));
			res[nbstr - 2] = (char *) malloc((i - from + 1) * sizeof (char));
			for (int j = 0; j < (i - from); ++j)
				res[nbstr - 2][j] = str[j + from];
			res[nbstr - 2][i - from] = '\0';
			from = i + 1;
			++i;
		}
	}
	res[nbstr - 1] =  NULL;
	return res;
}



void Client::GET(string str){
	try {
		socket_client=new ClientSocket (host, port );
	} catch (SocketException &e) {
		cerr << "ERROR(" << errno << ") in socket : " << strerror(errno) << endl;
		exit(errno);
	}
	ostringstream request;
	request << "GET " << str << " HTTP/1.1" << endl;
	request << "Host: " << host << ":" << port << endl;
	request << "User-Agent: Mozilla/5.0" << endl;
	request << "Accept: text/html,application/xhtml+xml,application/xml" << endl << endl;
	cerr << request.str() << endl;
	socket_client->send(request.str());
	this->rx=socket_client->recv(this->rx);
	this->socket_client->close();
}


void Client::POST(string str){
	try {
		socket_client=new ClientSocket (host, port );
	} catch (SocketException &e) {
		cerr << "ERROR(" << errno << ") in socket : " << strerror(errno) << endl;
		exit(errno);
	}
	ostringstream request;
	request << "POST " << str << " HTTP/1.1" << endl;
	request << "Host: " << host << ":" << port << endl;
	request << "User-Agent: Mozilla/5.0" << endl;
	request << "Accept: text/html,application/xhtml+xml,application/xml" << endl << endl;
	cerr << request.str() << endl;
	socket_client->send(request.str());
	this->rx=socket_client->recv(this->rx);
	this->socket_client->close();
}






int main(int argc, char* argv[]){
	Client* client=new Client("192.168.0.10",1337);

}


