StrBuf_NextToken: if the source is empty, flush the target.
[citadel.git] / libcitadel / lib / stringbuf.c
index aa4abdeeaca779aefb36fa9cb249dc2eed7f32cf..dfba39d7e13e904ffc202186139a53a90bdfc40f 100644 (file)
@@ -215,6 +215,23 @@ void dbg_Init(StrBuf *Buf)
 
 #endif
 
+/**
+ * @ingroup StrBuf
+ * @brief swaps the contents of two StrBufs
+ * this is to be used to have cheap switched between a work-buffer and a target buffer 
+ * @param A First one
+ * @param B second one
+ */
+static inline void SwapBuffers(StrBuf *A, StrBuf *B)
+{
+       StrBuf C;
+
+       memcpy(&C, A, sizeof(*A));
+       memcpy(A, B, sizeof(*B));
+       memcpy(B, &C, sizeof(C));
+
+}
+
 /** 
  * @ingroup StrBuf_Cast
  * @brief Cast operator to Plain String 
@@ -286,14 +303,16 @@ static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
 
 /**
  * @ingroup StrBuf_DeConstructors
- * @brief shrink an _EMPTY_ buffer if its Buffer superseeds threshhold to NewSize. Buffercontent is thoroughly ignored and flushed.
+ * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
  * @param Buf Buffer to shrink (has to be empty)
  * @param ThreshHold if the buffer is bigger then this, its readjusted
  * @param NewSize if we Shrink it, how big are we going to be afterwards?
  */
 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
 {
-       if ((Buf != NULL) && (Buf->BufUsed > ThreshHold)) {
+       if ((Buf != NULL) && 
+           (Buf->BufUsed == 0) &&
+           (Buf->BufSize < ThreshHold)) {
                free(Buf->buf);
                Buf->buf = (char*) malloc(NewSize);
                Buf->BufUsed = 0;
@@ -370,6 +389,75 @@ StrBuf* NewStrBufDup(const StrBuf *CopyMe)
        return NewBuf;
 }
 
+/** 
+ * @ingroup StrBuf_DeConstructors
+ * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
+ * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
+ * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
+ * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe 
+ * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
+ * @returns the new stringbuffer
+ */
+void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
+{
+       StrBuf *NewBuf;
+       
+       if (CreateRelpaceMe == NULL)
+               return;
+
+       if (NoMe != NULL)
+       {
+               if (*CreateRelpaceMe != NULL)
+                       StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
+               else 
+                       *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
+               return;
+       }
+
+       if (CopyFlushMe == NULL)
+       {
+               if (*CreateRelpaceMe != NULL)
+                       FlushStrBuf(*CreateRelpaceMe);
+               else 
+                       *CreateRelpaceMe = NewStrBuf();
+               return;
+       }
+
+       /* 
+        * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
+        * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
+        * be a big IO-Buffer...
+        */
+       if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
+       {
+               if (*CreateRelpaceMe == NULL)
+               {
+                       *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
+                       dbg_Init(NewBuf);
+               }
+               else 
+               {
+                       NewBuf = *CreateRelpaceMe;
+                       FlushStrBuf(NewBuf);
+               }
+               StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
+       }
+       else
+       {
+               if (*CreateRelpaceMe == NULL)
+               {
+                       *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
+                       dbg_Init(NewBuf);
+               }
+               else 
+                       NewBuf = *CreateRelpaceMe;
+               SwapBuffers (NewBuf, CopyFlushMe);
+       }
+       if (!KeepOriginal)
+               FlushStrBuf(CopyFlushMe);
+       return;
+}
+
 /**
  * @ingroup StrBuf_DeConstructors
  * @brief create a new Buffer using an existing c-string
@@ -395,6 +483,11 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
                Siz *= 2;
 
        NewBuf->buf = (char*) malloc(Siz);
+       if (NewBuf->buf == NULL)
+       {
+               free(NewBuf);
+               return NULL;
+       }
        NewBuf->BufSize = Siz;
        if (ptr != NULL) {
                memcpy(NewBuf->buf, ptr, CopySize);
@@ -407,9 +500,9 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
        }
        NewBuf->ConstBuf = 0;
 
-       dbg_Init(NewBuf)
+       dbg_Init(NewBuf);
 
-               return NewBuf;
+       return NewBuf;
 }
 
 /**
@@ -422,9 +515,18 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
  */
 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
 {
-       size_t Siz = Buf->BufSize;
+       size_t Siz;
        size_t CopySize;
 
+       if (Buf == NULL)
+               return -1;
+       if (ptr == NULL) {
+               FlushStrBuf(Buf);
+               return -1;
+       }
+
+       Siz = Buf->BufSize;
+
        if (nChars < 0)
                CopySize = strlen(ptr);
        else
@@ -974,6 +1076,26 @@ void StrBufTrim(StrBuf *Buf)
        }
        if (delta > 0) StrBufCutLeft(Buf, delta);
 }
+/**
+ * @ingroup StrBuf
+ * @brief changes all spaces in the string  (tab, linefeed...) to Blank (0x20)
+ * @param Buf the string to modify
+ */
+void StrBufSpaceToBlank(StrBuf *Buf)
+{
+       char *pche, *pch;
+
+       if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
+
+       pch = Buf->buf;
+       pche = pch + Buf->BufUsed;
+       while (pch < pche) 
+       {
+               if (isspace(*pch))
+                       *pch = ' ';
+               pch ++;
+       }
+}
 
 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
 {
@@ -997,7 +1119,7 @@ void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
                pBuff = Buf->buf;
        pRight = strchr(pBuff, rightboundary);
        if (pRight != NULL)
-               StrBufCutAt(Buf, 0, pRight - 1);
+               StrBufCutAt(Buf, 0, pRight);
        if (pLeft != NULL)
                StrBufCutLeft(Buf, pLeft - Buf->buf);
 }
@@ -1070,8 +1192,8 @@ int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
                if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
                        return -1;
 
-       memmove(Buf->buf + where + HowLong
-               Buf->buf + where + ReplLen,
+       memmove(Buf->buf + where + ReplLen
+               Buf->buf + where + HowLong,
                Buf->BufUsed - where - HowLong);
                                                
        memcpy(Buf->buf + where, 
@@ -1380,6 +1502,8 @@ int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pSt
            (Source->BufUsed == 0)      ) 
        {
                *pStart = StrBufNOTNULL;
+               if (dest != NULL)
+                       FlushStrBuf(dest);
                return -1;
        }
         
@@ -2248,6 +2372,7 @@ int StrBufDecodeBase64(StrBuf *Buf)
        if (Buf == NULL) return -1;
 
        xferbuf = (char*) malloc(Buf->BufSize);
+       *xferbuf = '\0';
        siz = CtdlDecodeBase64(xferbuf,
                               Buf->buf,
                               Buf->BufUsed);
@@ -2425,6 +2550,184 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
        return (*target)->BufUsed;;
 }
 
+
+
+static void AddRecipient(StrBuf *Target, 
+                        StrBuf *UserName, 
+                        StrBuf *EmailAddress, 
+                        StrBuf *EncBuf)
+{
+       int QuoteMe = 0;
+
+       if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
+       if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
+
+       if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
+       StrBufRFC2047encode(&EncBuf, UserName);
+       StrBufAppendBuf(Target, EncBuf, 0);
+       if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
+       else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
+
+       if (StrLength(EmailAddress) > 0){
+               StrBufAppendBufPlain(Target, HKEY("<"), 0);
+               StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
+               StrBufAppendBufPlain(Target, HKEY(">"), 0);
+       }
+}
+
+
+/**
+ * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
+ * \param Recp Source list of email recipients
+ * \param UserName Temporary buffer for internal use; Please provide valid buffer.
+ * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
+ * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
+ * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
+ */
+StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
+                                          StrBuf *UserName, 
+                                          StrBuf *EmailAddress,
+                                          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);
+
+       if (!CheckEncode(pch, -1, pche))
+               return NewStrBufDup(Recp);
+
+       Target = NewStrBufPlain(NULL, StrLength(Recp));
+
+       while ((pch != NULL) && (pch < pche))
+       {
+               int ColonOk = 0;
+
+               while (isspace(*pch)) pch++;
+               UserStart = UserEnd = EmailStart = EmailEnd = NULL;
+               
+               if ((*pch == '"') || (*pch == '\'')) {
+                       UserStart = pch + 1;
+                       
+                       UserEnd = strchr(UserStart, *pch);
+                       if (UserEnd == NULL) 
+                               break; ///TODO: Userfeedback??
+                       EmailStart = UserEnd + 1;
+                       while (isspace(*EmailStart))
+                               EmailStart++;
+                       if (UserEnd == UserStart) {
+                               UserStart = UserEnd = NULL;
+                       }
+                       
+                       if (*EmailStart == '<') {
+                               EmailStart++;
+                               EmailEnd = strchr(EmailStart, '>');
+                               if (EmailEnd == NULL)
+                                       EmailEnd = strchr(EmailStart, ',');
+                               
+                       }
+                       else {
+                               EmailEnd = strchr(EmailStart, ',');
+                       }
+                       if (EmailEnd == NULL)
+                               EmailEnd = pche;
+                       pch = EmailEnd + 1;
+                       ColonOk = 1;
+               }
+               else {
+                       int gt = 0;
+                       UserStart = pch;
+                       EmailEnd = strchr(UserStart, ',');
+                       if (EmailEnd == NULL) {
+                               EmailEnd = strchr(pch, '>');
+                               pch = NULL;
+                               if (EmailEnd != NULL) {
+                                       gt = 1;
+                               }
+                               else {
+                                       EmailEnd = pche;
+                               }
+                       }
+                       else {
+
+                               pch = EmailEnd + 1;
+                               while ((EmailEnd > UserStart) && !gt &&
+                                      ((*EmailEnd == ',') ||
+                                       (*EmailEnd == '>') ||
+                                       (isspace(*EmailEnd))))
+                               {
+                                       if (*EmailEnd == '>')
+                                               gt = 1;
+                                       else 
+                                               EmailEnd--;
+                               }
+                               if (EmailEnd == UserStart)
+                                       break;
+                       }
+                       if (gt) {
+                               EmailStart = strchr(UserStart, '<');
+                               if ((EmailStart == NULL) || (EmailStart > EmailEnd))
+                                       break;
+                               UserEnd = EmailStart;
+
+                               while ((UserEnd > UserStart) && 
+                                      isspace (*(UserEnd - 1)))
+                                       UserEnd --;
+                               EmailStart ++;
+                               if (UserStart >= UserEnd)
+                                       UserStart = UserEnd = NULL;
+                               At = strchr(EmailStart, '@');
+                       }
+                       else { /* this is a local recipient... no domain, just a realname */
+                               EmailStart = UserStart;
+                               At = strchr(EmailStart, '@');
+                               if (At == NULL) {
+                                       UserEnd = EmailEnd;
+                                       EmailEnd = NULL;
+                               }
+                               else {
+                                       EmailStart = UserStart;
+                                       UserStart = NULL;
+                               }
+                       }
+               }
+
+               if ((UserStart != NULL) && (UserEnd != NULL))
+                       StrBufPlain(UserName, UserStart, UserEnd - UserStart);
+               else if ((UserStart != NULL) && (UserEnd == NULL))
+                       StrBufPlain(UserName, UserStart, UserEnd - UserStart);
+               else
+                       FlushStrBuf(UserName);
+
+               if ((EmailStart != NULL) && (EmailEnd != NULL))
+                       StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
+               else if ((EmailStart != NULL) && (EmailEnd == NULL))
+                       StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
+               else 
+                       FlushStrBuf(EmailAddress);
+
+               AddRecipient(Target, UserName, EmailAddress, EncBuf);
+
+               if (pch == NULL)
+                       break;
+               
+               if ((pch != NULL) && (*pch == ','))
+                       pch ++;
+               if (pch != NULL) while (isspace(*pch))
+                       pch ++;
+       }
+       return Target;
+}
+
+
 /**
  * @ingroup StrBuf
  * @brief replaces all occurances of 'search' by 'replace'
@@ -2538,22 +2841,6 @@ static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
        return end;
 }
 
-/**
- * @ingroup StrBuf
- * @brief swaps the contents of two StrBufs
- * this is to be used to have cheap switched between a work-buffer and a target buffer 
- * @param A First one
- * @param B second one
- */
-static inline void SwapBuffers(StrBuf *A, StrBuf *B)
-{
-       StrBuf C;
-
-       memcpy(&C, A, sizeof(*A));
-       memcpy(A, B, sizeof(*B));
-       memcpy(B, &C, sizeof(C));
-
-}
 
 
 /**
@@ -2660,6 +2947,8 @@ inline static void DecodeSegment(StrBuf *Target,
        
        *encoding = toupper(*encoding);
        if (*encoding == 'B') { /**< base64 */
+               if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
+                       IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
                ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
                                                        ConvertBuf->buf, 
                                                        ConvertBuf->BufUsed);
@@ -2675,6 +2964,9 @@ inline static void DecodeSegment(StrBuf *Target,
                        pos++;
                }
                
+               if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
+                       IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
+
                ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
                        ConvertBuf2->buf, 
                        ConvertBuf->buf,
@@ -2700,7 +2992,7 @@ inline static void DecodeSegment(StrBuf *Target,
 
 /**
  * @ingroup StrBuf_DeEnCoder
- * @brief Handle subjects with RFC2047 encoding such as:
+ * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
  * @param Target where to put the decoded string to 
  * @param DecodeMe buffer with encoded string
@@ -2709,9 +3001,42 @@ inline static void DecodeSegment(StrBuf *Target,
  *        put it here for later use where no string might be known.
  */
 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
+{
+       StrBuf *ConvertBuf;
+       StrBuf *ConvertBuf2;
+       ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
+       ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
+       
+       StrBuf_RFC822_2_Utf8(Target, 
+                            DecodeMe, 
+                            DefaultCharset, 
+                            FoundCharset, 
+                            ConvertBuf, 
+                            ConvertBuf2);
+       FreeStrBuf(&ConvertBuf);
+       FreeStrBuf(&ConvertBuf2);
+}
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief Handle subjects with RFC2047 encoding such as:
+ * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
+ * @param Target where to put the decoded string to 
+ * @param DecodeMe buffer with encoded string
+ * @param DefaultCharset if we don't find one, which should we use?
+ * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
+ *        put it here for later use where no string might be known.
+ * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
+ * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
+ */
+void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
+                         const StrBuf *DecodeMe, 
+                         const StrBuf* DefaultCharset, 
+                         StrBuf *FoundCharset, 
+                         StrBuf *ConvertBuf, 
+                         StrBuf *ConvertBuf2)
 {
        StrBuf *DecodedInvalidBuf = NULL;
-       StrBuf *ConvertBuf, *ConvertBuf2;
        const StrBuf *DecodeMee = DecodeMe;
        const char *start, *end, *next, *nextend, *ptr = NULL;
 #ifdef HAVE_ICONV
@@ -2737,7 +3062,6 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf*
                }
        }
 
-       ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
        if ((illegal_non_rfc2047_encoding) &&
            (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
            (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
@@ -2762,12 +3086,10 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf*
                end = FindNextEnd (DecodeMee, start);
        else {
                StrBufAppendBuf(Target, DecodeMee, 0);
-               FreeStrBuf(&ConvertBuf);
                FreeStrBuf(&DecodedInvalidBuf);
                return;
        }
 
-       ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee));
 
        if (start != DecodeMee->buf) {
                long nFront;
@@ -2846,8 +3168,6 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf*
                if (ptr < nextend)
                        StrBufAppendBufPlain(Target, end, nextend - end, 0);
        }
-       FreeStrBuf(&ConvertBuf);
-       FreeStrBuf(&ConvertBuf2);
        FreeStrBuf(&DecodedInvalidBuf);
 }
 
@@ -3074,7 +3394,191 @@ int CompressBuffer(StrBuf *Buf)
        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;
+
+       /*
+        * 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;
+
+               if (already_read != 0) {
+                       long 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;
+                       }
+                       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);
+                               FB->ReadWritePointer = FB->Buf->buf + unread;
+                               bufremain = FB->Buf->BufSize - unread;
+                       }
+               }
+               
+       }
+       else {
+               FB->ReadWritePointer = FB->Buf->buf;
+               bufremain = FB->Buf->BufSize;
+       }
+       n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain - 1);
+
+       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->ReadWritePointer != NULL)
+       {
+               WriteRemain = FB->Buf->BufUsed - 
+                       (FB->ReadWritePointer - 
+                        FB->Buf->buf);
+       }
+       else {
+               FB->ReadWritePointer = FB->Buf->buf;
+               WriteRemain = FB->Buf->BufUsed;
+       }
+
+       n = write(fd, FB->ReadWritePointer, WriteRemain);
+       if (n > 0) {
+               FB->ReadWritePointer += n;
+
+               if (FB->ReadWritePointer == 
+                   FB->Buf->buf + FB->Buf->BufUsed)
+               {
+                       FlushStrBuf(FB->Buf);
+                       FB->ReadWritePointer = NULL;
+                       return 0;
+               }
+       // check whether we've got something to write
+       // get the maximum chunk plus the pointer we can send
+       // write whats there
+       // if not all was sent, remember the send pointer for the next time
+               return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
+       }
+       return n;
+}
+
+/**
+ * @ingroup StrBuf_IO
+ * @brief extract a "next line" from Buf; Ptr to persist across several iterations
+ * @param LineBuf your line will be copied here.
+ * @param FB BLOB with lines of text...
+ * @param Ptr moved arround to keep the next-line across several iterations
+ *        has to be &NULL on start; will be &NotNULL on end of buffer
+ * @returns size of copied buffer
+ */
+eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
+{
+       const char *aptr, *ptr, *eptr;
+       char *optr, *xptr;
+
+       if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
+               FB->ReadWritePointer = StrBufNOTNULL;
+               return eReadFail;
+       }
+
+       FlushStrBuf(LineBuf);
+       if (FB->ReadWritePointer == NULL)
+               ptr = aptr = FB->Buf->buf;
+       else
+               ptr = aptr = FB->ReadWritePointer;
+
+       optr = LineBuf->buf;
+       eptr = FB->Buf->buf + FB->Buf->BufUsed;
+       xptr = LineBuf->buf + LineBuf->BufSize - 1;
+
+       while ((ptr <= eptr) && 
+              (*ptr != '\n') &&
+              (*ptr != '\r') )
+       {
+               *optr = *ptr;
+               optr++; ptr++;
+               if (optr == xptr) {
+                       LineBuf->BufUsed = optr - LineBuf->buf;
+                       IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
+                       optr = LineBuf->buf + LineBuf->BufUsed;
+                       xptr = LineBuf->buf + LineBuf->BufSize - 1;
+               }
+       }
+
+       if (ptr >= eptr) {
+               if (optr > LineBuf->buf)
+                       optr --;
+               if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
+                       LineBuf->BufUsed = optr - LineBuf->buf;
+                       *optr = '\0';       
+                       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;
+}
 
 /*******************************************************************************
  *           File I/O; Prefer buffered read since its faster!                  *
@@ -3384,7 +3888,7 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
 
                        if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
                        {
-                               long apos;
+                               long apos = 0;
 
                                if (pLF != NULL) apos = pLF - IOBuf->buf;
                                IncreaseBuf(IOBuf, 1, -1);      
@@ -3495,7 +3999,7 @@ int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **E
 }
 
 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
-const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
+const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
 /**
  * @ingroup StrBuf_BufferedIO
  * @brief Input binary data from socket
@@ -3521,8 +4025,6 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
 {
        const char *pche;
        const char *pos;
-       int nSelects = 0;
-       int SelRes;
        int fdflags;
        int len = 0;
        int rlen, slen;
@@ -3533,7 +4035,8 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
        fd_set rfds;
        const char *pch;
        struct timeval tv;
-       int nSuccessLess;
+       int nSuccessLess = 0;
+       int MaxTries;
 
        if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
        {
@@ -3593,11 +4096,15 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
 
        fdflags = fcntl(*fd, F_GETFL);
        IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
+       if (IsNonBlock)
+               MaxTries =   1000;
+       else
+               MaxTries = 100000;
 
-       SelRes = 1;
        nBytes -= nRead;
        nRead = 0;
-       while ((nRead < nBytes) &&
+       while ((nSuccessLess < MaxTries) && 
+              (nRead < nBytes) &&
               (*fd != -1)) {
                if (IsNonBlock)
                {
@@ -3619,10 +4126,9 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
                                continue;
                        }
                }
-               nSuccessLess = 0;
                rlen = read(*fd, 
                            ptr,
-                           nBytes - nRead);
+                           IOBuf->BufSize - (ptr - IOBuf->buf));
                if (rlen == -1) {
                        close(*fd);
                        *fd = -1;
@@ -3630,7 +4136,6 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
                        return rlen;
                }
                else if (rlen == 0){
-                       nSuccessLess ++;
                        if ((check == NNN_TERM) && 
                            (nRead > 5) &&
                            (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) 
@@ -3639,18 +4144,27 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
                                StrBufCutRight(Blob, 5);
                                return Blob->BufUsed;
                        }
-                       if (nSelects > 10) {
+                       else if (!IsNonBlock) 
+                               nSuccessLess ++;
+                       else if (nSuccessLess > MaxTries) {
                                FlushStrBuf(IOBuf);
                                *Error = ErrRBB_too_many_selects;
                                return -1;
                        }
                }
                else if (rlen > 0) {
+                       nSuccessLess = 0;
                        nRead += rlen;
                        ptr += rlen;
                        IOBuf->BufUsed += rlen;
                }
        }
+       if (nSuccessLess >= MaxTries) {
+               FlushStrBuf(IOBuf);
+               *Error = ErrRBB_too_many_selects;
+               return -1;
+       }
+
        if (nRead > nBytes) {
                *Pos = IOBuf->buf + nBytes;
        }