#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>

#include "logger.h"
#include "pldstr.h"
#include "boundary-stack.h"

#ifndef FL
#define FL __FILE__,__LINE__
#endif

#define BS_STRLEN_MAX 1024
#define BS_BOUNDARY_DETECT_LIMIT_DEFAULT 4

struct BS_globals {
	int debug;
	int verbose;
	int syslogging;
	int errlogging;
	int count;
	int detect_limit;
	struct BS_node *boundarystack;
	char boundarystacksafe[BS_STRLEN_MAX];
};

struct BS_node {
	char *boundary;
	int boundary_length;
	struct BS_node *next;
};


static struct BS_globals glb;





/*-----------------------------------------------------------------\
 Function Name	: BS_init
 Returns Type	: int
 	----Parameter List
	1. void , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_init( void )
{
	glb.debug = 0;
	glb.verbose = 0;
	glb.syslogging = 1;
	glb.errlogging = 0;
	glb.count = 0;
	glb.detect_limit = BS_BOUNDARY_DETECT_LIMIT_DEFAULT;
	glb.boundarystack = NULL;

	return 0;
}

/*-----------------------------------------------------------------\
 Function Name	: BS_set_verbose
 Returns Type	: int
 	----Parameter List
	1. int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_set_verbose( int level )
{
	glb.verbose = level;

	return glb.verbose;
}

/*-----------------------------------------------------------------\
 Function Name	: BS_set_debug
 Returns Type	: int
 	----Parameter List
	1. int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_set_debug( int level )
{
	glb.debug = level;

	return glb.debug;
}

/*-----------------------------------------------------------------\
 Function Name	: BS_set_boundary_detect_limit
 Returns Type	: int
 	----Parameter List
	1. int limit , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_set_boundary_detect_limit( int limit )
{
	if ((limit > 0)&&(limit < BS_STRLEN_MAX))
	{
		glb.detect_limit = limit;
	}

	return glb.detect_limit;
}


/*-----------------------------------------------------------------\
 Function Name	: BS_clear
 Returns Type	: int
 	----Parameter List
	1. void , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_clear( void )
{
	struct BS_node *next;

	while (glb.boundarystack)
	{
		next = glb.boundarystack->next;
		free(glb.boundarystack->boundary);
		free(glb.boundarystack);
		glb.boundarystack = next;
	}

	glb.count = 0;

	return 0;
}



/*-----------------------------------------------------------------\
 Function Name	: BS_push
 Returns Type	: int
 	----Parameter List
	1. char *boundary , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_push( char *boundary )
{

	struct BS_node *node = malloc(sizeof(struct BS_node));


	if (node)
	{
		node->next = glb.boundarystack;
		glb.boundarystack = node;
		glb.boundarystack->boundary = strdup(boundary);
		glb.boundarystack->boundary_length = strlen(glb.boundarystack->boundary);
		glb.count++;
	}
	else
	    {
		LOGGER_log("%s:%d:BS_push:ERROR: Cannot allocate memory for boundary stack PUSH, %s", FL, strerror(errno));
	}

	return 0;
}


/*-----------------------------------------------------------------\
 Function Name	: *BS_pop
 Returns Type	: char
 	----Parameter List
	1. void , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
char *BS_pop( void )
{

	struct BS_node *node = glb.boundarystack;

	if (glb.boundarystack)
	{
		glb.boundarystack = glb.boundarystack->next;
		PLD_strncpy(glb.boundarystacksafe,node->boundary, BS_STRLEN_MAX);
		free(node->boundary);
		free(node);
		glb.count--;
	}

	return glb.boundarystacksafe;
}

/*-----------------------------------------------------------------\
 Function Name	: *BS_top
 Returns Type	: char
 	----Parameter List
	1. void , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
char *BS_top( void )
{

	if (glb.boundarystack)
	{
		return glb.boundarystack->boundary;
	}
	else return NULL;
}

int BS_count( void )
{
	return glb.count;
}


/*-----------------------------------------------------------------\
 Function Name	: BS_boundary_detect
 Returns Type	: int
 	----Parameter List
	1. char *needle, 
	2.  char *haystack , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_boundary_detect( char *haystack, char *needle, int needle_length )
{
	int result=1;
	int current_start = glb.detect_limit;
	char *haystack_start;

	haystack_start = haystack;
	while (current_start--)
	{
		if (strncmp( needle, haystack_start, needle_length )==0)
		{
			result = 0;  
			break; 
		}
		haystack_start++;
	}

	return result;
}



/*-----------------------------------------------------------------\
 Function Name	: BS_cmp
 Returns Type	: int
 	----Parameter List
	1. char *boundary, 
	2.  int len , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int BS_cmp( char *boundary, int len )
{

	char testspace[1024];
	int testspacelen=1023;
	int spin=1;
	struct BS_node *node=glb.boundarystack;
	struct BS_node *nodetmp=NULL, *nodedel=NULL;

	if ((!boundary)||(glb.count == 0)) return 0;

	// Crop the incoming string to fit in our testspace length
	if (len > testspacelen) len = testspacelen;

	// Copy the potential boundary into our testspace
	snprintf(testspace, testspacelen, "%s", boundary);

	// First, search through the stack looking for a boundary that matches
	// our search criterion
	//
	// When we do find one, we will jump out of this WHILE loop by setting
	// 'spin' to 0.

	while((node)&&(spin))
	{
		if (node->boundary_length <= len)
		{	
			if ((BS_boundary_detect(testspace, node->boundary, node->boundary_length))==0)
			{
				spin = 0;
			}
		}

		if (spin != 0) node = node->next;
	}

	// If we have a hit on the matching, then, according
	// to nested MIME rules, we must "remove" any previous
	// boundaries
	//
	// We know that we had a HIT in matching if spin == 0, because
	// in our previous code block that's what we set spin to if
	if(spin==0)
	{

		// If our "HIT" node is /NOT/ the one on the top of the
		// stack, then we need to pop off and deallocate the nodes
		// PRIOR/Above the hit node.
		//
		// ie, if "NODE" is not the top, then pop off until we
		// do get to the node

		if (node != glb.boundarystack)
		{
			nodetmp = glb.boundarystack;
			while ((nodetmp)&&(nodetmp != node))
			{
				// - Set the node to delete (nodedel) to the current temp
				// node (notetmp)
				// - Increment the nodetmp to the next node in the stack
				// - Free the node to delete (nodedel)

				nodedel = nodetmp;
				nodetmp = nodetmp->next;
				free(nodedel);
			}
			glb.boundarystack = node;
		}
		return 1;

	}
	else
	{
		return 0;
	}

	return 0;
}



