#include "../inc/swilib.h"
#include "main.h"
#include "string_util.h"
#include "xml_parser.h"
#include "md5.h"
#include "jabber.h"
#include "jabber_util.h"
#include "history.h"
#include "adv_login.h"
#include "lang.h"

extern JABBER_STATE Jabber_state;
extern const char JABBER_SERVER[];
extern const char USERNAME[];
extern const char PASSWORD[];
extern const char RESOURCE[30];
extern char My_JID_full[];
extern char My_JID[];
extern char logmsg[];

SASL_AUTH_DATA SASL_Auth_data = {NULL, NULL, NULL, NULL, NULL};


void Send_Welcome_Packet_SASL()
{
	char streamheader[] = "<?xml version='1.0' encoding='UTF-8'?>\n"
	                      "<stream:stream to='%s' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' xml:lang='%s' version='1.0'>";
	char* buf = malloc(256);
	sprintf(buf, streamheader, JABBER_SERVER, LG_XML_LANG);
	SendAnswer(buf);
	mfree(buf);
	LockSched();
	strcat(logmsg, "\nSend Extended Welcome");
	UnlockSched();
#ifdef LOG_ALL
	Log("CONN", logmsg);
#endif
}

void Destroy_SASL_Ctx()
{
	if(SASL_Auth_data.nonce)mfree(SASL_Auth_data.nonce);
	if(SASL_Auth_data.cnonce)mfree(SASL_Auth_data.cnonce);
	if(SASL_Auth_data.qop)mfree(SASL_Auth_data.qop);
	if(SASL_Auth_data.rsp_auth)mfree(SASL_Auth_data.rsp_auth);
	if(SASL_Auth_data.realm)mfree(SASL_Auth_data.realm);
	zeromem(&SASL_Auth_data, sizeof(SASL_AUTH_DATA));
}

// Сообщить серверу об использовании аунтитификации MD5-DIGEST
//Context:HELPER
void Use_Md5_Auth_Report()
{
	char s[] = "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5'/>";
	Jabber_state = JS_SASL_NEGOTIATION;
	SendAnswer(s);
}

// Сообщить серверу об использовании аунтитификации PLAIN
//Context:HELPER
void Use_Plain_Auth_Report()
{
// По мотивам e:\CPP_Proj\Miranda_IM\miranda\protocols\JabberG\jabber_secur.cpp

	char s[] = "<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"PLAIN\">%s</auth>";
	char patt[] = "%s@%s%c%s%c%s";
	unsigned short patlen = strlen(USERNAME) * 2 + strlen(JABBER_SERVER) + strlen(PASSWORD) + 3;
	char* authdata = malloc(patlen + 1);
	zeromem(authdata, patlen + 1);
	snprintf(authdata, patlen + 1, patt, USERNAME, JABBER_SERVER, 0, USERNAME, 0, PASSWORD);
	char* Data_To_Send = malloc(patlen * 2);
	zeromem(Data_To_Send, patlen * 2);
	Base64Encode(authdata, patlen, Data_To_Send, patlen * 2);
	char* fin = malloc(strlen(Data_To_Send) + strlen(s) + 1 - 2); // ибо %s нах не надо считать
	sprintf(fin, s, Data_To_Send);
	Jabber_state = JS_SASL_AUTH_ACK;  // Фишка - пропускаем несколько этапов ;)
	SendAnswer(fin);
	mfree(Data_To_Send);
	mfree(authdata);
	mfree(fin);
}

// Открываем новый поток к серверу по окончании авторизации
void SASL_Open_New_Stream()
{
	Jabber_state = JS_SASL_NEW_STREAM_ACK;
	SUBPROC((void*)Send_Welcome_Packet_SASL);
}

// Выполняем Resource Binding
void SASL_Bind_Resource()
{
	sprintf(My_JID, "%s@%s", USERNAME, JABBER_SERVER);
	sprintf(My_JID_full, "%s/%s", My_JID, RESOURCE);

	sprintf(logmsg, "Resource binding");
	REDRAW();
	char* bind_tpl = malloc(256);
	sprintf(bind_tpl, "<iq type='set' id='SieJC_bind_req'>"
	        "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
	        "<resource>%s</resource>"
	        "</bind>"
	        "</iq>", RESOURCE);
	Jabber_state = JS_SASL_RESBIND_ACK;
	SUBPROC((void*)_sendandfree, bind_tpl);
}

// Инициализация сессии
void SASL_Init_Session()
{
	sprintf(logmsg, "Session Init");
	REDRAW();

	static char sess_init_tpl[] = "<iq type='set' id='SieJC_sess_req'>"
	                              "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
	                              "</iq>";

	Jabber_state = JS_SASL_SESS_INIT_ACK;
	SUBPROC((void*)SendAnswer, sess_init_tpl);
}

void Decode_Challenge(char* challenge)
{
	char* decoded_challenge = malloc(1024);
	zeromem(decoded_challenge, 1024);
	int unk5 = 0;
	Base64Decode(challenge, strlen(challenge), decoded_challenge, 1024, NULL, &unk5);
	SASL_Auth_data.nonce = Get_Param_Value(decoded_challenge, "nonce", 1);
	SASL_Auth_data.qop   = Get_Param_Value(decoded_challenge, "qop", 1);
	SASL_Auth_data.realm = Get_Param_Value(decoded_challenge, "realm", 1);

//SASL_Auth_data.nonce = malloc(128);strcpy(SASL_Auth_data.nonce,"455564019");
//SASL_Auth_data.qop = malloc(128);strcpy(SASL_Auth_data.qop,"auth");

	mfree(decoded_challenge);
	char* cnonce = malloc(60);
	strcpy(cnonce, "7425da72aliuf242765");
	SASL_Auth_data.cnonce = cnonce;
}

void mkhex(md5_byte_t digest[16], char* hex_output)
{
	for (int di = 0; di < 16; ++di)sprintf(hex_output + di * 2, "%02x", digest[di]);
}

char ans[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>";

void Process_Auth_Answer(char* challenge, char sendanswer)
{
	char* decoded_challenge = malloc(256);
	zeromem(decoded_challenge, 256);
	int unk5 = 0;
	Base64Decode(challenge, strlen(challenge), decoded_challenge, 256, NULL, &unk5);
	SASL_Auth_data.rsp_auth   = Get_Param_Value(decoded_challenge, "rspauth", 0);
	mfree(decoded_challenge);
	Jabber_state = JS_SASL_AUTH_ACK;
	if(sendanswer) SUBPROC((void*)SendAnswer, ans);
}


void Send_Login_Packet()
{
	md5_state_t state;
	md5_byte_t digest[16];
	md5_byte_t  A1[16], A2[16], Response[16];
	char colon_t[] = ":";
	char _00000001[] = "00000001";
	char hex_output[16 * 2 + 1];
	char A1_HEX[16 * 2 + 1];
	char A2_HEX[16 * 2 + 1];
	char R_HEX[16 * 2 + 1];

	char* digest_uri = malloc(128);

	char* User_Realm_Pass = malloc(256);
	zeromem(digest_uri, 128);
	snprintf(digest_uri, 127, "AUTHENTICATE:xmpp/%s", JABBER_SERVER);

	md5_init(&state);
	md5_append(&state, (const md5_byte_t*)USERNAME, strlen(USERNAME));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);
	md5_append(&state, (const md5_byte_t*)SASL_Auth_data.realm, strlen(SASL_Auth_data.realm));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);
	md5_append(&state, (const md5_byte_t*)PASSWORD, strlen(PASSWORD));
	md5_finish(&state, digest);
	mkhex(digest, hex_output);

	md5_init(&state);
	md5_append(&state, (const md5_byte_t*)digest, 16);       // (MD5(user:realm:pass)
	md5_append(&state, (const md5_byte_t*)colon_t, 1);            // :
	md5_append(&state, (const md5_byte_t*)SASL_Auth_data.nonce, strlen(SASL_Auth_data.nonce));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);            // :
	md5_append(&state, (const md5_byte_t*)SASL_Auth_data.cnonce, strlen(SASL_Auth_data.cnonce));
	md5_finish(&state, A1);
	mkhex(A1, A1_HEX);

	md5_init(&state);
	md5_append(&state, (const md5_byte_t*)digest_uri, strlen(digest_uri));
	md5_finish(&state, A2);
	mkhex(A2, A2_HEX);

	md5_init(&state);
	md5_append(&state, (const md5_byte_t*)A1_HEX, strlen(A1_HEX));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);
	md5_append(&state, (const md5_byte_t*)SASL_Auth_data.nonce, strlen(SASL_Auth_data.nonce));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);
	md5_append(&state, (const md5_byte_t*)_00000001, strlen(_00000001));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);
	md5_append(&state, (const md5_byte_t*)SASL_Auth_data.cnonce, strlen(SASL_Auth_data.cnonce));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);
	md5_append(&state, (const md5_byte_t*)SASL_Auth_data.qop, strlen(SASL_Auth_data.qop));
	md5_append(&state, (const md5_byte_t*)colon_t, 1);
	md5_append(&state, (const md5_byte_t*)A2_HEX, strlen(A2_HEX));
	md5_finish(&state, Response);
	mkhex(Response, R_HEX);

	char* Response_STR = malloc(1024);
	zeromem(Response_STR, 1024);
	char Res_tpl[] =
	    "username=\"%s\",realm=\"%s\",nonce=\"%s\",nc=00000001,cnonce=\"%s\","
	    "qop=auth,digest-uri=\"xmpp/%s\",response=\"%s\",charset=utf-8";
	snprintf(Response_STR, 1024, Res_tpl,
	         USERNAME,
	         SASL_Auth_data.realm,
	         SASL_Auth_data.nonce,
	         SASL_Auth_data.cnonce,
	         JABBER_SERVER,
	         R_HEX
	        );
	char* Result_Resp = malloc(2048);
	zeromem(Result_Resp, 2048);
	Base64Encode(Response_STR, strlen(Response_STR), Result_Resp, 2048);
	const char resp_full_tpl[] = "<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>%s</response>";
	char* resp_full = malloc(1024);
	zeromem(resp_full, 1024);
	snprintf(resp_full, 1023, resp_full_tpl, Result_Resp);
	Jabber_state = JS_SASL_NEG_ANS_WAIT;
	SUBPROC((void*)_sendandfree, resp_full);
	mfree(Result_Resp);
	mfree(Response_STR);
	mfree(digest_uri);
	mfree(User_Realm_Pass);
}

// Обработка ошибок SASL
void SASL_Process_Error(XMLNode* nodeEx)
{
	Jabber_state = JS_ERROR;
	XMLNode* err = nodeEx->subnode;
	if(err)
	{
		strcpy(logmsg, "SASL error!\n");
		strcat(logmsg, err->name);  // Не юзер-френдли
		if(!strcmp(err->name, SASLERR_NOTAUTH))strcat(logmsg, "\nBad password");
	}
}
////////////////////////////////////////////////////////////////////////////////

// Запрос компрессии у сервера
void Compression_Ask()
{
	static  char zlib_ask[] = "<compress xmlns='http://jabber.org/protocol/compress'>"
	                          "<method>zlib</method>"
	                          "</compress>";
	Jabber_state = JS_ZLIB_INIT_ACK;
	SUBPROC((void*)SendAnswer, zlib_ask);
	strcat(logmsg, "\nUsing ZLib ack");
}

//EOL,EOF
