Support more HTTP multipart inner headers

Now Content-Length and Content-Type are parsed as well as
Content-Disposition
This commit is contained in:
Andrew Bettison 2013-10-28 22:27:27 +10:30
parent 9d54c629b2
commit b9faf54c91
5 changed files with 317 additions and 166 deletions

View File

@ -281,11 +281,13 @@ static const char * _reserve(struct http_request *r, struct substring str)
return ret;
}
#if 0
static const char * _reserve_str(struct http_request *r, const char *str)
{
struct substring sub = { .start = str, .end = str + strlen(str) };
return _reserve(r, sub);
}
#endif
static inline int _end_of_content(struct http_request *r)
{
@ -556,6 +558,60 @@ static unsigned _parse_ranges(struct http_request *r, struct http_range *range,
return i;
}
static int _parse_content_type(struct http_request *r, struct mime_content_type *ct)
{
size_t n = _parse_token(r, ct->type, sizeof ct->type);
if (n == 0)
return 0;
if (n >= sizeof ct->type) {
WARNF("HTTP Content-Type type truncated: %s", alloca_str_toprint(ct->type));
return 0;
}
if (!_skip_literal(r, "/"))
return 0;
n = _parse_token(r, ct->subtype, sizeof ct->subtype);
if (n == 0)
return 0;
if (n >= sizeof ct->subtype) {
WARNF("HTTP Content-Type subtype truncated: %s", alloca_str_toprint(ct->subtype));
return 0;
}
while (_skip_optional_space(r) && _skip_literal(r, ";") && _skip_optional_space(r)) {
const char *start = r->cursor;
if (_skip_literal(r, "charset=")) {
size_t n = _parse_token_or_quoted_string(r, ct->charset, sizeof ct->charset);
if (n == 0)
return 0;
if (n >= sizeof ct->charset) {
WARNF("HTTP Content-Type charset truncated: %s", alloca_str_toprint(ct->charset));
return 0;
}
continue;
}
r->cursor = start;
if (_skip_literal(r, "boundary=")) {
size_t n = _parse_token_or_quoted_string(r, ct->multipart_boundary, sizeof ct->multipart_boundary);
if (n == 0)
return 0;
if (n >= sizeof ct->multipart_boundary) {
WARNF("HTTP Content-Type boundary truncated: %s", alloca_str_toprint(ct->multipart_boundary));
return 0;
}
continue;
}
r->cursor = start;
struct substring param;
if (_skip_token(r, &param) && _skip_literal(r, "=") && _parse_token_or_quoted_string(r, NULL, 0)) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping HTTP Content-Type parameter: %s", alloca_substring_toprint(param));
continue;
}
WARNF("Malformed HTTP Content-Type: %s", alloca_toprint(50, r->cursor, r->end - r->cursor));
return 0;
}
return 1;
}
static int _parse_quoted_rfc822_time(struct http_request *r, time_t *timep)
{
char datestr[40];
@ -743,6 +799,13 @@ static int http_request_parse_header(struct http_request *r)
_rewind(r);
const char *const sol = r->cursor;
if (_skip_literal_nocase(r, "Content-Length:")) {
if (r->request_header.content_length != CONTENT_LENGTH_UNKNOWN) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP header Content-Length: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
return 0;
}
_skip_optional_space(r);
http_size_t length;
if (_parse_http_size_t(r, &length) && _skip_optional_space(r) && r->cursor == eol) {
@ -757,50 +820,35 @@ static int http_request_parse_header(struct http_request *r)
}
_rewind(r);
if (_skip_literal_nocase(r, "Content-Type:")) {
if (r->request_header.content_type.type[0]) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP header Content-Type: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
return 0;
}
_skip_optional_space(r);
struct substring type = substring_NULL;
struct substring subtype = substring_NULL;
char boundary[BOUNDARY_STRING_MAXLEN + 1];
boundary[0] = '\0';
if (_skip_token(r, &type) && _skip_literal(r, "/") && _skip_token(r, &subtype)) {
// Parse zero or more content-type parameters.
for (_skip_optional_space(r); r->cursor < eol && _skip_literal(r, ";"); _skip_optional_space(r)) {
_skip_optional_space(r);
const char *startparam = r->cursor;
if (_skip_literal(r, "boundary=")) {
size_t n = _parse_token_or_quoted_string(r, boundary, sizeof boundary);
if (n == 0 || n >= sizeof boundary || !is_valid_http_boundary_string(boundary))
goto malformed;
continue;
}
// Silently ignore unrecognised parameters (eg, charset=) if they are well formed.
r->cursor = startparam; // partial rewind
if (_skip_token(r, NULL) && _skip_literal(r, "=") && _parse_token_or_quoted_string(r, NULL, 0))
continue;
break;
}
if (r->cursor == eol) {
r->cursor = nextline;
_commit(r);
if ( (r->request_header.content_type = _reserve(r, type)) == NULL
|| (r->request_header.content_subtype = _reserve(r, subtype)) == NULL
|| (boundary[0] && (r->request_header.boundary = _reserve_str(r, boundary)) == NULL)
)
return 0; // error
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP request Content-type: %s/%s%s%s",
r->request_header.content_type,
r->request_header.content_subtype,
r->request_header.boundary ? "; boundary=" : "",
r->request_header.boundary ? alloca_str_toprint(r->request_header.boundary) : ""
);
return 0;
}
if ( _parse_content_type(r, &r->request_header.content_type)
&& _skip_optional_space(r)
&& r->cursor == eol
) {
r->cursor = nextline;
_commit(r);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP request Content-type: %s", alloca_mime_content_type(&r->request_header.content_type));
return 0;
}
goto malformed;
}
_rewind(r);
if (_skip_literal_nocase(r, "Range:")) {
if (r->request_header.content_range_count) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP header Range: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
return 0;
}
_skip_optional_space(r);
unsigned int n;
if ( _skip_literal(r, "bytes=")
@ -858,7 +906,7 @@ static int http_request_start_body(struct http_request *r)
DEBUGF("Malformed HTTP %s request: non-zero Content-Length not allowed", r->verb);
return 400;
}
if (r->request_header.content_type) {
if (r->request_header.content_type.type) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s request: Content-Type not allowed", r->verb);
return 400;
@ -871,26 +919,28 @@ static int http_request_start_body(struct http_request *r)
DEBUGF("Malformed HTTP %s request: missing Content-Length header", r->verb);
return 411;
}
if (r->request_header.content_type == NULL) {
if (r->request_header.content_type.type == NULL) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s request: missing Content-Type header", r->verb);
return 400;
}
if ( strcmp(r->request_header.content_type, "multipart") == 0
&& strcmp(r->request_header.content_subtype, "form-data") == 0
if ( strcmp(r->request_header.content_type.type, "multipart") == 0
&& strcmp(r->request_header.content_type.subtype, "form-data") == 0
) {
if (r->request_header.boundary == NULL || r->request_header.boundary[0] == '\0') {
if ( r->request_header.content_type.multipart_boundary == NULL
|| r->request_header.content_type.multipart_boundary[0] == '\0'
) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Malformed HTTP %s request: Content-Type %s/%s missing boundary parameter",
r->verb, r->request_header.content_type, r->request_header.content_subtype);
r->verb, r->request_header.content_type.type, r->request_header.content_type.subtype);
return 400;
}
r->parser = http_request_parse_body_form_data;
r->form_data_state = START;
} else {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Unsupported HTTP %s request: Content-Type %s/%s not supported",
r->verb, r->request_header.content_type, r->request_header.content_subtype);
DEBUGF("Unsupported HTTP %s request: Content-Type %s not supported",
r->verb, alloca_mime_content_type(&r->request_header.content_type));
return 415;
}
}
@ -909,7 +959,7 @@ static int http_request_start_body(struct http_request *r)
*/
static int _skip_mime_boundary(struct http_request *r)
{
if (!_skip_literal(r, "--") || !_skip_literal(r, r->request_header.boundary))
if (!_skip_literal(r, "--") || !_skip_literal(r, r->request_header.content_type.multipart_boundary))
return 0;
if (_skip_literal(r, "--") && _skip_crlf(r))
return 2;
@ -988,6 +1038,43 @@ malformed:
return 1;
}
static void http_request_form_data_start_part(struct http_request *r, int b)
{
switch (r->form_data_state) {
case BODY:
if ( r->part_header.content_length != CONTENT_LENGTH_UNKNOWN
&& r->part_body_length != r->part_header.content_length
) {
WARNF("HTTP multipart part body length (%"PRIhttp_size_t") does not match Content-Length header (%"PRIhttp_size_t")",
r->part_body_length,
r->part_header.content_length
);
}
// fall through...
case HEADER:
if (r->form_data.handle_mime_part_end) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_end()");
r->form_data.handle_mime_part_end(r);
}
break;
default:
break;
}
if (b == 1) {
r->form_data_state = HEADER;
bzero(&r->part_header, sizeof r->part_header);
r->part_body_length = 0;
r->part_header.content_length = CONTENT_LENGTH_UNKNOWN;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
} else
r->form_data_state = EPILOGUE;
}
/* If parsing completes (ie, parsed to end of epilogue), then sets r->parser to NULL and returns 0,
* so this function will not be called again. If parsing cannot complete due to running out of
* data, returns 100, so this function will not be called again until more data has been read.
@ -1029,15 +1116,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
_rewind_crlf(r);
_commit(r);
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
} else
r->form_data_state = EPILOGUE;
http_request_form_data_start_part(r, b);
return 0;
}
}
@ -1074,6 +1153,16 @@ static int http_request_parse_body_form_data(struct http_request *r)
// A blank line finishes the headers. The CRLF does not form part of the body.
if (_skip_crlf(r)) {
_commit(r);
if (r->form_data.handle_mime_part_header) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_header(Content-Length: %"PRIhttp_size_t", Content-Type: %s, Content-Disposition: %s)",
r->part_header.content_length,
alloca_mime_content_type(&r->part_header.content_type),
alloca_mime_content_disposition(&r->part_header.content_disposition)
);
r->form_data.handle_mime_part_header(r, &r->part_header);
}
r->form_data_state = BODY;
return 0;
}
@ -1086,23 +1175,9 @@ static int http_request_parse_body_form_data(struct http_request *r)
if ((b = _skip_mime_boundary(r))) {
_rewind_crlf(r);
_commit(r);
if (r->form_data.handle_mime_part_end) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_end()");
r->form_data.handle_mime_part_end(r);
}
// A boundary in the middle of headers finishes the current part and starts a new part.
// An end boundary terminates the current part and starts the epilogue.
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
}
else
r->form_data_state = EPILOGUE;
http_request_form_data_start_part(r, b);
return 0;
}
if (_run_out(r))
@ -1115,27 +1190,54 @@ static int http_request_parse_body_form_data(struct http_request *r)
strncpy(labelstr, label.start, labellen)[labellen] = '\0';
str_tolower_inplace(labelstr);
const char *value = r->cursor;
if (strcmp(labelstr, "content-disposition") == 0) {
struct mime_content_disposition cd;
bzero(&cd, sizeof cd);
if (_parse_content_disposition(r, &cd) && _skip_optional_space(r) && _skip_crlf(r)) {
if (strcmp(labelstr, "content-length") == 0) {
if (r->part_header.content_length != CONTENT_LENGTH_UNKNOWN) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP multipart header Content-Length: %s", alloca_toprint(50, sol, r->end - sol));
return 400;
}
http_size_t length;
if (_parse_http_size_t(r, &length) && _skip_optional_space(r) && _skip_crlf(r)) {
_rewind_crlf(r);
_commit(r);
if (r->form_data.handle_mime_content_disposition) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_content_disposition(%s)", alloca_mime_content_disposition(&cd));
r->form_data.handle_mime_content_disposition(r, &cd);
}
r->part_header.content_length = length;
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP multipart header Content-Length: %"PRIhttp_size_t, r->part_header.content_length);
return 0;
}
}
else if (strcmp(labelstr, "content-type") == 0) {
if (r->part_header.content_type.type[0]) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP multipart header Content-Type: %s", alloca_toprint(50, sol, r->end - sol));
return 400;
}
if (_parse_content_type(r, &r->part_header.content_type) && _skip_optional_space(r) && _skip_crlf(r)) {
_rewind_crlf(r);
_commit(r);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP multipart header Content-Type: %s", alloca_mime_content_type(&r->part_header.content_type));
return 0;
}
}
else if (strcmp(labelstr, "content-disposition") == 0) {
if (r->part_header.content_disposition.type[0]) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skipping duplicate HTTP multipart header Content-Disposition: %s", alloca_toprint(50, sol, r->end - sol));
return 400;
}
if (_parse_content_disposition(r, &r->part_header.content_disposition) && _skip_optional_space(r) && _skip_crlf(r)) {
_rewind_crlf(r);
_commit(r);
if (r->debug_flag && *r->debug_flag)
DEBUGF("Parsed HTTP multipart header Content-Disposition: %s", alloca_mime_content_disposition(&r->part_header.content_disposition));
return 0;
}
}
else if (_skip_to_crlf(r)) {
_commit(r);
if (r->form_data.handle_mime_header) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_header(%s, %s)", alloca_str_toprint(labelstr), alloca_toprint(-1, value, value - r->cursor));
r->form_data.handle_mime_header(r, labelstr, value, value - r->cursor); // excluding CRLF at end
}
if (r->debug_flag && *r->debug_flag)
DEBUGF("Skip HTTP multipart header: %s: %s", alloca_str_toprint(labelstr), alloca_toprint(-1, value, value - r->cursor));
return 0;
}
}
@ -1169,25 +1271,14 @@ static int http_request_parse_body_form_data(struct http_request *r)
if ((b = _skip_mime_boundary(r))) {
_rewind_crlf(r);
_commit(r);
assert(end_body >= start);
r->part_body_length += end_body - start;
if (end_body > start && r->form_data.handle_mime_body) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_body(%s length=%zu)", alloca_toprint(80, start, end_body - start), end_body - start);
r->form_data.handle_mime_body(r, start, end_body - start); // excluding CRLF at end
}
if (r->form_data.handle_mime_part_end) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_end()");
r->form_data.handle_mime_part_end(r);
}
r->form_data_state = EPILOGUE;
if (b == 1) {
r->form_data_state = HEADER;
if (r->form_data.handle_mime_part_start) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_part_start()");
r->form_data.handle_mime_part_start(r);
}
}
http_request_form_data_start_part(r, b);
return 0;
}
}
@ -1198,6 +1289,8 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
_rewind_optional_cr(r);
_commit(r);
assert(r->parsed >= start);
r->part_body_length += r->parsed - start;
if (r->parsed > start && r->form_data.handle_mime_body) {
if (r->debug_flag && *r->debug_flag)
DEBUGF("handle_mime_body(%s length=%zu)", alloca_toprint(80, start, r->parsed - start), r->parsed - start);
@ -1538,18 +1631,6 @@ static const char *httpResultString(int response_code)
}
}
static strbuf strbuf_append_quoted_string(strbuf sb, const char *str)
{
strbuf_putc(sb, '"');
for (; *str; ++str) {
if (*str == '"' || *str == '\\')
strbuf_putc(sb, '\\');
strbuf_putc(sb, *str);
}
strbuf_putc(sb, '"');
return sb;
}
/* Render the HTTP response into the current response buffer. Return 1 if it fits, 0 if it does
* not. The buffer response_pointer may be NULL, in which case no response is rendered, but the
* content_length is still computed

View File

@ -56,11 +56,16 @@ http_size_t http_range_bytes(const struct http_range *range, unsigned nranges);
#define CONTENT_LENGTH_UNKNOWN UINT64_MAX
struct mime_content_type {
char type[64];
char subtype[64];
char multipart_boundary[71];
char charset[31];
};
struct http_request_headers {
http_size_t content_length;
const char *content_type;
const char *content_subtype;
const char *boundary;
struct mime_content_type content_type;
unsigned short content_range_count;
struct http_range content_ranges[5];
};
@ -94,11 +99,16 @@ struct mime_content_disposition {
time_t read_date;
};
struct mime_part_headers {
http_size_t content_length;
struct mime_content_type content_type;
struct mime_content_disposition content_disposition;
};
struct http_mime_handler {
void (*handle_mime_preamble)(struct http_request *, const char *, size_t);
void (*handle_mime_part_start)(struct http_request *);
void (*handle_mime_content_disposition)(struct http_request *, const struct mime_content_disposition *);
void (*handle_mime_header)(struct http_request *, const char *label, const char *, size_t);
void (*handle_mime_part_header)(struct http_request *, const struct mime_part_headers *);
void (*handle_mime_body)(struct http_request *, const char *, size_t);
void (*handle_mime_part_end)(struct http_request *);
void (*handle_mime_epilogue)(struct http_request *, const char *, size_t);
@ -118,31 +128,49 @@ typedef int (*HTTP_REQUEST_PARSER)(struct http_request *);
struct http_request {
struct sched_ent alarm; // MUST BE FIRST ELEMENT
// The following control the lifetime of this struct.
enum http_request_phase { RECEIVE, TRANSMIT, DONE } phase;
bool_t *debug_flag;
bool_t *disable_tx_flag;
time_ms_t initiate_time; // time connection was initiated
time_ms_t idle_timeout; // disconnect if no bytes received for this long
struct sockaddr_in client_sockaddr_in;
HTTP_REQUEST_PARSER parser; // current parser function
HTTP_REQUEST_PARSER handle_first_line; // called after first line is parsed
HTTP_REQUEST_PARSER handle_headers; // called after all headers are parsed
HTTP_REQUEST_PARSER handle_content_end; // called after all content is received
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
struct http_mime_handler form_data; // called to parse multipart/form-data body
void (*finalise)(struct http_request *);
void (*free)(void*);
// These can be set up to point to config flags, to allow debug to be
// enabled indpendently for different instances HTTP server instances
// that use this code.
bool_t *debug_flag;
bool_t *disable_tx_flag;
// The following are used for parsing the HTTP request.
time_ms_t initiate_time; // time connection was initiated
time_ms_t idle_timeout; // disconnect if no bytes received for this long
struct sockaddr_in client_sockaddr_in; // caller may supply this
// The parsed HTTP request is accumulated into the following fields.
const char *verb; // points to nul terminated static string, "GET", "PUT", etc.
const char *path; // points into buffer; nul terminated
uint8_t version_major; // m from from HTTP/m.n
uint8_t version_minor; // n from HTTP/m.n
struct http_request_headers request_header;
// Parsing is done by setting 'parser' to point to a series of parsing
// functions as the parsing state progresses.
HTTP_REQUEST_PARSER parser; // current parser function
// The caller may set these up, and they are invoked by the parser as request
// parsing reaches different stages.
HTTP_REQUEST_PARSER handle_first_line; // called after first line is parsed
HTTP_REQUEST_PARSER handle_headers; // called after all HTTP headers are parsed
HTTP_REQUEST_PARSER handle_content_end; // called after all content is received
// The following are used for managing the buffer during RECEIVE phase.
const char *received; // start of received data in buffer[]
const char *end; // end of received data in buffer[]
const char *parsed; // start of unparsed data in buffer[]
const char *cursor; // for parsing
http_size_t request_content_remaining;
// The following are used for parsing a multipart body.
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
struct http_mime_handler form_data;
struct mime_part_headers part_header;
http_size_t part_body_length;
// The following are used for constructing the response that will be sent in
// TRANSMIT phase.
struct http_response response;
// The following are used during TRANSMIT phase to control buffering and
// sending.
http_size_t response_length; // total response bytes (header + content)
http_size_t response_sent; // for counting up to response_length
char *response_buffer;
@ -150,6 +178,7 @@ struct http_request {
size_t response_buffer_length;
size_t response_buffer_sent;
void (*response_free_buffer)(void*);
// This buffer is used during RECEIVE and TRANSMIT phase.
char buffer[8 * 1024];
};

View File

@ -54,7 +54,7 @@ static void rhizome_direct_clear_temporary_files(rhizome_http_request *r)
}
}
int rhizome_direct_import_end(struct http_request *hr)
static int rhizome_direct_import_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (!r->received_manifest) {
@ -168,7 +168,7 @@ int rhizome_direct_enquiry_end(struct http_request *hr)
return 0;
}
int rhizome_direct_addfile_end(struct http_request *hr)
static int rhizome_direct_addfile_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
// If given a file without a manifest, we should only accept if it we are configured to do so, and
@ -271,14 +271,14 @@ int rhizome_direct_addfile_end(struct http_request *hr)
}
}
void rhizome_direct_process_mime_start(struct http_request *hr)
static void rhizome_direct_process_mime_start(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
assert(r->current_part == NONE);
assert(r->part_fd == -1);
}
void rhizome_direct_process_mime_end(struct http_request *hr)
static void rhizome_direct_process_mime_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (r->part_fd != -1) {
@ -302,19 +302,19 @@ void rhizome_direct_process_mime_end(struct http_request *hr)
r->current_part = NONE;
}
void rhizome_direct_process_mime_content_disposition(struct http_request *hr, const struct mime_content_disposition *cd)
static void rhizome_direct_process_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (strcmp(cd->name, "data") == 0) {
if (strcmp(h->content_disposition.name, "data") == 0) {
r->current_part = DATA;
strncpy(r->data_file_name, cd->filename, sizeof r->data_file_name)[sizeof r->data_file_name - 1] = '\0';
strncpy(r->data_file_name, h->content_disposition.filename, sizeof r->data_file_name)[sizeof r->data_file_name - 1] = '\0';
}
else if (strcmp(cd->name, "manifest") == 0) {
else if (strcmp(h->content_disposition.name, "manifest") == 0) {
r->current_part = MANIFEST;
} else
return;
char path[512];
if (form_temporary_file_path(r, path, cd->name) == -1) {
if (form_temporary_file_path(r, path, h->content_disposition.name) == -1) {
http_request_simple_response(&r->http, 500, "Internal Error: Buffer overrun");
return;
}
@ -325,7 +325,7 @@ void rhizome_direct_process_mime_content_disposition(struct http_request *hr, co
}
}
void rhizome_direct_process_mime_body(struct http_request *hr, const char *buf, size_t len)
static void rhizome_direct_process_mime_body(struct http_request *hr, const char *buf, size_t len)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
if (r->part_fd != -1) {
@ -344,7 +344,7 @@ int rhizome_direct_import(rhizome_http_request *r, const char *remainder)
}
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
r->http.handle_content_end = rhizome_direct_import_end;
r->current_part = NONE;
@ -361,7 +361,7 @@ int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder)
}
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
r->http.handle_content_end = rhizome_direct_enquiry_end;
r->current_part = NONE;
@ -394,7 +394,7 @@ int rhizome_direct_addfile(rhizome_http_request *r, const char *remainder)
}
r->http.form_data.handle_mime_part_start = rhizome_direct_process_mime_start;
r->http.form_data.handle_mime_part_end = rhizome_direct_process_mime_end;
r->http.form_data.handle_mime_content_disposition = rhizome_direct_process_mime_content_disposition;
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
r->http.handle_content_end = rhizome_direct_addfile_end;
r->current_part = NONE;

View File

@ -402,6 +402,18 @@ strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt)
return sb;
}
strbuf strbuf_append_quoted_string(strbuf sb, const char *str)
{
strbuf_putc(sb, '"');
for (; *str; ++str) {
if (*str == '"' || *str == '\\')
strbuf_putc(sb, '\\');
strbuf_putc(sb, *str);
}
strbuf_putc(sb, '"');
return sb;
}
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels)
{
unsigned i;
@ -427,31 +439,47 @@ strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, uns
return sb;
}
strbuf strbuf_append_mime_content_disposition(strbuf sb, const struct mime_content_disposition *cd)
strbuf strbuf_append_mime_content_type(strbuf sb, const struct mime_content_type *ct)
{
strbuf_puts(sb, "type=");
strbuf_toprint_quoted(sb, "``", cd->type);
strbuf_puts(sb, " name=");
strbuf_toprint_quoted(sb, "``", cd->name);
strbuf_puts(sb, " filename=");
strbuf_toprint_quoted(sb, "``", cd->filename);
strbuf_puts(sb, " size=");
strbuf_sprintf(sb, "%"PRIhttp_size_t, cd->size);
struct tm tm;
strbuf_puts(sb, " creation_date=");
if (cd->creation_date)
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->creation_date, &tm));
else
strbuf_puts(sb, "0");
strbuf_puts(sb, " modification_date=");
if (cd->modification_date)
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->modification_date, &tm));
else
strbuf_puts(sb, "0");
strbuf_puts(sb, " read_date=");
if (cd->read_date)
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->read_date, &tm));
else
strbuf_puts(sb, "0");
strbuf_puts(sb, ct->type);
strbuf_putc(sb, '/');
strbuf_puts(sb, ct->subtype);
if (ct->charset) {
strbuf_puts(sb, "; charset=");
strbuf_append_quoted_string(sb, ct->charset);
}
if (ct->multipart_boundary) {
strbuf_puts(sb, "; boundary=");
strbuf_append_quoted_string(sb, ct->multipart_boundary);
}
return sb;
}
strbuf strbuf_append_mime_content_disposition(strbuf sb, const struct mime_content_disposition *cd)
{
strbuf_puts(sb, cd->type);
if (cd->name) {
strbuf_puts(sb, "; name=");
strbuf_append_quoted_string(sb, cd->name);
}
if (cd->filename) {
strbuf_puts(sb, "; filename=");
strbuf_append_quoted_string(sb, cd->filename);
}
if (cd->size)
strbuf_sprintf(sb, "; size=%"PRIhttp_size_t, cd->size);
struct tm tm;
if (cd->creation_date) {
strbuf_puts(sb, " creation_date=");
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->creation_date, &tm));
}
if (cd->modification_date) {
strbuf_puts(sb, " modification_date=");
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->modification_date, &tm));
}
if (cd->read_date) {
strbuf_puts(sb, " read_date=");
strbuf_append_strftime(sb, "\"%a, %d %b %Y %T %z\"", gmtime_r(&cd->read_date, &tm));
}
return sb;
}

View File

@ -145,6 +145,12 @@ struct iovec;
strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt);
#define alloca_iovec(iov,cnt) strbuf_str(strbuf_append_iovec(strbuf_alloca(200), (iov), (cnt)))
/* Append a string using HTTP quoted-string format: delimited by double quotes (") and
* internal double quotes and backslash escaped by leading backslash.
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_append_quoted_string(strbuf sb, const char *str);
/* Append a representation of a struct http_range[] array.
* @author Andrew Bettison <andrew@servalproject.com>
*/
@ -152,7 +158,14 @@ struct http_range;
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels);
#define alloca_http_ranges(ra) strbuf_str(strbuf_append_http_ranges(strbuf_alloca(25*NELS(ra)), (ra), NELS(ra)))
/* Append a representation of a struct mime_content_disposition struct.
/* Append a representation of a struct mime_content_type in HTTP header format.
* @author Andrew Bettison <andrew@servalproject.com>
*/
struct mime_content_type;
strbuf strbuf_append_mime_content_type(strbuf, const struct mime_content_type *);
#define alloca_mime_content_type(ct) strbuf_str(strbuf_append_mime_content_type(strbuf_alloca(500), (ct)))
/* Append a representation of a struct mime_content_disposition, in HTTP header format.
* @author Andrew Bettison <andrew@servalproject.com>
*/
struct mime_content_disposition;