/*
 * dynbuff.cpp
 *
 * (C) 1998-2002 Murat Deligonul
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
 */

#include "autoconf.h"

#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_SYS_FILIO_H
#   include <sys/filio.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#   include <sys/ioctl.h>
#endif
#include <sys/socket.h>
#include <string.h>
#include "dynbuff.h"
#include "debug.h"

/* create a new dynbuff
 *  Maxsize can be 0 or (< minsize) for unlimited length.
 */
dynbuff::dynbuff(unsigned int minsize, unsigned int maxsize)
{
    if (maxsize < minsize)
        maxsize = 0;
    if (minsize == 0)
        minsize = 1024;
    min = minsize;
    max = (maxsize == 0) ? 0xFFFFFFF : maxsize;
    /* add a 0 to the end */
    contents = new char[minsize + 1];
    alloced = minsize;
    contents[alloced] = 0;
    bytes_in = 0;
}

dynbuff::~dynbuff()
{
    delete[] contents;
}


/* add()
 *  return: # of bytes added */
int dynbuff::add(const char *data, unsigned int len)
{
    if (len == 0)
        len = strlen(data);
    if (len == 0)
        return 0;
    return -(bytes_in - append(data, len));
}

/* add()
 * read from a file into buffer
 * return:   > 0 - (num bytes added)
 *           = 0 - nothing to add (or no data waiting on socket)
 *           < 0 - error condition */
int dynbuff::add(int fd, unsigned int len)
{
    int ob = bytes_in;
    if (len == 0)
    {
        ioctl(fd, FIONREAD, &len);
        if (len == 0)
            return 0;
    }

    if ((signed) len + bytes_in > max)
        len = max - bytes_in;
    if (!len)
        return DYNBUFF_ERR_FULL;

    char *tmp = new char[len];
    if (!tmp)
        return DYNBUFF_ERR_MEM;

    switch ((signed) (len = read(fd, tmp, len)))
    {
    case -1:
        delete[] tmp;
        return (errno == EAGAIN) ? 0 : DYNBUFF_ERR_READ;
    case 0:
        delete[] tmp;
        return DYNBUFF_ERR_READ;
    }

    fd = append(tmp, len);
    delete[] tmp;
    if (fd < 0)
        return DYNBUFF_ERR_FULL;
    return (long) (fd - ob);
}


/* copy_line()
 *  Copies an arbitrary line from this buffer.
 *  Arguments:
 *     line        - 0 based index
 *     buffer      - where to store line
 *     maxsize     - how much to store at most
 *     leave       - leave the line in the internal buffer or remove it?
 *                    (optional, defaults to 0)
 *   must_have_crlf - line must end w/ cr, lf, or both.
 *
 *   Entire line will be removed if leave is 0, even if the length of the
 *      whole line is less than maxsize.
 *
 *  Return values: 0+   how long the string is in buffer. Can be 0,
 *                       if line was empty, as the ending \n or \r is not copied.
 *                -1:   line was not found
 *
 * FIXME: we check for 'out of bounds': this is wrong. What is going on?
 *        if we can peek out of bounds it's a segfault waiting to happen.
 */
int dynbuff::copy_line(unsigned line, char *buffer, unsigned int maxsize, bool leave,
                       bool need_crlf)
{
    const char *p, *cur = contents;
    unsigned curline = 0;

    /* Keep finding \n's until we find the line we need */
    while ((p = strchr(cur, '\n')) && (curline != line)
           && ((unsigned) (p - contents) < bytes_in)) {
        cur = p + 1;
        curline++;
    }
    if (unsigned(cur - contents) == bytes_in)
        return -1;      /* Oops, we've reached the end */

    if (curline == line && bytes_in)
    {
        const char *next = strchr(cur, '\n');
        if (!next && need_crlf)
            return -1;           /* incomplete line, ignore it */
        if (!next ||
            ((unsigned) (next - contents) > bytes_in))    /*  any more \n's ?  */
            next = contents + bytes_in;                 /* or is it out of bounds? */
        else
            next++;                                     /* include this \n so it will be removed */
        u_long len = next - cur;
        u_long real_len = len;

        if (len >= maxsize)
            len = maxsize - 1;
        memcpy(buffer, cur, len);
        buffer[len] = 0;
        /* get rid of any lf or cr at the end */
        if ((buffer[len - 1] == '\r') || (buffer[len - 1] == '\n'))
            buffer[--len] = 0;
        if (len && buffer[len - 1] == '\r')
            buffer[--len] = 0;
        /* remove this line if requested */
        if (!leave)
            remove(u_long(cur - contents), real_len);
        return len;
    }
    return -1;
}


/* append()
 *  adds data to the end of the buffer.
 *  resizing is done if needed.
 *  returns:
 *              >  0       -- new size of buffer
 *              <= 0       -- error conditions */
int dynbuff::append(const char *data, unsigned int len)
{
    if (bytes_in == max)
        return DYNBUFF_ERR_FULL;
    if (len == 0)
        return 0;

    unsigned int newsize = (len + bytes_in) > max ? max : len + bytes_in;
    unsigned int bytes2copy = newsize - bytes_in;

    if (newsize > alloced)
    {
        /* 
         * The new size will be too big for this buffer.
         *  we need to allocate bigger one.
         */
        char *newbuff;
        newbuff = new char[newsize + 1];
        memcpy(newbuff, contents, bytes_in);
        memcpy(newbuff + bytes_in, data, bytes2copy);

        /* clean up */
        delete[] contents;
        contents = newbuff;
        contents[newsize] = 0;  /* add a null char */
        alloced = newsize;
        bytes_in += bytes2copy;
    }
    else if (newsize <= alloced)
    {
        memcpy(contents + bytes_in, data, bytes2copy);
        bytes_in += bytes2copy;
    }
    contents[bytes_in] = 0;
    return bytes_in;
}

/* flush()
 *  Attempt to dump all contents to a file descriptor.
 *  return:  < 0     -- error condition
 *           > 0     -- # of bytes dumped down the pipe */
int dynbuff::flush(int fd, unsigned int bytes)
{
    int ob = bytes_in;
    if (!bytes || bytes > bytes_in)
        bytes = bytes_in;

    int wr = write(fd, contents, bytes);
    if (wr < 0)
        return DYNBUFF_ERR_WRITE;
    /* NEVER assume all of the contents were written */
    return ob - remove(0, wr);
}

/* remove()
 *  Starts at 'start' and removes 'howmuch' stuff 
 *  return: new size of buffer */
int dynbuff::remove(unsigned int start, unsigned int howmuch)
{
    u_long end = start + howmuch;
    u_long part2 = (end > bytes_in) ? end : bytes_in - end; 

    /* start at 'end'. move it to 'start' */
    memmove(contents + start, contents + end, part2);
    /* re-adjust */
    bytes_in = start + part2;
    contents[alloced] = 0;
    contents[bytes_in] = 0;
    return bytes_in;
}

/* optimize()
 *  Optimize memory usage, i.e:
 *  if allocated amount is > minimum and bytes_in is also 
 *  < min, then resize it. (But make size no smaller than min) */
int dynbuff::optimize(void)
{
    if (alloced > min && bytes_in <= min)
    {
        char *_new = new char[min + 1];
        if (!_new)
            return 0;
        memcpy(_new, contents, bytes_in);
        delete[] contents;
        alloced = min;
        contents = _new;
        contents[alloced] = 0;
        contents[bytes_in] = 0;
    }
    return alloced;
}


/* transfer()
 * add contents to target dynbuff
 * return # of bytes transfered */
int dynbuff::transfer(dynbuff * target)
{
    /* If target is empty, then
     * it's easy ... */
    if (!target->bytes_in)
    {
        delete[] target->contents;
        target->contents = contents;
        target->bytes_in = bytes_in;
        target->alloced = alloced;

        contents = new char[min + 1];
        alloced = min;
        contents[alloced] = 0;
        bytes_in = 0;
        return target->bytes_in;
    }
    else {
        int b = bytes_in;
        if (target->append(contents, bytes_in) <= 0)
            return 0;
        reset();
        return b;
    }
}

/*************************
 * SSL SUPPORT FUNCTIONS
 *************************/
#ifdef _USE_SSL
int dynbuff::add(SSL *ssl, unsigned int len)
{
    int ob = bytes_in;
    int i;
    char tmp[1024];
again:
    do {
    switch ((signed long) (len = SSL_read(ssl, tmp, 1024)))
    {
    case -1:
	switch (SSL_get_error(ssl,len)) {
	     case SSL_ERROR_WANT_READ:
	     case SSL_ERROR_WANT_WRITE:
	     case SSL_ERROR_WANT_X509_LOOKUP:
	    	    goto again;
	}
        return DYNBUFF_ERR_READ;
    }

    i = append(tmp, len);
    } while(SSL_pending(ssl));
    return long(i - ob);
}

int dynbuff::flush(SSL * ssl, unsigned int bytes)
{
    int ob = bytes_in;
    if (!bytes || bytes > bytes_in)
        bytes = bytes_in;

again:
    int i = SSL_write(ssl, contents, bytes);
    if (i==-1) {
	    int err=SSL_get_error(ssl,i);
		if ((err==SSL_ERROR_WANT_READ)||(err==SSL_ERROR_WANT_WRITE)||(err==SSL_ERROR_WANT_X509_LOOKUP))
		    goto again;
    }
    DEBUG("SSL_write() wrote: %d\n",i);
    return ob - remove(0, i);
}

#endif

 /************
 * db_parser
 ************/
db_parser::db_parser(dynbuff * target)
{
    buff = target;
    next_line_ptr = buff->contents;
}

/* get_next_line()
 *  Line by line dissasembly off the buffer
 *  null char is placed at crlf location and
 *  next_line_ptr is advanced
 *  args:
 *      crlf        -- require cr and/or lf at end
 *  return:
 *      direct pointer to line in buff */
char * db_parser::get_next_line(bool crlf)
{
    char * orig = next_line_ptr;
    if (!orig)
        return 0;

    next_line_ptr = strchr(next_line_ptr, '\n');
    if (!next_line_ptr)
    {
        if (crlf)
        {
            next_line_ptr = orig;
            return 0;
        }
        next_line_ptr = 0;
        return orig;
    }

    /* strip LF, (and CR if it's there) */
    *next_line_ptr = 0;
    if (*(next_line_ptr - 1) == '\r')
        *(next_line_ptr - 1) = 0;

    /* advance the pointer -- if we hit the end,
     * there'll be a null character to greet us on the next
     * call */
    ++next_line_ptr;
    return orig;
}



syntax highlighted by Code2HTML, v. 0.9.1