#include "header.h"

#include <sys/stat.h>

#ifdef __NetBSD__
#ifndef PATH_MAX
#include <sys/syslimits.h>
#endif
#endif

#ifdef __linux__
#ifndef PATH_MAX
#include <linux/limits.h> /* for PATH_MAX */
#endif
#endif

#if defined(__sun) && defined(__illumos__)
#include "/opt/magic/include/magic.h"
#else
#include <magic.h>
#endif

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "structures.h"
#include "utils.h"

/*
 * Writes 304 not modified header to the client.
 */
int
write_304_header(REQUEST* request)
{
	char time_buf[BUFSIZ] = {0};
	char buf[BUFSIZ] = {0};

	char* err_msg = NULL;

	struct stat st;
	struct tm* time_info = NULL;

	int len = snprintf(buf, sizeof(buf),
	                   "Date: %s\r\n"
	                   "Server: %s/%.1f\r\n",
	                   request->request_time, SERVER_NAME, SERVER_VERSION);
	if (len < 0)
	{
		err_msg = "snprintf failed while writing final headers";
		goto not_mod_error;
	}

	if (stat(request->parsed_path, &st) != 0)
	{
		err_msg = "stat failed while writing final headers";
		goto not_mod_error;
	}

	time_info = gmtime(&st.st_mtime);
	if (strftime(time_buf, BUFSIZ, "%A, %d %b %Y %T GMT", time_info) == 0)
	{
		err_msg = "strftime failed while formatting Last-Modified header";
		goto not_mod_error;
	}

	if (safe_write(request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto not_mod_error;
	}

	len = snprintf(buf, sizeof(buf), "Last-Modified: %s\r\n", time_buf);
	if (len < 0)
	{
		err_msg = "safe_write failed while writing final headers";
		goto not_mod_error;
	}

	if (safe_write(request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto not_mod_error;
	}

	return HTTP_OK;

not_mod_error:
	if (err_msg != NULL)
	{
		fprintf(stderr, "%s: warning - %s: %s\n", getprogname(), err_msg,
		        strerror(errno));
	}
	return HTTP_ERROR;
}

/*
 * Writes generic empty header when a 4XX or 5XX is returned.
 */
int
write_error_header(REQUEST* request)
{
	char buf[BUFSIZ] = {0};

	char* err_msg = NULL;

	int len = snprintf(buf, sizeof(buf),
	                   "Date: %s\r\n"
	                   "Server: %s/%.1f\r\n"
	                   "Content-Type: text/plain\r\n"
	                   "Content-Length: 0\r\n",
	                   request->request_time, SERVER_NAME, SERVER_VERSION);
	if (len < 0)
	{
		err_msg = "snprintf failed while writing final headers";
		goto err_error;
	}

	if (safe_write(request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto err_error;
	}

	return HTTP_OK;

err_error:
	if (err_msg != NULL)
	{
		fprintf(stderr, "%s: warning - %s: %s\n", getprogname(), err_msg,
		        strerror(errno));
	}
	return HTTP_ERROR;
}

/*
 * Writes the headers for a directory indexing request.
 */
int
write_dir_header(REQUEST* request, size_t content_length)
{
	char time_buf[BUFSIZ] = {0};
	char buf[BUFSIZ] = {0};

	char* err_msg = NULL;

	struct stat st;
	struct tm* time_info = NULL;

	int len = snprintf(buf, sizeof(buf),
	                   "Date: %s\r\n"
	                   "Server: %s/%.1f\r\n"
	                   "Content-Type: text/plain\r\n"
	                   "Content-Length: %ld\r\n",
	                   request->request_time, SERVER_NAME, SERVER_VERSION,
	                   content_length);

	if (len < 0)
	{
		err_msg = "snprintf failed while writing final headers";
		goto dir_error;
	}

	if (stat(request->parsed_path, &st) != 0)
	{
		err_msg = "stat failed while writing final headers";
		goto dir_error;
	}

	time_info = gmtime(&st.st_mtime);
	if (strftime(time_buf, BUFSIZ, "%A, %d %b %Y %T GMT", time_info) == 0)
	{
		err_msg = "strftime failed while formatting Last-Modified header";
		goto dir_error;
	}

	if (safe_write(request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto dir_error;
	}

	len = snprintf(buf, sizeof(buf), "Last-Modified: %s\r\n", time_buf);
	if (len < 0)
	{
		err_msg = "safe_write failed while writing final headers";
		goto dir_error;
	}

	if (safe_write(request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto dir_error;
	}

	return HTTP_OK;

dir_error:
	if (err_msg != NULL)
	{
		fprintf(stderr, "%s: warning - %s: %s\n", getprogname(), err_msg,
		        strerror(errno));
	}
	return HTTP_ERROR;
}

/*
 * Writes the headers from a cgi_header upon success.
 */
int
write_cgi_header(CGI* cgi)
{
	char time_buf[BUFSIZ] = {0};
	char buf[BUFSIZ] = {0};

	char* err_msg = NULL;

	struct stat st;
	struct tm* time_info = NULL;

	int len = snprintf(buf, sizeof(buf),
	                   "Date: %s\r\n"
	                   "Server: %s/%.1f\r\n"
	                   "Content-Type: %s\r\n"
	                   "Content-Length: %ld\r\n",
	                   cgi->http_request->request_time, SERVER_NAME,
	                   SERVER_VERSION, cgi->content_type, cgi->content_length);

	if (len < 0)
	{
		err_msg = "safe_write failed while writing error headers";
		goto cgi_error;
	}

	if (stat(cgi->http_request->parsed_path, &st) != 0)
	{
		err_msg = "safe_write failed while writing error headers";
		goto cgi_error;
	}

	time_info = gmtime(&st.st_mtime);
	if (strftime(time_buf, BUFSIZ, "%A, %d %b %Y %T GMT", time_info) == 0)
	{
		err_msg = "strftime failed while formatting Last-Modified header";
		goto cgi_error;
	}

	if (safe_write(cgi->http_request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto cgi_error;
	}

	len = snprintf(buf, sizeof(buf), "Last-Modified: %s\r\n", time_buf);
	if (len < 0)
	{
		err_msg = "snprintf failed while writing final headers";
		goto cgi_error;
	}

	if (safe_write(cgi->http_request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto cgi_error;
	}

	return HTTP_OK;

cgi_error:
	if (err_msg != NULL)
	{
		fprintf(stderr, "%s: warning - %s: %s\n", getprogname(), err_msg,
		        strerror(errno));
	}
	return HTTP_ERROR;
}

int
write_header(REQUEST* request)
{
	/* variables declared here to prevent the compiler from screaming because we
	 * used goto statements */

	char time_buf[BUFSIZ] = {0};
	char buf[BUFSIZ] = {0};
	int len = 0;

	char* err_msg = NULL;

	magic_t magic = NULL;
	struct stat st;
	struct tm* time_info = NULL;
	const char* ftype = NULL;

	len = snprintf(buf, sizeof(buf),
	               "Date: %s\r\n"
	               "Server: %s/%.1f\r\n",
	               request->request_time, SERVER_NAME, SERVER_VERSION);
	if (len < 0)
	{
		err_msg = "snprintf failed while writing Date & Server";
		goto http_error;
	}
	if (safe_write(request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing Date & Server";
		goto http_error;
	}

	magic = magic_open(MAGIC_MIME_TYPE);
	if (magic == NULL || magic_load(magic, NULL) < 0)
	{
		len = snprintf(buf, sizeof(buf),
		               "Content-Type: text/plain;charset=us-ascii\r\n"
		               "Content-Length: 0\r\n");
		if (len < 0)
		{
			err_msg = "snprintf failed while writing magic headers";
			goto http_error;
		}
		if (safe_write(request->client_fd, buf, (size_t)len) == -1)
		{
			err_msg = "safe_write failed while writing magic headers";
		}
		goto http_error;
	}

	if (stat(request->parsed_path, &st) != 0)
	{
		len = snprintf(buf, sizeof(buf),
		               "Content-Type: text/plain;charset=us-ascii\r\n"
		               "Content-Length: 0\r\n");
		if (len < 0)
		{
			err_msg = "snprintf failed while writing error headers";
			goto http_error;
		}
		if (safe_write(request->client_fd, buf, (size_t)len) == -1)
		{
			err_msg = "safe_write failed while writing error headers";
		}
		goto http_error;
	}

	time_info = gmtime(&st.st_mtime);
	if (strftime(time_buf, BUFSIZ, "%A, %d %b %Y %T GMT", time_info) == 0)
	{
		err_msg = "strftime failed while formatting Last-Modified header";
		goto http_error;
	}

	ftype = magic_file(magic, request->parsed_path);
	if (ftype == NULL)
	{
		err_msg = "magic_file returned NULL";
		goto http_error;
	}

	len = snprintf(buf, sizeof(buf),
	               "Last-Modified: %s\r\n"
	               "Content-Type: %s\r\n"
	               "Content-Length: %ld\r\n",
	               time_buf, ftype, st.st_size);
	if (len < 0)
	{
		err_msg = "snprintf failed while writing final headers";
		goto http_error;
	}
	if (safe_write(request->client_fd, buf, (size_t)len) == -1)
	{
		err_msg = "safe_write failed while writing final headers";
		goto http_error;
	}

	magic_close(magic);
	return HTTP_OK;

http_error:
	if (magic != NULL)
	{
		magic_close(magic);
	}
	if (err_msg != NULL)
	{
		fprintf(stderr, "%s: warning - %s: %s\n", getprogname(), err_msg,
		        strerror(errno));
	}
	return HTTP_ERROR;
}