/*=============================================================================
 * Project:	Course on Foundations of the World Wide Web
 * Version:	Summer 2008
 * Module:	server2.c
 * Purpose:	Simple Web Server: Echos Request.
 * Last Change:	16.11.2000
 * Language:	LaTeX
 * Authors:	Stefan Brass
 * Email:	Stefan.Brass@informatik.uni-halle.de
 * Address:	Univiversitaet Halle, D-06099 Halle, GERMANY
 * Copyright:	free
 * Copying:	Do with this what you want, but don't make me responsible!
 *		There are no warrenties whatsoever.
 *=============================================================================
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
extern int errno;

/*=============================================================================
 * Parameters:
 *=============================================================================
 */

#define PORT 8888 /* Port on which the server listens */
#define BACKLOG 10 /* Number of queued connection requests from clients */
#define REQ_SIZE 8192 /* Maximal Size of a Request */
#define CON_LEN_SIZE 100 /* Maximal Size of Content-Length Line */
#define LOG_FILE "http.log" /* Where to log requests */

/*=============================================================================
 * Boolean Constants:
 *=============================================================================
 */

typedef int bool_t;
#define bool_true 1 /* Boolean Constant "True" */
#define bool_false 0 /* Boolean Constant "False" */

/*=============================================================================
 * File Pointer for Log File:
 *=============================================================================
 */

FILE *log_file;

/*=============================================================================
 * Auxillary Function for Writing to Socket:
 *=============================================================================
 */

void write_socket(int s, const char* str)
{
	int n;
	int written;

	n = strlen(str);
	while(n > 0) {
		written = write(s, str, n);
		if(written < 0) {
			fprintf(log_file,
				"Error while writing to client: %s\n",
				strerror(errno));
			perror("Error while writing to client");
			exit(1);
		}
		else if (written == 0) {
			fprintf(log_file,
				"Impossible. 0 Bytes written!\n");
			fprintf(stderr, "Impossible. 0 Bytes written!\n");
			exit(1);
		}
		else {
			str = str + written;
			n = n - written;
		}
	}
}

/*=============================================================================
 * Example of Server Function:
 *=============================================================================
 */

void serve(int connected_socket, struct sockaddr_in *client_addr)
{
	bool_t at_newline;
	char *p;
	int n;
	char request[REQ_SIZE+1];
	char content_length[CON_LEN_SIZE];
	int req_size;

	/* Print information about client (for debugging purposes): */
	fprintf(log_file, "Connect request from %s, port %d\n",
		inet_ntoa(client_addr->sin_addr),
		client_addr->sin_port);
	printf("Connect request from %s, port %d\n",
		inet_ntoa(client_addr->sin_addr),
		client_addr->sin_port);

	/* Read Request until empty line: */
	p = request;
	at_newline = bool_true;
	req_size = 0;
	n = 0;
	while(req_size < REQ_SIZE && (n = read(connected_socket,p,1)) == 1) {
		req_size++;
		if(*p == '\n') {
			if(at_newline)
				break; /* End of Request reached */
			at_newline = bool_true;
		}
		else if (*p != '\r' && *p != '\t' && *p != ' ')
			at_newline = bool_false;
		p++;
	}
	if(n < 0) {
		fprintf(log_file, "Error while reading request: %s\n",
			strerror(errno));
		perror("Error while reading request");
		exit(1);
	}
	*p = 0;

	/* Send Response: */
	write_socket(connected_socket, "HTTP/1.0 200 OK\r\n");
	write_socket(connected_socket, "Content-Type: text/plain\r\n");
	sprintf(content_length, "Content-length: %d\r\n", req_size);
	write_socket(connected_socket, content_length);
	write_socket(connected_socket, "\r\n");
	write_socket(connected_socket, request);

	/* Print Request (for Debugging): */
	fprintf(log_file, "\nReceived Request:\n %s\n\n",
		request);
	printf("\n%s\n", request);

	/* Don't forget to close socket: */
	if(close(connected_socket) != 0) {
		fprintf(log_file, "Error closing socket: %s\n",
			strerror(errno));
		perror("Error closing socket");
		exit(1);
	}

}

/*=============================================================================
 * Main Function (Sets up Server, Accepts Connection Requests):
 *=============================================================================
 */

int main()
{
	int s; /* Listening Socket Descriptor */
	int c; /* Connected Socket Descriptor */
	struct sockaddr_in addr; /* Internet address */
		/* struct sockaddr_in is defined in <netinet/in.h>,
		 * which is included via <netdb.h>.
		 * It must be compatible with / will be casted to
		 * struct sockaddr, defined in <sys/socket.h>.
		 */
	struct sockaddr_in client; /* Internet address */
	socklen_t len; /* Length of address data structure */

	/*---------------------------------------------------------------------
	 * Open Log File:
	 *---------------------------------------------------------------------
	 */

	log_file = fopen(LOG_FILE, "a");
	if(log_file == NULL) {
		fprintf(stderr, "Cannot open log file `%s'!\n", LOG_FILE);
		exit(1);
	}

	/*---------------------------------------------------------------------
	 * First Step: Create/Allocate TCP/IP Socket.
	 *---------------------------------------------------------------------
	 */

	s = socket(AF_INET, SOCK_STREAM, 0);
	if(s == -1) {
		fprintf(log_file, "Error creating socket: %s\n",
			strerror(errno));
		perror("Error creating socket");
		exit(1);
	}

	/*---------------------------------------------------------------------
	 * Second Step: Create data structure with address of server (port).
	 *---------------------------------------------------------------------
	 */

	memset(&addr, 0, sizeof(addr));
		/* Initialize structure to zero */
		/* Alternative: Field sin_zero (8 bytes) at the end */
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	addr.sin_port = htons(PORT);

	/*---------------------------------------------------------------------
	 * Third Step: Bind socket to port.
	 *---------------------------------------------------------------------
	 */

	if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) != 0) {
		fprintf(log_file, "Error binding socket: %s\n",
			strerror(errno));
		perror("Error binding socket");
		exit(1);
	}

	/*---------------------------------------------------------------------
	 * Fourth Step: Turn this socket into a listening socket.
	 *---------------------------------------------------------------------
	 */

	if(listen(s, BACKLOG) != 0) {
		fprintf(log_file, "Error making socket listening: %s\n",
			strerror(errno));
		perror("Error making socket listening");
		exit(1);
	}

	/*---------------------------------------------------------------------
	 * Fifth Step: Accept connections.
	 *---------------------------------------------------------------------
	  */

	for(;;) { /* FOR EVER */
		len = sizeof(client);
		if((c = accept(s, (struct sockaddr *) &client, &len)) < 0) {
			fprintf(log_file, "Error accepting connection: %s\n",
				strerror(errno));
			perror("Error accepting connection");
			exit(1);
		}
		serve(c, &client);
		fflush(log_file);
	}

	/* Program ends: */
	return(0);
}


