/* ===========================================================
 * PROGRAM: Unknown Letters Solver
 * VERSION: 1.04
 * -----------------------------------------------------------
 * DESCRIPTION: 
 * Tekstineje (naud. ASCII ar UTF-8 koduote).csv duomenu baze-
 * je, kurioje surasyti vardai ir pavardes,  programa  suranda
 * irasus su trukstamais simboliais  (jie turi  buti  pazymeti
 * '?'), ir juos bando nuspeti.Pradzioje speja pagal salia nu-
 * rodyta e-mail adresa, o tada bando ieskoti  vardu/pavardziu
 * su spejamais simboliais internete (naudoja "Bing") ir kai
 * spejimo budu suranda daugiausia rezultatu, tai  tas raides,
 * su kuriomis buvo suformuluota daugiausia  rezultatu  davusi
 * uzklausa, suraso i rezultatu faila. Jei niekaip  nepavyksta
 * nieko rasti per "Bing",tai raides nuspejamos atsitiktinu-
 * mo budu. .CSV duomenu bazes struktura:
 * +-------------------------------------------------------+
 * |"Vard?s";"Pavarde";"email@domain.com";"kiti";"duomenys"|
 * |"Vard?s";"Pav?rde";"";"kiti";"duomenys2"               |
 * |"Vardas";"Pa??rde";"email@domain.com";"kiti";"duomenys"|
 * `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`
 * ,~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,   
 * |"Paskutinis";"Irasas";"invalid";"kiti";"duomenys"      |
 * +-------------------------------------------------------+
 *
 * .CSV DUOMENU BAZEJE NETURETU BUTI KLAIDU (PROGRAMA JAS TIK-
 * RINA, BET NEPAKANKAMAI).ESANT KLAIDAI PASEKMES NEPROGNOZUO-
 * JAMOS.
 *
 *       ----> USE THIS PROGRAM AT YOUR OWN RISK! <----
 *
 * -----------------------------------------------------------
 * REVISION:
 *  1.0  - Initial version
 *  1.01 - Some bugfixes:
 *         1. Invalid references then solving UTF-8 data
 *		   2. Improved error checking (but not completed yet!)
 *		   3. Improved communication with "Bing" algorithm
 *		   4. Added ability to enter how much questions try to
 *		      solve via "Bing". *
 *  1.02 - Some bugfixes:
 *		   1. Fixed some major bugs in unknown letters solu-
 *            tion by email address algorithm.
 *		   2. Improved search via "Bing". 
 *  1.03 - Improved accuracy of solution
 *  1.04 - 1. Changed search engine. Now this program uses 
 *            "Bing" because "Bing" now don't allow automated 
 *            queues.
 *         2. Added ability to cancel conversion
 *         3. Now record solved by random are marked as RANDOM
 *            in destination file.
 * 
 *  (P.S.: Sorry for my poor English.)
 * -----------------------------------------------------------
 * (c)COPYLEFT 2010, Ąžuolas - Faustas BAGDONAS
 * Programa leidziama visiems modifikuoti, tobulinti, taikyti
 * saviems tikslams. Tai tera Quick & Dirty  versija,  skirta
 * mano asmeninems poreikiams.
 * =========================================================== */
 
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <winsock2.h>

#define FALSE	0
#define TRUE	1	
typedef int		BOOL;

/* Global varriables */
BOOL create_log;
FILE *flog;
struct sockaddr_in s_in;

/* Function prototypes */
void info(FILE *out);
void logging (char *logstr);
BOOL ns_by_mail(char *name, char *sirname, char *email);
void toasc(char *dst, char *src);
BOOL solveq(char *qdata, char *search);
unsigned char findq(char *str);
BOOL ns_by_bing(char *name, char *sirname, SOCKET sBing, float percent);
void ns_by_random(char *name, char *sirname);
BOOL store(FILE *fout, char *name, char *sirname, char *email);
SOCKET prep_winsock();
void conv4httpquery(char *dst, char *src);
void removedocs(char *str);
void closeall(FILE *fin, FILE *fout, SOCKET *socket);
char *get_answer(unsigned int *received, SOCKET *sck);
BOOL reconnect(SOCKET *sck);

/* Function delegations */
int main (void){
	char  infname[256], outfname[256], logfname[256];
	FILE *fin = NULL,  *fout = NULL;
	char progress[80];
	unsigned long int sbemail = 0, sbbing = 0, sbrandom = 0, ignored = 0;
	SOCKET sBing;
	unsigned char svBing;
	//struct sockaddr_in s_in;

	srand((unsigned int)time(NULL)); //randomize();

	create_log = FALSE;
	flog = NULL;

	info(stdout);

	sBing = prep_winsock();
	if (!sBing){
		char ans;
		logging("Cannot load WinSock! Bing search will not available\n");
		putchar('\a');
		printf("Press ENTER to continue or Q and ENTER to exit. . . ");
		fflush(stdin);
		ans = getchar();
		if((ans == 'q') || (ans == 'q'))
			return 1;
	}

	printf("\nWARNING: if exist output and log files, then they will be overwritten!\a\n\n");
    /* Failu paruosimas darbui */
	do{
		/* Jei "Bing" prieinama, kiek daugiausia simboliu spresti per "Bing"? */
		if(sBing){
			do{
				printf("Max. num. of \'?\' to solve via Bing (rec. will be ignored if more)[4]: ");
				fflush(stdin);
				svBing = getchar();
				(svBing == '\n') ? (svBing = 4) : (svBing -= '0');
				if ((svBing < 1) || (svBing > 7)){
					printf("Invalid value entered! 7 is maximum.\a\n");
				}
			}
			while((svBing < 1) || (svBing > 7));
		}

		printf("\nInput file (INPUT.CSV): ");
		fflush(stdin);
		gets(infname);
		if (!infname[0])
			strcpy(infname, "INPUT.CSV");
		/* Jei faila pavyko atidaryti, klausti kitu failu (isvesties ir log)*/
		if ((fin = fopen(infname, "r")) != NULL){
			printf("Output file (OUTPUT.CSV): ");
			fflush(stdin);
			gets(outfname);
			if (!outfname[0])
				strcpy(outfname, "OUTPUT.CSV");
		    /* Jei faila pavyko atidaryti, klausti LOG failo */
			if ((fout = fopen(outfname, "w+")) != NULL){
				printf("Log file (none): ");
				fflush(stdin);
				gets(logfname);
				if (!logfname[0])
					strcpy(logfname, "none");
				/* Jei nebuvo ivesta "none", t.y jei LOG reikalingas. */
				if (stricmp(logfname, "none")){
					create_log = TRUE;
					flog = fopen(logfname, "w+");
				}
			}
		}

		if(!(fin && fout && (flog || (!create_log)))){
			printf("Cannot ");
			if (fin == NULL)
				printf("open \"%s\".\n\a", infname);
			else if(!(fout && (flog || (!create_log))))
				printf("create \"%s\".\n\a", fout ? logfname : outfname);
		}
		putchar('\n');
	}
	while(!(fin && fout && (flog || (!create_log))));

	/* Pasakau, kad iseities failai naudos UTF-8 */
    fputc(0xEF, fout);
	fputc(0xBB, fout);
	fputc(0xBF, fout);
	if(flog){
		fputc(0xEF, flog);
		fputc(0xBB, flog);
		fputc(0xBF, flog);
	}

	/* Failus atidariau sekmingai, rasau duomenis i log'a */
	info(flog);
	logging("Files opened and created successfuly.\n");
	logging("INPUT FILE: ");
	logging(infname);
	logging("\nOUTPUT FILE: ");
	logging(outfname);
	logging("\nLOG FILE: ");
	logging(logfname);
	logging("\nMax. number of unknown leters to solve via Bing: ");
	putchar((int)(svBing + '0'));
	if (flog) fputc((int)(svBing + '0'), flog);
	logging("\n");

	/* DIRBU SU DUOMENIMIS */
	{
		/* Cia butu geriau naudoti dinamini atminties rezervavima, bet del
		   paprastumo tebunie bus statiskai. Vistiek cia tera Quick & Dirty. */
		char name[256] = "";
		char sirname[256] = "";
		char email[256] = "";
		unsigned char x, qnum;
		char qnumascii[4];
		unsigned long int records = 0, current = 0;
		float percent;
		unsigned char chr;
		BOOL ignore_this = FALSE;
		BOOL current_byrandom;

		/* Skaiciuojama, kiek yra irasu */
		while(!feof(fin)){
			if (fgetc(fin) == '\n')
				records++;
			/*else if (feof(fin))
				records++; - tobulinimui ateityje.Kad nepakibtu, jei nera '\n' failo pabaigoje*/
		}
		if (fseek(fin, 0, SEEK_SET)){
			logging ("\nSeeking failed!\n");
			fclose(fin);
			fclose(fout);
			fclose(flog);
			/* Baigiu rysi su Bing, jei buvau prisijunges */
			if (sBing){
				closesocket(sBing);
				WSACleanup();
			}
			return 1;
		}

		while (current < records /*(!feof(fin)) && records*/){
			current_byrandom = FALSE;
			if (kbhit()){
				if (getch() == 'q'){
					logging("Program ended by user. Copying remaining data...\n");
				    while(!feof(fin)){
						chr = fgetc(fin);
						if (fputc(chr, fout) == EOF){
							logging("ERROR: Probably no free space left on destination disk\n");
							logging("Exitting to OS...\n");
							closeall(fin, fout, &sBing);
						}
					}
					logging("Copied. Exitting...\n");
					break;
				}
				else fflush(stdin);
		    }
			printf("\nI: Press q if you want to exit.\n");
			percent = ((float)current/(float)records)*100;
			sprintf (progress, "\n--> Processing record %u of %u. %0.1f percent done. <--\n", current + 1, records, percent); 
			logging (progress);

			logging ("Identifying first character in current line... ");
			chr = fgetc(fin);
			if (chr == 0xEF){
				logging("0xEF found. Ignoring next two characters...");
				fgetc(fin);
				fgetc(fin);
				chr = fgetc (fin);
			}
			if((chr == '\n') || (chr == '\r')){
				logging("[FREE]\nNo data found at current line.\n");
				current++;
				continue;
			}

			else if ((chr != ';') && (chr != '\"')){
				logging("[FAIL]\nERROR: Invalid character found. Probably incorrect file specified\nExitting to OS...\n");
				closeall(fin, fout, &sBing);
				return 1;
			}
			
			if (chr == '\"'){
				logging ("Quotes.\nCopying NAME to memory... ");
				for(x = 0; name[x - 1] != '\"'; x++)
					name[x] = fgetc(fin);
				name[x - 1] = 0;
				logging ("Got: ");
				logging (name);
				logging ("\n");
			}
			else {
				logging("Semicolon.\n Name not found.");
				name[0] = 0;
			}

			logging ("Searching semicolon... ");
			while(fgetc(fin) != ';');
			logging ("Found.\nCopying SIRNAME to memory... ");
			if(fgetc(fin) == '\"'){
				for(x = 0; sirname[x - 1] != '\"'; x++)
					sirname[x] = fgetc(fin);
				sirname[x - 1] = 0;
				logging ("Got: ");
				logging (sirname);
			}
			else{
				sirname[0] = 0;
				logging("Not found.");
			}
			logging ("\n");

			logging ("Searching semicolon... ");
			while(fgetc(fin) != ';');
			logging ("Found.\nCopying EMAIL to memory... ");
			if(fgetc(fin) == '\"'){
				for(x = 0; email[x - 1] != '\"'; x++)
					email[x] = fgetc(fin);
				email[x - 1] = 0;
				logging ("Got: ");
				logging (email);
			}
			else{
				email[0] = 0;
				logging("Not found.");
			}

			/* --------------------------- */
			logging("\nTrying to find unknown symbols (question marks)... \n");
			qnum = findq(name) + findq(sirname);
			itoa(qnum, qnumascii, 10);
			if(qnum){
				logging("Found: ");
				logging(qnumascii);
				if (qnum >= 7){
					ignored++;
					logging (" - too much. Ignoring.\n");
					current++;
					if (!store(fout, name, sirname, email)){
						logging("\nCannot store data. Probably out of free space.\n\a");
						closeall(fin, fout, &sBing);
						return 1;
					}

					logging ("\nCopying another data (if exists)...");	
					while((chr = fgetc(fin)) != '\n'){
						if (fputc(chr, fout) == EOF){
							logging("ERROR: Probably no free space left on destination disk\n");
							logging("Exitting to OS...\n");
							closeall(fin, fout, &sBing);
						}
					}
					fputc(';', fout);
					fputc('\n', fout);

					continue;
				}
				logging("\n");
			}
			else{
				logging("Not found. Ignoring.\n");
				current++;
				if (!store(fout, name, sirname, email)){
					logging("\nCannot store data. Probably out of free space.\n\a");
					/* Uzdarau failus */
					if (fin)  fclose(fin);
					if (fout) fclose(fout);
					if (flog) fclose(flog);
					
					/* Baigiu rysi su Bing, jei buvau prisijunges */
					if (sBing){
						closesocket(sBing);
						WSACleanup();
					}
					return 1;
				}
				logging ("\nCopying another data (if exists)...\n");	
				while((chr = fgetc(fin)) != '\n'){
					if (fputc(chr, fout) == EOF){
						logging("ERROR: Probably no free space left on destination disk\n");
						logging("Exitting to OS...\n");
						closeall(fin, fout, &sBing);
					}
				}
				fputc(';', fout);
				fputc('\n', fout);
				continue;
			}

			logging("Trying to solve name/sirname by email...");
			if (!ns_by_mail(name, sirname, email)){
				logging(" - not solved.\nTrying to search the internet via Bing...\n");
				/* Va cia jau reikia prisijungti WinSock ir ieskoti internete*/
				if (svBing >= findq(name) + findq(sirname)){
					if (!ns_by_bing(name, sirname, sBing, percent)){
						logging(" - not solved.\nRandom values will be written. ");
						ns_by_random(name, sirname);
						logging("Random characters written.\n");
						sbrandom++;
						current_byrandom = TRUE;
					}
					else sbbing++;
				}
				else{
					logging("Too much question marks for that. Ignoring...");
					ignore_this = TRUE;
					ignored++;
				}
			}
			else sbemail++;

			ignore_this ? logging("\nRecord ignored.") : logging("\nRecord solved.");
			ignore_this = FALSE;
			logging("\nNAME: ");
			logging(name);
			logging("\nSIRNAME: ");
			logging(sirname);
			logging("\n");
			
			if (!store(fout, name, sirname, email)){
				logging("\nCannot store data. Probably out of free space.\n\a");
				/* Uzdarau failus */
				if (fin)  fclose(fin);
				if (fout) fclose(fout);
				if (flog) fclose(flog);
				/* Baigiu rysi su Bing, jei buvau prisijunges */
				if (sBing){
					closesocket(sBing);
					WSACleanup();
				}
				return 1;
			}

			logging ("\nCopying another data (if exists)...\n");	
			while((chr = fgetc(fin)) != '\n'){
				if (fputc(chr, fout) == EOF){
					logging("ERROR: Probably no free space left on destination disk\n");
					logging("Exitting to OS...\n");
					closeall(fin, fout, &sBing);
				}
			}
			if(current_byrandom)
				fputs("RANDOM", fout);

			fputc(';', fout);
			fputc('\n', fout);

			current++;
		}
	}

	logging("\n\nDONE!\n\n");
    /* Rodau statistika */
	logging("Statistic:\n\n");
	printf("Solved by EMail: %u\n", sbemail);
	printf("Solved by Bing search: %u\n", sbbing);
	printf("Random characters written: %u\n\n", sbrandom);
	printf("Total solved: %u\nIgnored: %u\n", sbrandom+sbbing+sbemail, ignored);
	if(flog){
		fprintf(flog, "Solved by EMail: %u\n", sbemail);
		fprintf(flog, "Solved by Bing search: %u\n", sbbing);
		fprintf(flog, "Random characters written: %u\n\n", sbrandom);
		fprintf(flog, "Total solved: %u\nIgnored: %u\n", sbrandom+sbbing+sbemail, ignored);
	}

	closeall(fin, fout, &sBing);

	puts("\a\a\a");
	getchar();

	return 0;
}

/* -------------------------------------------------
   Function   : info()
   Description: isveda informacija apie programa
   ------------------------------------------------- */
void info(FILE *out){
	if (out){
		fprintf(out, "Unknown Letters in Name/Sirname Solver v1.04\n");
		fprintf(out, "(c)2010, Azuolas - Faustas Bagdonas\n");
		fprintf(out, "============================================\n\n");
	}
}

/* -------------------------------------------------
   Function   : logging()
   Arguments  : 1. Log file pointer
				2. Log string
   Description: raso teksta i log'a.
   ------------------------------------------------- */
void logging (char *logstr){
	printf (logstr);
	if (create_log)
		fprintf (flog, logstr);
}

/* -------------------------------------------------
   Function   : ns_by_mail()
   Arguments  : 1. Name
				2. Sirname
				3. EMail
   Return     : TRUE, jei pavyko nuspeti varda/pavar-
                de pagal email.Nuspetas vardas/pavar-
				de issaugoma atitinkamai ten, kur rodo
				argumentuai	name, sirname.
   Description: raso teksta i log'a.
   ------------------------------------------------- */
BOOL ns_by_mail(char *name, char *sirname, char *email){
	BOOL nameslv;
	BOOL sirnameslv;

	findq(name) ? (nameslv = solveq(name, email)):(nameslv = TRUE);
	findq(sirname) ? (sirnameslv = solveq(sirname, email)):(sirnameslv = TRUE);

	return (nameslv && sirnameslv);

}

/* -------------------------------------------------
   Function   : toasc()
   Arguments  : 1. Destination
				2. Source
   Description: Is UTF-8 konvertuoja i ASCII.
   ------------------------------------------------- */
void toasc(char *dst, char *src){
	unsigned char x, y, z;
	BOOL found = FALSE;

	/* Is tiesu si lentele turetu buti daug didesne arba gal tam naudojamas
	   koks nors algoritmas?                                                */
	char conv[16][3] = {{0xC3, 0xA1, 0x61}, //a su desininiu --> a [LOTYNU CP]
						{0xC3, 0xAD, 0x69}, //i su desininiu --> i
						{0xC3, 0xA9, 0x65}, //e su desininiu --> e
						{0xC4, 0x85, 0x61}, //a su nosine --> a [BALTU CP]
						{0xC4, 0x8D, 0x63}, //c su varna --> c
						{0xC5, 0xAB, 0x75}, //u ilgoji --> u
						{0xC4, 0x97, 0x65}, //e su tasku --> e
						{0xC5, 0xBE, 0x7A}, //z su pauksciu --> z
						{0xC3, 0x81, 0x41}, //A su desininiu --> A
						{0xC3, 0x8D, 0x49}, //I su desininiu --> I
						{0xC3, 0x89, 0x45}, //E su desininiu --> E
						{0xC4, 0x84, 0x41}, //A su nosine -> A
						{0xC4, 0x8C, 0x43}, //C su varna --> C
						{0xC5, 0xAA, 0x55}, //U su bruksniu --> U
						{0xC4, 0x96, 0x45}, //E su tasku --> E
						{0xC5, 0xBD, 0x5A}};//Z su varna --> Z

	for (x = 0, z = 0; src[x]; x++, z++){
		found = FALSE;
		/* Jei MSB = 1, tai ne ASCII simbolis. Tikimasi UTF-8 */
		if (src[x] & 0x80){
			for (y = 0; y < 16; y++){
				if ((conv[y][0] == src[x]) && (conv[y][1] == src[x + 1])){
					dst[z] = conv[y][2];
					x++;
					found = TRUE;
					break;
				}
			}
		}
		if(!found)
			dst[z] = src[x];
	}
	dst[z] = 0;
}

/* -------------------------------------------------
   Function   : solveq()
   Arguments  : 1. Duomenys su klaustukais (name arba
                   sirname;
				2. Kur galiu rasti trukstamus simbo-
				   lius (email).
   Return     : TRUE, jei pavyko isspresti.
   Description: Randa trukstamus simbolius.
   ------------------------------------------------- */
BOOL solveq(char *qdata, char *search){
	unsigned char qprad, qreplace[7], qn, x;
	char fromemail[256];
	char tempname[256], tempnamebkp[256];

    /* Konvertuoju varda/pavarde (duomenis), kad butu naudojami tik ASCII simboliai */ 
	toasc(tempname, qdata);

	/* Jei pabaigoje randu tarpa, tai ji istrinu */
	if (tempname[strlen(tempname) - 1] == ' ')
		tempname[strlen(tempname) - 1] = 0;

	strcpy(tempnamebkp, tempname);

	for(qprad = 0; qdata[qprad] == '?'; qprad++);
	x = qprad;
	while((search[x] != '@') && (x != strlen(search)) && (x != 255)){
		qn = 0;
		strcpy(tempname, tempnamebkp);

		for(;(tolower(search[x]) != tolower(tempname[qprad])); x++){
			if((search[x] == '@') || (x == strlen(search))){
				x = 255;
				break;
			}
		}
		if(x != 255){
			unsigned char z;

			memcpy(fromemail, &search[x - qprad], strlen(tempname));
			fromemail[strlen(tempname)] = 0;

			for(z = 0; z < strlen(tempname); z++){
				if (tempname[z] == '?'){
					tempname[z] = fromemail[z];
					qreplace[qn++] = fromemail[z];
				}
			}
			
			if(!stricmp(tempname, fromemail)){
				unsigned char z;
				/* Sutapo */
				qn = 0;
				for(z = 0; z < strlen(qdata); z++){
					if (qdata[z] == '?')
						qdata[z] = qreplace[qn++];
				}
				return TRUE;
			}
		}

		if(x != 255) x++;
	}
	return FALSE;
}

/* -------------------------------------------------
   Function   : findq()
   Arguments  : 1. Stringas [su klaustukais]
   Return     : Klaustuku skaicius
   Description: Randa, kiek klaustuku yra string'e.
   ------------------------------------------------- */
unsigned char findq(char *str){
	unsigned char x, q = 0;
	for (x = 0; x < strlen(str); x++){
		if (str[x] == '?')
			q++;
	}
	return q;
}
/* -------------------------------------------------
   Function   : ns_by_bing()
   Arguments  : 1. Vardas, UTF-8 [su klaustukais]
                2. Pavarde,UTF-8 [su klaustukais]
				3. Socket
				4. Status (for displaying only)
   Return     : TRUE, jei rasti pavyko
   Description: Iesko vardo&pavardes per bing su
                spejamomis raidemis. Tinkama paren-
				ka pagal rezultatu skaiciu.
   ------------------------------------------------- */
BOOL ns_by_bing(char *name, char *sirname, SOCKET sBing, float percent){
	unsigned int x, y, nameendpos;
	unsigned char qpos[7], qposb[7], q, totalq, bkpq; //qpos[] - su "Bing" uzklausa, o qposb[] - su duomenim faile
	char query_start[] = "GET /search?q=%2B%27";
		//azuolas%27+%27bagdonas//	
	char query_end[] = "%27&form=QBRE&qs=n&sk= HTTP/1.1\r\nHost: www.bing.com\r\nConnection: close\r\n\r\n";
	//"%22&aq=f&aqi=&aql=&oq=&gs_rfai=&cad=h&as_qdr=all HTTP/1.1\r\nAccept: */*\r\nAccept-Language: lt\r\nUser-Agent: UnkLettersSolver/1.04\r\nHost: www.bing.lt\r\nConnection: Close\r\n\r\n";
	const char result_div[] = "<span class=\"sb_count\" id=\"count\">";
	const char no_results_str[] = "We did not find any results for";
	const char bad_results_str[] = "We didn't find any results for ";
	char namesirname[512], tmp[512];
	BOOL allz = TRUE;
	BOOL incnext;
	__int64 maxresults = 0, results;
	char letters_max_res[7], letters_now[7];
	BOOL found = FALSE, noresults = FALSE;
	unsigned int bytes_received = 0;
	char *data = NULL;
	const unsigned char unk_letters_table[22][2] ={ /* Centrines Europos */
											{0xc5, 0x95}, //ŕ 0
											{0xc4, 0x83}, //ă 1 
											{0xc4, 0x87}, //ć 2
											{0xc4, 0x8d}, //č 3
											{0xc4, 0x99}, //ę 4
											{0xc4, 0x9b}, //ě 5
											{0xc4, 0x8f}, //đ 6
											{0xc5, 0x84}, //ń 7
											{0xc5, 0x88}, //ň 8
											{0xc5, 0x91}, //ő 9
											{0xc5, 0x99}, //ř 10
											{0xc5, 0xaf}, //ů 11
											{0xc5, 0xb1}, //ű 12
											{0xc5, 0xa3}, //ţ 13
											{0xc3, 0xa2}, //â 14 
											{0xc5, 0x9f}, //ş 15
											{0xc5, 0x9b}, //ś 16
											{0xc5, 0xba}, //ź 17
											{0xc5, 0x82}, //ł 18
											{0xc5, 0xa3}, //ţ 19
											 /*  Baltu  */
											{0xc4, 0x85}, //ą 20 
											//{0xc4, 0x97}, //ė 15
											//{0xc4, 0xaf}, //į 16
											//{0xc5, 0xa1}, //š 17
											//{0xc5, 0xb3}, //ų 18
											//{0xc5, 0xab}, //ū 19
											{0xc5, 0xbe}, //ž 21
									  };
	const char no_table_rows = 22;
	//unsigned int dabar;
	BOOL retval = FALSE;

	/* Jei blogas socketas, nieko nedarau */
	if(!sBing)
		return FALSE;

	memset(letters_now, 0, 7);

	/* Konvertuoju duomenis taip, kad itikciau HTTP protokolui */
	conv4httpquery(namesirname, name);
    strcat(namesirname, "%27+%27");
	conv4httpquery(&namesirname[strlen(namesirname)], sirname);

	/* Surasau klaustuku pozicijas varde ir pavardeje,
	   nustatau pradines raides vietoje klaustuku      */
	for (x = 0, q = 0; x < strlen(namesirname); x++){
		if (namesirname[x] == '?'){
			qpos[q++] = x;
			strcpy(tmp, &namesirname[x]);
			strcpy(&namesirname[x+5], tmp);
			memset(&namesirname[x], 0xFF, 6);
		}
	}
	for(x = 0, q = 0; x < strlen(name); x++){
		if (name[x] == '?'){
			qposb[q++] = x;
		    strcpy(tmp, &name[x]);
			strcpy(&name[x+1], tmp);
		    name[x] = 0xFF;
		    name[x + 1] = 0xFF;
		}
	}
	x++;
	
	for(; x < (strlen(name) + strlen(sirname) + 1); x++){ ///!!!!
		if(sirname[x - strlen(name) - 1] == '?'){
			qposb[q++] = x; // - strlen(name); ///!!!
			strcpy(tmp, &sirname[x - strlen(name) - 1]);
			strcpy(&sirname[x - strlen(name)], tmp);
			sirname[x - strlen(name) - 1] = 0xFF;
			sirname[x - strlen(name)] = 0xFF;
		}
	}
	totalq = q;
	q = 0;

	/* Ieskau per Bing rezultatu su spejamomis raidemis */
	do{
		//struct fd_set fds = {
		//	fds.fd_count = 1,
		//	fds.fd_array[0] = sBing
		//};
		//struct timeval tv = {
		//	tv.tv_sec = 5,    
		//	tv.tv_usec = 100000  
		//};

		/* Surasau spejamas raides is lenteles */
		{
			char insert_str[7];
			for (q = 0; q < totalq; q++){
				sprintf(insert_str, "%%%0.2X%%%0.2X", unk_letters_table[letters_now[q]][0],unk_letters_table[letters_now[q]][1]);
				memcpy(&namesirname[qpos[q]], insert_str, 6);
			}

		}

		/* Siunciu uzklausa */
		send(sBing, query_start, strlen(query_start), 0);
		send(sBing, namesirname, strlen(namesirname), 0);
		send(sBing, query_end, strlen(query_end), 0);
		//select(0, &fds, NULL, NULL, &tv);

		/* Priimu atsakyma */
		{
		  int rtry;
		  data = (char *)0x274C;
		  bytes_received = 0xFFFF;
		  for (rtry = 0; (rtry < 10) && (data == (char *)0x274C) && (bytes_received == 0xFFFF); rtry++){
		    if (rtry)
				logging("Retrying... \n");
		    data = get_answer(&bytes_received, &sBing);
		  }
		  if (bytes_received == 0xFFFF) {
			  data = NULL;
			  free(data);
		  } /////////////
		}

		/* Jei atminties neuzteko net atsakymo pradziai 
		   (<=1430 baitu!),tai prasau vartotojo atlais-
		   vinti atminties.                             */
		if((data == NULL) && (bytes_received != 0xFFFF)){
			/* "Out of memory" jau buvo pasakyta */
			logging("Less than 1430 bytes remaining (can be bug in this program).\n"
				    "Free memory and then press ENTER for retry . . .\n\a");
			fflush(stdin);
			getchar();
		}

		/* Is naujo jungiuosi prie serverio, kad galeciau siusti sekancia uzklausa */
		if (!reconnect(&sBing)){
			logging("\nProblems with connection! Cannot reconnect to server for the next query.\n");
		}

		if ((data != NULL) && (bytes_received != 0xFFFF) && (bytes_received > 100)){ //>100 - turetu buti teisingi duomenys
			unsigned int n;
			/* Bing atsakyme ieskau to, kas mane domina (zr. result_div ir no_results_str) */
			for(n = 0; (n < (bytes_received - strlen(result_div))); n++){
				/* Ar yra rezultatu? */
				if (!memicmp(&data[n], result_div, strlen(result_div))){
					found = TRUE;
					noresults = FALSE;
					x = n;
					//break;
				}
				/* Ar nera rezultatu? */
				else if (!memcmp(&data[n], no_results_str, strlen(no_results_str))){
					found = TRUE;
					noresults = TRUE;
					break;
				}
				/* Ar rasti rezultatai TIKSLIAI atitinka uzklausos zodzius? */ 
				else if (!memcmp(&data[n], bad_results_str, strlen(bad_results_str))){
					found = TRUE;
					noresults = TRUE;
					break;
				}
			}
		}
		else{
			found = TRUE;
			noresults = TRUE;
		}

		/* Analizuoti atsakyma */
		if (found && (!noresults)){

			x += (strlen(result_div)); 

			/* Ignoruojami duomenys iki " of ". */
			while((memcmp(&data[x], " of ", 4)) && (x < bytes_received - 4)) x++;
			x += 4;

			for (y = 0; data[x + y] != ' '; y++);
			data[x + y] = 0;
			removedocs(&data[x]);
			results = _atoi64(&data[x]); 
			
			if(results > maxresults){
				maxresults = results;
				/* Surasau spejamas raides i daugiausia rezultatu
				   pelniusiu raidziu masyva.                      */
				for(x = 0; x < totalq; x++)
					letters_max_res[x] = letters_now[x]; //namesirname[qpos[x]];
			}
		}
		/* Jei rezultatu nera, tiesiog praleidziu */
		else if (found && noresults);

		/* Jei atsakyme nebuvo rasta "<div id=resultStats>". Tai
		   gali buti todel, kad Bing paieskos sistemoje buvo
		   padaryti pakeitimai. Sita dalyka butu galima bandyti
		   daryti konfiguruojama, kad esant pakeitimams nesunku
		   butu pakeisti, bet siandien del paprastumo tebunie
		   taip.                                                 */
		else if(!found){
			logging("\nError solving: ");
			puts(namesirname);
			if (flog) fputs(namesirname, flog);
			logging("ERROR: Cannot find \"");
			logging((char*)result_div);
			logging(" or \"");
			logging((char*)no_results_str);
			logging("\" in Bing answer. Posible reasons: \n"
					"- Bad network connection (if you see this some times)\n"	
					"- Connection lost (if you see this error message many times)\n" 
					"- Automated queries was blocked\n"
					"- Probably this program is too old. Bing has been updated (if you see this many times)\n");
		}

		found = FALSE;
		
		/* Atlaisvinama "Bing" atsakymui rezervuota atmintis */
		if (data != NULL){
			free(data);
			data = NULL;
		}

		printf("Guess: %s. Percent completed: %0.1f%%\n", namesirname, percent);

		/* Tikrinu, ar ne visas raides isbandziau */
		allz = TRUE;
		for(x = 0; (x < totalq) && allz; x++){
			if (letters_now[x] != (no_table_rows - 1))
				allz = FALSE;
		}

		/* Kitas raidziu spejimas */
		incnext = FALSE;
		bkpq = q;

		do{
			if (letters_now[q - 1] != (no_table_rows - 1)){ 
				letters_now[q - 1]++;
				incnext = FALSE;
			}
			else{
				letters_now[q - 1] = 0;
				q--;
				incnext = TRUE;
			}
		}
		while(incnext && (q)); 
		q = bkpq;
	}
	while (!allz);

	/* Isbandziau visas galimas kombinacijas ir radau, su kuria
	   kombinacija rezultatu yra daugiausia. Ruosiu atsakyma.   */
	if(maxresults){
		nameendpos = strlen(name);
		for (q = 0; q < totalq; q++){
			if(qposb[q] < nameendpos){
				name[qposb[q]] = unk_letters_table[letters_max_res[q]][0];
				name[qposb[q] + 1] = unk_letters_table[letters_max_res[q]][1];
			}
			else{
				sirname[qposb[q] - nameendpos - 1] = unk_letters_table[letters_max_res[q]][0];
				sirname[qposb[q] - nameendpos] = unk_letters_table[letters_max_res[q]][1];
			}
		}
	}

    if ((findq(name) == 0) && (findq(sirname) == 0) && (maxresults > 0)) 
		retval = TRUE;
	
	/* Jei yra, tai pasalinu 0xFF, kuriuos iterpiau pries tai */
	for (x = 0; x < strlen(name); x++){
		if ((unsigned char)name[x] == 0xFF)
			name[x] = '?';
	}
	for (x = 0; x < strlen(sirname); x++){
		if ((unsigned char)sirname[x] == 0xFF)
			sirname[x] = '?';
	}

	for (x = 0; x < strlen(name); x++){
		if ((unsigned char)name[x] == '?'){
			strcpy(tmp, &name[x + 1]);
			strcpy(&name[x], tmp);
		}
	}
	for (x = 0; x < strlen(sirname); x++){
		if ((unsigned char)sirname[x] == '?'){
			strcpy(tmp, &sirname[x + 1]);
			strcpy(&sirname[x], tmp);
		}
	}

	return retval;
}

/* -------------------------------------------------
   Function   : ns_by_random()
   Arguments  : 1. Vardas [su klaustukais]
                2. Pavarde [su klaustukais]
   Description: Iraso atsitiktines raides vietoje
                klaustuku.
   ------------------------------------------------- */
void ns_by_random(char *name, char *sirname){
	unsigned long int x;
	char chr;

	for (x = 0; x < strlen(name); x++){
		if (name[x] == '?'){
			do chr = (rand() % 25) + 0x61;
			while((chr == 'x')||(chr == 'q')||(chr == 'w'));
			name[x] = chr;
		}
	}

	for (x = 0; x < strlen(sirname); x++){
		if (sirname[x] == '?'){
			do chr = (rand() % 25) + 0x61;
			while((chr == 'x')||(chr == 'q')||(chr == 'w'));
			sirname[x] = chr;
		}
	}
}

/* -------------------------------------------------
   Function   : store()
   Arguments  : 1. Isvesties failas
				2. Vardas
				3. Pavarde
				4. EMail
   Return     : TRUE, jei irasiau sekmingai
   Description: Iraso nurodytus duomenis i faila
   ------------------------------------------------- */
BOOL store(FILE *fout, char *name, char *sirname, char *email){
	return (fprintf(fout, "\"%s\";\"%s\";\"%s\"", name, sirname, email) >= 0);
}

/* -------------------------------------------------
   Function   : prep_winsock()
   Return     : Windows Socket, jei sekmingai. Ki-
                taip FALSE (0).
   Description: Paruosia WinSock ir prisijungia prie
                Bing web serverio.
   ------------------------------------------------- */
SOCKET prep_winsock(){
	WSADATA wsaData;
	SOCKET sBingSrv;
	struct hostent *BingHost;
	//struct sockaddr_in s_in;
	char ipnum[4], x;

	memset(&s_in, 0, sizeof(s_in));

	/* Bandau ikrauti WinSock biblioteka */
	if(WSAStartup(MAKEWORD(1, 0), &wsaData) != NO_ERROR){
		logging("\nFailed to start WinSock!\n");
		return FALSE;
	}
	logging("\nWinSock library loaded.\n");

	/* Sprendziu www.bing.com ir nustatau s_in */
	logging("Solving www.bing.com... ");
	BingHost = gethostbyname("www.bing.com");
	if(BingHost == NULL){
		logging("[FAIL]\n\n--> Probably no network connection. <--\n\n");
		printf("Error at gethostbyname(): 0x%0.8lX\n", WSAGetLastError());
		if(flog) fprintf(flog, "Error at gethostbyname(): 0x%0.8lX\n", WSAGetLastError());
		WSACleanup();
		return FALSE;
	}
	logging("[ OK ]\n");
	s_in.sin_port = htons(80);
	s_in.sin_family = AF_INET;
	s_in.sin_addr.S_un.S_addr = *(unsigned long*)(BingHost->h_addr_list[0]);
	/* Isvedu gauta IP adresa */
	logging("Got IP address: ");
	for(x = 0; x < 4; x++){
		itoa((unsigned char)((BingHost->h_addr_list[0]))[x], ipnum, 10);
		logging(ipnum);
		logging(".");
	}
	logging("\nConneting to server... ");
	if (!reconnect(&sBingSrv)){
		logging("[FAIL]\n");
		return FALSE;
	}

	logging("[ OK ]\n");
 	return sBingSrv;
}

BOOL reconnect(SOCKET *sck){
	struct timeval tv = {
		tv.tv_sec = 2,
		tv.tv_usec = 0
	};
	closesocket(*sck);

    //logging("Creating socket... ");
	/* Sukuriu socketa bendravimui su Bing serveriu */
	*sck = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (*sck == INVALID_SOCKET){
		//logging("[FAIL]\n");
		printf("\nError at socket(): 0x%0.8lX\n", WSAGetLastError());
		if(flog) fprintf(flog, "\nError at socket(): 0x%0.8lX\n", WSAGetLastError());
		//WSACleanup();
		return FALSE;
	}
	//logging ("[ OK ]\n");

	/* Nustatau timeout'us */
	if(setsockopt(*sck, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) == SOCKET_ERROR)
		logging("Cannot set timeouts. Timeouts can be too long.\n");

	/* Jungiuosi prie sukurto socketo */
	//logging("Connecting to server for the first time... ");
	if (connect(*sck, (struct sockaddr*)&s_in, sizeof(s_in)) == SOCKET_ERROR){
		//logging("[FAIL]\n");
		printf("\nError at connect(): 0x%0.8lX\n\a", WSAGetLastError());
		if(flog) fprintf(flog, "\nError at connect(): 0x%0.8lX\n", WSAGetLastError());
		Sleep(1000);
		return FALSE;
	}
	//logging("[ OK ]\n");
	return TRUE;
}

/* -------------------------------------------------
   Function   : conv4httpquery()
   Arguments  : 1. Destination
				2. Source
   Description: Paruosia stringa taip, kad jis
                itiktu HTTP protokolui. Klaustukams
				darau isimti, nes jie bus veliau
				keiciami spejamais ASCII simboliais.
   ------------------------------------------------- */
void conv4httpquery(char *dst, char *src){
	unsigned int srcpos, dstpos;
	char hexdigit[3];

	for (srcpos = 0, dstpos = 0; srcpos < strlen(src); srcpos++){
		if (((src[srcpos] >= '0') && (src[srcpos] <= '9'))||((src[srcpos] >= 'A') && 
			 (src[srcpos] <= 'Z'))||((src[srcpos] >= 'a') && (src[srcpos] <= 'z'))||(src[srcpos] == '?'))
			dst[dstpos++] = src[srcpos];
		else{
			dst[dstpos] = '%';
			itoa((unsigned char)src[srcpos], hexdigit, 16);
			strcpy(&dst[dstpos+1], hexdigit);
			dstpos += (1 + strlen(hexdigit));
		}
	}
	dst[dstpos] = 0;
}

/* -------------------------------------------------
   Function   : removedocs()
   Arguments  : 1. String (src & dst)
   Description: Pasalina taskus is string'o. Si f()
                reikalinga, kad pasalinti taskus is
				"Bing" atsakymo, nes yra thousand
				separator'ius naudojamas. atoi() jo
				nesupranta.
   ------------------------------------------------- */
void removedocs(char *str){
	unsigned int len = strlen(str);
	unsigned int x;

	for (x = len; x; x--){
		if(str[x] == ','){
			strcpy(&str[x], &str[x + 1]);
			str[strlen(str)] = 0;
		}
	}
}

/* -------------------------------------------------
   Function   : closeall()
   Arguments  : 1. Failo ptr. 1 (fin tikimasi)
				2. Failo ptr. 2 (fout tikimasi)
				3. Socketo ptr.
   Description: Uzdaro failus ir WinSock.
   ------------------------------------------------- */
void closeall(FILE *fin, FILE *fout, SOCKET *socket){
	/* Uzdarau failus */
	if (fin)  fclose(fin);
	if (fout) fclose(fout);
	if (flog) fclose(flog);

	/* Uzbaigiu rysi su Bing, jei buvau prisijunges */
	if (*socket){
		closesocket(*socket);
		WSACleanup();
	}
}

/* -------------------------------------------------
   Function   : get_answer()
   Arguments  : 1. Ptr. to UINT witch keeps number of
                   received bytes
				2. Socket ptr.
   Return     : Allocated  buffer  with  answer from 
                server. If cannot allocate memory, 
				then return NULL. If problems with
				recv(), then writes 0xFFFF to UINT
				pointed by *received and return value
				is WSA error code.
   Description: Get answer from server. This function
                reads data until connection closed by
				remote host. Function will allocate
				memory for answer.
   ------------------------------------------------- */
char *get_answer(unsigned int *received, SOCKET *sck){
	/*struct fd_set fds = {
		fds.fd_count = 1,
		fds.fd_array[0] = *sck
	};
	struct timeval tv = {
		tv.tv_sec = 2,    
		tv.tv_usec = 0  
	};*/
	unsigned int current_recv, recv_stat;
	char *data = NULL, *olddata = NULL;
	char tmp[1430];

	*received = 0;

	do{
		current_recv = recv(*sck, tmp, 1430, MSG_PEEK);
		
		/* Rezervuoju atminti. olddata - jei nepasiseke rezervuoti,
		   tai galiu dar is bedos pasinaudoti senu pointeriu.       */ 
		if (current_recv == SOCKET_ERROR)
			break;
		olddata = data;
		data = realloc(data, *received + current_recv);
		if (data == NULL){
			data = olddata;
			logging("\nERROR: Out of memory!!!\n");
			break;
		}

		*received += current_recv;
		recv_stat = recv(*sck, &data[*received - current_recv], current_recv, MSG_WAITALL);
	}
	while((recv_stat) && (recv_stat != SOCKET_ERROR));

	printf("Downloaded %u bytes.\n", *received);
	//Neverta loginti; if(flog) fprintf(flog, "Downloaded %u bytes.", *received);	
	
	if ((recv_stat == SOCKET_ERROR) || (current_recv == SOCKET_ERROR)){
		printf("Error at recv(): 0x%0.8lX\n", WSAGetLastError());
		if(flog) fprintf(flog, "Error at recv(): 0x%0.8lX\n", WSAGetLastError());

		/* Jei data pointerio reiksme yra data == 0x274C ir *received == 0xFFFF, tai teks kartoti sia f() */
		//if(!*received){
		data = (char *)WSAGetLastError();
		*received = 0xFFFF;	
		
	}

	return data;
}
/* --------------------[ End of File ]-------------------- */
