StringBuf: add URL-encoder that also encodes the @ character.
[citadel.git] / libcitadel / lib / stringbuf.c
index dfba39d7e13e904ffc202186139a53a90bdfc40f..31e99b52f06c15a477990a726314595cdda34956 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * Copyright (c) 1987-2011 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
+ * the Free Software Foundation; either version 3 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _GNU_SOURCE
 #include "sysdep.h"
 #include <ctype.h>
 #include <errno.h>
 #include <sys/types.h>
 #define SHOW_ME_VAPPEND_PRINTF
 #include <stdarg.h>
+
+#ifndef LINUX_SENDFILE
+#include <sys/sendfile.h>
+#endif
 #include "libcitadel.h"
 
 #ifdef HAVE_ICONV
 #include <execinfo.h>
 #endif
 
+#ifdef LINUX_SENDFILE
+#include <sys/sendfile.h>
+#endif
+
 #ifdef HAVE_ZLIB
 #include <zlib.h>
 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
@@ -276,9 +303,12 @@ static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
                return -1;
                
        if (DestSize > 0)
-               while (NewSize <= DestSize)
+               while ((NewSize <= DestSize) && (NewSize != 0))
                        NewSize *= 2;
 
+       if (NewSize == 0)
+               return -1;
+
        NewBuf= (char*) malloc(NewSize);
        if (NewBuf == NULL)
                return -1;
@@ -479,9 +509,14 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
        else
                CopySize = nChars;
 
-       while (Siz <= CopySize)
+       while ((Siz <= CopySize) && (Siz != 0))
                Siz *= 2;
 
+       if (Siz == 0)
+       {
+               return NULL;
+       }
+
        NewBuf->buf = (char*) malloc(Siz);
        if (NewBuf->buf == NULL)
        {
@@ -532,9 +567,14 @@ int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
        else
                CopySize = nChars;
 
-       while (Siz <= CopySize)
+       while ((Siz <= CopySize) && (Siz != 0))
                Siz *= 2;
 
+       if (Siz == 0) {
+               FlushStrBuf(Buf);
+               return -1;
+       }
+
        if (Siz != Buf->BufSize)
                IncreaseBuf(Buf, 0, Siz);
        memcpy(Buf->buf, ptr, CopySize);
@@ -858,7 +898,8 @@ void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
                va_end(apl);
                newused = Offset + nWritten;
                if (newused >= Buf->BufSize) {
-                       IncreaseBuf(Buf, 1, newused);
+                       if (IncreaseBuf(Buf, 1, newused) == -1)
+                               return; /* TODO: error handling? */
                        newused = Buf->BufSize + 1;
                }
                else {
@@ -899,7 +940,8 @@ void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
                va_end(arg_ptr);
                newused = Buf->BufUsed + nWritten;
                if (newused >= Buf->BufSize) {
-                       IncreaseBuf(Buf, 1, newused);
+                       if (IncreaseBuf(Buf, 1, newused) == -1)
+                               return; /* TODO: error handling? */
                        newused = Buf->BufSize + 1;
                }
                else {
@@ -930,7 +972,8 @@ void StrBufPrintf(StrBuf *Buf, const char *format, ...)
                nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
                va_end(arg_ptr);
                if (nWritten >= Buf->BufSize) {
-                       IncreaseBuf(Buf, 0, 0);
+                       if (IncreaseBuf(Buf, 0, 0) == -1)
+                               return; /* TODO: error handling? */
                        nWritten = Buf->BufSize + 1;
                        continue;
                }
@@ -1333,7 +1376,7 @@ int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char se
        //cit_backtrace();
        //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
 
-       while ((s<e) && !IsEmptyStr(s)) {
+       while ((s < e) && !IsEmptyStr(s)) {
                if (*s == separator) {
                        ++current_token;
                }
@@ -1620,7 +1663,7 @@ int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator
        //cit_backtrace();
        //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
 
-       while ((s<EndBuffer) && !IsEmptyStr(s)) {
+       while ((s < EndBuffer) && !IsEmptyStr(s)) {
                if (*s == separator) {
                        ++current_token;
                }
@@ -1783,12 +1826,12 @@ void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
 
 /** 
  * @ingroup StrBuf_DeEnCoder
- * @brief append a string in hex encoding to the buffer
+ * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
  * @param OutBuf the output buffer
  * @param In Buffer to encode
  * @param PlainIn way in from plain old c strings
  */
-void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
+void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
 {
        const char *pch, *pche;
        char *pt, *pte;
@@ -1807,6 +1850,70 @@ void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
                len = In->BufUsed;
        }
 
+       if (len == 0) 
+               return;
+
+       pt = OutBuf->buf + OutBuf->BufUsed;
+       pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
+
+       while (pch < pche) {
+               if (pt >= pte) {
+                       IncreaseBuf(OutBuf, 1, -1);
+                       pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
+                       pt = OutBuf->buf + OutBuf->BufUsed;
+               }
+
+               if((*pch >= 'a' && *pch <= 'z') ||
+                  (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
+                  (*pch >= '0' && *pch <= ':') || /* 0-9 : */
+                  (*pch == '!') || (*pch == '_') || 
+                  (*pch == ',') || (*pch == '.'))
+               {
+                       *(pt++) = *(pch++);
+                       OutBuf->BufUsed++;
+               }                       
+               else {
+                       *pt = '%';
+                       *(pt + 1) = HexList[(unsigned char)*pch][0];
+                       *(pt + 2) = HexList[(unsigned char)*pch][1];
+                       pt += 3;
+                       OutBuf->BufUsed += 3;
+                       pch ++;
+               }
+       }
+       *pt = '\0';
+}
+
+/** 
+ * @ingroup StrBuf_DeEnCoder
+ * @brief append a string in hex encoding 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?
+ */
+void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
+{
+       const unsigned char *pch, *pche;
+       char *pt, *pte;
+       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 unsigned char*)In->buf;
+               pche = pch + In->BufUsed;
+               len = In->BufUsed;
+       }
+
        if (len == 0) 
                return;
 
@@ -1820,14 +1927,26 @@ void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
                        pt = OutBuf->buf + OutBuf->BufUsed;
                }
 
-               *pt = HexList[(unsigned char)*pch][0];
+               *pt = HexList[*pch][0];
                pt ++;
-               *pt = HexList[(unsigned char)*pch][1];
+               *pt = HexList[*pch][1];
                pt ++; pch ++; OutBuf->BufUsed += 2;
        }
        *pt = '\0';
 }
 
+/** 
+ * @ingroup StrBuf_DeEnCoder
+ * @brief append a string in hex encoding to the buffer
+ * @param OutBuf the output buffer
+ * @param In Buffer to encode
+ * @param PlainIn way in from plain old c strings
+ */
+void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
+{
+       StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
+}
+
 /**
  * @ingroup StrBuf_DeEnCoder
  * @brief Append a string, escaping characters which have meaning in HTML.  
@@ -2093,6 +2212,7 @@ long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
        const char *aptr, *eiptr;
        char *bptr, *eptr;
        long len;
+       int IsUtf8Sequence;
 
        if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
                return -1;
@@ -2112,31 +2232,83 @@ long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
                return -1;
 
        bptr = Target->buf + Target->BufUsed;
-       eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in...  */
+       eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
 
        while (aptr < eiptr){
                if(bptr >= eptr) {
                        IncreaseBuf(Target, 1, -1);
-                       eptr = Target->buf + Target->BufSize - 3; 
+                       eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
                        bptr = Target->buf + Target->BufUsed;
                }
-               if (*aptr == '"') {
+               switch (*aptr) {
+               case '\n':
+                       memcpy(bptr, HKEY("\\n"));
+                       bptr += 2;
+                       Target->BufUsed += 2;                           
+                       break;
+               case '\r':
+                       memcpy(bptr, HKEY("\\r"));
+                       bptr += 2;
+                       Target->BufUsed += 2;
+                       break;
+               case '"':
                        *bptr = '\\';
                        bptr ++;
                        *bptr = '"';
                        bptr ++;
                        Target->BufUsed += 2;
-               } else if (*aptr == '\\') {
+                       break;
+               case '\\':
+                       if ((*(aptr + 1) == 'u') &&
+                           isxdigit(*(aptr + 2)) &&
+                           isxdigit(*(aptr + 3)) &&
+                           isxdigit(*(aptr + 4)) &&
+                           isxdigit(*(aptr + 5)))
+                       { /* oh, a unicode escaper. let it pass through. */
+                               memcpy(bptr, aptr, 6);
+                               aptr += 5;
+                               bptr +=6;
+                               Target->BufUsed += 6;
+                       }
+                       else 
+                       {
+                               *bptr = '\\';
+                               bptr ++;
+                               *bptr = '\\';
+                               bptr ++;
+                               Target->BufUsed += 2;
+                       }
+                       break;
+               case '\b':
                        *bptr = '\\';
                        bptr ++;
+                       *bptr = 'b';
+                       bptr ++;
+                       Target->BufUsed += 2;
+                       break;
+               case '\f':
                        *bptr = '\\';
                        bptr ++;
+                       *bptr = 'f';
+                       bptr ++;
                        Target->BufUsed += 2;
-               }
-               else{
-                       *bptr = *aptr;
-                       bptr++;
-                       Target->BufUsed ++;
+                       break;
+               case '\t':
+                       *bptr = '\\';
+                       bptr ++;
+                       *bptr = 't';
+                       bptr ++;
+                       Target->BufUsed += 2;
+                       break;
+               default:
+                       IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
+                       while (IsUtf8Sequence > 0){
+                               *bptr = *aptr;
+                               Target->BufUsed ++;
+                               if (--IsUtf8Sequence)
+                                       aptr++;
+                               bptr++;
+                       }
                }
                aptr ++;
        }
@@ -2190,83 +2362,127 @@ long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *Plai
                        eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
                        bptr = Target->buf + Target->BufUsed;
                }
-               if (*aptr == '<') {
-                       memcpy(bptr, "&lt;", 4);
+               switch (*aptr) {
+               case '<':
+                       memcpy(bptr, HKEY("&lt;"));
                        bptr += 4;
                        Target->BufUsed += 4;
-               }
-               else if (*aptr == '>') {
-                       memcpy(bptr, "&gt;", 4);
+                       break;
+               case '>':
+                       memcpy(bptr, HKEY("&gt;"));
                        bptr += 4;
                        Target->BufUsed += 4;
-               }
-               else if (*aptr == '&') {
-                       memcpy(bptr, "&amp;", 5);
+                       break;
+               case '&':
+                       memcpy(bptr, HKEY("&amp;"));
                        bptr += 5;
                        Target->BufUsed += 5;
-               }
-               else if (*aptr == LB) {
+                       break;
+               case LB:
                        *bptr = '<';
                        bptr ++;
                        Target->BufUsed ++;
-               }
-               else if (*aptr == RB) {
+                       break;
+               case RB:
                        *bptr = '>';
                        bptr ++;
                        Target->BufUsed ++;
-               }
-               else if ((*aptr == 32) && (nbsp == 1)) {
-                       memcpy(bptr, "&nbsp;", 6);
-                       bptr += 6;
-                       Target->BufUsed += 6;
-               }
-               else if ((*aptr == '\n') && (nolinebreaks == 1)) {
-                       *bptr='\0';     /* nothing */
-               }
-               else if ((*aptr == '\n') && (nolinebreaks == 2)) {
-                       memcpy(bptr, "&lt;br/&gt;", 11);
-                       bptr += 11;
-                       Target->BufUsed += 11;
-               }
-
-               else if ((*aptr == '\r') && (nolinebreaks != 0)) {
-                       *bptr='\0';     /* nothing */
-               }
-
-               else if ((*aptr == '"') || (*aptr == QU)) {
+                       break;
+               case '\n':
+                       switch (nolinebreaks) {
+                       case 1:
+                               *bptr='\0';     /* nothing */
+                               break;
+                       case 2:
+                               memcpy(bptr, HKEY("&lt;br/&gt;"));
+                               bptr += 11;
+                               Target->BufUsed += 11;
+                               break;
+                       default:
+                               memcpy(bptr, HKEY("\\n"));
+                               bptr += 2;
+                               Target->BufUsed += 2;                           
+                       }
+                       break;
+               case '\r':
+                       switch (nolinebreaks) {
+                       case 1:
+                       case 2:
+                               *bptr='\0';     /* nothing */
+                               break;
+                       default:
+                               memcpy(bptr, HKEY("\\r"));
+                               bptr += 2;
+                               Target->BufUsed += 2;
+                               break;
+                       }
+                       break;
+               case '"':
+               case QU:
                        *bptr = '\\';
                        bptr ++;
                        *bptr = '"';
                        bptr ++;
                        Target->BufUsed += 2;
-               } else if (*aptr == '\\') {
+                       break;
+               case '\\':
+                       if ((*(aptr + 1) == 'u') &&
+                           isxdigit(*(aptr + 2)) &&
+                           isxdigit(*(aptr + 3)) &&
+                           isxdigit(*(aptr + 4)) &&
+                           isxdigit(*(aptr + 5)))
+                       { /* oh, a unicode escaper. let it pass through. */
+                               memcpy(bptr, aptr, 6);
+                               aptr += 5;
+                               bptr +=6;
+                               Target->BufUsed += 6;
+                       }
+                       else 
+                       {
+                               *bptr = '\\';
+                               bptr ++;
+                               *bptr = '\\';
+                               bptr ++;
+                               Target->BufUsed += 2;
+                       }
+                       break;
+               case '\b':
                        *bptr = '\\';
                        bptr ++;
+                       *bptr = 'b';
+                       bptr ++;
+                       Target->BufUsed += 2;
+                       break;
+               case '\f':
                        *bptr = '\\';
                        bptr ++;
+                       *bptr = 'f';
+                       bptr ++;
                        Target->BufUsed += 2;
-               }
-               else {
-                       if (((unsigned char)*aptr) >= 0x20)
-                       {
-                               IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
-                               
+                       break;
+               case '\t':
+                       *bptr = '\\';
+                       bptr ++;
+                       *bptr = 't';
+                       bptr ++;
+                       Target->BufUsed += 2;
+                       break;
+               case  32:
+                       if (nbsp == 1) {
+                               memcpy(bptr, HKEY("&nbsp;"));
+                               bptr += 6;
+                               Target->BufUsed += 6;
+                               break;
+                       }
+               default:
+                       IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
+                       while (IsUtf8Sequence > 0){
                                *bptr = *aptr;
                                Target->BufUsed ++;
-                               while (IsUtf8Sequence > 1){
-                                       if(bptr + IsUtf8Sequence >= eptr) {
-                                               IncreaseBuf(Target, 1, -1);
-                                               eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
-                                               bptr = Target->buf + Target->BufUsed - 1;
-                                       }
-                                       bptr++; aptr++;
-                                       IsUtf8Sequence --;
-                                       *bptr = *aptr;
-                                       Target->BufUsed ++;
-                               }
+                               if (--IsUtf8Sequence)
+                                       aptr++;
                                bptr++;
                        }
-
                }
                aptr ++;
        }
@@ -2531,12 +2747,23 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
                if ((*target)->BufUsed + 4 >= (*target)->BufSize)
                        IncreaseBuf(*target, 1, 0);
                ch = (unsigned char) source->buf[i];
-               if ((ch < 32) || (ch > 126) || (ch == 61)) {
+               if ((ch  <  32) || 
+                   (ch  > 126) || 
+                   (ch ==  61) ||
+                   (ch == '=') ||
+                    (ch == '?') ||
+                   (ch == '_') ||
+                   (ch == '[') ||
+                   (ch == ']')   )
+               {
                        sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
                        (*target)->BufUsed += 3;
                }
                else {
-                       (*target)->buf[(*target)->BufUsed] = ch;
+                       if (ch == ' ')
+                               (*target)->buf[(*target)->BufUsed] = '_';
+                       else
+                               (*target)->buf[(*target)->BufUsed] = ch;
                        (*target)->BufUsed++;
                }
        }
@@ -2590,15 +2817,12 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                                           StrBuf *EncBuf)
 {
        StrBuf *Target;
-       int need_to_encode;
-
        const char *pch, *pche;
        const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
 
        if ((Recp == NULL) || (StrLength(Recp) == 0))
                return NULL;
 
-       need_to_encode = 0;
        pch = ChrPtr(Recp);
        pche = pch + StrLength(Recp);
 
@@ -2609,8 +2833,6 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
 
        while ((pch != NULL) && (pch < pche))
        {
-               int ColonOk = 0;
-
                while (isspace(*pch)) pch++;
                UserStart = UserEnd = EmailStart = EmailEnd = NULL;
                
@@ -2640,7 +2862,6 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                        if (EmailEnd == NULL)
                                EmailEnd = pche;
                        pch = EmailEnd + 1;
-                       ColonOk = 1;
                }
                else {
                        int gt = 0;
@@ -2748,7 +2969,7 @@ void StrBufReplaceChars(StrBuf *buf, char search, char replace)
 
 /**
  * @ingroup StrBuf
- * @brief removes all \r s from the string, or replaces them with \n if its not a combination of both.
+ * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
  * @param buf Buffer to modify
  */
 void StrBufToUnixLF(StrBuf *buf)
@@ -2830,7 +3051,8 @@ static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
                return NULL;
 
        if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
-           ((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && 
+           (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
+            ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
            (*(end + 2) == '?')) {
                /* skip on to the end of the cluster, the next ?= */
                end = strstr(end + 3, "?=");
@@ -3083,7 +3305,7 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target,
        start = strstr(DecodeMee->buf, "=?");
        eptr = DecodeMee->buf + DecodeMee->BufUsed;
        if (start != NULL) 
-               end = FindNextEnd (DecodeMee, start);
+               end = FindNextEnd (DecodeMee, start + 2);
        else {
                StrBufAppendBuf(Target, DecodeMee, 0);
                FreeStrBuf(&DecodedInvalidBuf);
@@ -3179,19 +3401,24 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target,
  * @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
+ * @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 = 1;
-        char test = (1<<7);
-       
-       while ((n < 8) && ((test & *CharS) != 0)) {
-               test = test << 1;
+       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 = 1;
+               n = 0;
        return n;
 }
 
@@ -3204,7 +3431,7 @@ static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *Char
 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
 {
 /** 11??.???? indicates an UTF8 Sequence. */
-       return ((Char & 0xC0) != 0);
+       return ((Char & 0xC0) == 0xC0);
 }
 
 /**
@@ -3402,18 +3629,27 @@ 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 = FB->ReadWritePointer - FB->Buf->buf;
-               bufremain = FB->Buf->BufSize - FB->Buf->BufUsed;
+               long already_read;
+               
+               already_read = FB->ReadWritePointer - FB->Buf->buf;
+               bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
 
                if (already_read != 0) {
-                       long unread = FB->Buf->BufUsed - already_read;
+                       long unread;
+                       
+                       unread = FB->Buf->BufUsed - already_read;
+
                        /* else nothing to compact... */
                        if (unread == 0) {
                                FB->ReadWritePointer = FB->Buf->buf;
@@ -3432,22 +3668,35 @@ long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
                                else
                                        memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
                                FB->ReadWritePointer = FB->Buf->buf;
-                               bufremain = FB->Buf->BufSize - unread;
+                               bufremain = FB->Buf->BufSize - unread - 1;
                        }
-                       else if (bufremain < (FB->Buf->BufSize / 10)) {
-                               /* get a bigger buffer */ ///TODO: special increase function that won't copy the already read!
-                               IncreaseBuf(FB->Buf, 0, -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;
+
+                               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;
+               bufremain = FB->Buf->BufSize - 1;
        }
-       n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain - 1);
+
+       n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
 
        if (n > 0) {
                FB->Buf->BufUsed += n;
@@ -3461,6 +3710,9 @@ 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 - 
@@ -3540,7 +3792,18 @@ eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
                        optr --;
                if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
                        LineBuf->BufUsed = optr - LineBuf->buf;
-                       *optr = '\0';       
+                       *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;
                }
        }
@@ -3580,6 +3843,203 @@ eReadState StrBufCheckBuffer(IOBuffer *FB)
        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 = NewStrBufPlain(NULL, TotalSendSize + 1);
+#else
+       pipe(FDB->SplicePipe);
+#endif
+       FDB->OtherFD = FD;
+}
+
+void FDIOBufferDelete(FDIOBuffer *FDB)
+{
+#ifndef LINUX_SENDFILE
+       FreeStrBuf(&FDB->ChunkBuffer);
+#else
+       close(FDB->SplicePipe[0]);
+       close(FDB->SplicePipe[1]);
+       
+#endif
+       close(FDB->OtherFD);
+       memset(FDB, 0, sizeof(FDIOBuffer));     
+}
+
+int FileSendChunked(FDIOBuffer *FDB, const char **Err)
+{
+       char *pRead;
+       long nRead = 0;
+       
+#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;
+       FDB->TotalSentAlready += sent;
+       return FDB->ChunkSendRemain;
+#else
+       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->ChunkSendRemain);
+
+       if (nRead >= 0) {
+               FDB->TotalSentAlready += nRead;
+               FDB->ChunkSendRemain -= nRead;
+               return FDB->ChunkSendRemain;
+       }
+       else {
+               return nRead;
+       }
+#endif
+}
+
+int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
+{
+       ssize_t sent, pipesize;
+
+#ifdef LINUX_SENDFILE
+
+       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;
+       }
+       
+       sent = splice(FDB->SplicePipe[0], NULL, 
+                     FDB->OtherFD, &FDB->TotalSentAlready, 
+                     pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
+       if (sent == -1)
+       {
+               *Err = strerror(errno);
+               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; 
+               
+               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;
+
+               }
+               FDB->ChunkBuffer->BufUsed = 0;
+               FDB->TotalSentAlready += sent;
+               FDB->ChunkSendRemain -= sent;
+               return FDB->ChunkSendRemain;
+       }
+       else if (sent < 0) {
+               *Err = strerror(errno);
+               return sent;
+       }
+
+#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!                  *
  *******************************************************************************/
@@ -3933,7 +4393,7 @@ static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong argumen
 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
 {
        int fdflags;
-       int len, rlen, slen;
+       int rlen;
        int nSuccessLess;
        int nRead = 0;
        char *ptr;
@@ -3953,8 +4413,6 @@ int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **E
 
        ptr = Buf->buf + Buf->BufUsed;
 
-       slen = len = Buf->BufUsed;
-
        fdflags = fcntl(*fd, F_GETFL);
        IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
        nSuccessLess = 0;
@@ -4023,17 +4481,15 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
                           int check, 
                           const char **Error)
 {
-       const char *pche;
        const char *pos;
        int fdflags;
        int len = 0;
-       int rlen, slen;
+       int rlen;
        int nRead = 0;
        int nAlreadyRead = 0;
        int IsNonBlock;
        char *ptr;
        fd_set rfds;
-       const char *pch;
        struct timeval tv;
        int nSuccessLess = 0;
        int MaxTries;
@@ -4062,9 +4518,6 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
            (pos != NULL) && 
            (pos < IOBuf->buf + IOBuf->BufUsed)) 
        {
-               pche = IOBuf->buf + IOBuf->BufUsed;
-               pch = pos;
-
                if (rlen < nBytes) {
                        memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
                        Blob->BufUsed += rlen;
@@ -4092,7 +4545,7 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
                IncreaseBuf(IOBuf, 0, nBytes - nRead);
        ptr = IOBuf->buf;
 
-       slen = len = Blob->BufUsed;
+       len = Blob->BufUsed;
 
        fdflags = fcntl(*fd, F_GETFL);
        IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
@@ -4183,9 +4636,9 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
  * @param Buf 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
+ * @returns size of remaining buffer
  */
-int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
+int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
 {
        const char *aptr, *ptr, *eptr;
        char *optr, *xptr;
@@ -4263,9 +4716,10 @@ void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
                        b++; a++;
                }
        }
-       if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
-               *b = '/';
-               b++;
+       if ((RemoveTrailingSlash) &&
+           (b > Dir->buf) && 
+           (*(b - 1) == '/')){
+               b--;
        }
        *b = '\0';
        Dir->BufUsed = b - Dir->buf;