diff --git a/main.c b/main.c index 6e8044b..4f890ad 100644 --- a/main.c +++ b/main.c @@ -31,6 +31,11 @@ struct ConnectionState { struct stat targetStat; }; +struct RequestRange { + off_t start; + off_t length; +}; + char *shareRoot; struct MHD_Response *error403Response; struct MHD_Response *error404Response; @@ -54,12 +59,97 @@ void request_completed(void *cls, free(connstate); } +int parse_range(const char *range, off_t total_len, struct RequestRange *result) { + char *numstr; + char *dashptr; + int dashpos; + off_t num; + + LOG(LVL_DEBUG, "Requested Range is %s", range); + + if(range == NULL) { + result->start = 0; + result->length = total_len; + return 0; + } + + numstr = strchr(range, '='); + if(numstr == NULL) { + return -1; + } + + numstr += 1; + + LOG(LVL_DUMP, "Numeric part of range: %s", numstr); + + dashptr = strchr(numstr, '-'); + if(dashptr == NULL) { + // no '-' found + return -1; + } + + dashpos = dashptr - numstr; + LOG(LVL_DUMP, "Dash found at position: %i", dashpos); + + if(dashpos == 0) { + // example: bytes=-500 + // -> letzte 500 bytes ausgeben + if(sscanf(dashptr+1, "%li", &num) != 1) { + return -1; + } + + // total = 1000, num = 100 -> start=500, length=500 + result->start = total_len - num - 1; + result->length = num; + } else { + // examples: bytes=100-200; bytes=300- + + // parse the first number + if(sscanf(numstr, "%li", &num) != 1) { + return -1; + } + + result->start = num; + + if(numstr[dashpos + 1] == '\0') { + // everything from num to the end + result->length = total_len - num; + return 0; + } + + // a complete range was given -> parse the second number + if(sscanf(numstr+dashpos+1, "%li", &num) != 1) { + return -1; + } + + if(num < result->start) { + return -1; + } + + result->length = num - result->start + 1; + } + + return 0; +} + int serv_regular_file(struct MHD_Connection *connection, struct ConnectionState *connstate) { struct MHD_Response *response; + struct RequestRange range; + const char *rangestr; + char buf[256]; int ret; LOG(LVL_DEBUG, "Serving %s as a regular file.", connstate->localFileName); + rangestr = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Range"); + + if(0 != parse_range(rangestr, connstate->targetStat.st_size, &range)) { + LOG(LVL_ERR, "Cannot parse range header."); + return MHD_queue_response(connection, MHD_HTTP_INTERNAL_SERVER_ERROR, error500Response); + } + + LOG(LVL_DUMP, "Requested range: start=%lu, length=%lu", range.start, range.length); + // open a file descriptor to the file int fd = open(connstate->localFileName, O_RDONLY); @@ -71,9 +161,16 @@ int serv_regular_file(struct MHD_Connection *connection, struct ConnectionState } // no error so far -> serve the file - response = MHD_create_response_from_fd( - connstate->targetStat.st_size, - fd); + response = MHD_create_response_from_fd_at_offset( + range.length, + fd, + range.start); + + // build content range header + sprintf(buf, "bytes %li-%li/%li", range.start, range.start+range.length-1, connstate->targetStat.st_size); + MHD_add_response_header(response, MHD_HTTP_HEADER_CONTENT_RANGE, buf); + + LOG(LVL_DUMP, "Content-Range: %s", buf); #ifdef HAVE_MAGIC // if libmagic is available, determine the correct MIME type for the file