/* * 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 #include #include #ifdef HAVE_SYS_FILIO_H # include #endif #ifdef HAVE_SYS_IOCTL_H # include #endif #include #include #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; }