#include "../include/config.h"
#include "../include/common.h"
#include "../include/locations.h"
#include "../include/defaults.h"

/*
 * This file holds random utility functions shared by cgi's and
 * core, as well as all global variables needed by both.
 */
int date_format;
int interval_length;
char *illegal_output_chars;
char illegal_output_char_map[] = CHAR_MAP_INIT(0);
time_t program_start = 0L;
int check_external_commands;
int log_rotation_method;

char *object_cache_file;
struct object_count num_objects;

int process_performance_data;
char *status_file;

int nagios_pid = 0;
int daemon_mode = FALSE;

time_t last_log_rotation = 0L;

int check_external_commands;

int enable_timing_point = FALSE; /* cgi's never set this to TRUE */

int enable_flap_detection;
int enable_notifications;
int execute_service_checks;
int accept_passive_service_checks;
int execute_host_checks;
int accept_passive_host_checks;
int enable_event_handlers;
int obsess_over_services;
int obsess_over_hosts;

char *config_file_dir = NULL;

void init_shared_cfg_vars(int first_time) {
	date_format = DATE_FORMAT_US;
	interval_length = DEFAULT_INTERVAL_LENGTH;
	if(first_time) {
		/* Not sure why these are not reset in reset_variables() */
		illegal_output_chars = NULL;
	}
	program_start = 0L;
	log_rotation_method = LOG_ROTATION_NONE;

	object_cache_file = strdup(DEFAULT_OBJECT_CACHE_FILE);

	process_performance_data = DEFAULT_PROCESS_PERFORMANCE_DATA;
	status_file = NULL;

	check_external_commands = DEFAULT_CHECK_EXTERNAL_COMMANDS;

	enable_flap_detection = DEFAULT_ENABLE_FLAP_DETECTION;
	enable_notifications = TRUE;
	execute_service_checks = TRUE;
	accept_passive_service_checks = TRUE;
	execute_host_checks = TRUE;
	accept_passive_host_checks = TRUE;
	enable_event_handlers = TRUE;
	obsess_over_services = FALSE;
	obsess_over_hosts = FALSE;

	return;
}


/* silly debug-ish helper used to track down hotspots in config parsing */
void timing_point(const char *fmt, ...) {
	static struct timeval last = {0, 0}, first = {0, 0};
	struct timeval now;
	va_list ap;

	if(!enable_timing_point)
		return;

	if(first.tv_sec == 0) {
		gettimeofday(&first, NULL);
		last.tv_sec = first.tv_sec;
		last.tv_usec = first.tv_usec;
		printf("[0.0000 (+0.0000)] ");
		}
	else {
		gettimeofday(&now, NULL);
		printf("[%.4f (+%.4f)] ", tv_delta_f(&first, &now), tv_delta_f(&last, &now));
		last.tv_sec = now.tv_sec;
		last.tv_usec = now.tv_usec;
		}
	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
	}

/* fix the problem with strtok() skipping empty options between tokens */
char *my_strtok(char *buffer, const char *tokens) {
	char *token_position = NULL;
	char *sequence_head = NULL;
	static char *my_strtok_buffer = NULL;
	static char *original_my_strtok_buffer = NULL;

	if(buffer != NULL) {
		my_free(original_my_strtok_buffer);
		if((my_strtok_buffer = (char *)strdup(buffer)) == NULL)
			return NULL;
		original_my_strtok_buffer = my_strtok_buffer;
		}

	sequence_head = my_strtok_buffer;

	if(sequence_head[0] == '\x0')
		return NULL;

	token_position = strchr(my_strtok_buffer, tokens[0]);

	if(token_position == NULL) {
		my_strtok_buffer = strchr(my_strtok_buffer, '\x0');
		return sequence_head;
		}

	token_position[0] = '\x0';
	my_strtok_buffer = token_position + 1;

	return sequence_head;
	}

/* fix the problem with my_strtok() strduping and causing intermittent memory leaks
 * use as regular my_strtok, specifying FALSE for free_orig
 * when done (before calling again), specify TRUE for free_orig for it to handle the free() */
char *my_strtok_with_free(char *buffer, const char *tokens, int free_orig) {
	char *token_position = NULL;
	char *sequence_head = NULL;
	static char *my_strtok_buffer = NULL;
	static char *original_my_strtok_buffer = NULL;

	if(buffer != NULL) {
		my_free(original_my_strtok_buffer);
		if((my_strtok_buffer = (char *)strdup(buffer)) == NULL)
			return NULL;
		original_my_strtok_buffer = my_strtok_buffer;
		}
	else if (free_orig == TRUE) {
		my_free(original_my_strtok_buffer);
		return NULL;
		}

	sequence_head = my_strtok_buffer;

	if(sequence_head[0] == '\x0')
		return NULL;

	token_position = strchr(my_strtok_buffer, tokens[0]);

	if(token_position == NULL) {
		my_strtok_buffer = strchr(my_strtok_buffer, '\x0');
		return sequence_head;
		}

	token_position[0] = '\x0';
	my_strtok_buffer = token_position + 1;

	return sequence_head;
	}

/* fixes compiler problems under Solaris, since strsep() isn't included */
/* this code is taken from the glibc source */
char *my_strsep(char **stringp, const char *delim) {
	char *begin, *end;

	begin = *stringp;
	if(begin == NULL)
		return NULL;

	/* A frequent case is when the delimiter string contains only one
	 * character.  Here we don't need to call the expensive `strpbrk'
	 * function and instead work using `strchr'.  */
	if(delim[0] == '\0' || delim[1] == '\0') {
		char ch = delim[0];

		if(ch == '\0' || begin[0] == '\0')
			end = NULL;
		else {
			if(*begin == ch)
				end = begin;
			else
				end = strchr(begin + 1, ch);
			}
		}
	else {
		/* find the end of the token.  */
		end = strpbrk(begin, delim);
		}

	if(end) {
		/* terminate the token and set *STRINGP past NUL character.  */
		*end++ = '\0';
		*stringp = end;
		}
	else
		/* no more delimiters; this is the last token.  */
		*stringp = NULL;

	return begin;
	}

/* open a file read-only via mmap() */
mmapfile *mmap_fopen(const char *filename) {
	mmapfile *new_mmapfile = NULL;
	int fd = 0;
	void *mmap_buf = NULL;
	struct stat statbuf;
	int mode = O_RDONLY;
	unsigned long file_size = 0L;

	if(filename == NULL)
		return NULL;

	/* allocate memory */
	if((new_mmapfile = (mmapfile *) malloc(sizeof(mmapfile))) == NULL)
		return NULL;

	/* open the file */
	if((fd = open(filename, mode)) == -1) {
		my_free(new_mmapfile);
		return NULL;
		}

	/* get file info */
	if((fstat(fd, &statbuf)) == -1) {
		close(fd);
		my_free(new_mmapfile);
		return NULL;
		}

	/* get file size */
	file_size = (unsigned long)statbuf.st_size;

	/* only mmap() if we have a file greater than 0 bytes */
	if(file_size > 0) {

		/* mmap() the file - allocate one extra byte for processing zero-byte files */
		if((mmap_buf =
		            (void *)mmap(0, file_size, PROT_READ, MAP_PRIVATE, fd,
		                         0)) == MAP_FAILED) {
			close(fd);
			my_free(new_mmapfile);
			return NULL;
			}
		}
	else
		mmap_buf = NULL;

	/* populate struct info for later use */
	new_mmapfile->path = (char *)strdup(filename);
	new_mmapfile->fd = fd;
	new_mmapfile->file_size = (unsigned long)file_size;
	new_mmapfile->current_position = 0L;
	new_mmapfile->current_line = 0L;
	new_mmapfile->mmap_buf = mmap_buf;

	return new_mmapfile;
	}

/* close a file originally opened via mmap() */
int mmap_fclose(mmapfile * temp_mmapfile) {

	if(temp_mmapfile == NULL)
		return ERROR;

	/* un-mmap() the file */
	if(temp_mmapfile->file_size > 0L)
		munmap(temp_mmapfile->mmap_buf, temp_mmapfile->file_size);

	/* close the file */
	close(temp_mmapfile->fd);

	/* free memory */
	my_free(temp_mmapfile->path);
	my_free(temp_mmapfile);

	return OK;
	}

/* gets one line of input from an mmap()'ed file */
char *mmap_fgets(mmapfile * temp_mmapfile) {
	char *buf = NULL;
	unsigned long x = 0L;
	int len = 0;

	if(temp_mmapfile == NULL)
		return NULL;

	/* size of file is 0 bytes */
	if(temp_mmapfile->file_size == 0L)
		return NULL;

	/* we've reached the end of the file */
	if(temp_mmapfile->current_position >= temp_mmapfile->file_size)
		return NULL;

	/* find the end of the string (or buffer) */
	for(x = temp_mmapfile->current_position; x < temp_mmapfile->file_size;
	        x++) {
		if(*((char *)(temp_mmapfile->mmap_buf) + x) == '\n') {
			x++;
			break;
			}
		}

	/* calculate length of line we just read */
	len = (int)(x - temp_mmapfile->current_position);

	/* allocate memory for the new line */
	if((buf = (char *)malloc(len + 1)) == NULL)
		return NULL;

	/* copy string to newly allocated memory and terminate the string */
	memcpy(buf,
	       ((char *)(temp_mmapfile->mmap_buf) +
	        temp_mmapfile->current_position), len);
	buf[len] = '\x0';

	/* update the current position */
	temp_mmapfile->current_position = x;

	/* increment the current line */
	temp_mmapfile->current_line++;

	return buf;
	}

/* gets one line of input from an mmap()'ed file (may be contained on more than one line in the source file) */
char *mmap_fgets_multiline(mmapfile * temp_mmapfile) {
	char *buf = NULL;
	char *tempbuf = NULL;
	char *stripped = NULL;
	int len = 0;
	int len2 = 0;
	int end = 0;

	if(temp_mmapfile == NULL)
		return NULL;

	while(1) {

		my_free(tempbuf);

		if((tempbuf = mmap_fgets(temp_mmapfile)) == NULL)
			break;

		if(buf == NULL) {
			len = strlen(tempbuf);
			if((buf = (char *)malloc(len + 1)) == NULL)
				break;
			memcpy(buf, tempbuf, len);
			buf[len] = '\x0';
			}
		else {
			/* strip leading white space from continuation lines */
			stripped = tempbuf;
			while(*stripped == ' ' || *stripped == '\t')
				stripped++;
			len = strlen(stripped);
			len2 = strlen(buf);
			if((buf =
			            (char *)realloc(buf, len + len2 + 1)) == NULL)
				break;
			strcat(buf, stripped);
			len += len2;
			buf[len] = '\x0';
			}

		if(len == 0)
			break;

		/* handle Windows/DOS CR/LF */
		if(len >= 2 && buf[len - 2] == '\r')
			end = len - 3;
		/* normal Unix LF */
		else if(len >= 1 && buf[len - 1] == '\n')
			end = len - 2;
		else
			end = len - 1;

		/* two backslashes found. unescape first backslash first and break */
		if(end >= 1 && buf[end - 1] == '\\' && buf[end] == '\\') {
			buf[end] = '\n';
			buf[end + 1] = '\x0';
			break;
			}

		/* one backslash found. continue reading the next line */
		else if(end > 0 && buf[end] == '\\')
			buf[end] = '\x0';

		/* no continuation marker was found, so break */
		else
			break;
		}

	my_free(tempbuf);

	return buf;
	}

/* strip newline, carriage return, and tab characters from beginning and end of a string */
void strip(char *buffer) {
	register int x, z;
	int len;

	if(buffer == NULL || buffer[0] == '\x0')
		return;

	/* strip end of string */
	len = (int)strlen(buffer);
	for(x = len - 1; x >= 0; x--) {
		switch(buffer[x]) {
			case ' ':
			case '\n':
			case '\r':
			case '\t':
				buffer[x] = '\x0';
				continue;
			}
		break;
		}

	/* if we stripped all of it, just return */
	if(!x)
		return;

	/* save last position for later... */
	z = x;

	/* strip beginning of string (by shifting) */
	/* NOTE: this is very expensive to do, so avoid it whenever possible */
	for(x = 0;; x++) {
		switch(buffer[x]) {
			case ' ':
			case '\n':
			case '\r':
			case '\t':
				continue;
			}
		break;
		}

	if(x > 0 && z > 0) {
		/* new length of the string after we stripped the end */
		len = z + 1;

		/* shift chars towards beginning of string to remove leading whitespace */
		for(z = x; z < len; z++)
			buffer[z - x] = buffer[z];
		buffer[len - x] = '\x0';
		}
	}

/**************************************************
 *************** HASH FUNCTIONS *******************
 **************************************************/
/* dual hash function */
int hashfunc(const char *name1, const char *name2, int hashslots) {
	unsigned int i, result;

	result = 0;

	if(name1)
		for(i = 0; i < strlen(name1); i++)
			result += name1[i];

	if(name2)
		for(i = 0; i < strlen(name2); i++)
			result += name2[i];

	result = result % hashslots;

	return result;
	}

/* dual hash data comparison */
int compare_hashdata(const char *val1a, const char *val1b, const char *val2a,
                     const char *val2b) {
	int result = 0;

	/* NOTE: If hash calculation changes, update the compare_strings() function! */

	/* check first name */
	if(val1a == NULL && val2a == NULL)
		result = 0;
	else if(val1a == NULL)
		result = 1;
	else if(val2a == NULL)
		result = -1;
	else
		result = strcmp(val1a, val2a);

	/* check second name if necessary */
	if(result == 0) {
		if(val1b == NULL && val2b == NULL)
			result = 0;
		else if(val1b == NULL)
			result = 1;
		else if(val2b == NULL)
			result = -1;
		else
			result = strcmp(val1b, val2b);
		}

	return result;
	}
/*
 * given a date/time in time_t format, produce a corresponding
 * date/time string, including timezone
 */
void get_datetime_string(time_t * raw_time, char *buffer, int buffer_length,
                         int type) {
	time_t t;
	struct tm *tm_ptr, tm_s;
	int hour;
	int minute;
	int second;
	int month;
	int day;
	int year;
	const char *weekdays[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
	const char *months[12] = {
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
		"Oct", "Nov", "Dec"
		};
	const char *tzone = "";

	if(raw_time == NULL)
		time(&t);
	else
		t = *raw_time;

	if(type == HTTP_DATE_TIME)
		tm_ptr = gmtime_r(&t, &tm_s);
	else
		tm_ptr = localtime_r(&t, &tm_s);

	hour = tm_ptr->tm_hour;
	minute = tm_ptr->tm_min;
	second = tm_ptr->tm_sec;
	month = tm_ptr->tm_mon + 1;
	day = tm_ptr->tm_mday;
	year = tm_ptr->tm_year + 1900;

#ifdef HAVE_TM_ZONE
	tzone = tm_ptr->tm_zone;
#else
	tzone = (tm_ptr->tm_isdst) ? tzname[1] : tzname[0];
#endif

	/* ctime() style date/time */
	if(type == LONG_DATE_TIME)
		snprintf(buffer, buffer_length, "%s %s %d %02d:%02d:%02d %s %d",
		         weekdays[tm_ptr->tm_wday], months[tm_ptr->tm_mon], day,
		         hour, minute, second, tzone, year);

	/* short date/time */
	else if(type == SHORT_DATE_TIME) {
		if(date_format == DATE_FORMAT_EURO)
			snprintf(buffer, buffer_length,
			         "%02d-%02d-%04d %02d:%02d:%02d", day, month,
			         year, hour, minute, second);
		else if(date_format == DATE_FORMAT_ISO8601
		        || date_format == DATE_FORMAT_STRICT_ISO8601)
			snprintf(buffer, buffer_length,
			         "%04d-%02d-%02d%c%02d:%02d:%02d", year, month,
			         day,
			         (date_format ==
			          DATE_FORMAT_STRICT_ISO8601) ? 'T' : ' ', hour,
			         minute, second);
		else
			snprintf(buffer, buffer_length,
			         "%02d-%02d-%04d %02d:%02d:%02d", month, day,
			         year, hour, minute, second);
		}

	/* short date */
	else if(type == SHORT_DATE) {
		if(date_format == DATE_FORMAT_EURO)
			snprintf(buffer, buffer_length, "%02d-%02d-%04d", day,
			         month, year);
		else if(date_format == DATE_FORMAT_ISO8601
		        || date_format == DATE_FORMAT_STRICT_ISO8601)
			snprintf(buffer, buffer_length, "%04d-%02d-%02d", year,
			         month, day);
		else
			snprintf(buffer, buffer_length, "%02d-%02d-%04d", month,
			         day, year);
		}

	/* expiration date/time for HTTP headers */
	else if(type == HTTP_DATE_TIME)
		snprintf(buffer, buffer_length,
		         "%s, %02d %s %d %02d:%02d:%02d GMT",
		         weekdays[tm_ptr->tm_wday], day, months[tm_ptr->tm_mon],
		         year, hour, minute, second);

	/* short time */
	else
		snprintf(buffer, buffer_length, "%02d:%02d:%02d", hour, minute,
		         second);

	buffer[buffer_length - 1] = '\x0';
	}

/* get days, hours, minutes, and seconds from a raw time_t format or total seconds */
void get_time_breakdown(unsigned long raw_time, int *days, int *hours,
                        int *minutes, int *seconds) {
	unsigned long temp_time;
	int temp_days;
	int temp_hours;
	int temp_minutes;
	int temp_seconds;

	temp_time = raw_time;

	temp_days = temp_time / 86400;
	temp_time -= (temp_days * 86400);
	temp_hours = temp_time / 3600;
	temp_time -= (temp_hours * 3600);
	temp_minutes = temp_time / 60;
	temp_time -= (temp_minutes * 60);
	temp_seconds = (int)temp_time;

	*days = temp_days;
	*hours = temp_hours;
	*minutes = temp_minutes;
	*seconds = temp_seconds;
	}

/*
 * ensure last character of path is the path separator '/'
 */
void ensure_path_separator(char *path, size_t size)
{
	const size_t len = strlen(path);
	if (len > 0 && len < size - 1) {
		if (path[len - 1] != '/') {
			path[len] = '/';
			path[len + 1] = '\0';
		}
	}
}
