Code:
/* Donner ... "to give" in French. It's a server...get it? Eh? :(
Only one file thus far (this one): donner.c
*/
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <dirent.h>
/* Arg format: 1=port */
#define BUFSIZE 8196
struct {
char *ext;
char *filetype;
} extensions [] = {
{"gif","image/gif"},
{"jpg","image/jpeg"},
{"jpeg","image/jpeg"},
{"png","image/png"},
{"zip","image/zip"},
{"gz","image/gz"},
{"tar","image/tar"},
{"htm","text/html"},
{"html","text/html"},
{"c","text/text"},
{0,0}
};
void writeFile(char *filename, char *toWrite)
{
FILE * thefile;
thefile = fopen(filename,"w");
fprintf(thefile,"%s",toWrite);
fclose(thefile);
perror("Error was ");
}
int isDir(char *candidate)
{
DIR * dir = opendir(candidate);
if (dir == NULL)
{
return 0;
} else
{
return 1;
}
}
int processGet(int fd, char * buffer)
{
int j,ret,len,buflen,file_fd;
long i;
char *fstr;
/* for (j=0;j<i-1;j++) if (buffer[j] == '.' && buffer[j+1] == '.') printf("no h4x0ring allowed"); */
char *firstline;
char *reqtype; /* Slightly useless since only GET requests make it this far */
char *reqfile;
char *requrl;
firstline = strtok(buffer,"\r\n");
reqtype = strtok(firstline," ");
reqfile = strtok(NULL," ");
char prefix[BUFSIZE+1] = "/home/abbas/donner";
char error[BUFSIZE+1] = "./error404.html";
requrl = strcat(prefix,reqfile);
if (isDir(prefix))
{
len = strlen(prefix);
if (prefix[len-1] != 47) /* is last char "/" ? */
{
fprintf(stdout, "Trailing slash for '%s'. Request: %s\n", reqfile, requrl);
requrl = strcat(reqfile,"/");
(void)sprintf(buffer,"HTTP/1.1 302 Found\r\nLocation: %s/\r\n", reqfile);
fprintf(stdout, "%s", buffer);
if ( write(fd,buffer,strlen(buffer)) < 0 ) fprintf(stdout, "Error sending header!\n");
#ifdef LINUX
sleep(1);
#endif
close(fd);
return 1;
}
requrl = strcat(prefix,"index.html");
}
if ((file_fd = open(&prefix,O_RDONLY)) == -1) file_fd = open("./error404.html",O_RDONLY);
buflen = strlen(prefix);
fstr = (char *)0;
for (i=0;extensions[i].ext != 0;i++)
{
len = strlen(extensions[i].ext);
if (!strncmp(&prefix[buflen-len], extensions[i].ext, len))
{
fstr = extensions[i].filetype;
break;
}
}
/* see if we can't open that file */
(void)sprintf(buffer,"HTTP/1.1 200 OK\r\nContent-Type: %s\r\n\r\n",fstr);
(void)write(fd,buffer,strlen(buffer));
while ((ret=read(file_fd,buffer,BUFSIZE))>0)
{
(void) write(fd,buffer,ret);
}
#ifdef LINUX
sleep(1);
#endif
close(fd);
return 1;
}
void *processRequest(void *arg)
{
static char buffer[BUFSIZE+1]; /* this is where we will buffer the HTTP request */
/* because arg is void in order to work with the thread library I'm using */
int fd = (int*)arg;
long ret;
/* Now we read the HTTP request and see what it is */
ret = read(fd,buffer,BUFSIZE);
if (ret == 0 || ret == -1) printf("error, man. freakin' error."); /* <-- eventually we hope to implement a logging
system, where all instances of printf
will go bye bye */
if (ret > 0 && ret < BUFSIZE) buffer[ret] = 0; /* makes it
easier to tell when you're at the end */
else buffer[0] = 0;
if (!strncmp(buffer,"GET",3) || ! strncmp(buffer,"get",3))
{
processGet(fd,buffer);
}
return NULL;
}
int main (int argc, char ** argv)
{
/* Local variable time! */
int fd, port, connection_fd, socklen; /* stream file
handle, port, connection handle, and the ... wtf is socklen? */
struct sockaddr_in sockaddr; /* this is our gateway to the internet, man */
pthread_t pth;
fd = socket(PF_INET, SOCK_STREAM,0);
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = INADDR_ANY;
port = atoi(argv[1]); /* this two part cliffhanger
takes the char argument, turns it into an int, and then puts it in
network byte order*/
sockaddr.sin_port = htons(port);
if ( bind (fd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) != 0 ) printf("Couldn't open socket"); /* bind
the socket */
if ( listen(fd, 1) != 0 ) printf("Error opening socket!"); /*
begin listening .......... head */
while (1) /* go forever, should probably find a way to change that */
{
connection_fd; /* this is the file descriptor for our session stream */
socklen = sizeof(sockaddr); /* it's just necessary, okay? */
connection_fd = accept(fd, (struct sockaddr *)&sockaddr, &socklen); /* make the connection meaningful */
pthread_create(&pth,NULL,processRequest,connection_fd); /* create our thread to process the request */
pthread_join(pth,NULL); /* then kill it when it's done */
}
pthread_join(pth,NULL); /* it doesn't hurt */
exit(EXIT_SUCCESS);
printf("test");
}
This relies on the pthread library...I think gcc comes with it. (This program can be compiled on any *nix OS, including MacOSX.) In case it slips your mind, compile with -lpthread to make pthread work.