first commit
This commit is contained in:
419
CS4210/Project 2/common/http/http.c
Normal file
419
CS4210/Project 2/common/http/http.c
Normal file
@@ -0,0 +1,419 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include "http.h"
|
||||
#include "../defs.h"
|
||||
|
||||
#define SERVER_NAME "ubermicro_httpd"
|
||||
#define SERVER_URL "http://www.puyan.org"
|
||||
#define PROTOCOL "HTTP/1.1"
|
||||
#define RFC1123FMT "%a, %d %b %Y %H:%M:%S GMT"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define S_ISDIR(a) (a & S_IFDIR)
|
||||
char lower(const char a);
|
||||
int strcasecmp(const char *s1, const char *s2);
|
||||
#endif
|
||||
|
||||
static void file_details( char* dir, char* name , FILE *socket_file);
|
||||
static void send_error( int status, char* title, char* extra_header, char* text, FILE *socket_file );
|
||||
static void send_headers( int status, char* title, char* extra_header, char* mime_type, off_t length, time_t mod, FILE *socket_file );
|
||||
static char* get_mime_type( char* name );
|
||||
static void strdecode( char* to, char* from );
|
||||
static int hexit( char c );
|
||||
static void strencode( char* to, size_t tosize, const char* from );
|
||||
|
||||
int parse_request(char *request, http_request *package) {
|
||||
if ( sscanf(request, "%[^ ] %[^ ] %[^ ]", package->method, package->path, package->protocol ) != 3 ) {
|
||||
return EXIT_FAILURE;
|
||||
} else {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
return EXIT_FAILURE; // Never Reached
|
||||
}
|
||||
|
||||
void http_proto(int socket, char *request) {
|
||||
char idx[2000], location[2000];
|
||||
char *method, *path, *protocol;
|
||||
char* file;
|
||||
size_t len;
|
||||
int ich;
|
||||
struct stat sb;
|
||||
FILE* fp;
|
||||
FILE* socket_file;
|
||||
struct dirent **dl;
|
||||
int i, n;
|
||||
char *brkt;
|
||||
char *true_request;
|
||||
http_request request_package;
|
||||
|
||||
VPRINTF(("\n\nStart http proc\n"));
|
||||
|
||||
VPRINTF(("Full Request:\n%s\n", request));
|
||||
|
||||
true_request=strtok_r(request,"\r", &brkt);
|
||||
|
||||
VPRINTF(("Request size: %d\n", strlen(request)));
|
||||
VPRINTF(("Small Request: \n%s\n", true_request));
|
||||
VPRINTF(("1 error on line %d\n", __LINE__));
|
||||
|
||||
socket_file = fdopen(socket, "w");
|
||||
fflush( socket_file ); //FIXME: May not be necessary, nothing written yet
|
||||
|
||||
if(strlen(request) >= BUFFERSIZE) {
|
||||
send_error( 403, "Bad Request", (char*) 0, "No request found." , socket_file );
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if (EXIT_FAILURE == parse_request(request, &request_package)) {
|
||||
send_error( 400, "Bad Request", (char*) 0, "Can't parse request." , socket_file );
|
||||
goto complete;
|
||||
} else {
|
||||
method = request_package.method;
|
||||
path = request_package.path;
|
||||
protocol = request_package.protocol;
|
||||
}
|
||||
|
||||
VPRINTF(("Method: %s\n", method));
|
||||
VPRINTF(("Path: %s\n", path));
|
||||
VPRINTF(("Protocol: %s\n", protocol));
|
||||
VPRINTF(("2 error on line %d\n", __LINE__));
|
||||
|
||||
if ( strcasecmp( method, "get" ) != 0 ) {
|
||||
send_error( 501, "Not Implemented", (char*) 0, "That method is not implemented." , socket_file );
|
||||
goto complete;
|
||||
}
|
||||
|
||||
VPRINTF(("3 error on line %d\n", __LINE__));
|
||||
|
||||
if ( path[0] != '/' ) {
|
||||
send_error( 400, "Bad Request", (char*) 0, "Bad filename." , socket_file );
|
||||
goto complete;
|
||||
}
|
||||
|
||||
file = &(path[1]);
|
||||
strdecode( file, file );
|
||||
|
||||
if ( file[0] == '\0' ) {
|
||||
file = "./";
|
||||
}
|
||||
|
||||
len = strlen( file );
|
||||
|
||||
VPRINTF(("4 error on line %d\n", __LINE__));
|
||||
|
||||
if ( file[0] == '/'
|
||||
|| strcmp( file, ".." ) == 0
|
||||
|| strncmp( file, "../", 3 ) == 0
|
||||
|| strstr( file, "/../" ) != (char*) 0
|
||||
|| strcmp( &(file[len-3]), "/.." ) == 0 )
|
||||
{
|
||||
send_error( 400, "Bad Request", (char*) 0, "Illegal filename." , socket_file );
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if ( stat( file, &sb ) < 0 ) {
|
||||
send_error( 404, "Not Found", (char*) 0, "File not found." , socket_file );
|
||||
goto complete;
|
||||
}
|
||||
|
||||
if ( S_ISDIR( sb.st_mode ) ) {
|
||||
if ( file[len-1] != '/' ) {
|
||||
snprintf(location, sizeof(location), "Location: %s/", path );
|
||||
send_error( 302, "Found", location, "Directories must end with a slash." , socket_file );
|
||||
goto complete;
|
||||
}
|
||||
|
||||
snprintf( idx, sizeof(idx), "%sindex.html", file );
|
||||
|
||||
if ( stat( idx, &sb ) >= 0 )
|
||||
{
|
||||
file = idx;
|
||||
goto do_file; //asshole
|
||||
}
|
||||
|
||||
/* IMPORTANT */
|
||||
send_headers( 200, "Ok", (char*) 0, "text/html", -1, sb.st_mtime , socket_file );
|
||||
|
||||
fprintf(socket_file,
|
||||
"<html><head><title>Index of %s</title></head>\n<body bgcolor=\"lightblue\"><h4>Index of %s</h4>\n<pre>\n",
|
||||
file, file
|
||||
);
|
||||
|
||||
n = scandir( file, &dl, NULL, alphasort ); /* IMPORTANT */
|
||||
|
||||
if ( n < 0 ) {
|
||||
perror( "scandir" );
|
||||
} else {
|
||||
for ( i = 0; i < n; ++i ) {
|
||||
/* IMPORTANT */
|
||||
file_details( file, dl[i]->d_name, socket_file );
|
||||
free(dl[i]);
|
||||
/* the guy that wrote this was a n00b */
|
||||
}
|
||||
free(dl); /* Must kill this guy */
|
||||
}
|
||||
|
||||
|
||||
fprintf(socket_file,
|
||||
"</pre>\n<hr>\n<address><a href=\"%s\">%s</a></address>\n</body></html>\n",
|
||||
SERVER_URL, SERVER_NAME
|
||||
);
|
||||
} else {
|
||||
do_file:
|
||||
fp = fopen( file, "r" );
|
||||
|
||||
if ( fp == (FILE*) 0 ) {
|
||||
send_error( 403, "Forbidden", (char*) 0, "File is protected.", socket_file );
|
||||
goto complete;
|
||||
}
|
||||
|
||||
/* IMPORTANT */
|
||||
send_headers( 200, "Ok", (char*) 0, get_mime_type( file ), sb.st_size, sb.st_mtime , socket_file );
|
||||
|
||||
while (fp && ( ich = getc( fp ) ) != EOF ) {
|
||||
fprintf(socket_file,"%c", ich );
|
||||
}
|
||||
|
||||
if (fp) fclose(fp);
|
||||
}
|
||||
|
||||
complete: //because this guy wrote terrible code, goto is necessary
|
||||
|
||||
fflush( socket_file );
|
||||
|
||||
fclose(socket_file);
|
||||
}
|
||||
|
||||
|
||||
static void file_details( char* dir, char* name, FILE *socket_file )
|
||||
{
|
||||
static char encoded_name[1000];
|
||||
static char path[2000];
|
||||
struct stat sb;
|
||||
char timestr[16];
|
||||
|
||||
strencode( encoded_name, sizeof(encoded_name), name );
|
||||
snprintf( path, sizeof(path), "%s/%s", dir, name );
|
||||
|
||||
#ifdef _WIN32
|
||||
if ( stat( path, &sb ) < 0 ) {
|
||||
#else
|
||||
if ( lstat( path, &sb ) < 0 ) {
|
||||
#endif
|
||||
fprintf(socket_file, "<a href=\"%s\">%-32.32s</a> ???\n", encoded_name, name );
|
||||
} else {
|
||||
strftime(timestr, sizeof(timestr),
|
||||
"%d%b%Y %H:%M", localtime( &sb.st_mtime )
|
||||
);
|
||||
fprintf(socket_file,
|
||||
"<a href=\"%s\">%-32.32s</a> %15s %14lld\n", encoded_name,
|
||||
name, timestr, (int64_t) sb.st_size
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void send_error( int status, char* title, char* extra_header, char* text, FILE *socket_file )
|
||||
{
|
||||
send_headers( status, title, extra_header, "text/html", -1, -1, socket_file );
|
||||
|
||||
fprintf(socket_file,
|
||||
"<html><head><title>%d %s</title></head>\n<body bgcolor=\"#cc9999\"><h4>%d %s</h4>\n",
|
||||
status, title, status, title
|
||||
);
|
||||
|
||||
fprintf(socket_file, "%s\n", text );
|
||||
fprintf(socket_file, "<hr>\n<address><a href=\"%s\">%s</a></address>\n", SERVER_URL, SERVER_NAME);
|
||||
//fprintf(socket_file, "<img src=\"/pwner.jpg\">\n");
|
||||
fprintf(socket_file, "</body></html>\n");
|
||||
|
||||
fflush( socket_file ); //flush socket
|
||||
}
|
||||
|
||||
|
||||
static void send_headers( int status, char* title, char* extra_header, char* mime_type, off_t length, time_t mod , FILE *socket_file)
|
||||
{
|
||||
time_t now;
|
||||
char timebuf[100];
|
||||
|
||||
fprintf(socket_file, "%s %d %s\015\012", PROTOCOL, status, title );
|
||||
fprintf(socket_file, "Server: %s\015\012", SERVER_NAME );
|
||||
|
||||
now = time( (time_t*) 0 );
|
||||
|
||||
strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &now ) );
|
||||
fprintf(socket_file, "Date: %s\015\012", timebuf );
|
||||
|
||||
if ( extra_header != (char*) 0 ) {
|
||||
fprintf(socket_file, "%s\015\012", extra_header );
|
||||
}
|
||||
if ( mime_type != (char*) 0 ) {
|
||||
fprintf(socket_file, "Content-Type: %s\015\012", mime_type );
|
||||
}
|
||||
if ( length >= 0 ) {
|
||||
fprintf(socket_file, "Content-Length: %lld\015\012", (int64_t) length );
|
||||
}
|
||||
if ( mod != (time_t) -1 )
|
||||
{
|
||||
strftime( timebuf, sizeof(timebuf), RFC1123FMT, gmtime( &mod ) );
|
||||
fprintf(socket_file, "Last-Modified: %s\015\012", timebuf );
|
||||
}
|
||||
fprintf(socket_file, "Connection: close\015\012" );
|
||||
fprintf(socket_file, "\015\012" );
|
||||
}
|
||||
|
||||
|
||||
static char* get_mime_type( char* name )
|
||||
{
|
||||
char* dot;
|
||||
|
||||
dot = strrchr( name, '.' );
|
||||
|
||||
if ( dot == (char*) 0 )
|
||||
return "text/plain; charset=iso-8859-1";
|
||||
|
||||
if ( strcmp( dot, ".html" ) == 0 || strcmp( dot, ".htm" ) == 0 )
|
||||
return "text/html; charset=iso-8859-1";
|
||||
|
||||
if ( strcmp( dot, ".jpg" ) == 0 || strcmp( dot, ".jpeg" ) == 0 )
|
||||
return "image/jpeg";
|
||||
|
||||
if ( strcmp( dot, ".gif" ) == 0 )
|
||||
return "image/gif";
|
||||
|
||||
if ( strcmp( dot, ".png" ) == 0 )
|
||||
return "image/png";
|
||||
|
||||
if ( strcmp( dot, ".css" ) == 0 )
|
||||
return "text/css";
|
||||
|
||||
if ( strcmp( dot, ".au" ) == 0 )
|
||||
return "audio/basic";
|
||||
|
||||
if ( strcmp( dot, ".wav" ) == 0 )
|
||||
return "audio/wav";
|
||||
|
||||
if ( strcmp( dot, ".avi" ) == 0 )
|
||||
return "video/x-msvideo";
|
||||
|
||||
if ( strcmp( dot, ".mov" ) == 0 || strcmp( dot, ".qt" ) == 0 )
|
||||
return "video/quicktime";
|
||||
|
||||
if ( strcmp( dot, ".mpeg" ) == 0 || strcmp( dot, ".mpe" ) == 0 )
|
||||
return "video/mpeg";
|
||||
|
||||
if ( strcmp( dot, ".vrml" ) == 0 || strcmp( dot, ".wrl" ) == 0 )
|
||||
return "model/vrml";
|
||||
|
||||
if ( strcmp( dot, ".midi" ) == 0 || strcmp( dot, ".mid" ) == 0 )
|
||||
return "audio/midi";
|
||||
|
||||
if ( strcmp( dot, ".mp3" ) == 0 )
|
||||
return "audio/mpeg";
|
||||
|
||||
if ( strcmp( dot, ".m4a" ) == 0 )
|
||||
return "audio/mp4";
|
||||
|
||||
if ( strcmp( dot, ".pdf" ) == 0 )
|
||||
return "application/pdf";
|
||||
|
||||
if ( strcmp( dot, ".ogg" ) == 0 )
|
||||
return "application/ogg";
|
||||
|
||||
if ( strcmp( dot, ".pac" ) == 0 )
|
||||
return "application/x-ns-proxy-autoconfig";
|
||||
|
||||
return "text/plain; charset=iso-8859-1";
|
||||
}
|
||||
|
||||
|
||||
static void strdecode( char* to, char* from )
|
||||
{
|
||||
for ( ; *from != '\0'; ++to, ++from )
|
||||
{
|
||||
if ( from[0] == '%' && isxdigit( from[1] ) && isxdigit( from[2] ) )
|
||||
{
|
||||
*to = hexit( from[1] ) * 16 + hexit( from[2] );
|
||||
from += 2;
|
||||
}
|
||||
else
|
||||
*to = *from;
|
||||
}
|
||||
*to = '\0';
|
||||
}
|
||||
|
||||
|
||||
static int hexit( char c )
|
||||
{
|
||||
if ( c >= '0' && c <= '9' )
|
||||
return c - '0';
|
||||
|
||||
if ( c >= 'a' && c <= 'f' )
|
||||
return c - 'a' + 10;
|
||||
|
||||
if ( c >= 'A' && c <= 'F' )
|
||||
return c - 'A' + 10;
|
||||
|
||||
//default
|
||||
return 0; /* shouldn't happen, we're guarded by isxdigit() */
|
||||
}
|
||||
|
||||
|
||||
static void strencode( char* to, size_t tosize, const char* from )
|
||||
{
|
||||
int tolen;
|
||||
|
||||
for ( tolen = 0; *from != '\0' && tolen + 4 < tosize; ++from )
|
||||
{
|
||||
if ( isalnum(*from) || strchr( "/_.-~", *from ) != (char*) 0 )
|
||||
{
|
||||
*to = *from;
|
||||
++to;
|
||||
++tolen;
|
||||
} else {
|
||||
sprintf( to, "%%%02x", (int) *from & 0xff );
|
||||
to += 3;
|
||||
tolen += 3;
|
||||
}
|
||||
}
|
||||
|
||||
*to = '\0';
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
char lower(const char a) {
|
||||
if (!isalpha(a)) return a;
|
||||
|
||||
if (!(a < 'z' && a > 'a')) {
|
||||
return a+32;
|
||||
} else {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
/* strcasecmp is not ANSI */
|
||||
int strcasecmp(const char *s1, const char *s2){
|
||||
size_t i;
|
||||
|
||||
for(i=0;; i++){
|
||||
if(!(lower(*(s1+i)) == lower(*(s2+i)) ) ) {
|
||||
return *(s1+i) - *(s2+i);
|
||||
}
|
||||
if(*(s1+i) == '\0'){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user