#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/sendfile.h>
#include <netinet/in.h>
#include <netdb.h>
#include <dirent.h>
#include <time.h>

#define LOGFILE		"/tmp/h9log.##"	// Replace ## with your 2 digit ID:
#define PORT		473##
#define K		1024
#define CHUNK_SIZE	K*10

// List of mime types for various file extensions:
struct mime_t {
  char *ext;
  char *type;
} mimetypes[] = {
  {".html", "text/html"}, {".htm", "text/html"}, {".css", "text/css"},
  {".pdf", "application/pdf"}, {".gif", "image/gif"}, {".png", "image/png"},
  {".jpg", "image/jpeg"}, {".jpeg", "image/jpeg"}, {".gz", "application/x-gzip"},
  {NULL, NULL}
};

// Weekday and Month strings for printing times:
char *wday[] = {"Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat"};
char *month[] = {
  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

// A big buffer for "printing" a directory listing into:
char wbuf[K*64];
// To be set to the root web path (The servers current working directory):
char *root;

/*
 * Signal handler to reap dead processes or shutdown the server on a signal:
 */
void sighandler(int sig)
{
  int status;
  // If we get a SIGCHLD signal, reap the dead child process with wait:

  // Otherwise shutdown the server:



}

/*
 * Launches the server into the background:
 */
void daemonize(char *wd)
{
  int null, fd;

  // If the wd argument is not null, attempt to change to the directory specified
  // by it:

  // Set "root" to the current directory (use get_current_dir_name(3)):

  
  // Open /dev/null for reading:



  // Open LOGFILE for writing, create it if it doesn't exist, truncate it if it
  // does:

  
  
  
  // Make /dev/null our stdin and the LOGFILE our stdout and stderr:



  // Close the /dev/null and LOGFILE descriptors:


  // Print a startup message to stderr, messages printed to stdout and stderr
  // will go to the LOGFILE:

  // Use the daemon(3) function to daemonize our server, don't change directory
  // and don't close the file descriptors:

}

/* An example HTTP request a browser might make, we are really only interested
 * in the first line, but must read the entire request which is terminated by
 * a blank line:

  GET / HTTP/1.1
  Host: localhost:8000
  User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:40.0) Gecko/20100101 Firefox/40.0
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,* /*;q=0.8
  Accept-Language: en-US,en;q=0.5
  Accept-Encoding: gzip, deflate
  DNT: 1
  Connection: keep-alive

*/
/*
 * Reads the request header from the browser, like the above.  Returns the
 * path given by the GET request (second word, between GET and HTTP/1.1):
 * If there is any error anywhere, return a NULL instead.
 */
char *readrequest(FILE *sock)
{
  char buf[K], *s, *path;

  // Read the first line, use fgets():

  // Check that it begins with "GET ":

  // Use strtok to extract the second word:


  // Use strdup() to copy the second word into "path":

  // Read out header, until a empty line is reached:




  return path;
}

/* Checks that the path we got from the browser lies within the root of our
 * document. Returns the full real path to the file/directory:
 */
char *checkpath(char *path)
{
  char cpath[PATH_MAX], *real;

  // Concatenate the root and path together:

  // Use realpath(3) to get the real path location:

  // Check if the prefix of the real path above and "root" are the same,
  // If not return NULL, if so, return the real path.

  
  
  
  
}

/* Checks what kind of file the path is, returns one of:
 * 0 = not a file/not accessible/error
 * 1 = a regular file, size of the file returned in "size"
 * 2 = a PHP script.
 * 3 = a directory.
 * date is always set to the last modified time of the file/directory.
 */
int filetype(char *path, long *size, time_t *date)
{
  struct stat st;
  int len;

  // Use stat(2) to get the inode information for "path":

  // Set date argument to the last modified time.


  // If it's a directory, return 3:

  // If it's a real file:

  
    // Check if we can read it using access(2), if not, return 0:

    // Check if the last 4 characters in the path end in .php, if so, return 2:

  
    // otherwise return 1:


  // Not one of the above, so return 0:
  return 0;
}

/*
 * Returns the mime type given the path.
 */
char *mimedetect(char *path)
{
  int len = strlen(path), extlen;
  int i;
  
  // Loop through all the mimetypes[]:

    // Get the length of the ext field:

    // Check that the last "extlen" number of characterse of the path and
    // mimetypes[i].ext match.  If so return mimetypes[i].type:


  // Return a default of "text/plain" if no extension matched:
  return "text/plain";
}

/*
 * Outputs the response header to the browser:
 * All lines must be terminated by carriage-return + newline: i.e. "\r\n"
 * sock = a FILE * stream representing the network connection.
 * status = The HTTP status code number to send
 * message = The response message corresponding to the status code
 *   (e.g. when status == 200, message = "OK")
 * size = The size of the file (content-size) or -1 indicating chunked transfers
 *   (required when sending php data)
 * date = The file date or the current time (time(NULL)) for PHP transfers.
 * mimetype = The mime-type string for the content-type header.
 */
void emitheader(FILE *sock, int status, char *message, int size, time_t date, char *mimetype)
{
  struct tm *lt = localtime(&date);
  // Print the HTTP/1.1 status message provided by status and message arguments:

  // Print the date.  Use localtime(3) with "date" to and use the struct tm
  // structure to format a date like: Tue, 16 Feb 2016 17:35:16 GMT
  // Use the wday and month arrays define above for those strings



  // Print a Server: string.  Make up a name for your server. ex:
  // Server: h7/1.0.0 (Linux) PHP/5.6.17

  // Print "Accept-Ranges: bytes":

  // If size < 0, print the transfer-encoding chunked header, otherwise print
  // the content-length header:





  // Print the Content-Type: mimetype header:

  // Print a blank line:

}


/* Outputs a 404 "Not Found" header then some HTML for the web user.
 * The size of the HTML must be known before printing the header so that
 * the Content-Length field can be set properly.  So print the HTML message
 * into wbuf, then use the strlen(wbuf) to properly fill out the emitheader()
 * function.  Call exit(2) after closing the socket.
 */
void badrequest(FILE *sock, char *path)
{
  // "Print" a error message to wbuf using sprintf:








  emitheader(sock, 404, "Not Found", strlen(wbuf), time(0), "text/html");
  fwrite(error, 1, strlen(wbuf), sock);
  fclose(sock);
  exit(0);
}

/*
 * Opens a pipe to the php-cgi program to process a PHP page.
 */
void phppipe(FILE *sock, char *path)
{
  char chunk[CHUNK_SIZE];
  int pd[2], r;
  pid_t pid;
  
  // Create a pipe:





  // Fork a new process for the php-cgi program:


  // The child process (php-cgi):
    // Close the parents read descriptor:

    // Open the PHP file for reading:


    // Make the PHP file the childs stdin (descriptor 0):

    // Make the write side of the pipe stdout and stderr:


    // Close the orginal PHP file and pipe write side descriptors:


    // Exec /usr/bin/php-cgi with the "-q" command line option:

    // Print an error message if the exec failed and exit:


  // The parent process:
    // Close the clients pipe (write side):

    // Make a loop to read CHUNK_SIZE sized data from the pipe and output
    // to the browser in Transfer-Encoding: chunked format, i.e. chunk size
    // in hex + "\r\n" + chunk data + "\r\n":




    // Print a zero length chunk at the end:

    // Close pipe:

    // Use waitpid(2) to wait on the child to exit:


}

// Used by scandir to filter out files beginning with ".". An example of using
// scandir(3) is available at the bottom of it's man page.
int filter(const struct dirent *de)
{
  // If the file begins with '.', return 0, otherwise return 1:
  if (de->d_name[0] == '.') return 0;
  return 1;
}

/* Uses sprintf() to write a directory listing to the global wbuf, then
 * sends that to the browser.
 */
void dirlist(FILE *sock, char *realpath, char *path)
{
  struct dirent **dents;
  int i, off;

  // Output the HTML for a directory listing to wbuf, you can use sprintf(3)
  // in this manner:
  //   off = sprintf(wbuf, "first part");
  //   off += sprintf(wbuf+off, "second+ parts");

  // Write out the HTML header portion of the directory listing:  




  
  // Use scandir(3) and filter() above (to filter out all files beginning with
  // ".") to read the directory (remember to free things as you go.):



  // Loop through the directory and add a HTML link to it in the dir listing:








  // Write out the closing portion of the HTML:


  // Emit the header and send the data:
  emitheader(sock, 200, "OK", off, time(0), "text/html");
  fwrite(wbuf, 1, off, sock);
}


/*
 * Routine to handle a connection from start to finish:
 */
void handle(int fd)
{
  char *path, *realpath, npath[PATH_MAX], *mimetype;
  int ftype, reloc = 0, inf;
  long size;
  time_t date;
  FILE *sock;
  
  // Use fdopen(3) to make a stream from the socket file descriptor fd:




  // Get the path via readrequest, return a badrequest() if the path is NULL
  // or outside of our web area (checkpath()):

  
  

  // Get the file type of the file, return a badrequest if it's 0:


  // If it's a directory search for a index.html or index.php inside of it, and
  // if found, we'll use it by recomputing the real path and file type for that
  // file:

  
  
  
  
  
  
  
  // Switch on the file type:
  switch(ftype) {
    case 1:	// Regular files:
      mimetype = mimedetect(realpath);
      inf = open(realpath, O_RDONLY);
      if (inf < 0) return badrequest(sock, path);
      emitheader(sock, 200, "OK", size, date, mimetype);
      // Must flush buffered I/O in sock, before sendfile() which does not use
      // stream I/O:
      fflush(sock);
      sendfile(fd, inf, 0, size);
      close(inf);
      break;
    case 2:	// PHP-CGI
      emitheader(sock, 200, "OK", -1, time(0), "text/html");
      phppipe(sock, realpath);
      break;
    case 3:	// A directory listing:
      dirlist(sock, realpath, path);
      break;
    default:
      badrequest(sock, path);
      break;
  }
  fclose(sock);
  exit(0);
}

int main(int argc, char *argv[])
{
  struct sigaction sa;
  struct sockaddr_in server;
  struct epoll_event epv;
  int epfd, sock, fd, r;
  pid_t pid;

  // Daemonize the server first, then setup signal handlers:
  daemonize(argv[1]);
  // Setup signal handlers:

  
  // Create a socket for the server:








  // Bind it to any interface on PORT, then begin listening for connections:

  
  
  
  
  
  
  
  
  
  
  
  // Create an epoll instance, then add the server socket to it:

  
  
  
  
  
  
  
  // Loop forever:
  for(;;) {
    // Wait for an event to occur on the epoll instance:

    
    
    // If there was an error on the socket, break out of the loop:

    
    
    // Accept a new connection:

    
    
    
    // Fork a new process to handle the connection:









  }

  fprintf(stderr,"Error on socket -- server shutting down.\n");
  return 0;
}
