2 * Copyright (c) 1987-2018 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #include "libcitadel.h"
35 #include "b64/cencode.h"
36 #include "b64/cdecode.h"
52 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
53 const Bytef * source, uLong sourceLen, int level);
55 int BaseStrBufSize = 64;
57 int ZLibCompressionRatio = -1; /* defaults to 6 */
59 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
60 #define OS_CODE 0x03 /*< unix */
61 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
64 const char *StrBufNOTNULL = ((char*) NULL) - 1;
66 const char HexList[256][3] = {
67 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
68 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
69 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
70 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
71 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
72 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
73 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
74 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
75 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
76 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
77 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
78 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
79 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
80 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
81 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
82 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
85 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
86 * StrBuf is a versatile class, aiding the handling of dynamic strings
87 * * reduce de/reallocations
88 * * reduce the need to remeasure it
89 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
90 * * allow asyncroneous IO for line and Blob based operations
91 * * reduce the use of memove in those
92 * * Quick filling in several operations with append functions
96 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
101 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
103 * use these operators to interfere with code demanding char*;
104 * if you need to own the content, smash me. Avoid, since we loose the length information.
108 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
110 * operations to get your Strings into a StrBuf, manipulating them, or appending
113 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
115 * Quick tokenizer; demands of the user to pull its tokens in sequence
119 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
121 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
125 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
127 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
128 * External Cursor to maintain the current read position inside of the buffer
129 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
133 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
139 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
141 * these functions translate the content of a buffer into another representation;
142 * some are combined Fillers and encoders
146 * Private Structure for the Stringbuffer
149 char *buf; /**< the pointer to the dynamic buffer */
150 long BufSize; /**< how many spcae do we optain */
151 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
152 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
154 long nIncreases; /**< for profiling; cound how many times we needed more */
155 char bt [SIZ]; /**< Stacktrace of last increase */
156 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
161 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
162 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
165 #ifdef HAVE_BACKTRACE
166 static void StrBufBacktrace(StrBuf *Buf, int which)
170 void *stack_frames[50];
175 pstart = pch = Buf->bt;
177 pstart = pch = Buf->bt_lastinc;
178 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
179 strings = backtrace_symbols(stack_frames, size);
180 for (i = 0; i < size; i++) {
182 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
184 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
193 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
195 if (hFreeDbglog == -1){
196 pid_t pid = getpid();
198 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
199 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
201 if ((*FreeMe)->nIncreases > 0)
205 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
207 (*FreeMe)->nIncreases,
211 (*FreeMe)->bt_lastinc);
212 n = write(hFreeDbglog, buf, n);
218 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
222 n = write(hFreeDbglog, buf, n);
226 void dbg_IncreaseBuf(StrBuf *IncMe)
229 #ifdef HAVE_BACKTRACE
230 StrBufBacktrace(Buf, 1);
234 void dbg_Init(StrBuf *Buf)
238 Buf->bt_lastinc[0] = '\0';
239 #ifdef HAVE_BACKTRACE
240 StrBufBacktrace(Buf, 0);
246 #define dbg_FreeStrBuf(a, b)
247 #define dbg_IncreaseBuf(a)
254 * @brief swaps the contents of two StrBufs
255 * this is to be used to have cheap switched between a work-buffer and a target buffer
257 * @param B second one
259 static inline void iSwapBuffers(StrBuf *A, StrBuf *B)
263 memcpy(&C, A, sizeof(*A));
264 memcpy(A, B, sizeof(*B));
265 memcpy(B, &C, sizeof(C));
269 void SwapBuffers(StrBuf *A, StrBuf *B)
276 * @ingroup StrBuf_Cast
277 * @brief Cast operator to Plain String
278 * @note if the buffer is altered by StrBuf operations, this pointer may become
279 * invalid. So don't lean on it after altering the buffer!
280 * Since this operation is considered cheap, rather call it often than risking
281 * your pointer to become invalid!
282 * @param Str the string we want to get the c-string representation for
283 * @returns the Pointer to the Content. Don't mess with it!
285 inline const char *ChrPtr(const StrBuf *Str)
293 * @ingroup StrBuf_Cast
294 * @brief since we know strlen()'s result, provide it here.
295 * @param Str the string to return the length to
296 * @returns contentlength of the buffer
298 inline int StrLength(const StrBuf *Str)
300 return (Str != NULL) ? Str->BufUsed : 0;
304 * @ingroup StrBuf_DeConstructors
305 * @brief local utility function to resize the buffer
306 * @param Buf the buffer whichs storage we should increase
307 * @param KeepOriginal should we copy the original buffer or just start over with a new one
308 * @param DestSize what should fit in after?
310 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
313 size_t NewSize = Buf->BufSize * 2;
319 while ((NewSize <= DestSize) && (NewSize != 0))
325 NewBuf= (char*) malloc(NewSize);
329 if (KeepOriginal && (Buf->BufUsed > 0))
331 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
340 Buf->BufSize = NewSize;
342 dbg_IncreaseBuf(Buf);
348 * @ingroup StrBuf_DeConstructors
349 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
350 * @param Buf Buffer to shrink (has to be empty)
351 * @param ThreshHold if the buffer is bigger then this, its readjusted
352 * @param NewSize if we Shrink it, how big are we going to be afterwards?
354 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
357 (Buf->BufUsed == 0) &&
358 (Buf->BufSize < ThreshHold)) {
360 Buf->buf = (char*) malloc(NewSize);
362 Buf->BufSize = NewSize;
367 * @ingroup StrBuf_DeConstructors
368 * @brief shrink long term buffers to their real size so they don't waste memory
369 * @param Buf buffer to shrink
370 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
371 * @returns physical size of the buffer
373 long StrBufShrinkToFit(StrBuf *Buf, int Force)
378 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
382 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
386 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
387 Buf->BufSize = Buf->BufUsed + 1;
395 * @ingroup StrBuf_DeConstructors
396 * @brief Allocate a new buffer with default buffer size
397 * @returns the new stringbuffer
399 StrBuf* NewStrBuf(void)
403 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
407 NewBuf->buf = (char*) malloc(BaseStrBufSize);
408 if (NewBuf->buf == NULL)
413 NewBuf->buf[0] = '\0';
414 NewBuf->BufSize = BaseStrBufSize;
416 NewBuf->ConstBuf = 0;
424 * @ingroup StrBuf_DeConstructors
425 * @brief Copy Constructor; returns a duplicate of CopyMe
426 * @param CopyMe Buffer to faxmilate
427 * @returns the new stringbuffer
429 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
436 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
440 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
441 if (NewBuf->buf == NULL)
447 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
448 NewBuf->BufUsed = CopyMe->BufUsed;
449 NewBuf->BufSize = CopyMe->BufSize;
450 NewBuf->ConstBuf = 0;
458 * @ingroup StrBuf_DeConstructors
459 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
460 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
461 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
462 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
463 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
464 * @returns the new stringbuffer
466 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
470 if (CreateRelpaceMe == NULL)
475 if (*CreateRelpaceMe != NULL)
476 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
478 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
482 if (CopyFlushMe == NULL)
484 if (*CreateRelpaceMe != NULL)
485 FlushStrBuf(*CreateRelpaceMe);
487 *CreateRelpaceMe = NewStrBuf();
492 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
493 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
494 * be a big IO-Buffer...
496 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
498 if (*CreateRelpaceMe == NULL)
500 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
505 NewBuf = *CreateRelpaceMe;
508 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
512 if (*CreateRelpaceMe == NULL)
514 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
518 NewBuf = *CreateRelpaceMe;
519 iSwapBuffers (NewBuf, CopyFlushMe);
522 FlushStrBuf(CopyFlushMe);
527 * @ingroup StrBuf_DeConstructors
528 * @brief create a new Buffer using an existing c-string
529 * this function should also be used if you want to pre-suggest
530 * the buffer size to allocate in conjunction with ptr == NULL
531 * @param ptr the c-string to copy; may be NULL to create a blank instance
532 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
533 * @returns the new stringbuffer
535 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
538 size_t Siz = BaseStrBufSize;
541 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
546 CopySize = strlen((ptr != NULL)?ptr:"");
550 while ((Siz <= CopySize) && (Siz != 0))
559 NewBuf->buf = (char*) malloc(Siz);
560 if (NewBuf->buf == NULL)
565 NewBuf->BufSize = Siz;
567 memcpy(NewBuf->buf, ptr, CopySize);
568 NewBuf->buf[CopySize] = '\0';
569 NewBuf->BufUsed = CopySize;
572 NewBuf->buf[0] = '\0';
575 NewBuf->ConstBuf = 0;
583 * @ingroup StrBuf_DeConstructors
584 * @brief Set an existing buffer from a c-string
585 * @param Buf buffer to load
586 * @param ptr c-string to put into
587 * @param nChars set to -1 if we should work 0-terminated
588 * @returns the new length of the string
590 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
605 CopySize = strlen(ptr);
609 while ((Siz <= CopySize) && (Siz != 0))
617 if (Siz != Buf->BufSize)
618 IncreaseBuf(Buf, 0, Siz);
619 memcpy(Buf->buf, ptr, CopySize);
620 Buf->buf[CopySize] = '\0';
621 Buf->BufUsed = CopySize;
628 * @ingroup StrBuf_DeConstructors
629 * @brief use strbuf as wrapper for a string constant for easy handling
630 * @param StringConstant a string to wrap
631 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
633 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
637 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
640 NewBuf->buf = (char*) StringConstant;
641 NewBuf->BufSize = SizeOfStrConstant;
642 NewBuf->BufUsed = SizeOfStrConstant;
643 NewBuf->ConstBuf = 1;
652 * @ingroup StrBuf_DeConstructors
653 * @brief flush the content of a Buf; keep its struct
654 * @param buf Buffer to flush
656 int FlushStrBuf(StrBuf *buf)
658 if ((buf == NULL) || (buf->buf == NULL))
668 * @ingroup StrBuf_DeConstructors
669 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
670 * @param buf Buffer to wipe
672 int FLUSHStrBuf(StrBuf *buf)
678 if (buf->BufUsed > 0) {
679 memset(buf->buf, 0, buf->BufUsed);
686 int hFreeDbglog = -1;
689 * @ingroup StrBuf_DeConstructors
690 * @brief Release a Buffer
691 * Its a double pointer, so it can NULL your pointer
692 * so fancy SIG11 appear instead of random results
693 * @param FreeMe Pointer Pointer to the buffer to free
695 void FreeStrBuf (StrBuf **FreeMe)
700 dbg_FreeStrBuf(FreeMe, 'F');
702 if (!(*FreeMe)->ConstBuf)
703 free((*FreeMe)->buf);
709 * @ingroup StrBuf_DeConstructors
710 * @brief flatten a Buffer to the Char * we return
711 * Its a double pointer, so it can NULL your pointer
712 * so fancy SIG11 appear instead of random results
713 * The Callee then owns the buffer and is responsible for freeing it.
714 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
715 * @returns the pointer of the buffer; Callee owns the memory thereafter.
717 char *SmashStrBuf (StrBuf **SmashMe)
721 if ((SmashMe == NULL) || (*SmashMe == NULL))
724 dbg_FreeStrBuf(SmashMe, 'S');
726 Ret = (*SmashMe)->buf;
733 * @ingroup StrBuf_DeConstructors
734 * @brief Release the buffer
735 * If you want put your StrBuf into a Hash, use this as Destructor.
736 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
738 void HFreeStrBuf (void *VFreeMe)
740 StrBuf *FreeMe = (StrBuf*)VFreeMe;
744 dbg_FreeStrBuf(SmashMe, 'H');
746 if (!FreeMe->ConstBuf)
752 /*******************************************************************************
753 * Simple string transformations *
754 *******************************************************************************/
758 * @brief Wrapper around atol
760 long StrTol(const StrBuf *Buf)
765 return atol(Buf->buf);
772 * @brief Wrapper around atoi
774 int StrToi(const StrBuf *Buf)
778 if (Buf->BufUsed > 0)
779 return atoi(Buf->buf);
786 * @brief Checks to see if the string is a pure number
787 * @param Buf The buffer to inspect
788 * @returns 1 if its a pure number, 0, if not.
790 int StrBufIsNumber(const StrBuf *Buf) {
792 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
795 strtoll(Buf->buf, &pEnd, 10);
796 if (pEnd == Buf->buf)
798 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
800 if (Buf->buf == pEnd)
806 * @ingroup StrBuf_Filler
807 * @brief modifies a Single char of the Buf
808 * You can point to it via char* or a zero-based integer
809 * @param Buf The buffer to manipulate
810 * @param ptr char* to zero; use NULL if unused
811 * @param nThChar zero based pointer into the string; use -1 if unused
812 * @param PeekValue The Character to place into the position
814 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
819 nThChar = ptr - Buf->buf;
820 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
822 Buf->buf[nThChar] = PeekValue;
827 * @ingroup StrBuf_Filler
828 * @brief modifies a range of chars of the Buf
829 * You can point to it via char* or a zero-based integer
830 * @param Buf The buffer to manipulate
831 * @param ptr char* to zero; use NULL if unused
832 * @param nThChar zero based pointer into the string; use -1 if unused
833 * @param nChars how many chars are to be flushed?
834 * @param PookValue The Character to place into that area
836 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
841 nThChar = ptr - Buf->buf;
842 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
844 if (nThChar + nChars > Buf->BufUsed)
845 nChars = Buf->BufUsed - nThChar;
847 memset(Buf->buf + nThChar, PookValue, nChars);
848 /* just to be shure... */
849 Buf->buf[Buf->BufUsed] = 0;
854 * @ingroup StrBuf_Filler
855 * @brief Append a StringBuffer to the buffer
856 * @param Buf Buffer to modify
857 * @param AppendBuf Buffer to copy at the end of our buffer
858 * @param Offset Should we start copying from an offset?
860 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
862 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
863 (Buf == NULL) || (Buf->buf == NULL))
866 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
869 AppendBuf->BufUsed + Buf->BufUsed);
871 memcpy(Buf->buf + Buf->BufUsed,
872 AppendBuf->buf + Offset,
873 AppendBuf->BufUsed - Offset);
874 Buf->BufUsed += AppendBuf->BufUsed - Offset;
875 Buf->buf[Buf->BufUsed] = '\0';
880 * @ingroup StrBuf_Filler
881 * @brief Append a C-String to the buffer
882 * @param Buf Buffer to modify
883 * @param AppendBuf Buffer to copy at the end of our buffer
884 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
885 * @param Offset Should we start copying from an offset?
887 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
890 long BufSizeRequired;
892 if ((AppendBuf == NULL) || (Buf == NULL))
896 aps = strlen(AppendBuf + Offset);
898 aps = AppendSize - Offset;
900 BufSizeRequired = Buf->BufUsed + aps + 1;
901 if (Buf->BufSize <= BufSizeRequired)
902 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
904 memcpy(Buf->buf + Buf->BufUsed,
908 Buf->buf[Buf->BufUsed] = '\0';
912 * @ingroup StrBuf_Filler
913 * @brief sprintf like function appending the formated string to the buffer
914 * vsnprintf version to wrap into own calls
915 * @param Buf Buffer to extend by format and Params
916 * @param format printf alike format to add
917 * @param ap va_list containing the items for format
919 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
927 if ((Buf == NULL) || (format == NULL))
930 BufSize = Buf->BufSize;
931 nWritten = Buf->BufSize + 1;
932 Offset = Buf->BufUsed;
933 newused = Offset + nWritten;
935 while (newused >= BufSize) {
937 nWritten = vsnprintf(Buf->buf + Offset,
938 Buf->BufSize - Offset,
941 newused = Offset + nWritten;
942 if (newused >= Buf->BufSize) {
943 if (IncreaseBuf(Buf, 1, newused) == -1)
944 return; /* TODO: error handling? */
945 newused = Buf->BufSize + 1;
948 Buf->BufUsed = Offset + nWritten;
949 BufSize = Buf->BufSize;
956 * @ingroup StrBuf_Filler
957 * @brief sprintf like function appending the formated string to the buffer
958 * @param Buf Buffer to extend by format and Params
959 * @param format printf alike format to add
961 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
969 if ((Buf == NULL) || (format == NULL))
972 BufSize = Buf->BufSize;
973 nWritten = Buf->BufSize + 1;
974 Offset = Buf->BufUsed;
975 newused = Offset + nWritten;
977 while (newused >= BufSize) {
978 va_start(arg_ptr, format);
979 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
980 Buf->BufSize - Buf->BufUsed,
983 newused = Buf->BufUsed + nWritten;
984 if (newused >= Buf->BufSize) {
985 if (IncreaseBuf(Buf, 1, newused) == -1)
986 return; /* TODO: error handling? */
987 newused = Buf->BufSize + 1;
990 Buf->BufUsed += nWritten;
991 BufSize = Buf->BufSize;
998 * @ingroup StrBuf_Filler
999 * @brief sprintf like function putting the formated string into the buffer
1000 * @param Buf Buffer to extend by format and Parameters
1001 * @param format printf alike format to add
1003 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
1008 if ((Buf == NULL) || (format == NULL))
1011 nWritten = Buf->BufSize + 1;
1012 while (nWritten >= Buf->BufSize) {
1013 va_start(arg_ptr, format);
1014 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1016 if (nWritten >= Buf->BufSize) {
1017 if (IncreaseBuf(Buf, 0, 0) == -1)
1018 return; /* TODO: error handling? */
1019 nWritten = Buf->BufSize + 1;
1022 Buf->BufUsed = nWritten ;
1027 * @ingroup StrBuf_Filler
1028 * @brief Callback for cURL to append the webserver reply to a buffer
1029 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1030 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1031 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1032 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1034 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1043 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1044 return size * nmemb;
1050 * @brief extracts a substring from Source into dest
1051 * @param dest buffer to place substring into
1052 * @param Source string to copy substring from
1053 * @param Offset chars to skip from start
1054 * @param nChars number of chars to copy
1055 * @returns the number of chars copied; may be different from nChars due to the size of Source
1057 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1059 size_t NCharsRemain;
1060 if (Offset > Source->BufUsed)
1066 if (Offset + nChars < Source->BufUsed)
1068 if ((nChars >= dest->BufSize) &&
1069 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1071 memcpy(dest->buf, Source->buf + Offset, nChars);
1072 dest->BufUsed = nChars;
1073 dest->buf[dest->BufUsed] = '\0';
1076 NCharsRemain = Source->BufUsed - Offset;
1077 if ((NCharsRemain >= dest->BufSize) &&
1078 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1080 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1081 dest->BufUsed = NCharsRemain;
1082 dest->buf[dest->BufUsed] = '\0';
1083 return NCharsRemain;
1088 * @brief Cut nChars from the start of the string
1089 * @param Buf Buffer to modify
1090 * @param nChars how many chars should be skipped?
1092 void StrBufCutLeft(StrBuf *Buf, int nChars)
1094 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1095 if (nChars >= Buf->BufUsed) {
1099 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1100 Buf->BufUsed -= nChars;
1101 Buf->buf[Buf->BufUsed] = '\0';
1106 * @brief Cut the trailing n Chars from the string
1107 * @param Buf Buffer to modify
1108 * @param nChars how many chars should be trunkated?
1110 void StrBufCutRight(StrBuf *Buf, int nChars)
1112 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1115 if (nChars >= Buf->BufUsed) {
1119 Buf->BufUsed -= nChars;
1120 Buf->buf[Buf->BufUsed] = '\0';
1125 * @brief Cut the string after n Chars
1126 * @param Buf Buffer to modify
1127 * @param AfternChars after how many chars should we trunkate the string?
1128 * @param At if non-null and points inside of our string, cut it there.
1130 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1132 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1134 AfternChars = At - Buf->buf;
1137 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1139 Buf->BufUsed = AfternChars;
1140 Buf->buf[Buf->BufUsed] = '\0';
1146 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1147 * @param Buf the string to modify
1149 void StrBufTrim(StrBuf *Buf)
1152 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1154 while ((Buf->BufUsed > 0) &&
1155 isspace(Buf->buf[Buf->BufUsed - 1]))
1159 Buf->buf[Buf->BufUsed] = '\0';
1161 if (Buf->BufUsed == 0) return;
1163 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1166 if (delta > 0) StrBufCutLeft(Buf, delta);
1170 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1171 * @param Buf the string to modify
1173 void StrBufSpaceToBlank(StrBuf *Buf)
1177 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1180 pche = pch + Buf->BufUsed;
1189 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1194 if ((Buf == NULL) || (Buf->buf == NULL)) {
1198 pRight = strchr(Buf->buf, rightboundary);
1199 if (pRight != NULL) {
1200 StrBufCutAt(Buf, 0, pRight);
1203 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1204 if (pLeft != NULL) {
1205 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1211 * @ingroup StrBuf_Filler
1212 * @brief uppercase the contents of a buffer
1213 * @param Buf the buffer to translate
1215 void StrBufUpCase(StrBuf *Buf)
1219 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1222 pche = pch + Buf->BufUsed;
1223 while (pch < pche) {
1224 *pch = toupper(*pch);
1231 * @ingroup StrBuf_Filler
1232 * @brief lowercase the contents of a buffer
1233 * @param Buf the buffer to translate
1235 void StrBufLowerCase(StrBuf *Buf)
1239 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1242 pche = pch + Buf->BufUsed;
1243 while (pch < pche) {
1244 *pch = tolower(*pch);
1250 /*******************************************************************************
1251 * a tokenizer that kills, maims, and destroys *
1252 *******************************************************************************/
1255 * @ingroup StrBuf_Tokenizer
1256 * @brief Replace a token at a given place with a given length by another token with given length
1257 * @param Buf String where to work on
1258 * @param where where inside of the Buf is the search-token
1259 * @param HowLong How long is the token to be replaced
1260 * @param Repl Token to insert at 'where'
1261 * @param ReplLen Length of repl
1262 * @returns -1 if fail else length of resulting Buf
1264 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1265 const char *Repl, long ReplLen)
1268 if ((Buf == NULL) ||
1269 (where > Buf->BufUsed) ||
1270 (where + HowLong > Buf->BufUsed))
1273 if (where + ReplLen - HowLong > Buf->BufSize)
1274 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1277 memmove(Buf->buf + where + ReplLen,
1278 Buf->buf + where + HowLong,
1279 Buf->BufUsed - where - HowLong);
1281 memcpy(Buf->buf + where,
1284 Buf->BufUsed += ReplLen - HowLong;
1286 return Buf->BufUsed;
1290 * @ingroup StrBuf_Tokenizer
1291 * @brief Counts the numbmer of tokens in a buffer
1292 * @param source String to count tokens in
1293 * @param tok Tokenizer char to count
1294 * @returns numbers of tokenizer chars found
1296 int StrBufNum_tokens(const StrBuf *source, char tok)
1300 if ((source == NULL) || (source->BufUsed == 0))
1302 if ((source->BufUsed == 1) && (*source->buf == tok))
1306 pche = pch + source->BufUsed;
1317 * @ingroup StrBuf_Tokenizer
1318 * @brief a string tokenizer
1319 * @param Source StringBuffer to read into
1320 * @param parmnum n'th Parameter to remove
1321 * @param separator tokenizer character
1322 * @returns -1 if not found, else length of token.
1324 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1327 char *d, *s, *end; /* dest, source */
1330 /* Find desired @parameter */
1331 end = Source->buf + Source->BufUsed;
1333 while ((d <= end) &&
1336 /* End of string, bail! */
1341 if (*d == separator) {
1346 if ((d == NULL) || (d >= end))
1347 return 0; /* @Parameter not found */
1349 /* Find next @parameter */
1351 while ((s <= end) &&
1352 (*s && *s != separator))
1356 if (*s == separator)
1360 /* Hack and slash */
1365 memmove(d, s, Source->BufUsed - (s - Source->buf));
1366 Source->BufUsed += ReducedBy;
1367 Source->buf[Source->BufUsed] = '\0';
1369 else if (d == Source->buf) {
1371 Source->BufUsed = 0;
1375 Source->BufUsed += ReducedBy;
1386 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1388 const StrBuf Temp = {
1401 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1405 * @ingroup StrBuf_Tokenizer
1406 * @brief a string tokenizer
1407 * @param dest Destination StringBuffer
1408 * @param Source StringBuffer to read into
1409 * @param parmnum n'th Parameter to extract
1410 * @param separator tokenizer character
1411 * @returns -1 if not found, else length of token.
1413 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1415 const char *s, *e; //* source * /
1416 int len = 0; //* running total length of extracted string * /
1417 int current_token = 0; //* token currently being processed * /
1420 dest->buf[0] = '\0';
1426 if ((Source == NULL) || (Source->BufUsed ==0)) {
1430 e = s + Source->BufUsed;
1433 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1435 while ((s < e) && !IsEmptyStr(s)) {
1436 if (*s == separator) {
1439 if (len >= dest->BufSize) {
1440 dest->BufUsed = len;
1441 if (IncreaseBuf(dest, 1, -1) < 0) {
1446 if ( (current_token == parmnum) &&
1447 (*s != separator)) {
1448 dest->buf[len] = *s;
1451 else if (current_token > parmnum) {
1457 dest->buf[len] = '\0';
1458 dest->BufUsed = len;
1460 if (current_token < parmnum) {
1461 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1464 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1473 * @ingroup StrBuf_Tokenizer
1474 * @brief a string tokenizer to fetch an integer
1475 * @param Source String containing tokens
1476 * @param parmnum n'th Parameter to extract
1477 * @param separator tokenizer character
1478 * @returns 0 if not found, else integer representation of the token
1480 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1490 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1497 * @ingroup StrBuf_Tokenizer
1498 * @brief a string tokenizer to fetch a long integer
1499 * @param Source String containing tokens
1500 * @param parmnum n'th Parameter to extract
1501 * @param separator tokenizer character
1502 * @returns 0 if not found, else long integer representation of the token
1504 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1514 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1522 * @ingroup StrBuf_Tokenizer
1523 * @brief a string tokenizer to fetch an unsigned long
1524 * @param Source String containing tokens
1525 * @param parmnum n'th Parameter to extract
1526 * @param separator tokenizer character
1527 * @returns 0 if not found, else unsigned long representation of the token
1529 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1540 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1544 return (unsigned long) atol(pnum);
1553 * @ingroup StrBuf_NextTokenizer
1554 * @brief a string tokenizer; Bounds checker
1555 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1556 * @param Source our tokenbuffer
1557 * @param pStart the token iterator pointer to inspect
1558 * @returns whether the revolving pointer is inside of the search range
1560 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1562 if ((Source == NULL) ||
1563 (*pStart == StrBufNOTNULL) ||
1564 (Source->BufUsed == 0))
1568 if (*pStart == NULL)
1572 else if (*pStart > Source->buf + Source->BufUsed)
1576 else if (*pStart <= Source->buf)
1585 * @ingroup StrBuf_NextTokenizer
1586 * @brief a string tokenizer
1587 * @param dest Destination StringBuffer
1588 * @param Source StringBuffer to read into
1589 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1590 * @param separator tokenizer
1591 * @returns -1 if not found, else length of token.
1593 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1595 const char *s; /* source */
1596 const char *EndBuffer; /* end stop of source buffer */
1597 int current_token = 0; /* token currently being processed */
1598 int len = 0; /* running total length of extracted string */
1600 if ((Source == NULL) ||
1601 (Source->BufUsed == 0) )
1603 *pStart = StrBufNOTNULL;
1609 EndBuffer = Source->buf + Source->BufUsed;
1613 dest->buf[0] = '\0';
1618 *pStart = EndBuffer + 1;
1622 if (*pStart == NULL)
1624 *pStart = Source->buf; /* we're starting to examine this buffer. */
1626 else if ((*pStart < Source->buf) ||
1627 (*pStart > EndBuffer ) )
1629 return -1; /* no more tokens to find. */
1633 /* start to find the next token */
1634 while ((s <= EndBuffer) &&
1635 (current_token == 0) )
1637 if (*s == separator)
1639 /* we found the next token */
1643 if (len >= dest->BufSize)
1645 /* our Dest-buffer isn't big enough, increase it. */
1646 dest->BufUsed = len;
1648 if (IncreaseBuf(dest, 1, -1) < 0) {
1649 /* WHUT? no more mem? bail out. */
1656 if ( (current_token == 0 ) && /* are we in our target token? */
1657 (!IsEmptyStr(s) ) &&
1658 (separator != *s) ) /* don't copy the token itself */
1660 dest->buf[len] = *s; /* Copy the payload */
1661 ++len; /* remember the bigger size. */
1667 /* did we reach the end? */
1668 if ((s > EndBuffer)) {
1669 EndBuffer = StrBufNOTNULL;
1670 *pStart = EndBuffer;
1673 *pStart = s; /* remember the position for the next run */
1676 /* sanitize our extracted token */
1677 dest->buf[len] = '\0';
1678 dest->BufUsed = len;
1685 * @ingroup StrBuf_NextTokenizer
1686 * @brief a string tokenizer
1687 * @param Source StringBuffer to read from
1688 * @param pStart pointer to the end of the last token. Feed with NULL.
1689 * @param separator tokenizer character
1690 * @param nTokens number of tokens to fastforward over
1691 * @returns -1 if not found, else length of token.
1693 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1695 const char *s, *EndBuffer; //* source * /
1696 int len = 0; //* running total length of extracted string * /
1697 int current_token = 0; //* token currently being processed * /
1699 if ((Source == NULL) ||
1700 (Source->BufUsed ==0)) {
1704 return Source->BufUsed;
1706 if (*pStart == NULL)
1707 *pStart = Source->buf;
1709 EndBuffer = Source->buf + Source->BufUsed;
1711 if ((*pStart < Source->buf) ||
1712 (*pStart > EndBuffer)) {
1720 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1722 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1723 if (*s == separator) {
1726 if (current_token >= nTokens) {
1738 * @ingroup StrBuf_NextTokenizer
1739 * @brief a string tokenizer to fetch an integer
1740 * @param Source StringBuffer to read from
1741 * @param pStart Cursor on the tokenstring
1742 * @param separator tokenizer character
1743 * @returns 0 if not found, else integer representation of the token
1745 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1755 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1762 * @ingroup StrBuf_NextTokenizer
1763 * @brief a string tokenizer to fetch a long integer
1764 * @param Source StringBuffer to read from
1765 * @param pStart Cursor on the tokenstring
1766 * @param separator tokenizer character
1767 * @returns 0 if not found, else long integer representation of the token
1769 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1779 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1787 * @ingroup StrBuf_NextTokenizer
1788 * @brief a string tokenizer to fetch an unsigned long
1789 * @param Source StringBuffer to read from
1790 * @param pStart Cursor on the tokenstring
1791 * @param separator tokenizer character
1792 * @returns 0 if not found, else unsigned long representation of the token
1794 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1805 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1809 return (unsigned long) atol(pnum);
1819 /*******************************************************************************
1820 * Escape Appending *
1821 *******************************************************************************/
1824 * @ingroup StrBuf_DeEnCoder
1825 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1826 * @param OutBuf the output buffer
1827 * @param In Buffer to encode
1828 * @param PlainIn way in from plain old c strings
1830 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1832 const char *pch, *pche;
1836 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1838 if (PlainIn != NULL) {
1839 len = strlen(PlainIn);
1845 pche = pch + In->BufUsed;
1852 pt = OutBuf->buf + OutBuf->BufUsed;
1853 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1855 while (pch < pche) {
1857 IncreaseBuf(OutBuf, 1, -1);
1858 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1859 pt = OutBuf->buf + OutBuf->BufUsed;
1862 if((*pch >= 'a' && *pch <= 'z') ||
1863 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1864 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1865 (*pch == '!') || (*pch == '_') ||
1866 (*pch == ',') || (*pch == '.'))
1873 *(pt + 1) = HexList[(unsigned char)*pch][0];
1874 *(pt + 2) = HexList[(unsigned char)*pch][1];
1876 OutBuf->BufUsed += 3;
1884 * @ingroup StrBuf_DeEnCoder
1885 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1886 * @param OutBuf the output buffer
1887 * @param In Buffer to encode
1888 * @param PlainIn way in from plain old c strings
1890 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1892 const char *pch, *pche;
1896 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1898 if (PlainIn != NULL) {
1899 len = strlen(PlainIn);
1905 pche = pch + In->BufUsed;
1912 pt = OutBuf->buf + OutBuf->BufUsed;
1913 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1915 while (pch < pche) {
1917 IncreaseBuf(OutBuf, 1, -1);
1918 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1919 pt = OutBuf->buf + OutBuf->BufUsed;
1922 if((*pch >= 'a' && *pch <= 'z') ||
1923 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1924 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1925 (*pch == '!') || (*pch == '_') ||
1926 (*pch == ',') || (*pch == '.'))
1933 *(pt + 1) = HexList[(unsigned char)*pch][0];
1934 *(pt + 2) = HexList[(unsigned char)*pch][1];
1936 OutBuf->BufUsed += 3;
1944 * @ingroup StrBuf_DeEnCoder
1945 * @brief append a string with characters having a special meaning in xml encoded to the buffer
1946 * @param OutBuf the output buffer
1947 * @param In Buffer to encode
1948 * @param PlainIn way in from plain old c strings
1949 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1950 * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1952 void StrBufXMLEscAppend(StrBuf *OutBuf,
1954 const char *PlainIn,
1956 int OverrideLowChars)
1958 const char *pch, *pche;
1963 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1965 if (PlainIn != NULL) {
1967 len = strlen((const char*)PlainIn);
1974 pch = (const char*)In->buf;
1975 pche = pch + In->BufUsed;
1982 pt = OutBuf->buf + OutBuf->BufUsed;
1983 /**< we max append 6 chars at once plus the \0 */
1984 pte = OutBuf->buf + OutBuf->BufSize - 6;
1986 while (pch < pche) {
1988 OutBuf->BufUsed = pt - OutBuf->buf;
1989 IncreaseBuf(OutBuf, 1, -1);
1990 pte = OutBuf->buf + OutBuf->BufSize - 6;
1991 /**< we max append 3 chars at once plus the \0 */
1993 pt = OutBuf->buf + OutBuf->BufUsed;
1997 memcpy(pt, HKEY("<"));
2001 else if (*pch == '>') {
2002 memcpy(pt, HKEY(">"));
2006 else if (*pch == '&') {
2007 memcpy(pt, HKEY("&"));
2011 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2016 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
2019 while ((IsUtf8Sequence > 0) &&
2032 *pt = HexList[*(unsigned char*)pch][0];
2034 *pt = HexList[*(unsigned char*)pch][1];
2043 OutBuf->BufUsed = pt - OutBuf->buf;
2048 * @ingroup StrBuf_DeEnCoder
2049 * @brief append a string in hex encoding to the buffer
2050 * @param OutBuf the output buffer
2051 * @param In Buffer to encode
2052 * @param PlainIn way in from plain old c strings
2053 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2055 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2057 const unsigned char *pch, *pche;
2061 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2063 if (PlainIn != NULL) {
2065 len = strlen((const char*)PlainIn);
2072 pch = (const unsigned char*)In->buf;
2073 pche = pch + In->BufUsed;
2080 pt = OutBuf->buf + OutBuf->BufUsed;
2081 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2083 while (pch < pche) {
2085 IncreaseBuf(OutBuf, 1, -1);
2086 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2087 pt = OutBuf->buf + OutBuf->BufUsed;
2090 *pt = HexList[*pch][0];
2092 *pt = HexList[*pch][1];
2093 pt ++; pch ++; OutBuf->BufUsed += 2;
2098 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2105 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2107 if (PlainIn != NULL) {
2109 len = strlen(PlainIn);
2122 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2124 if (ExpectLen > OutBuf->BufSize)
2125 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2128 pt = OutBuf->buf + OutBuf->BufUsed;
2130 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2133 OutBuf->BufUsed += len;
2138 * @ingroup StrBuf_DeEnCoder
2139 * @brief append a string in hex encoding to the buffer
2140 * @param OutBuf the output buffer
2141 * @param In Buffer to encode
2142 * @param PlainIn way in from plain old c strings
2144 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2146 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2150 * @ingroup StrBuf_DeEnCoder
2151 * @brief Append a string, escaping characters which have meaning in HTML.
2153 * @param Target target buffer
2154 * @param Source source buffer; set to NULL if you just have a C-String
2155 * @param PlainIn Plain-C string to append; set to NULL if unused
2156 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2157 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2158 * if set to 2, linebreaks are replaced by <br/>
2160 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2162 const char *aptr, *eiptr;
2166 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2169 if (PlainIn != NULL) {
2171 len = strlen(PlainIn);
2176 eiptr = aptr + Source->BufUsed;
2177 len = Source->BufUsed;
2183 bptr = Target->buf + Target->BufUsed;
2184 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2186 while (aptr < eiptr){
2188 IncreaseBuf(Target, 1, -1);
2189 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2190 bptr = Target->buf + Target->BufUsed;
2193 memcpy(bptr, "<", 4);
2195 Target->BufUsed += 4;
2197 else if (*aptr == '>') {
2198 memcpy(bptr, ">", 4);
2200 Target->BufUsed += 4;
2202 else if (*aptr == '&') {
2203 memcpy(bptr, "&", 5);
2205 Target->BufUsed += 5;
2207 else if (*aptr == '"') {
2208 memcpy(bptr, """, 6);
2210 Target->BufUsed += 6;
2212 else if (*aptr == '\'') {
2213 memcpy(bptr, "'", 5);
2215 Target->BufUsed += 5;
2217 else if (*aptr == LB) {
2222 else if (*aptr == RB) {
2227 else if (*aptr == QU) {
2232 else if ((*aptr == 32) && (nbsp == 1)) {
2233 memcpy(bptr, " ", 6);
2235 Target->BufUsed += 6;
2237 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2238 *bptr='\0'; /* nothing */
2240 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2241 memcpy(bptr, "<br/>", 11);
2243 Target->BufUsed += 11;
2247 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2248 *bptr='\0'; /* nothing */
2258 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2260 return Target->BufUsed;
2264 * @ingroup StrBuf_DeEnCoder
2265 * @brief Append a string, escaping characters which have meaning in HTML.
2266 * Converts linebreaks into blanks; escapes single quotes
2267 * @param Target target buffer
2268 * @param Source source buffer; set to NULL if you just have a C-String
2269 * @param PlainIn Plain-C string to append; set to NULL if unused
2271 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2273 const char *aptr, *eiptr;
2277 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2280 if (PlainIn != NULL) {
2282 len = strlen(PlainIn);
2287 eiptr = aptr + Source->BufUsed;
2288 len = Source->BufUsed;
2294 eptr = Target->buf + Target->BufSize - 8;
2295 tptr = Target->buf + Target->BufUsed;
2297 while (aptr < eiptr){
2299 IncreaseBuf(Target, 1, -1);
2300 eptr = Target->buf + Target->BufSize - 8;
2301 tptr = Target->buf + Target->BufUsed;
2304 if (*aptr == '\n') {
2308 else if (*aptr == '\r') {
2312 else if (*aptr == '\'') {
2318 Target->BufUsed += 5;
2331 * @ingroup StrBuf_DeEnCoder
2332 * @brief Append a string, escaping characters which have meaning in ICAL.
2334 * @param Target target buffer
2335 * @param Source source buffer; set to NULL if you just have a C-String
2336 * @param PlainIn Plain-C string to append; set to NULL if unused
2338 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2340 const char *aptr, *eiptr;
2344 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2347 if (PlainIn != NULL) {
2349 len = strlen(PlainIn);
2354 eiptr = aptr + Source->BufUsed;
2355 len = Source->BufUsed;
2361 eptr = Target->buf + Target->BufSize - 8;
2362 tptr = Target->buf + Target->BufUsed;
2364 while (aptr < eiptr){
2365 if(tptr + 3 >= eptr) {
2366 IncreaseBuf(Target, 1, -1);
2367 eptr = Target->buf + Target->BufSize - 8;
2368 tptr = Target->buf + Target->BufUsed;
2371 if (*aptr == '\n') {
2378 else if (*aptr == '\r') {
2385 else if (*aptr == ',') {
2401 * @ingroup StrBuf_DeEnCoder
2402 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2404 * @param Target target buffer
2405 * @param Source source buffer; set to NULL if you just have a C-String
2406 * @param PlainIn Plain-C string to append; set to NULL if unused
2407 * @returns size of result or -1
2409 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2411 const char *aptr, *eiptr;
2416 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2419 if (PlainIn != NULL) {
2421 len = strlen(PlainIn);
2426 eiptr = aptr + Source->BufUsed;
2427 len = Source->BufUsed;
2433 bptr = Target->buf + Target->BufUsed;
2434 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2436 while (aptr < eiptr){
2438 IncreaseBuf(Target, 1, -1);
2439 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2440 bptr = Target->buf + Target->BufUsed;
2444 memcpy(bptr, HKEY("\\n"));
2446 Target->BufUsed += 2;
2449 memcpy(bptr, HKEY("\\r"));
2451 Target->BufUsed += 2;
2458 Target->BufUsed += 2;
2461 if ((*(aptr + 1) == 'u') &&
2462 isxdigit(*(aptr + 2)) &&
2463 isxdigit(*(aptr + 3)) &&
2464 isxdigit(*(aptr + 4)) &&
2465 isxdigit(*(aptr + 5)))
2466 { /* oh, a unicode escaper. let it pass through. */
2467 memcpy(bptr, aptr, 6);
2470 Target->BufUsed += 6;
2478 Target->BufUsed += 2;
2486 Target->BufUsed += 2;
2493 Target->BufUsed += 2;
2500 Target->BufUsed += 2;
2503 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2504 while (IsUtf8Sequence > 0){
2507 if (--IsUtf8Sequence)
2515 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2517 return Target->BufUsed;
2521 * @ingroup StrBuf_DeEnCoder
2522 * @brief Append a string, escaping characters which have meaning in HTML + json.
2524 * @param Target target buffer
2525 * @param Source source buffer; set to NULL if you just have a C-String
2526 * @param PlainIn Plain-C string to append; set to NULL if unused
2527 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2528 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2529 * if set to 2, linebreaks are replaced by <br/>
2531 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2533 const char *aptr, *eiptr;
2536 int IsUtf8Sequence = 0;
2538 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2541 if (PlainIn != NULL) {
2543 len = strlen(PlainIn);
2548 eiptr = aptr + Source->BufUsed;
2549 len = Source->BufUsed;
2555 bptr = Target->buf + Target->BufUsed;
2556 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2558 while (aptr < eiptr){
2560 IncreaseBuf(Target, 1, -1);
2561 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2562 bptr = Target->buf + Target->BufUsed;
2566 memcpy(bptr, HKEY("<"));
2568 Target->BufUsed += 4;
2571 memcpy(bptr, HKEY(">"));
2573 Target->BufUsed += 4;
2576 memcpy(bptr, HKEY("&"));
2578 Target->BufUsed += 5;
2591 switch (nolinebreaks) {
2593 *bptr='\0'; /* nothing */
2596 memcpy(bptr, HKEY("<br/>"));
2598 Target->BufUsed += 11;
2601 memcpy(bptr, HKEY("\\n"));
2603 Target->BufUsed += 2;
2607 switch (nolinebreaks) {
2610 *bptr='\0'; /* nothing */
2613 memcpy(bptr, HKEY("\\r"));
2615 Target->BufUsed += 2;
2625 Target->BufUsed += 2;
2628 if ((*(aptr + 1) == 'u') &&
2629 isxdigit(*(aptr + 2)) &&
2630 isxdigit(*(aptr + 3)) &&
2631 isxdigit(*(aptr + 4)) &&
2632 isxdigit(*(aptr + 5)))
2633 { /* oh, a unicode escaper. let it pass through. */
2634 memcpy(bptr, aptr, 6);
2637 Target->BufUsed += 6;
2645 Target->BufUsed += 2;
2653 Target->BufUsed += 2;
2660 Target->BufUsed += 2;
2667 Target->BufUsed += 2;
2671 memcpy(bptr, HKEY(" "));
2673 Target->BufUsed += 6;
2677 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2678 while (IsUtf8Sequence > 0){
2681 if (--IsUtf8Sequence)
2689 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2691 return Target->BufUsed;
2696 * @ingroup StrBuf_DeEnCoder
2697 * @brief replace all non-Ascii characters by another
2698 * @param Buf buffer to inspect
2699 * @param repl charater to stamp over non ascii chars
2701 void StrBufAsciify(StrBuf *Buf, const char repl)
2705 for (offset = 0; offset < Buf->BufUsed; offset ++)
2706 if (!isascii(Buf->buf[offset]))
2707 Buf->buf[offset] = repl;
2712 * @ingroup StrBuf_DeEnCoder
2713 * @brief unhide special chars hidden to the HTML escaper
2714 * @param target buffer to put the unescaped string in
2715 * @param source buffer to unescape
2717 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2722 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2728 FlushStrBuf(target);
2730 len = source->BufUsed;
2731 for (a = 0; a < len; ++a) {
2732 if (target->BufUsed >= target->BufSize)
2733 IncreaseBuf(target, 1, -1);
2735 if (source->buf[a] == '=') {
2736 hex[0] = source->buf[a + 1];
2737 hex[1] = source->buf[a + 2];
2740 sscanf(hex, "%02x", &b);
2741 target->buf[target->BufUsed] = b;
2742 target->buf[++target->BufUsed] = 0;
2746 target->buf[target->BufUsed] = source->buf[a];
2747 target->buf[++target->BufUsed] = 0;
2754 * @ingroup StrBuf_DeEnCoder
2755 * @brief hide special chars from the HTML escapers and friends
2756 * @param target buffer to put the escaped string in
2757 * @param source buffer to escape
2759 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2764 FlushStrBuf(target);
2766 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2771 len = source->BufUsed;
2772 for (i=0; i<len; ++i) {
2773 if (target->BufUsed + 4 >= target->BufSize)
2774 IncreaseBuf(target, 1, -1);
2775 if ( (isalnum(source->buf[i])) ||
2776 (source->buf[i]=='-') ||
2777 (source->buf[i]=='_') ) {
2778 target->buf[target->BufUsed++] = source->buf[i];
2781 sprintf(&target->buf[target->BufUsed],
2783 (0xFF &source->buf[i]));
2784 target->BufUsed += 3;
2787 target->buf[target->BufUsed + 1] = '\0';
2791 /*******************************************************************************
2792 * Quoted Printable de/encoding *
2793 *******************************************************************************/
2796 * @ingroup StrBuf_DeEnCoder
2797 * @brief decode a buffer from base 64 encoding; destroys original
2798 * @param Buf Buffor to transform
2800 int StrBufDecodeBase64(StrBuf *Buf)
2808 xferbuf = (char*) malloc(Buf->BufSize);
2809 if (xferbuf == NULL)
2813 siz = CtdlDecodeBase64(xferbuf,
2820 Buf->buf[Buf->BufUsed] = '\0';
2825 * @ingroup StrBuf_DeEnCoder
2826 * @brief decode a buffer from base 64 encoding; expects targetbuffer
2827 * @param BufIn Buffor to transform
2828 * @param BufOut Buffer to put result into
2830 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2832 if ((BufIn == NULL) || (BufOut == NULL))
2835 if (BufOut->BufSize < BufIn->BufUsed)
2836 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2838 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2841 return BufOut->BufUsed;
2844 typedef struct __z_enc_stream {
2849 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err)
2851 base64_decodestate *state;;
2858 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2859 base64_init_decodestate(state);
2860 return (vStreamT*) state;
2865 z_enc_stream *stream;
2868 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2869 memset(stream, 0, sizeof(z_enc_stream));
2870 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2871 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2873 err = inflateInit(&stream->zstream);
2876 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2880 return (vStreamT*) stream;
2885 z_enc_stream *stream;
2888 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2889 memset(stream, 0, sizeof(z_enc_stream));
2890 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2891 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2892 /* write gzip header */
2893 stream->OutBuf.BufUsed = snprintf
2894 (stream->OutBuf.buf,
2895 stream->OutBuf.BufSize,
2896 "%c%c%c%c%c%c%c%c%c%c",
2897 gz_magic[0], gz_magic[1], Z_DEFLATED,
2898 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2901 err = deflateInit2(&stream->zstream,
2902 ZLibCompressionRatio,
2906 Z_DEFAULT_STRATEGY);
2908 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2912 return (vStreamT*) stream;
2922 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err)
2928 if ((vStream == NULL) || (*vStream==NULL)) {
2929 *Err = strerror(EINVAL);
2941 z_enc_stream *stream = (z_enc_stream *)*vStream;
2942 (void)inflateEnd(&stream->zstream);
2943 free(stream->OutBuf.buf);
2948 z_enc_stream *stream = (z_enc_stream *)*vStream;
2949 err = deflateEnd(&stream->zstream);
2954 free(stream->OutBuf.buf);
2965 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err)
2974 // base64_decodestate *state = (base64_decodestate*)vStream;
2980 pInLen = In->BufUsed;
2982 if ((In == NULL) || (vStream == NULL))
2985 ExpectLen = (pInLen / 4) * 3;
2987 if (Target->BufSize - Target->BufUsed < ExpectLen)
2989 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2992 //// ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2993 Target->BufUsed += ExpectLen;
2994 Target->buf[Target->BufUsed] = '\0';
3001 base64_decodestate *state = (base64_decodestate *)vStream;
3007 pInLen = In->BufUsed;
3009 if ((pIn == NULL) || (vStream == NULL))
3012 ExpectLen = (pInLen / 4) * 3;
3014 if (Target->BufSize - Target->BufUsed < ExpectLen)
3016 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3019 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3020 Target->BufUsed += ExpectLen;
3021 Target->buf[Target->BufUsed] = '\0';
3027 z_enc_stream *stream = (z_enc_stream *)vStream;
3028 int org_outbuf_len = stream->OutBuf.BufUsed;
3030 unsigned int chunkavail;
3032 if (In->ReadWritePointer != NULL)
3034 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3035 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3036 (In->ReadWritePointer - In->Buf->buf);
3040 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3041 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3044 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3045 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3047 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3049 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3053 (stream->OutBuf.BufUsed != org_outbuf_len)
3056 iSwapBuffers(Target->Buf, &stream->OutBuf);
3059 if (stream->zstream.avail_in == 0)
3061 FlushStrBuf(In->Buf);
3062 In->ReadWritePointer = NULL;
3066 if (stream->zstream.avail_in < 64)
3068 memmove(In->Buf->buf,
3069 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3070 stream->zstream.avail_in);
3072 In->Buf->BufUsed = stream->zstream.avail_in;
3073 In->Buf->buf[In->Buf->BufUsed] = '\0';
3078 In->ReadWritePointer = In->Buf->buf +
3079 (In->Buf->BufUsed - stream->zstream.avail_in);
3082 rc = (LastChunk && (err != Z_FINISH));
3083 if (!rc && (err != Z_OK)) {
3090 z_enc_stream *stream = (z_enc_stream *)vStream;
3091 int org_outbuf_len = stream->zstream.total_out;
3094 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3095 if (In->ReadWritePointer != NULL)
3097 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3098 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3099 (In->ReadWritePointer - In->Buf->buf);
3103 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3104 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3108 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3109 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3111 err = inflate(&stream->zstream, Z_NO_FLUSH);
3113 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
3116 err = Z_DATA_ERROR; /* and fall through */
3121 (void)inflateEnd(&stream->zstream);
3125 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3127 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
3129 if (stream->zstream.avail_in == 0)
3131 FlushStrBuf(In->Buf);
3132 In->ReadWritePointer = NULL;
3136 if (stream->zstream.avail_in < 64)
3138 memmove(In->Buf->buf,
3139 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3140 stream->zstream.avail_in);
3142 In->Buf->BufUsed = stream->zstream.avail_in;
3143 In->Buf->buf[In->Buf->BufUsed] = '\0';
3148 In->ReadWritePointer = In->Buf->buf +
3149 (In->Buf->BufUsed - stream->zstream.avail_in);
3163 * @ingroup StrBuf_DeEnCoder
3164 * @brief decode a buffer from base 64 encoding; destroys original
3165 * @param Buf Buffor to transform
3167 int StrBufDecodeHex(StrBuf *Buf)
3170 char *pch, *pche, *pchi;
3172 if (Buf == NULL) return -1;
3174 pch = pchi = Buf->buf;
3175 pche = pch + Buf->BufUsed;
3177 while (pchi < pche){
3178 ch = decode_hex(pchi);
3185 Buf->BufUsed = pch - Buf->buf;
3186 return Buf->BufUsed;
3190 * @ingroup StrBuf_DeEnCoder
3191 * @brief replace all chars >0x20 && < 0x7F with Mute
3192 * @param Mute char to put over invalid chars
3193 * @param Buf Buffor to transform
3195 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3199 if (Buf == NULL) return -1;
3200 pch = (unsigned char *)Buf->buf;
3201 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3202 if ((*pch < 0x20) || (*pch > 0x7F))
3206 return Buf->BufUsed;
3211 * @ingroup StrBuf_DeEnCoder
3212 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3213 * @param Buf Buffer to translate
3214 * @param StripBlanks Reduce several blanks to one?
3216 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3225 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3226 Buf->buf[Buf->BufUsed - 1] = '\0';
3231 while (a < Buf->BufUsed) {
3232 if (Buf->buf[a] == '+')
3234 else if (Buf->buf[a] == '%') {
3235 /* don't let % chars through, rather truncate the input. */
3236 if (a + 2 > Buf->BufUsed) {
3241 hex[0] = Buf->buf[a + 1];
3242 hex[1] = Buf->buf[a + 2];
3245 sscanf(hex, "%02x", &b);
3246 Buf->buf[a] = (char) b;
3247 len = Buf->BufUsed - a - 2;
3249 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3261 * @ingroup StrBuf_DeEnCoder
3262 * @brief RFC2047-encode a header field if necessary.
3263 * If no non-ASCII characters are found, the string
3264 * will be copied verbatim without encoding.
3266 * @param target Target buffer.
3267 * @param source Source string to be encoded.
3268 * @returns encoded length; -1 if non success.
3270 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3272 const char headerStr[] = "=?UTF-8?Q?";
3273 int need_to_encode = 0;
3277 if ((source == NULL) ||
3281 while ((i < source->BufUsed) &&
3282 (!IsEmptyStr (&source->buf[i])) &&
3283 (need_to_encode == 0)) {
3284 if (((unsigned char) source->buf[i] < 32) ||
3285 ((unsigned char) source->buf[i] > 126)) {
3291 if (!need_to_encode) {
3292 if (*target == NULL) {
3293 *target = NewStrBufPlain(source->buf, source->BufUsed);
3296 FlushStrBuf(*target);
3297 StrBufAppendBuf(*target, source, 0);
3300 return (*target)->BufUsed;
3304 if (*target == NULL)
3305 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3306 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3307 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3308 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3309 (*target)->BufUsed = sizeof(headerStr) - 1;
3310 for (i=0; (i < source->BufUsed); ++i) {
3311 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3312 IncreaseBuf(*target, 1, 0);
3313 ch = (unsigned char) source->buf[i];
3322 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3323 (*target)->BufUsed += 3;
3327 (*target)->buf[(*target)->BufUsed] = '_';
3329 (*target)->buf[(*target)->BufUsed] = ch;
3330 (*target)->BufUsed++;
3334 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3335 IncreaseBuf(*target, 1, 0);
3337 (*target)->buf[(*target)->BufUsed++] = '?';
3338 (*target)->buf[(*target)->BufUsed++] = '=';
3339 (*target)->buf[(*target)->BufUsed] = '\0';
3340 return (*target)->BufUsed;;
3344 * @ingroup StrBuf_DeEnCoder
3345 * @brief Quoted-Printable encode a message; make it < 80 columns width.
3346 * @param source Source string to be encoded.
3347 * @returns buffer with encoded message.
3349 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3353 const char *ptr, *eptr;
3357 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3359 OEptr = OutBuf->buf + OutBuf->BufSize;
3360 ptr = EncodeMe->buf;
3361 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3366 if (Optr + 4 >= OEptr)
3369 Offset = Optr - OutBuf->buf;
3370 OutBuf->BufUsed = Optr - OutBuf->buf;
3371 IncreaseBuf(OutBuf, 1, 0);
3372 Optr = OutBuf->buf + Offset;
3373 OEptr = OutBuf->buf + OutBuf->BufSize;
3377 /* ignore carriage returns */
3380 else if (*ptr == '\n') {
3381 /* hard line break */
3382 memcpy(Optr, HKEY("=0A"));
3387 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3388 ( (*ptr >= 62) && (*ptr <= 126) ))
3399 *Optr = HexList[ch][0];
3401 *Optr = HexList[ch][1];
3408 /* soft line break */
3409 if (isspace(*(Optr - 1))) {
3414 *Optr = HexList[ch][0];
3416 *Optr = HexList[ch][1];
3428 OutBuf->BufUsed = Optr - OutBuf->buf;
3434 static void AddRecipient(StrBuf *Target,
3436 StrBuf *EmailAddress,
3441 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3442 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3444 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3445 StrBufRFC2047encode(&EncBuf, UserName);
3446 StrBufAppendBuf(Target, EncBuf, 0);
3447 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3448 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3450 if (StrLength(EmailAddress) > 0){
3451 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3452 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3453 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3459 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3460 * \param Recp Source list of email recipients
3461 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3462 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3463 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3464 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3466 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3468 StrBuf *EmailAddress,
3472 const char *pch, *pche;
3473 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3475 if ((Recp == NULL) || (StrLength(Recp) == 0))
3479 pche = pch + StrLength(Recp);
3481 if (!CheckEncode(pch, -1, pche))
3482 return NewStrBufDup(Recp);
3484 Target = NewStrBufPlain(NULL, StrLength(Recp));
3486 while ((pch != NULL) && (pch < pche))
3488 while (isspace(*pch)) pch++;
3489 UserEnd = EmailStart = EmailEnd = NULL;
3491 if ((*pch == '"') || (*pch == '\'')) {
3492 UserStart = pch + 1;
3494 UserEnd = strchr(UserStart, *pch);
3495 if (UserEnd == NULL)
3496 break; ///TODO: Userfeedback??
3497 EmailStart = UserEnd + 1;
3498 while (isspace(*EmailStart))
3500 if (UserEnd == UserStart) {
3501 UserStart = UserEnd = NULL;
3504 if (*EmailStart == '<') {
3506 EmailEnd = strchr(EmailStart, '>');
3507 if (EmailEnd == NULL)
3508 EmailEnd = strchr(EmailStart, ',');
3512 EmailEnd = strchr(EmailStart, ',');
3514 if (EmailEnd == NULL)
3521 EmailEnd = strchr(UserStart, ',');
3522 if (EmailEnd == NULL) {
3523 EmailEnd = strchr(pch, '>');
3525 if (EmailEnd != NULL) {
3535 while ((EmailEnd > UserStart) && !gt &&
3536 ((*EmailEnd == ',') ||
3537 (*EmailEnd == '>') ||
3538 (isspace(*EmailEnd))))
3540 if (*EmailEnd == '>')
3545 if (EmailEnd == UserStart)
3549 EmailStart = strchr(UserStart, '<');
3550 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3552 UserEnd = EmailStart;
3554 while ((UserEnd > UserStart) &&
3555 isspace (*(UserEnd - 1)))
3558 if (UserStart >= UserEnd)
3559 UserStart = UserEnd = NULL;
3561 else { /* this is a local recipient... no domain, just a realname */
3562 EmailStart = UserStart;
3563 At = strchr(EmailStart, '@');
3569 EmailStart = UserStart;
3575 if ((UserStart != NULL) && (UserEnd != NULL))
3576 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3577 else if ((UserStart != NULL) && (UserEnd == NULL))
3578 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3580 FlushStrBuf(UserName);
3582 if ((EmailStart != NULL) && (EmailEnd != NULL))
3583 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3584 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3585 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3587 FlushStrBuf(EmailAddress);
3589 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3594 if ((pch != NULL) && (*pch == ','))
3596 if (pch != NULL) while (isspace(*pch))
3605 * @brief replaces all occurances of 'search' by 'replace'
3606 * @param buf Buffer to modify
3607 * @param search character to search
3608 * @param replace character to replace search by
3610 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3615 for (i=0; i<buf->BufUsed; i++)
3616 if (buf->buf[i] == search)
3617 buf->buf[i] = replace;
3623 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3624 * @param buf Buffer to modify
3626 void StrBufToUnixLF(StrBuf *buf)
3628 char *pche, *pchS, *pchT;
3632 pche = buf->buf + buf->BufUsed;
3633 pchS = pchT = buf->buf;
3639 if (*pchS != '\n') {
3648 buf->BufUsed = pchT - buf->buf;
3652 /*******************************************************************************
3653 * Iconv Wrapper; RFC822 de/encoding *
3654 *******************************************************************************/
3657 * @ingroup StrBuf_DeEnCoder
3658 * @brief Wrapper around iconv_open()
3659 * Our version adds aliases for non-standard Microsoft charsets
3660 * such as 'MS950', aliasing them to names like 'CP950'
3662 * @param tocode Target encoding
3663 * @param fromcode Source encoding
3664 * @param pic anonimized pointer to iconv struct
3666 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3669 iconv_t ic = (iconv_t)(-1) ;
3670 ic = iconv_open(tocode, fromcode);
3671 if (ic == (iconv_t)(-1) ) {
3672 char alias_fromcode[64];
3673 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3674 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3675 alias_fromcode[0] = 'C';
3676 alias_fromcode[1] = 'P';
3677 ic = iconv_open(tocode, alias_fromcode);
3680 *(iconv_t *)pic = ic;
3686 * @ingroup StrBuf_DeEnCoder
3687 * @brief find one chunk of a RFC822 encoded string
3688 * @param Buffer where to search
3689 * @param bptr where to start searching
3690 * @returns found position, NULL if none.
3692 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3695 /* Find the next ?Q? */
3696 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3699 end = strchr(bptr + 2, '?');
3704 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3705 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3706 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3707 (*(end + 2) == '?')) {
3708 /* skip on to the end of the cluster, the next ?= */
3709 end = strstr(end + 3, "?=");
3712 /* sort of half valid encoding, try to find an end. */
3713 end = strstr(bptr, "?=");
3720 * @ingroup StrBuf_DeEnCoder
3721 * @brief convert one buffer according to the preselected iconv pointer PIC
3722 * @param ConvertBuf buffer we need to translate
3723 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3724 * @param pic Pointer to the iconv-session Object
3726 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3732 char *ibuf; /**< Buffer of characters to be converted */
3733 char *obuf; /**< Buffer for converted characters */
3734 size_t ibuflen; /**< Length of input buffer */
3735 size_t obuflen; /**< Length of output buffer */
3738 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3741 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3742 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3743 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3745 ic = *(iconv_t*)pic;
3746 ibuf = ConvertBuf->buf;
3747 ibuflen = ConvertBuf->BufUsed;
3749 obuflen = TmpBuf->BufSize;
3751 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3754 if (errno == E2BIG) {
3756 IncreaseBuf(TmpBuf, 0, 0);
3761 else if (errno == EILSEQ){
3762 /* hm, invalid utf8 sequence... what to do now? */
3763 /* An invalid multibyte sequence has been encountered in the input */
3765 else if (errno == EINVAL) {
3766 /* An incomplete multibyte sequence has been encountered in the input. */
3769 FlushStrBuf(TmpBuf);
3772 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3773 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3775 /* little card game: wheres the red lady? */
3776 iSwapBuffers(ConvertBuf, TmpBuf);
3777 FlushStrBuf(TmpBuf);
3784 * @ingroup StrBuf_DeEnCoder
3785 * @brief catches one RFC822 encoded segment, and decodes it.
3786 * @param Target buffer to fill with result
3787 * @param DecodeMe buffer with stuff to process
3788 * @param SegmentStart points to our current segment in DecodeMe
3789 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3790 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3791 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3792 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3794 inline static void DecodeSegment(StrBuf *Target,
3795 const StrBuf *DecodeMe,
3796 const char *SegmentStart,
3797 const char *SegmentEnd,
3799 StrBuf *ConvertBuf2,
3800 StrBuf *FoundCharset)
3806 iconv_t ic = (iconv_t)(-1);
3810 /* Now we handle foreign character sets properly encoded
3811 * in RFC2047 format.
3813 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3814 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3815 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3816 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3817 if (FoundCharset != NULL) {
3818 FlushStrBuf(FoundCharset);
3819 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3821 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3822 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3824 *encoding = toupper(*encoding);
3825 if (*encoding == 'B') { /**< base64 */
3826 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3827 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3828 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3830 ConvertBuf->BufUsed);
3832 else if (*encoding == 'Q') { /**< quoted-printable */
3836 while (pos < ConvertBuf->BufUsed)
3838 if (ConvertBuf->buf[pos] == '_')
3839 ConvertBuf->buf[pos] = ' ';
3843 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3844 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3846 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3849 ConvertBuf->BufUsed);
3852 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3855 ctdl_iconv_open("UTF-8", charset, &ic);
3856 if (ic != (iconv_t)(-1) ) {
3858 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3859 StrBufAppendBuf(Target, ConvertBuf2, 0);
3864 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3870 * @ingroup StrBuf_DeEnCoder
3871 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3872 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3873 * @param Target where to put the decoded string to
3874 * @param DecodeMe buffer with encoded string
3875 * @param DefaultCharset if we don't find one, which should we use?
3876 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3877 * put it here for later use where no string might be known.
3879 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3882 StrBuf *ConvertBuf2;
3883 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3884 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3886 StrBuf_RFC822_2_Utf8(Target,
3892 FreeStrBuf(&ConvertBuf);
3893 FreeStrBuf(&ConvertBuf2);
3897 * @ingroup StrBuf_DeEnCoder
3898 * @brief Handle subjects with RFC2047 encoding such as:
3899 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3900 * @param Target where to put the decoded string to
3901 * @param DecodeMe buffer with encoded string
3902 * @param DefaultCharset if we don't find one, which should we use?
3903 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3904 * put it here for later use where no string might be known.
3905 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3906 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3908 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3909 const StrBuf *DecodeMe,
3910 const StrBuf* DefaultCharset,
3911 StrBuf *FoundCharset,
3913 StrBuf *ConvertBuf2)
3915 StrBuf *DecodedInvalidBuf = NULL;
3916 const StrBuf *DecodeMee = DecodeMe;
3917 const char *start, *end, *next, *nextend, *ptr = NULL;
3919 iconv_t ic = (iconv_t)(-1) ;
3924 int illegal_non_rfc2047_encoding = 0;
3927 if (DecodeMe == NULL)
3929 /* Sometimes, badly formed messages contain strings which were simply
3930 * written out directly in some foreign character set instead of
3931 * using RFC2047 encoding. This is illegal but we will attempt to
3932 * handle it anyway by converting from a user-specified default
3933 * charset to UTF-8 if we see any nonprintable characters.
3936 for (i=0; i<DecodeMe->BufUsed; ++i) {
3937 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3938 illegal_non_rfc2047_encoding = 1;
3943 if ((illegal_non_rfc2047_encoding) &&
3944 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3945 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3948 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3949 if (ic != (iconv_t)(-1) ) {
3950 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3951 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3952 DecodeMee = DecodedInvalidBuf;
3958 /* pre evaluate the first pair */
3960 start = strstr(DecodeMee->buf, "=?");
3961 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3963 end = FindNextEnd (DecodeMee, start + 2);
3965 StrBufAppendBuf(Target, DecodeMee, 0);
3966 FreeStrBuf(&DecodedInvalidBuf);
3971 if (start != DecodeMee->buf) {
3974 nFront = start - DecodeMee->buf;
3975 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3978 * Since spammers will go to all sorts of absurd lengths to get their
3979 * messages through, there are LOTS of corrupt headers out there.
3980 * So, prevent a really badly formed RFC2047 header from throwing
3981 * this function into an infinite loop.
3983 while ((start != NULL) &&
3990 DecodeSegment(Target,
3998 next = strstr(end, "=?");
4000 if ((next != NULL) &&
4002 nextend = FindNextEnd(DecodeMee, next);
4003 if (nextend == NULL)
4006 /* did we find two partitions */
4007 if ((next != NULL) &&
4011 while ((ptr < next) &&
4018 * did we find a gab just filled with blanks?
4019 * if not, copy its stuff over.
4023 StrBufAppendBufPlain(Target,
4029 /* our next-pair is our new first pair now. */
4035 nextend = DecodeMee->buf + DecodeMee->BufUsed;
4036 if ((end != NULL) && (end < nextend)) {
4038 while ( (ptr < nextend) &&
4045 StrBufAppendBufPlain(Target, end, nextend - end, 0);
4047 FreeStrBuf(&DecodedInvalidBuf);
4050 /*******************************************************************************
4051 * Manipulating UTF-8 Strings *
4052 *******************************************************************************/
4056 * @brief evaluate the length of an utf8 special character sequence
4057 * @param Char the character to examine
4058 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4060 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4063 unsigned char test = (1<<7);
4065 if ((*CharS & 0xC0) != 0xC0)
4069 ((test & ((unsigned char)*CharS)) != 0))
4074 if ((n > 6) || ((CharE - CharS) < n))
4081 * @brief detect whether this char starts an utf-8 encoded char
4082 * @param Char character to inspect
4083 * @returns yes or no
4085 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4087 /** 11??.???? indicates an UTF8 Sequence. */
4088 return ((Char & 0xC0) == 0xC0);
4093 * @brief measure the number of glyphs in an UTF8 string...
4094 * @param Buf string to measure
4095 * @returns the number of glyphs in Buf
4097 long StrBuf_Utf8StrLen(StrBuf *Buf)
4103 if ((Buf == NULL) || (Buf->BufUsed == 0))
4106 eptr = Buf->buf + Buf->BufUsed;
4107 while ((aptr < eptr) && (*aptr != '\0')) {
4108 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4109 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4110 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4123 * @brief cuts a string after maxlen glyphs
4124 * @param Buf string to cut to maxlen glyphs
4125 * @param maxlen how long may the string become?
4126 * @returns current length of the string
4128 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4134 eptr = Buf->buf + Buf->BufUsed;
4135 while ((aptr < eptr) && (*aptr != '\0')) {
4136 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4137 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4138 while ((*aptr++ != '\0') && (m-- > 0));
4147 Buf->BufUsed = aptr - Buf->buf;
4148 return Buf->BufUsed;
4151 return Buf->BufUsed;
4159 /*******************************************************************************
4161 *******************************************************************************/
4164 * @ingroup StrBuf_DeEnCoder
4165 * @brief uses the same calling syntax as compress2(), but it
4166 * creates a stream compatible with HTTP "Content-encoding: gzip"
4167 * @param dest compressed buffer
4168 * @param destLen length of the compresed data
4169 * @param source source to encode
4170 * @param sourceLen length of source to encode
4171 * @param level compression level
4174 int ZEXPORT compress_gzip(Bytef * dest,
4176 const Bytef * source,
4180 /* write gzip header */
4181 snprintf((char *) dest, *destLen,
4182 "%c%c%c%c%c%c%c%c%c%c",
4183 gz_magic[0], gz_magic[1], Z_DEFLATED,
4184 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4187 /* normal deflate */
4190 stream.next_in = (Bytef *) source;
4191 stream.avail_in = (uInt) sourceLen;
4192 stream.next_out = dest + 10L; // after header
4193 stream.avail_out = (uInt) * destLen;
4194 if ((uLong) stream.avail_out != *destLen)
4197 stream.zalloc = (alloc_func) 0;
4198 stream.zfree = (free_func) 0;
4199 stream.opaque = (voidpf) 0;
4201 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4202 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4206 err = deflate(&stream, Z_FINISH);
4207 if (err != Z_STREAM_END) {
4208 deflateEnd(&stream);
4209 return err == Z_OK ? Z_BUF_ERROR : err;
4211 *destLen = stream.total_out + 10L;
4213 /* write CRC and Length */
4214 uLong crc = crc32(0L, source, sourceLen);
4216 for (n = 0; n < 4; ++n, ++*destLen) {
4217 dest[*destLen] = (int) (crc & 0xff);
4220 uLong len = stream.total_in;
4221 for (n = 0; n < 4; ++n, ++*destLen) {
4222 dest[*destLen] = (int) (len & 0xff);
4225 err = deflateEnd(&stream);
4232 * @ingroup StrBuf_DeEnCoder
4233 * @brief compress the buffer with gzip
4234 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4235 * @param Buf buffer whose content is to be gzipped
4237 int CompressBuffer(StrBuf *Buf)
4240 char *compressed_data = NULL;
4241 size_t compressed_len, bufsize;
4244 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
4245 compressed_data = malloc(compressed_len);
4247 if (compressed_data == NULL)
4249 /* Flush some space after the used payload so valgrind shuts up... */
4250 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4251 Buf->buf[Buf->BufUsed + i++] = '\0';
4252 if (compress_gzip((Bytef *) compressed_data,
4255 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4258 Buf->buf = compressed_data;
4259 Buf->BufUsed = compressed_len;
4260 Buf->BufSize = bufsize;
4261 /* Flush some space after the used payload so valgrind shuts up... */
4263 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4264 Buf->buf[Buf->BufUsed + i++] = '\0';
4267 free(compressed_data);
4269 #endif /* HAVE_ZLIB */
4273 /*******************************************************************************
4274 * File I/O; Callbacks to libevent *
4275 *******************************************************************************/
4277 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4282 if ((FB == NULL) || (FB->Buf == NULL))
4286 * check whether the read pointer is somewhere in a range
4287 * where a cut left is inexpensive
4290 if (FB->ReadWritePointer != NULL)
4294 already_read = FB->ReadWritePointer - FB->Buf->buf;
4295 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4297 if (already_read != 0) {
4300 unread = FB->Buf->BufUsed - already_read;
4302 /* else nothing to compact... */
4304 FB->ReadWritePointer = FB->Buf->buf;
4305 bufremain = FB->Buf->BufSize;
4307 else if ((unread < 64) ||
4308 (bufremain < already_read))
4311 * if its just a tiny bit remaining, or we run out of space...
4314 FB->Buf->BufUsed = unread;
4315 if (unread < already_read)
4316 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4318 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4319 FB->ReadWritePointer = FB->Buf->buf;
4320 bufremain = FB->Buf->BufSize - unread - 1;
4322 else if (bufremain < (FB->Buf->BufSize / 10))
4324 /* get a bigger buffer */
4326 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4328 FB->ReadWritePointer = FB->Buf->buf + unread;
4330 bufremain = FB->Buf->BufSize - unread - 1;
4331 /*TODO: special increase function that won't copy the already read! */
4334 else if (bufremain < 10) {
4335 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4337 FB->ReadWritePointer = FB->Buf->buf;
4339 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4344 FB->ReadWritePointer = FB->Buf->buf;
4345 bufremain = FB->Buf->BufSize - 1;
4348 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4351 FB->Buf->BufUsed += n;
4352 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4357 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4362 if ((FB == NULL) || (FB->Buf == NULL))
4365 if (FB->ReadWritePointer != NULL)
4367 WriteRemain = FB->Buf->BufUsed -
4368 (FB->ReadWritePointer -
4372 FB->ReadWritePointer = FB->Buf->buf;
4373 WriteRemain = FB->Buf->BufUsed;
4376 n = write(fd, FB->ReadWritePointer, WriteRemain);
4378 FB->ReadWritePointer += n;
4380 if (FB->ReadWritePointer ==
4381 FB->Buf->buf + FB->Buf->BufUsed)
4383 FlushStrBuf(FB->Buf);
4384 FB->ReadWritePointer = NULL;
4387 // check whether we've got something to write
4388 // get the maximum chunk plus the pointer we can send
4389 // write whats there
4390 // if not all was sent, remember the send pointer for the next time
4391 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4397 * @ingroup StrBuf_IO
4398 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4399 * @param LineBuf your line will be copied here.
4400 * @param FB BLOB with lines of text...
4401 * @param Ptr moved arround to keep the next-line across several iterations
4402 * has to be &NULL on start; will be &NotNULL on end of buffer
4403 * @returns size of copied buffer
4405 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4407 const char *aptr, *ptr, *eptr;
4410 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4414 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4415 FB->ReadWritePointer = StrBufNOTNULL;
4419 FlushStrBuf(LineBuf);
4420 if (FB->ReadWritePointer == NULL)
4421 ptr = aptr = FB->Buf->buf;
4423 ptr = aptr = FB->ReadWritePointer;
4425 optr = LineBuf->buf;
4426 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4427 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4429 while ((ptr <= eptr) &&
4436 LineBuf->BufUsed = optr - LineBuf->buf;
4437 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4438 optr = LineBuf->buf + LineBuf->BufUsed;
4439 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4444 if (optr > LineBuf->buf)
4446 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4447 LineBuf->BufUsed = optr - LineBuf->buf;
4449 if ((FB->ReadWritePointer != NULL) &&
4450 (FB->ReadWritePointer != FB->Buf->buf))
4452 /* Ok, the client application read all the data
4453 it was interested in so far. Since there is more to read,
4454 we now shrink the buffer, and move the rest over.
4456 StrBufCutLeft(FB->Buf,
4457 FB->ReadWritePointer - FB->Buf->buf);
4458 FB->ReadWritePointer = FB->Buf->buf;
4460 return eMustReadMore;
4463 LineBuf->BufUsed = optr - LineBuf->buf;
4465 if ((ptr <= eptr) && (*ptr == '\r'))
4467 if ((ptr <= eptr) && (*ptr == '\n'))
4471 FB->ReadWritePointer = ptr;
4474 FlushStrBuf(FB->Buf);
4475 FB->ReadWritePointer = NULL;
4478 return eReadSuccess;
4482 * @ingroup StrBuf_CHUNKED_IO
4483 * @brief check whether the chunk-buffer has more data waiting or not.
4484 * @param FB Chunk-Buffer to inspect
4486 eReadState StrBufCheckBuffer(IOBuffer *FB)
4490 if (FB->Buf->BufUsed == 0)
4491 return eReadSuccess;
4492 if (FB->ReadWritePointer == NULL)
4493 return eBufferNotEmpty;
4494 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4495 return eBufferNotEmpty;
4496 return eReadSuccess;
4499 long IOBufferStrLength(IOBuffer *FB)
4501 if ((FB == NULL) || (FB->Buf == NULL))
4503 if (FB->ReadWritePointer == NULL)
4504 return StrLength(FB->Buf);
4506 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4510 /*******************************************************************************
4511 * File I/O; Prefer buffered read since its faster! *
4512 *******************************************************************************/
4515 * @ingroup StrBuf_IO
4516 * @brief Read a line from socket
4517 * flushes and closes the FD on error
4518 * @param buf the buffer to get the input to
4519 * @param fd pointer to the filedescriptor to read
4520 * @param append Append to an existing string or replace?
4521 * @param Error strerror() on error
4522 * @returns numbers of chars read
4524 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4526 int len, rlen, slen;
4528 if ((buf == NULL) || (buf->buf == NULL)) {
4529 *Error = strerror(EINVAL);
4536 slen = len = buf->BufUsed;
4538 rlen = read(*fd, &buf->buf[len], 1);
4540 *Error = strerror(errno);
4547 if (buf->buf[len] == '\n')
4549 if (buf->buf[len] != '\r')
4551 if (len + 2 >= buf->BufSize) {
4553 buf->buf[len+1] = '\0';
4554 IncreaseBuf(buf, 1, -1);
4558 buf->buf[len] = '\0';
4564 * @ingroup StrBuf_BufferedIO
4565 * @brief Read a line from socket
4566 * flushes and closes the FD on error
4567 * @param Line the line to read from the fd / I/O Buffer
4568 * @param buf the buffer to get the input to
4569 * @param fd pointer to the filedescriptor to read
4570 * @param timeout number of successless selects until we bail out
4571 * @param selectresolution how long to wait on each select
4572 * @param Error strerror() on error
4573 * @returns numbers of chars read
4575 int StrBufTCP_read_buffered_line(StrBuf *Line,
4579 int selectresolution,
4583 int nSuccessLess = 0;
4590 if (buf->BufUsed > 0) {
4591 pch = strchr(buf->buf, '\n');
4594 len = pch - buf->buf;
4595 if (len > 0 && (*(pch - 1) == '\r') )
4597 StrBufSub(Line, buf, 0, len - rlen);
4598 StrBufCutLeft(buf, len + 1);
4603 if (buf->BufSize - buf->BufUsed < 10)
4604 IncreaseBuf(buf, 1, -1);
4606 fdflags = fcntl(*fd, F_GETFL);
4607 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4609 while ((nSuccessLess < timeout) && (pch == NULL)) {
4611 tv.tv_sec = selectresolution;
4616 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4617 *Error = strerror(errno);
4623 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4628 &buf->buf[buf->BufUsed],
4629 buf->BufSize - buf->BufUsed - 1);
4631 *Error = strerror(errno);
4636 else if (rlen > 0) {
4638 buf->BufUsed += rlen;
4639 buf->buf[buf->BufUsed] = '\0';
4640 pch = strchr(buf->buf, '\n');
4641 if ((pch == NULL) &&
4642 (buf->BufUsed + 10 > buf->BufSize) &&
4643 (IncreaseBuf(buf, 1, -1) == -1))
4651 len = pch - buf->buf;
4652 if (len > 0 && (*(pch - 1) == '\r') )
4654 StrBufSub(Line, buf, 0, len - rlen);
4655 StrBufCutLeft(buf, len + 1);
4662 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4663 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4664 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4666 * @ingroup StrBuf_BufferedIO
4667 * @brief Read a line from socket
4668 * flushes and closes the FD on error
4669 * @param Line where to append our Line read from the fd / I/O Buffer;
4670 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4671 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4672 * @param fd pointer to the filedescriptor to read
4673 * @param timeout number of successless selects until we bail out
4674 * @param selectresolution how long to wait on each select
4675 * @param Error strerror() on error
4676 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4678 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4683 int selectresolution,
4686 const char *pche = NULL;
4687 const char *pos = NULL;
4689 int len, rlen, retlen;
4690 int nSuccessLess = 0;
4692 const char *pch = NULL;
4698 if ((Line == NULL) ||
4705 *Error = ErrRBLF_PreConditionFailed;
4710 if ((IOBuf->BufUsed > 0) &&
4712 (pos < IOBuf->buf + IOBuf->BufUsed))
4716 pche = IOBuf->buf + IOBuf->BufUsed;
4720 while ((pch < pche) && (*pch != '\n'))
4722 if (Line->BufUsed + 10 > Line->BufSize)
4725 apos = pcht - Line->buf;
4727 IncreaseBuf(Line, 1, -1);
4728 pcht = Line->buf + apos;
4736 if (len > 0 && (*(pch - 1) == '\r') )
4745 if ((pch >= pche) || (*pch == '\0'))
4753 if ((pch != NULL) &&
4756 if (pch + 1 >= pche) {
4769 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4771 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4772 IncreaseBuf(IOBuf, 1, -1);
4774 fdflags = fcntl(*fd, F_GETFL);
4775 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4778 while ((nSuccessLess < timeout) &&
4788 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4789 *Error = strerror(errno);
4793 *Error = ErrRBLF_SelectFailed;
4796 if (! FD_ISSET(*fd, &rfds) != 0) {
4802 &IOBuf->buf[IOBuf->BufUsed],
4803 IOBuf->BufSize - IOBuf->BufUsed - 1);
4805 *Error = strerror(errno);
4810 else if (rlen > 0) {
4812 pLF = IOBuf->buf + IOBuf->BufUsed;
4813 IOBuf->BufUsed += rlen;
4814 IOBuf->buf[IOBuf->BufUsed] = '\0';
4816 pche = IOBuf->buf + IOBuf->BufUsed;
4818 while ((pLF < pche) && (*pLF != '\n'))
4820 if ((pLF >= pche) || (*pLF == '\0'))
4823 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4827 if (pLF != NULL) apos = pLF - IOBuf->buf;
4828 IncreaseBuf(IOBuf, 1, -1);
4829 if (pLF != NULL) pLF = IOBuf->buf + apos;
4843 if (len > 0 && (*(pLF - 1) == '\r') )
4845 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4846 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4852 return retlen + len;
4854 *Error = ErrRBLF_NotEnoughSentFromServer;
4859 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4861 * @ingroup StrBuf_IO
4862 * @brief Input binary data from socket
4863 * flushes and closes the FD on error
4864 * @param Buf the buffer to get the input to
4865 * @param fd pointer to the filedescriptor to read
4866 * @param append Append to an existing string or replace?
4867 * @param nBytes the maximal number of bytes to read
4868 * @param Error strerror() on error
4869 * @returns numbers of chars read
4871 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4882 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4884 *Error = ErrRBLF_BLOBPreConditionFailed;
4889 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4890 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4892 ptr = Buf->buf + Buf->BufUsed;
4894 fdflags = fcntl(*fd, F_GETFL);
4895 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4897 while ((nRead < nBytes) &&
4907 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4908 *Error = strerror(errno);
4912 *Error = ErrRBLF_SelectFailed;
4915 if (! FD_ISSET(*fd, &rfds) != 0) {
4921 if ((rlen = read(*fd,
4923 nBytes - nRead)) == -1) {
4926 *Error = strerror(errno);
4931 Buf->BufUsed += rlen;
4933 Buf->buf[Buf->BufUsed] = '\0';
4937 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4938 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4940 * @ingroup StrBuf_BufferedIO
4941 * @brief Input binary data from socket
4942 * flushes and closes the FD on error
4943 * @param Blob put binary thing here
4944 * @param IOBuf the buffer to get the input to
4945 * @param Pos offset inside of IOBuf
4946 * @param fd pointer to the filedescriptor to read
4947 * @param append Append to an existing string or replace?
4948 * @param nBytes the maximal number of bytes to read
4949 * @param check whether we should search for '000\n' terminators in case of timeouts
4950 * @param Error strerror() on error
4951 * @returns numbers of chars read
4953 int StrBufReadBLOBBuffered(StrBuf *Blob,
4966 int nAlreadyRead = 0;
4971 int nSuccessLess = 0;
4974 if ((Blob == NULL) ||
4981 *Error = ErrRBB_BLOBFPreConditionFailed;
4987 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4988 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4993 rlen = pos - IOBuf->buf;
4994 rlen = IOBuf->BufUsed - rlen;
4997 if ((IOBuf->BufUsed > 0) &&
4999 (pos < IOBuf->buf + IOBuf->BufUsed))
5001 if (rlen < nBytes) {
5002 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5003 Blob->BufUsed += rlen;
5004 Blob->buf[Blob->BufUsed] = '\0';
5005 nAlreadyRead = nRead = rlen;
5008 if (rlen >= nBytes) {
5009 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5010 Blob->BufUsed += nBytes;
5011 Blob->buf[Blob->BufUsed] = '\0';
5012 if (rlen == nBytes) {
5024 if (IOBuf->BufSize < nBytes - nRead)
5025 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5028 fdflags = fcntl(*fd, F_GETFL);
5029 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5037 while ((nSuccessLess < MaxTries) &&
5047 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5048 *Error = strerror(errno);
5052 *Error = ErrRBLF_SelectFailed;
5055 if (! FD_ISSET(*fd, &rfds) != 0) {
5062 IOBuf->BufSize - (ptr - IOBuf->buf));
5066 *Error = strerror(errno);
5069 else if (rlen == 0){
5070 if ((check == NNN_TERM) &&
5072 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
5074 StrBufPlain(Blob, HKEY("\n000\n"));
5075 StrBufCutRight(Blob, 5);
5076 return Blob->BufUsed;
5078 else if (!IsNonBlock)
5080 else if (nSuccessLess > MaxTries) {
5082 *Error = ErrRBB_too_many_selects;
5086 else if (rlen > 0) {
5090 IOBuf->BufUsed += rlen;
5093 if (nSuccessLess >= MaxTries) {
5095 *Error = ErrRBB_too_many_selects;
5099 if (nRead > nBytes) {
5100 *Pos = IOBuf->buf + nBytes;
5102 Blob->buf[Blob->BufUsed] = '\0';
5103 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5107 return nRead + nAlreadyRead;
5111 * @ingroup StrBuf_IO
5112 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5113 * @param LineBuf your line will be copied here.
5114 * @param Buf BLOB with lines of text...
5115 * @param Ptr moved arround to keep the next-line across several iterations
5116 * has to be &NULL on start; will be &NotNULL on end of buffer
5117 * @returns size of remaining buffer
5119 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5121 const char *aptr, *ptr, *eptr;
5124 if ((Buf == NULL) ||
5125 (*Ptr == StrBufNOTNULL) ||
5127 (LineBuf->buf == NULL))
5129 *Ptr = StrBufNOTNULL;
5133 FlushStrBuf(LineBuf);
5135 ptr = aptr = Buf->buf;
5139 optr = LineBuf->buf;
5140 eptr = Buf->buf + Buf->BufUsed;
5141 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5143 while ((ptr <= eptr) &&
5150 LineBuf->BufUsed = optr - LineBuf->buf;
5151 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5152 optr = LineBuf->buf + LineBuf->BufUsed;
5153 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5157 if ((ptr >= eptr) && (optr > LineBuf->buf))
5159 LineBuf->BufUsed = optr - LineBuf->buf;
5161 if ((ptr <= eptr) && (*ptr == '\r'))
5163 if ((ptr <= eptr) && (*ptr == '\n'))
5170 *Ptr = StrBufNOTNULL;
5173 return Buf->BufUsed - (ptr - Buf->buf);
5178 * @ingroup StrBuf_IO
5179 * @brief removes double slashes from pathnames
5180 * @param Dir directory string to filter
5181 * @param RemoveTrailingSlash allows / disallows trailing slashes
5183 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5189 while (!IsEmptyStr(a)) {
5201 if ((RemoveTrailingSlash) &&
5207 Dir->BufUsed = b - Dir->buf;
5212 * Decode a quoted-printable encoded StrBuf buffer "in place"
5213 * This is possible because the decoded will always be shorter than the encoded
5214 * so we don't have to worry about the buffer being to small.
5216 void StrBufDecodeQP(StrBuf *Buf)
5218 if (!Buf) { // sanity check #1
5222 int source_len = StrLength(Buf);
5223 if (source_len < 1) { // sanity check #2
5227 int spos = 0; // source position
5228 int tpos = 0; // target position
5230 while (spos < source_len) {
5231 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
5234 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
5237 else if (Buf->buf[spos] == '=') {
5240 sscanf(&Buf->buf[spos], "%02x", &ch);
5241 Buf->buf[tpos++] = ch;
5245 Buf->buf[tpos++] = Buf->buf[spos++];
5250 Buf->BufUsed = tpos;