/////////////////////////////////////////////////
//                                             //
//           Proxy Tutroial Code               //
//           -------------------               //
//                                             //
//  An example program for a proxy tutorial    //
//                                             //
//  Date: 13/12/2002                           //
//  Copyright (c) 2002 Goblineye Entertainment //
//  Programmed by: Saar (a.k.a Tornado)        //
//                       tornado@goblineye.com //
//                     Goblineye Entertainment //
//                   http://www.goblineye.com/ //
// Use Win32 Console Application(MSVC)         //
/////////////////////////////////////////////////

#define WIN32_LEAN_AND_MEAN

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <windows.h>

typedef unsigned char byte;
#pragma comment(lib,"ws2_32.lib")

char g_szServer[128]; // server (e.g. www.goblineye.com)
char g_szDocument[256]; // document on the server (e.g. index.php or somedirectory/somefile.zip)
char g_szProxyServer[128]; // proxy server. this is always retrieved.
WORD g_wProxyPort; // proxy server port

char g_szUsername[32]; // username
char g_szPassword[32]; // password



// Base64 encoder
// written by Tornado (That's me :))
// lookup table
const char BASE64_LOOKUP[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
// Base64_GetEncodedLen() retrieves the exact encoded string length. it's a bit crude though, and might give problems on other compilers, so watch out.
__forceinline unsigned int Base64_GetEncodedLen(char *lpStr) { unsigned int uiRet = (unsigned int)(((lstrlen(lpStr)*8)/6)); while (uiRet%4 != 0) uiRet++; return(uiRet); }

void Base64_Encode(const char *lpinBuffer,char *lpoutBuffer) // no checking is done here (lpinBuffer and lpoutBuffer might be NULL on error), so look out
{
	unsigned int uiOutProg = 0;
	byte bNewLetter = 0;
	byte bInProg = 0;

	while ((lpinBuffer != NULL) && (*lpinBuffer != '\0'))
	{
		// bInProg is the amount of bits we already have in bNewLetter
		// let's get what we can and scram
		bNewLetter |= (*lpinBuffer & ((0xFC << bInProg) & 0xFF)) >> bInProg;
		bNewLetter >>= 2;
		lpoutBuffer[uiOutProg++] = BASE64_LOOKUP[bNewLetter];

		bNewLetter = 0; // starting over
		bInProg = 8 - (2 + bInProg); // this is how much we need (8 - (6 - bInProg)) - the (-8) is just to make stuff below faster
		bNewLetter = (*lpinBuffer & (0xFF >> bInProg)) << bInProg;
		bInProg = 8 - bInProg;

		// if we're full - unload
		if (bInProg == 6)
		{
			bNewLetter >>= 2;
			lpoutBuffer[uiOutProg++] = BASE64_LOOKUP[bNewLetter];
			bInProg = 0;
			bNewLetter = 0;
		}

		lpinBuffer++;
	}

	if (bInProg > 0)
	{
		bNewLetter >>= 2;
		lpoutBuffer[uiOutProg++] = BASE64_LOOKUP[bNewLetter];
	}

	while (uiOutProg%4 != 0) { lpoutBuffer[uiOutProg++] = '='; }
	lpoutBuffer[uiOutProg] = '\0';
}


int main(void)
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2,0),&wsaData); // normally you will need tons of error checking here, but this is just an example. Winsock v2.0 is assumed.

	do
	{
		printf("What server would you like to connect to? (e.g. www.goblineye.com) ");
		gets(g_szServer);
	} while (lstrlen(g_szServer) == 0);

	do
	{
		printf("What document would you like to download from the server? (e.g. somepic.png) ");
		gets(g_szDocument);
	} while (lstrlen(g_szDocument) == 0);

	do
	{
		printf("What proxy server would you like to use? (e.g. proxy.myisp.net) ");
		gets(g_szProxyServer);
	} while (lstrlen(g_szProxyServer) == 0);


	char szTemp[32]; // temp
	printf("What is the proxy server's port? (Press Return for the default - 8080) ");
	gets(szTemp);

	if (lstrlen(szTemp) == 0)
	{
		g_wProxyPort = 8080; // default
	}
	else
	{
		g_wProxyPort = atoi(szTemp);
	}
	

	printf("Enter the username for the authentication: (e.g. Enter nothing for no authentication) ");
	gets(g_szUsername);
	
	if (lstrlen(g_szUsername) > 0)
	{
		do
		{
			printf("Enter the password for the authentication: ");
			gets(g_szPassword);
		} while (lstrlen(g_szPassword) == 0);
	}




	// ok now let's connect
	SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // create a new socket using TCP. no error checking here, could be dangerous.

	// start the connection
	LPHOSTENT lpHostEntry = gethostbyname(g_szProxyServer);
	if (lpHostEntry == NULL) // some error-checking, heh :)
	{
		shutdown(s,SD_BOTH);
		closesocket(s);
		printf("Could not find proxy server! Try again");
		return(0);
	}


	// ok now let's make a connection already
	SOCKADDR_IN saServer;
	
	saServer.sin_family = AF_INET;
	saServer.sin_addr = *((LPIN_ADDR)*lpHostEntry->h_addr_list); // address
	saServer.sin_port = htons(g_wProxyPort); // the port

	// do the connection
	printf("Connecting to server...\r\n");
	if (connect(s,(LPSOCKADDR)&saServer,sizeof(saServer)) == SOCKET_ERROR)
	{
		shutdown(s,SD_BOTH);
		closesocket(s);
		printf("Could not connect to proxy server, try again later");
		return(0);
	}
	printf("Connected!\r\n");

	char szData[2048]; // used for sending/receiving data
	wsprintf(szData,"GET http://%s/%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\nAccept: */*\r\nUser-Agent: MyINetApp/0.0.0.1\r\nCache-Control: no-store, no-cache\r\nPragma: no-cache\r\n",g_szServer,g_szDocument,g_szServer);

	if (lstrlen(g_szUsername) > 0) // if we're doing authentication
	{
		lstrcat(szData,"Proxy-Authorization: Basic ");
		char *lpFinalBuffer = NULL; // this buffer will hold the encoded string when we're done
		char szTempBuf[258]; // more than enough
		lstrcpy(szTempBuf,g_szUsername);
		lstrcat(szTempBuf,":");
		lstrcat(szTempBuf,g_szPassword);

		lpFinalBuffer = new char[Base64_GetEncodedLen(szTempBuf) + 1];
		Base64_Encode(szTempBuf,lpFinalBuffer);
		lstrcat(szData,lpFinalBuffer);
		lstrcat(szData,"\r\n");
		delete [] lpFinalBuffer;
	}
	lstrcat(szData,"\r\n"); // finish the request

	send(s,szData,lstrlen(szData),0); // send it out (no error checking!)
	printf("Sent request headers\r\n");

	// now receive the data back
	// before we get the data back, we will create the file the data will be written to
	FILE *hFile = fopen("download.txt","wb");

	// PLEASE NOTE: that the saved data is not the actual file, it's the returned headers
	// followed by the actual requested file
	// also, if you try to access any dynamic content (PHP/ASP etc. pages), the returned data
	// will probably be in a different form than expected, since web servers usually send dynamic data using
	// a transfer encoding named "chunked" - but that's for another tutorial :)
	printf("Receiving data\r\n");
	int iResult;
	while (1)
	{
		iResult = recv(s,szData,sizeof(szData)-1,0); // use the same buffer for the data
		if ((iResult == 0) || (iResult == SOCKET_ERROR)) // we're done
		{
			break;
		}
		else
		{
			szData[iResult] = '\0'; // close it
			fwrite(szData,iResult,1,hFile);
		}
	}
	fclose(hFile);
	if (iResult == SOCKET_ERROR) // error
	{
		printf("Error while receiving data\r\n");
	}
	else
	{
		printf("Done receiving data\r\n");
	}

	shutdown(s,SD_BOTH); // kill the socket
	closesocket(s); // close it
	WSACleanup();
	return(0);
}
