X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=libcitadel%2Flib%2Fstringbuf.c;h=436864690bd62d0af5eb9f638053de41ac9fe495;hb=fc6cb49257a19bff1336d735a075f78fae2f4728;hp=afaa2e6e539edc0cc9cbb5abcd75b4ca76086ffb;hpb=199d7d38e7b09fc7bd49311ae2afbff8015b1313;p=citadel.git diff --git a/libcitadel/lib/stringbuf.c b/libcitadel/lib/stringbuf.c index afaa2e6e5..436864690 100644 --- a/libcitadel/lib/stringbuf.c +++ b/libcitadel/lib/stringbuf.c @@ -1,3 +1,22 @@ +/* + * 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 + * 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 #include @@ -10,8 +29,12 @@ #include #define SHOW_ME_VAPPEND_PRINTF #include + #include "libcitadel.h" +#include "b64/cencode.h" +#include "b64/cdecode.h" + #ifdef HAVE_ICONV #include #endif @@ -20,12 +43,17 @@ #include #endif +#ifdef UNDEF_MEMCPY +#undef memcpy +#endif + #ifdef HAVE_ZLIB #include int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, const Bytef * source, uLong sourceLen, int level); #endif int BaseStrBufSize = 64; +int EnableSplice = 0; const char *StrBufNOTNULL = ((char*) NULL) - 1; @@ -215,6 +243,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 @@ -259,9 +304,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; @@ -286,14 +334,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; @@ -315,7 +365,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); @@ -334,7 +389,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; @@ -359,7 +422,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; @@ -370,6 +442,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 @@ -386,15 +527,29 @@ 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 CopySize = nChars; - while (Siz <= CopySize) + while ((Siz <= CopySize) && (Siz != 0)) Siz *= 2; + if (Siz == 0) + { + free(NewBuf); + return NULL; + } + 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 +562,9 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars) } NewBuf->ConstBuf = 0; - dbg_Init(NewBuf) + dbg_Init(NewBuf); - return NewBuf; + return NewBuf; } /** @@ -422,17 +577,31 @@ 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 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); @@ -454,6 +623,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; @@ -472,7 +643,7 @@ StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant) */ int FlushStrBuf(StrBuf *buf) { - if (buf == NULL) + if ((buf == NULL) || (buf->buf == NULL)) return -1; if (buf->ConstBuf) return -1; @@ -618,8 +789,9 @@ int StrBufIsNumber(const StrBuf *Buf) { return 0; return 0; } + /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief modifies a Single char of the Buf * You can point to it via char* or a zero-based integer * @param Buf The buffer to manipulate @@ -640,7 +812,34 @@ long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue) } /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler + * @brief modifies a range of chars of the Buf + * You can point to it via char* or a zero-based integer + * @param Buf The buffer to manipulate + * @param ptr char* to zero; use NULL if unused + * @param nThChar zero based pointer into the string; use -1 if unused + * @param nChars how many chars are to be flushed? + * @param PookValue The Character to place into that area + */ +long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue) +{ + if (Buf == NULL) + return -1; + if (ptr != NULL) + nThChar = ptr - Buf->buf; + if ((nThChar < 0) || (nThChar > Buf->BufUsed)) + return -1; + if (nThChar + nChars > Buf->BufUsed) + nChars = Buf->BufUsed - nThChar; + + memset(Buf->buf + nThChar, PookValue, nChars); + /* just to be shure... */ + Buf->buf[Buf->BufUsed] = 0; + return nChars; +} + +/** + * @ingroup StrBuf_Filler * @brief Append a StringBuffer to the buffer * @param Buf Buffer to modify * @param AppendBuf Buffer to copy at the end of our buffer @@ -648,7 +847,8 @@ long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue) */ void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset) { - if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL)) + if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) || + (Buf == NULL) || (Buf->buf == NULL)) return; if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1) @@ -665,7 +865,7 @@ void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset) /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief Append a C-String to the buffer * @param Buf Buffer to modify * @param AppendBuf Buffer to copy at the end of our buffer @@ -697,7 +897,7 @@ void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, u } /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief sprintf like function appending the formated string to the buffer * vsnprintf version to wrap into own calls * @param Buf Buffer to extend by format and Params @@ -728,7 +928,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 { @@ -740,7 +941,7 @@ void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap) } /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief sprintf like function appending the formated string to the buffer * @param Buf Buffer to extend by format and Params * @param format printf alike format to add @@ -769,7 +970,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 { @@ -781,7 +983,7 @@ void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...) } /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief sprintf like function putting the formated string into the buffer * @param Buf Buffer to extend by format and Parameters * @param format printf alike format to add @@ -800,7 +1002,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; } @@ -809,7 +1012,7 @@ void StrBufPrintf(StrBuf *Buf, const char *format, ...) } /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief Callback for cURL to append the webserver reply to a buffer * @param ptr pre-defined by the cURL API; see man 3 curl for mre info * @param size pre-defined by the cURL API; see man 3 curl for mre info @@ -844,21 +1047,24 @@ int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t n size_t NCharsRemain; if (Offset > Source->BufUsed) { - FlushStrBuf(dest); + if (dest != NULL) + FlushStrBuf(dest); return 0; } 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'; @@ -891,7 +1097,9 @@ void StrBufCutLeft(StrBuf *Buf, int nChars) */ void StrBufCutRight(StrBuf *Buf, int nChars) { - if ((Buf == NULL) || (Buf->BufUsed == 0)) return; + if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL)) + return; + if (nChars >= Buf->BufUsed) { FlushStrBuf(Buf); return; @@ -931,20 +1139,64 @@ void StrBufTrim(StrBuf *Buf) int delta = 0; if ((Buf == NULL) || (Buf->BufUsed == 0)) return; + while ((Buf->BufUsed > 0) && + isspace(Buf->buf[Buf->BufUsed - 1])) + { + Buf->BufUsed --; + } + Buf->buf[Buf->BufUsed] = '\0'; + + if (Buf->BufUsed == 0) return; + while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){ delta ++; } 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->BufUsed == 0) return; - while (isspace(Buf->buf[Buf->BufUsed - 1])){ - Buf->BufUsed --; + 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) +{ + const char *pLeft; + const char *pRight; + + if ((Buf == NULL) || (Buf->buf == NULL)) { + return; + } + + pRight = strchr(Buf->buf, rightboundary); + if (pRight != NULL) { + StrBufCutAt(Buf, 0, pRight); + } + + pLeft = strrchr(ChrPtr(Buf), leftboundary); + if (pLeft != NULL) { + StrBufCutLeft(Buf, pLeft - Buf->buf + 1); } - Buf->buf[Buf->BufUsed] = '\0'; } + /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief uppercase the contents of a buffer * @param Buf the buffer to translate */ @@ -964,7 +1216,7 @@ void StrBufUpCase(StrBuf *Buf) /** - * @ingroup StrBuf + * @ingroup StrBuf_Filler * @brief lowercase the contents of a buffer * @param Buf the buffer to translate */ @@ -987,6 +1239,41 @@ void StrBufLowerCase(StrBuf *Buf) * a tokenizer that kills, maims, and destroys * *******************************************************************************/ +/** + * @ingroup StrBuf_Tokenizer + * @brief Replace a token at a given place with a given length by another token with given length + * @param Buf String where to work on + * @param where where inside of the Buf is the search-token + * @param HowLong How long is the token to be replaced + * @param Repl Token to insert at 'where' + * @param ReplLen Length of repl + * @returns -1 if fail else length of resulting Buf + */ +int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, + const char *Repl, long ReplLen) +{ + + if ((Buf == NULL) || + (where > Buf->BufUsed) || + (where + HowLong > Buf->BufUsed)) + return -1; + + if (where + ReplLen - HowLong > Buf->BufSize) + if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0) + return -1; + + memmove(Buf->buf + where + ReplLen, + Buf->buf + where + HowLong, + Buf->BufUsed - where - HowLong); + + memcpy(Buf->buf + where, + Repl, ReplLen); + + Buf->BufUsed += ReplLen - HowLong; + + return Buf->BufUsed; +} + /** * @ingroup StrBuf_Tokenizer * @brief Counts the numbmer of tokens in a buffer @@ -1084,6 +1371,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 @@ -1116,7 +1420,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 ((sBufUsed == 0) ) { *pStart = StrBufNOTNULL; + if (dest != NULL) + FlushStrBuf(dest); return -1; } @@ -1401,7 +1707,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= '@' && *pch <= 'Z') || /* @ A-Z */ (*pch >= '0' && *pch <= ':') || /* 0-9 : */ (*pch == '!') || (*pch == '_') || - (*pch == ',') || (*pch == '.') || - (*pch == ',')) + (*pch == ',') || (*pch == '.')) { *(pt++) = *(pch++); OutBuf->BufUsed++; @@ -1565,12 +1870,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; @@ -1593,64 +1898,302 @@ void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) return; pt = OutBuf->buf + OutBuf->BufUsed; - pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */ + 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 - 3; /**< we max append 3 chars at once plus the \0 */ + pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */ pt = OutBuf->buf + OutBuf->BufUsed; } - *pt = HexList[(unsigned char)*pch][0]; - pt ++; - *pt = HexList[(unsigned char)*pch][1]; - pt ++; pch ++; OutBuf->BufUsed += 2; + 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, escaping characters which have meaning in HTML. - * - * @param Target target buffer - * @param Source source buffer; set to NULL if you just have a C-String - * @param PlainIn Plain-C string to append; set to NULL if unused - * @param nbsp If nonzero, spaces are converted to non-breaking spaces. - * @param nolinebreaks if set to 1, linebreaks are removed from the string. - * if set to 2, linebreaks are replaced by <br/> + * @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? */ -long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) +void StrBufXMLEscAppend(StrBuf *OutBuf, + const StrBuf *In, + const char *PlainIn, + long PlainInLen, + int OverrideLowChars) { - const char *aptr, *eiptr; - char *bptr, *eptr; - long len; - - if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) - return -1; + const char *pch, *pche; + char *pt, *pte; + int IsUtf8Sequence; + int len; + if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) ) + return; if (PlainIn != NULL) { - aptr = PlainIn; - len = strlen(PlainIn); - eiptr = aptr + len; + if (PlainInLen < 0) + len = strlen((const char*)PlainIn); + else + len = PlainInLen; + pch = PlainIn; + pche = pch + len; } else { - aptr = Source->buf; - eiptr = aptr + Source->BufUsed; - len = Source->BufUsed; + pch = (const char*)In->buf; + pche = pch + In->BufUsed; + len = In->BufUsed; } - if (len == 0) - return -1; + if (len == 0) + return; - bptr = Target->buf + Target->BufUsed; - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ + pt = OutBuf->buf + OutBuf->BufUsed; + /**< we max append 6 chars at once plus the \0 */ + pte = OutBuf->buf + OutBuf->BufSize - 6; - while (aptr < eiptr){ - if(bptr >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ + 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("<")); + pt += 4; + pch ++; + } + else if (*pch == '>') { + memcpy(pt, HKEY(">")); + pt += 4; + pch ++; + } + else if (*pch == '&') { + memcpy(pt, HKEY("&")); + 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 + * @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; + + pt = OutBuf->buf + OutBuf->BufUsed; + pte = OutBuf->buf + OutBuf->BufSize - 3; /**< 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 - 3; /**< we max append 3 chars at once plus the \0 */ + pt = OutBuf->buf + OutBuf->BufUsed; + } + + *pt = HexList[*pch][0]; + pt ++; + *pt = HexList[*pch][1]; + pt ++; pch ++; OutBuf->BufUsed += 2; + } + *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 + * @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. + * + * @param Target target buffer + * @param Source source buffer; set to NULL if you just have a C-String + * @param PlainIn Plain-C string to append; set to NULL if unused + * @param nbsp If nonzero, spaces are converted to non-breaking spaces. + * @param nolinebreaks if set to 1, linebreaks are removed from the string. + * if set to 2, linebreaks are replaced by <br/> + */ +long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) +{ + const char *aptr, *eiptr; + char *bptr, *eptr; + long len; + + if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) + return -1; + + if (PlainIn != NULL) { + aptr = PlainIn; + len = strlen(PlainIn); + eiptr = aptr + len; + } + else { + aptr = Source->buf; + eiptr = aptr + Source->BufUsed; + len = Source->BufUsed; + } + + if (len == 0) + return -1; + + bptr = Target->buf + Target->BufUsed; + eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ + + while (aptr < eiptr){ + if(bptr >= eptr) { + IncreaseBuf(Target, 1, -1); + eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ bptr = Target->buf + Target->BufUsed; } if (*aptr == '<') { @@ -1875,6 +2418,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; @@ -1894,31 +2438,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; + break; + case '\t': *bptr = '\\'; bptr ++; + *bptr = 't'; + bptr ++; Target->BufUsed += 2; - } - else{ - *bptr = *aptr; - bptr++; - Target->BufUsed ++; + break; + default: + IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr); + while (IsUtf8Sequence > 0){ + *bptr = *aptr; + Target->BufUsed ++; + if (--IsUtf8Sequence) + aptr++; + bptr++; + } } aptr ++; } @@ -1972,83 +2568,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, "<", 4); + switch (*aptr) { + case '<': + memcpy(bptr, HKEY("<")); bptr += 4; Target->BufUsed += 4; - } - else if (*aptr == '>') { - memcpy(bptr, ">", 4); + break; + case '>': + memcpy(bptr, HKEY(">")); bptr += 4; Target->BufUsed += 4; - } - else if (*aptr == '&') { - memcpy(bptr, "&", 5); + break; + case '&': + memcpy(bptr, HKEY("&")); 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, " ", 6); - bptr += 6; - Target->BufUsed += 6; - } - else if ((*aptr == '\n') && (nolinebreaks == 1)) { - *bptr='\0'; /* nothing */ - } - else if ((*aptr == '\n') && (nolinebreaks == 2)) { - memcpy(bptr, "<br/>", 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("<br/>")); + 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(" ")); + 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 ++; } @@ -2058,25 +2698,42 @@ long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *Plai return Target->BufUsed; } + /** * @ingroup StrBuf_DeEnCoder - * @brief unhide special chars hidden to the HTML escaper - * @param target buffer to put the unescaped string in - * @param source buffer to unescape + * @brief replace all non-Ascii characters by another + * @param Buf buffer to inspect + * @param repl charater to stamp over non ascii chars */ -void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) +void StrBufAsciify(StrBuf *Buf, const char repl) { - int a, b, len; - char hex[3]; + long offset; - if (target != NULL) - FlushStrBuf(target); + 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 + * @param target buffer to put the unescaped string in + * @param source buffer to unescape + */ +void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) +{ + int a, b, len; + char hex[3]; - if (source == NULL ||target == NULL) + if ((source == NULL) || (target == NULL) || (target->buf == NULL)) { return; } + if (target != NULL) + FlushStrBuf(target); + len = source->BufUsed; for (a = 0; a < len; ++a) { if (target->BufUsed >= target->BufSize) @@ -2113,7 +2770,7 @@ void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) if (target != NULL) FlushStrBuf(target); - if (source == NULL ||target == NULL) + if ((source == NULL) || (target == NULL) || (target->buf == NULL)) { return; } @@ -2151,18 +2808,96 @@ 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, Buf->BufUsed); 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; +} + +void *StrBufNewStreamContext(eStreamType type) +{ + base64_decodestate *state;; + + switch (type) + { + case eBase64Decode: + state = (base64_decodestate*) malloc(sizeof(base64_decodestate)); + base64_init_decodestate(state); + return state; + break; + } + return NULL; +} + +void StrBufDestroyStreamContext(eStreamType type, void **Stream) +{ + switch (type) + { + case eBase64Decode: + free(*Stream); + *Stream = NULL; + break; + } +} +void StrBufStreamDecodeTo(StrBuf *Target, const StrBuf *In, const char* pIn, long pInLen, void *Stream) +{ + base64_decodestate *state = Stream; + long ExpectLen; + + if (In != NULL) + { + pIn = In->buf; + pInLen = In->BufUsed; + } + if ((pIn == NULL) || (Stream == 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'; +} + /** * @ingroup StrBuf_DeEnCoder * @brief decode a buffer from base 64 encoding; destroys original @@ -2300,7 +3035,10 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source) FlushStrBuf(*target); StrBufAppendBuf(*target, source, 0); } - return (*target)->BufUsed; + if (*target != 0) + return (*target)->BufUsed; + else + return 0; } if (*target == NULL) *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2); @@ -2312,12 +3050,22 @@ 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 == '=') || + (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++; } } @@ -2331,6 +3079,266 @@ 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, + 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; + const char *pch, *pche; + const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At; + + if ((Recp == NULL) || (StrLength(Recp) == 0)) + return NULL; + + 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)) + { + while (isspace(*pch)) pch++; + 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; + } + 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; + } + 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' @@ -2349,6 +3357,36 @@ 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. + * @param buf Buffer to modify + */ +void StrBufToUnixLF(StrBuf *buf) +{ + char *pche, *pchS, *pchT; + if (buf == NULL) + return; + + pche = buf->buf + buf->BufUsed; + pchS = pchT = buf->buf; + while (pchS < pche) + { + if (*pchS == '\r') + { + pchS ++; + if (*pchS != '\n') { + *pchT = '\n'; + pchT++; + } + } + *pchT = *pchS; + pchT++; pchS++; + } + *pchT = '\0'; + buf->BufUsed = pchT - buf->buf; +} + /******************************************************************************* * Iconv Wrapper; RFC822 de/encoding * @@ -2390,9 +3428,9 @@ void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic) * @param bptr where to start searching * @returns found position, NULL if none. */ -static inline char *FindNextEnd (const StrBuf *Buf, char *bptr) +static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr) { - char * end; + const char * end; /* Find the next ?Q? */ if (Buf->BufUsed - (bptr - Buf->buf) < 6) return NULL; @@ -2403,7 +3441,8 @@ static inline char *FindNextEnd (const StrBuf *Buf, 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, "?="); @@ -2414,22 +3453,6 @@ static inline char *FindNextEnd (const StrBuf *Buf, 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)); - -} /** @@ -2451,6 +3474,9 @@ void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic) size_t obuflen; /**< Length of output buffer */ + if ((ConvertBuf == NULL) || (TmpBuf == NULL)) + return; + /* since we're converting to utf-8, one glyph may take up to 6 bytes */ if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize) IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6); @@ -2506,8 +3532,8 @@ TRYAGAIN: */ inline static void DecodeSegment(StrBuf *Target, const StrBuf *DecodeMe, - char *SegmentStart, - char *SegmentEnd, + const char *SegmentStart, + const char *SegmentEnd, StrBuf *ConvertBuf, StrBuf *ConvertBuf2, StrBuf *FoundCharset) @@ -2523,7 +3549,7 @@ inline static void DecodeSegment(StrBuf *Target, /* Now we handle foreign character sets properly encoded * in RFC2047 format. */ - StaticBuf.buf = SegmentStart; + StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */ StaticBuf.BufUsed = SegmentEnd - SegmentStart; StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf); extract_token(charset, SegmentStart, 1, '?', sizeof charset); @@ -2536,6 +3562,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); @@ -2551,6 +3579,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, @@ -2576,7 +3607,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 @@ -2585,19 +3616,55 @@ 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; - char *start, *end, *next, *nextend, *ptr = NULL; + const char *start, *end, *next, *nextend, *ptr = NULL; #ifdef HAVE_ICONV iconv_t ic = (iconv_t)(-1) ; #endif const char *eptr; int passes = 0; - int i, len, delta; + int i; int illegal_non_rfc2047_encoding = 0; + + if (DecodeMe == NULL) + return; /* Sometimes, badly formed messages contain strings which were simply * written out directly in some foreign character set instead of * using RFC2047 encoding. This is illegal but we will attempt to @@ -2605,7 +3672,6 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* * charset to UTF-8 if we see any nonprintable characters. */ - len = StrLength(DecodeMe); for (i=0; iBufUsed; ++i) { if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) { illegal_non_rfc2047_encoding = 1; @@ -2613,7 +3679,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")) ) @@ -2630,27 +3695,23 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* } /* pre evaluate the first pair */ - nextend = end = NULL; - len = StrLength(DecodeMee); + end = NULL; 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(&ConvertBuf); FreeStrBuf(&DecodedInvalidBuf); return; } - ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee)); if (start != DecodeMee->buf) { long nFront; nFront = start - DecodeMee->buf; StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0); - len -= nFront; } /* * Since spammers will go to all sorts of absurd lengths to get their @@ -2692,22 +3753,16 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* (*ptr == '\n') || (*ptr == '\t'))) ptr ++; - /* did we find a gab just filled with blanks? */ - if (ptr == next) + /* + * did we find a gab just filled with blanks? + * if not, copy its stuff over. + */ + if (ptr != next) { - long gap = next - start; - memmove (end + 2, - next, - len - (gap)); - len -= gap; - /* now terminate the gab at the end */ - delta = (next - end) - 2; ////TODO: const! - ((StrBuf*)DecodeMee)->BufUsed -= delta; - ((StrBuf*)DecodeMee)->buf[DecodeMee->BufUsed] = '\0'; - - /* move next to its new location. */ - next -= delta; - nextend -= delta; + StrBufAppendBufPlain(Target, + end + 2, + next - end - 2, + 0); } } /* our next-pair is our new first pair now. */ @@ -2728,8 +3783,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); } @@ -2741,19 +3794,24 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* * @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; } @@ -2766,7 +3824,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); } /** @@ -2791,173 +3849,823 @@ long StrBuf_Utf8StrLen(StrBuf *Buf) while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) ); n ++; } - else { - n++; - aptr++; + else { + n++; + aptr++; + } + } + return n; +} + +/** + * @ingroup StrBuf + * @brief cuts a string after maxlen glyphs + * @param Buf string to cut to maxlen glyphs + * @param maxlen how long may the string become? + * @returns current length of the string + */ +long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen) +{ + char *aptr, *eptr; + int n = 0, m = 0; + + aptr = Buf->buf; + eptr = Buf->buf + Buf->BufUsed; + while ((aptr < eptr) && (*aptr != '\0')) { + if (Ctdl_IsUtf8SequenceStart(*aptr)){ + m = Ctdl_GetUtf8SequenceLength(aptr, eptr); + while ((*aptr++ != '\0') && (m-- > 0)); + n ++; + } + else { + n++; + aptr++; + } + if (n > maxlen) { + *aptr = '\0'; + Buf->BufUsed = aptr - Buf->buf; + return Buf->BufUsed; + } + } + return Buf->BufUsed; + +} + + + + + +/******************************************************************************* + * wrapping ZLib * + *******************************************************************************/ + +#ifdef HAVE_ZLIB +#define DEF_MEM_LEVEL 8 /*< memlevel??? */ +#define OS_CODE 0x03 /*< unix */ + +/** + * @ingroup StrBuf_DeEnCoder + * @brief uses the same calling syntax as compress2(), but it + * creates a stream compatible with HTTP "Content-encoding: gzip" + * @param dest compressed buffer + * @param destLen length of the compresed data + * @param source source to encode + * @param sourceLen length of source to encode + * @param level compression level + */ +int ZEXPORT compress_gzip(Bytef * dest, + size_t * destLen, + const Bytef * source, + uLong sourceLen, + int level) +{ + const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */ + + /* write gzip header */ + snprintf((char *) dest, *destLen, + "%c%c%c%c%c%c%c%c%c%c", + gz_magic[0], gz_magic[1], Z_DEFLATED, + 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ , + OS_CODE); + + /* normal deflate */ + z_stream stream; + int err; + stream.next_in = (Bytef *) source; + stream.avail_in = (uInt) sourceLen; + stream.next_out = dest + 10L; // after header + stream.avail_out = (uInt) * destLen; + if ((uLong) stream.avail_out != *destLen) + return Z_BUF_ERROR; + + stream.zalloc = (alloc_func) 0; + stream.zfree = (free_func) 0; + stream.opaque = (voidpf) 0; + + err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out + 10L; + + /* write CRC and Length */ + uLong crc = crc32(0L, source, sourceLen); + int n; + for (n = 0; n < 4; ++n, ++*destLen) { + dest[*destLen] = (int) (crc & 0xff); + crc >>= 8; + } + uLong len = stream.total_in; + for (n = 0; n < 4; ++n, ++*destLen) { + dest[*destLen] = (int) (len & 0xff); + len >>= 8; + } + err = deflateEnd(&stream); + return err; +} +#endif + + +/** + * @ingroup StrBuf_DeEnCoder + * @brief compress the buffer with gzip + * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself! + * @param Buf buffer whose content is to be gzipped + */ +int CompressBuffer(StrBuf *Buf) +{ +#ifdef HAVE_ZLIB + char *compressed_data = NULL; + size_t compressed_len, bufsize; + int i = 0; + + bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100; + compressed_data = malloc(compressed_len); + + if (compressed_data == NULL) + return -1; + /* Flush some space after the used payload so valgrind shuts up... */ + while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) + Buf->buf[Buf->BufUsed + i++] = '\0'; + if (compress_gzip((Bytef *) compressed_data, + &compressed_len, + (Bytef *) Buf->buf, + (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) { + if (!Buf->ConstBuf) + free(Buf->buf); + Buf->buf = compressed_data; + Buf->BufUsed = compressed_len; + Buf->BufSize = bufsize; + /* Flush some space after the used payload so valgrind shuts up... */ + i = 0; + while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) + Buf->buf[Buf->BufUsed + i++] = '\0'; + return 1; + } else { + free(compressed_data); + } +#endif /* HAVE_ZLIB */ + return 0; +} + +/******************************************************************************* + * File I/O; Callbacks to libevent * + *******************************************************************************/ + +long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB) +{ + long bufremain = 0; + int n; + + if ((FB == NULL) || (FB->Buf == NULL)) + return -1; + + /* + * check whether the read pointer is somewhere in a range + * where a cut left is inexpensive + */ + + if (FB->ReadWritePointer != NULL) + { + long already_read; + + already_read = FB->ReadWritePointer - FB->Buf->buf; + bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1; + + if (already_read != 0) { + long unread; + + unread = FB->Buf->BufUsed - already_read; + + /* else nothing to compact... */ + if (unread == 0) { + FB->ReadWritePointer = FB->Buf->buf; + bufremain = FB->Buf->BufSize; + } + else if ((unread < 64) || + (bufremain < already_read)) + { + /* + * if its just a tiny bit remaining, or we run out of space... + * lets tidy up. + */ + FB->Buf->BufUsed = unread; + if (unread < already_read) + memcpy(FB->Buf->buf, FB->ReadWritePointer, unread); + else + memmove(FB->Buf->buf, FB->ReadWritePointer, unread); + FB->ReadWritePointer = FB->Buf->buf; + bufremain = FB->Buf->BufSize - unread - 1; + } + else if (bufremain < (FB->Buf->BufSize / 10)) + { + /* get a bigger buffer */ + + IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1); + + FB->ReadWritePointer = FB->Buf->buf + unread; + + bufremain = FB->Buf->BufSize - unread - 1; +/*TODO: special increase function that won't copy the already read! */ + } + } + else if (bufremain < 10) { + IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10); + + FB->ReadWritePointer = FB->Buf->buf; + + bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1; + } + + } + else { + FB->ReadWritePointer = FB->Buf->buf; + bufremain = FB->Buf->BufSize - 1; + } + + n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain); + + if (n > 0) { + FB->Buf->BufUsed += n; + FB->Buf->buf[FB->Buf->BufUsed] = '\0'; + } + return n; +} + +int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB) +{ + long WriteRemain; + int n; + + if ((FB == NULL) || (FB->Buf == NULL)) + return -1; + + if (FB->ReadWritePointer != NULL) + { + WriteRemain = FB->Buf->BufUsed - + (FB->ReadWritePointer - + FB->Buf->buf); + } + else { + FB->ReadWritePointer = FB->Buf->buf; + WriteRemain = FB->Buf->BufUsed; + } + + n = write(fd, FB->ReadWritePointer, WriteRemain); + if (n > 0) { + FB->ReadWritePointer += n; + + if (FB->ReadWritePointer == + FB->Buf->buf + FB->Buf->BufUsed) + { + FlushStrBuf(FB->Buf); + FB->ReadWritePointer = NULL; + return 0; + } + // check whether we've got something to write + // get the maximum chunk plus the pointer we can send + // write whats there + // if not all was sent, remember the send pointer for the next time + return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed; + } + return n; +} + +/** + * @ingroup StrBuf_IO + * @brief extract a "next line" from Buf; Ptr to persist across several iterations + * @param LineBuf your line will be copied here. + * @param FB BLOB with lines of text... + * @param Ptr moved arround to keep the next-line across several iterations + * has to be &NULL on start; will be &NotNULL on end of buffer + * @returns size of copied buffer + */ +eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB) +{ + const char *aptr, *ptr, *eptr; + char *optr, *xptr; + + if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL)) + return eReadFail; + + + if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) { + FB->ReadWritePointer = StrBufNOTNULL; + return eReadFail; + } + + FlushStrBuf(LineBuf); + if (FB->ReadWritePointer == NULL) + ptr = aptr = FB->Buf->buf; + else + ptr = aptr = FB->ReadWritePointer; + + optr = LineBuf->buf; + eptr = FB->Buf->buf + FB->Buf->BufUsed; + xptr = LineBuf->buf + LineBuf->BufSize - 1; + + while ((ptr <= eptr) && + (*ptr != '\n') && + (*ptr != '\r') ) + { + *optr = *ptr; + optr++; ptr++; + if (optr == xptr) { + LineBuf->BufUsed = optr - LineBuf->buf; + IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1); + optr = LineBuf->buf + LineBuf->BufUsed; + xptr = LineBuf->buf + LineBuf->BufSize - 1; + } + } + + if (ptr >= eptr) { + if (optr > LineBuf->buf) + optr --; + if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) { + LineBuf->BufUsed = optr - LineBuf->buf; + *optr = '\0'; + if ((FB->ReadWritePointer != NULL) && + (FB->ReadWritePointer != FB->Buf->buf)) + { + /* Ok, the client application read all the data + it was interested in so far. Since there is more to read, + we now shrink the buffer, and move the rest over. + */ + StrBufCutLeft(FB->Buf, + FB->ReadWritePointer - FB->Buf->buf); + FB->ReadWritePointer = FB->Buf->buf; + } + return eMustReadMore; + } + } + LineBuf->BufUsed = optr - LineBuf->buf; + *optr = '\0'; + if ((ptr <= eptr) && (*ptr == '\r')) + ptr ++; + if ((ptr <= eptr) && (*ptr == '\n')) + ptr ++; + + if (ptr < eptr) { + FB->ReadWritePointer = ptr; + } + else { + FlushStrBuf(FB->Buf); + FB->ReadWritePointer = NULL; + } + + return eReadSuccess; +} + +/** + * @ingroup StrBuf_CHUNKED_IO + * @brief check whether the chunk-buffer has more data waiting or not. + * @param FB Chunk-Buffer to inspect + */ +eReadState StrBufCheckBuffer(IOBuffer *FB) +{ + if (FB == NULL) + return eReadFail; + if (FB->Buf->BufUsed == 0) + return eReadSuccess; + if (FB->ReadWritePointer == NULL) + return eBufferNotEmpty; + if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer) + return eBufferNotEmpty; + return eReadSuccess; +} + +long IOBufferStrLength(IOBuffer *FB) +{ + if ((FB == NULL) || (FB->Buf == NULL)) + return 0; + if (FB->ReadWritePointer == NULL) + return StrLength(FB->Buf); + + return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf); +} + +inline static void FDIOBufferFlush(FDIOBuffer *FDB) +{ + memset(FDB, 0, sizeof(FDIOBuffer)); + FDB->OtherFD = -1; + FDB->SplicePipe[0] = -1; + FDB->SplicePipe[1] = -1; +} + +void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize) +{ + FDIOBufferFlush(FDB); + + FDB->TotalSendSize = TotalSendSize; + if (TotalSendSize > 0) + FDB->ChunkSize = TotalSendSize; + else + { + TotalSendSize = SIZ * 10; + FDB->ChunkSize = TotalSendSize; + } + FDB->IOB = IO; + +#ifdef LINUX_SPLICE + if (EnableSplice) + pipe(FDB->SplicePipe); + else +#endif + FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1); + + FDB->OtherFD = FD; +} + +void FDIOBufferDelete(FDIOBuffer *FDB) +{ +#ifdef LINUX_SPLICE + if (EnableSplice) + { + if (FDB->SplicePipe[0] > 0) + close(FDB->SplicePipe[0]); + if (FDB->SplicePipe[1] > 0) + close(FDB->SplicePipe[1]); + } + else +#endif + FreeStrBuf(&FDB->ChunkBuffer); + + if (FDB->OtherFD > 0) + close(FDB->OtherFD); + FDIOBufferFlush(FDB); +} + +int FileSendChunked(FDIOBuffer *FDB, const char **Err) +{ + ssize_t sent, pipesize; + + if (FDB->TotalSendSize > 0) + { +#ifdef LINUX_SPLICE + if (EnableSplice) + { + if (FDB->PipeSize == 0) + { + pipesize = splice(FDB->OtherFD, + &FDB->TotalSentAlready, + FDB->SplicePipe[1], + NULL, + FDB->ChunkSendRemain, + SPLICE_F_MOVE); + + if (pipesize == -1) + { + *Err = strerror(errno); + return pipesize; + } + FDB->PipeSize = pipesize; + } + sent = splice(FDB->SplicePipe[0], + NULL, + FDB->IOB->fd, + NULL, + FDB->PipeSize, + SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK); + if (sent == -1) + { + *Err = strerror(errno); + return sent; + } + FDB->PipeSize -= sent; + FDB->ChunkSendRemain -= sent; + return sent; + } + else +#endif + { + char *pRead; + long nRead = 0; + + pRead = FDB->ChunkBuffer->buf; + while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0)) + { + nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed); + if (nRead > 0) { + FDB->ChunkBuffer->BufUsed += nRead; + FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0'; + } + else if (nRead == 0) {} + else return nRead; + } + + nRead = write(FDB->IOB->fd, + FDB->ChunkBuffer->buf + FDB->TotalSentAlready, + FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready); + + if (nRead >= 0) { + FDB->TotalSentAlready += nRead; + FDB->ChunkSendRemain -= nRead; + return FDB->ChunkSendRemain; + } + else { + return nRead; + } + } + } + else + { +#ifdef LINUX_SPLICE + if (EnableSplice) + { + if (FDB->PipeSize == 0) + { + pipesize = splice(FDB->OtherFD, + &FDB->TotalSentAlready, + FDB->SplicePipe[1], + NULL, + SIZ * 10, + SPLICE_F_MOVE); + + if (pipesize == -1) + { + *Err = strerror(errno); + return pipesize; + } + FDB->PipeSize = pipesize; + if (pipesize == 0) + return -1; + } + sent = splice(FDB->SplicePipe[0], + NULL, + FDB->IOB->fd, + NULL, + FDB->PipeSize, + SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK); + if (sent == -1) + { + *Err = strerror(errno); + return sent; + } + FDB->PipeSize -= sent; + FDB->ChunkSendRemain -= sent; + return sent; + } + else +#endif + { + char *pRead; + long nRead = 0; + + pRead = FDB->ChunkBuffer->buf; + while ((FDB->ChunkSendRemain == 0) && + (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) && + (nRead >= 0)) + { + FDB->TotalSentAlready = 0; + nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed); + if (nRead > 0) { + FDB->ChunkBuffer->BufUsed += nRead; + FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0'; + FDB->ChunkSendRemain += nRead; + } + else if (nRead == 0) + { + return -1; + } + else + { + *Err = strerror(errno); + return nRead; + } + } + + nRead = write(FDB->IOB->fd, + FDB->ChunkBuffer->buf + FDB->TotalSentAlready, + FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready); + + if (nRead >= 0) { + FDB->TotalSentAlready += nRead; + FDB->ChunkSendRemain -= nRead; + if (FDB->ChunkSendRemain == 0) + { + FDB->ChunkBuffer->BufUsed = 0; + FDB->TotalSentAlready = 0; + } + return FDB->ChunkSendRemain; + } + else { + return nRead; + } } } - return n; } -/** - * @ingroup StrBuf - * @brief cuts a string after maxlen glyphs - * @param Buf string to cut to maxlen glyphs - * @param maxlen how long may the string become? - * @returns current length of the string - */ -long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen) +int FileRecvChunked(FDIOBuffer *FDB, const char **Err) { - char *aptr, *eptr; - int n = 0, m = 0; + ssize_t sent, pipesize; - aptr = Buf->buf; - eptr = Buf->buf + Buf->BufUsed; - while ((aptr < eptr) && (*aptr != '\0')) { - if (Ctdl_IsUtf8SequenceStart(*aptr)){ - m = Ctdl_GetUtf8SequenceLength(aptr, eptr); - while ((*aptr++ != '\0') && (m-- > 0)); - n ++; +#ifdef LINUX_SPLICE + if (EnableSplice) + { + 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; } - else { - n++; - aptr++; + + 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; } - if (n > maxlen) { - *aptr = '\0'; - Buf->BufUsed = aptr - Buf->buf; - return Buf->BufUsed; - } + FDB->PipeSize -= sent; + FDB->ChunkSendRemain -= sent; + return sent; } - return Buf->BufUsed; - -} + else +#endif + { + 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; + } + 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->PipeSize -= sent; + FDB->ChunkSendRemain -= sent; + 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; -/******************************************************************************* - * wrapping ZLib * - *******************************************************************************/ + 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; -#ifdef HAVE_ZLIB -#define DEF_MEM_LEVEL 8 /*< memlevel??? */ -#define OS_CODE 0x03 /*< unix */ + } + FDB->ChunkBuffer->BufUsed = 0; + FDB->TotalSentAlready += sent; + FDB->ChunkSendRemain -= sent; + return FDB->ChunkSendRemain; + } + else if (sent < 0) { + *Err = strerror(errno); + return sent; + } + return 0; + } +} -/** - * @ingroup StrBuf_DeEnCoder - * @brief uses the same calling syntax as compress2(), but it - * creates a stream compatible with HTTP "Content-encoding: gzip" - * @param dest compressed buffer - * @param destLen length of the compresed data - * @param source source to encode - * @param sourceLen length of source to encode - * @param level compression level - */ -int ZEXPORT compress_gzip(Bytef * dest, - size_t * destLen, - const Bytef * source, - uLong sourceLen, - int level) +eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error) { - const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */ - - /* write gzip header */ - snprintf((char *) dest, *destLen, - "%c%c%c%c%c%c%c%c%c%c", - gz_magic[0], gz_magic[1], Z_DEFLATED, - 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ , - OS_CODE); - - /* normal deflate */ - z_stream stream; - int err; - stream.next_in = (Bytef *) source; - stream.avail_in = (uInt) sourceLen; - stream.next_out = dest + 10L; // after header - stream.avail_out = (uInt) * destLen; - if ((uLong) stream.avail_out != *destLen) - return Z_BUF_ERROR; + int IsNonBlock; + int fdflags; + long rlen; + long should_write; + int nSuccessLess = 0; + struct timeval tv; + fd_set rfds; - stream.zalloc = (alloc_func) 0; - stream.zfree = (free_func) 0; - stream.opaque = (voidpf) 0; + fdflags = fcntl(FDB->OtherFD, F_GETFL); + IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; - err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS, - DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (err != Z_OK) - return err; + 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; + } - err = deflate(&stream, Z_FINISH); - if (err != Z_STREAM_END) { - deflateEnd(&stream); - return err == Z_OK ? Z_BUF_ERROR : err; - } - *destLen = stream.total_out + 10L; + should_write = FDB->IOB->Buf->BufUsed - + (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf); + if (should_write > FDB->ChunkSendRemain) + should_write = FDB->ChunkSendRemain; - /* write CRC and Length */ - uLong crc = crc32(0L, source, sourceLen); - int n; - for (n = 0; n < 4; ++n, ++*destLen) { - dest[*destLen] = (int) (crc & 0xff); - crc >>= 8; + 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; } - uLong len = stream.total_in; - for (n = 0; n < 4; ++n, ++*destLen) { - dest[*destLen] = (int) (len & 0xff); - len >>= 8; + if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed) + { + FlushStrBuf(FDB->IOB->Buf); + FDB->IOB->ReadWritePointer = NULL; } - err = deflateEnd(&stream); - return err; -} -#endif - - -/** - * @ingroup StrBuf_DeEnCoder - * @brief compress the buffer with gzip - * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself! - * @param Buf buffer whose content is to be gzipped - */ -int CompressBuffer(StrBuf *Buf) -{ -#ifdef HAVE_ZLIB - char *compressed_data = NULL; - size_t compressed_len, bufsize; - int i = 0; - bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100; - compressed_data = malloc(compressed_len); - - if (compressed_data == NULL) - return -1; - /* Flush some space after the used payload so valgrind shuts up... */ - while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) - Buf->buf[Buf->BufUsed + i++] = '\0'; - if (compress_gzip((Bytef *) compressed_data, - &compressed_len, - (Bytef *) Buf->buf, - (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) { - if (!Buf->ConstBuf) - free(Buf->buf); - Buf->buf = compressed_data; - Buf->BufUsed = compressed_len; - Buf->BufSize = bufsize; - /* Flush some space after the used payload so valgrind shuts up... */ - i = 0; - while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize)) - Buf->buf[Buf->BufUsed + i++] = '\0'; - return 1; - } else { - free(compressed_data); - } -#endif /* HAVE_ZLIB */ - return 0; + if (FDB->ChunkSendRemain == 0) + return eReadSuccess; + else + return eMustReadMore; } - - /******************************************************************************* * File I/O; Prefer buffered read since its faster! * *******************************************************************************/ @@ -2976,6 +4684,11 @@ int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error) { int len, rlen, slen; + if ((buf == NULL) || (buf->buf == NULL)) { + *Error = strerror(EINVAL); + return -1; + } + if (!append) FlushStrBuf(buf); @@ -3082,10 +4795,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; } @@ -3110,14 +4824,14 @@ static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line * @ingroup StrBuf_BufferedIO * @brief Read a line from socket * flushes and closes the FD on error - * @param Line Line to read from the fd / I/O Buffer - * @param IOBuf the buffer to get the input to - * @param Pos pointer to the current read position, should be NULL initialized! + * @param Line where to append our Line read from the fd / I/O Buffer; + * @param IOBuf the buffer to get the input to; lifetime pair to FD + * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.! * @param fd pointer to the filedescriptor to read * @param timeout number of successless selects until we bail out * @param selectresolution how long to wait on each select * @param Error strerror() on error - * @returns numbers of chars read + * @returns numbers of chars read or -1 in case of error. "\n" will become 0 */ int StrBufTCP_read_buffered_line_fast(StrBuf *Line, StrBuf *IOBuf, @@ -3130,7 +4844,7 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line, const char *pche = NULL; const char *pos = NULL; const char *pLF; - int len, rlen; + int len, rlen, retlen; int nSuccessLess = 0; fd_set rfds; const char *pch = NULL; @@ -3138,6 +4852,7 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line, int IsNonBlock; struct timeval tv; + retlen = 0; if ((Line == NULL) || (Pos == NULL) || (IOBuf == NULL) || @@ -3172,11 +4887,13 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line, } *pcht++ = *pch++; Line->BufUsed++; + retlen++; } len = pch - pos; if (len > 0 && (*(pch - 1) == '\r') ) { + retlen--; len --; pcht --; Line->BufUsed --; @@ -3201,7 +4918,7 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line, else *Pos = pch + 1; - return StrLength(Line); + return retlen; } else FlushStrBuf(IOBuf); @@ -3263,31 +4980,34 @@ int StrBufTCP_read_buffered_line_fast(StrBuf *Line, if (IOBuf->BufUsed + 10 > IOBuf->BufSize) { - long apos; + long apos = 0; - apos = pLF - IOBuf->buf; + if (pLF != NULL) apos = pLF - IOBuf->buf; IncreaseBuf(IOBuf, 1, -1); - pLF = IOBuf->buf + apos; + if (pLF != NULL) pLF = IOBuf->buf + apos; } continue; } + else + { + nSuccessLess++; + } } + *Pos = NULL; if (pLF != NULL) { pos = IOBuf->buf; - rlen = 0; len = pLF - pos; if (len > 0 && (*(pLF - 1) == '\r') ) - rlen ++; - StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len - rlen, 0); + len --; + StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0); if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed) { FlushStrBuf(IOBuf); - *Pos = NULL; } else *Pos = pLF + 1; - return StrLength(Line); + return retlen + len; } *Error = ErrRBLF_NotEnoughSentFromServer; return -1; @@ -3309,7 +5029,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; @@ -3317,7 +5037,7 @@ int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **E struct timeval tv; fd_set rfds; - if ((Buf == NULL) || (*fd == -1)) + if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1)) { *Error = ErrRBLF_BLOBPreConditionFailed; return -1; @@ -3329,8 +5049,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; @@ -3375,7 +5093,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 @@ -3399,25 +5117,24 @@ int StrBufReadBLOBBuffered(StrBuf *Blob, int check, const char **Error) { - const char *pche; const char *pos; - int nSelects = 0; - int SelRes; int fdflags; - int len = 0; - int rlen, slen; + int rlen = 0; int nRead = 0; int nAlreadyRead = 0; int IsNonBlock; char *ptr; 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)) + if ((Blob == NULL) || + (*fd == -1) || + (IOBuf == NULL) || + (Pos == NULL)) { - if (*Pos != NULL) + if (Pos != NULL) *Pos = NULL; *Error = ErrRBB_BLOBFPreConditionFailed; return -1; @@ -3430,18 +5147,15 @@ int StrBufReadBLOBBuffered(StrBuf *Blob, pos = *Pos; - if (pos > 0) - len = pos - IOBuf->buf; - rlen = IOBuf->BufUsed - len; + if (pos != NULL) + rlen = pos - IOBuf->buf; + rlen = IOBuf->BufUsed - rlen; if ((IOBuf->BufUsed > 0) && (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; @@ -3469,15 +5183,17 @@ int StrBufReadBLOBBuffered(StrBuf *Blob, IncreaseBuf(IOBuf, 0, nBytes - nRead); ptr = IOBuf->buf; - slen = len = Blob->BufUsed; - 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) { @@ -3499,10 +5215,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; @@ -3510,7 +5225,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)) @@ -3519,18 +5233,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; } @@ -3549,14 +5272,18 @@ 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; - if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) { + if ((Buf == NULL) || + (*Ptr == StrBufNOTNULL) || + (LineBuf == NULL)|| + (LineBuf->buf == NULL)) + { *Ptr = StrBufNOTNULL; return 0; } @@ -3629,9 +5356,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;