jimw 00/12/16 16:36:39
Modified: . Changes
c apache_multipart_buffer.c apache_multipart_buffer.h
apache_request.c
Log:
initial multipart_buffer cleanup (with style more-or-less conforming to
existing code, this time)
Revision Changes Path
1.22 +10 -1 httpd-apreq/Changes
Index: Changes
===================================================================
RCS file: /home/cvs/httpd-apreq/Changes,v
retrieving revision 1.21
retrieving revision 1.22
diff -u -r1.21 -r1.22
--- Changes 2000/12/16 23:36:45 1.21
+++ Changes 2000/12/17 00:36:37 1.22
@@ -2,7 +2,16 @@
=over 4
-=item 0.32 - August 16, 1999
+=item 0.32 - tbd
+
+keep reusing same buffer when handling file uploads to prevent overzealous
+memory allocation [Yeasah Pell, Jim Winstead <[EMAIL PROTECTED]>]
+
+handle internet explorer for the macintosh bug that sends corrupt mime
+boundaries when image submit buttons are used in multipart/form-data forms
+[Yeasah Pell]
+
+fix uninitialized variable in ApacheRequest_parse_urlencoded [Yeasah Pell]
multipart_buffer.[ch] renamed to apache_multipart_buffer.[ch]
1.2 +220 -216 httpd-apreq/c/apache_multipart_buffer.c
Index: apache_multipart_buffer.c
===================================================================
RCS file: /home/cvs/httpd-apreq/c/apache_multipart_buffer.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- apache_multipart_buffer.c 2000/03/30 06:04:41 1.1
+++ apache_multipart_buffer.c 2000/12/17 00:36:38 1.2
@@ -57,271 +57,275 @@
#include "apache_multipart_buffer.h"
-#define FILLUNIT (1024 * 5)
-#ifndef CRLF
-#define CRLF "\015\012"
-#endif
-#define CRLF_CRLF "\015\012\015\012"
+/*********************** internal functions *********************/
-static char *my_join(pool *p, char *one, int lenone, char *two, int lentwo)
+/*
+ search for a string in a fixed-length byte string.
+ if partial is true, partial matches are allowed at the end of the buffer.
+ returns NULL if not found, or a pointer to the start of the first match.
+*/
+void* my_memstr(void* haystack, int haystacklen, const char* needle,
+ int partial)
{
- char *res;
- int len = lenone+lentwo;
- res = (char *)ap_palloc(p, len + 1);
- memcpy(res, one, lenone);
- memcpy(res+lenone, two, lentwo);
- return res;
-}
+ int needlen = strlen(needle);
+ int len = haystacklen;
+ void *ptr = haystack;
+
+ /* iterate through first character matches */
+ while( (ptr = memchr(ptr, needle[0], len)) ) {
+ /* calculate length after match */
+ len = haystacklen - (ptr - haystack);
+
+ /* done if matches up to capacity of buffer */
+ if(memcmp(needle, ptr, needlen < len ? needlen : len) == 0 &&
+ (partial || len >= needlen))
+ break;
-static char *
-my_ninstr(register char *big, register char *bigend, char *little, char
*lend)
-{
- register char *s, *x;
- register int first = *little;
- register char *littleend = lend;
-
- if (!first && little >= littleend) {
- return big;
- }
- if (bigend - big < littleend - little) {
- return NULL;
- }
- bigend -= littleend - little++;
- while (big <= bigend) {
- if (*big++ != first) {
- continue;
- }
- for (x=big,s=little; s < littleend; /**/ ) {
- if (*s++ != *x++) {
- s--;
- break;
- }
- }
- if (s >= littleend) {
- return big-1;
- }
+ /* next character */
+ ptr++; len--;
}
- return NULL;
-}
+ return ptr;
+}
+
/*
- * This fills up our internal buffer in such a way that the
- * boundary is never split between reads
- */
-void multipart_buffer_fill(multipart_buffer *self, long bytes)
+ fill up the buffer with client data.
+ returns number of bytes added to buffer.
+*/
+int fill_buffer(multipart_buffer *self)
{
- int len_read, length, bytes_to_read;
- int buffer_len = self->buffer_len;
+ int bytes_to_read, actual_read = 0;
- if (!self->length) {
- return;
- }
- length = (bytes - buffer_len) + self->boundary_length + 2;
- if (self->length < length) {
- length = self->length;
+ /* shift the existing data if necessary */
+ if(self->bytes_in_buffer > 0 && self->buf_begin != self->buffer)
+ memmove(self->buffer, self->buf_begin, self->bytes_in_buffer);
+ self->buf_begin = self->buffer;
+
+ /* calculate the free space in the buffer */
+ bytes_to_read = self->bufsize - self->bytes_in_buffer;
+
+ /* read the required number of bytes */
+ if(bytes_to_read > 0) {
+ char *buf = self->buffer + self->bytes_in_buffer;
+ ap_hard_timeout("[libapreq] multipart_buffer.c:fill_buffer", self->r);
+ actual_read = ap_get_client_block(self->r, buf, bytes_to_read);
+ ap_kill_timeout(self->r);
+
+ /* update the buffer length */
+ if(actual_read > 0)
+ self->bytes_in_buffer += actual_read;
}
- bytes_to_read = length;
-
- ap_hard_timeout("multipart_buffer_fill", self->r);
- while (bytes_to_read > 0) {
- /*XXX: we can optimize this loop*/
- char *buff = (char *)ap_pcalloc(self->subp,
- sizeof(char) * bytes_to_read + 1);
- len_read = ap_get_client_block(self->r, buff, bytes_to_read);
- if (len_read <= 0) {
- ap_log_rerror(MPB_ERROR,
- "[libapreq] client dropped connection during read");
- self->length = 0;
- self->buffer = NULL;
- self->buffer_len = 0;
- break;
- }
-
- self->buffer = self->buffer ?
- my_join(self->r->pool,
- self->buffer, self->buffer_len,
- buff, len_read) :
- ap_pstrndup(self->r->pool, buff, len_read);
-
- self->total += len_read;
- self->buffer_len += len_read;
- self->length -= len_read;
- bytes_to_read -= len_read;
+ return actual_read;
+}
- ap_reset_timeout(self->r);
+/*
+ gets the next CRLF terminated line from the input buffer.
+ if it doesn't find a CRLF, and the buffer isn't completely full, returns
+ NULL; otherwise, returns the beginning of the null-terminated line,
+ minus the CRLF.
+
+ note that we really just look for LF terminated lines. this works
+ around a bug in internet explorer for the macintosh which sends mime
+ boundaries that are only LF terminated when you use an image submit
+ button in a multipart/form-data form.
+ */
+char* next_line(multipart_buffer *self)
+{
+ /* look for LF in the data */
+ char* line = self->buf_begin;
+ char* ptr = memchr(self->buf_begin, '\n', self->bytes_in_buffer);
+
+ /* LF found */
+ if(ptr) {
+ /* terminate the string, remove CRLF */
+ if((ptr - line) > 0 && *(ptr-1) == '\r') *(ptr-1) = 0;
+ else *ptr = 0;
+
+ /* bump the pointer */
+ self->buf_begin = ptr + 1;
+ self->bytes_in_buffer -= (self->buf_begin - line);
+ }
+
+ /* no LF found */
+ else {
+ /* buffer isn't completely full, fail */
+ if(self->bytes_in_buffer < self->bufsize)
+ return NULL;
+
+ /* return entire buffer as a partial line */
+ line[self->bufsize] = 0;
+ self->buf_begin = ptr;
+ self->bytes_in_buffer = 0;
}
- ap_kill_timeout(self->r);
- ap_clear_pool(self->subp);
+
+ return line;
}
-char *multipart_buffer_read(multipart_buffer *self, long bytes, int *blen)
+/* returns the next CRLF terminated line from the client */
+char* get_line(multipart_buffer *self)
{
- int start = -1;
- char *retval, *str;
+ char* ptr = next_line(self);
- /* default number of bytes to read */
- if (!bytes) {
- bytes = FILLUNIT;
+ if(!ptr) {
+ fill_buffer(self);
+ ptr = next_line(self);
}
+
+#ifdef DEBUG
+ ap_log_rerror(MPB_ERROR, "get_line: '%s'", ptr);
+#endif
- /*
- * Fill up our internal buffer in such a way that the boundary
- * is never split between reads.
- */
- multipart_buffer_fill(self, bytes);
-
- /* Find the boundary in the buffer (it may not be there). */
- if (self->buffer) {
- if ((str = my_ninstr(self->buffer,
- self->buffer+self->buffer_len,
- self->boundary,
- self->boundary+self->boundary_length)))
- {
- start = str - self->buffer;
- }
- }
+ return ptr;
+}
+
+/* finds a boundary */
+int find_boundary(multipart_buffer *self, char *boundary)
+{
+ int len, bound_len = strlen(boundary);
+ char *line;
+
+ /* loop thru lines */
+ while( (line = get_line(self)) ) {
+#ifdef DEBUG
+ ap_log_rerror(MPB_ERROR, "find_boundary: '%s' ?= '%s'",
+ line, boundary);
+#endif
- /* protect against malformed multipart POST operations */
- if (!(start >= 0 || self->length > 0)) {
- ap_log_rerror(MPB_ERROR,
- "[libapreq] malformed upload: start=%d, self->length=%d",
- start, (int)self->length);
- return NULL;
+ /* finished if we found the boundary */
+ if(strcmp(line, boundary) == 0)
+ return 1;
}
- /*
- * If the boundary begins the data, then skip past it
- * and return NULL. The +2 here is a fiendish plot to
- * remove the CR/LF pair at the end of the boundary.
- */
- if (start == 0) {
- /* clear us out completely if we've hit the last boundary. */
- if (strEQ(self->buffer, self->boundary_end)) {
- self->buffer = NULL;
- self->buffer_len = 0;
- self->length = 0;
- return NULL;
- }
+ /* didn't find the boundary */
+ return 0;
+}
- /* otherwise just remove the boundary. */
- self->buffer += (self->boundary_length + 2);
- self->buffer_len -= (self->boundary_length + 2);
- return NULL;
- }
+/*********************** external functions *********************/
- if (start > 0) { /* read up to the boundary */
- *blen = start > bytes ? bytes : start;
- }
- else { /* read the requested number of bytes */
- /*
- * leave enough bytes in the buffer to allow us to read
- * the boundary. Thanks to Kevin Hendrick for finding
- * this one.
- */
- *blen = bytes - (self->boundary_length + 1);
- }
-
- retval = ap_pstrndup(self->r->pool, self->buffer, *blen);
-
- self->buffer += *blen;
- self->buffer_len -= *blen;
+/* create new multipart_buffer structure */
+multipart_buffer *multipart_buffer_new(char *boundary, long length,
request_rec *r)
+{
+ multipart_buffer *self = (multipart_buffer *)
+ ap_pcalloc(r->pool, sizeof(multipart_buffer));
- /* If we hit the boundary, remove the CRLF from the end. */
- if (start > 0) {
- *blen -= 2;
- retval[*blen] = '\0';
- }
+ int minsize = strlen(boundary)+6;
+ if(minsize < FILLUNIT) minsize = FILLUNIT;
+
+ self->r = r;
+ self->buffer = (char *) ap_pcalloc(r->pool, minsize+1);
+ self->bufsize = minsize;
+ self->request_length = length;
+ self->boundary = ap_pstrcat(r->pool, "--", boundary, NULL);
+ self->boundary_next = ap_pstrcat(r->pool, "\n", self->boundary, NULL);
+ self->buf_begin = self->buffer;
+ self->bytes_in_buffer = 0;
- return retval;
+ return self;
}
+/* parse headers and return them in an apache table */
table *multipart_buffer_headers(multipart_buffer *self)
{
- int end=0, ok=0, bad=0;
table *tab;
- char *header;
-
- do {
- char *str;
- multipart_buffer_fill(self, FILLUNIT);
- if ((str = strstr(self->buffer, CRLF_CRLF))) {
- ++ok;
- end = str - self->buffer;
- }
- if (self->buffer == NULL || *self->buffer == '\0') {
- ++ok;
- }
- if (!ok && self->length <= 0) {
- ++bad;
- }
- } while (!ok && !bad);
+ char *line;
- if (bad) {
- return NULL;
- }
-
- header = ap_pstrndup(self->r->pool, self->buffer, end+2);
- /*XXX: need to merge continuation lines here?*/
+ /* didn't find boundary, abort */
+ if(!find_boundary(self, self->boundary)) return NULL;
+ /* get lines of text, or CRLF_CRLF */
tab = ap_make_table(self->r->pool, 10);
- self->buffer += end+4;
- self->buffer_len -= end+4;
+ while( (line = get_line(self)) && strlen(line) > 0 ) {
+ /* add header to table */
+ char *key = line;
+ char *value = strchr(line, ':');
+
+ if(value) {
+ *value = 0;
+ do { value++; } while(ap_isspace(*value));
- {
- char *entry;
- while ((entry = ap_getword_nc(self->r->pool, &header, '\r')) &&
*header) {
- char *key;
- key = ap_getword_nc(self->r->pool, &entry, ':');
- while (ap_isspace(*entry)) {
- ++entry;
- }
- if (*header == '\n') {
- ++header;
- }
- ap_table_add(tab, key, entry);
+#ifdef DEBUG
+ ap_log_rerror(MPB_ERROR,
+ "multipart_buffer_headers: '%s' = '%s'",
+ key, value);
+#endif
+
+ ap_table_add(tab, key, value);
}
+ else {
+#ifdef DEBUG
+ ap_log_rerror(MPB_ERROR,
+ "multipart_buffer_headers: '%s' = ''", key);
+#endif
+
+ ap_table_add(tab, key, "");
+ }
}
return tab;
}
-char *multipart_buffer_read_body(multipart_buffer *self)
+/* read until a boundary condition */
+int multipart_buffer_read(multipart_buffer *self, char *buf, int bytes)
{
- char *data, *retval=NULL;
- int blen = 0, old_len = 0;
+ int len, max;
+ char *bound;
+
+ /* fill buffer if needed */
+ if(bytes > self->bytes_in_buffer) fill_buffer(self);
+
+ /* look for a potential boundary match, only read data up to that point
*/
+ if( (bound = my_memstr(self->buf_begin, self->bytes_in_buffer,
+ self->boundary_next, 1)) )
+ max = bound - self->buf_begin;
+ else
+ max = self->bytes_in_buffer;
- while ((data = multipart_buffer_read(self, 0, &blen))) {
- retval = retval ?
- my_join(self->r->pool, retval, old_len, data, blen) :
- ap_pstrndup(self->r->pool, data, blen);
- old_len = blen;
+ /* maximum number of bytes we are reading */
+ len = max < bytes-1 ? max : bytes-1;
+
+ /* if we read any data... */
+ if(len > 0) {
+ /* copy the data */
+ memcpy(buf, self->buf_begin, len);
+ buf[len] = 0;
+ if(bound && len > 0 && buf[len-1] == '\r') buf[len--] = 0;
+
+ /* update the buffer */
+ self->bytes_in_buffer -= len;
+ self->buf_begin += len;
}
+
+#ifdef DEBUG
+ ap_log_rerror(MPB_ERROR, "multipart_buffer_read: %d bytes", len);
+#endif
- return retval;
+ return len;
}
-multipart_buffer *multipart_buffer_new(char *boundary, long length,
request_rec *r)
+/*
+ XXX: this is horrible memory-usage-wise, but we only expect
+ to do this on small pieces of form data.
+*/
+char *multipart_buffer_read_body(multipart_buffer *self)
{
- int blen;
- multipart_buffer *self = (multipart_buffer *)
- ap_pcalloc(r->pool, sizeof(multipart_buffer));
+ char buf[FILLUNIT], *out = "";
- self->r = r;
- self->length = length;
- self->boundary = ap_pstrcat(r->pool, "--", boundary, NULL);
- self->boundary_length = strlen(self->boundary);
- self->boundary_end = ap_pstrcat(r->pool, self->boundary, "--", NULL);
- self->buffer = NULL;
- self->buffer_len = 0;
- self->subp = ap_make_sub_pool(self->r->pool);
+ while(multipart_buffer_read(self, buf, sizeof(buf)))
+ out = ap_pstrcat(self->r->pool, out, buf, NULL);
- /* Read the preamble and the topmost (boundary) line plus the CRLF. */
- (void)multipart_buffer_read(self, 0, &blen);
+#ifdef DEBUG
+ ap_log_rerror(MPB_ERROR, "multipart_buffer_read_body: '%s'", out);
+#endif
- if (multipart_buffer_eof(self)) {
- return NULL;
- }
+ return out;
+}
- return self;
+/* eof if we are out of bytes, or if we hit the final boundary */
+int multipart_buffer_eof(multipart_buffer *self)
+{
+ if( (self->bytes_in_buffer == 0 && fill_buffer(self) < 1) )
+ return 1;
+ else
+ return 0;
}
1.2 +20 -15 httpd-apreq/c/apache_multipart_buffer.h
Index: apache_multipart_buffer.h
===================================================================
RCS file: /home/cvs/httpd-apreq/c/apache_multipart_buffer.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- apache_multipart_buffer.h 2000/03/30 06:04:41 1.1
+++ apache_multipart_buffer.h 2000/12/17 00:36:38 1.2
@@ -1,24 +1,29 @@
#include "apache_request.h"
+/*#define DEBUG 1*/
+#define FILLUNIT (1024 * 5)
+#define MPB_ERROR APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, self->r
+
typedef struct {
+ /* request info */
request_rec *r;
- pool *subp;
- long length;
- long total;
- long boundary_length;
+ long request_length;
+
+ /* read buffer */
+ char *buffer;
+ char *buf_begin;
+ int bufsize;
+ int bytes_in_buffer;
+
+ /* boundary info */
char *boundary;
+ char *boundary_next;
char *boundary_end;
- char *buffer;
- long buffer_len;
} multipart_buffer;
-#define multipart_buffer_eof(self) \
-(((self->buffer == NULL) || (*self->buffer == '\0')) && (self->length <= 0))
-
-char *multipart_buffer_read_body(multipart_buffer *self);
+multipart_buffer *
+ multipart_buffer_new(char *boundary, long length, request_rec *r);
table *multipart_buffer_headers(multipart_buffer *self);
-void multipart_buffer_fill(multipart_buffer *self, long bytes);
-char *multipart_buffer_read(multipart_buffer *self, long bytes, int *blen);
-multipart_buffer *multipart_buffer_new(char *boundary, long length,
request_rec *r);
-
-#define MPB_ERROR APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, self->r
+int multipart_buffer_read(multipart_buffer *self, char *buf, int bytes);
+char *multipart_buffer_read_body(multipart_buffer *self);
+int multipart_buffer_eof(multipart_buffer *self);
1.8 +10 -11 httpd-apreq/c/apache_request.c
Index: apache_request.c
===================================================================
RCS file: /home/cvs/httpd-apreq/c/apache_request.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- apache_request.c 2000/03/30 06:04:41 1.7
+++ apache_request.c 2000/12/17 00:36:38 1.8
@@ -269,8 +269,8 @@
req->parsed = 1;
if (r->args) {
- split_to_parms(req, r->args);
- }
+ split_to_parms(req, r->args);
+ }
if (r->method_number == M_POST) {
const char *ct = ap_table_get(r->headers_in, "Content-type");
@@ -297,7 +297,7 @@
int rc = OK;
if (r->method_number == M_POST) {
- const char *data, *type;
+ const char *data = NULL, *type;
type = ap_table_get(r->headers_in, "Content-Type");
@@ -374,7 +374,8 @@
while (!multipart_buffer_eof(mbuff)) {
table *header = multipart_buffer_headers(mbuff);
- const char *cd, *param=NULL, *filename=NULL, *buff;
+ const char *cd, *param=NULL, *filename=NULL;
+ char buff[FILLUNIT];
int blen;
if (!header) {
@@ -401,10 +402,11 @@
}
}
if (!filename) {
- char *value = multipart_buffer_read_body(mbuff);
- ap_table_add(req->parms, param, value);
+ char *value = multipart_buffer_read_body(mbuff);
+ ap_table_add(req->parms, param, value);
continue;
}
+ if (!param) continue; /* shouldn't happen, but just in case. */
ap_table_add(req->parms, param, filename);
if (upload) {
@@ -424,8 +426,9 @@
upload->filename = ap_pstrdup(req->r->pool, filename);
upload->name = ap_pstrdup(req->r->pool, param);
- while ((buff = multipart_buffer_read(mbuff, 0, &blen))) {
+ while ((blen = multipart_buffer_read(mbuff, buff, sizeof(buff)))) {
/* write the file */
+ /* XXX: do better error-checking on the fwrite? */
upload->size += fwrite(buff, 1, blen, upload->fp);
}
@@ -437,10 +440,6 @@
}
}
}
-
- if (r->args) {
- split_to_parms(req, r->args);
- }
return OK;
}