Handle situation in where we have multiple buffers to send at the end.
[citadel.git] / libcitadel / lib / stringbuf.c
index 0d88f7ca14f9c2e9925b744300194791c3334f7f..7b2c663656e08fd874d788250c80a5333acd2877 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1987-2011 by the citadel.org team
+ * Copyright (c) 1987-2013 by the citadel.org team
  *
  * This program is open source software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,6 +32,9 @@
 
 #include "libcitadel.h"
 
+#include "b64/cencode.h"
+#include "b64/cdecode.h"
+
 #ifdef HAVE_ICONV
 #include <iconv.h>
 #endif
@@ -40,8 +43,8 @@
 #include <execinfo.h>
 #endif
 
-#ifdef LINUX_SENDFILE
-#include <sys/sendfile.h>
+#ifdef UNDEF_MEMCPY
+#undef memcpy
 #endif
 
 #ifdef HAVE_ZLIB
@@ -50,6 +53,13 @@ int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
                           const Bytef * source, uLong sourceLen, int level);
 #endif
 int BaseStrBufSize = 64;
+int EnableSplice = 0;
+int ZLibCompressionRatio = -1; /* defaults to 6 */
+#ifdef HAVE_ZLIB
+#define DEF_MEM_LEVEL 8 /*< memlevel??? */
+#define OS_CODE 0x03   /*< unix */
+const int gz_magic[2] = { 0x1f, 0x8b };        /* gzip magic header */
+#endif
 
 const char *StrBufNOTNULL = ((char*) NULL) - 1;
 
@@ -361,7 +371,12 @@ long StrBufShrinkToFit(StrBuf *Buf, int Force)
        if (Force || 
            (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
        {
-               char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
+               char *TmpBuf;
+
+               TmpBuf = (char*) malloc(Buf->BufUsed + 1);
+               if (TmpBuf == NULL)
+                       return -1;
+
                memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
                Buf->BufSize = Buf->BufUsed + 1;
                free(Buf->buf);
@@ -380,7 +395,15 @@ StrBuf* NewStrBuf(void)
        StrBuf *NewBuf;
 
        NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
+       if (NewBuf == NULL)
+               return NULL;
+
        NewBuf->buf = (char*) malloc(BaseStrBufSize);
+       if (NewBuf->buf == NULL)
+       {
+               free(NewBuf);
+               return NULL;
+       }
        NewBuf->buf[0] = '\0';
        NewBuf->BufSize = BaseStrBufSize;
        NewBuf->BufUsed = 0;
@@ -405,7 +428,16 @@ StrBuf* NewStrBufDup(const StrBuf *CopyMe)
                return NewStrBuf();
 
        NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
+       if (NewBuf == NULL)
+               return NULL;
+
        NewBuf->buf = (char*) malloc(CopyMe->BufSize);
+       if (NewBuf->buf == NULL)
+       {
+               free(NewBuf);
+               return NULL;
+       }
+
        memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
        NewBuf->BufUsed = CopyMe->BufUsed;
        NewBuf->BufSize = CopyMe->BufSize;
@@ -501,6 +533,9 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
        size_t CopySize;
 
        NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
+       if (NewBuf == NULL)
+               return NULL;
+
        if (nChars < 0)
                CopySize = strlen((ptr != NULL)?ptr:"");
        else
@@ -594,6 +629,8 @@ StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
        StrBuf *NewBuf;
 
        NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
+       if (NewBuf == NULL)
+               return NULL;
        NewBuf->buf = (char*) StringConstant;
        NewBuf->BufSize = SizeOfStrConstant;
        NewBuf->BufUsed = SizeOfStrConstant;
@@ -1022,16 +1059,18 @@ int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t n
        }
        if (Offset + nChars < Source->BufUsed)
        {
-               if (nChars >= dest->BufSize)
-                       IncreaseBuf(dest, 0, nChars + 1);
+               if ((nChars >= dest->BufSize) && 
+                   (IncreaseBuf(dest, 0, nChars + 1) == -1))
+                       return 0;
                memcpy(dest->buf, Source->buf + Offset, nChars);
                dest->BufUsed = nChars;
                dest->buf[dest->BufUsed] = '\0';
                return nChars;
        }
        NCharsRemain = Source->BufUsed - Offset;
-       if (NCharsRemain  >= dest->BufSize)
-               IncreaseBuf(dest, 0, NCharsRemain + 1);
+       if ((NCharsRemain  >= dest->BufSize) && 
+           (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
+               return 0;
        memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
        dest->BufUsed = NCharsRemain;
        dest->buf[dest->BufUsed] = '\0';
@@ -1143,29 +1182,22 @@ void StrBufSpaceToBlank(StrBuf *Buf)
 
 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
 {
-       const char *pBuff;
        const char *pLeft;
        const char *pRight;
 
-       if ((Buf == NULL) || (Buf->buf == NULL))
+       if ((Buf == NULL) || (Buf->buf == NULL)) {
                return;
-       pLeft = pBuff = Buf->buf;
-       while (pBuff != NULL) {
-               pLeft = pBuff;
-               pBuff = strchr(pBuff, leftboundary);
-               if (pBuff != NULL)
-                       pBuff++;
        }
-               
-       if (pLeft != NULL)
-               pBuff = pLeft;
-       else
-               pBuff = Buf->buf;
-       pRight = strchr(pBuff, rightboundary);
-       if (pRight != NULL)
+
+       pRight = strchr(Buf->buf, rightboundary);
+       if (pRight != NULL) {
                StrBufCutAt(Buf, 0, pRight);
-       if (pLeft != NULL)
-               StrBufCutLeft(Buf, pLeft - Buf->buf);
+       }
+
+       pLeft = strrchr(ChrPtr(Buf), leftboundary);
+       if (pLeft != NULL) {
+               StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
+       }
 }
 
 
@@ -1345,6 +1377,23 @@ int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
        return ReducedBy;
 }
 
+int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
+{
+       const StrBuf Temp = {
+               (char*)Source,
+               SourceLen,
+               SourceLen,
+               1
+#ifdef SIZE_DEBUG
+               ,
+               0,
+               "",
+               ""
+#endif
+       };
+
+       return StrBufExtract_token(dest, &Temp, parmnum, separator);
+}
 
 /**
  * @ingroup StrBuf_Tokenizer
@@ -1885,6 +1934,129 @@ void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
        *pt = '\0';
 }
 
+/** 
+ * @ingroup StrBuf_DeEnCoder
+ * @brief append a string with characters having a special meaning in xml encoded to the buffer
+ * @param OutBuf the output buffer
+ * @param In Buffer to encode
+ * @param PlainIn way in from plain old c strings
+ * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
+ * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
+ */
+void StrBufXMLEscAppend(StrBuf *OutBuf,
+                       const StrBuf *In,
+                       const char *PlainIn,
+                       long PlainInLen,
+                       int OverrideLowChars)
+{
+       const char *pch, *pche;
+       char *pt, *pte;
+       int IsUtf8Sequence;
+       int len;
+
+       if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
+               return;
+       if (PlainIn != NULL) {
+               if (PlainInLen < 0)
+                       len = strlen((const char*)PlainIn);
+               else
+                       len = PlainInLen;
+               pch = PlainIn;
+               pche = pch + len;
+       }
+       else {
+               pch = (const char*)In->buf;
+               pche = pch + In->BufUsed;
+               len = In->BufUsed;
+       }
+
+       if (len == 0)
+               return;
+
+       pt = OutBuf->buf + OutBuf->BufUsed;
+       /**< we max append 6 chars at once plus the \0 */
+       pte = OutBuf->buf + OutBuf->BufSize - 6;
+
+       while (pch < pche) {
+               if (pt >= pte) {
+                       OutBuf->BufUsed = pt - OutBuf->buf;
+                       IncreaseBuf(OutBuf, 1, -1);
+                       pte = OutBuf->buf + OutBuf->BufSize - 6;
+                       /**< we max append 3 chars at once plus the \0 */
+
+                       pt = OutBuf->buf + OutBuf->BufUsed;
+               }
+
+               if (*pch == '<') {
+                       memcpy(pt, HKEY("&lt;"));
+                       pt += 4;
+                       pch ++;
+               }
+               else if (*pch == '>') {
+                       memcpy(pt, HKEY("&gt;"));
+                       pt += 4;
+                       pch ++;
+               }
+               else if (*pch == '&') {
+                       memcpy(pt, HKEY("&amp;"));
+                       pt += 5;
+                       pch++;
+               }
+               else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
+                       *pt = *pch;
+                       pt++; pch++;
+               }
+               else if (*pch < 0x20) {
+                       /* we probably shouldn't be doing this */
+                       if (OverrideLowChars)
+                       {
+                               *pt = '_';
+                               pt ++;
+                               pch ++;
+                       }
+                       else
+                       {
+                               *pt = '&';
+                               pt++;
+                               *pt = HexList[*(unsigned char*)pch][0];
+                               pt ++;
+                               *pt = HexList[*(unsigned char*)pch][1];
+                               pt ++; pch ++;
+                               *pt = '&';
+                               pt++;
+                               pch ++;
+                       }
+               }
+               else {
+                       IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(pch, pche);
+                       if (IsUtf8Sequence)
+                       {
+                               while (IsUtf8Sequence > 0){
+                                       *pt = *pch;
+                                       pt ++;
+                                       pch ++;
+                                       --IsUtf8Sequence;
+                               }
+                       }
+                       else
+                       {
+                               *pt = '&';
+                               pt++;
+                               *pt = HexList[*(unsigned char*)pch][0];
+                               pt ++;
+                               *pt = HexList[*(unsigned char*)pch][1];
+                               pt ++; pch ++;
+                               *pt = '&';
+                               pt++;
+                               pch ++;
+                       }
+               }
+       }
+       *pt = '\0';
+       OutBuf->BufUsed = pt - OutBuf->buf;
+}
+
+
 /** 
  * @ingroup StrBuf_DeEnCoder
  * @brief append a string in hex encoding to the buffer
@@ -1936,6 +2108,45 @@ void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *P
        *pt = '\0';
 }
 
+void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
+{
+       const char *pch;
+       char *pt;
+       int len;
+       long ExpectLen;
+       
+       if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
+               return;
+       if (PlainIn != NULL) {
+               if (PlainInLen < 0)
+                       len = strlen(PlainIn);
+               else
+                       len = PlainInLen;
+               pch = PlainIn;
+       }
+       else {
+               pch = In->buf;
+               len = In->BufUsed;
+       }
+
+       if (len == 0) 
+               return;
+
+       ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
+
+       if (ExpectLen > OutBuf->BufSize)
+               if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
+                       return;
+
+       pt = OutBuf->buf + OutBuf->BufUsed;
+
+       len = CtdlEncodeBase64(pt, pch, len, linebreaks);
+
+       pt += len;
+       OutBuf->BufUsed += len;
+       *pt = '\0';
+}
+
 /** 
  * @ingroup StrBuf_DeEnCoder
  * @brief append a string in hex encoding to the buffer
@@ -2493,6 +2704,23 @@ long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *Plai
        return Target->BufUsed;
 }
 
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief replace all non-Ascii characters by another
+ * @param Buf buffer to inspect
+ * @param repl charater to stamp over non ascii chars
+ */
+void StrBufAsciify(StrBuf *Buf, const char repl)
+{
+       long offset;
+
+       for (offset = 0; offset < Buf->BufUsed; offset ++)
+               if (!isascii(Buf->buf[offset]))
+                       Buf->buf[offset] = repl;
+       
+}
+
 /**
  * @ingroup StrBuf_DeEnCoder
  * @brief unhide special chars hidden to the HTML escaper
@@ -2586,9 +2814,14 @@ int StrBufDecodeBase64(StrBuf *Buf)
 {
        char *xferbuf;
        size_t siz;
-       if (Buf == NULL) return -1;
+
+       if (Buf == NULL)
+               return -1;
 
        xferbuf = (char*) malloc(Buf->BufSize);
+       if (xferbuf == NULL)
+               return -1;
+
        *xferbuf = '\0';
        siz = CtdlDecodeBase64(xferbuf,
                               Buf->buf,
@@ -2596,9 +2829,328 @@ int StrBufDecodeBase64(StrBuf *Buf)
        free(Buf->buf);
        Buf->buf = xferbuf;
        Buf->BufUsed = siz;
+
+       Buf->buf[Buf->BufUsed] = '\0';
        return siz;
 }
 
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief decode a buffer from base 64 encoding; expects targetbuffer
+ * @param BufIn Buffor to transform
+ * @param BufOut Buffer to put result into
+ */
+int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
+{
+       if ((BufIn == NULL) || (BufOut == NULL))
+               return -1;
+
+       if (BufOut->BufSize < BufIn->BufUsed)
+               IncreaseBuf(BufOut, BufIn->BufUsed, 0);
+
+       BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
+                                          BufIn->buf,
+                                          BufIn->BufUsed);
+       return BufOut->BufUsed;
+}
+
+typedef struct __z_enc_stream {
+       StrBuf OutBuf;
+       z_stream zstream;
+} z_enc_stream;
+
+void *StrBufNewStreamContext(eStreamType type)
+{
+       base64_decodestate *state;;
+
+       switch (type)
+       {
+       case eBase64Decode:
+       case eBase64Encode:
+               state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
+               base64_init_decodestate(state);
+               return state;
+               break;
+       case eZLibDecode:
+       {
+
+               z_enc_stream *stream;
+               int err;
+
+               stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
+               memset(stream, 0, sizeof(z_enc_stream));
+               stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
+               stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
+
+               err = inflateInit(&stream->zstream);
+
+               if (err != Z_OK) {
+                       StrBufDestroyStreamContext(type, (void**)&stream);
+                       return NULL;
+               }
+               return stream;
+
+       }
+       case eZLibEncode:
+       {
+               z_enc_stream *stream;
+               int err;
+
+               stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
+               memset(stream, 0, sizeof(z_enc_stream));
+               stream->OutBuf.BufSize = 4*SIZ; /// todo 64
+               stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
+               /* write gzip header */
+               stream->OutBuf.BufUsed = snprintf
+                       (stream->OutBuf.buf,
+                        stream->OutBuf.BufSize, 
+                        "%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);
+
+               err = deflateInit2(&stream->zstream,
+                                  ZLibCompressionRatio,
+                                  Z_DEFLATED,
+                                  -MAX_WBITS,
+                                  DEF_MEM_LEVEL,
+                                  Z_DEFAULT_STRATEGY);
+               if (err != Z_OK) {
+                       StrBufDestroyStreamContext(type, (void**) &stream);
+                       return NULL;
+               }
+               return stream;
+       }
+       case eEmtyCodec:
+               /// TODO
+
+               break;
+       }
+       return NULL;
+}
+
+void StrBufDestroyStreamContext(eStreamType type, void **vStream)
+{
+       if (*vStream) {
+               return;
+       }
+       switch (type)
+       {
+       case eBase64Encode:
+       case eBase64Decode:
+               free(*vStream);
+               *vStream = NULL;
+               break;
+       case eZLibDecode:
+       {
+               z_enc_stream *stream = (z_enc_stream *)*vStream;
+               (void)inflateEnd(&stream->zstream);
+               free(stream->OutBuf.buf);
+               free(stream);
+       }
+       case eZLibEncode:
+       {
+               z_enc_stream *stream = (z_enc_stream *)*vStream;
+               free(stream->OutBuf.buf);
+               free(stream);
+// todo more?
+               *vStream = NULL;
+               break;
+       }
+       case eEmtyCodec: 
+               break; /// TODO
+       }
+}
+
+int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, void *vStream, int LastChunk)
+{
+
+       switch (type)
+       {
+       case eBase64Encode:
+       {
+               /// TODO
+/*
+               // base64_decodestate *state = (base64_decodestate*)vStream;
+               long ExpectLen;
+               
+               if (In != NULL)
+               {
+                       pIn = In->buf;
+                       pInLen = In->BufUsed;
+               }
+               if ((In == NULL) || (vStream == NULL))
+                       return;
+               
+               ExpectLen = (pInLen / 4) * 3;
+
+               if (Target->BufSize - Target->BufUsed < ExpectLen)
+               {
+                       IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
+               }
+
+               ////    ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
+               Target->BufUsed += ExpectLen;
+               Target->buf[Target->BufUsed] = '\0';
+*/
+       }
+       break;
+       case eBase64Decode:
+       {
+/*
+               base64_decodestate *state = (base64_decodestate *)vStream;
+               long ExpectLen;
+               
+               if (In != NULL)
+               {
+                       pIn = In->buf;
+                       pInLen = In->BufUsed;
+               }
+               if ((pIn == NULL) || (vStream == NULL))
+                       return;
+               
+               ExpectLen = (pInLen / 4) * 3;
+
+               if (Target->BufSize - Target->BufUsed < ExpectLen)
+               {
+                       IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
+               }
+
+               ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
+               Target->BufUsed += ExpectLen;
+               Target->buf[Target->BufUsed] = '\0';
+*/
+       }
+       break;
+       case eZLibEncode:
+       {
+               z_enc_stream *stream = (z_enc_stream *)vStream;
+               int org_outbuf_len = stream->zstream.total_out;
+               int err;
+               unsigned int chunkavail;
+
+               if (In->ReadWritePointer != NULL)
+               {
+                       stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
+                       stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
+                               (In->ReadWritePointer - In->Buf->buf);
+               }
+               else
+               {
+                       stream->zstream.next_in = (Bytef *) In->Buf->buf;
+                       stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
+               }
+               
+               stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
+               stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
+
+               err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
+
+               stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
+               /// todo + org_outbuf_len;
+
+               if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
+
+               if (stream->zstream.avail_in == 0)
+               {
+                       FlushStrBuf(In->Buf);
+                       In->ReadWritePointer = NULL;
+               }
+               else
+               {
+                       if (stream->zstream.avail_in < 64)
+                       {
+                               memmove(In->Buf->buf,
+                                       In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
+                                       stream->zstream.avail_in);
+
+                               In->Buf->BufUsed = stream->zstream.avail_in;
+                               In->Buf->buf[In->Buf->BufUsed] = '\0';
+                       }
+                       else
+                       {
+                               
+                               In->ReadWritePointer = In->Buf->buf + 
+                                       (In->Buf->BufUsed - stream->zstream.avail_in);
+                       }
+               }
+               return (LastChunk && (err != Z_FINISH));
+
+       }
+       break;
+       case eZLibDecode: {
+               z_enc_stream *stream = (z_enc_stream *)vStream;
+               int org_outbuf_len = stream->zstream.total_out;
+               int err;
+
+               if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
+                       if (In->ReadWritePointer != NULL)
+                       {
+                               stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
+                               stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
+                                       (In->ReadWritePointer - In->Buf->buf);
+                       }
+                       else
+                       {
+                               stream->zstream.next_in = (Bytef *) In->Buf->buf;
+                               stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
+                       }
+               }
+
+               stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
+               stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
+
+               err = inflate(&stream->zstream, Z_NO_FLUSH);
+
+               ///assert(ret != Z_STREAM_ERROR);  /* state not clobbered * /
+               switch (err) {
+               case Z_NEED_DICT:
+                       err = Z_DATA_ERROR;     /* and fall through */
+
+               case Z_DATA_ERROR:
+                       fprintf(stderr, "sanoteuh\n");
+               case Z_MEM_ERROR:
+                       (void)inflateEnd(&stream->zstream);
+                       return err;
+               }
+
+               stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
+
+               if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
+
+               if (stream->zstream.avail_in == 0)
+               {
+                       FlushStrBuf(In->Buf);
+                       In->ReadWritePointer = NULL;
+               }
+               else
+               {
+                       if (stream->zstream.avail_in < 64)
+                       {
+                               memmove(In->Buf->buf,
+                                       In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
+                                       stream->zstream.avail_in);
+
+                               In->Buf->BufUsed = stream->zstream.avail_in;
+                               In->Buf->buf[In->Buf->BufUsed] = '\0';
+                       }
+                       else
+                       {
+                               
+                               In->ReadWritePointer = In->Buf->buf + 
+                                       (In->Buf->BufUsed - stream->zstream.avail_in);
+                       }
+               }
+       }
+               break;
+       case eEmtyCodec: {
+
+       }
+               break; /// TODO
+       }
+       return 0;
+}
+
 /**
  * @ingroup StrBuf_DeEnCoder
  * @brief decode a buffer from base 64 encoding; destroys original
@@ -2753,7 +3305,6 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
                ch = (unsigned char) source->buf[i];
                if ((ch  <  32) || 
                    (ch  > 126) || 
-                   (ch ==  61) ||
                    (ch == '=') ||
                     (ch == '?') ||
                    (ch == '_') ||
@@ -2781,6 +3332,95 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
        return (*target)->BufUsed;;
 }
 
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief      Quoted-Printable encode a message; make it < 80 columns width.
+ * @param      source          Source string to be encoded.
+ * @returns     buffer with encoded message.
+ */
+StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
+{
+       StrBuf *OutBuf;
+       char *Optr, *OEptr;
+       const char *ptr, *eptr;
+       unsigned char ch;
+       int LinePos;
+
+       OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
+       Optr = OutBuf->buf;
+       OEptr = OutBuf->buf + OutBuf->BufSize;
+       ptr = EncodeMe->buf;
+       eptr = EncodeMe->buf + EncodeMe->BufUsed;
+       LinePos = 0;
+
+       while (ptr < eptr)
+       {
+               if (Optr + 4 >= OEptr)
+               {
+                       long Offset;
+                       Offset = Optr - OutBuf->buf;
+                       OutBuf->BufUsed = Optr - OutBuf->buf;
+                       IncreaseBuf(OutBuf, 1, 0);
+                       Optr = OutBuf->buf + Offset;
+                       OEptr = OutBuf->buf + OutBuf->BufSize;
+               }
+               if (*ptr == '\r')
+               {
+                       /* ignore carriage returns */
+                       ptr ++;
+               }
+               else if (*ptr == '\n') {
+                       /* hard line break */
+                       memcpy(Optr, HKEY("=0A"));
+                       Optr += 3;
+                       LinePos += 3;
+                       ptr ++;
+               }
+               else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
+                        ( (*ptr >= 62) && (*ptr <= 126) ))
+               {
+                       *Optr = *ptr;
+                       Optr ++;
+                       ptr ++;
+                       LinePos ++;
+               }
+               else {
+                       ch = *ptr;
+                       *Optr = '=';
+                       Optr ++;
+                       *Optr = HexList[ch][0];
+                       Optr ++;
+                       *Optr = HexList[ch][1];
+                       Optr ++;
+                       LinePos += 3;
+                       ptr ++;
+               }
+
+               if (LinePos > 72) {
+                       /* soft line break */
+                       if (isspace(*(Optr - 1))) {
+                               ch = *(Optr - 1);
+                               Optr --;
+                               *Optr = '=';
+                               Optr ++;
+                               *Optr = HexList[ch][0];
+                               Optr ++;
+                               *Optr = HexList[ch][1];
+                               Optr ++;
+                               LinePos += 3;
+                       }
+                       *Optr = '=';
+                       Optr ++;
+                       *Optr = '\n';
+                       Optr ++;
+                       LinePos = 0;
+               }
+       }
+       *Optr = '\0';
+       OutBuf->BufUsed = Optr - OutBuf->buf;
+
+       return OutBuf;
+}
 
 
 static void AddRecipient(StrBuf *Target, 
@@ -3512,10 +4152,6 @@ long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
  *                               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
@@ -3526,14 +4162,13 @@ long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
  * @param sourceLen length of source to encode 
  * @param level compression level
  */
+#ifdef HAVE_ZLIB
 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",
@@ -3863,76 +4498,215 @@ long IOBufferStrLength(IOBuffer *FB)
        return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
 }
 
-void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
+inline static void FDIOBufferFlush(FDIOBuffer *FDB)
 {
        memset(FDB, 0, sizeof(FDIOBuffer));
-       FDB->ChunkSize = 
-               FDB->TotalSendSize = TotalSendSize;
+       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;
-#ifndef LINUX_SPLICE
-       FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
-#else
-       pipe(FDB->SplicePipe);
+
+#ifdef LINUX_SPLICE
+       if (EnableSplice)
+               pipe(FDB->SplicePipe);
+       else
 #endif
+               FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
+
        FDB->OtherFD = FD;
 }
 
 void FDIOBufferDelete(FDIOBuffer *FDB)
 {
-#ifndef LINUX_SPLICE
-       FreeStrBuf(&FDB->ChunkBuffer);
-#else
-       close(FDB->SplicePipe[0]);
-       close(FDB->SplicePipe[1]);
-       
+#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
-       close(FDB->OtherFD);
-       memset(FDB, 0, sizeof(FDIOBuffer));     
+               FreeStrBuf(&FDB->ChunkBuffer);
+       
+       if (FDB->OtherFD > 0)
+               close(FDB->OtherFD);
+       FDIOBufferFlush(FDB);
 }
 
 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
 {
+       ssize_t sent, pipesize;
 
-#ifdef LINUX_SPLICE
-       ssize_t sent;
-       sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
-       if (sent == -1)
+       if (FDB->TotalSendSize > 0)
        {
-               *Err = strerror(errno);
-               return sent;
-       }
-       FDB->ChunkSendRemain -= sent;
-       FDB->TotalSentAlready += sent;
-       return FDB->ChunkSendRemain;
-#else
+#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;
+                       }
 
-       char *pRead;
-       long nRead = 0;
+                       nRead = write(FDB->IOB->fd,
+                                     FDB->ChunkBuffer->buf     + FDB->TotalSentAlready,
+                                     FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
 
-       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';
+                       if (nRead >= 0) {
+                               FDB->TotalSentAlready += nRead;
+                               FDB->ChunkSendRemain -= nRead;
+                               return FDB->ChunkSendRemain;
+                       }
+                       else {
+                               return nRead;
+                       }
                }
-               else if (nRead == 0) {}
-               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->ChunkSendRemain);
+                       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;
+                       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;
+                       }
+               }
        }
-#endif
 }
 
 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
@@ -3940,57 +4714,143 @@ int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
        ssize_t sent, pipesize;
 
 #ifdef LINUX_SPLICE
-
-       pipesize = splice(FDB->IOB->fd, NULL, 
-                         FDB->SplicePipe[1], NULL, 
-                         FDB->ChunkSendRemain, 
-                         SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
-       if (pipesize == -1)
+       if (EnableSplice)
        {
-               *Err = strerror(errno);
-               return pipesize;
-       }
+               if (FDB->PipeSize == 0)
+               {
+                       pipesize = splice(FDB->IOB->fd,
+                                         NULL, 
+                                         FDB->SplicePipe[1],
+                                         NULL, 
+                                         FDB->ChunkSendRemain, 
+                                         SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
+
+                       if (pipesize == -1)
+                       {
+                               *Err = strerror(errno);
+                               return pipesize;
+                       }
+                       FDB->PipeSize = pipesize;
+               }
        
-       sent = splice(FDB->SplicePipe[0], NULL, 
-                     FDB->OtherFD, &FDB->TotalSentAlready, 
-                     pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
-       if (sent == -1)
-       {
-               *Err = strerror(errno);
+               sent = splice(FDB->SplicePipe[0],
+                             NULL, 
+                             FDB->OtherFD,
+                             &FDB->TotalSentAlready, 
+                             FDB->PipeSize,
+                             SPLICE_F_MORE | SPLICE_F_MOVE);
+
+               if (sent == -1)
+               {
+                       *Err = strerror(errno);
+                       return sent;
+               }
+               FDB->PipeSize -= sent;
+               FDB->ChunkSendRemain -= sent;
                return sent;
        }
-       FDB->ChunkSendRemain -= sent;
-       return sent;
-#else
-       
-       sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
-       if (sent > 0) {
-               int nWritten = 0;
-               int rc; 
+       else
+#endif
+       {
+               sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
+               if (sent > 0) {
+                       int nWritten = 0;
+                       int rc; 
                
-               FDB->ChunkBuffer->BufUsed = sent;
+                       FDB->ChunkBuffer->BufUsed = sent;
+
+                       while (nWritten < FDB->ChunkBuffer->BufUsed) {
+                               rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
+                               if (rc < 0) {
+                                       *Err = strerror(errno);
+                                       return rc;
+                               }
+                               nWritten += rc;
 
-               while (nWritten < FDB->ChunkBuffer->BufUsed) {
-                       rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
-                       if (rc < 0) {
-                               *Err = strerror(errno);
-                               return rc;
                        }
-                       nWritten += rc;
+                       FDB->ChunkBuffer->BufUsed = 0;
+                       FDB->TotalSentAlready += sent;
+                       FDB->ChunkSendRemain -= sent;
+                       return FDB->ChunkSendRemain;
+               }
+               else if (sent < 0) {
+                       *Err = strerror(errno);
+                       return sent;
+               }
+               return 0;
+       }
+}
+
+int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
+{
+       ssize_t sent, pipesize;
 
+#ifdef LINUX_SPLICE
+       if (EnableSplice)
+       {
+               if (FDB->PipeSize == 0)
+               {
+                       pipesize = splice(FDB->IOB->fd,
+                                         &FDB->TotalReadAlready, 
+                                         FDB->SplicePipe[1],
+                                         NULL, 
+                                         FDB->ChunkSendRemain, 
+                                         SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
+                       
+                       if (pipesize == -1)
+                       {
+                               *Err = strerror(errno);
+                               return pipesize;
+                       }
+                       FDB->PipeSize = pipesize;
+               }
+               
+               sent = splice(FDB->SplicePipe[0],
+                             NULL, 
+                             FDB->OtherFD,
+                             &FDB->TotalSentAlready, 
+                             FDB->PipeSize,
+                             SPLICE_F_MORE | SPLICE_F_MOVE);
+               
+               if (sent == -1)
+               {
+                       *Err = strerror(errno);
+                       return sent;
                }
-               FDB->ChunkBuffer->BufUsed = 0;
-               FDB->TotalSentAlready += sent;
+               FDB->PipeSize -= sent;
                FDB->ChunkSendRemain -= sent;
-               return FDB->ChunkSendRemain;
-       }
-       else if (sent < 0) {
-               *Err = strerror(errno);
                return sent;
        }
+       else
+#endif 
+       {
+               sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
+               if (sent > 0) {
+                       int nWritten = 0;
+                       int rc; 
+               
+                       FDB->ChunkBuffer->BufUsed = sent;
 
-#endif
-       return 0;
+                       while (nWritten < FDB->ChunkBuffer->BufUsed) {
+                               rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
+                               if (rc < 0) {
+                                       *Err = strerror(errno);
+                                       return rc;
+                               }
+                               nWritten += rc;
+
+                       }
+                       FDB->ChunkBuffer->BufUsed = 0;
+                       FDB->TotalSentAlready += sent;
+                       FDB->ChunkSendRemain -= sent;
+                       return FDB->ChunkSendRemain;
+               }
+               else if (sent < 0) {
+                       *Err = strerror(errno);
+                       return sent;
+               }
+               return 0;
+       }
 }
 
 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
@@ -4183,10 +5043,11 @@ int StrBufTCP_read_buffered_line(StrBuf *Line,
                        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');
+                       if ((pch == NULL) &&
+                           (buf->BufUsed + 10 > buf->BufSize) &&
+                           (IncreaseBuf(buf, 1, -1) == -1))
+                               return -1;
                        continue;
                }
                
@@ -4376,6 +5237,10 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
 
                        continue;
                }
+               else
+               {
+                       nSuccessLess++;
+               }
        }
        *Pos = NULL;
        if (pLF != NULL) {
@@ -4512,9 +5377,12 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
        int nSuccessLess = 0;
        int MaxTries;
 
-       if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
+       if ((Blob == NULL)  ||
+           (*fd == -1)     ||
+           (IOBuf == NULL) ||
+           (Pos == NULL))
        {
-               if (*Pos != NULL)
+               if (Pos != NULL)
                        *Pos = NULL;
                *Error = ErrRBB_BLOBFPreConditionFailed;
                return -1;