1 // Copyright (c) 1987-2022 by the citadel.org team
3 // This program is open source software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <sys/select.h>
27 #include <sys/types.h>
28 #define SHOW_ME_VAPPEND_PRINTF
31 #include "libcitadel.h"
33 #include "b64/cencode.h"
34 #include "b64/cdecode.h"
50 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
51 const Bytef * source, uLong sourceLen, int level);
53 int BaseStrBufSize = 64;
55 int ZLibCompressionRatio = -1; /* defaults to 6 */
57 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
58 #define OS_CODE 0x03 /*< unix */
59 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
62 const char *StrBufNOTNULL = ((char*) NULL) - 1;
64 const char HexList[256][3] = {
65 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
66 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
67 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
68 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
69 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
70 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
71 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
72 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
73 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
74 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
75 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
76 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
77 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
78 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
79 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
80 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
83 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
84 * StrBuf is a versatile class, aiding the handling of dynamic strings
85 * * reduce de/reallocations
86 * * reduce the need to remeasure it
87 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
88 * * allow asyncroneous IO for line and Blob based operations
89 * * reduce the use of memove in those
90 * * Quick filling in several operations with append functions
94 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
99 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
101 * use these operators to interfere with code demanding char*;
102 * if you need to own the content, smash me. Avoid, since we loose the length information.
106 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
108 * operations to get your Strings into a StrBuf, manipulating them, or appending
111 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
113 * Quick tokenizer; demands of the user to pull its tokens in sequence
117 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
119 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
123 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
125 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
126 * External Cursor to maintain the current read position inside of the buffer
127 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
131 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
137 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
139 * these functions translate the content of a buffer into another representation;
140 * some are combined Fillers and encoders
144 * Private Structure for the Stringbuffer
147 char *buf; /**< the pointer to the dynamic buffer */
148 long BufSize; /**< how many spcae do we optain */
149 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
150 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
152 long nIncreases; /**< for profiling; cound how many times we needed more */
153 char bt [SIZ]; /**< Stacktrace of last increase */
154 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
159 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
160 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
163 #ifdef HAVE_BACKTRACE
164 static void StrBufBacktrace(StrBuf *Buf, int which)
168 void *stack_frames[50];
173 pstart = pch = Buf->bt;
175 pstart = pch = Buf->bt_lastinc;
176 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
177 strings = backtrace_symbols(stack_frames, size);
178 for (i = 0; i < size; i++) {
180 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
182 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
191 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
193 if (hFreeDbglog == -1){
194 pid_t pid = getpid();
196 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
197 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
199 if ((*FreeMe)->nIncreases > 0)
203 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
205 (*FreeMe)->nIncreases,
209 (*FreeMe)->bt_lastinc);
210 n = write(hFreeDbglog, buf, n);
216 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
220 n = write(hFreeDbglog, buf, n);
224 void dbg_IncreaseBuf(StrBuf *IncMe)
227 #ifdef HAVE_BACKTRACE
228 StrBufBacktrace(Buf, 1);
232 void dbg_Init(StrBuf *Buf)
236 Buf->bt_lastinc[0] = '\0';
237 #ifdef HAVE_BACKTRACE
238 StrBufBacktrace(Buf, 0);
244 #define dbg_FreeStrBuf(a, b)
245 #define dbg_IncreaseBuf(a)
252 * @brief swaps the contents of two StrBufs
253 * this is to be used to have cheap switched between a work-buffer and a target buffer
255 * @param B second one
257 static inline void iSwapBuffers(StrBuf *A, StrBuf *B)
261 memcpy(&C, A, sizeof(*A));
262 memcpy(A, B, sizeof(*B));
263 memcpy(B, &C, sizeof(C));
267 void SwapBuffers(StrBuf *A, StrBuf *B)
274 * @ingroup StrBuf_Cast
275 * @brief Cast operator to Plain String
276 * @note if the buffer is altered by StrBuf operations, this pointer may become
277 * invalid. So don't lean on it after altering the buffer!
278 * Since this operation is considered cheap, rather call it often than risking
279 * your pointer to become invalid!
280 * @param Str the string we want to get the c-string representation for
281 * @returns the Pointer to the Content. Don't mess with it!
283 inline const char *ChrPtr(const StrBuf *Str)
291 * @ingroup StrBuf_Cast
292 * @brief since we know strlen()'s result, provide it here.
293 * @param Str the string to return the length to
294 * @returns contentlength of the buffer
296 inline int StrLength(const StrBuf *Str)
298 return (Str != NULL) ? Str->BufUsed : 0;
301 // local utility function to resize the buffer
302 // Buf the buffer whichs storage we should increase
303 // KeepOriginal should we copy the original buffer or just start over with a new one
304 // DestSize what should fit in after?
305 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
307 size_t NewSize = Buf->BufSize * 2;
314 while ((NewSize <= DestSize) && (NewSize != 0)) {
323 NewBuf = (char*) malloc(NewSize);
324 if (NewBuf == NULL) {
328 if (KeepOriginal && (Buf->BufUsed > 0)) {
329 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
337 Buf->BufSize = NewSize;
339 dbg_IncreaseBuf(Buf);
345 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
346 // Buf Buffer to shrink (has to be empty)
347 // ThreshHold if the buffer is bigger then this, its readjusted
348 // NewSize if we Shrink it, how big are we going to be afterwards?
349 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
350 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
352 Buf->buf = (char*) malloc(NewSize);
354 Buf->BufSize = NewSize;
360 * @ingroup StrBuf_DeConstructors
361 * @brief shrink long term buffers to their real size so they don't waste memory
362 * @param Buf buffer to shrink
363 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
364 * @returns physical size of the buffer
366 long StrBufShrinkToFit(StrBuf *Buf, int Force)
371 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
375 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
379 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
380 Buf->BufSize = Buf->BufUsed + 1;
388 * @ingroup StrBuf_DeConstructors
389 * @brief Allocate a new buffer with default buffer size
390 * @returns the new stringbuffer
392 StrBuf* NewStrBuf(void)
396 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
400 NewBuf->buf = (char*) malloc(BaseStrBufSize);
401 if (NewBuf->buf == NULL)
406 NewBuf->buf[0] = '\0';
407 NewBuf->BufSize = BaseStrBufSize;
409 NewBuf->ConstBuf = 0;
417 * @ingroup StrBuf_DeConstructors
418 * @brief Copy Constructor; returns a duplicate of CopyMe
419 * @param CopyMe Buffer to faxmilate
420 * @returns the new stringbuffer
422 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
429 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
433 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
434 if (NewBuf->buf == NULL)
440 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
441 NewBuf->BufUsed = CopyMe->BufUsed;
442 NewBuf->BufSize = CopyMe->BufSize;
443 NewBuf->ConstBuf = 0;
451 * @ingroup StrBuf_DeConstructors
452 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
453 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
454 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
455 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
456 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
457 * @returns the new stringbuffer
459 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
463 if (CreateRelpaceMe == NULL)
468 if (*CreateRelpaceMe != NULL)
469 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
471 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
475 if (CopyFlushMe == NULL)
477 if (*CreateRelpaceMe != NULL)
478 FlushStrBuf(*CreateRelpaceMe);
480 *CreateRelpaceMe = NewStrBuf();
485 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
486 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
487 * be a big IO-Buffer...
489 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
491 if (*CreateRelpaceMe == NULL)
493 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
498 NewBuf = *CreateRelpaceMe;
501 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
505 if (*CreateRelpaceMe == NULL)
507 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
511 NewBuf = *CreateRelpaceMe;
512 iSwapBuffers (NewBuf, CopyFlushMe);
515 FlushStrBuf(CopyFlushMe);
520 * @ingroup StrBuf_DeConstructors
521 * @brief create a new Buffer using an existing c-string
522 * this function should also be used if you want to pre-suggest
523 * the buffer size to allocate in conjunction with ptr == NULL
524 * @param ptr the c-string to copy; may be NULL to create a blank instance
525 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
526 * @returns the new stringbuffer
528 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
531 size_t Siz = BaseStrBufSize;
534 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
539 CopySize = strlen((ptr != NULL)?ptr:"");
543 while ((Siz <= CopySize) && (Siz != 0))
552 NewBuf->buf = (char*) malloc(Siz);
553 if (NewBuf->buf == NULL)
558 NewBuf->BufSize = Siz;
560 memcpy(NewBuf->buf, ptr, CopySize);
561 NewBuf->buf[CopySize] = '\0';
562 NewBuf->BufUsed = CopySize;
565 NewBuf->buf[0] = '\0';
568 NewBuf->ConstBuf = 0;
576 * @ingroup StrBuf_DeConstructors
577 * @brief Set an existing buffer from a c-string
578 * @param Buf buffer to load
579 * @param ptr c-string to put into
580 * @param nChars set to -1 if we should work 0-terminated
581 * @returns the new length of the string
583 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
598 CopySize = strlen(ptr);
602 while ((Siz <= CopySize) && (Siz != 0))
610 if (Siz != Buf->BufSize)
611 IncreaseBuf(Buf, 0, Siz);
612 memcpy(Buf->buf, ptr, CopySize);
613 Buf->buf[CopySize] = '\0';
614 Buf->BufUsed = CopySize;
621 * @ingroup StrBuf_DeConstructors
622 * @brief use strbuf as wrapper for a string constant for easy handling
623 * @param StringConstant a string to wrap
624 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
626 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
630 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
633 NewBuf->buf = (char*) StringConstant;
634 NewBuf->BufSize = SizeOfStrConstant;
635 NewBuf->BufUsed = SizeOfStrConstant;
636 NewBuf->ConstBuf = 1;
645 * @ingroup StrBuf_DeConstructors
646 * @brief flush the content of a Buf; keep its struct
647 * @param buf Buffer to flush
649 int FlushStrBuf(StrBuf *buf)
651 if ((buf == NULL) || (buf->buf == NULL))
661 * @ingroup StrBuf_DeConstructors
662 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
663 * @param buf Buffer to wipe
665 int FLUSHStrBuf(StrBuf *buf)
671 if (buf->BufUsed > 0) {
672 memset(buf->buf, 0, buf->BufUsed);
679 int hFreeDbglog = -1;
682 * @ingroup StrBuf_DeConstructors
683 * @brief Release a Buffer
684 * Its a double pointer, so it can NULL your pointer
685 * so fancy SIG11 appear instead of random results
686 * @param FreeMe Pointer Pointer to the buffer to free
688 void FreeStrBuf (StrBuf **FreeMe)
693 dbg_FreeStrBuf(FreeMe, 'F');
695 if (!(*FreeMe)->ConstBuf)
696 free((*FreeMe)->buf);
702 * @ingroup StrBuf_DeConstructors
703 * @brief flatten a Buffer to the Char * we return
704 * Its a double pointer, so it can NULL your pointer
705 * so fancy SIG11 appear instead of random results
706 * The Callee then owns the buffer and is responsible for freeing it.
707 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
708 * @returns the pointer of the buffer; Callee owns the memory thereafter.
710 char *SmashStrBuf (StrBuf **SmashMe)
714 if ((SmashMe == NULL) || (*SmashMe == NULL))
717 dbg_FreeStrBuf(SmashMe, 'S');
719 Ret = (*SmashMe)->buf;
726 * @ingroup StrBuf_DeConstructors
727 * @brief Release the buffer
728 * If you want put your StrBuf into a Hash, use this as Destructor.
729 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
731 void HFreeStrBuf (void *VFreeMe)
733 StrBuf *FreeMe = (StrBuf*)VFreeMe;
737 dbg_FreeStrBuf(SmashMe, 'H');
739 if (!FreeMe->ConstBuf)
745 /*******************************************************************************
746 * Simple string transformations *
747 *******************************************************************************/
751 * @brief Wrapper around atol
753 long StrTol(const StrBuf *Buf)
758 return atol(Buf->buf);
765 * @brief Wrapper around atoi
767 int StrToi(const StrBuf *Buf)
771 if (Buf->BufUsed > 0)
772 return atoi(Buf->buf);
779 * @brief Checks to see if the string is a pure number
780 * @param Buf The buffer to inspect
781 * @returns 1 if its a pure number, 0, if not.
783 int StrBufIsNumber(const StrBuf *Buf) {
785 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
788 strtoll(Buf->buf, &pEnd, 10);
789 if (pEnd == Buf->buf)
791 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
793 if (Buf->buf == pEnd)
799 * @ingroup StrBuf_Filler
800 * @brief modifies a Single char of the Buf
801 * You can point to it via char* or a zero-based integer
802 * @param Buf The buffer to manipulate
803 * @param ptr char* to zero; use NULL if unused
804 * @param nThChar zero based pointer into the string; use -1 if unused
805 * @param PeekValue The Character to place into the position
807 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
812 nThChar = ptr - Buf->buf;
813 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
815 Buf->buf[nThChar] = PeekValue;
820 * @ingroup StrBuf_Filler
821 * @brief modifies a range of chars of the Buf
822 * You can point to it via char* or a zero-based integer
823 * @param Buf The buffer to manipulate
824 * @param ptr char* to zero; use NULL if unused
825 * @param nThChar zero based pointer into the string; use -1 if unused
826 * @param nChars how many chars are to be flushed?
827 * @param PookValue The Character to place into that area
829 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
834 nThChar = ptr - Buf->buf;
835 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
837 if (nThChar + nChars > Buf->BufUsed)
838 nChars = Buf->BufUsed - nThChar;
840 memset(Buf->buf + nThChar, PookValue, nChars);
841 /* just to be shure... */
842 Buf->buf[Buf->BufUsed] = 0;
847 * @ingroup StrBuf_Filler
848 * @brief Append a StringBuffer to the buffer
849 * @param Buf Buffer to modify
850 * @param AppendBuf Buffer to copy at the end of our buffer
851 * @param Offset Should we start copying from an offset?
853 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
855 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
856 (Buf == NULL) || (Buf->buf == NULL))
859 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
860 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
862 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
863 Buf->BufUsed += AppendBuf->BufUsed - Offset;
864 Buf->buf[Buf->BufUsed] = '\0';
868 // Append a C-String to the buffer
869 // Buf Buffer to modify
870 // AppendBuf Buffer to copy at the end of our buffer
871 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
872 // Offset Should we start copying from an offset?
873 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
875 long BufSizeRequired;
877 if ((AppendBuf == NULL) || (Buf == NULL))
880 if (AppendSize < 0) {
881 aps = strlen(AppendBuf + Offset);
884 aps = AppendSize - Offset;
887 BufSizeRequired = Buf->BufUsed + aps + 1;
888 if (Buf->BufSize <= BufSizeRequired) {
889 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
892 memcpy(Buf->buf + Buf->BufUsed,
896 Buf->buf[Buf->BufUsed] = '\0';
900 * @ingroup StrBuf_Filler
901 * @brief sprintf like function appending the formated string to the buffer
902 * vsnprintf version to wrap into own calls
903 * @param Buf Buffer to extend by format and Params
904 * @param format printf alike format to add
905 * @param ap va_list containing the items for format
907 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
915 if ((Buf == NULL) || (format == NULL))
918 BufSize = Buf->BufSize;
919 nWritten = Buf->BufSize + 1;
920 Offset = Buf->BufUsed;
921 newused = Offset + nWritten;
923 while (newused >= BufSize) {
925 nWritten = vsnprintf(Buf->buf + Offset,
926 Buf->BufSize - Offset,
929 newused = Offset + nWritten;
930 if (newused >= Buf->BufSize) {
931 if (IncreaseBuf(Buf, 1, newused) == -1)
932 return; /* TODO: error handling? */
933 newused = Buf->BufSize + 1;
936 Buf->BufUsed = Offset + nWritten;
937 BufSize = Buf->BufSize;
944 * @ingroup StrBuf_Filler
945 * @brief sprintf like function appending the formated string to the buffer
946 * @param Buf Buffer to extend by format and Params
947 * @param format printf alike format to add
949 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
957 if ((Buf == NULL) || (format == NULL))
960 BufSize = Buf->BufSize;
961 nWritten = Buf->BufSize + 1;
962 Offset = Buf->BufUsed;
963 newused = Offset + nWritten;
965 while (newused >= BufSize) {
966 va_start(arg_ptr, format);
967 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
968 Buf->BufSize - Buf->BufUsed,
971 newused = Buf->BufUsed + nWritten;
972 if (newused >= Buf->BufSize) {
973 if (IncreaseBuf(Buf, 1, newused) == -1)
974 return; /* TODO: error handling? */
975 newused = Buf->BufSize + 1;
978 Buf->BufUsed += nWritten;
979 BufSize = Buf->BufSize;
986 * @ingroup StrBuf_Filler
987 * @brief sprintf like function putting the formated string into the buffer
988 * @param Buf Buffer to extend by format and Parameters
989 * @param format printf alike format to add
991 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
996 if ((Buf == NULL) || (format == NULL))
999 nWritten = Buf->BufSize + 1;
1000 while (nWritten >= Buf->BufSize) {
1001 va_start(arg_ptr, format);
1002 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1004 if (nWritten >= Buf->BufSize) {
1005 if (IncreaseBuf(Buf, 0, 0) == -1)
1006 return; /* TODO: error handling? */
1007 nWritten = Buf->BufSize + 1;
1010 Buf->BufUsed = nWritten ;
1015 * @ingroup StrBuf_Filler
1016 * @brief Callback for cURL to append the webserver reply to a buffer
1017 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1018 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1019 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1020 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1022 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1031 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1032 return size * nmemb;
1038 * @brief extracts a substring from Source into dest
1039 * @param dest buffer to place substring into
1040 * @param Source string to copy substring from
1041 * @param Offset chars to skip from start
1042 * @param nChars number of chars to copy
1043 * @returns the number of chars copied; may be different from nChars due to the size of Source
1045 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1047 size_t NCharsRemain;
1048 if (Offset > Source->BufUsed)
1054 if (Offset + nChars < Source->BufUsed)
1056 if ((nChars >= dest->BufSize) &&
1057 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1059 memcpy(dest->buf, Source->buf + Offset, nChars);
1060 dest->BufUsed = nChars;
1061 dest->buf[dest->BufUsed] = '\0';
1064 NCharsRemain = Source->BufUsed - Offset;
1065 if ((NCharsRemain >= dest->BufSize) &&
1066 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1068 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1069 dest->BufUsed = NCharsRemain;
1070 dest->buf[dest->BufUsed] = '\0';
1071 return NCharsRemain;
1076 * @brief Cut nChars from the start of the string
1077 * @param Buf Buffer to modify
1078 * @param nChars how many chars should be skipped?
1080 void StrBufCutLeft(StrBuf *Buf, int nChars)
1082 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1083 if (nChars >= Buf->BufUsed) {
1087 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1088 Buf->BufUsed -= nChars;
1089 Buf->buf[Buf->BufUsed] = '\0';
1094 * @brief Cut the trailing n Chars from the string
1095 * @param Buf Buffer to modify
1096 * @param nChars how many chars should be trunkated?
1098 void StrBufCutRight(StrBuf *Buf, int nChars)
1100 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1103 if (nChars >= Buf->BufUsed) {
1107 Buf->BufUsed -= nChars;
1108 Buf->buf[Buf->BufUsed] = '\0';
1113 * @brief Cut the string after n Chars
1114 * @param Buf Buffer to modify
1115 * @param AfternChars after how many chars should we trunkate the string?
1116 * @param At if non-null and points inside of our string, cut it there.
1118 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1120 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1122 AfternChars = At - Buf->buf;
1125 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1127 Buf->BufUsed = AfternChars;
1128 Buf->buf[Buf->BufUsed] = '\0';
1134 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1135 * @param Buf the string to modify
1137 void StrBufTrim(StrBuf *Buf)
1140 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1142 while ((Buf->BufUsed > 0) &&
1143 isspace(Buf->buf[Buf->BufUsed - 1]))
1147 Buf->buf[Buf->BufUsed] = '\0';
1149 if (Buf->BufUsed == 0) return;
1151 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1154 if (delta > 0) StrBufCutLeft(Buf, delta);
1158 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1159 * @param Buf the string to modify
1161 void StrBufSpaceToBlank(StrBuf *Buf)
1165 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1168 pche = pch + Buf->BufUsed;
1177 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1182 if ((Buf == NULL) || (Buf->buf == NULL)) {
1186 pRight = strchr(Buf->buf, rightboundary);
1187 if (pRight != NULL) {
1188 StrBufCutAt(Buf, 0, pRight);
1191 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1192 if (pLeft != NULL) {
1193 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1199 * @ingroup StrBuf_Filler
1200 * @brief uppercase the contents of a buffer
1201 * @param Buf the buffer to translate
1203 void StrBufUpCase(StrBuf *Buf)
1207 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1210 pche = pch + Buf->BufUsed;
1211 while (pch < pche) {
1212 *pch = toupper(*pch);
1219 * @ingroup StrBuf_Filler
1220 * @brief lowercase the contents of a buffer
1221 * @param Buf the buffer to translate
1223 void StrBufLowerCase(StrBuf *Buf)
1227 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1230 pche = pch + Buf->BufUsed;
1231 while (pch < pche) {
1232 *pch = tolower(*pch);
1238 /*******************************************************************************
1239 * a tokenizer that kills, maims, and destroys *
1240 *******************************************************************************/
1243 * @ingroup StrBuf_Tokenizer
1244 * @brief Replace a token at a given place with a given length by another token with given length
1245 * @param Buf String where to work on
1246 * @param where where inside of the Buf is the search-token
1247 * @param HowLong How long is the token to be replaced
1248 * @param Repl Token to insert at 'where'
1249 * @param ReplLen Length of repl
1250 * @returns -1 if fail else length of resulting Buf
1252 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1253 const char *Repl, long ReplLen)
1256 if ((Buf == NULL) ||
1257 (where > Buf->BufUsed) ||
1258 (where + HowLong > Buf->BufUsed))
1261 if (where + ReplLen - HowLong > Buf->BufSize)
1262 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1265 memmove(Buf->buf + where + ReplLen,
1266 Buf->buf + where + HowLong,
1267 Buf->BufUsed - where - HowLong);
1269 memcpy(Buf->buf + where,
1272 Buf->BufUsed += ReplLen - HowLong;
1274 return Buf->BufUsed;
1278 * @ingroup StrBuf_Tokenizer
1279 * @brief Counts the numbmer of tokens in a buffer
1280 * @param source String to count tokens in
1281 * @param tok Tokenizer char to count
1282 * @returns numbers of tokenizer chars found
1284 int StrBufNum_tokens(const StrBuf *source, char tok)
1288 if ((source == NULL) || (source->BufUsed == 0))
1290 if ((source->BufUsed == 1) && (*source->buf == tok))
1294 pche = pch + source->BufUsed;
1305 * @ingroup StrBuf_Tokenizer
1306 * @brief a string tokenizer
1307 * @param Source StringBuffer to read into
1308 * @param parmnum n'th Parameter to remove
1309 * @param separator tokenizer character
1310 * @returns -1 if not found, else length of token.
1312 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1315 char *d, *s, *end; /* dest, source */
1318 /* Find desired @parameter */
1319 end = Source->buf + Source->BufUsed;
1321 while ((d <= end) &&
1324 /* End of string, bail! */
1329 if (*d == separator) {
1334 if ((d == NULL) || (d >= end))
1335 return 0; /* @Parameter not found */
1337 /* Find next @parameter */
1339 while ((s <= end) &&
1340 (*s && *s != separator))
1344 if (*s == separator)
1348 /* Hack and slash */
1353 memmove(d, s, Source->BufUsed - (s - Source->buf));
1354 Source->BufUsed += ReducedBy;
1355 Source->buf[Source->BufUsed] = '\0';
1357 else if (d == Source->buf) {
1359 Source->BufUsed = 0;
1363 Source->BufUsed += ReducedBy;
1374 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1376 const StrBuf Temp = {
1389 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1393 * @ingroup StrBuf_Tokenizer
1394 * @brief a string tokenizer
1395 * @param dest Destination StringBuffer
1396 * @param Source StringBuffer to read into
1397 * @param parmnum n'th Parameter to extract
1398 * @param separator tokenizer character
1399 * @returns -1 if not found, else length of token.
1401 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1403 const char *s, *e; //* source * /
1404 int len = 0; //* running total length of extracted string * /
1405 int current_token = 0; //* token currently being processed * /
1408 dest->buf[0] = '\0';
1414 if ((Source == NULL) || (Source->BufUsed ==0)) {
1418 e = s + Source->BufUsed;
1421 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1423 while ((s < e) && !IsEmptyStr(s)) {
1424 if (*s == separator) {
1427 if (len >= dest->BufSize) {
1428 dest->BufUsed = len;
1429 if (IncreaseBuf(dest, 1, -1) < 0) {
1434 if ( (current_token == parmnum) &&
1435 (*s != separator)) {
1436 dest->buf[len] = *s;
1439 else if (current_token > parmnum) {
1445 dest->buf[len] = '\0';
1446 dest->BufUsed = len;
1448 if (current_token < parmnum) {
1449 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1452 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1461 * @ingroup StrBuf_Tokenizer
1462 * @brief a string tokenizer to fetch an integer
1463 * @param Source String containing tokens
1464 * @param parmnum n'th Parameter to extract
1465 * @param separator tokenizer character
1466 * @returns 0 if not found, else integer representation of the token
1468 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1478 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1485 * @ingroup StrBuf_Tokenizer
1486 * @brief a string tokenizer to fetch a long integer
1487 * @param Source String containing tokens
1488 * @param parmnum n'th Parameter to extract
1489 * @param separator tokenizer character
1490 * @returns 0 if not found, else long integer representation of the token
1492 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1502 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1510 * @ingroup StrBuf_Tokenizer
1511 * @brief a string tokenizer to fetch an unsigned long
1512 * @param Source String containing tokens
1513 * @param parmnum n'th Parameter to extract
1514 * @param separator tokenizer character
1515 * @returns 0 if not found, else unsigned long representation of the token
1517 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1528 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1532 return (unsigned long) atol(pnum);
1541 * @ingroup StrBuf_NextTokenizer
1542 * @brief a string tokenizer; Bounds checker
1543 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1544 * @param Source our tokenbuffer
1545 * @param pStart the token iterator pointer to inspect
1546 * @returns whether the revolving pointer is inside of the search range
1548 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1550 if ((Source == NULL) ||
1551 (*pStart == StrBufNOTNULL) ||
1552 (Source->BufUsed == 0))
1556 if (*pStart == NULL)
1560 else if (*pStart > Source->buf + Source->BufUsed)
1564 else if (*pStart <= Source->buf)
1573 * @ingroup StrBuf_NextTokenizer
1574 * @brief a string tokenizer
1575 * @param dest Destination StringBuffer
1576 * @param Source StringBuffer to read into
1577 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1578 * @param separator tokenizer
1579 * @returns -1 if not found, else length of token.
1581 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1583 const char *s; /* source */
1584 const char *EndBuffer; /* end stop of source buffer */
1585 int current_token = 0; /* token currently being processed */
1586 int len = 0; /* running total length of extracted string */
1588 if ((Source == NULL) ||
1589 (Source->BufUsed == 0) )
1591 *pStart = StrBufNOTNULL;
1597 EndBuffer = Source->buf + Source->BufUsed;
1601 dest->buf[0] = '\0';
1606 *pStart = EndBuffer + 1;
1610 if (*pStart == NULL)
1612 *pStart = Source->buf; /* we're starting to examine this buffer. */
1614 else if ((*pStart < Source->buf) ||
1615 (*pStart > EndBuffer ) )
1617 return -1; /* no more tokens to find. */
1621 /* start to find the next token */
1622 while ((s <= EndBuffer) &&
1623 (current_token == 0) )
1625 if (*s == separator)
1627 /* we found the next token */
1631 if (len >= dest->BufSize)
1633 /* our Dest-buffer isn't big enough, increase it. */
1634 dest->BufUsed = len;
1636 if (IncreaseBuf(dest, 1, -1) < 0) {
1637 /* WHUT? no more mem? bail out. */
1644 if ( (current_token == 0 ) && /* are we in our target token? */
1645 (!IsEmptyStr(s) ) &&
1646 (separator != *s) ) /* don't copy the token itself */
1648 dest->buf[len] = *s; /* Copy the payload */
1649 ++len; /* remember the bigger size. */
1655 /* did we reach the end? */
1656 if ((s > EndBuffer)) {
1657 EndBuffer = StrBufNOTNULL;
1658 *pStart = EndBuffer;
1661 *pStart = s; /* remember the position for the next run */
1664 /* sanitize our extracted token */
1665 dest->buf[len] = '\0';
1666 dest->BufUsed = len;
1673 * @ingroup StrBuf_NextTokenizer
1674 * @brief a string tokenizer
1675 * @param Source StringBuffer to read from
1676 * @param pStart pointer to the end of the last token. Feed with NULL.
1677 * @param separator tokenizer character
1678 * @param nTokens number of tokens to fastforward over
1679 * @returns -1 if not found, else length of token.
1681 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1683 const char *s, *EndBuffer; //* source * /
1684 int len = 0; //* running total length of extracted string * /
1685 int current_token = 0; //* token currently being processed * /
1687 if ((Source == NULL) ||
1688 (Source->BufUsed ==0)) {
1692 return Source->BufUsed;
1694 if (*pStart == NULL)
1695 *pStart = Source->buf;
1697 EndBuffer = Source->buf + Source->BufUsed;
1699 if ((*pStart < Source->buf) ||
1700 (*pStart > EndBuffer)) {
1708 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1710 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1711 if (*s == separator) {
1714 if (current_token >= nTokens) {
1726 * @ingroup StrBuf_NextTokenizer
1727 * @brief a string tokenizer to fetch an integer
1728 * @param Source StringBuffer to read from
1729 * @param pStart Cursor on the tokenstring
1730 * @param separator tokenizer character
1731 * @returns 0 if not found, else integer representation of the token
1733 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1743 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1750 * @ingroup StrBuf_NextTokenizer
1751 * @brief a string tokenizer to fetch a long integer
1752 * @param Source StringBuffer to read from
1753 * @param pStart Cursor on the tokenstring
1754 * @param separator tokenizer character
1755 * @returns 0 if not found, else long integer representation of the token
1757 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1767 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1775 * @ingroup StrBuf_NextTokenizer
1776 * @brief a string tokenizer to fetch an unsigned long
1777 * @param Source StringBuffer to read from
1778 * @param pStart Cursor on the tokenstring
1779 * @param separator tokenizer character
1780 * @returns 0 if not found, else unsigned long representation of the token
1782 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1793 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1797 return (unsigned long) atol(pnum);
1807 /*******************************************************************************
1808 * Escape Appending *
1809 *******************************************************************************/
1812 * @ingroup StrBuf_DeEnCoder
1813 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1814 * @param OutBuf the output buffer
1815 * @param In Buffer to encode
1816 * @param PlainIn way in from plain old c strings
1818 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1820 const char *pch, *pche;
1824 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1826 if (PlainIn != NULL) {
1827 len = strlen(PlainIn);
1833 pche = pch + In->BufUsed;
1840 pt = OutBuf->buf + OutBuf->BufUsed;
1841 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1843 while (pch < pche) {
1845 IncreaseBuf(OutBuf, 1, -1);
1846 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1847 pt = OutBuf->buf + OutBuf->BufUsed;
1850 if((*pch >= 'a' && *pch <= 'z') ||
1851 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1852 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1853 (*pch == '!') || (*pch == '_') ||
1854 (*pch == ',') || (*pch == '.'))
1861 *(pt + 1) = HexList[(unsigned char)*pch][0];
1862 *(pt + 2) = HexList[(unsigned char)*pch][1];
1864 OutBuf->BufUsed += 3;
1872 * @ingroup StrBuf_DeEnCoder
1873 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1874 * @param OutBuf the output buffer
1875 * @param In Buffer to encode
1876 * @param PlainIn way in from plain old c strings
1878 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1880 const char *pch, *pche;
1884 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1886 if (PlainIn != NULL) {
1887 len = strlen(PlainIn);
1893 pche = pch + In->BufUsed;
1900 pt = OutBuf->buf + OutBuf->BufUsed;
1901 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1903 while (pch < pche) {
1905 IncreaseBuf(OutBuf, 1, -1);
1906 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1907 pt = OutBuf->buf + OutBuf->BufUsed;
1910 if((*pch >= 'a' && *pch <= 'z') ||
1911 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1912 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1913 (*pch == '!') || (*pch == '_') ||
1914 (*pch == ',') || (*pch == '.'))
1921 *(pt + 1) = HexList[(unsigned char)*pch][0];
1922 *(pt + 2) = HexList[(unsigned char)*pch][1];
1924 OutBuf->BufUsed += 3;
1932 * @ingroup StrBuf_DeEnCoder
1933 * @brief append a string with characters having a special meaning in xml encoded to the buffer
1934 * @param OutBuf the output buffer
1935 * @param In Buffer to encode
1936 * @param PlainIn way in from plain old c strings
1937 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1938 * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1940 void StrBufXMLEscAppend(StrBuf *OutBuf,
1942 const char *PlainIn,
1944 int OverrideLowChars)
1946 const char *pch, *pche;
1951 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1953 if (PlainIn != NULL) {
1955 len = strlen((const char*)PlainIn);
1962 pch = (const char*)In->buf;
1963 pche = pch + In->BufUsed;
1970 pt = OutBuf->buf + OutBuf->BufUsed;
1971 /**< we max append 6 chars at once plus the \0 */
1972 pte = OutBuf->buf + OutBuf->BufSize - 6;
1974 while (pch < pche) {
1976 OutBuf->BufUsed = pt - OutBuf->buf;
1977 IncreaseBuf(OutBuf, 1, -1);
1978 pte = OutBuf->buf + OutBuf->BufSize - 6;
1979 /**< we max append 3 chars at once plus the \0 */
1981 pt = OutBuf->buf + OutBuf->BufUsed;
1985 memcpy(pt, HKEY("<"));
1989 else if (*pch == '>') {
1990 memcpy(pt, HKEY(">"));
1994 else if (*pch == '&') {
1995 memcpy(pt, HKEY("&"));
1999 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2004 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
2007 while ((IsUtf8Sequence > 0) &&
2020 *pt = HexList[*(unsigned char*)pch][0];
2022 *pt = HexList[*(unsigned char*)pch][1];
2031 OutBuf->BufUsed = pt - OutBuf->buf;
2036 * @ingroup StrBuf_DeEnCoder
2037 * @brief append a string in hex encoding to the buffer
2038 * @param OutBuf the output buffer
2039 * @param In Buffer to encode
2040 * @param PlainIn way in from plain old c strings
2041 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2043 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2045 const unsigned char *pch, *pche;
2049 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2051 if (PlainIn != NULL) {
2053 len = strlen((const char*)PlainIn);
2060 pch = (const unsigned char*)In->buf;
2061 pche = pch + In->BufUsed;
2068 pt = OutBuf->buf + OutBuf->BufUsed;
2069 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2071 while (pch < pche) {
2073 IncreaseBuf(OutBuf, 1, -1);
2074 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2075 pt = OutBuf->buf + OutBuf->BufUsed;
2078 *pt = HexList[*pch][0];
2080 *pt = HexList[*pch][1];
2081 pt ++; pch ++; OutBuf->BufUsed += 2;
2086 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2093 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2095 if (PlainIn != NULL) {
2097 len = strlen(PlainIn);
2110 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2112 if (ExpectLen > OutBuf->BufSize)
2113 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2116 pt = OutBuf->buf + OutBuf->BufUsed;
2118 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2121 OutBuf->BufUsed += len;
2126 * @ingroup StrBuf_DeEnCoder
2127 * @brief append a string in hex encoding to the buffer
2128 * @param OutBuf the output buffer
2129 * @param In Buffer to encode
2130 * @param PlainIn way in from plain old c strings
2132 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2134 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2138 * @ingroup StrBuf_DeEnCoder
2139 * @brief Append a string, escaping characters which have meaning in HTML.
2141 * @param Target target buffer
2142 * @param Source source buffer; set to NULL if you just have a C-String
2143 * @param PlainIn Plain-C string to append; set to NULL if unused
2144 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2145 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2146 * if set to 2, linebreaks are replaced by <br/>
2148 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2150 const char *aptr, *eiptr;
2154 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2157 if (PlainIn != NULL) {
2159 len = strlen(PlainIn);
2164 eiptr = aptr + Source->BufUsed;
2165 len = Source->BufUsed;
2171 bptr = Target->buf + Target->BufUsed;
2172 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2174 while (aptr < eiptr){
2176 IncreaseBuf(Target, 1, -1);
2177 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2178 bptr = Target->buf + Target->BufUsed;
2181 memcpy(bptr, "<", 4);
2183 Target->BufUsed += 4;
2185 else if (*aptr == '>') {
2186 memcpy(bptr, ">", 4);
2188 Target->BufUsed += 4;
2190 else if (*aptr == '&') {
2191 memcpy(bptr, "&", 5);
2193 Target->BufUsed += 5;
2195 else if (*aptr == '"') {
2196 memcpy(bptr, """, 6);
2198 Target->BufUsed += 6;
2200 else if (*aptr == '\'') {
2201 memcpy(bptr, "'", 5);
2203 Target->BufUsed += 5;
2205 else if (*aptr == LB) {
2210 else if (*aptr == RB) {
2215 else if (*aptr == QU) {
2220 else if ((*aptr == 32) && (nbsp == 1)) {
2221 memcpy(bptr, " ", 6);
2223 Target->BufUsed += 6;
2225 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2226 *bptr='\0'; /* nothing */
2228 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2229 memcpy(bptr, "<br/>", 11);
2231 Target->BufUsed += 11;
2235 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2236 *bptr='\0'; /* nothing */
2246 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2248 return Target->BufUsed;
2252 * @ingroup StrBuf_DeEnCoder
2253 * @brief Append a string, escaping characters which have meaning in HTML.
2254 * Converts linebreaks into blanks; escapes single quotes
2255 * @param Target target buffer
2256 * @param Source source buffer; set to NULL if you just have a C-String
2257 * @param PlainIn Plain-C string to append; set to NULL if unused
2259 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2261 const char *aptr, *eiptr;
2265 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2268 if (PlainIn != NULL) {
2270 len = strlen(PlainIn);
2275 eiptr = aptr + Source->BufUsed;
2276 len = Source->BufUsed;
2282 eptr = Target->buf + Target->BufSize - 8;
2283 tptr = Target->buf + Target->BufUsed;
2285 while (aptr < eiptr){
2287 IncreaseBuf(Target, 1, -1);
2288 eptr = Target->buf + Target->BufSize - 8;
2289 tptr = Target->buf + Target->BufUsed;
2292 if (*aptr == '\n') {
2296 else if (*aptr == '\r') {
2300 else if (*aptr == '\'') {
2306 Target->BufUsed += 5;
2319 * @ingroup StrBuf_DeEnCoder
2320 * @brief Append a string, escaping characters which have meaning in ICAL.
2322 * @param Target target buffer
2323 * @param Source source buffer; set to NULL if you just have a C-String
2324 * @param PlainIn Plain-C string to append; set to NULL if unused
2326 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2328 const char *aptr, *eiptr;
2332 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2335 if (PlainIn != NULL) {
2337 len = strlen(PlainIn);
2342 eiptr = aptr + Source->BufUsed;
2343 len = Source->BufUsed;
2349 eptr = Target->buf + Target->BufSize - 8;
2350 tptr = Target->buf + Target->BufUsed;
2352 while (aptr < eiptr){
2353 if(tptr + 3 >= eptr) {
2354 IncreaseBuf(Target, 1, -1);
2355 eptr = Target->buf + Target->BufSize - 8;
2356 tptr = Target->buf + Target->BufUsed;
2359 if (*aptr == '\n') {
2366 else if (*aptr == '\r') {
2373 else if (*aptr == ',') {
2389 * @ingroup StrBuf_DeEnCoder
2390 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2392 * @param Target target buffer
2393 * @param Source source buffer; set to NULL if you just have a C-String
2394 * @param PlainIn Plain-C string to append; set to NULL if unused
2395 * @returns size of result or -1
2397 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2399 const char *aptr, *eiptr;
2404 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2407 if (PlainIn != NULL) {
2409 len = strlen(PlainIn);
2414 eiptr = aptr + Source->BufUsed;
2415 len = Source->BufUsed;
2421 bptr = Target->buf + Target->BufUsed;
2422 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2424 while (aptr < eiptr){
2426 IncreaseBuf(Target, 1, -1);
2427 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2428 bptr = Target->buf + Target->BufUsed;
2432 memcpy(bptr, HKEY("\\n"));
2434 Target->BufUsed += 2;
2437 memcpy(bptr, HKEY("\\r"));
2439 Target->BufUsed += 2;
2446 Target->BufUsed += 2;
2449 if ((*(aptr + 1) == 'u') &&
2450 isxdigit(*(aptr + 2)) &&
2451 isxdigit(*(aptr + 3)) &&
2452 isxdigit(*(aptr + 4)) &&
2453 isxdigit(*(aptr + 5)))
2454 { /* oh, a unicode escaper. let it pass through. */
2455 memcpy(bptr, aptr, 6);
2458 Target->BufUsed += 6;
2466 Target->BufUsed += 2;
2474 Target->BufUsed += 2;
2481 Target->BufUsed += 2;
2488 Target->BufUsed += 2;
2491 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2492 while (IsUtf8Sequence > 0){
2495 if (--IsUtf8Sequence)
2503 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2505 return Target->BufUsed;
2509 * @ingroup StrBuf_DeEnCoder
2510 * @brief Append a string, escaping characters which have meaning in HTML + json.
2512 * @param Target target buffer
2513 * @param Source source buffer; set to NULL if you just have a C-String
2514 * @param PlainIn Plain-C string to append; set to NULL if unused
2515 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2516 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2517 * if set to 2, linebreaks are replaced by <br/>
2519 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2521 const char *aptr, *eiptr;
2524 int IsUtf8Sequence = 0;
2526 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2529 if (PlainIn != NULL) {
2531 len = strlen(PlainIn);
2536 eiptr = aptr + Source->BufUsed;
2537 len = Source->BufUsed;
2543 bptr = Target->buf + Target->BufUsed;
2544 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2546 while (aptr < eiptr){
2548 IncreaseBuf(Target, 1, -1);
2549 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2550 bptr = Target->buf + Target->BufUsed;
2554 memcpy(bptr, HKEY("<"));
2556 Target->BufUsed += 4;
2559 memcpy(bptr, HKEY(">"));
2561 Target->BufUsed += 4;
2564 memcpy(bptr, HKEY("&"));
2566 Target->BufUsed += 5;
2579 switch (nolinebreaks) {
2581 *bptr='\0'; /* nothing */
2584 memcpy(bptr, HKEY("<br/>"));
2586 Target->BufUsed += 11;
2589 memcpy(bptr, HKEY("\\n"));
2591 Target->BufUsed += 2;
2595 switch (nolinebreaks) {
2598 *bptr='\0'; /* nothing */
2601 memcpy(bptr, HKEY("\\r"));
2603 Target->BufUsed += 2;
2613 Target->BufUsed += 2;
2616 if ((*(aptr + 1) == 'u') &&
2617 isxdigit(*(aptr + 2)) &&
2618 isxdigit(*(aptr + 3)) &&
2619 isxdigit(*(aptr + 4)) &&
2620 isxdigit(*(aptr + 5)))
2621 { /* oh, a unicode escaper. let it pass through. */
2622 memcpy(bptr, aptr, 6);
2625 Target->BufUsed += 6;
2633 Target->BufUsed += 2;
2641 Target->BufUsed += 2;
2648 Target->BufUsed += 2;
2655 Target->BufUsed += 2;
2659 memcpy(bptr, HKEY(" "));
2661 Target->BufUsed += 6;
2665 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2666 while (IsUtf8Sequence > 0){
2669 if (--IsUtf8Sequence)
2677 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2679 return Target->BufUsed;
2684 * @ingroup StrBuf_DeEnCoder
2685 * @brief replace all non-Ascii characters by another
2686 * @param Buf buffer to inspect
2687 * @param repl charater to stamp over non ascii chars
2689 void StrBufAsciify(StrBuf *Buf, const char repl)
2693 for (offset = 0; offset < Buf->BufUsed; offset ++)
2694 if (!isascii(Buf->buf[offset]))
2695 Buf->buf[offset] = repl;
2700 * @ingroup StrBuf_DeEnCoder
2701 * @brief unhide special chars hidden to the HTML escaper
2702 * @param target buffer to put the unescaped string in
2703 * @param source buffer to unescape
2705 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2710 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2716 FlushStrBuf(target);
2718 len = source->BufUsed;
2719 for (a = 0; a < len; ++a) {
2720 if (target->BufUsed >= target->BufSize)
2721 IncreaseBuf(target, 1, -1);
2723 if (source->buf[a] == '=') {
2724 hex[0] = source->buf[a + 1];
2725 hex[1] = source->buf[a + 2];
2728 sscanf(hex, "%02x", &b);
2729 target->buf[target->BufUsed] = b;
2730 target->buf[++target->BufUsed] = 0;
2734 target->buf[target->BufUsed] = source->buf[a];
2735 target->buf[++target->BufUsed] = 0;
2742 * @ingroup StrBuf_DeEnCoder
2743 * @brief hide special chars from the HTML escapers and friends
2744 * @param target buffer to put the escaped string in
2745 * @param source buffer to escape
2747 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2752 FlushStrBuf(target);
2754 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2759 len = source->BufUsed;
2760 for (i=0; i<len; ++i) {
2761 if (target->BufUsed + 4 >= target->BufSize)
2762 IncreaseBuf(target, 1, -1);
2763 if ( (isalnum(source->buf[i])) ||
2764 (source->buf[i]=='-') ||
2765 (source->buf[i]=='_') ) {
2766 target->buf[target->BufUsed++] = source->buf[i];
2769 sprintf(&target->buf[target->BufUsed],
2771 (0xFF &source->buf[i]));
2772 target->BufUsed += 3;
2775 target->buf[target->BufUsed + 1] = '\0';
2779 /*******************************************************************************
2780 * Quoted Printable de/encoding *
2781 *******************************************************************************/
2784 * @ingroup StrBuf_DeEnCoder
2785 * @brief decode a buffer from base 64 encoding; destroys original
2786 * @param Buf Buffor to transform
2788 int StrBufDecodeBase64(StrBuf *Buf)
2796 xferbuf = (char*) malloc(Buf->BufSize);
2797 if (xferbuf == NULL)
2801 siz = CtdlDecodeBase64(xferbuf,
2808 Buf->buf[Buf->BufUsed] = '\0';
2813 * @ingroup StrBuf_DeEnCoder
2814 * @brief decode a buffer from base 64 encoding; expects targetbuffer
2815 * @param BufIn Buffor to transform
2816 * @param BufOut Buffer to put result into
2818 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2820 if ((BufIn == NULL) || (BufOut == NULL))
2823 if (BufOut->BufSize < BufIn->BufUsed)
2824 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2826 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2829 return BufOut->BufUsed;
2832 typedef struct __z_enc_stream {
2837 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err)
2839 base64_decodestate *state;;
2846 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2847 base64_init_decodestate(state);
2848 return (vStreamT*) state;
2853 z_enc_stream *stream;
2856 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2857 memset(stream, 0, sizeof(z_enc_stream));
2858 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2859 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2861 err = inflateInit(&stream->zstream);
2864 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2868 return (vStreamT*) stream;
2873 z_enc_stream *stream;
2876 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2877 memset(stream, 0, sizeof(z_enc_stream));
2878 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2879 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2880 /* write gzip header */
2881 stream->OutBuf.BufUsed = snprintf
2882 (stream->OutBuf.buf,
2883 stream->OutBuf.BufSize,
2884 "%c%c%c%c%c%c%c%c%c%c",
2885 gz_magic[0], gz_magic[1], Z_DEFLATED,
2886 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2889 err = deflateInit2(&stream->zstream,
2890 ZLibCompressionRatio,
2894 Z_DEFAULT_STRATEGY);
2896 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2900 return (vStreamT*) stream;
2910 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err)
2916 if ((vStream == NULL) || (*vStream==NULL)) {
2917 *Err = strerror(EINVAL);
2929 z_enc_stream *stream = (z_enc_stream *)*vStream;
2930 (void)inflateEnd(&stream->zstream);
2931 free(stream->OutBuf.buf);
2936 z_enc_stream *stream = (z_enc_stream *)*vStream;
2937 err = deflateEnd(&stream->zstream);
2942 free(stream->OutBuf.buf);
2953 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err)
2962 // base64_decodestate *state = (base64_decodestate*)vStream;
2968 pInLen = In->BufUsed;
2970 if ((In == NULL) || (vStream == NULL))
2973 ExpectLen = (pInLen / 4) * 3;
2975 if (Target->BufSize - Target->BufUsed < ExpectLen)
2977 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2980 //// ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2981 Target->BufUsed += ExpectLen;
2982 Target->buf[Target->BufUsed] = '\0';
2989 base64_decodestate *state = (base64_decodestate *)vStream;
2995 pInLen = In->BufUsed;
2997 if ((pIn == NULL) || (vStream == NULL))
3000 ExpectLen = (pInLen / 4) * 3;
3002 if (Target->BufSize - Target->BufUsed < ExpectLen)
3004 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3007 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3008 Target->BufUsed += ExpectLen;
3009 Target->buf[Target->BufUsed] = '\0';
3015 z_enc_stream *stream = (z_enc_stream *)vStream;
3016 int org_outbuf_len = stream->OutBuf.BufUsed;
3018 unsigned int chunkavail;
3020 if (In->ReadWritePointer != NULL)
3022 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3023 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3024 (In->ReadWritePointer - In->Buf->buf);
3028 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3029 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3032 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3033 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3035 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3037 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3041 (stream->OutBuf.BufUsed != org_outbuf_len)
3044 iSwapBuffers(Target->Buf, &stream->OutBuf);
3047 if (stream->zstream.avail_in == 0)
3049 FlushStrBuf(In->Buf);
3050 In->ReadWritePointer = NULL;
3054 if (stream->zstream.avail_in < 64)
3056 memmove(In->Buf->buf,
3057 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3058 stream->zstream.avail_in);
3060 In->Buf->BufUsed = stream->zstream.avail_in;
3061 In->Buf->buf[In->Buf->BufUsed] = '\0';
3066 In->ReadWritePointer = In->Buf->buf +
3067 (In->Buf->BufUsed - stream->zstream.avail_in);
3070 rc = (LastChunk && (err != Z_FINISH));
3071 if (!rc && (err != Z_OK)) {
3078 z_enc_stream *stream = (z_enc_stream *)vStream;
3079 int org_outbuf_len = stream->zstream.total_out;
3082 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3083 if (In->ReadWritePointer != NULL)
3085 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3086 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3087 (In->ReadWritePointer - In->Buf->buf);
3091 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3092 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3096 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3097 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3099 err = inflate(&stream->zstream, Z_NO_FLUSH);
3101 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
3104 err = Z_DATA_ERROR; /* and fall through */
3109 (void)inflateEnd(&stream->zstream);
3113 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3115 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
3117 if (stream->zstream.avail_in == 0)
3119 FlushStrBuf(In->Buf);
3120 In->ReadWritePointer = NULL;
3124 if (stream->zstream.avail_in < 64)
3126 memmove(In->Buf->buf,
3127 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3128 stream->zstream.avail_in);
3130 In->Buf->BufUsed = stream->zstream.avail_in;
3131 In->Buf->buf[In->Buf->BufUsed] = '\0';
3136 In->ReadWritePointer = In->Buf->buf +
3137 (In->Buf->BufUsed - stream->zstream.avail_in);
3151 * @ingroup StrBuf_DeEnCoder
3152 * @brief decode a buffer from base 64 encoding; destroys original
3153 * @param Buf Buffor to transform
3155 int StrBufDecodeHex(StrBuf *Buf)
3158 char *pch, *pche, *pchi;
3160 if (Buf == NULL) return -1;
3162 pch = pchi = Buf->buf;
3163 pche = pch + Buf->BufUsed;
3165 while (pchi < pche){
3166 ch = decode_hex(pchi);
3173 Buf->BufUsed = pch - Buf->buf;
3174 return Buf->BufUsed;
3178 * @ingroup StrBuf_DeEnCoder
3179 * @brief replace all chars >0x20 && < 0x7F with Mute
3180 * @param Mute char to put over invalid chars
3181 * @param Buf Buffor to transform
3183 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3187 if (Buf == NULL) return -1;
3188 pch = (unsigned char *)Buf->buf;
3189 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3190 if ((*pch < 0x20) || (*pch > 0x7F))
3194 return Buf->BufUsed;
3199 * @ingroup StrBuf_DeEnCoder
3200 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3201 * @param Buf Buffer to translate
3202 * @param StripBlanks Reduce several blanks to one?
3204 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3213 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3214 Buf->buf[Buf->BufUsed - 1] = '\0';
3219 while (a < Buf->BufUsed) {
3220 if (Buf->buf[a] == '+')
3222 else if (Buf->buf[a] == '%') {
3223 /* don't let % chars through, rather truncate the input. */
3224 if (a + 2 > Buf->BufUsed) {
3229 hex[0] = Buf->buf[a + 1];
3230 hex[1] = Buf->buf[a + 2];
3233 sscanf(hex, "%02x", &b);
3234 Buf->buf[a] = (char) b;
3235 len = Buf->BufUsed - a - 2;
3237 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3249 * @ingroup StrBuf_DeEnCoder
3250 * @brief RFC2047-encode a header field if necessary.
3251 * If no non-ASCII characters are found, the string
3252 * will be copied verbatim without encoding.
3254 * @param target Target buffer.
3255 * @param source Source string to be encoded.
3256 * @returns encoded length; -1 if non success.
3258 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3260 const char headerStr[] = "=?UTF-8?Q?";
3261 int need_to_encode = 0;
3265 if ((source == NULL) ||
3269 while ((i < source->BufUsed) &&
3270 (!IsEmptyStr (&source->buf[i])) &&
3271 (need_to_encode == 0)) {
3272 if (((unsigned char) source->buf[i] < 32) ||
3273 ((unsigned char) source->buf[i] > 126)) {
3279 if (!need_to_encode) {
3280 if (*target == NULL) {
3281 *target = NewStrBufPlain(source->buf, source->BufUsed);
3284 FlushStrBuf(*target);
3285 StrBufAppendBuf(*target, source, 0);
3288 return (*target)->BufUsed;
3292 if (*target == NULL)
3293 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3294 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3295 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3296 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3297 (*target)->BufUsed = sizeof(headerStr) - 1;
3298 for (i=0; (i < source->BufUsed); ++i) {
3299 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3300 IncreaseBuf(*target, 1, 0);
3301 ch = (unsigned char) source->buf[i];
3310 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3311 (*target)->BufUsed += 3;
3315 (*target)->buf[(*target)->BufUsed] = '_';
3317 (*target)->buf[(*target)->BufUsed] = ch;
3318 (*target)->BufUsed++;
3322 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3323 IncreaseBuf(*target, 1, 0);
3325 (*target)->buf[(*target)->BufUsed++] = '?';
3326 (*target)->buf[(*target)->BufUsed++] = '=';
3327 (*target)->buf[(*target)->BufUsed] = '\0';
3328 return (*target)->BufUsed;;
3332 * @ingroup StrBuf_DeEnCoder
3333 * @brief Quoted-Printable encode a message; make it < 80 columns width.
3334 * @param source Source string to be encoded.
3335 * @returns buffer with encoded message.
3337 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3341 const char *ptr, *eptr;
3345 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3347 OEptr = OutBuf->buf + OutBuf->BufSize;
3348 ptr = EncodeMe->buf;
3349 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3354 if (Optr + 4 >= OEptr)
3357 Offset = Optr - OutBuf->buf;
3358 OutBuf->BufUsed = Optr - OutBuf->buf;
3359 IncreaseBuf(OutBuf, 1, 0);
3360 Optr = OutBuf->buf + Offset;
3361 OEptr = OutBuf->buf + OutBuf->BufSize;
3365 /* ignore carriage returns */
3368 else if (*ptr == '\n') {
3369 /* hard line break */
3370 memcpy(Optr, HKEY("=0A"));
3375 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3376 ( (*ptr >= 62) && (*ptr <= 126) ))
3387 *Optr = HexList[ch][0];
3389 *Optr = HexList[ch][1];
3396 /* soft line break */
3397 if (isspace(*(Optr - 1))) {
3402 *Optr = HexList[ch][0];
3404 *Optr = HexList[ch][1];
3416 OutBuf->BufUsed = Optr - OutBuf->buf;
3422 static void AddRecipient(StrBuf *Target,
3424 StrBuf *EmailAddress,
3429 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3430 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3432 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3433 StrBufRFC2047encode(&EncBuf, UserName);
3434 StrBufAppendBuf(Target, EncBuf, 0);
3435 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3436 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3438 if (StrLength(EmailAddress) > 0){
3439 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3440 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3441 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3447 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3448 * \param Recp Source list of email recipients
3449 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3450 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3451 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3452 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3454 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3456 StrBuf *EmailAddress,
3460 const char *pch, *pche;
3461 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3463 if ((Recp == NULL) || (StrLength(Recp) == 0))
3467 pche = pch + StrLength(Recp);
3469 if (!CheckEncode(pch, -1, pche))
3470 return NewStrBufDup(Recp);
3472 Target = NewStrBufPlain(NULL, StrLength(Recp));
3474 while ((pch != NULL) && (pch < pche))
3476 while (isspace(*pch)) pch++;
3477 UserEnd = EmailStart = EmailEnd = NULL;
3479 if ((*pch == '"') || (*pch == '\'')) {
3480 UserStart = pch + 1;
3482 UserEnd = strchr(UserStart, *pch);
3483 if (UserEnd == NULL)
3484 break; ///TODO: Userfeedback??
3485 EmailStart = UserEnd + 1;
3486 while (isspace(*EmailStart))
3488 if (UserEnd == UserStart) {
3489 UserStart = UserEnd = NULL;
3492 if (*EmailStart == '<') {
3494 EmailEnd = strchr(EmailStart, '>');
3495 if (EmailEnd == NULL)
3496 EmailEnd = strchr(EmailStart, ',');
3500 EmailEnd = strchr(EmailStart, ',');
3502 if (EmailEnd == NULL)
3509 EmailEnd = strchr(UserStart, ',');
3510 if (EmailEnd == NULL) {
3511 EmailEnd = strchr(pch, '>');
3513 if (EmailEnd != NULL) {
3523 while ((EmailEnd > UserStart) && !gt &&
3524 ((*EmailEnd == ',') ||
3525 (*EmailEnd == '>') ||
3526 (isspace(*EmailEnd))))
3528 if (*EmailEnd == '>')
3533 if (EmailEnd == UserStart)
3537 EmailStart = strchr(UserStart, '<');
3538 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3540 UserEnd = EmailStart;
3542 while ((UserEnd > UserStart) &&
3543 isspace (*(UserEnd - 1)))
3546 if (UserStart >= UserEnd)
3547 UserStart = UserEnd = NULL;
3549 else { /* this is a local recipient... no domain, just a realname */
3550 EmailStart = UserStart;
3551 At = strchr(EmailStart, '@');
3557 EmailStart = UserStart;
3563 if ((UserStart != NULL) && (UserEnd != NULL))
3564 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3565 else if ((UserStart != NULL) && (UserEnd == NULL))
3566 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3568 FlushStrBuf(UserName);
3570 if ((EmailStart != NULL) && (EmailEnd != NULL))
3571 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3572 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3573 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3575 FlushStrBuf(EmailAddress);
3577 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3582 if ((pch != NULL) && (*pch == ','))
3584 if (pch != NULL) while (isspace(*pch))
3593 * @brief replaces all occurances of 'search' by 'replace'
3594 * @param buf Buffer to modify
3595 * @param search character to search
3596 * @param replace character to replace search by
3598 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3603 for (i=0; i<buf->BufUsed; i++)
3604 if (buf->buf[i] == search)
3605 buf->buf[i] = replace;
3611 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3612 * @param buf Buffer to modify
3614 void StrBufToUnixLF(StrBuf *buf)
3616 char *pche, *pchS, *pchT;
3620 pche = buf->buf + buf->BufUsed;
3621 pchS = pchT = buf->buf;
3627 if (*pchS != '\n') {
3636 buf->BufUsed = pchT - buf->buf;
3640 /*******************************************************************************
3641 * Iconv Wrapper; RFC822 de/encoding *
3642 *******************************************************************************/
3645 * @ingroup StrBuf_DeEnCoder
3646 * @brief Wrapper around iconv_open()
3647 * Our version adds aliases for non-standard Microsoft charsets
3648 * such as 'MS950', aliasing them to names like 'CP950'
3650 * @param tocode Target encoding
3651 * @param fromcode Source encoding
3652 * @param pic anonimized pointer to iconv struct
3654 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3657 iconv_t ic = (iconv_t)(-1) ;
3658 ic = iconv_open(tocode, fromcode);
3659 if (ic == (iconv_t)(-1) ) {
3660 char alias_fromcode[64];
3661 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3662 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3663 alias_fromcode[0] = 'C';
3664 alias_fromcode[1] = 'P';
3665 ic = iconv_open(tocode, alias_fromcode);
3668 *(iconv_t *)pic = ic;
3674 * @ingroup StrBuf_DeEnCoder
3675 * @brief find one chunk of a RFC822 encoded string
3676 * @param Buffer where to search
3677 * @param bptr where to start searching
3678 * @returns found position, NULL if none.
3680 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3683 /* Find the next ?Q? */
3684 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3687 end = strchr(bptr + 2, '?');
3692 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3693 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3694 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3695 (*(end + 2) == '?')) {
3696 /* skip on to the end of the cluster, the next ?= */
3697 end = strstr(end + 3, "?=");
3700 /* sort of half valid encoding, try to find an end. */
3701 end = strstr(bptr, "?=");
3708 * @ingroup StrBuf_DeEnCoder
3709 * @brief convert one buffer according to the preselected iconv pointer PIC
3710 * @param ConvertBuf buffer we need to translate
3711 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3712 * @param pic Pointer to the iconv-session Object
3714 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3720 char *ibuf; /**< Buffer of characters to be converted */
3721 char *obuf; /**< Buffer for converted characters */
3722 size_t ibuflen; /**< Length of input buffer */
3723 size_t obuflen; /**< Length of output buffer */
3726 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3729 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3730 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3731 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3733 ic = *(iconv_t*)pic;
3734 ibuf = ConvertBuf->buf;
3735 ibuflen = ConvertBuf->BufUsed;
3737 obuflen = TmpBuf->BufSize;
3739 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3742 if (errno == E2BIG) {
3744 IncreaseBuf(TmpBuf, 0, 0);
3749 else if (errno == EILSEQ){
3750 /* hm, invalid utf8 sequence... what to do now? */
3751 /* An invalid multibyte sequence has been encountered in the input */
3753 else if (errno == EINVAL) {
3754 /* An incomplete multibyte sequence has been encountered in the input. */
3757 FlushStrBuf(TmpBuf);
3760 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3761 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3763 /* little card game: wheres the red lady? */
3764 iSwapBuffers(ConvertBuf, TmpBuf);
3765 FlushStrBuf(TmpBuf);
3772 * @ingroup StrBuf_DeEnCoder
3773 * @brief catches one RFC822 encoded segment, and decodes it.
3774 * @param Target buffer to fill with result
3775 * @param DecodeMe buffer with stuff to process
3776 * @param SegmentStart points to our current segment in DecodeMe
3777 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3778 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3779 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3780 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3782 inline static void DecodeSegment(StrBuf *Target,
3783 const StrBuf *DecodeMe,
3784 const char *SegmentStart,
3785 const char *SegmentEnd,
3787 StrBuf *ConvertBuf2,
3788 StrBuf *FoundCharset)
3794 iconv_t ic = (iconv_t)(-1);
3798 /* Now we handle foreign character sets properly encoded
3799 * in RFC2047 format.
3801 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3802 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3803 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3804 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3805 if (FoundCharset != NULL) {
3806 FlushStrBuf(FoundCharset);
3807 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3809 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3810 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3812 *encoding = toupper(*encoding);
3813 if (*encoding == 'B') { /**< base64 */
3814 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3815 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3816 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3818 ConvertBuf->BufUsed);
3820 else if (*encoding == 'Q') { /**< quoted-printable */
3824 while (pos < ConvertBuf->BufUsed)
3826 if (ConvertBuf->buf[pos] == '_')
3827 ConvertBuf->buf[pos] = ' ';
3831 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3832 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3834 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3837 ConvertBuf->BufUsed);
3840 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3843 ctdl_iconv_open("UTF-8", charset, &ic);
3844 if (ic != (iconv_t)(-1) ) {
3846 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3847 StrBufAppendBuf(Target, ConvertBuf2, 0);
3852 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3858 * @ingroup StrBuf_DeEnCoder
3859 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3860 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3861 * @param Target where to put the decoded string to
3862 * @param DecodeMe buffer with encoded string
3863 * @param DefaultCharset if we don't find one, which should we use?
3864 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3865 * put it here for later use where no string might be known.
3867 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3870 StrBuf *ConvertBuf2;
3871 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3872 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3874 StrBuf_RFC822_2_Utf8(Target,
3880 FreeStrBuf(&ConvertBuf);
3881 FreeStrBuf(&ConvertBuf2);
3885 * @ingroup StrBuf_DeEnCoder
3886 * @brief Handle subjects with RFC2047 encoding such as:
3887 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3888 * @param Target where to put the decoded string to
3889 * @param DecodeMe buffer with encoded string
3890 * @param DefaultCharset if we don't find one, which should we use?
3891 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3892 * put it here for later use where no string might be known.
3893 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3894 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3896 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3897 const StrBuf *DecodeMe,
3898 const StrBuf* DefaultCharset,
3899 StrBuf *FoundCharset,
3901 StrBuf *ConvertBuf2)
3903 StrBuf *DecodedInvalidBuf = NULL;
3904 const StrBuf *DecodeMee = DecodeMe;
3905 const char *start, *end, *next, *nextend, *ptr = NULL;
3907 iconv_t ic = (iconv_t)(-1) ;
3912 int illegal_non_rfc2047_encoding = 0;
3915 if (DecodeMe == NULL)
3917 /* Sometimes, badly formed messages contain strings which were simply
3918 * written out directly in some foreign character set instead of
3919 * using RFC2047 encoding. This is illegal but we will attempt to
3920 * handle it anyway by converting from a user-specified default
3921 * charset to UTF-8 if we see any nonprintable characters.
3924 for (i=0; i<DecodeMe->BufUsed; ++i) {
3925 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3926 illegal_non_rfc2047_encoding = 1;
3931 if ((illegal_non_rfc2047_encoding) &&
3932 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3933 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3936 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3937 if (ic != (iconv_t)(-1) ) {
3938 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3939 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3940 DecodeMee = DecodedInvalidBuf;
3946 /* pre evaluate the first pair */
3948 start = strstr(DecodeMee->buf, "=?");
3949 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3951 end = FindNextEnd (DecodeMee, start + 2);
3953 StrBufAppendBuf(Target, DecodeMee, 0);
3954 FreeStrBuf(&DecodedInvalidBuf);
3959 if (start != DecodeMee->buf) {
3962 nFront = start - DecodeMee->buf;
3963 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3966 * Since spammers will go to all sorts of absurd lengths to get their
3967 * messages through, there are LOTS of corrupt headers out there.
3968 * So, prevent a really badly formed RFC2047 header from throwing
3969 * this function into an infinite loop.
3971 while ((start != NULL) &&
3978 DecodeSegment(Target,
3986 next = strstr(end, "=?");
3988 if ((next != NULL) &&
3990 nextend = FindNextEnd(DecodeMee, next);
3991 if (nextend == NULL)
3994 /* did we find two partitions */
3995 if ((next != NULL) &&
3999 while ((ptr < next) &&
4006 * did we find a gab just filled with blanks?
4007 * if not, copy its stuff over.
4011 StrBufAppendBufPlain(Target,
4017 /* our next-pair is our new first pair now. */
4023 nextend = DecodeMee->buf + DecodeMee->BufUsed;
4024 if ((end != NULL) && (end < nextend)) {
4026 while ( (ptr < nextend) &&
4033 StrBufAppendBufPlain(Target, end, nextend - end, 0);
4035 FreeStrBuf(&DecodedInvalidBuf);
4038 /*******************************************************************************
4039 * Manipulating UTF-8 Strings *
4040 *******************************************************************************/
4044 * @brief evaluate the length of an utf8 special character sequence
4045 * @param Char the character to examine
4046 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4048 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4051 unsigned char test = (1<<7);
4053 if ((*CharS & 0xC0) != 0xC0)
4057 ((test & ((unsigned char)*CharS)) != 0))
4062 if ((n > 6) || ((CharE - CharS) < n))
4069 * @brief detect whether this char starts an utf-8 encoded char
4070 * @param Char character to inspect
4071 * @returns yes or no
4073 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4075 /** 11??.???? indicates an UTF8 Sequence. */
4076 return ((Char & 0xC0) == 0xC0);
4081 * @brief measure the number of glyphs in an UTF8 string...
4082 * @param Buf string to measure
4083 * @returns the number of glyphs in Buf
4085 long StrBuf_Utf8StrLen(StrBuf *Buf)
4091 if ((Buf == NULL) || (Buf->BufUsed == 0))
4094 eptr = Buf->buf + Buf->BufUsed;
4095 while ((aptr < eptr) && (*aptr != '\0')) {
4096 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4097 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4098 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4111 * @brief cuts a string after maxlen glyphs
4112 * @param Buf string to cut to maxlen glyphs
4113 * @param maxlen how long may the string become?
4114 * @returns current length of the string
4116 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4122 eptr = Buf->buf + Buf->BufUsed;
4123 while ((aptr < eptr) && (*aptr != '\0')) {
4124 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4125 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4126 while ((*aptr++ != '\0') && (m-- > 0));
4135 Buf->BufUsed = aptr - Buf->buf;
4136 return Buf->BufUsed;
4139 return Buf->BufUsed;
4147 /*******************************************************************************
4149 *******************************************************************************/
4152 * @ingroup StrBuf_DeEnCoder
4153 * @brief uses the same calling syntax as compress2(), but it
4154 * creates a stream compatible with HTTP "Content-encoding: gzip"
4155 * @param dest compressed buffer
4156 * @param destLen length of the compresed data
4157 * @param source source to encode
4158 * @param sourceLen length of source to encode
4159 * @param level compression level
4162 int ZEXPORT compress_gzip(Bytef * dest,
4164 const Bytef * source,
4168 /* write gzip header */
4169 snprintf((char *) dest, *destLen,
4170 "%c%c%c%c%c%c%c%c%c%c",
4171 gz_magic[0], gz_magic[1], Z_DEFLATED,
4172 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4175 /* normal deflate */
4178 stream.next_in = (Bytef *) source;
4179 stream.avail_in = (uInt) sourceLen;
4180 stream.next_out = dest + 10L; // after header
4181 stream.avail_out = (uInt) * destLen;
4182 if ((uLong) stream.avail_out != *destLen)
4185 stream.zalloc = (alloc_func) 0;
4186 stream.zfree = (free_func) 0;
4187 stream.opaque = (voidpf) 0;
4189 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4190 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4194 err = deflate(&stream, Z_FINISH);
4195 if (err != Z_STREAM_END) {
4196 deflateEnd(&stream);
4197 return err == Z_OK ? Z_BUF_ERROR : err;
4199 *destLen = stream.total_out + 10L;
4201 /* write CRC and Length */
4202 uLong crc = crc32(0L, source, sourceLen);
4204 for (n = 0; n < 4; ++n, ++*destLen) {
4205 dest[*destLen] = (int) (crc & 0xff);
4208 uLong len = stream.total_in;
4209 for (n = 0; n < 4; ++n, ++*destLen) {
4210 dest[*destLen] = (int) (len & 0xff);
4213 err = deflateEnd(&stream);
4220 * @ingroup StrBuf_DeEnCoder
4221 * @brief compress the buffer with gzip
4222 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4223 * @param Buf buffer whose content is to be gzipped
4225 int CompressBuffer(StrBuf *Buf)
4228 char *compressed_data = NULL;
4229 size_t compressed_len, bufsize;
4232 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
4233 compressed_data = malloc(compressed_len);
4235 if (compressed_data == NULL)
4237 /* Flush some space after the used payload so valgrind shuts up... */
4238 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4239 Buf->buf[Buf->BufUsed + i++] = '\0';
4240 if (compress_gzip((Bytef *) compressed_data,
4243 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4246 Buf->buf = compressed_data;
4247 Buf->BufUsed = compressed_len;
4248 Buf->BufSize = bufsize;
4249 /* Flush some space after the used payload so valgrind shuts up... */
4251 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4252 Buf->buf[Buf->BufUsed + i++] = '\0';
4255 free(compressed_data);
4257 #endif /* HAVE_ZLIB */
4261 /*******************************************************************************
4262 * File I/O; Callbacks to libevent *
4263 *******************************************************************************/
4265 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4270 if ((FB == NULL) || (FB->Buf == NULL))
4274 * check whether the read pointer is somewhere in a range
4275 * where a cut left is inexpensive
4278 if (FB->ReadWritePointer != NULL)
4282 already_read = FB->ReadWritePointer - FB->Buf->buf;
4283 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4285 if (already_read != 0) {
4288 unread = FB->Buf->BufUsed - already_read;
4290 /* else nothing to compact... */
4292 FB->ReadWritePointer = FB->Buf->buf;
4293 bufremain = FB->Buf->BufSize;
4295 else if ((unread < 64) ||
4296 (bufremain < already_read))
4299 * if its just a tiny bit remaining, or we run out of space...
4302 FB->Buf->BufUsed = unread;
4303 if (unread < already_read)
4304 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4306 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4307 FB->ReadWritePointer = FB->Buf->buf;
4308 bufremain = FB->Buf->BufSize - unread - 1;
4310 else if (bufremain < (FB->Buf->BufSize / 10))
4312 /* get a bigger buffer */
4314 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4316 FB->ReadWritePointer = FB->Buf->buf + unread;
4318 bufremain = FB->Buf->BufSize - unread - 1;
4319 /*TODO: special increase function that won't copy the already read! */
4322 else if (bufremain < 10) {
4323 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4325 FB->ReadWritePointer = FB->Buf->buf;
4327 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4332 FB->ReadWritePointer = FB->Buf->buf;
4333 bufremain = FB->Buf->BufSize - 1;
4336 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4339 FB->Buf->BufUsed += n;
4340 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4345 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4350 if ((FB == NULL) || (FB->Buf == NULL))
4353 if (FB->ReadWritePointer != NULL)
4355 WriteRemain = FB->Buf->BufUsed -
4356 (FB->ReadWritePointer -
4360 FB->ReadWritePointer = FB->Buf->buf;
4361 WriteRemain = FB->Buf->BufUsed;
4364 n = write(fd, FB->ReadWritePointer, WriteRemain);
4366 FB->ReadWritePointer += n;
4368 if (FB->ReadWritePointer ==
4369 FB->Buf->buf + FB->Buf->BufUsed)
4371 FlushStrBuf(FB->Buf);
4372 FB->ReadWritePointer = NULL;
4375 // check whether we've got something to write
4376 // get the maximum chunk plus the pointer we can send
4377 // write whats there
4378 // if not all was sent, remember the send pointer for the next time
4379 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4385 * @ingroup StrBuf_IO
4386 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4387 * @param LineBuf your line will be copied here.
4388 * @param FB BLOB with lines of text...
4389 * @param Ptr moved arround to keep the next-line across several iterations
4390 * has to be &NULL on start; will be &NotNULL on end of buffer
4391 * @returns size of copied buffer
4393 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4395 const char *aptr, *ptr, *eptr;
4398 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4402 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4403 FB->ReadWritePointer = StrBufNOTNULL;
4407 FlushStrBuf(LineBuf);
4408 if (FB->ReadWritePointer == NULL)
4409 ptr = aptr = FB->Buf->buf;
4411 ptr = aptr = FB->ReadWritePointer;
4413 optr = LineBuf->buf;
4414 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4415 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4417 while ((ptr <= eptr) &&
4424 LineBuf->BufUsed = optr - LineBuf->buf;
4425 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4426 optr = LineBuf->buf + LineBuf->BufUsed;
4427 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4432 if (optr > LineBuf->buf)
4434 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4435 LineBuf->BufUsed = optr - LineBuf->buf;
4437 if ((FB->ReadWritePointer != NULL) &&
4438 (FB->ReadWritePointer != FB->Buf->buf))
4440 /* Ok, the client application read all the data
4441 it was interested in so far. Since there is more to read,
4442 we now shrink the buffer, and move the rest over.
4444 StrBufCutLeft(FB->Buf,
4445 FB->ReadWritePointer - FB->Buf->buf);
4446 FB->ReadWritePointer = FB->Buf->buf;
4448 return eMustReadMore;
4451 LineBuf->BufUsed = optr - LineBuf->buf;
4453 if ((ptr <= eptr) && (*ptr == '\r'))
4455 if ((ptr <= eptr) && (*ptr == '\n'))
4459 FB->ReadWritePointer = ptr;
4462 FlushStrBuf(FB->Buf);
4463 FB->ReadWritePointer = NULL;
4466 return eReadSuccess;
4470 * @ingroup StrBuf_CHUNKED_IO
4471 * @brief check whether the chunk-buffer has more data waiting or not.
4472 * @param FB Chunk-Buffer to inspect
4474 eReadState StrBufCheckBuffer(IOBuffer *FB)
4478 if (FB->Buf->BufUsed == 0)
4479 return eReadSuccess;
4480 if (FB->ReadWritePointer == NULL)
4481 return eBufferNotEmpty;
4482 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4483 return eBufferNotEmpty;
4484 return eReadSuccess;
4487 long IOBufferStrLength(IOBuffer *FB)
4489 if ((FB == NULL) || (FB->Buf == NULL))
4491 if (FB->ReadWritePointer == NULL)
4492 return StrLength(FB->Buf);
4494 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4498 /*******************************************************************************
4499 * File I/O; Prefer buffered read since its faster! *
4500 *******************************************************************************/
4503 * @ingroup StrBuf_IO
4504 * @brief Read a line from socket
4505 * flushes and closes the FD on error
4506 * @param buf the buffer to get the input to
4507 * @param fd pointer to the filedescriptor to read
4508 * @param append Append to an existing string or replace?
4509 * @param Error strerror() on error
4510 * @returns numbers of chars read
4512 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4514 int len, rlen, slen;
4516 if ((buf == NULL) || (buf->buf == NULL)) {
4517 *Error = strerror(EINVAL);
4524 slen = len = buf->BufUsed;
4526 rlen = read(*fd, &buf->buf[len], 1);
4528 *Error = strerror(errno);
4535 if (buf->buf[len] == '\n')
4537 if (buf->buf[len] != '\r')
4539 if (len + 2 >= buf->BufSize) {
4541 buf->buf[len+1] = '\0';
4542 IncreaseBuf(buf, 1, -1);
4546 buf->buf[len] = '\0';
4552 * @ingroup StrBuf_BufferedIO
4553 * @brief Read a line from socket
4554 * flushes and closes the FD on error
4555 * @param Line the line to read from the fd / I/O Buffer
4556 * @param buf the buffer to get the input to
4557 * @param fd pointer to the filedescriptor to read
4558 * @param timeout number of successless selects until we bail out
4559 * @param selectresolution how long to wait on each select
4560 * @param Error strerror() on error
4561 * @returns numbers of chars read
4563 int StrBufTCP_read_buffered_line(StrBuf *Line,
4567 int selectresolution,
4571 int nSuccessLess = 0;
4578 if (buf->BufUsed > 0) {
4579 pch = strchr(buf->buf, '\n');
4582 len = pch - buf->buf;
4583 if (len > 0 && (*(pch - 1) == '\r') )
4585 StrBufSub(Line, buf, 0, len - rlen);
4586 StrBufCutLeft(buf, len + 1);
4591 if (buf->BufSize - buf->BufUsed < 10)
4592 IncreaseBuf(buf, 1, -1);
4594 fdflags = fcntl(*fd, F_GETFL);
4595 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4597 while ((nSuccessLess < timeout) && (pch == NULL)) {
4599 tv.tv_sec = selectresolution;
4604 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4605 *Error = strerror(errno);
4611 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4616 &buf->buf[buf->BufUsed],
4617 buf->BufSize - buf->BufUsed - 1);
4619 *Error = strerror(errno);
4624 else if (rlen > 0) {
4626 buf->BufUsed += rlen;
4627 buf->buf[buf->BufUsed] = '\0';
4628 pch = strchr(buf->buf, '\n');
4629 if ((pch == NULL) &&
4630 (buf->BufUsed + 10 > buf->BufSize) &&
4631 (IncreaseBuf(buf, 1, -1) == -1))
4639 len = pch - buf->buf;
4640 if (len > 0 && (*(pch - 1) == '\r') )
4642 StrBufSub(Line, buf, 0, len - rlen);
4643 StrBufCutLeft(buf, len + 1);
4650 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4651 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4652 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4654 * @ingroup StrBuf_BufferedIO
4655 * @brief Read a line from socket
4656 * flushes and closes the FD on error
4657 * @param Line where to append our Line read from the fd / I/O Buffer;
4658 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4659 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4660 * @param fd pointer to the filedescriptor to read
4661 * @param timeout number of successless selects until we bail out
4662 * @param selectresolution how long to wait on each select
4663 * @param Error strerror() on error
4664 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4666 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4671 int selectresolution,
4674 const char *pche = NULL;
4675 const char *pos = NULL;
4677 int len, rlen, retlen;
4678 int nSuccessLess = 0;
4680 const char *pch = NULL;
4686 if ((Line == NULL) ||
4693 *Error = ErrRBLF_PreConditionFailed;
4698 if ((IOBuf->BufUsed > 0) &&
4700 (pos < IOBuf->buf + IOBuf->BufUsed))
4704 pche = IOBuf->buf + IOBuf->BufUsed;
4708 while ((pch < pche) && (*pch != '\n'))
4710 if (Line->BufUsed + 10 > Line->BufSize)
4713 apos = pcht - Line->buf;
4715 IncreaseBuf(Line, 1, -1);
4716 pcht = Line->buf + apos;
4724 if (len > 0 && (*(pch - 1) == '\r') )
4733 if ((pch >= pche) || (*pch == '\0'))
4741 if ((pch != NULL) &&
4744 if (pch + 1 >= pche) {
4757 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4759 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4760 IncreaseBuf(IOBuf, 1, -1);
4762 fdflags = fcntl(*fd, F_GETFL);
4763 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4766 while ((nSuccessLess < timeout) &&
4776 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4777 *Error = strerror(errno);
4781 *Error = ErrRBLF_SelectFailed;
4784 if (! FD_ISSET(*fd, &rfds) != 0) {
4790 &IOBuf->buf[IOBuf->BufUsed],
4791 IOBuf->BufSize - IOBuf->BufUsed - 1);
4793 *Error = strerror(errno);
4798 else if (rlen > 0) {
4800 pLF = IOBuf->buf + IOBuf->BufUsed;
4801 IOBuf->BufUsed += rlen;
4802 IOBuf->buf[IOBuf->BufUsed] = '\0';
4804 pche = IOBuf->buf + IOBuf->BufUsed;
4806 while ((pLF < pche) && (*pLF != '\n'))
4808 if ((pLF >= pche) || (*pLF == '\0'))
4811 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4815 if (pLF != NULL) apos = pLF - IOBuf->buf;
4816 IncreaseBuf(IOBuf, 1, -1);
4817 if (pLF != NULL) pLF = IOBuf->buf + apos;
4831 if (len > 0 && (*(pLF - 1) == '\r') )
4833 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4834 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4840 return retlen + len;
4842 *Error = ErrRBLF_NotEnoughSentFromServer;
4847 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4849 * @ingroup StrBuf_IO
4850 * @brief Input binary data from socket
4851 * flushes and closes the FD on error
4852 * @param Buf the buffer to get the input to
4853 * @param fd pointer to the filedescriptor to read
4854 * @param append Append to an existing string or replace?
4855 * @param nBytes the maximal number of bytes to read
4856 * @param Error strerror() on error
4857 * @returns numbers of chars read
4859 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4870 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4872 *Error = ErrRBLF_BLOBPreConditionFailed;
4877 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4878 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4880 ptr = Buf->buf + Buf->BufUsed;
4882 fdflags = fcntl(*fd, F_GETFL);
4883 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4885 while ((nRead < nBytes) &&
4895 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4896 *Error = strerror(errno);
4900 *Error = ErrRBLF_SelectFailed;
4903 if (! FD_ISSET(*fd, &rfds) != 0) {
4909 if ((rlen = read(*fd,
4911 nBytes - nRead)) == -1) {
4914 *Error = strerror(errno);
4919 Buf->BufUsed += rlen;
4921 Buf->buf[Buf->BufUsed] = '\0';
4925 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4926 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4928 * @ingroup StrBuf_BufferedIO
4929 * @brief Input binary data from socket
4930 * flushes and closes the FD on error
4931 * @param Blob put binary thing here
4932 * @param IOBuf the buffer to get the input to
4933 * @param Pos offset inside of IOBuf
4934 * @param fd pointer to the filedescriptor to read
4935 * @param append Append to an existing string or replace?
4936 * @param nBytes the maximal number of bytes to read
4937 * @param check whether we should search for '000\n' terminators in case of timeouts
4938 * @param Error strerror() on error
4939 * @returns numbers of chars read
4941 int StrBufReadBLOBBuffered(StrBuf *Blob,
4954 int nAlreadyRead = 0;
4959 int nSuccessLess = 0;
4962 if ((Blob == NULL) ||
4969 *Error = ErrRBB_BLOBFPreConditionFailed;
4975 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4976 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4981 rlen = pos - IOBuf->buf;
4983 rlen = IOBuf->BufUsed - rlen;
4986 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed))
4988 if (rlen < nBytes) {
4989 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4990 Blob->BufUsed += rlen;
4991 Blob->buf[Blob->BufUsed] = '\0';
4992 nAlreadyRead = nRead = rlen;
4995 if (rlen >= nBytes) {
4996 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4997 Blob->BufUsed += nBytes;
4998 Blob->buf[Blob->BufUsed] = '\0';
4999 if (rlen == nBytes) {
5011 if (IOBuf->BufSize < nBytes - nRead) {
5012 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5016 fdflags = fcntl(*fd, F_GETFL);
5017 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5025 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
5032 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5033 *Error = strerror(errno);
5036 if (*Error == NULL) {
5037 *Error = ErrRBLF_SelectFailed;
5041 if (! FD_ISSET(*fd, &rfds) != 0) {
5046 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
5047 // if (rlen == -1) { 2021feb27 ajc changed this, apparently we will always get at least 1 byte unless the connection is broken
5051 *Error = strerror(errno);
5054 else if (rlen == 0){
5055 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
5056 StrBufPlain(Blob, HKEY("\n000\n"));
5057 StrBufCutRight(Blob, 5);
5058 return Blob->BufUsed;
5060 else if (!IsNonBlock)
5062 else if (nSuccessLess > MaxTries) {
5064 *Error = ErrRBB_too_many_selects;
5068 else if (rlen > 0) {
5072 IOBuf->BufUsed += rlen;
5075 if (nSuccessLess >= MaxTries) {
5077 *Error = ErrRBB_too_many_selects;
5081 if (nRead > nBytes) {
5082 *Pos = IOBuf->buf + nBytes;
5084 Blob->buf[Blob->BufUsed] = '\0';
5085 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5089 return nRead + nAlreadyRead;
5093 * @ingroup StrBuf_IO
5094 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5095 * @param LineBuf your line will be copied here.
5096 * @param Buf BLOB with lines of text...
5097 * @param Ptr moved arround to keep the next-line across several iterations
5098 * has to be &NULL on start; will be &NotNULL on end of buffer
5099 * @returns size of remaining buffer
5101 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5103 const char *aptr, *ptr, *eptr;
5106 if ((Buf == NULL) ||
5107 (*Ptr == StrBufNOTNULL) ||
5109 (LineBuf->buf == NULL))
5111 *Ptr = StrBufNOTNULL;
5115 FlushStrBuf(LineBuf);
5117 ptr = aptr = Buf->buf;
5121 optr = LineBuf->buf;
5122 eptr = Buf->buf + Buf->BufUsed;
5123 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5125 while ((ptr <= eptr) &&
5132 LineBuf->BufUsed = optr - LineBuf->buf;
5133 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5134 optr = LineBuf->buf + LineBuf->BufUsed;
5135 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5139 if ((ptr >= eptr) && (optr > LineBuf->buf))
5141 LineBuf->BufUsed = optr - LineBuf->buf;
5143 if ((ptr <= eptr) && (*ptr == '\r'))
5145 if ((ptr <= eptr) && (*ptr == '\n'))
5152 *Ptr = StrBufNOTNULL;
5155 return Buf->BufUsed - (ptr - Buf->buf);
5160 * @ingroup StrBuf_IO
5161 * @brief removes double slashes from pathnames
5162 * @param Dir directory string to filter
5163 * @param RemoveTrailingSlash allows / disallows trailing slashes
5165 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5171 while (!IsEmptyStr(a)) {
5183 if ((RemoveTrailingSlash) &&
5189 Dir->BufUsed = b - Dir->buf;
5194 * Decode a quoted-printable encoded StrBuf buffer "in place"
5195 * This is possible because the decoded will always be shorter than the encoded
5196 * so we don't have to worry about the buffer being to small.
5198 void StrBufDecodeQP(StrBuf *Buf)
5200 if (!Buf) { // sanity check #1
5204 int source_len = StrLength(Buf);
5205 if (source_len < 1) { // sanity check #2
5209 int spos = 0; // source position
5210 int tpos = 0; // target position
5212 while (spos < source_len) {
5213 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
5216 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
5219 else if (Buf->buf[spos] == '=') {
5222 sscanf(&Buf->buf[spos], "%02x", &ch);
5223 Buf->buf[tpos++] = ch;
5227 Buf->buf[tpos++] = Buf->buf[spos++];
5232 Buf->BufUsed = tpos;