/* --- SECURITY.CPP ---
/  
/ Written by Phil Himsworth, 2001-2002.
/
/*/

#include <windows.h>
#include <wincrypt.h>
#include <mmsystem.h>

#include "types.h"
#include "main.h"
#include "html.h"
#include "plugin.h"

extern char password[], username[];
extern int securepassword;

extern winampGeneralPurposePlugin plugin;



// Base64 character array
//					   0         1         2         3         4         5         6
//					   01234567890123456789012345678901234567890123456789012345678901234
char bchars[] =		  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+*";
char achars[256]; 

// --------------------------------------------------------------------------------
// Calculates MD5 of a string
bool MD5(char * input, char * output, int output_len)
{
	//MessageBox(NULL,input,"MD5 input",MB_TOPMOST | MB_OK);
	HCRYPTPROV hProv = 0;
	HCRYPTHASH Hash;

	if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) 
	{
		LPVOID lpMsgBuf;
		FormatMessage( 
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL 
		);
		// Process any inserts in lpMsgBuf.
		// ...
		// Display the string.
		MessageBox( NULL, (LPCTSTR)lpMsgBuf, "MD5 AcquireContext Error", MB_OK | MB_ICONINFORMATION );
		// Free the buffer.
		LocalFree( lpMsgBuf );

		return false;
	}

	CryptCreateHash(hProv,CALG_MD5, 0, 0, &Hash);

	int input_len = lstrlen(input);

	BYTE md5raw[2048];
	int md5raw_len = 2048;
	CryptHashData(Hash,(unsigned char*)input,input_len, 0);	// md5 of "<time>:<password>"
	CryptGetHashParam(Hash, HP_HASHVAL, md5raw,(DWORD*)&md5raw_len,0);

	wsprintf(output,"");		// Make output a terminated string
	
	char temp[4];
	//wsprintf(temp,"%d",md5raw_len);
	//MessageBox(NULL,temp,"gen_httpSrv md5raw_len",MB_OK);

	for (int i=0;i<md5raw_len;i++)
	{
		wsprintf(temp,"%2.2x", md5raw[i]);
		lstrcat(output,temp);
	}

	//MessageBox(NULL,output,"gen_httpSrv debug MD5", MB_OK);




	CryptReleaseContext(hProv,0);
	return true;
}




// --------------------------------------------------------------------------------
// Sends a 401 for Basic authentication
int SendBasic401(connection * conn, char * realm)
{
	prints(conn,"HTTP/1.0 401 Unauthorized\nWWW-Authenticate: Basic realm=\"");
	prints(conn,realm);
	prints(conn,"\"\n");
	prints(conn,"Content-Type: text/html\n\n<html>\n<head>\n<title>Login Failed</title>\n");
	Style(conn);
	CloseHeader(conn);
	OpenPageBody(conn);
	prints(conn,"<h1>Login Failed</h1>\n<p><b>Winamp Web Interface could not log you in.</b></p>\n<p><a href=\"/main\">Back to main page</a></p>");
	CloseBody(conn);
	conn->http.responsecode = 401;
	return ST_CLOSE;
}

// --------------------------------------------------------------------------------
// Finds a substring in another string. Probably should be in strings.cpp...
char * FindSubString(char * buffer,char * target)
{
	//MessageBox(NULL,buffer,"gen_httpSrv FindSubString",MB_OK);
	int len = lstrlen(buffer) - lstrlen(target);
	for (int i=0;i<=len;i++)
	{
		if (StartString(target,buffer+i))
			return (char*)(buffer+i);
	}
	return NULL;
}




// --------------------------------------------------------------------------------
// Finds auth headers in a request
void FindBasicAuth(connection * conn, char * buffer)
{
	//int ptr;
	//MessageBox(NULL,buffer,"gen_httpSrv FindBasicAuth: buffer",MB_OK);
	char * chptr = FindSubString(buffer,"Authorization:");	// Look for auth
	if (!chptr)
	{														// Not found		
		chptr = FindSubString(buffer,"Authorisation:");		// Misspellings?
		if (!chptr)											// Still no; fail
		{
			//MessageBox(NULL,"No Auth","gen_httpSrv debug FindBasicAuth",MB_OK);	
			conn->http.auth = CheckCredentials(conn,"anon","");
			return;
		}
	}
	
	// Authorisation section found
	//MessageBox(NULL,chptr,"gen_httpSrv debug",MB_OK);
	
	char temp[100];


	char * cl_user;
	char * cl_pass;

	chptr = GetAString(chptr,(char*)&temp);		// chptr now points at Authorisation, so get it
	chptr = GetAString(chptr,(char*)&temp);		// get auth mode - 'basic'
	chptr = GetAString(chptr,(char*)&temp);		// get user/pass string
	
	char userpass[100];
	FromBase64((char*)&temp,(char*)&userpass);

	int i=0;
	while(userpass[i] != ':')
	{
		if (userpass[i] == 0)
		{
			conn->http.auth = CheckCredentials(conn,"anon","");
			return;
		}
		i++;
	}
	userpass[i] = 0;
	cl_pass = &userpass[i+1];
	cl_user = (char*)&userpass;


	//wsprintf(temp,"User: %s\nPassword: %s",cl_user,cl_pass);
	//MessageBox(NULL,temp,"user/password",MB_TOPMOST | MB_OK);
	//MessageBox(NULL,temp,"gen_httpSrv debug  base64 from client",MB_OK);
	
	conn->http.auth = CheckCredentials(conn, cl_user, cl_pass);


}
// --------------------------------------------------------------------------------
// Converts a string to Base64
void ToBase64(char * instr, char * outstr)
{
	//MessageBox(NULL,instr,"instr",MB_OK);

	int i=0;
	int o=0;
	int j = lstrlen(instr);

	bool done = false;

	while(instr[i] != 0 && done == false)
	{
		outstr[o] = bchars[(instr[i] & 0xFC) / 4];
		outstr[o+1] = bchars[((instr[i] & 0x03) * 16) + ((instr[i+1] & 0xF0) / 16)];
		if (instr[i+1] == 0)
		{
			outstr[o+2] = '=';
			outstr[o+3] = '=';
			done = true;
		}
		else
		{
			outstr[o+2] = bchars[((instr[i+1] & 0x0F) * 4) + ((instr[i+2] & 0xC0) /64)];
			if (instr[i+2] == 0)
			{
				outstr[o+3] = '=';
				done = true;
			}
			else
				outstr[o+3] = bchars[(instr[i+2] & 0x3F)];
		}

		o += 4;
		i += 3;
	}

	outstr[o] = 0;
}

// --------------------------------------------------------------------------------
// Converts a Base64 string to plaintext
void FromBase64(char * instr,char * outstr)
{
	int i=0;
	int o=0;
	int j=lstrlen(instr);

	for (int n=0;n<j;n++)		// reduce ascii codes to numbers
	{
		if (instr[n] != '=')
			instr[n] = achars[instr[n]];
		else
			instr[n] = (char)255;
	}

	while(instr[i] != 0)
	{
		outstr[o] =		((instr[i] & 0x3F) * 4) + ((instr[i+1] & 0x30) / 16);
	
		if (instr[i+2] == (char)255)
			outstr[o+1] = 0;
		else
		{
			outstr[o+1] =	((instr[i+1] & 0x0F) * 16) + ((instr[i+2] & 0x3C) / 4);

			if (instr[i+3] == (char)255)
				outstr[o+2] = 0;
			else
				outstr[o+2] =	((instr[i+2] & 0x03) * 64) + (instr[i+3] & 0x3F);
		}

		i += 4;
		o += 3;
	}
	outstr[o] = 0;
}

// --------------------------------------------------------------------------------
// Checks credentials with those in ini file
int CheckCredentials(connection * conn, char * user, char * pass)
{

	char ini_file[MAX_PATH], *p;
	GetModuleFileName(plugin.hDllInstance,ini_file,sizeof(ini_file));
	p=ini_file+lstrlen(ini_file);
	while (p >= ini_file && *p != '\\') p--;
	if (++p >= ini_file) *p = 0;
	lstrcat(ini_file,"gen_httpsrv.ini");

	if (StrComp(user,"anon"))
		return GetUserAccess(ini_file,user);

	char password[40];

	GetPrivateProfileString("Users",user,"",(char*)&password,40,ini_file);

	char md5pass[40];
	if (securepassword == 1)
		MD5(pass,(char*)&md5pass,40);
	else
		ToBase64(pass,md5pass);

	//if (password[0] == 0)
	//	return AUTH_ANON;

	//char temp[150];
	//wsprintf(temp,"file: %s\nrecv: %s",password,md5pass);
	//MessageBox(NULL,temp,"auth",MB_OK | MB_TOPMOST);
	
	if (StrComp(password,md5pass))
	{
		wsprintf(conn->http.user,user);
		return GetUserAccess(ini_file, user);
	}
	else
		return AUTH_ANON;
}

// --------------------------------------------------------------------------------
// Gets access rights from the ini file
int GetUserAccess(char * ini_file,char * user)
{
	char useraccesskey[30];
	wsprintf(useraccesskey, "#%s_access", user);
	return GetPrivateProfileInt("Users",useraccesskey,AUTH_ANON,ini_file);
}
