/* ====================================================================
 * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. All advertising materials mentioning features or use of this
 *    software must display the following acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * 4. The names "Apache Server" and "Apache Group" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For written permission, please contact
 *    apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * 6. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by the Apache Group
 *    for use in the Apache HTTP server project (http://www.apache.org/)."
 *
 * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Group and was originally based
 * on public domain software written at the National Center for
 * Supercomputing Applications, University of Illinois, Urbana-Champaign.
 * For more information on the Apache Group and the Apache HTTP server
 * project, please see <http://www.apache.org/>.
 *
 */

#include "multipart_buffer.h"

#define FILLUNIT (1024 * 5)
#ifndef CRLF
#define CRLF "\015\012"
#endif
#define CRLF_CRLF "\015\012\015\012"

static char *my_join(pool *p, char *one, int lenone, char *two, int lentwo)
{
    char *res; 
    int len = lenone+lentwo;
    res = (char *)ap_palloc(p, len + 1); 
    memcpy(res, one, lenone);  
    memcpy(res+lenone, two, lentwo);
    return res;
}

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; 
	}
    }
    return NULL; 
} 

/*
 * 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)
{
    int len_read, length, bytes_to_read;
    int buffer_len = self->buffer_len;

    if (!self->length) {
	return;
    }
    length = (bytes - buffer_len) + self->boundary_length + 2;
    if (self->length < length) {
	length = self->length;
    }
    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;
	    return;
	}

	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;

	ap_reset_timeout(self->r);
    }
    ap_kill_timeout(self->r);
    ap_clear_pool(self->subp);
}

char *multipart_buffer_read(multipart_buffer *self, long bytes, int *blen)
{
    int start = -1;
    char *retval, *str;

    /* default number of bytes to read */
    if (!bytes) {
	bytes = FILLUNIT;       
    }

    /*
     * 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;
	}
    }

    /* 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;
    }

    /*
     * 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;
	}

        /* otherwise just remove the boundary. */
	self->buffer += (self->boundary_length + 2);
	self->buffer_len -= (self->boundary_length + 2);
	return NULL;
    }

    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;

    /* If we hit the boundary, remove the CRLF from the end. */
    if (start > 0) {
	*blen -= 2;
	retval[*blen] = '\0';
    }

    return retval;
}

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);

    if (bad) {
	return NULL;
    }

    header = ap_pstrndup(self->r->pool, self->buffer, end+2);
    /*XXX: need to merge continuation lines here?*/

    tab = ap_make_table(self->r->pool, 10);
    self->buffer += end+4;
    self->buffer_len -= end+4;

    {
	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);
	}
    }

    return tab;
}

char *multipart_buffer_read_body(multipart_buffer *self) 
{
    char *data, *retval=NULL;
    int blen = 0, old_len = 0;

    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;
    }

    return retval;
}

multipart_buffer *multipart_buffer_new(char *boundary, long length, request_rec *r)
{
    int blen;
    multipart_buffer *self = (multipart_buffer *)
	ap_pcalloc(r->pool, sizeof(multipart_buffer));

    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);

    /* Read the preamble and the topmost (boundary) line plus the CRLF. */
    (void)multipart_buffer_read(self, 0, &blen);

    if (multipart_buffer_eof(self)) {
	return NULL;
    }

    return self;
}
