+ FreeStrBuf(&DecodedInvalidBuf);
+}
+
+/*******************************************************************************
+ * Manipulating UTF-8 Strings *
+ *******************************************************************************/
+
+/**
+ * @ingroup StrBuf
+ * @brief evaluate the length of an utf8 special character sequence
+ * @param Char the character to examine
+ * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
+ */
+static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
+{
+ int n = 0;
+ unsigned char test = (1<<7);
+
+ if ((*CharS & 0xC0) != 0xC0)
+ return 1;
+
+ while ((n < 8) &&
+ ((test & ((unsigned char)*CharS)) != 0))
+ {
+ test = test >> 1;
+ n ++;
+ }
+ if ((n > 6) || ((CharE - CharS) < n))
+ n = 0;
+ return n;
+}
+
+/**
+ * @ingroup StrBuf
+ * @brief detect whether this char starts an utf-8 encoded char
+ * @param Char character to inspect
+ * @returns yes or no
+ */
+static inline int Ctdl_IsUtf8SequenceStart(const char Char)
+{
+/** 11??.???? indicates an UTF8 Sequence. */
+ return ((Char & 0xC0) == 0xC0);
+}
+
+/**
+ * @ingroup StrBuf
+ * @brief measure the number of glyphs in an UTF8 string...
+ * @param Buf string to measure
+ * @returns the number of glyphs in Buf
+ */
+long StrBuf_Utf8StrLen(StrBuf *Buf)
+{
+ int n = 0;
+ int m = 0;
+ char *aptr, *eptr;
+
+ if ((Buf == NULL) || (Buf->BufUsed == 0))
+ return 0;
+ aptr = Buf->buf;
+ eptr = Buf->buf + Buf->BufUsed;
+ while ((aptr < eptr) && (*aptr != '\0')) {
+ if (Ctdl_IsUtf8SequenceStart(*aptr)){
+ m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
+ while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
+ n ++;
+ }
+ else {
+ n++;
+ aptr++;
+ }
+ }
+ return n;
+}
+
+/**
+ * @ingroup StrBuf
+ * @brief cuts a string after maxlen glyphs
+ * @param Buf string to cut to maxlen glyphs
+ * @param maxlen how long may the string become?
+ * @returns current length of the string
+ */
+long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
+{
+ char *aptr, *eptr;
+ int n = 0, m = 0;
+
+ aptr = Buf->buf;
+ eptr = Buf->buf + Buf->BufUsed;
+ while ((aptr < eptr) && (*aptr != '\0')) {
+ if (Ctdl_IsUtf8SequenceStart(*aptr)){
+ m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
+ while ((*aptr++ != '\0') && (m-- > 0));
+ n ++;
+ }
+ else {
+ n++;
+ aptr++;
+ }
+ if (n > maxlen) {
+ *aptr = '\0';
+ Buf->BufUsed = aptr - Buf->buf;
+ return Buf->BufUsed;
+ }
+ }
+ return Buf->BufUsed;
+
+}
+
+
+
+
+
+/*******************************************************************************
+ * wrapping ZLib *
+ *******************************************************************************/
+
+#ifdef HAVE_ZLIB
+#define DEF_MEM_LEVEL 8 /*< memlevel??? */
+#define OS_CODE 0x03 /*< unix */
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief uses the same calling syntax as compress2(), but it
+ * creates a stream compatible with HTTP "Content-encoding: gzip"
+ * @param dest compressed buffer
+ * @param destLen length of the compresed data
+ * @param source source to encode
+ * @param sourceLen length of source to encode
+ * @param level compression level
+ */
+int ZEXPORT compress_gzip(Bytef * dest,
+ size_t * destLen,
+ const Bytef * source,
+ uLong sourceLen,
+ int level)
+{
+ const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
+
+ /* write gzip header */
+ snprintf((char *) dest, *destLen,
+ "%c%c%c%c%c%c%c%c%c%c",
+ gz_magic[0], gz_magic[1], Z_DEFLATED,
+ 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
+ OS_CODE);
+
+ /* normal deflate */
+ z_stream stream;
+ int err;
+ stream.next_in = (Bytef *) source;
+ stream.avail_in = (uInt) sourceLen;
+ stream.next_out = dest + 10L; // after header
+ stream.avail_out = (uInt) * destLen;
+ if ((uLong) stream.avail_out != *destLen)
+ return Z_BUF_ERROR;
+
+ stream.zalloc = (alloc_func) 0;
+ stream.zfree = (free_func) 0;
+ stream.opaque = (voidpf) 0;
+
+ err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
+ DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (err != Z_OK)
+ return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *destLen = stream.total_out + 10L;
+
+ /* write CRC and Length */
+ uLong crc = crc32(0L, source, sourceLen);
+ int n;
+ for (n = 0; n < 4; ++n, ++*destLen) {
+ dest[*destLen] = (int) (crc & 0xff);
+ crc >>= 8;
+ }
+ uLong len = stream.total_in;
+ for (n = 0; n < 4; ++n, ++*destLen) {
+ dest[*destLen] = (int) (len & 0xff);
+ len >>= 8;
+ }
+ err = deflateEnd(&stream);
+ return err;
+}
+#endif
+
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief compress the buffer with gzip
+ * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
+ * @param Buf buffer whose content is to be gzipped
+ */
+int CompressBuffer(StrBuf *Buf)
+{
+#ifdef HAVE_ZLIB
+ char *compressed_data = NULL;
+ size_t compressed_len, bufsize;
+ int i = 0;
+
+ bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
+ compressed_data = malloc(compressed_len);
+
+ if (compressed_data == NULL)
+ return -1;
+ /* Flush some space after the used payload so valgrind shuts up... */
+ while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
+ Buf->buf[Buf->BufUsed + i++] = '\0';
+ if (compress_gzip((Bytef *) compressed_data,
+ &compressed_len,
+ (Bytef *) Buf->buf,
+ (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
+ if (!Buf->ConstBuf)
+ free(Buf->buf);
+ Buf->buf = compressed_data;
+ Buf->BufUsed = compressed_len;
+ Buf->BufSize = bufsize;
+ /* Flush some space after the used payload so valgrind shuts up... */
+ i = 0;
+ while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
+ Buf->buf[Buf->BufUsed + i++] = '\0';
+ return 1;
+ } else {
+ free(compressed_data);
+ }
+#endif /* HAVE_ZLIB */
+ return 0;
+}
+
+/*******************************************************************************
+ * File I/O; Callbacks to libevent *
+ *******************************************************************************/
+
+long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
+{
+ long bufremain = 0;
+ int n;
+
+ if ((FB == NULL) || (FB->Buf == NULL))
+ return -1;
+
+ /*
+ * check whether the read pointer is somewhere in a range
+ * where a cut left is inexpensive
+ */
+
+ if (FB->ReadWritePointer != NULL)
+ {
+ long already_read;
+
+ already_read = FB->ReadWritePointer - FB->Buf->buf;
+ bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
+
+ if (already_read != 0) {
+ long unread;
+
+ unread = FB->Buf->BufUsed - already_read;
+
+ /* else nothing to compact... */
+ if (unread == 0) {
+ FB->ReadWritePointer = FB->Buf->buf;
+ bufremain = FB->Buf->BufSize;
+ }
+ else if ((unread < 64) ||
+ (bufremain < already_read))
+ {
+ /*
+ * if its just a tiny bit remaining, or we run out of space...
+ * lets tidy up.
+ */
+ FB->Buf->BufUsed = unread;
+ if (unread < already_read)
+ memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
+ else
+ memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
+ FB->ReadWritePointer = FB->Buf->buf;
+ bufremain = FB->Buf->BufSize - unread - 1;
+ }
+ else if (bufremain < (FB->Buf->BufSize / 10))
+ {
+ /* get a bigger buffer */
+
+ IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
+
+ FB->ReadWritePointer = FB->Buf->buf + unread;
+
+ bufremain = FB->Buf->BufSize - unread - 1;
+/*TODO: special increase function that won't copy the already read! */
+ }
+ }
+ else if (bufremain < 10) {
+ IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
+
+ FB->ReadWritePointer = FB->Buf->buf;
+
+ bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
+ }
+
+ }
+ else {
+ FB->ReadWritePointer = FB->Buf->buf;
+ bufremain = FB->Buf->BufSize - 1;
+ }
+
+ n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
+
+ if (n > 0) {
+ FB->Buf->BufUsed += n;
+ FB->Buf->buf[FB->Buf->BufUsed] = '\0';
+ }
+ return n;
+}
+
+int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
+{
+ long WriteRemain;
+ int n;
+
+ if ((FB == NULL) || (FB->Buf == NULL))
+ return -1;
+
+ if (FB->ReadWritePointer != NULL)
+ {
+ WriteRemain = FB->Buf->BufUsed -
+ (FB->ReadWritePointer -
+ FB->Buf->buf);
+ }
+ else {
+ FB->ReadWritePointer = FB->Buf->buf;
+ WriteRemain = FB->Buf->BufUsed;
+ }
+
+ n = write(fd, FB->ReadWritePointer, WriteRemain);
+ if (n > 0) {
+ FB->ReadWritePointer += n;
+
+ if (FB->ReadWritePointer ==
+ FB->Buf->buf + FB->Buf->BufUsed)
+ {
+ FlushStrBuf(FB->Buf);
+ FB->ReadWritePointer = NULL;
+ return 0;
+ }
+ // check whether we've got something to write
+ // get the maximum chunk plus the pointer we can send
+ // write whats there
+ // if not all was sent, remember the send pointer for the next time
+ return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
+ }
+ return n;
+}
+
+/**
+ * @ingroup StrBuf_IO
+ * @brief extract a "next line" from Buf; Ptr to persist across several iterations
+ * @param LineBuf your line will be copied here.
+ * @param FB BLOB with lines of text...
+ * @param Ptr moved arround to keep the next-line across several iterations
+ * has to be &NULL on start; will be &NotNULL on end of buffer
+ * @returns size of copied buffer
+ */
+eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
+{
+ const char *aptr, *ptr, *eptr;
+ char *optr, *xptr;
+
+ if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
+ FB->ReadWritePointer = StrBufNOTNULL;
+ return eReadFail;
+ }
+
+ FlushStrBuf(LineBuf);
+ if (FB->ReadWritePointer == NULL)
+ ptr = aptr = FB->Buf->buf;
+ else
+ ptr = aptr = FB->ReadWritePointer;
+
+ optr = LineBuf->buf;
+ eptr = FB->Buf->buf + FB->Buf->BufUsed;
+ xptr = LineBuf->buf + LineBuf->BufSize - 1;
+
+ while ((ptr <= eptr) &&
+ (*ptr != '\n') &&
+ (*ptr != '\r') )
+ {
+ *optr = *ptr;
+ optr++; ptr++;
+ if (optr == xptr) {
+ LineBuf->BufUsed = optr - LineBuf->buf;
+ IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
+ optr = LineBuf->buf + LineBuf->BufUsed;
+ xptr = LineBuf->buf + LineBuf->BufSize - 1;
+ }
+ }
+
+ if (ptr >= eptr) {
+ if (optr > LineBuf->buf)
+ optr --;
+ if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
+ LineBuf->BufUsed = optr - LineBuf->buf;
+ *optr = '\0';
+ if ((FB->ReadWritePointer != NULL) &&
+ (FB->ReadWritePointer != FB->Buf->buf))
+ {
+ /* Ok, the client application read all the data
+ it was interested in so far. Since there is more to read,
+ we now shrink the buffer, and move the rest over.
+ */
+ StrBufCutLeft(FB->Buf,
+ FB->ReadWritePointer - FB->Buf->buf);
+ FB->ReadWritePointer = FB->Buf->buf;
+ }
+ return eMustReadMore;
+ }
+ }
+ LineBuf->BufUsed = optr - LineBuf->buf;
+ *optr = '\0';
+ if ((ptr <= eptr) && (*ptr == '\r'))
+ ptr ++;
+ if ((ptr <= eptr) && (*ptr == '\n'))
+ ptr ++;
+
+ if (ptr < eptr) {
+ FB->ReadWritePointer = ptr;
+ }
+ else {
+ FlushStrBuf(FB->Buf);
+ FB->ReadWritePointer = NULL;
+ }
+
+ return eReadSuccess;
+}
+
+/**
+ * @ingroup StrBuf_CHUNKED_IO
+ * @brief check whether the chunk-buffer has more data waiting or not.
+ * @param FB Chunk-Buffer to inspect
+ */
+eReadState StrBufCheckBuffer(IOBuffer *FB)
+{
+ if (FB == NULL)
+ return eReadFail;
+ if (FB->Buf->BufUsed == 0)
+ return eReadSuccess;
+ if (FB->ReadWritePointer == NULL)
+ return eBufferNotEmpty;
+ if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
+ return eBufferNotEmpty;
+ return eReadSuccess;
+}
+
+long IOBufferStrLength(IOBuffer *FB)
+{
+ if (FB->ReadWritePointer == NULL)
+ return StrLength(FB->Buf);
+
+ return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
+}
+
+
+
+void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
+{
+ memset(FDB, 0, sizeof(FDIOBuffer));
+ FDB->ChunkSize =
+ FDB->TotalSendSize = TotalSendSize;
+ FDB->IOB = IO;
+#ifndef LINUX_SENDFILE
+ FDB->ChunkBuffer = NewStrBuf();
+#endif
+ FDB->OtherFD = FD;
+}
+
+int FileSendChunked(FDIOBuffer *FDB, const char **Err)
+{
+
+#ifdef LINUX_SENDFILE
+ ssize_t sent;
+ sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
+ if (sent == -1)
+ {
+ *Err = strerror(errno);
+ return sent;
+ }
+ FDB->ChunkSendRemain -= sent;
+ return FDB->ChunkSendRemain;
+#else
+#endif
+ return 0;
+}
+
+int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
+{
+
+#ifdef LINUX_SENDFILE
+ ssize_t sent;
+ sent = sendfile(FDB->OtherFD, FDB->IOB->fd, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
+ if (sent == -1)
+ {
+ *Err = strerror(errno);
+ return sent;
+ }
+ FDB->ChunkSendRemain -= sent;
+ return FDB->ChunkSendRemain;
+#else
+#endif
+ return 0;
+}
+
+eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
+{
+ int IsNonBlock;
+ int fdflags;
+ long rlen;
+ long should_write;
+ int nSuccessLess = 0;
+ struct timeval tv;
+ fd_set rfds;
+
+ fdflags = fcntl(FDB->OtherFD, F_GETFL);
+ IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
+
+ while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
+ (FDB->ChunkSendRemain > 0))
+ {
+ if (IsNonBlock){
+ tv.tv_sec = 1; /* selectresolution; */
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(FDB->OtherFD, &rfds);
+ if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
+ *Error = strerror(errno);
+ return eReadFail;
+ }
+ }
+ if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
+ nSuccessLess ++;
+ continue;
+ }
+
+ should_write = FDB->IOB->Buf->BufUsed -
+ (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
+ if (should_write > FDB->ChunkSendRemain)
+ should_write = FDB->ChunkSendRemain;
+
+ rlen = write(FDB->OtherFD,
+ FDB->IOB->ReadWritePointer,
+ should_write);
+ if (rlen < 1) {
+ *Error = strerror(errno);
+
+ return eReadFail;
+ }
+ FDB->TotalSentAlready += rlen;
+ FDB->IOB->ReadWritePointer += rlen;
+ FDB->ChunkSendRemain -= rlen;
+ }
+ if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
+ {
+ FlushStrBuf(FDB->IOB->Buf);
+ FDB->IOB->ReadWritePointer = NULL;
+ }
+
+ if (FDB->ChunkSendRemain == 0)
+ return eReadSuccess;
+ else
+ return eMustReadMore;
+}
+
+/*******************************************************************************
+ * File I/O; Prefer buffered read since its faster! *
+ *******************************************************************************/
+
+/**
+ * @ingroup StrBuf_IO
+ * @brief Read a line from socket
+ * flushes and closes the FD on error
+ * @param buf the buffer to get the input to
+ * @param fd pointer to the filedescriptor to read
+ * @param append Append to an existing string or replace?
+ * @param Error strerror() on error
+ * @returns numbers of chars read
+ */
+int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
+{
+ int len, rlen, slen;
+
+ if (!append)
+ FlushStrBuf(buf);
+
+ slen = len = buf->BufUsed;
+ while (1) {
+ rlen = read(*fd, &buf->buf[len], 1);
+ if (rlen < 1) {
+ *Error = strerror(errno);
+
+ close(*fd);
+ *fd = -1;
+
+ return -1;
+ }
+ if (buf->buf[len] == '\n')
+ break;
+ if (buf->buf[len] != '\r')
+ len ++;
+ if (len + 2 >= buf->BufSize) {
+ buf->BufUsed = len;
+ buf->buf[len+1] = '\0';
+ IncreaseBuf(buf, 1, -1);
+ }
+ }
+ buf->BufUsed = len;
+ buf->buf[len] = '\0';
+ return len - slen;
+}
+
+/**
+ * @ingroup StrBuf_BufferedIO
+ * @brief Read a line from socket
+ * flushes and closes the FD on error
+ * @param Line the line to read from the fd / I/O Buffer
+ * @param buf the buffer to get the input to
+ * @param fd pointer to the filedescriptor to read
+ * @param timeout number of successless selects until we bail out
+ * @param selectresolution how long to wait on each select
+ * @param Error strerror() on error
+ * @returns numbers of chars read
+ */
+int StrBufTCP_read_buffered_line(StrBuf *Line,
+ StrBuf *buf,
+ int *fd,
+ int timeout,
+ int selectresolution,
+ const char **Error)
+{
+ int len, rlen;
+ int nSuccessLess = 0;
+ fd_set rfds;
+ char *pch = NULL;
+ int fdflags;
+ int IsNonBlock;
+ struct timeval tv;
+
+ if (buf->BufUsed > 0) {
+ pch = strchr(buf->buf, '\n');
+ if (pch != NULL) {
+ rlen = 0;
+ len = pch - buf->buf;
+ if (len > 0 && (*(pch - 1) == '\r') )
+ rlen ++;
+ StrBufSub(Line, buf, 0, len - rlen);
+ StrBufCutLeft(buf, len + 1);
+ return len - rlen;
+ }
+ }
+
+ if (buf->BufSize - buf->BufUsed < 10)
+ IncreaseBuf(buf, 1, -1);
+
+ fdflags = fcntl(*fd, F_GETFL);
+ IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
+
+ while ((nSuccessLess < timeout) && (pch == NULL)) {
+ if (IsNonBlock){
+ tv.tv_sec = selectresolution;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(*fd, &rfds);
+ if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
+ *Error = strerror(errno);
+ close (*fd);
+ *fd = -1;
+ return -1;
+ }
+ }
+ if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
+ nSuccessLess ++;
+ continue;
+ }
+ rlen = read(*fd,
+ &buf->buf[buf->BufUsed],
+ buf->BufSize - buf->BufUsed - 1);
+ if (rlen < 1) {
+ *Error = strerror(errno);
+ close(*fd);
+ *fd = -1;
+ return -1;
+ }
+ else if (rlen > 0) {
+ nSuccessLess = 0;
+ buf->BufUsed += rlen;
+ buf->buf[buf->BufUsed] = '\0';
+ if (buf->BufUsed + 10 > buf->BufSize) {
+ IncreaseBuf(buf, 1, -1);
+ }
+ pch = strchr(buf->buf, '\n');
+ continue;
+ }
+
+ }
+ if (pch != NULL) {
+ rlen = 0;
+ len = pch - buf->buf;
+ if (len > 0 && (*(pch - 1) == '\r') )
+ rlen ++;
+ StrBufSub(Line, buf, 0, len - rlen);
+ StrBufCutLeft(buf, len + 1);
+ return len - rlen;
+ }
+ return -1;
+
+}
+
+static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
+static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
+static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
+/**
+ * @ingroup StrBuf_BufferedIO
+ * @brief Read a line from socket
+ * flushes and closes the FD on error
+ * @param Line where to append our Line read from the fd / I/O Buffer;
+ * @param IOBuf the buffer to get the input to; lifetime pair to FD
+ * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
+ * @param fd pointer to the filedescriptor to read
+ * @param timeout number of successless selects until we bail out
+ * @param selectresolution how long to wait on each select
+ * @param Error strerror() on error
+ * @returns numbers of chars read or -1 in case of error. "\n" will become 0
+ */
+int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
+ StrBuf *IOBuf,
+ const char **Pos,
+ int *fd,
+ int timeout,
+ int selectresolution,
+ const char **Error)
+{
+ const char *pche = NULL;
+ const char *pos = NULL;
+ const char *pLF;
+ int len, rlen, retlen;
+ int nSuccessLess = 0;
+ fd_set rfds;
+ const char *pch = NULL;
+ int fdflags;
+ int IsNonBlock;
+ struct timeval tv;
+
+ retlen = 0;
+ if ((Line == NULL) ||
+ (Pos == NULL) ||
+ (IOBuf == NULL) ||
+ (*fd == -1))
+ {
+ if (Pos != NULL)
+ *Pos = NULL;
+ *Error = ErrRBLF_PreConditionFailed;
+ return -1;
+ }
+
+ pos = *Pos;
+ if ((IOBuf->BufUsed > 0) &&
+ (pos != NULL) &&
+ (pos < IOBuf->buf + IOBuf->BufUsed))
+ {
+ char *pcht;
+
+ pche = IOBuf->buf + IOBuf->BufUsed;
+ pch = pos;
+ pcht = Line->buf;
+
+ while ((pch < pche) && (*pch != '\n'))
+ {
+ if (Line->BufUsed + 10 > Line->BufSize)
+ {
+ long apos;
+ apos = pcht - Line->buf;
+ *pcht = '\0';
+ IncreaseBuf(Line, 1, -1);
+ pcht = Line->buf + apos;
+ }
+ *pcht++ = *pch++;
+ Line->BufUsed++;
+ retlen++;
+ }
+
+ len = pch - pos;
+ if (len > 0 && (*(pch - 1) == '\r') )
+ {
+ retlen--;
+ len --;
+ pcht --;
+ Line->BufUsed --;
+ }
+ *pcht = '\0';
+
+ if ((pch >= pche) || (*pch == '\0'))
+ {
+ FlushStrBuf(IOBuf);
+ *Pos = NULL;
+ pch = NULL;
+ pos = 0;
+ }
+
+ if ((pch != NULL) &&
+ (pch <= pche))
+ {
+ if (pch + 1 >= pche) {
+ *Pos = NULL;
+ FlushStrBuf(IOBuf);
+ }
+ else
+ *Pos = pch + 1;
+
+ return retlen;
+ }
+ else
+ FlushStrBuf(IOBuf);
+ }
+
+ /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
+
+ if (IOBuf->BufSize - IOBuf->BufUsed < 10)
+ IncreaseBuf(IOBuf, 1, -1);
+
+ fdflags = fcntl(*fd, F_GETFL);
+ IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
+
+ pLF = NULL;
+ while ((nSuccessLess < timeout) &&
+ (pLF == NULL) &&
+ (*fd != -1)) {
+ if (IsNonBlock)
+ {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(*fd, &rfds);
+ if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
+ *Error = strerror(errno);
+ close (*fd);
+ *fd = -1;
+ if (*Error == NULL)
+ *Error = ErrRBLF_SelectFailed;
+ return -1;
+ }
+ if (! FD_ISSET(*fd, &rfds) != 0) {
+ nSuccessLess ++;
+ continue;
+ }
+ }
+ rlen = read(*fd,
+ &IOBuf->buf[IOBuf->BufUsed],
+ IOBuf->BufSize - IOBuf->BufUsed - 1);
+ if (rlen < 1) {
+ *Error = strerror(errno);
+ close(*fd);
+ *fd = -1;
+ return -1;
+ }
+ else if (rlen > 0) {
+ nSuccessLess = 0;
+ pLF = IOBuf->buf + IOBuf->BufUsed;
+ IOBuf->BufUsed += rlen;
+ IOBuf->buf[IOBuf->BufUsed] = '\0';
+
+ pche = IOBuf->buf + IOBuf->BufUsed;
+
+ while ((pLF < pche) && (*pLF != '\n'))
+ pLF ++;
+ if ((pLF >= pche) || (*pLF == '\0'))
+ pLF = NULL;
+
+ if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
+ {
+ long apos = 0;
+
+ if (pLF != NULL) apos = pLF - IOBuf->buf;
+ IncreaseBuf(IOBuf, 1, -1);
+ if (pLF != NULL) pLF = IOBuf->buf + apos;
+ }
+
+ continue;
+ }
+ }
+ *Pos = NULL;
+ if (pLF != NULL) {
+ pos = IOBuf->buf;
+ len = pLF - pos;
+ if (len > 0 && (*(pLF - 1) == '\r') )
+ len --;
+ StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
+ if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
+ {
+ FlushStrBuf(IOBuf);
+ }
+ else
+ *Pos = pLF + 1;
+ return retlen + len;
+ }
+ *Error = ErrRBLF_NotEnoughSentFromServer;
+ return -1;
+
+}
+
+static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
+/**
+ * @ingroup StrBuf_IO
+ * @brief Input binary data from socket
+ * flushes and closes the FD on error
+ * @param Buf the buffer to get the input to
+ * @param fd pointer to the filedescriptor to read
+ * @param append Append to an existing string or replace?
+ * @param nBytes the maximal number of bytes to read
+ * @param Error strerror() on error
+ * @returns numbers of chars read
+ */
+int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
+{
+ int fdflags;
+ int rlen;
+ int nSuccessLess;
+ int nRead = 0;
+ char *ptr;
+ int IsNonBlock;
+ struct timeval tv;
+ fd_set rfds;
+
+ if ((Buf == NULL) || (*fd == -1))
+ {
+ *Error = ErrRBLF_BLOBPreConditionFailed;
+ return -1;
+ }
+ if (!append)
+ FlushStrBuf(Buf);
+ if (Buf->BufUsed + nBytes >= Buf->BufSize)
+ IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
+
+ ptr = Buf->buf + Buf->BufUsed;
+
+ fdflags = fcntl(*fd, F_GETFL);
+ IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
+ nSuccessLess = 0;
+ while ((nRead < nBytes) &&
+ (*fd != -1))
+ {
+ if (IsNonBlock)
+ {
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(*fd, &rfds);
+ if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
+ *Error = strerror(errno);
+ close (*fd);
+ *fd = -1;
+ if (*Error == NULL)
+ *Error = ErrRBLF_SelectFailed;
+ return -1;
+ }
+ if (! FD_ISSET(*fd, &rfds) != 0) {
+ nSuccessLess ++;
+ continue;
+ }
+ }
+
+ if ((rlen = read(*fd,
+ ptr,
+ nBytes - nRead)) == -1) {
+ close(*fd);
+ *fd = -1;
+ *Error = strerror(errno);
+ return rlen;
+ }
+ nRead += rlen;
+ ptr += rlen;
+ Buf->BufUsed += rlen;
+ }
+ Buf->buf[Buf->BufUsed] = '\0';
+ return nRead;