#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <openssl/md5.h>
#include <sys/stat.h>
#include <openssl/evp.h>

#include "Maat_utils.h"
pid_t gettid()
{
	return syscall(SYS_gettid);
}
const char*	module_name_str(const char*name)
{
	static __thread char module[64];
	snprintf(module,sizeof(module),"%s(%d)", name, gettid());
	return module;
}

int converHextoint(char srctmp)
{
	if(isdigit(srctmp))
	{
		return srctmp-'0';
	}
	else
	{
		char temp=toupper(srctmp);
		temp=temp-'A'+10;
		return temp;
	}
}
int  hex2bin(char *hex,int hex_len,char *binary,int size)
{
	int i=0;
	int resultlen=0;
	int high,low;
	for(i=0;i<hex_len&&size>resultlen; i+=2,resultlen++)
	{
		high=converHextoint(hex[i]);
		low=converHextoint(hex[i+1]);
		binary[resultlen]=high*16+low;
	}
	size=resultlen;
	binary[resultlen]='\0';
	return resultlen;
}
//functioned as strdup, for dictator compatible.
char* _maat_strdup(const char* s)
{
	char*d=NULL;
	if(s==NULL)
	{
		return NULL;
	}
	d=(char*)malloc(strlen(s)+1);
	memcpy(d,s,strlen(s)+1);
	return d;
}
char* str_tolower(char* string)
{
	int i=0;
	for(i=0;i<(int)strlen(string);i++)
	{
		string[i]=(char)tolower(string[i]);
	}
	return string;
}
char * strchr_esc(char* s,const char delim)
{
	char *token;
	if(s==NULL)
		return NULL;
	for(token=s;*token!='\0';token++)
	{
		if(*token=='\\')
		{
			token++;
			continue;
		}
		if(*token==delim)
			break;
	}
	if (*token == '\0')
	{ 
		return NULL;
	}
	else
	{
		return token;
	}
}
char *strtok_r_esc(char *s, const char delim, char **save_ptr)
{  
    char *token;  
  
    if (s == NULL) s = *save_ptr;  
  
    /* Scan leading delimiters.  */
	token=strchr_esc(s,delim);
	if(token==NULL)
	{
		*save_ptr=token;
		return s;
	}
    /* Find the end of the token.  */  
	*token='\0';
	token++;
	*save_ptr=token;

    return s;  
}   
char *str_unescape_and(char*s)
{
	int i=0,j=0;
	for(i=0,j=0;i<(int)strlen(s);i++)
	{
		if(s[i]=='\\'&&s[i+1]=='&')
		{
			s[j]='&';
			i++;
			j++;
		}
		else{
			s[j]=s[i];
			j++;
		}
	}
	s[j]='\0';
	return s;
}
char* str_unescape(char* s)
{
	int i=0,j=0;
	int len=strlen(s);
	for(i=0,j=0;i<len;i++)
	{
		if(s[i]=='\\')
		{
			switch(s[i+1])
			{
				case '&':
					s[j]='&';
					break;
				case 'b':
					s[j]=' ';//space,0x20;
					break;
				case '\\':
					s[j]='\\';
					break;
				default:
					s[j]=s[i];
					i--;	//undo the followed i++
					break;
			}
			i++;
			j++;
		}
		else
		{
			s[j]=s[i];
			j++;
		}
	}
	s[j]='\0';
	return s;
}
size_t memcat(void**dest, size_t offset, size_t *n_dest, const void* src, size_t n_src)
{
	if(*n_dest<offset+n_src)
	{
		*n_dest=(offset+n_src)*2;
		*dest=realloc(*dest, sizeof(char)*(*n_dest));
	}
	memcpy((char*)*dest+offset, src, n_src);
	return n_src;
}

int get_column_pos(const char* line, int column_seq, size_t *offset, size_t *len)
{
	const char* seps=" \t";
	char* saveptr=NULL, *subtoken=NULL, *str=NULL;
	char* dup_line=_maat_strdup(line);
	int i=0, ret=-1;
	for (str = dup_line; ; str = NULL)
	{
		subtoken = strtok_r(str, seps, &saveptr);
		if (subtoken == NULL)
			break;
		if(i==column_seq-1)
		{
			*offset=subtoken-dup_line;
			*len=strlen(subtoken);
			ret=0;
			break;
		}
		i++;
	}
	free(dup_line);
	return ret;
}

#define MAX_SYSTEM_CMD_LEN	512

int system_cmd_mkdir(const char* path)
{
	char cmd[MAX_SYSTEM_CMD_LEN] = { 0 };
	snprintf(cmd, sizeof(cmd),"mkdir -p %s", path);
	return  system(cmd) ;
}
int system_cmd_mv(const char* src_file,const char*dst_file)
{
	char cmd[MAX_SYSTEM_CMD_LEN] = { 0 };
	snprintf(cmd, sizeof(cmd), "mv %s %s", src_file, dst_file);
	return  system(cmd);
}
int system_cmd_cp(const char* src_file,const char*dst_file)
{
	char cmd[MAX_SYSTEM_CMD_LEN] = { 0 };
	snprintf(cmd, sizeof(cmd), "cp -f %s %s", src_file, dst_file);
	return  system(cmd);
}
int system_cmd_encrypt(const char* src_file, const char* dst_file, const char* password)
{
	char cmd[MAX_SYSTEM_CMD_LEN] = { 0 };
	snprintf(cmd,sizeof(cmd), "openssl enc -e -aes-256-cbc -k %s -p -nosalt -in %s -out %s", password, src_file, dst_file);
	return  system(cmd);
}
int system_cmd_rm(const char* src_file)
{
	char cmd[MAX_SYSTEM_CMD_LEN] = { 0 };
	snprintf(cmd,sizeof(cmd), "rm %s -f", src_file);
	return  system(cmd);
}
char* md5_file(const char* filename, char* md5string)
{
	FILE* fp=NULL;
	int i=0;
	unsigned char md5[MD5_DIGEST_LENGTH];
	struct stat file_info;	
	stat(filename, &file_info);
	size_t file_size=file_info.st_size;

	fp=fopen(filename,"r");
	if(fp==NULL)
	{
		return NULL;
	}
	char* file_buff=(char*)malloc(file_size);
	fread(file_buff,1,file_size,fp);
	fclose(fp);
	
	MD5((const unsigned char *)(file_buff), (unsigned long)(file_size), md5);
	for(i = 0; i < MD5_DIGEST_LENGTH; ++i)
	{
		sprintf(&md5string[i*2], "%02x", (unsigned int)md5[i]);
	}
	free(file_buff);
	return md5string;
}
const char* CHARSET_STRING[]={"NONE","gbk","big5","unicode","utf8","bin",
							"unicode_ascii_esc","unicode_ascii_aligned","unicode_ncr_dec","unicode_ncr_hex","url_encode_gb2312","url_encode_utf8", "windows-1251", ""};

const char** charset_get_all_name(void)
{
	return CHARSET_STRING;
}
const char* charset_get_name(enum MAAT_CHARSET charset)
{
	return CHARSET_STRING[charset];
}
int lqueue_destroy_cb(void *data, long data_len, void *arg)
{
	assert(0);
	return 0;
}

int crypt_memory(const unsigned char* inbuf, size_t inlen, unsigned char** pp_out, size_t *out_sz, const char* key, const char* algorithm, int do_encrypt, char* err_str, size_t err_str_sz)
{
	int ret=0, out_blk_len=0;
	int out_buff_len=0, out_buff_offset=0;
	EVP_CIPHER_CTX *ctx;

	unsigned char cipher_key[EVP_MAX_KEY_LENGTH];
	unsigned char cipher_iv[EVP_MAX_IV_LENGTH];
	memset(cipher_key,0,sizeof(cipher_key));
	memset(cipher_iv,0,sizeof(cipher_iv));

	const EVP_CIPHER *cipher;
	const EVP_MD *dgst=NULL;
	const unsigned char *salt=NULL;

	OpenSSL_add_all_algorithms();
	cipher=EVP_get_cipherbyname(algorithm);
	if(cipher==NULL)
	{
		snprintf(err_str, err_str_sz, "Cipher %s is not supported.", algorithm);
		return 0;
	}
	dgst=EVP_get_digestbyname("md5");
	if(dgst==NULL)
	{
		snprintf(err_str, err_str_sz, "Get MD5 object failed.");
		return 0;
	}
	ret=EVP_BytesToKey(cipher, dgst, salt, (unsigned char*)key, strlen((const char*)key), 1, cipher_key,  cipher_iv);
	if(ret==0)
	{
		snprintf(err_str, err_str_sz, "Key and IV generatioin failed.");
		return 0;
	}
     /* Don't set key or IV right away; we want to check lengths */
	ctx = EVP_CIPHER_CTX_new();
	EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, do_encrypt);
	OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) % 16==0);
	OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);
	 
	/* Now we can set key and IV */
	//It should be set to 1 for encryption, 0 for decryption and -1 to leave the value unchanged (the actual value of 'enc' being supplied in a previous call).
	EVP_CipherInit_ex(ctx, NULL, NULL, cipher_key, cipher_iv, -1);
	out_buff_len=inlen+EVP_CIPHER_block_size(cipher)-1;
	*pp_out=(unsigned char*)malloc(out_buff_len*sizeof(unsigned char));
	if (!EVP_CipherUpdate(ctx, *pp_out+out_buff_offset, &out_blk_len, inbuf, inlen))
	{
		snprintf(err_str, err_str_sz, "EVP_CipherUpdate failed.");
		EVP_CIPHER_CTX_free(ctx);
		goto error_out;
	}
	out_buff_offset+=out_blk_len;
	if (!EVP_CipherFinal_ex(ctx, *pp_out+out_buff_offset, &out_blk_len))
	{
		snprintf(err_str, err_str_sz, "EVP_CipherFinal_ex failed. Maybe password is wrong?");
		EVP_CIPHER_CTX_free(ctx);
		goto error_out;
	}
	out_buff_offset+=out_blk_len;
	EVP_CIPHER_CTX_free(ctx);
	*out_sz=out_buff_offset;
	return 0;	
	
error_out:	
	free(*pp_out);
	*pp_out=NULL;
	return -1;
}
int load_file_to_memory(const char* file_name, unsigned char**pp_out, size_t *out_sz)
{
	int ret=0;
	FILE* fp=NULL;		
	struct stat fstat_buf;
	size_t read_size=0;
	
	ret=stat(file_name, &fstat_buf);
	if(ret!=0)
	{
		return -1;
	}
	fp=fopen(file_name, "r");
	if(fp==NULL)
	{
		return -1;
	}
	*out_sz=fstat_buf.st_size;
	*pp_out=(unsigned char*)calloc(1, *out_sz+1);
	read_size=fread(*pp_out, 1, *out_sz, fp);
	if(read_size!= *out_sz)
	{
		free(*pp_out);
		pp_out=NULL;
		return -1;
	}	
	
	fclose(fp);
	fp=NULL;
	return 0;
}
int decrypt_open(const char* file_name, const char* key, const char* algorithm, unsigned char**pp_out, size_t *out_sz, char* err_str, size_t err_str_sz)
{
	int ret=0;
	size_t file_sz=0;
	unsigned char* file_buff=NULL;
	ret=load_file_to_memory(file_name, &file_buff, &file_sz);
	if(ret<0)
	{
		return -1;
	}
	ret=crypt_memory(file_buff, file_sz, pp_out, out_sz, key, algorithm, 0, err_str, err_str_sz);
	free(file_buff);
	file_buff=NULL;
	return ret;
}
enum MAAT_IP_FORMAT ip_format_str2int(const char* format)
{
	if(0==strcasecmp(format, "range"))
	{
		return FORMAT_RANGE;
	}
	else if(0==strcasecmp(format, "mask"))
	{
		return FORMAT_MASK;
	}
	else if(0==strcasecmp(format, "CIDR"))
	{
		return FORMAT_CIDR;
	}
	else
	{
		assert(0);
	}
	return FORMAT_UNKNOWN;
}
int ip_format2range(int ip_type, enum MAAT_IP_FORMAT format, const char* ip1, const char* ip2, unsigned int range_begin[], unsigned int range_end[])
{
	unsigned int ipv4_addr=0, ipv4_mask=0, ipv4_range_end=0;
	unsigned int ipv6_addr[4]={0}, ipv6_mask[4]={0}, ipv6_range_end[4]={0};
	int cidr=0, bit32=0;
	int ret=0, i=0;
	if(ip_type!=4 && ip_type!=6)
	{
		assert(0);
		return -1;
	}
	if(ip_type==4)
	{
		ret=inet_pton(AF_INET, ip1, &ipv4_addr);
		if(ret<=0)
		{
			return -1;
		}
		ipv4_addr=ntohl(ipv4_addr);
		switch (format)
		{
			case FORMAT_RANGE:
				range_begin[0]=ipv4_addr;
				ret=inet_pton(AF_INET, ip2, &ipv4_range_end);
				if(ret<=0)
				{
					return -1;
				}				
				ipv4_range_end=ntohl(ipv4_range_end);
				range_end[0]=ipv4_range_end;
				break;
			case FORMAT_MASK:
				ret=inet_pton(AF_INET, ip2, &ipv4_mask);
				if(ret<=0)
				{
					return -1;
				}
				ipv4_mask=ntohl(ipv4_mask);
				range_begin[0]=ipv4_addr&ipv4_mask;
				range_end[0]=ipv4_addr|~ipv4_mask;
				break;
			case FORMAT_CIDR:
				cidr=atoi(ip2);
				if(cidr>32||cidr<0)
				{
					return -1;
				}
				ipv4_mask = (0xFFFFFFFFUL << (32 - cidr)) & 0xFFFFFFFFUL;
				range_begin[0]=ipv4_addr&ipv4_mask;
				range_end[0]=ipv4_addr|~ipv4_mask;
				break;
			default:
				assert(0);
		}
	}
	else	//ipv6
	{
		ret=inet_pton(AF_INET6, ip1, ipv6_addr);
		if(ret<=0)
		{
			return -1;
		}
		ipv6_ntoh(ipv6_addr);
		switch(format)
		{
			case FORMAT_RANGE:
				ret=inet_pton(AF_INET6, ip2, ipv6_range_end);
				if(ret<=0)
				{
					return -1;
				}
				ipv6_ntoh(ipv6_range_end);
				memcpy(range_begin, ipv6_addr, sizeof(ipv6_addr));				
				memcpy(range_end, ipv6_range_end, sizeof(ipv6_range_end));
				break;
			case FORMAT_MASK:
				ret=inet_pton(AF_INET6, ip2, ipv6_mask);
				if(ret<=0)
				{
					return -1;
				}
				ipv6_ntoh(ipv6_mask);
				for(i=0; i<4; i++)
				{
					range_begin[i]=ipv6_addr[i]&ipv6_mask[i];	
					range_end[i] = ipv6_addr[i]|~ipv6_mask[i];
				}
				break;
			case FORMAT_CIDR:
				cidr=atoi(ip2);
				if(cidr>128||cidr<0)
				{
					return -1;
				}
				for(i=0; i<4; i++)
				{
					bit32=128-cidr-32*(3-i);
					if(bit32<0) bit32=0;
					ipv6_mask[i]=(0xFFFFFFFFUL << bit32) & 0xFFFFFFFFUL;
					range_begin[i]=ipv6_addr[i]&ipv6_mask[i];	
					range_end[i] = ipv6_addr[i]|~ipv6_mask[i];
				}
				break;
			default:
				assert(0);
		}
	}
	return 0;
}

