#include #include #include #include #include #include #include #include #include #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, "Index of %s\n

Index of %s

\n
\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,  
            "
\n
\n
%s
\n\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, "%-32.32s ???\n", encoded_name, name ); } else { strftime(timestr, sizeof(timestr), "%d%b%Y %H:%M", localtime( &sb.st_mtime ) ); fprintf(socket_file, "%-32.32s %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, "%d %s\n

%d %s

\n", status, title, status, title ); fprintf(socket_file, "%s\n", text ); fprintf(socket_file, "
\n
%s
\n", SERVER_URL, SERVER_NAME); //fprintf(socket_file, "\n"); fprintf(socket_file, "\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