#include <sys/socket.h>		/*  socket definitions        */
#include <sys/types.h>		/*  socket types              */
#include <arpa/inet.h>		/*  inet (3) funtions         */
#include <unistd.h>		/*  misc. UNIX functions      */

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <libgen.h>

/* #define DEBUG */

/*  Global constants  */

#define ECHO_PORT          (2002)
#define MAX_LINE           (65535)

#define LISTENQ        (1024)	/*  Backlog for listen()   */

ssize_t Readline(int fd, void *vptr, size_t maxlen);
ssize_t Writeline(int fc, const void *vptr, size_t maxlen);

/*  Read a line from a socket  */

ssize_t Readline(int sockd, void *vptr, size_t maxlen)
{
	ssize_t n, rc;
	char c, *buffer;

	buffer = vptr;

	for (n = 1; n < maxlen; n++) {

		if ((rc = read(sockd, &c, 1)) == 1) {
			*buffer++ = c;
			if (c == '\n')
				break;
		} else if (rc == 0) {
			if (n == 1)
				return 0;
			else
				break;
		} else {
			if (errno == EINTR)
				continue;
			return -1;
		}
	}

	*buffer = 0;
	return n;
}

/*  Write a line to a socket  */

ssize_t Writeline(int sockd, const void *vptr, size_t n)
{
	size_t nleft;
	ssize_t nwritten;
	const char *buffer;

	buffer = vptr;
	nleft = n;

	while (nleft > 0) {
		if ((nwritten = write(sockd, buffer, nleft)) <= 0) {
			if (errno == EINTR)
				nwritten = 0;
			else
				return -1;
		}
		nleft -= nwritten;
		buffer += nwritten;
		printf("LOOP");
		fflush(stdout);
	}

	return n;
}

void show_buffer(unsigned char *buf, int len)
{
	int i, j;
	char str[17];

	str[16] = 0;
	printf("\n");
	for (i = 0; i < len; i += 16) {
		fprintf(stdout, "   ");
		for (j = 0; j < 16 && i + j < len; j++) {
			fprintf(stdout, "%02x ", buf[i + j]);

			if (buf[i + j] > 32 && buf[i + j] < 127)
				str[j] = buf[i + j];
			else
				str[j] = '.';
		}
		for (; j < 16; j++) {
			fprintf(stdout, "   ");
			str[j] = ' ';
		}
		printf("   |   %s   |\n", str);
	}
	fflush(stdout);
}

int main(int argc, char *argv[])
{
	int list_s;		/*  listening socket          */
	int conn_s;		/*  connection socket         */
	short int port;		/*  port number               */
	struct sockaddr_in servaddr;	/*  socket address structure  */
	char buffer[MAX_LINE];	/*  character buffer          */
	char *endptr;		/*  for strtol()              */
	int len;
	int i;

	/*  Get port number from the command line, and
	   set to default port if no arguments were supplied  */

	if (argc == 2) {
		port = strtol(argv[1], &endptr, 0);
		if (*endptr) {
			fprintf(stderr, "ECHOSERV: Invalid port number.\n");
			exit(EXIT_FAILURE);
		}
	} else if (argc < 2) {
		port = ECHO_PORT;
	} else {
		fprintf(stderr, "ECHOSERV: Invalid arguments.\n");
		exit(EXIT_FAILURE);
	}

	/*  Create the listening socket  */

	if ((list_s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		fprintf(stderr, "ECHOSERV: Error creating listening socket.\n");
		exit(EXIT_FAILURE);
	}

	/*  Set all bytes in socket address structure to
	   zero, and fill in the relevant data members   */

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port = htons(port);

	/*  Bind our socket addresss to the 
	   listening socket, and call listen()  */

	if (bind(list_s, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
		fprintf(stderr, "ECHOSERV: Error calling bind()\n");
		exit(EXIT_FAILURE);
	}

	if (listen(list_s, LISTENQ) < 0) {
		fprintf(stderr, "ECHOSERV: Error calling listen()\n");
		exit(EXIT_FAILURE);
	}

	/*  Enter an infinite loop to respond
	   to client requests and echo input  */

	while (1) {

		/*  Wait for a connection, then accept() it  */

		if ((conn_s = accept(list_s, NULL, NULL)) < 0) {
			fprintf(stderr, "ECHOSERV: Error calling accept()\n");
			exit(EXIT_FAILURE);
		}

		/*  Retrieve an input line from the connected socket
		   then simply write it back to the same socket.     */

		len = Readline(conn_s, buffer, MAX_LINE - 1);
		/* Writeline(conn_s, buffer, strlen(buffer)); */

		if (len > 0 && len < MAX_LINE)
			buffer[len] = 0;
		else
			buffer[0] = 0;

		/* Check HTTP method */
		if (strncmp(buffer, "GET ", 4) && strncmp(buffer, "get ", 4))
			continue;

		/* buffer shuold look like 'GET URL HTTP/1.0' */
		/* remove everything after URL */
		for (i = 4; i < MAX_LINE; i++) {
			if (buffer[i] == ' ') {
				buffer[i] = 0;
				break;
			}
		}

		printf("%s\n", &buffer[5]);
		fflush(stdout);

		sprintf(buffer, "HTTP/1.1 404 Not Found\r\n"
			"Server: nginx\r\n"
			"Content-Type: text/html\r\n\r\n"
			"<html>\n"
			"<head><title>404 Not Found</title></head>\n"
			"<body bgcolor=\"white\">\n"
			"<center><h1>404 Not Found</h1></center>\n"
			"<hr><center>nginx</center>\n"
			"</body>\n" "</html>\n\r\n");

		len = send(conn_s, buffer, strlen(buffer), 0);
#ifdef DEBUG
		printf("sent len: %d\n", len);
#endif

		/*  Close the connected socket  */

		if (close(conn_s) < 0) {
			fprintf(stderr, "ECHOSERV: Error calling close()\n");
			exit(EXIT_FAILURE);
		}
	}
}