#include "./include/radius_pub.h"
#include "./include/radius_inc.h"

#define RADIUS_DEBUG_ID	31

static char ascii[16] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
						 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};

static BYTE	log_help[] = {
"RADIUS Debug Monitor Help:\n\r\
\n\r\
1.[help]							display help menu\n\r\
2.[log all/none]					display all/none logs\n\r\
3.[log error on/off]					display error logs\n\r\
4.[log warn on/off]					display warn logs\n\r\
5.[log radius msg on/off]				display radius msg logs\n\r\
6.[list nas]						display nas information\n\r\n\r"
};

static	WORD	disp_page[10];
static	BYTE	*disp_ptr = (BYTE *) disp_page;

static 	DWORD	radius_debug_status_id[20] = 
{
	1,3,6,1,4,1,1373,1,1,2,3,1,2,RADIUS_DEBUG_ID + 2,1
};
	
static	BYTE 	radius_debug_status = 1;

static DWORD	radius_debug_name_id[20] = 
{
	1,3,6,1,4,1,1373,1,1,2,3,1,2,RADIUS_DEBUG_ID + 2,2
};
	
static DWORD	radius_debug_ascin_id[20] = 
{
	1,3,6,1,4,1,1373,1,1,2,3,1,2,RADIUS_DEBUG_ID + 2,3
};

static DWORD	radius_debug_ascout_id[20] = 
{
	1,3,6,1,4,1,1373,1,1,2,3,1,2,RADIUS_DEBUG_ID + 2,4
};
		
static DWORD	radius_debug_page_title[20] = 		
{
	1,3,6,1,4,1,1373,1,1,2,3,1,2,RADIUS_DEBUG_ID + 2,1,1
};
	
static DWORD	radius_debug_page_line[20] = 		
{
	1,3,6,1,4,1,1373,1,1,2,3,1,2,RADIUS_DEBUG_ID + 2,1,2,1
};
		
const static BYTE	RADIUS_BASE_ID_LEN = 15;
const static BYTE	RADIUS_PAGE_POINT = 14;
const static BYTE  	RADIUS_LINE_POINT 	= 15;

static BYTE	radius_title1_p[] = 
{
	"    RADIUS    Page 02   \n\r\
                   0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15\n\r\
Subpage\n\r\
RADIUS_PID\n\r"
};

BYTE radius_disp_line(BYTE page, BYTE line)
{
	BYTE	disp_length;
	
	disp_length = 16;
	disp_ptr = (BYTE *) disp_page;
	
	switch (page)
	{
		case 1:	// Page 1: pid Info
			if (line == 0)
			{
				disp_ptr = (BYTE *) &disp_page[page];
				disp_length = 2;
			}
			else
				disp_ptr = (BYTE *) radius_pid + disp_page[page] * sizeof(RADIUS_PID) + (line - 1) * 16;
			break;
		default:
			disp_length = 0;
			break;
	}
	
	return disp_length;
}

void radius_disp_page(BYTE page)
{
	BYTE	disp_line;
	BYTE	disp_length;

	radius_debug_page_line[RADIUS_PAGE_POINT] = page + 5;
	
	for (disp_line = 0; disp_line < 20; disp_line++)
	{
		disp_length = radius_disp_line(page, disp_line);
		radius_debug_page_line[RADIUS_LINE_POINT] = disp_line + 2;
		radius_debug_page_line[RADIUS_LINE_POINT + 1] = 2;	// Data Pointer
		debug_set_response(RADIUS_BASE_ID_LEN + 2, radius_debug_page_line, disp_ptr, disp_length);
	}
}

int radius_debug_set(void)
{
	BYTE	page;
	BYTE	data[10];
	BYTE 	*ptr;
	BYTE	version[16];
	
	bzero(disp_page, 20);
	
	ptr = data;

	sprintf((char*)version, "V%d.%d.%d%d", RADIUS_Version[0], RADIUS_Version[1], RADIUS_Version[2], RADIUS_Version[3]);
	debug_set_response(RADIUS_BASE_ID_LEN, radius_debug_status_id, (BYTE *)&radius_debug_status, 1);
	debug_set_response(RADIUS_BASE_ID_LEN, radius_debug_name_id, version, 10);
	debug_set_response(RADIUS_BASE_ID_LEN, radius_debug_ascin_id, radius_asci_in_buf, 4096);
	debug_set_response(RADIUS_BASE_ID_LEN, radius_debug_ascout_id, radius_asci_out_buf, 4096);
	
	for (page = 1; page < 4; page++)
	{
		switch (page)
		{
			case 1: //page 1
				ptr = radius_title1_p;
				break;
			default:
				break;				
		}
		
		radius_debug_page_title[RADIUS_PAGE_POINT] = 5 + page;
		debug_set_response(RADIUS_BASE_ID_LEN + 1, radius_debug_page_title, ptr, strlen((char *)ptr));
		radius_disp_page(page);
	}
	
	return 1;
}

int radius_asciout_proc(char *out_str)
{
	int out_len;
	
	out_len = strlen(out_str);

	radius_asci_out_buf[RADIUS_MAX_ASCIIOUT_LEN - 1] = '\0';
		
	if (out_len + strlen((char *)radius_asci_out_buf) > RADIUS_MAX_ASCIIOUT_LEN - 2)
	{
		strcpy((char *)radius_asci_out_buf, out_str);
	}
	else
	{
		strcat((char *)radius_asci_out_buf, out_str);
	}

	radius_asci_out_buf[RADIUS_MAX_ASCIIOUT_LEN - 1] = '\0';
	
	return 1;
}

void radius_list_nas()
{
	int					i;
	BYTE				tmp_buf[256] = "";
	BYTE				out_buf[2048] = "";
	struct in_addr		nas_addr;
	RADIUS_NAS_INFO	*nas_info_ptr;

	sprintf((char*)out_buf, "NAS id\tip address\t\tpassword\t\t\tshare key\r\n");

	for (i = 0; i < RADIUS_MAX_NUM_OF_NAS; i++)
	{
		nas_info_ptr = radius_get_nas_info_ptr(i);

		if (nas_info_ptr->flag != 1)
			continue;

		nas_addr.s_addr = nas_info_ptr->nas_ip;

		sprintf((char*)tmp_buf, "%d\t%s\t\t%s\t\t\t%s\r\n", i, inet_ntoa(nas_addr), nas_info_ptr->password, nas_info_ptr->share_key);
		strcat((char*)out_buf, (char*)tmp_buf);
	}

	radius_asciout_proc((char*)out_buf);
}

void radius_mon(void)
{
	BYTE				in_page, err_fg = 0;
	BYTE				*ascii_in_ptr = NULL;
	BYTE				*str_start	= NULL;
	WORD				str_len;
	WORD				num;

	if ((str_len = strlen((char *)radius_asci_in_buf)) > 0)
	{
		in_page = radius_asci_in_buf[0] - 1;
		ascii_in_ptr = radius_asci_in_buf + 1;
		
		if (in_page > 3)
			err_fg = 1;
		else if (strcmp((char *)ascii_in_ptr,"log error on") == 0)
		{
			radius_mon_flag = radius_mon_flag | RADIUS_MONITOR_ERROR;
		}
		else if (strcmp((char *)ascii_in_ptr,"log error off") == 0)
		{
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_ERROR);
		}
		else if (strcmp((char *)ascii_in_ptr,"log warn on") == 0)
		{
			radius_mon_flag = radius_mon_flag | RADIUS_MONITOR_WARN;
		}
		else if (strcmp((char *)ascii_in_ptr,"log warn off") == 0)
		{
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_WARN);
		}
		else if (strcmp((char *)ascii_in_ptr,"log radius msg on") == 0)
		{
			radius_mon_flag = radius_mon_flag | RADIUS_MONITOR_SDRV_MSG;
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_ACCESS_MSG);
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_ACCT_MSG);
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_DISC_MSG);
		}
		else if (strcmp((char *)ascii_in_ptr,"log radius msg off") == 0)
		{
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_SDRV_MSG);
		}
		else if (strcmp((char *)ascii_in_ptr,"log access msg on") == 0)
		{
			radius_mon_flag = radius_mon_flag | RADIUS_MONITOR_ACCESS_MSG;
		}
		else if (strcmp((char *)ascii_in_ptr,"log access msg off") == 0)
		{
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_ACCESS_MSG);
		}
		else if (strcmp((char *)ascii_in_ptr,"log acct msg on") == 0)
		{
			radius_mon_flag = radius_mon_flag | RADIUS_MONITOR_ACCT_MSG;
		}
		else if (strcmp((char *)ascii_in_ptr,"log acct msg off") == 0)
		{
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_ACCT_MSG);
		}
		else if (strcmp((char *)ascii_in_ptr,"log disc msg on") == 0)
		{
			radius_mon_flag = radius_mon_flag | RADIUS_MONITOR_DISC_MSG;
		}
		else if (strcmp((char *)ascii_in_ptr,"log disc msg off") == 0)
		{
			radius_mon_flag = radius_mon_flag & (~RADIUS_MONITOR_DISC_MSG);
		}
		else if (strcmp((char *)ascii_in_ptr,"log all") == 0)
		{
			radius_mon_flag = RADIUS_MONITOR_ALL;
		}
		else if (strcmp((char *)ascii_in_ptr,"log none") == 0)
		{
			radius_mon_flag = RADIUS_MONITOR_NONE;
		}
		else if (strcmp((char *)ascii_in_ptr, "list nas") == 0)
		{
			radius_list_nas();
		}
		else if (strcmp((char *)ascii_in_ptr,"help") == 0)
		{
			radius_asciout_proc((char*)log_help);
		}
		else if (isdigit(ascii_in_ptr[0]))
		{
			num = strtoul((char *)ascii_in_ptr, NULL, 10);
			disp_page[in_page] = num;
			radius_disp_page(in_page);
		}
		else if ((str_start = (BYTE *)strstr((char *)ascii_in_ptr, ">")) != NULL)
		{
			num = strtoul((char *)(str_start + 1), NULL, 10);
			disp_page[in_page] += num;
			radius_disp_page(in_page);
		}
		else if ((str_start = (BYTE *)strstr((char *)ascii_in_ptr, "<"))!= NULL)
		{
			num = strtoul((char *)(str_start + 1), NULL, 10);
			disp_page[in_page] -= num;
			radius_disp_page(in_page);
		}
		else
			err_fg = 1;
		
		if (err_fg == 0)
			radius_asciout_proc("Command OK!\n\r");
		else
			radius_asciout_proc("Command Error!\n\r");
		
		strcpy((char *)radius_asci_in_buf, "\0");
	}
}

int radius_hex_to_ascii(BYTE *from_hex, BYTE from_len, BYTE *to_asc)
{
	int	i, j;
	
	j = 0;
	
	for (i = 0; i< from_len; i++)
	{
		to_asc[j] = ascii[from_hex[i] >> 4];
		j++;
		to_asc[j] = ascii[from_hex[i] & 0x0F];
		j++;
		to_asc[j] = ' ';
		j++;
	}
	
	to_asc[j] = '\0';
	
	return 1;
}

int radius_log_err(const char *fmt, ...)
{
	va_list			ap;
	char				err_msg[2048];
	char				time_str[1024];
	struct tm		*t;
	struct timeval	tv;
	struct timezone	tz;

	if ((radius_mon_flag & RADIUS_MONITOR_ERROR) == RADIUS_MONITOR_ERROR)
	{
		gettimeofday(&tv, &tz);
		t = localtime(&tv.tv_sec);
		sprintf(time_str, "[%d:%d:%d.%ld]:", t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec);

		va_start(ap, fmt);
		vsprintf(err_msg, fmt, ap);

		if (strlen((char *)err_msg) >= RADIUS_MAX_ASCITMP_LEN)
			sprintf((char *)radius_asci_tmp_buf, "\33[31m%slog msg is too long: %zu\r\n", time_str, strlen((char *)err_msg));
		else
			sprintf((char *)radius_asci_tmp_buf, "\33[31m%s%s\r\n", time_str, err_msg);

		strcat((char *)radius_asci_tmp_buf, "\33[37m");

		radius_asciout_proc((char*)radius_asci_tmp_buf);
		va_end(ap);
	}
	
	return 0;
}


int radius_log_event(const char *fmt, ...)
{
	char				time_str[1024];
	char				msg[2048];
	va_list			ap;
	struct tm		*t;
	struct timeval	tv;
	struct timezone	tz;

	if ((radius_mon_flag & RADIUS_MONITOR_EVENT) == RADIUS_MONITOR_EVENT)
	{
		gettimeofday(&tv, &tz);
		t = localtime(&tv.tv_sec);
		sprintf(time_str, "[%d:%d:%d.%ld]:", t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec);

		va_start(ap, fmt);
		vsprintf(msg, fmt, ap);

		radius_asciout_proc("\33[34m");

		if ((strlen((char *)msg) + strlen(time_str)) >= RADIUS_MAX_ASCITMP_LEN)
			sprintf((char *)radius_asci_tmp_buf, "%s log msg is too long: %zu\n\r", time_str,  (strlen((char *)msg) + strlen(time_str)));
		else
			sprintf((char *)radius_asci_tmp_buf, "%s%s\n\r", time_str, msg);

		strcat((char *)radius_asci_tmp_buf, "\33[37m");
		radius_asciout_proc((char *)radius_asci_tmp_buf);

		va_end(ap);
	}

	return 0;
}

int radius_log_warn(const char *fmt, ...)
{

	char				timestr[1024];
	char				msg[2048];
	va_list			ap;
	struct tm 		*t;
	struct timeval 	tv;
	struct timezone 	tz;

	if ((radius_mon_flag & RADIUS_MONITOR_WARN) == RADIUS_MONITOR_WARN)
	{
		gettimeofday(&tv, &tz);
		t = localtime(&tv.tv_sec);
		sprintf(timestr, "[%d:%d:%d.%ld]:", t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec);

		va_start(ap, fmt);
		vsprintf(msg, fmt, ap);

		radius_asciout_proc("\33[33m");

		if ((strlen((char *)msg) + strlen(timestr)) >= RADIUS_MAX_ASCITMP_LEN)
			sprintf((char *)radius_asci_tmp_buf, "%s log msg is too long: %zu\n\r", timestr,  (strlen((char *)msg) + strlen(timestr)));
		else
			sprintf((char *)radius_asci_tmp_buf, "%s%s\n\r", timestr, msg);

		strcat((char *)radius_asci_tmp_buf, "\33[37m");
		radius_asciout_proc((char *)radius_asci_tmp_buf);

		va_end(ap);
	}

	return 0;
}

int radius_log_send_recv_msg(BYTE *msg, int len, RADIUS_ADDR *addr, BYTE msg_type, BYTE flag)
{
	struct tm			*t;
	struct timeval		tv;
	struct timezone		tz;
	char					str[RADIUS_MAX_LEN_OF_MSG] = "";
	char					time_str[1024];
	struct sockaddr_in		src_addr;
	BYTE				log_flag = 0;

	switch (msg_type)
	{
	case RADIUS_MSG_TYPE_ACCESS_ACCEPT:
	case RADIUS_MSG_TYPE_ACCESS_REQUEST:
	case RADIUS_MSG_TYPE_ACCESS_REJECT:
	case RADIUS_MSG_TYPE_ACCESS_CHALLENGE:
		if ((radius_mon_flag & RADIUS_MONITOR_ACCESS_MSG) == RADIUS_MONITOR_ACCESS_MSG)
			log_flag = 1;
		break;
	case RADIUS_MSG_TYPE_ACCOUNTING_REQUEST:
	case RADIUS_MSG_TYPE_ACCOUNTING_RESPONSE:
		if ((radius_mon_flag & RADIUS_MONITOR_ACCT_MSG) == RADIUS_MONITOR_ACCT_MSG)
			log_flag = 1;
		break;
	case RADIUS_MSG_TYPE_DISCONNECT_REQUEST:
	case RADIUS_MSG_TYPE_DISCONNECT_ACK:
	case RADIUS_MSG_TYPE_DISCONNECT_NAK:
		if ((radius_mon_flag & RADIUS_MONITOR_DISC_MSG) == RADIUS_MONITOR_DISC_MSG)
			log_flag = 1;
		break;
	default:
		if ((radius_mon_flag & RADIUS_MONITOR_SDRV_MSG) == RADIUS_MONITOR_SDRV_MSG)
			log_flag = 1;
		break;
	}

	if (log_flag == 1)
	{
		gettimeofday(&tv, &tz);
		t = localtime(&tv.tv_sec);
		sprintf(time_str, "[%d:%d:%d.%ld]:", t->tm_hour, t->tm_min, t->tm_sec, tv.tv_usec);

		radius_msg_to_fmt(msg, len, str, RADIUS_MAX_LEN_OF_MSG);

		radius_asciout_proc("\33[34m");

		if ((strlen(str) + strlen(time_str) + 64) >= RADIUS_MAX_ASCITMP_LEN)
			sprintf((char *)radius_asci_tmp_buf, "%s log msg is too long: %zu\n\r", time_str,  (strlen(str) + strlen(time_str)));
		else
		{
			src_addr.sin_addr.s_addr = addr->addr.ipV4;
			if (flag == RADIUS_RECV_MSG_FLAG)
			{
				sprintf((char *)radius_asci_tmp_buf, "%sRecieve Msg<===src port: %d, src ip: %s\r\n%s\n\r", time_str, addr->port, inet_ntoa(src_addr.sin_addr), str);
			}
			else
			{
				sprintf((char *)radius_asci_tmp_buf, "%sSend Msg===>dest port: %d, dest ip: %s\r\n%s\n\r", time_str, addr->port, inet_ntoa(src_addr.sin_addr), str);
			}
		}

		strcat((char *)radius_asci_tmp_buf, "\33[37m");
		radius_asciout_proc((char *)radius_asci_tmp_buf);
	}

	return 0;
}

