My server.c code is working perfectly but my check50 is showing errors. Even my cat image is loading. I've tried everything and honestly don't even know anymore which function might be creating problem. The TODO functions were LOAD, PARSE, INDEXES, LOOKUP
#define _GNU_SOURCE
#define _XOPEN_SOURCE 700
#define _XOPEN_SOURCE_EXTENDED
// limits on an HTTP request's size, based on Apache's
// http://httpd.apache.org/docs/2.2/mod/core.html
#define LimitRequestFields 50
#define LimitRequestFieldSize 4094
#define LimitRequestLine 8190
// number of bytes for buffers
#define BYTES 512
// header files
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
// types
typedef char BYTE;
// prototypes
bool connected(void);
void error(unsigned short code);
void freedir(struct dirent** namelist, int n);
void handler(int signal);
char* htmlspecialchars(const char* s);
char* indexes(const char* path);
void interpret(const char* path, const char* query);
void list(const char* path);
bool load(FILE* file, BYTE** content, size_t* length);
const char* lookup(const char* path);
bool parse(const char* line, char* path, char* query);
const char* reason(unsigned short code);
void redirect(const char* uri);
bool request(char** message, size_t* length);
void respond(int code, const char* headers, const char* body, size_t length);
void start(short port, const char* path);
void stop(void);
void transfer(const char* path, const char* type);
char* urldecode(const char* s);
// server's root
char* root = NULL;
// file descriptor for sockets
int cfd = -1, sfd = -1;
// flag indicating whether control-c has been heard
bool signaled = false;
int main(int argc, char* argv[])
{
// a global variable defined in errno.h that's "set by system
// calls and some library functions [to a nonzero value]
// in the event of an error to indicate what went wrong"
errno = 0;
// default to port 8080
int port = 8080;
// usage
const char* usage = "Usage: server [-p port] /path/to/root";
// parse command-line arguments
int opt;
while ((opt = getopt(argc, argv, "hp:")) != -1)
{
switch (opt)
{
// -h
case 'h':
printf("%s\n", usage);
return 0;
// -p port
case 'p':
port = atoi(optarg);
break;
}
}
// ensure port is a non-negative short and path to server's root is specified
if (port < 0 || port > SHRT_MAX || argv[optind] == NULL || strlen(argv[optind]) == 0)
{
// announce usage
printf("%s\n", usage);
// return 2 just like bash's builtins
return 2;
}
// start server
start(port, argv[optind]);
// listen for SIGINT (aka control-c)
struct sigaction act;
act.sa_handler = handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
sigaction(SIGINT, &act, NULL);
// a message and its length
char* message = NULL;
size_t length = 0;
// path requested
char* path = NULL;
// accept connections one at a time
while (true)
{
// free last path, if any
if (path != NULL)
{
free(path);
path = NULL;
}
// free last message, if any
if (message != NULL)
{
free(message);
message = NULL;
}
length = 0;
// close last client's socket, if any
if (cfd != -1)
{
close(cfd);
cfd = -1;
}
// check for control-c
if (signaled)
{
stop();
}
// check whether client has connected
if (connected())
{
// check for request
if (request(&message, &length))
{
// extract message's request-line
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html
const char* haystack = message;
const char* needle = strstr(haystack, "\r\n");
if (needle == NULL)
{
error(500);
continue;
}
char line[needle - haystack + 2 + 1];
strncpy(line, haystack, needle - haystack + 2);
line[needle - haystack + 2] = '\0';
// log request-line
printf("%s", line);
// parse request-line
char abs_path[LimitRequestLine + 1];
char query[LimitRequestLine + 1];
if (parse(line, abs_path, query))
{
// URL-decode absolute-path
char* p = urldecode(abs_path);
if (p == NULL)
{
error(500);
continue;
}
// resolve absolute-path to local path
path = malloc(strlen(root) + strlen(p) + 1);
if (path == NULL)
{
error(500);
continue;
}
strcpy(path, root);
strcat(path, p);
free(p);
// ensure path exists
if (access(path, F_OK) == -1)
{
error(404);
continue;
}
// if path to directory
struct stat sb;
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode))
{
// redirect from absolute-path to absolute-path/
if (abs_path[strlen(abs_path) - 1] != '/')
{
char uri[strlen(abs_path) + 1 + 1];
strcpy(uri, abs_path);
strcat(uri, "/");
redirect(uri);
continue;
}
// use path/index.php or path/index.html, if present, instead of directory's path
char* index = indexes(path);
if (index != NULL)
{
free(path);
path = index;
}
// list contents of directory
else
{
list(path);
continue;
}
}
// look up MIME type for file at path
const char* type = lookup(path);
if (type == NULL)
{
error(501);
continue;
}
// interpret PHP script at path
if (strcasecmp("text/x-php", type) == 0)
{
interpret(path, query);
}
// transfer file at path
else
{
transfer(path, type);
}
}
}
}
}
}
/**
* Checks (without blocking) whether a client has connected to server.
* Returns true iff so.
*/
bool connected(void)
{
struct sockaddr_in cli_addr;
memset(&cli_addr, 0, sizeof(cli_addr));
socklen_t cli_len = sizeof(cli_addr);
cfd = accept(sfd, (struct sockaddr*) &cli_addr, &cli_len);
if (cfd == -1)
{
return false;
}
return true;
}
/**
* Responds to client with specified status code.
*/
void error(unsigned short code)
{
// determine code's reason-phrase
const char* phrase = reason(code);
if (phrase == NULL)
{
return;
}
// template for response's content
char* template = "<html><head><title>%i %s</title></head><body><h1>%i %s</h1></body></html>";
// render template
char body[(strlen(template) - 2 - ((int) log10(code) + 1) - 2 + strlen(phrase)) * 2 + 1];
int length = sprintf(body, template, code, phrase, code, phrase);
if (length < 0)
{
body[0] = '\0';
length = 0;
}
// respond with error
char* headers = "Content-Type: text/html\r\n";
respond(code, headers, body, length);
}
/**
* Frees memory allocated by scandir.
*/
void freedir(struct dirent** namelist, int n)
{
if (namelist != NULL)
{
for (int i = 0; i < n; i++)
{
free(namelist[i]);
}
free(namelist);
}
}
/**
* Handles signals.
*/
void handler(int signal)
{
// control-c
if (signal == SIGINT)
{
signaled = true;
}
}
/**
* Escapes string for HTML. Returns dynamically allocated memory for escaped
* string that must be deallocated by caller.
*/
char* htmlspecialchars(const char* s)
{
// ensure s is not NULL
if (s == NULL)
{
return NULL;
}
// allocate enough space for an unescaped copy of s
char* t = malloc(strlen(s) + 1);
if (t == NULL)
{
return NULL;
}
t[0] = '\0';
// iterate over characters in s, escaping as needed
for (int i = 0, old = strlen(s), new = old; i < old; i++)
{
// escape &
if (s[i] == '&')
{
const char* entity = "&";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape "
else if (s[i] == '"')
{
const char* entity = """;
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape '
else if (s[i] == '\'')
{
const char* entity = "'";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape <
else if (s[i] == '<')
{
const char* entity = "<";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// escape >
else if (s[i] == '>')
{
const char* entity = ">";
new += strlen(entity);
t = realloc(t, new);
if (t == NULL)
{
return NULL;
}
strcat(t, entity);
}
// don't escape
else
{
strncat(t, s + i, 1);
}
}
// escaped string
return t;
}
/**
* Checks, in order, whether index.php or index.html exists inside of path.
* Returns path to first match if so, else NULL.
*/
char* indexes(const char* path)
{
// TODO
char *index = malloc(strlen(path) + 10 + 1);
strcpy(index, path);
strcat(index,"index.php");
if (access(index, F_OK) == 0)
{
return index;
}
strcpy(index, path);
strcat(index,"index.html");
if (access(index, F_OK) == 0)
{
return index;
}
else
return NULL;
}
/**
* Interprets PHP file at path using query string.
*/
void interpret(const char* path, const char* query)
{
// ensure path is readable
if (access(path, R_OK) == -1)
{
error(403);
return;
}
// open pipe to PHP interpreter
char* format = "QUERY_STRING=\"%s\" REDIRECT_STATUS=200 SCRIPT_FILENAME=\"%s\" php-cgi";
char command[strlen(format) + (strlen(path) - 2) + (strlen(query) - 2) + 1];
if (sprintf(command, format, query, path) < 0)
{
error(500);
return;
}
FILE* file = popen(command, "r");
if (file == NULL)
{
error(500);
return;
}
// load interpreter's content
char* content;
size_t length;
if (load(file, &content, &length) == false)
{
error(500);
return;
}
// close pipe
pclose(file);
// subtract php-cgi's headers from content's length to get body's length
char* haystack = content;
char* needle = strstr(haystack, "\r\n\r\n");
if (needle == NULL)
{
free(content);
error(500);
return;
}
// extract headers
char headers[needle + 2 - haystack + 1];
strncpy(headers, content, needle + 2 - haystack);
headers[needle + 2 - haystack] = '\0';
// respond with interpreter's content
respond(200, headers, needle + 4, length - (needle - haystack + 4));
// free interpreter's content
free(content);
}
/**
* Responds to client with directory listing of path.
*/
void list(const char* path)
{
// ensure path is readable and executable
if (access(path, R_OK | X_OK) == -1)
{
error(403);
return;
}
// open directory
DIR* dir = opendir(path);
if (dir == NULL)
{
return;
}
// buffer for list items
char* list = malloc(1);
list[0] = '\0';
// iterate over directory entries
struct dirent** namelist = NULL;
int n = scandir(path, &namelist, NULL, alphasort);
for (int i = 0; i < n; i++)
{
// omit . from list
if (strcmp(namelist[i]->d_name, ".") == 0)
{
continue;
}
// escape entry's name
char* name = htmlspecialchars(namelist[i]->d_name);
if (name == NULL)
{
free(list);
freedir(namelist, n);
error(500);
return;
}
// append list item to buffer
char* template = "<li><a href=\"%s\">%s</a></li>";
list = realloc(list, strlen(list) + strlen(template) - 2 + strlen(name) - 2 + strlen(name) + 1);
if (list == NULL)
{
free(name);
freedir(namelist, n);
error(500);
return;
}
if (sprintf(list + strlen(list), template, name, name) < 0)
{
free(name);
freedir(namelist, n);
free(list);
error(500);
return;
}
// free escaped name
free(name);
}
// free memory allocated by scandir
freedir(namelist, n);
// prepare response
const char* relative = path + strlen(root);
char* template = "<html><head><title>%s</title></head><body><h1>%s</h1><ul>%s</ul></body></html>";
char body[strlen(template) - 2 + strlen(relative) - 2 + strlen(relative) - 2 + strlen(list) + 1];
int length = sprintf(body, template, relative, relative, list);
if (length < 0)
{
free(list);
closedir(dir);
error(500);
return;
}
// free buffer
free(list);
// close directory
closedir(dir);
// respond with list
char* headers = "Content-Type: text/html\r\n";
respond(200, headers, body, length);
}
/**
* Loads a file into memory dynamically allocated on heap.
* Stores address thereof in *content and length thereof in *length.
*/
bool load(FILE* file, BYTE** content, size_t* length)
{
// TODO
// ensure file is open
if (file == NULL)
{
return -1;
}
*length = 0;
BYTE buffer[BYTES];
*content = NULL;
while (true)
{
// try to read a buffer's worth of bytes
ssize_t bytes = fread(buffer, sizeof(BYTE), BYTES, file);
// check for error
if (ferror(file) != 0)
{
if (*content != NULL)
{
free(*content);
*content = NULL;
}
return 0;
}
// if bytes were read, append to *content
if (bytes > 0)
{
*content = realloc(*content, *length + bytes);
if (*content == NULL)
{
return -1;
}
memcpy(*content + *length, buffer, bytes);
*length += bytes;
}
// check for EOF
if (feof(file) != 0)
{
break;
}
}
return true;
}
/**
* Returns MIME type for supported extensions, else NULL.
*/
const char* lookup(const char* path)
{
// TODO
const char *ptr = NULL;
if(strcasestr(path,".css") != NULL)
{
const char tp[18] = "text/css";
ptr = tp;
}
else if(strcasestr(path,".html") != NULL)
{
const char tp[18] = "text/html";
ptr = tp;
}
else if(strcasestr(path,".gif") != NULL)
{
const char tp[18] = "image/gif";
ptr = tp;
}
else if(strcasestr(path,".ico") != NULL)
{
const char tp[18] = "image/x-icon";
ptr = tp;
}
else if(strcasestr(path,".jpg") != NULL)
{
const char tp[18] = "image/jpeg";
ptr = tp;
}
else if(strcasestr(path,".js") != NULL)
{
const char tp[18] = "text/javascript";
ptr = tp;
}
else if(strcasestr(path,".php") != NULL)
{
const char tp[18] = "text/x-php";
ptr = tp;
}
else if(strcasestr(path,".png") != NULL)
{
const char tp[18] = "image/png";
ptr = tp;
}
return ptr;
}
/**
* Parses a request-line, storing its absolute-path at abs_path
* and its query string at query, both of which are assumed
* to be at least of length LimitRequestLine + 1.
*/
bool parse(const char* line, char* abs_path, char* query)
{
// TODO
int i = 0;
// METHOD has to be GET
char method[5];
for(i = 0; line[i] != ' '; i++)
{
method[i] = line[i];
}
method[i] = '\0';
if (strcmp("GET", method) != 0)
{
error(405);
return false;
}
// Check for multiple spaces after GET
if(strncasecmp(line, "GET ", 5) == 0)
{
error(400);
return false;
}
// Request-target must begin with /
char* target = strchr(line, ' ');
if (target[1] != '/')
{
error(501);
return false;
}
// Request target may not contain a "
int flag = 0;
for(i = 1; target[i] != ' '; i++)
{
if (target[i] == '"')
{
flag++;
break;
}
}
if (flag != 0)
{
error(400);
return false;
}
// Multiple spaces before version
if(target[i+1] == ' ')
{
error(400);
return false;
}
// Only HTTP/1.1 is supported
char* occur = strrchr(line, ' ');
char version[11];
for(i = 0; occur[i] != '\r'; i++)
{
version[i] = occur[i];
}
version[i] = '\0';
if (strcmp(version, " HTTP/1.1") != 0)
{
printf("%s", version);
error(505);
}
// Extract abs_path
for(i = 1; target[i] != ' ' && target[i] != '?'; i++)
{
abs_path[i - 1] = target[i];
}
abs_path[i - 1] = '\0';
flag = 0;
for(i = 0; abs_path[i] != '\0'; i++)
{
if (abs_path[i] == ' ')
{
flag++;
break;
}
}
if (flag != 0)
{
error(400);
return false;
}
// extract query from request-target
char real[50];
for(i = 1; target[i] != ' '; i++)
{
real[i - 1] = target[i];
}
real[i - 1] = '\0';
char* q = strchr(real, '?');
if (q == NULL || q[1] == '\0')
{
query[0] = '\0';
}
else
{
for(i = 1; q[i] != '\0'; i++)
{
query[i - 1] = q[i];
}
query[i - 1] = '\0';
}
return true;
}