+ 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 == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
+ return eReadFail;
+
+
+ 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 == NULL) || (FB->Buf == NULL))
+ return 0;
+ if (FB->ReadWritePointer == NULL)
+ return StrLength(FB->Buf);
+
+ return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
+}
+
+inline static void FDIOBufferFlush(FDIOBuffer *FDB)
+{
+ memset(FDB, 0, sizeof(FDIOBuffer));
+ FDB->OtherFD = -1;
+ FDB->SplicePipe[0] = -1;
+ FDB->SplicePipe[1] = -1;
+}
+
+void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
+{
+ FDIOBufferFlush(FDB);
+
+ FDB->TotalSendSize = TotalSendSize;
+ if (TotalSendSize > 0)
+ FDB->ChunkSize = TotalSendSize;
+ else
+ {
+ TotalSendSize = SIZ * 10;
+ FDB->ChunkSize = TotalSendSize;
+ }
+ FDB->IOB = IO;
+
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ pipe(FDB->SplicePipe);
+ else
+#endif
+ FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
+
+ FDB->OtherFD = FD;
+}
+
+void FDIOBufferDelete(FDIOBuffer *FDB)
+{
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ {
+ if (FDB->SplicePipe[0] > 0)
+ close(FDB->SplicePipe[0]);
+ if (FDB->SplicePipe[1] > 0)
+ close(FDB->SplicePipe[1]);
+ }
+ else
+#endif
+ FreeStrBuf(&FDB->ChunkBuffer);
+
+ if (FDB->OtherFD > 0)
+ close(FDB->OtherFD);
+ FDIOBufferFlush(FDB);
+}
+
+int FileSendChunked(FDIOBuffer *FDB, const char **Err)
+{
+ ssize_t sent, pipesize;
+
+ if (FDB->TotalSendSize > 0)
+ {
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ {
+ if (FDB->PipeSize == 0)
+ {
+ pipesize = splice(FDB->OtherFD,
+ &FDB->TotalSentAlready,
+ FDB->SplicePipe[1],
+ NULL,
+ FDB->ChunkSendRemain,
+ SPLICE_F_MOVE);
+
+ if (pipesize == -1)
+ {
+ *Err = strerror(errno);
+ return pipesize;
+ }
+ FDB->PipeSize = pipesize;
+ }
+ sent = splice(FDB->SplicePipe[0],
+ NULL,
+ FDB->IOB->fd,
+ NULL,
+ FDB->PipeSize,
+ SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+ if (sent == -1)
+ {
+ *Err = strerror(errno);
+ return sent;
+ }
+ FDB->PipeSize -= sent;
+ FDB->ChunkSendRemain -= sent;
+ return sent;
+ }
+ else
+#endif
+ {
+ char *pRead;
+ long nRead = 0;
+
+ pRead = FDB->ChunkBuffer->buf;
+ while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
+ {
+ nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
+ if (nRead > 0) {
+ FDB->ChunkBuffer->BufUsed += nRead;
+ FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
+ }
+ else if (nRead == 0) {}
+ else return nRead;
+ }
+
+ nRead = write(FDB->IOB->fd,
+ FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
+ FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
+
+ if (nRead >= 0) {
+ FDB->TotalSentAlready += nRead;
+ FDB->ChunkSendRemain -= nRead;
+ return FDB->ChunkSendRemain;
+ }
+ else {
+ return nRead;
+ }
+ }
+ }
+ else
+ {
+#ifdef LINUX_SPLICE
+ if (EnableSplice)
+ {
+ if (FDB->PipeSize == 0)
+ {
+ pipesize = splice(FDB->OtherFD,
+ &FDB->TotalSentAlready,
+ FDB->SplicePipe[1],
+ NULL,
+ SIZ * 10,
+ SPLICE_F_MOVE);
+
+ if (pipesize == -1)
+ {
+ *Err = strerror(errno);
+ return pipesize;
+ }
+ FDB->PipeSize = pipesize;
+ if (pipesize == 0)
+ return -1;
+ }
+ sent = splice(FDB->SplicePipe[0],
+ NULL,
+ FDB->IOB->fd,
+ NULL,
+ FDB->PipeSize,
+ SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+ if (sent == -1)
+ {
+ *Err = strerror(errno);
+ return sent;
+ }
+ FDB->PipeSize -= sent;
+ FDB->ChunkSendRemain -= sent;
+ return sent;
+ }
+ else
+#endif
+ {
+ char *pRead;
+ long nRead = 0;
+
+ pRead = FDB->ChunkBuffer->buf;
+ while ((FDB->ChunkSendRemain == 0) &&
+ (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
+ (nRead >= 0))
+ {
+ FDB->TotalSentAlready = 0;
+ nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
+ if (nRead > 0) {
+ FDB->ChunkBuffer->BufUsed += nRead;
+ FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
+ FDB->ChunkSendRemain += nRead;
+ }
+ else if (nRead == 0)
+ {
+ return -1;
+ }
+ else
+ {
+ *Err = strerror(errno);
+ return nRead;
+ }
+ }
+
+ nRead = write(FDB->IOB->fd,
+ FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
+ FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
+
+ if (nRead >= 0) {
+ FDB->TotalSentAlready += nRead;
+ FDB->ChunkSendRemain -= nRead;
+ if (FDB->ChunkSendRemain == 0)
+ {
+ FDB->ChunkBuffer->BufUsed = 0;
+ FDB->TotalSentAlready = 0;
+ }
+ return FDB->ChunkSendRemain;
+ }
+ else {
+ return nRead;
+ }