#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LISTEN_BACKLOG 50
#define BUF_LEN 4096
#define BLOCK_SIZE 4096

//strictly speaking, this must be a daemon
//program. well, its not :)
int main (int argc, char *argv[]) {

    int sfd, cfd, fd;
    struct sockaddr peer_addr;
    socklen_t peer_addrlen;
    struct addrinfo *res;
    int cpid;
    char buf[BUF_LEN];

    // create a tcp/ip socket.
    sfd = socket (AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
	perror ("socket()");
	exit (1);
    }
    // we need to bind it to 0.0.0.0:4096, use getaddrinfo() to 
    // get a sockaddr structure for the address.
    if (getaddrinfo ("0.0.0.0", "4096", NULL, &res) != 0) {
	fprintf (stderr, "getaddrinfo() failed.\n");
	exit (1);
    }
    // bind this process to address and port as said above.
    if (bind (sfd, res->ai_addr, res->ai_addrlen) != 0) {
	perror ("bind()");
	exit (1);
    }

    //listen on the binded address
    if (listen (sfd, LISTEN_BACKLOG) != 0) {
	perror ("listen()");
	exit (1);
    }

    //now the service starts...
    while (1) {

	peer_addrlen = sizeof (peer_addr);
	cfd = accept (sfd, &peer_addr, &peer_addrlen);
	if (cfd == -1) {
	    perror ("accept()");
	    exit (1);
	}

	// we now have a client. serve it with a new process.
	cpid = fork();
	if (cpid == 0) {
	    int len;
	    //child now. serve.
	    //client sends the filename (full path).
	    if ((len = read (cfd, buf, BUF_LEN)) == -1) {
		perror ("read()");
		exit (1);
	    }
	    buf[len] = '\0';
	    fd = open (buf, O_RDONLY);
	    if (fd == -1) {
		//strictly speaking, we need to tell the client
		//about the error. well, i'm lazy...
		perror ("open()");
		exit (1);
	    }
	    // send the file to the client and close the connection
	    printf ("Sending file %s: ", buf);
	    {
		int bytes;
		while (1) {

		    // read BLOCK_SIZE bytes at a time for efficiency
		    bytes = read (fd, buf, BLOCK_SIZE);
		    if (bytes == -1) {
			perror ("read()");
			exit (1);
		    }
		    // send
		    if (write (cfd, buf, bytes) != bytes) {
			perror ("write()");
			exit (1);
		    }
		    
		    if (bytes < BLOCK_SIZE) {
			//nothing more to read
			printf ("File sent.\n");
			exit(0);
		    }
		    else
			continue; //ha ha
		}
	    }
	}
	else if (cpid < 0) {
	    perror ("fork()");
	    exit (1);
	}
	else  {
	    // parent: just continue the loop.
	    continue;
	}
    }

    return 0;
}

