2 * Copyright (c) 1987-2013 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #include "libcitadel.h"
35 #include "b64/cencode.h"
36 #include "b64/cdecode.h"
52 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
53 const Bytef * source, uLong sourceLen, int level);
55 int BaseStrBufSize = 64;
57 int ZLibCompressionRatio = -1; /* defaults to 6 */
59 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
60 #define OS_CODE 0x03 /*< unix */
61 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
64 const char *StrBufNOTNULL = ((char*) NULL) - 1;
66 const char HexList[256][3] = {
67 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
68 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
69 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
70 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
71 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
72 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
73 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
74 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
75 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
76 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
77 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
78 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
79 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
80 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
81 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
82 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
85 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
86 * StrBuf is a versatile class, aiding the handling of dynamic strings
87 * * reduce de/reallocations
88 * * reduce the need to remeasure it
89 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
90 * * allow asyncroneous IO for line and Blob based operations
91 * * reduce the use of memove in those
92 * * Quick filling in several operations with append functions
96 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
101 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
103 * use these operators to interfere with code demanding char*;
104 * if you need to own the content, smash me. Avoid, since we loose the length information.
108 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
110 * operations to get your Strings into a StrBuf, manipulating them, or appending
113 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
115 * Quick tokenizer; demands of the user to pull its tokens in sequence
119 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
121 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
125 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
127 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
128 * External Cursor to maintain the current read position inside of the buffer
129 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
133 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
139 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
141 * these functions translate the content of a buffer into another representation;
142 * some are combined Fillers and encoders
146 * Private Structure for the Stringbuffer
149 char *buf; /**< the pointer to the dynamic buffer */
150 long BufSize; /**< how many spcae do we optain */
151 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
152 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
154 long nIncreases; /**< for profiling; cound how many times we needed more */
155 char bt [SIZ]; /**< Stacktrace of last increase */
156 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
161 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
162 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
165 #ifdef HAVE_BACKTRACE
166 static void StrBufBacktrace(StrBuf *Buf, int which)
170 void *stack_frames[50];
175 pstart = pch = Buf->bt;
177 pstart = pch = Buf->bt_lastinc;
178 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
179 strings = backtrace_symbols(stack_frames, size);
180 for (i = 0; i < size; i++) {
182 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
184 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
193 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
195 if (hFreeDbglog == -1){
196 pid_t pid = getpid();
198 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
199 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
201 if ((*FreeMe)->nIncreases > 0)
205 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
207 (*FreeMe)->nIncreases,
211 (*FreeMe)->bt_lastinc);
212 n = write(hFreeDbglog, buf, n);
218 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
222 n = write(hFreeDbglog, buf, n);
226 void dbg_IncreaseBuf(StrBuf *IncMe)
229 #ifdef HAVE_BACKTRACE
230 StrBufBacktrace(Buf, 1);
234 void dbg_Init(StrBuf *Buf)
238 Buf->bt_lastinc[0] = '\0';
239 #ifdef HAVE_BACKTRACE
240 StrBufBacktrace(Buf, 0);
246 #define dbg_FreeStrBuf(a, b)
247 #define dbg_IncreaseBuf(a)
254 * @brief swaps the contents of two StrBufs
255 * this is to be used to have cheap switched between a work-buffer and a target buffer
257 * @param B second one
259 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
263 memcpy(&C, A, sizeof(*A));
264 memcpy(A, B, sizeof(*B));
265 memcpy(B, &C, sizeof(C));
270 * @ingroup StrBuf_Cast
271 * @brief Cast operator to Plain String
272 * @note if the buffer is altered by StrBuf operations, this pointer may become
273 * invalid. So don't lean on it after altering the buffer!
274 * Since this operation is considered cheap, rather call it often than risking
275 * your pointer to become invalid!
276 * @param Str the string we want to get the c-string representation for
277 * @returns the Pointer to the Content. Don't mess with it!
279 inline const char *ChrPtr(const StrBuf *Str)
287 * @ingroup StrBuf_Cast
288 * @brief since we know strlen()'s result, provide it here.
289 * @param Str the string to return the length to
290 * @returns contentlength of the buffer
292 inline int StrLength(const StrBuf *Str)
294 return (Str != NULL) ? Str->BufUsed : 0;
298 * @ingroup StrBuf_DeConstructors
299 * @brief local utility function to resize the buffer
300 * @param Buf the buffer whichs storage we should increase
301 * @param KeepOriginal should we copy the original buffer or just start over with a new one
302 * @param DestSize what should fit in after?
304 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
307 size_t NewSize = Buf->BufSize * 2;
313 while ((NewSize <= DestSize) && (NewSize != 0))
319 NewBuf= (char*) malloc(NewSize);
323 if (KeepOriginal && (Buf->BufUsed > 0))
325 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
334 Buf->BufSize = NewSize;
336 dbg_IncreaseBuf(Buf);
342 * @ingroup StrBuf_DeConstructors
343 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
344 * @param Buf Buffer to shrink (has to be empty)
345 * @param ThreshHold if the buffer is bigger then this, its readjusted
346 * @param NewSize if we Shrink it, how big are we going to be afterwards?
348 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
351 (Buf->BufUsed == 0) &&
352 (Buf->BufSize < ThreshHold)) {
354 Buf->buf = (char*) malloc(NewSize);
356 Buf->BufSize = NewSize;
361 * @ingroup StrBuf_DeConstructors
362 * @brief shrink long term buffers to their real size so they don't waste memory
363 * @param Buf buffer to shrink
364 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
365 * @returns physical size of the buffer
367 long StrBufShrinkToFit(StrBuf *Buf, int Force)
372 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
376 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
380 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
381 Buf->BufSize = Buf->BufUsed + 1;
389 * @ingroup StrBuf_DeConstructors
390 * @brief Allocate a new buffer with default buffer size
391 * @returns the new stringbuffer
393 StrBuf* NewStrBuf(void)
397 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
401 NewBuf->buf = (char*) malloc(BaseStrBufSize);
402 if (NewBuf->buf == NULL)
407 NewBuf->buf[0] = '\0';
408 NewBuf->BufSize = BaseStrBufSize;
410 NewBuf->ConstBuf = 0;
418 * @ingroup StrBuf_DeConstructors
419 * @brief Copy Constructor; returns a duplicate of CopyMe
420 * @param CopyMe Buffer to faxmilate
421 * @returns the new stringbuffer
423 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
430 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
434 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
435 if (NewBuf->buf == NULL)
441 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
442 NewBuf->BufUsed = CopyMe->BufUsed;
443 NewBuf->BufSize = CopyMe->BufSize;
444 NewBuf->ConstBuf = 0;
452 * @ingroup StrBuf_DeConstructors
453 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
454 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
455 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
456 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
457 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
458 * @returns the new stringbuffer
460 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
464 if (CreateRelpaceMe == NULL)
469 if (*CreateRelpaceMe != NULL)
470 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
472 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
476 if (CopyFlushMe == NULL)
478 if (*CreateRelpaceMe != NULL)
479 FlushStrBuf(*CreateRelpaceMe);
481 *CreateRelpaceMe = NewStrBuf();
486 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
487 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
488 * be a big IO-Buffer...
490 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
492 if (*CreateRelpaceMe == NULL)
494 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
499 NewBuf = *CreateRelpaceMe;
502 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
506 if (*CreateRelpaceMe == NULL)
508 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
512 NewBuf = *CreateRelpaceMe;
513 SwapBuffers (NewBuf, CopyFlushMe);
516 FlushStrBuf(CopyFlushMe);
521 * @ingroup StrBuf_DeConstructors
522 * @brief create a new Buffer using an existing c-string
523 * this function should also be used if you want to pre-suggest
524 * the buffer size to allocate in conjunction with ptr == NULL
525 * @param ptr the c-string to copy; may be NULL to create a blank instance
526 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
527 * @returns the new stringbuffer
529 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
532 size_t Siz = BaseStrBufSize;
535 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
540 CopySize = strlen((ptr != NULL)?ptr:"");
544 while ((Siz <= CopySize) && (Siz != 0))
553 NewBuf->buf = (char*) malloc(Siz);
554 if (NewBuf->buf == NULL)
559 NewBuf->BufSize = Siz;
561 memcpy(NewBuf->buf, ptr, CopySize);
562 NewBuf->buf[CopySize] = '\0';
563 NewBuf->BufUsed = CopySize;
566 NewBuf->buf[0] = '\0';
569 NewBuf->ConstBuf = 0;
577 * @ingroup StrBuf_DeConstructors
578 * @brief Set an existing buffer from a c-string
579 * @param Buf buffer to load
580 * @param ptr c-string to put into
581 * @param nChars set to -1 if we should work 0-terminated
582 * @returns the new length of the string
584 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
599 CopySize = strlen(ptr);
603 while ((Siz <= CopySize) && (Siz != 0))
611 if (Siz != Buf->BufSize)
612 IncreaseBuf(Buf, 0, Siz);
613 memcpy(Buf->buf, ptr, CopySize);
614 Buf->buf[CopySize] = '\0';
615 Buf->BufUsed = CopySize;
622 * @ingroup StrBuf_DeConstructors
623 * @brief use strbuf as wrapper for a string constant for easy handling
624 * @param StringConstant a string to wrap
625 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
627 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
631 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
634 NewBuf->buf = (char*) StringConstant;
635 NewBuf->BufSize = SizeOfStrConstant;
636 NewBuf->BufUsed = SizeOfStrConstant;
637 NewBuf->ConstBuf = 1;
646 * @ingroup StrBuf_DeConstructors
647 * @brief flush the content of a Buf; keep its struct
648 * @param buf Buffer to flush
650 int FlushStrBuf(StrBuf *buf)
652 if ((buf == NULL) || (buf->buf == NULL))
662 * @ingroup StrBuf_DeConstructors
663 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
664 * @param buf Buffer to wipe
666 int FLUSHStrBuf(StrBuf *buf)
672 if (buf->BufUsed > 0) {
673 memset(buf->buf, 0, buf->BufUsed);
680 int hFreeDbglog = -1;
683 * @ingroup StrBuf_DeConstructors
684 * @brief Release a Buffer
685 * Its a double pointer, so it can NULL your pointer
686 * so fancy SIG11 appear instead of random results
687 * @param FreeMe Pointer Pointer to the buffer to free
689 void FreeStrBuf (StrBuf **FreeMe)
694 dbg_FreeStrBuf(FreeMe, 'F');
696 if (!(*FreeMe)->ConstBuf)
697 free((*FreeMe)->buf);
703 * @ingroup StrBuf_DeConstructors
704 * @brief flatten a Buffer to the Char * we return
705 * Its a double pointer, so it can NULL your pointer
706 * so fancy SIG11 appear instead of random results
707 * The Callee then owns the buffer and is responsible for freeing it.
708 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
709 * @returns the pointer of the buffer; Callee owns the memory thereafter.
711 char *SmashStrBuf (StrBuf **SmashMe)
715 if ((SmashMe == NULL) || (*SmashMe == NULL))
718 dbg_FreeStrBuf(SmashMe, 'S');
720 Ret = (*SmashMe)->buf;
727 * @ingroup StrBuf_DeConstructors
728 * @brief Release the buffer
729 * If you want put your StrBuf into a Hash, use this as Destructor.
730 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
732 void HFreeStrBuf (void *VFreeMe)
734 StrBuf *FreeMe = (StrBuf*)VFreeMe;
738 dbg_FreeStrBuf(SmashMe, 'H');
740 if (!FreeMe->ConstBuf)
746 /*******************************************************************************
747 * Simple string transformations *
748 *******************************************************************************/
752 * @brief Wrapper around atol
754 long StrTol(const StrBuf *Buf)
759 return atol(Buf->buf);
766 * @brief Wrapper around atoi
768 int StrToi(const StrBuf *Buf)
772 if (Buf->BufUsed > 0)
773 return atoi(Buf->buf);
780 * @brief Checks to see if the string is a pure number
781 * @param Buf The buffer to inspect
782 * @returns 1 if its a pure number, 0, if not.
784 int StrBufIsNumber(const StrBuf *Buf) {
786 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
789 strtoll(Buf->buf, &pEnd, 10);
790 if (pEnd == Buf->buf)
792 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
794 if (Buf->buf == pEnd)
800 * @ingroup StrBuf_Filler
801 * @brief modifies a Single char of the Buf
802 * You can point to it via char* or a zero-based integer
803 * @param Buf The buffer to manipulate
804 * @param ptr char* to zero; use NULL if unused
805 * @param nThChar zero based pointer into the string; use -1 if unused
806 * @param PeekValue The Character to place into the position
808 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
813 nThChar = ptr - Buf->buf;
814 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
816 Buf->buf[nThChar] = PeekValue;
821 * @ingroup StrBuf_Filler
822 * @brief modifies a range of chars of the Buf
823 * You can point to it via char* or a zero-based integer
824 * @param Buf The buffer to manipulate
825 * @param ptr char* to zero; use NULL if unused
826 * @param nThChar zero based pointer into the string; use -1 if unused
827 * @param nChars how many chars are to be flushed?
828 * @param PookValue The Character to place into that area
830 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
835 nThChar = ptr - Buf->buf;
836 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
838 if (nThChar + nChars > Buf->BufUsed)
839 nChars = Buf->BufUsed - nThChar;
841 memset(Buf->buf + nThChar, PookValue, nChars);
842 /* just to be shure... */
843 Buf->buf[Buf->BufUsed] = 0;
848 * @ingroup StrBuf_Filler
849 * @brief Append a StringBuffer to the buffer
850 * @param Buf Buffer to modify
851 * @param AppendBuf Buffer to copy at the end of our buffer
852 * @param Offset Should we start copying from an offset?
854 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
856 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
857 (Buf == NULL) || (Buf->buf == NULL))
860 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
863 AppendBuf->BufUsed + Buf->BufUsed);
865 memcpy(Buf->buf + Buf->BufUsed,
866 AppendBuf->buf + Offset,
867 AppendBuf->BufUsed - Offset);
868 Buf->BufUsed += AppendBuf->BufUsed - Offset;
869 Buf->buf[Buf->BufUsed] = '\0';
874 * @ingroup StrBuf_Filler
875 * @brief Append a C-String to the buffer
876 * @param Buf Buffer to modify
877 * @param AppendBuf Buffer to copy at the end of our buffer
878 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
879 * @param Offset Should we start copying from an offset?
881 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
884 long BufSizeRequired;
886 if ((AppendBuf == NULL) || (Buf == NULL))
890 aps = strlen(AppendBuf + Offset);
892 aps = AppendSize - Offset;
894 BufSizeRequired = Buf->BufUsed + aps + 1;
895 if (Buf->BufSize <= BufSizeRequired)
896 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
898 memcpy(Buf->buf + Buf->BufUsed,
902 Buf->buf[Buf->BufUsed] = '\0';
906 * @ingroup StrBuf_Filler
907 * @brief sprintf like function appending the formated string to the buffer
908 * vsnprintf version to wrap into own calls
909 * @param Buf Buffer to extend by format and Params
910 * @param format printf alike format to add
911 * @param ap va_list containing the items for format
913 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
921 if ((Buf == NULL) || (format == NULL))
924 BufSize = Buf->BufSize;
925 nWritten = Buf->BufSize + 1;
926 Offset = Buf->BufUsed;
927 newused = Offset + nWritten;
929 while (newused >= BufSize) {
931 nWritten = vsnprintf(Buf->buf + Offset,
932 Buf->BufSize - Offset,
935 newused = Offset + nWritten;
936 if (newused >= Buf->BufSize) {
937 if (IncreaseBuf(Buf, 1, newused) == -1)
938 return; /* TODO: error handling? */
939 newused = Buf->BufSize + 1;
942 Buf->BufUsed = Offset + nWritten;
943 BufSize = Buf->BufSize;
950 * @ingroup StrBuf_Filler
951 * @brief sprintf like function appending the formated string to the buffer
952 * @param Buf Buffer to extend by format and Params
953 * @param format printf alike format to add
955 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
963 if ((Buf == NULL) || (format == NULL))
966 BufSize = Buf->BufSize;
967 nWritten = Buf->BufSize + 1;
968 Offset = Buf->BufUsed;
969 newused = Offset + nWritten;
971 while (newused >= BufSize) {
972 va_start(arg_ptr, format);
973 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
974 Buf->BufSize - Buf->BufUsed,
977 newused = Buf->BufUsed + nWritten;
978 if (newused >= Buf->BufSize) {
979 if (IncreaseBuf(Buf, 1, newused) == -1)
980 return; /* TODO: error handling? */
981 newused = Buf->BufSize + 1;
984 Buf->BufUsed += nWritten;
985 BufSize = Buf->BufSize;
992 * @ingroup StrBuf_Filler
993 * @brief sprintf like function putting the formated string into the buffer
994 * @param Buf Buffer to extend by format and Parameters
995 * @param format printf alike format to add
997 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
1002 if ((Buf == NULL) || (format == NULL))
1005 nWritten = Buf->BufSize + 1;
1006 while (nWritten >= Buf->BufSize) {
1007 va_start(arg_ptr, format);
1008 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1010 if (nWritten >= Buf->BufSize) {
1011 if (IncreaseBuf(Buf, 0, 0) == -1)
1012 return; /* TODO: error handling? */
1013 nWritten = Buf->BufSize + 1;
1016 Buf->BufUsed = nWritten ;
1021 * @ingroup StrBuf_Filler
1022 * @brief Callback for cURL to append the webserver reply to a buffer
1023 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1024 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1025 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1026 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1028 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1037 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1038 return size * nmemb;
1044 * @brief extracts a substring from Source into dest
1045 * @param dest buffer to place substring into
1046 * @param Source string to copy substring from
1047 * @param Offset chars to skip from start
1048 * @param nChars number of chars to copy
1049 * @returns the number of chars copied; may be different from nChars due to the size of Source
1051 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1053 size_t NCharsRemain;
1054 if (Offset > Source->BufUsed)
1060 if (Offset + nChars < Source->BufUsed)
1062 if ((nChars >= dest->BufSize) &&
1063 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1065 memcpy(dest->buf, Source->buf + Offset, nChars);
1066 dest->BufUsed = nChars;
1067 dest->buf[dest->BufUsed] = '\0';
1070 NCharsRemain = Source->BufUsed - Offset;
1071 if ((NCharsRemain >= dest->BufSize) &&
1072 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1074 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1075 dest->BufUsed = NCharsRemain;
1076 dest->buf[dest->BufUsed] = '\0';
1077 return NCharsRemain;
1082 * @brief Cut nChars from the start of the string
1083 * @param Buf Buffer to modify
1084 * @param nChars how many chars should be skipped?
1086 void StrBufCutLeft(StrBuf *Buf, int nChars)
1088 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1089 if (nChars >= Buf->BufUsed) {
1093 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1094 Buf->BufUsed -= nChars;
1095 Buf->buf[Buf->BufUsed] = '\0';
1100 * @brief Cut the trailing n Chars from the string
1101 * @param Buf Buffer to modify
1102 * @param nChars how many chars should be trunkated?
1104 void StrBufCutRight(StrBuf *Buf, int nChars)
1106 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1109 if (nChars >= Buf->BufUsed) {
1113 Buf->BufUsed -= nChars;
1114 Buf->buf[Buf->BufUsed] = '\0';
1119 * @brief Cut the string after n Chars
1120 * @param Buf Buffer to modify
1121 * @param AfternChars after how many chars should we trunkate the string?
1122 * @param At if non-null and points inside of our string, cut it there.
1124 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1126 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1128 AfternChars = At - Buf->buf;
1131 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1133 Buf->BufUsed = AfternChars;
1134 Buf->buf[Buf->BufUsed] = '\0';
1140 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1141 * @param Buf the string to modify
1143 void StrBufTrim(StrBuf *Buf)
1146 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1148 while ((Buf->BufUsed > 0) &&
1149 isspace(Buf->buf[Buf->BufUsed - 1]))
1153 Buf->buf[Buf->BufUsed] = '\0';
1155 if (Buf->BufUsed == 0) return;
1157 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1160 if (delta > 0) StrBufCutLeft(Buf, delta);
1164 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1165 * @param Buf the string to modify
1167 void StrBufSpaceToBlank(StrBuf *Buf)
1171 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1174 pche = pch + Buf->BufUsed;
1183 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1188 if ((Buf == NULL) || (Buf->buf == NULL)) {
1192 pRight = strchr(Buf->buf, rightboundary);
1193 if (pRight != NULL) {
1194 StrBufCutAt(Buf, 0, pRight);
1197 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1198 if (pLeft != NULL) {
1199 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1205 * @ingroup StrBuf_Filler
1206 * @brief uppercase the contents of a buffer
1207 * @param Buf the buffer to translate
1209 void StrBufUpCase(StrBuf *Buf)
1213 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1216 pche = pch + Buf->BufUsed;
1217 while (pch < pche) {
1218 *pch = toupper(*pch);
1225 * @ingroup StrBuf_Filler
1226 * @brief lowercase the contents of a buffer
1227 * @param Buf the buffer to translate
1229 void StrBufLowerCase(StrBuf *Buf)
1233 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1236 pche = pch + Buf->BufUsed;
1237 while (pch < pche) {
1238 *pch = tolower(*pch);
1244 /*******************************************************************************
1245 * a tokenizer that kills, maims, and destroys *
1246 *******************************************************************************/
1249 * @ingroup StrBuf_Tokenizer
1250 * @brief Replace a token at a given place with a given length by another token with given length
1251 * @param Buf String where to work on
1252 * @param where where inside of the Buf is the search-token
1253 * @param HowLong How long is the token to be replaced
1254 * @param Repl Token to insert at 'where'
1255 * @param ReplLen Length of repl
1256 * @returns -1 if fail else length of resulting Buf
1258 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1259 const char *Repl, long ReplLen)
1262 if ((Buf == NULL) ||
1263 (where > Buf->BufUsed) ||
1264 (where + HowLong > Buf->BufUsed))
1267 if (where + ReplLen - HowLong > Buf->BufSize)
1268 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1271 memmove(Buf->buf + where + ReplLen,
1272 Buf->buf + where + HowLong,
1273 Buf->BufUsed - where - HowLong);
1275 memcpy(Buf->buf + where,
1278 Buf->BufUsed += ReplLen - HowLong;
1280 return Buf->BufUsed;
1284 * @ingroup StrBuf_Tokenizer
1285 * @brief Counts the numbmer of tokens in a buffer
1286 * @param source String to count tokens in
1287 * @param tok Tokenizer char to count
1288 * @returns numbers of tokenizer chars found
1290 int StrBufNum_tokens(const StrBuf *source, char tok)
1294 if ((source == NULL) || (source->BufUsed == 0))
1296 if ((source->BufUsed == 1) && (*source->buf == tok))
1300 pche = pch + source->BufUsed;
1311 * @ingroup StrBuf_Tokenizer
1312 * @brief a string tokenizer
1313 * @param Source StringBuffer to read into
1314 * @param parmnum n'th Parameter to remove
1315 * @param separator tokenizer character
1316 * @returns -1 if not found, else length of token.
1318 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1321 char *d, *s, *end; /* dest, source */
1324 /* Find desired @parameter */
1325 end = Source->buf + Source->BufUsed;
1327 while ((d <= end) &&
1330 /* End of string, bail! */
1335 if (*d == separator) {
1340 if ((d == NULL) || (d >= end))
1341 return 0; /* @Parameter not found */
1343 /* Find next @parameter */
1345 while ((s <= end) &&
1346 (*s && *s != separator))
1350 if (*s == separator)
1354 /* Hack and slash */
1359 memmove(d, s, Source->BufUsed - (s - Source->buf));
1360 Source->BufUsed += ReducedBy;
1361 Source->buf[Source->BufUsed] = '\0';
1363 else if (d == Source->buf) {
1365 Source->BufUsed = 0;
1369 Source->BufUsed += ReducedBy;
1380 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1382 const StrBuf Temp = {
1395 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1399 * @ingroup StrBuf_Tokenizer
1400 * @brief a string tokenizer
1401 * @param dest Destination StringBuffer
1402 * @param Source StringBuffer to read into
1403 * @param parmnum n'th Parameter to extract
1404 * @param separator tokenizer character
1405 * @returns -1 if not found, else length of token.
1407 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1409 const char *s, *e; //* source * /
1410 int len = 0; //* running total length of extracted string * /
1411 int current_token = 0; //* token currently being processed * /
1414 dest->buf[0] = '\0';
1420 if ((Source == NULL) || (Source->BufUsed ==0)) {
1424 e = s + Source->BufUsed;
1427 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1429 while ((s < e) && !IsEmptyStr(s)) {
1430 if (*s == separator) {
1433 if (len >= dest->BufSize) {
1434 dest->BufUsed = len;
1435 if (IncreaseBuf(dest, 1, -1) < 0) {
1440 if ( (current_token == parmnum) &&
1441 (*s != separator)) {
1442 dest->buf[len] = *s;
1445 else if (current_token > parmnum) {
1451 dest->buf[len] = '\0';
1452 dest->BufUsed = len;
1454 if (current_token < parmnum) {
1455 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1458 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1467 * @ingroup StrBuf_Tokenizer
1468 * @brief a string tokenizer to fetch an integer
1469 * @param Source String containing tokens
1470 * @param parmnum n'th Parameter to extract
1471 * @param separator tokenizer character
1472 * @returns 0 if not found, else integer representation of the token
1474 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1484 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1491 * @ingroup StrBuf_Tokenizer
1492 * @brief a string tokenizer to fetch a long integer
1493 * @param Source String containing tokens
1494 * @param parmnum n'th Parameter to extract
1495 * @param separator tokenizer character
1496 * @returns 0 if not found, else long integer representation of the token
1498 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1508 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1516 * @ingroup StrBuf_Tokenizer
1517 * @brief a string tokenizer to fetch an unsigned long
1518 * @param Source String containing tokens
1519 * @param parmnum n'th Parameter to extract
1520 * @param separator tokenizer character
1521 * @returns 0 if not found, else unsigned long representation of the token
1523 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1534 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1538 return (unsigned long) atol(pnum);
1547 * @ingroup StrBuf_NextTokenizer
1548 * @brief a string tokenizer; Bounds checker
1549 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1550 * @param Source our tokenbuffer
1551 * @param pStart the token iterator pointer to inspect
1552 * @returns whether the revolving pointer is inside of the search range
1554 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1556 if ((Source == NULL) ||
1557 (*pStart == StrBufNOTNULL) ||
1558 (Source->BufUsed == 0))
1562 if (*pStart == NULL)
1566 else if (*pStart > Source->buf + Source->BufUsed)
1570 else if (*pStart <= Source->buf)
1579 * @ingroup StrBuf_NextTokenizer
1580 * @brief a string tokenizer
1581 * @param dest Destination StringBuffer
1582 * @param Source StringBuffer to read into
1583 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1584 * @param separator tokenizer
1585 * @returns -1 if not found, else length of token.
1587 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1589 const char *s; /* source */
1590 const char *EndBuffer; /* end stop of source buffer */
1591 int current_token = 0; /* token currently being processed */
1592 int len = 0; /* running total length of extracted string */
1594 if ((Source == NULL) ||
1595 (Source->BufUsed == 0) )
1597 *pStart = StrBufNOTNULL;
1603 EndBuffer = Source->buf + Source->BufUsed;
1607 dest->buf[0] = '\0';
1612 *pStart = EndBuffer + 1;
1616 if (*pStart == NULL)
1618 *pStart = Source->buf; /* we're starting to examine this buffer. */
1620 else if ((*pStart < Source->buf) ||
1621 (*pStart > EndBuffer ) )
1623 return -1; /* no more tokens to find. */
1627 /* start to find the next token */
1628 while ((s <= EndBuffer) &&
1629 (current_token == 0) )
1631 if (*s == separator)
1633 /* we found the next token */
1637 if (len >= dest->BufSize)
1639 /* our Dest-buffer isn't big enough, increase it. */
1640 dest->BufUsed = len;
1642 if (IncreaseBuf(dest, 1, -1) < 0) {
1643 /* WHUT? no more mem? bail out. */
1650 if ( (current_token == 0 ) && /* are we in our target token? */
1651 (!IsEmptyStr(s) ) &&
1652 (separator != *s) ) /* don't copy the token itself */
1654 dest->buf[len] = *s; /* Copy the payload */
1655 ++len; /* remember the bigger size. */
1661 /* did we reach the end? */
1662 if ((s > EndBuffer)) {
1663 EndBuffer = StrBufNOTNULL;
1664 *pStart = EndBuffer;
1667 *pStart = s; /* remember the position for the next run */
1670 /* sanitize our extracted token */
1671 dest->buf[len] = '\0';
1672 dest->BufUsed = len;
1679 * @ingroup StrBuf_NextTokenizer
1680 * @brief a string tokenizer
1681 * @param Source StringBuffer to read from
1682 * @param pStart pointer to the end of the last token. Feed with NULL.
1683 * @param separator tokenizer character
1684 * @param nTokens number of tokens to fastforward over
1685 * @returns -1 if not found, else length of token.
1687 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1689 const char *s, *EndBuffer; //* source * /
1690 int len = 0; //* running total length of extracted string * /
1691 int current_token = 0; //* token currently being processed * /
1693 if ((Source == NULL) ||
1694 (Source->BufUsed ==0)) {
1698 return Source->BufUsed;
1700 if (*pStart == NULL)
1701 *pStart = Source->buf;
1703 EndBuffer = Source->buf + Source->BufUsed;
1705 if ((*pStart < Source->buf) ||
1706 (*pStart > EndBuffer)) {
1714 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1716 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1717 if (*s == separator) {
1720 if (current_token >= nTokens) {
1732 * @ingroup StrBuf_NextTokenizer
1733 * @brief a string tokenizer to fetch an integer
1734 * @param Source StringBuffer to read from
1735 * @param pStart Cursor on the tokenstring
1736 * @param separator tokenizer character
1737 * @returns 0 if not found, else integer representation of the token
1739 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1749 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1756 * @ingroup StrBuf_NextTokenizer
1757 * @brief a string tokenizer to fetch a long integer
1758 * @param Source StringBuffer to read from
1759 * @param pStart Cursor on the tokenstring
1760 * @param separator tokenizer character
1761 * @returns 0 if not found, else long integer representation of the token
1763 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1773 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1781 * @ingroup StrBuf_NextTokenizer
1782 * @brief a string tokenizer to fetch an unsigned long
1783 * @param Source StringBuffer to read from
1784 * @param pStart Cursor on the tokenstring
1785 * @param separator tokenizer character
1786 * @returns 0 if not found, else unsigned long representation of the token
1788 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1799 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1803 return (unsigned long) atol(pnum);
1813 /*******************************************************************************
1814 * Escape Appending *
1815 *******************************************************************************/
1818 * @ingroup StrBuf_DeEnCoder
1819 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1820 * @param OutBuf the output buffer
1821 * @param In Buffer to encode
1822 * @param PlainIn way in from plain old c strings
1824 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1826 const char *pch, *pche;
1830 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1832 if (PlainIn != NULL) {
1833 len = strlen(PlainIn);
1839 pche = pch + In->BufUsed;
1846 pt = OutBuf->buf + OutBuf->BufUsed;
1847 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1849 while (pch < pche) {
1851 IncreaseBuf(OutBuf, 1, -1);
1852 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1853 pt = OutBuf->buf + OutBuf->BufUsed;
1856 if((*pch >= 'a' && *pch <= 'z') ||
1857 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1858 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1859 (*pch == '!') || (*pch == '_') ||
1860 (*pch == ',') || (*pch == '.'))
1867 *(pt + 1) = HexList[(unsigned char)*pch][0];
1868 *(pt + 2) = HexList[(unsigned char)*pch][1];
1870 OutBuf->BufUsed += 3;
1878 * @ingroup StrBuf_DeEnCoder
1879 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1880 * @param OutBuf the output buffer
1881 * @param In Buffer to encode
1882 * @param PlainIn way in from plain old c strings
1884 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1886 const char *pch, *pche;
1890 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1892 if (PlainIn != NULL) {
1893 len = strlen(PlainIn);
1899 pche = pch + In->BufUsed;
1906 pt = OutBuf->buf + OutBuf->BufUsed;
1907 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1909 while (pch < pche) {
1911 IncreaseBuf(OutBuf, 1, -1);
1912 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1913 pt = OutBuf->buf + OutBuf->BufUsed;
1916 if((*pch >= 'a' && *pch <= 'z') ||
1917 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1918 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1919 (*pch == '!') || (*pch == '_') ||
1920 (*pch == ',') || (*pch == '.'))
1927 *(pt + 1) = HexList[(unsigned char)*pch][0];
1928 *(pt + 2) = HexList[(unsigned char)*pch][1];
1930 OutBuf->BufUsed += 3;
1938 * @ingroup StrBuf_DeEnCoder
1939 * @brief append a string with characters having a special meaning in xml encoded to the buffer
1940 * @param OutBuf the output buffer
1941 * @param In Buffer to encode
1942 * @param PlainIn way in from plain old c strings
1943 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1944 * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1946 void StrBufXMLEscAppend(StrBuf *OutBuf,
1948 const char *PlainIn,
1950 int OverrideLowChars)
1952 const char *pch, *pche;
1957 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1959 if (PlainIn != NULL) {
1961 len = strlen((const char*)PlainIn);
1968 pch = (const char*)In->buf;
1969 pche = pch + In->BufUsed;
1976 pt = OutBuf->buf + OutBuf->BufUsed;
1977 /**< we max append 6 chars at once plus the \0 */
1978 pte = OutBuf->buf + OutBuf->BufSize - 6;
1980 while (pch < pche) {
1982 OutBuf->BufUsed = pt - OutBuf->buf;
1983 IncreaseBuf(OutBuf, 1, -1);
1984 pte = OutBuf->buf + OutBuf->BufSize - 6;
1985 /**< we max append 3 chars at once plus the \0 */
1987 pt = OutBuf->buf + OutBuf->BufUsed;
1991 memcpy(pt, HKEY("<"));
1995 else if (*pch == '>') {
1996 memcpy(pt, HKEY(">"));
2000 else if (*pch == '&') {
2001 memcpy(pt, HKEY("&"));
2005 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2010 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
2013 while ((IsUtf8Sequence > 0) &&
2026 *pt = HexList[*(unsigned char*)pch][0];
2028 *pt = HexList[*(unsigned char*)pch][1];
2037 OutBuf->BufUsed = pt - OutBuf->buf;
2042 * @ingroup StrBuf_DeEnCoder
2043 * @brief append a string in hex encoding to the buffer
2044 * @param OutBuf the output buffer
2045 * @param In Buffer to encode
2046 * @param PlainIn way in from plain old c strings
2047 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2049 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2051 const unsigned char *pch, *pche;
2055 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2057 if (PlainIn != NULL) {
2059 len = strlen((const char*)PlainIn);
2066 pch = (const unsigned char*)In->buf;
2067 pche = pch + In->BufUsed;
2074 pt = OutBuf->buf + OutBuf->BufUsed;
2075 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2077 while (pch < pche) {
2079 IncreaseBuf(OutBuf, 1, -1);
2080 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2081 pt = OutBuf->buf + OutBuf->BufUsed;
2084 *pt = HexList[*pch][0];
2086 *pt = HexList[*pch][1];
2087 pt ++; pch ++; OutBuf->BufUsed += 2;
2092 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2099 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2101 if (PlainIn != NULL) {
2103 len = strlen(PlainIn);
2116 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2118 if (ExpectLen > OutBuf->BufSize)
2119 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2122 pt = OutBuf->buf + OutBuf->BufUsed;
2124 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2127 OutBuf->BufUsed += len;
2132 * @ingroup StrBuf_DeEnCoder
2133 * @brief append a string in hex encoding to the buffer
2134 * @param OutBuf the output buffer
2135 * @param In Buffer to encode
2136 * @param PlainIn way in from plain old c strings
2138 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2140 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2144 * @ingroup StrBuf_DeEnCoder
2145 * @brief Append a string, escaping characters which have meaning in HTML.
2147 * @param Target target buffer
2148 * @param Source source buffer; set to NULL if you just have a C-String
2149 * @param PlainIn Plain-C string to append; set to NULL if unused
2150 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2151 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2152 * if set to 2, linebreaks are replaced by <br/>
2154 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2156 const char *aptr, *eiptr;
2160 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2163 if (PlainIn != NULL) {
2165 len = strlen(PlainIn);
2170 eiptr = aptr + Source->BufUsed;
2171 len = Source->BufUsed;
2177 bptr = Target->buf + Target->BufUsed;
2178 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2180 while (aptr < eiptr){
2182 IncreaseBuf(Target, 1, -1);
2183 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2184 bptr = Target->buf + Target->BufUsed;
2187 memcpy(bptr, "<", 4);
2189 Target->BufUsed += 4;
2191 else if (*aptr == '>') {
2192 memcpy(bptr, ">", 4);
2194 Target->BufUsed += 4;
2196 else if (*aptr == '&') {
2197 memcpy(bptr, "&", 5);
2199 Target->BufUsed += 5;
2201 else if (*aptr == '"') {
2202 memcpy(bptr, """, 6);
2204 Target->BufUsed += 6;
2206 else if (*aptr == '\'') {
2207 memcpy(bptr, "'", 5);
2209 Target->BufUsed += 5;
2211 else if (*aptr == LB) {
2216 else if (*aptr == RB) {
2221 else if (*aptr == QU) {
2226 else if ((*aptr == 32) && (nbsp == 1)) {
2227 memcpy(bptr, " ", 6);
2229 Target->BufUsed += 6;
2231 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2232 *bptr='\0'; /* nothing */
2234 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2235 memcpy(bptr, "<br/>", 11);
2237 Target->BufUsed += 11;
2241 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2242 *bptr='\0'; /* nothing */
2252 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2254 return Target->BufUsed;
2258 * @ingroup StrBuf_DeEnCoder
2259 * @brief Append a string, escaping characters which have meaning in HTML.
2260 * Converts linebreaks into blanks; escapes single quotes
2261 * @param Target target buffer
2262 * @param Source source buffer; set to NULL if you just have a C-String
2263 * @param PlainIn Plain-C string to append; set to NULL if unused
2265 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2267 const char *aptr, *eiptr;
2271 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2274 if (PlainIn != NULL) {
2276 len = strlen(PlainIn);
2281 eiptr = aptr + Source->BufUsed;
2282 len = Source->BufUsed;
2288 eptr = Target->buf + Target->BufSize - 8;
2289 tptr = Target->buf + Target->BufUsed;
2291 while (aptr < eiptr){
2293 IncreaseBuf(Target, 1, -1);
2294 eptr = Target->buf + Target->BufSize - 8;
2295 tptr = Target->buf + Target->BufUsed;
2298 if (*aptr == '\n') {
2302 else if (*aptr == '\r') {
2306 else if (*aptr == '\'') {
2312 Target->BufUsed += 5;
2325 * @ingroup StrBuf_DeEnCoder
2326 * @brief Append a string, escaping characters which have meaning in ICAL.
2328 * @param Target target buffer
2329 * @param Source source buffer; set to NULL if you just have a C-String
2330 * @param PlainIn Plain-C string to append; set to NULL if unused
2332 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2334 const char *aptr, *eiptr;
2338 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2341 if (PlainIn != NULL) {
2343 len = strlen(PlainIn);
2348 eiptr = aptr + Source->BufUsed;
2349 len = Source->BufUsed;
2355 eptr = Target->buf + Target->BufSize - 8;
2356 tptr = Target->buf + Target->BufUsed;
2358 while (aptr < eiptr){
2359 if(tptr + 3 >= eptr) {
2360 IncreaseBuf(Target, 1, -1);
2361 eptr = Target->buf + Target->BufSize - 8;
2362 tptr = Target->buf + Target->BufUsed;
2365 if (*aptr == '\n') {
2372 else if (*aptr == '\r') {
2379 else if (*aptr == ',') {
2395 * @ingroup StrBuf_DeEnCoder
2396 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2398 * @param Target target buffer
2399 * @param Source source buffer; set to NULL if you just have a C-String
2400 * @param PlainIn Plain-C string to append; set to NULL if unused
2401 * @returns size of result or -1
2403 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2405 const char *aptr, *eiptr;
2410 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2413 if (PlainIn != NULL) {
2415 len = strlen(PlainIn);
2420 eiptr = aptr + Source->BufUsed;
2421 len = Source->BufUsed;
2427 bptr = Target->buf + Target->BufUsed;
2428 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2430 while (aptr < eiptr){
2432 IncreaseBuf(Target, 1, -1);
2433 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2434 bptr = Target->buf + Target->BufUsed;
2438 memcpy(bptr, HKEY("\\n"));
2440 Target->BufUsed += 2;
2443 memcpy(bptr, HKEY("\\r"));
2445 Target->BufUsed += 2;
2452 Target->BufUsed += 2;
2455 if ((*(aptr + 1) == 'u') &&
2456 isxdigit(*(aptr + 2)) &&
2457 isxdigit(*(aptr + 3)) &&
2458 isxdigit(*(aptr + 4)) &&
2459 isxdigit(*(aptr + 5)))
2460 { /* oh, a unicode escaper. let it pass through. */
2461 memcpy(bptr, aptr, 6);
2464 Target->BufUsed += 6;
2472 Target->BufUsed += 2;
2480 Target->BufUsed += 2;
2487 Target->BufUsed += 2;
2494 Target->BufUsed += 2;
2497 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2498 while (IsUtf8Sequence > 0){
2501 if (--IsUtf8Sequence)
2509 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2511 return Target->BufUsed;
2515 * @ingroup StrBuf_DeEnCoder
2516 * @brief Append a string, escaping characters which have meaning in HTML + json.
2518 * @param Target target buffer
2519 * @param Source source buffer; set to NULL if you just have a C-String
2520 * @param PlainIn Plain-C string to append; set to NULL if unused
2521 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2522 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2523 * if set to 2, linebreaks are replaced by <br/>
2525 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2527 const char *aptr, *eiptr;
2530 int IsUtf8Sequence = 0;
2532 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2535 if (PlainIn != NULL) {
2537 len = strlen(PlainIn);
2542 eiptr = aptr + Source->BufUsed;
2543 len = Source->BufUsed;
2549 bptr = Target->buf + Target->BufUsed;
2550 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2552 while (aptr < eiptr){
2554 IncreaseBuf(Target, 1, -1);
2555 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2556 bptr = Target->buf + Target->BufUsed;
2560 memcpy(bptr, HKEY("<"));
2562 Target->BufUsed += 4;
2565 memcpy(bptr, HKEY(">"));
2567 Target->BufUsed += 4;
2570 memcpy(bptr, HKEY("&"));
2572 Target->BufUsed += 5;
2585 switch (nolinebreaks) {
2587 *bptr='\0'; /* nothing */
2590 memcpy(bptr, HKEY("<br/>"));
2592 Target->BufUsed += 11;
2595 memcpy(bptr, HKEY("\\n"));
2597 Target->BufUsed += 2;
2601 switch (nolinebreaks) {
2604 *bptr='\0'; /* nothing */
2607 memcpy(bptr, HKEY("\\r"));
2609 Target->BufUsed += 2;
2619 Target->BufUsed += 2;
2622 if ((*(aptr + 1) == 'u') &&
2623 isxdigit(*(aptr + 2)) &&
2624 isxdigit(*(aptr + 3)) &&
2625 isxdigit(*(aptr + 4)) &&
2626 isxdigit(*(aptr + 5)))
2627 { /* oh, a unicode escaper. let it pass through. */
2628 memcpy(bptr, aptr, 6);
2631 Target->BufUsed += 6;
2639 Target->BufUsed += 2;
2647 Target->BufUsed += 2;
2654 Target->BufUsed += 2;
2661 Target->BufUsed += 2;
2665 memcpy(bptr, HKEY(" "));
2667 Target->BufUsed += 6;
2671 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2672 while (IsUtf8Sequence > 0){
2675 if (--IsUtf8Sequence)
2683 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2685 return Target->BufUsed;
2690 * @ingroup StrBuf_DeEnCoder
2691 * @brief replace all non-Ascii characters by another
2692 * @param Buf buffer to inspect
2693 * @param repl charater to stamp over non ascii chars
2695 void StrBufAsciify(StrBuf *Buf, const char repl)
2699 for (offset = 0; offset < Buf->BufUsed; offset ++)
2700 if (!isascii(Buf->buf[offset]))
2701 Buf->buf[offset] = repl;
2706 * @ingroup StrBuf_DeEnCoder
2707 * @brief unhide special chars hidden to the HTML escaper
2708 * @param target buffer to put the unescaped string in
2709 * @param source buffer to unescape
2711 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2716 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2722 FlushStrBuf(target);
2724 len = source->BufUsed;
2725 for (a = 0; a < len; ++a) {
2726 if (target->BufUsed >= target->BufSize)
2727 IncreaseBuf(target, 1, -1);
2729 if (source->buf[a] == '=') {
2730 hex[0] = source->buf[a + 1];
2731 hex[1] = source->buf[a + 2];
2734 sscanf(hex, "%02x", &b);
2735 target->buf[target->BufUsed] = b;
2736 target->buf[++target->BufUsed] = 0;
2740 target->buf[target->BufUsed] = source->buf[a];
2741 target->buf[++target->BufUsed] = 0;
2748 * @ingroup StrBuf_DeEnCoder
2749 * @brief hide special chars from the HTML escapers and friends
2750 * @param target buffer to put the escaped string in
2751 * @param source buffer to escape
2753 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2758 FlushStrBuf(target);
2760 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2765 len = source->BufUsed;
2766 for (i=0; i<len; ++i) {
2767 if (target->BufUsed + 4 >= target->BufSize)
2768 IncreaseBuf(target, 1, -1);
2769 if ( (isalnum(source->buf[i])) ||
2770 (source->buf[i]=='-') ||
2771 (source->buf[i]=='_') ) {
2772 target->buf[target->BufUsed++] = source->buf[i];
2775 sprintf(&target->buf[target->BufUsed],
2777 (0xFF &source->buf[i]));
2778 target->BufUsed += 3;
2781 target->buf[target->BufUsed + 1] = '\0';
2785 /*******************************************************************************
2786 * Quoted Printable de/encoding *
2787 *******************************************************************************/
2790 * @ingroup StrBuf_DeEnCoder
2791 * @brief decode a buffer from base 64 encoding; destroys original
2792 * @param Buf Buffor to transform
2794 int StrBufDecodeBase64(StrBuf *Buf)
2802 xferbuf = (char*) malloc(Buf->BufSize);
2803 if (xferbuf == NULL)
2807 siz = CtdlDecodeBase64(xferbuf,
2814 Buf->buf[Buf->BufUsed] = '\0';
2819 * @ingroup StrBuf_DeEnCoder
2820 * @brief decode a buffer from base 64 encoding; expects targetbuffer
2821 * @param BufIn Buffor to transform
2822 * @param BufOut Buffer to put result into
2824 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2826 if ((BufIn == NULL) || (BufOut == NULL))
2829 if (BufOut->BufSize < BufIn->BufUsed)
2830 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2832 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2835 return BufOut->BufUsed;
2838 typedef struct __z_enc_stream {
2843 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err)
2845 base64_decodestate *state;;
2852 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2853 base64_init_decodestate(state);
2854 return (vStreamT*) state;
2859 z_enc_stream *stream;
2862 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2863 memset(stream, 0, sizeof(z_enc_stream));
2864 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2865 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2867 err = inflateInit(&stream->zstream);
2870 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2874 return (vStreamT*) stream;
2879 z_enc_stream *stream;
2882 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2883 memset(stream, 0, sizeof(z_enc_stream));
2884 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2885 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2886 /* write gzip header */
2887 stream->OutBuf.BufUsed = snprintf
2888 (stream->OutBuf.buf,
2889 stream->OutBuf.BufSize,
2890 "%c%c%c%c%c%c%c%c%c%c",
2891 gz_magic[0], gz_magic[1], Z_DEFLATED,
2892 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2895 err = deflateInit2(&stream->zstream,
2896 ZLibCompressionRatio,
2900 Z_DEFAULT_STRATEGY);
2902 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2906 return (vStreamT*) stream;
2916 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err)
2922 if ((vStream == NULL) || (*vStream==NULL)) {
2923 *Err = strerror(EINVAL);
2935 z_enc_stream *stream = (z_enc_stream *)*vStream;
2936 (void)inflateEnd(&stream->zstream);
2937 free(stream->OutBuf.buf);
2942 z_enc_stream *stream = (z_enc_stream *)*vStream;
2943 err = deflateEnd(&stream->zstream);
2948 free(stream->OutBuf.buf);
2959 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err)
2968 // base64_decodestate *state = (base64_decodestate*)vStream;
2974 pInLen = In->BufUsed;
2976 if ((In == NULL) || (vStream == NULL))
2979 ExpectLen = (pInLen / 4) * 3;
2981 if (Target->BufSize - Target->BufUsed < ExpectLen)
2983 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2986 //// ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2987 Target->BufUsed += ExpectLen;
2988 Target->buf[Target->BufUsed] = '\0';
2995 base64_decodestate *state = (base64_decodestate *)vStream;
3001 pInLen = In->BufUsed;
3003 if ((pIn == NULL) || (vStream == NULL))
3006 ExpectLen = (pInLen / 4) * 3;
3008 if (Target->BufSize - Target->BufUsed < ExpectLen)
3010 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3013 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3014 Target->BufUsed += ExpectLen;
3015 Target->buf[Target->BufUsed] = '\0';
3021 z_enc_stream *stream = (z_enc_stream *)vStream;
3022 int org_outbuf_len = stream->OutBuf.BufUsed;
3024 unsigned int chunkavail;
3026 if (In->ReadWritePointer != NULL)
3028 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3029 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3030 (In->ReadWritePointer - In->Buf->buf);
3034 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3035 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3038 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3039 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3041 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3043 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3047 (stream->OutBuf.BufUsed != org_outbuf_len)
3050 SwapBuffers(Target->Buf, &stream->OutBuf);
3053 if (stream->zstream.avail_in == 0)
3055 FlushStrBuf(In->Buf);
3056 In->ReadWritePointer = NULL;
3060 if (stream->zstream.avail_in < 64)
3062 memmove(In->Buf->buf,
3063 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3064 stream->zstream.avail_in);
3066 In->Buf->BufUsed = stream->zstream.avail_in;
3067 In->Buf->buf[In->Buf->BufUsed] = '\0';
3072 In->ReadWritePointer = In->Buf->buf +
3073 (In->Buf->BufUsed - stream->zstream.avail_in);
3076 rc = (LastChunk && (err != Z_FINISH));
3077 if (!rc && (err != Z_OK)) {
3084 z_enc_stream *stream = (z_enc_stream *)vStream;
3085 int org_outbuf_len = stream->zstream.total_out;
3088 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3089 if (In->ReadWritePointer != NULL)
3091 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3092 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3093 (In->ReadWritePointer - In->Buf->buf);
3097 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3098 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3102 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3103 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3105 err = inflate(&stream->zstream, Z_NO_FLUSH);
3107 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
3110 err = Z_DATA_ERROR; /* and fall through */
3115 (void)inflateEnd(&stream->zstream);
3119 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3121 if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
3123 if (stream->zstream.avail_in == 0)
3125 FlushStrBuf(In->Buf);
3126 In->ReadWritePointer = NULL;
3130 if (stream->zstream.avail_in < 64)
3132 memmove(In->Buf->buf,
3133 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3134 stream->zstream.avail_in);
3136 In->Buf->BufUsed = stream->zstream.avail_in;
3137 In->Buf->buf[In->Buf->BufUsed] = '\0';
3142 In->ReadWritePointer = In->Buf->buf +
3143 (In->Buf->BufUsed - stream->zstream.avail_in);
3157 * @ingroup StrBuf_DeEnCoder
3158 * @brief decode a buffer from base 64 encoding; destroys original
3159 * @param Buf Buffor to transform
3161 int StrBufDecodeHex(StrBuf *Buf)
3164 char *pch, *pche, *pchi;
3166 if (Buf == NULL) return -1;
3168 pch = pchi = Buf->buf;
3169 pche = pch + Buf->BufUsed;
3171 while (pchi < pche){
3172 ch = decode_hex(pchi);
3179 Buf->BufUsed = pch - Buf->buf;
3180 return Buf->BufUsed;
3184 * @ingroup StrBuf_DeEnCoder
3185 * @brief replace all chars >0x20 && < 0x7F with Mute
3186 * @param Mute char to put over invalid chars
3187 * @param Buf Buffor to transform
3189 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3193 if (Buf == NULL) return -1;
3194 pch = (unsigned char *)Buf->buf;
3195 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3196 if ((*pch < 0x20) || (*pch > 0x7F))
3200 return Buf->BufUsed;
3205 * @ingroup StrBuf_DeEnCoder
3206 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3207 * @param Buf Buffer to translate
3208 * @param StripBlanks Reduce several blanks to one?
3210 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3219 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3220 Buf->buf[Buf->BufUsed - 1] = '\0';
3225 while (a < Buf->BufUsed) {
3226 if (Buf->buf[a] == '+')
3228 else if (Buf->buf[a] == '%') {
3229 /* don't let % chars through, rather truncate the input. */
3230 if (a + 2 > Buf->BufUsed) {
3235 hex[0] = Buf->buf[a + 1];
3236 hex[1] = Buf->buf[a + 2];
3239 sscanf(hex, "%02x", &b);
3240 Buf->buf[a] = (char) b;
3241 len = Buf->BufUsed - a - 2;
3243 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3255 * @ingroup StrBuf_DeEnCoder
3256 * @brief RFC2047-encode a header field if necessary.
3257 * If no non-ASCII characters are found, the string
3258 * will be copied verbatim without encoding.
3260 * @param target Target buffer.
3261 * @param source Source string to be encoded.
3262 * @returns encoded length; -1 if non success.
3264 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3266 const char headerStr[] = "=?UTF-8?Q?";
3267 int need_to_encode = 0;
3271 if ((source == NULL) ||
3275 while ((i < source->BufUsed) &&
3276 (!IsEmptyStr (&source->buf[i])) &&
3277 (need_to_encode == 0)) {
3278 if (((unsigned char) source->buf[i] < 32) ||
3279 ((unsigned char) source->buf[i] > 126)) {
3285 if (!need_to_encode) {
3286 if (*target == NULL) {
3287 *target = NewStrBufPlain(source->buf, source->BufUsed);
3290 FlushStrBuf(*target);
3291 StrBufAppendBuf(*target, source, 0);
3294 return (*target)->BufUsed;
3298 if (*target == NULL)
3299 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3300 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3301 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3302 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3303 (*target)->BufUsed = sizeof(headerStr) - 1;
3304 for (i=0; (i < source->BufUsed); ++i) {
3305 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3306 IncreaseBuf(*target, 1, 0);
3307 ch = (unsigned char) source->buf[i];
3316 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3317 (*target)->BufUsed += 3;
3321 (*target)->buf[(*target)->BufUsed] = '_';
3323 (*target)->buf[(*target)->BufUsed] = ch;
3324 (*target)->BufUsed++;
3328 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3329 IncreaseBuf(*target, 1, 0);
3331 (*target)->buf[(*target)->BufUsed++] = '?';
3332 (*target)->buf[(*target)->BufUsed++] = '=';
3333 (*target)->buf[(*target)->BufUsed] = '\0';
3334 return (*target)->BufUsed;;
3338 * @ingroup StrBuf_DeEnCoder
3339 * @brief Quoted-Printable encode a message; make it < 80 columns width.
3340 * @param source Source string to be encoded.
3341 * @returns buffer with encoded message.
3343 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3347 const char *ptr, *eptr;
3351 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3353 OEptr = OutBuf->buf + OutBuf->BufSize;
3354 ptr = EncodeMe->buf;
3355 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3360 if (Optr + 4 >= OEptr)
3363 Offset = Optr - OutBuf->buf;
3364 OutBuf->BufUsed = Optr - OutBuf->buf;
3365 IncreaseBuf(OutBuf, 1, 0);
3366 Optr = OutBuf->buf + Offset;
3367 OEptr = OutBuf->buf + OutBuf->BufSize;
3371 /* ignore carriage returns */
3374 else if (*ptr == '\n') {
3375 /* hard line break */
3376 memcpy(Optr, HKEY("=0A"));
3381 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3382 ( (*ptr >= 62) && (*ptr <= 126) ))
3393 *Optr = HexList[ch][0];
3395 *Optr = HexList[ch][1];
3402 /* soft line break */
3403 if (isspace(*(Optr - 1))) {
3408 *Optr = HexList[ch][0];
3410 *Optr = HexList[ch][1];
3422 OutBuf->BufUsed = Optr - OutBuf->buf;
3428 static void AddRecipient(StrBuf *Target,
3430 StrBuf *EmailAddress,
3435 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3436 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3438 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3439 StrBufRFC2047encode(&EncBuf, UserName);
3440 StrBufAppendBuf(Target, EncBuf, 0);
3441 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3442 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3444 if (StrLength(EmailAddress) > 0){
3445 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3446 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3447 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3453 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3454 * \param Recp Source list of email recipients
3455 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3456 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3457 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3458 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3460 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3462 StrBuf *EmailAddress,
3466 const char *pch, *pche;
3467 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3469 if ((Recp == NULL) || (StrLength(Recp) == 0))
3473 pche = pch + StrLength(Recp);
3475 if (!CheckEncode(pch, -1, pche))
3476 return NewStrBufDup(Recp);
3478 Target = NewStrBufPlain(NULL, StrLength(Recp));
3480 while ((pch != NULL) && (pch < pche))
3482 while (isspace(*pch)) pch++;
3483 UserEnd = EmailStart = EmailEnd = NULL;
3485 if ((*pch == '"') || (*pch == '\'')) {
3486 UserStart = pch + 1;
3488 UserEnd = strchr(UserStart, *pch);
3489 if (UserEnd == NULL)
3490 break; ///TODO: Userfeedback??
3491 EmailStart = UserEnd + 1;
3492 while (isspace(*EmailStart))
3494 if (UserEnd == UserStart) {
3495 UserStart = UserEnd = NULL;
3498 if (*EmailStart == '<') {
3500 EmailEnd = strchr(EmailStart, '>');
3501 if (EmailEnd == NULL)
3502 EmailEnd = strchr(EmailStart, ',');
3506 EmailEnd = strchr(EmailStart, ',');
3508 if (EmailEnd == NULL)
3515 EmailEnd = strchr(UserStart, ',');
3516 if (EmailEnd == NULL) {
3517 EmailEnd = strchr(pch, '>');
3519 if (EmailEnd != NULL) {
3529 while ((EmailEnd > UserStart) && !gt &&
3530 ((*EmailEnd == ',') ||
3531 (*EmailEnd == '>') ||
3532 (isspace(*EmailEnd))))
3534 if (*EmailEnd == '>')
3539 if (EmailEnd == UserStart)
3543 EmailStart = strchr(UserStart, '<');
3544 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3546 UserEnd = EmailStart;
3548 while ((UserEnd > UserStart) &&
3549 isspace (*(UserEnd - 1)))
3552 if (UserStart >= UserEnd)
3553 UserStart = UserEnd = NULL;
3555 else { /* this is a local recipient... no domain, just a realname */
3556 EmailStart = UserStart;
3557 At = strchr(EmailStart, '@');
3563 EmailStart = UserStart;
3569 if ((UserStart != NULL) && (UserEnd != NULL))
3570 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3571 else if ((UserStart != NULL) && (UserEnd == NULL))
3572 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3574 FlushStrBuf(UserName);
3576 if ((EmailStart != NULL) && (EmailEnd != NULL))
3577 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3578 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3579 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3581 FlushStrBuf(EmailAddress);
3583 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3588 if ((pch != NULL) && (*pch == ','))
3590 if (pch != NULL) while (isspace(*pch))
3599 * @brief replaces all occurances of 'search' by 'replace'
3600 * @param buf Buffer to modify
3601 * @param search character to search
3602 * @param replace character to replace search by
3604 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3609 for (i=0; i<buf->BufUsed; i++)
3610 if (buf->buf[i] == search)
3611 buf->buf[i] = replace;
3617 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3618 * @param buf Buffer to modify
3620 void StrBufToUnixLF(StrBuf *buf)
3622 char *pche, *pchS, *pchT;
3626 pche = buf->buf + buf->BufUsed;
3627 pchS = pchT = buf->buf;
3633 if (*pchS != '\n') {
3642 buf->BufUsed = pchT - buf->buf;
3646 /*******************************************************************************
3647 * Iconv Wrapper; RFC822 de/encoding *
3648 *******************************************************************************/
3651 * @ingroup StrBuf_DeEnCoder
3652 * @brief Wrapper around iconv_open()
3653 * Our version adds aliases for non-standard Microsoft charsets
3654 * such as 'MS950', aliasing them to names like 'CP950'
3656 * @param tocode Target encoding
3657 * @param fromcode Source encoding
3658 * @param pic anonimized pointer to iconv struct
3660 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3663 iconv_t ic = (iconv_t)(-1) ;
3664 ic = iconv_open(tocode, fromcode);
3665 if (ic == (iconv_t)(-1) ) {
3666 char alias_fromcode[64];
3667 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3668 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3669 alias_fromcode[0] = 'C';
3670 alias_fromcode[1] = 'P';
3671 ic = iconv_open(tocode, alias_fromcode);
3674 *(iconv_t *)pic = ic;
3680 * @ingroup StrBuf_DeEnCoder
3681 * @brief find one chunk of a RFC822 encoded string
3682 * @param Buffer where to search
3683 * @param bptr where to start searching
3684 * @returns found position, NULL if none.
3686 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3689 /* Find the next ?Q? */
3690 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3693 end = strchr(bptr + 2, '?');
3698 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3699 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3700 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3701 (*(end + 2) == '?')) {
3702 /* skip on to the end of the cluster, the next ?= */
3703 end = strstr(end + 3, "?=");
3706 /* sort of half valid encoding, try to find an end. */
3707 end = strstr(bptr, "?=");
3714 * @ingroup StrBuf_DeEnCoder
3715 * @brief convert one buffer according to the preselected iconv pointer PIC
3716 * @param ConvertBuf buffer we need to translate
3717 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3718 * @param pic Pointer to the iconv-session Object
3720 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3726 char *ibuf; /**< Buffer of characters to be converted */
3727 char *obuf; /**< Buffer for converted characters */
3728 size_t ibuflen; /**< Length of input buffer */
3729 size_t obuflen; /**< Length of output buffer */
3732 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3735 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3736 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3737 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3739 ic = *(iconv_t*)pic;
3740 ibuf = ConvertBuf->buf;
3741 ibuflen = ConvertBuf->BufUsed;
3743 obuflen = TmpBuf->BufSize;
3745 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3748 if (errno == E2BIG) {
3750 IncreaseBuf(TmpBuf, 0, 0);
3755 else if (errno == EILSEQ){
3756 /* hm, invalid utf8 sequence... what to do now? */
3757 /* An invalid multibyte sequence has been encountered in the input */
3759 else if (errno == EINVAL) {
3760 /* An incomplete multibyte sequence has been encountered in the input. */
3763 FlushStrBuf(TmpBuf);
3766 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3767 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3769 /* little card game: wheres the red lady? */
3770 SwapBuffers(ConvertBuf, TmpBuf);
3771 FlushStrBuf(TmpBuf);
3778 * @ingroup StrBuf_DeEnCoder
3779 * @brief catches one RFC822 encoded segment, and decodes it.
3780 * @param Target buffer to fill with result
3781 * @param DecodeMe buffer with stuff to process
3782 * @param SegmentStart points to our current segment in DecodeMe
3783 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3784 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3785 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3786 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3788 inline static void DecodeSegment(StrBuf *Target,
3789 const StrBuf *DecodeMe,
3790 const char *SegmentStart,
3791 const char *SegmentEnd,
3793 StrBuf *ConvertBuf2,
3794 StrBuf *FoundCharset)
3800 iconv_t ic = (iconv_t)(-1);
3804 /* Now we handle foreign character sets properly encoded
3805 * in RFC2047 format.
3807 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3808 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3809 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3810 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3811 if (FoundCharset != NULL) {
3812 FlushStrBuf(FoundCharset);
3813 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3815 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3816 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3818 *encoding = toupper(*encoding);
3819 if (*encoding == 'B') { /**< base64 */
3820 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3821 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3822 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3824 ConvertBuf->BufUsed);
3826 else if (*encoding == 'Q') { /**< quoted-printable */
3830 while (pos < ConvertBuf->BufUsed)
3832 if (ConvertBuf->buf[pos] == '_')
3833 ConvertBuf->buf[pos] = ' ';
3837 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3838 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3840 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3843 ConvertBuf->BufUsed);
3846 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3849 ctdl_iconv_open("UTF-8", charset, &ic);
3850 if (ic != (iconv_t)(-1) ) {
3852 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3853 StrBufAppendBuf(Target, ConvertBuf2, 0);
3858 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3864 * @ingroup StrBuf_DeEnCoder
3865 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3866 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3867 * @param Target where to put the decoded string to
3868 * @param DecodeMe buffer with encoded string
3869 * @param DefaultCharset if we don't find one, which should we use?
3870 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3871 * put it here for later use where no string might be known.
3873 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3876 StrBuf *ConvertBuf2;
3877 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3878 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3880 StrBuf_RFC822_2_Utf8(Target,
3886 FreeStrBuf(&ConvertBuf);
3887 FreeStrBuf(&ConvertBuf2);
3891 * @ingroup StrBuf_DeEnCoder
3892 * @brief Handle subjects with RFC2047 encoding such as:
3893 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3894 * @param Target where to put the decoded string to
3895 * @param DecodeMe buffer with encoded string
3896 * @param DefaultCharset if we don't find one, which should we use?
3897 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3898 * put it here for later use where no string might be known.
3899 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3900 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3902 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3903 const StrBuf *DecodeMe,
3904 const StrBuf* DefaultCharset,
3905 StrBuf *FoundCharset,
3907 StrBuf *ConvertBuf2)
3909 StrBuf *DecodedInvalidBuf = NULL;
3910 const StrBuf *DecodeMee = DecodeMe;
3911 const char *start, *end, *next, *nextend, *ptr = NULL;
3913 iconv_t ic = (iconv_t)(-1) ;
3918 int illegal_non_rfc2047_encoding = 0;
3921 if (DecodeMe == NULL)
3923 /* Sometimes, badly formed messages contain strings which were simply
3924 * written out directly in some foreign character set instead of
3925 * using RFC2047 encoding. This is illegal but we will attempt to
3926 * handle it anyway by converting from a user-specified default
3927 * charset to UTF-8 if we see any nonprintable characters.
3930 for (i=0; i<DecodeMe->BufUsed; ++i) {
3931 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3932 illegal_non_rfc2047_encoding = 1;
3937 if ((illegal_non_rfc2047_encoding) &&
3938 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3939 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3942 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3943 if (ic != (iconv_t)(-1) ) {
3944 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3945 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3946 DecodeMee = DecodedInvalidBuf;
3952 /* pre evaluate the first pair */
3954 start = strstr(DecodeMee->buf, "=?");
3955 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3957 end = FindNextEnd (DecodeMee, start + 2);
3959 StrBufAppendBuf(Target, DecodeMee, 0);
3960 FreeStrBuf(&DecodedInvalidBuf);
3965 if (start != DecodeMee->buf) {
3968 nFront = start - DecodeMee->buf;
3969 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3972 * Since spammers will go to all sorts of absurd lengths to get their
3973 * messages through, there are LOTS of corrupt headers out there.
3974 * So, prevent a really badly formed RFC2047 header from throwing
3975 * this function into an infinite loop.
3977 while ((start != NULL) &&
3984 DecodeSegment(Target,
3992 next = strstr(end, "=?");
3994 if ((next != NULL) &&
3996 nextend = FindNextEnd(DecodeMee, next);
3997 if (nextend == NULL)
4000 /* did we find two partitions */
4001 if ((next != NULL) &&
4005 while ((ptr < next) &&
4012 * did we find a gab just filled with blanks?
4013 * if not, copy its stuff over.
4017 StrBufAppendBufPlain(Target,
4023 /* our next-pair is our new first pair now. */
4029 nextend = DecodeMee->buf + DecodeMee->BufUsed;
4030 if ((end != NULL) && (end < nextend)) {
4032 while ( (ptr < nextend) &&
4039 StrBufAppendBufPlain(Target, end, nextend - end, 0);
4041 FreeStrBuf(&DecodedInvalidBuf);
4044 /*******************************************************************************
4045 * Manipulating UTF-8 Strings *
4046 *******************************************************************************/
4050 * @brief evaluate the length of an utf8 special character sequence
4051 * @param Char the character to examine
4052 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4054 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4057 unsigned char test = (1<<7);
4059 if ((*CharS & 0xC0) != 0xC0)
4063 ((test & ((unsigned char)*CharS)) != 0))
4068 if ((n > 6) || ((CharE - CharS) < n))
4075 * @brief detect whether this char starts an utf-8 encoded char
4076 * @param Char character to inspect
4077 * @returns yes or no
4079 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4081 /** 11??.???? indicates an UTF8 Sequence. */
4082 return ((Char & 0xC0) == 0xC0);
4087 * @brief measure the number of glyphs in an UTF8 string...
4088 * @param Buf string to measure
4089 * @returns the number of glyphs in Buf
4091 long StrBuf_Utf8StrLen(StrBuf *Buf)
4097 if ((Buf == NULL) || (Buf->BufUsed == 0))
4100 eptr = Buf->buf + Buf->BufUsed;
4101 while ((aptr < eptr) && (*aptr != '\0')) {
4102 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4103 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4104 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4117 * @brief cuts a string after maxlen glyphs
4118 * @param Buf string to cut to maxlen glyphs
4119 * @param maxlen how long may the string become?
4120 * @returns current length of the string
4122 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4128 eptr = Buf->buf + Buf->BufUsed;
4129 while ((aptr < eptr) && (*aptr != '\0')) {
4130 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4131 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4132 while ((*aptr++ != '\0') && (m-- > 0));
4141 Buf->BufUsed = aptr - Buf->buf;
4142 return Buf->BufUsed;
4145 return Buf->BufUsed;
4153 /*******************************************************************************
4155 *******************************************************************************/
4158 * @ingroup StrBuf_DeEnCoder
4159 * @brief uses the same calling syntax as compress2(), but it
4160 * creates a stream compatible with HTTP "Content-encoding: gzip"
4161 * @param dest compressed buffer
4162 * @param destLen length of the compresed data
4163 * @param source source to encode
4164 * @param sourceLen length of source to encode
4165 * @param level compression level
4168 int ZEXPORT compress_gzip(Bytef * dest,
4170 const Bytef * source,
4174 /* write gzip header */
4175 snprintf((char *) dest, *destLen,
4176 "%c%c%c%c%c%c%c%c%c%c",
4177 gz_magic[0], gz_magic[1], Z_DEFLATED,
4178 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4181 /* normal deflate */
4184 stream.next_in = (Bytef *) source;
4185 stream.avail_in = (uInt) sourceLen;
4186 stream.next_out = dest + 10L; // after header
4187 stream.avail_out = (uInt) * destLen;
4188 if ((uLong) stream.avail_out != *destLen)
4191 stream.zalloc = (alloc_func) 0;
4192 stream.zfree = (free_func) 0;
4193 stream.opaque = (voidpf) 0;
4195 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4196 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4200 err = deflate(&stream, Z_FINISH);
4201 if (err != Z_STREAM_END) {
4202 deflateEnd(&stream);
4203 return err == Z_OK ? Z_BUF_ERROR : err;
4205 *destLen = stream.total_out + 10L;
4207 /* write CRC and Length */
4208 uLong crc = crc32(0L, source, sourceLen);
4210 for (n = 0; n < 4; ++n, ++*destLen) {
4211 dest[*destLen] = (int) (crc & 0xff);
4214 uLong len = stream.total_in;
4215 for (n = 0; n < 4; ++n, ++*destLen) {
4216 dest[*destLen] = (int) (len & 0xff);
4219 err = deflateEnd(&stream);
4226 * @ingroup StrBuf_DeEnCoder
4227 * @brief compress the buffer with gzip
4228 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4229 * @param Buf buffer whose content is to be gzipped
4231 int CompressBuffer(StrBuf *Buf)
4234 char *compressed_data = NULL;
4235 size_t compressed_len, bufsize;
4238 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
4239 compressed_data = malloc(compressed_len);
4241 if (compressed_data == NULL)
4243 /* Flush some space after the used payload so valgrind shuts up... */
4244 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4245 Buf->buf[Buf->BufUsed + i++] = '\0';
4246 if (compress_gzip((Bytef *) compressed_data,
4249 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4252 Buf->buf = compressed_data;
4253 Buf->BufUsed = compressed_len;
4254 Buf->BufSize = bufsize;
4255 /* Flush some space after the used payload so valgrind shuts up... */
4257 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4258 Buf->buf[Buf->BufUsed + i++] = '\0';
4261 free(compressed_data);
4263 #endif /* HAVE_ZLIB */
4267 /*******************************************************************************
4268 * File I/O; Callbacks to libevent *
4269 *******************************************************************************/
4271 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4276 if ((FB == NULL) || (FB->Buf == NULL))
4280 * check whether the read pointer is somewhere in a range
4281 * where a cut left is inexpensive
4284 if (FB->ReadWritePointer != NULL)
4288 already_read = FB->ReadWritePointer - FB->Buf->buf;
4289 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4291 if (already_read != 0) {
4294 unread = FB->Buf->BufUsed - already_read;
4296 /* else nothing to compact... */
4298 FB->ReadWritePointer = FB->Buf->buf;
4299 bufremain = FB->Buf->BufSize;
4301 else if ((unread < 64) ||
4302 (bufremain < already_read))
4305 * if its just a tiny bit remaining, or we run out of space...
4308 FB->Buf->BufUsed = unread;
4309 if (unread < already_read)
4310 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4312 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4313 FB->ReadWritePointer = FB->Buf->buf;
4314 bufremain = FB->Buf->BufSize - unread - 1;
4316 else if (bufremain < (FB->Buf->BufSize / 10))
4318 /* get a bigger buffer */
4320 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4322 FB->ReadWritePointer = FB->Buf->buf + unread;
4324 bufremain = FB->Buf->BufSize - unread - 1;
4325 /*TODO: special increase function that won't copy the already read! */
4328 else if (bufremain < 10) {
4329 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4331 FB->ReadWritePointer = FB->Buf->buf;
4333 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4338 FB->ReadWritePointer = FB->Buf->buf;
4339 bufremain = FB->Buf->BufSize - 1;
4342 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4345 FB->Buf->BufUsed += n;
4346 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4351 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4356 if ((FB == NULL) || (FB->Buf == NULL))
4359 if (FB->ReadWritePointer != NULL)
4361 WriteRemain = FB->Buf->BufUsed -
4362 (FB->ReadWritePointer -
4366 FB->ReadWritePointer = FB->Buf->buf;
4367 WriteRemain = FB->Buf->BufUsed;
4370 n = write(fd, FB->ReadWritePointer, WriteRemain);
4372 FB->ReadWritePointer += n;
4374 if (FB->ReadWritePointer ==
4375 FB->Buf->buf + FB->Buf->BufUsed)
4377 FlushStrBuf(FB->Buf);
4378 FB->ReadWritePointer = NULL;
4381 // check whether we've got something to write
4382 // get the maximum chunk plus the pointer we can send
4383 // write whats there
4384 // if not all was sent, remember the send pointer for the next time
4385 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4391 * @ingroup StrBuf_IO
4392 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4393 * @param LineBuf your line will be copied here.
4394 * @param FB BLOB with lines of text...
4395 * @param Ptr moved arround to keep the next-line across several iterations
4396 * has to be &NULL on start; will be &NotNULL on end of buffer
4397 * @returns size of copied buffer
4399 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4401 const char *aptr, *ptr, *eptr;
4404 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4408 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4409 FB->ReadWritePointer = StrBufNOTNULL;
4413 FlushStrBuf(LineBuf);
4414 if (FB->ReadWritePointer == NULL)
4415 ptr = aptr = FB->Buf->buf;
4417 ptr = aptr = FB->ReadWritePointer;
4419 optr = LineBuf->buf;
4420 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4421 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4423 while ((ptr <= eptr) &&
4430 LineBuf->BufUsed = optr - LineBuf->buf;
4431 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4432 optr = LineBuf->buf + LineBuf->BufUsed;
4433 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4438 if (optr > LineBuf->buf)
4440 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4441 LineBuf->BufUsed = optr - LineBuf->buf;
4443 if ((FB->ReadWritePointer != NULL) &&
4444 (FB->ReadWritePointer != FB->Buf->buf))
4446 /* Ok, the client application read all the data
4447 it was interested in so far. Since there is more to read,
4448 we now shrink the buffer, and move the rest over.
4450 StrBufCutLeft(FB->Buf,
4451 FB->ReadWritePointer - FB->Buf->buf);
4452 FB->ReadWritePointer = FB->Buf->buf;
4454 return eMustReadMore;
4457 LineBuf->BufUsed = optr - LineBuf->buf;
4459 if ((ptr <= eptr) && (*ptr == '\r'))
4461 if ((ptr <= eptr) && (*ptr == '\n'))
4465 FB->ReadWritePointer = ptr;
4468 FlushStrBuf(FB->Buf);
4469 FB->ReadWritePointer = NULL;
4472 return eReadSuccess;
4476 * @ingroup StrBuf_CHUNKED_IO
4477 * @brief check whether the chunk-buffer has more data waiting or not.
4478 * @param FB Chunk-Buffer to inspect
4480 eReadState StrBufCheckBuffer(IOBuffer *FB)
4484 if (FB->Buf->BufUsed == 0)
4485 return eReadSuccess;
4486 if (FB->ReadWritePointer == NULL)
4487 return eBufferNotEmpty;
4488 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4489 return eBufferNotEmpty;
4490 return eReadSuccess;
4493 long IOBufferStrLength(IOBuffer *FB)
4495 if ((FB == NULL) || (FB->Buf == NULL))
4497 if (FB->ReadWritePointer == NULL)
4498 return StrLength(FB->Buf);
4500 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4503 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
4505 memset(FDB, 0, sizeof(FDIOBuffer));
4507 FDB->SplicePipe[0] = -1;
4508 FDB->SplicePipe[1] = -1;
4511 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
4513 FDIOBufferFlush(FDB);
4515 FDB->TotalSendSize = TotalSendSize;
4516 if (TotalSendSize > 0)
4517 FDB->ChunkSize = TotalSendSize;
4520 TotalSendSize = SIZ * 10;
4521 FDB->ChunkSize = TotalSendSize;
4527 pipe(FDB->SplicePipe);
4530 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
4535 void FDIOBufferDelete(FDIOBuffer *FDB)
4540 if (FDB->SplicePipe[0] > 0)
4541 close(FDB->SplicePipe[0]);
4542 if (FDB->SplicePipe[1] > 0)
4543 close(FDB->SplicePipe[1]);
4547 FreeStrBuf(&FDB->ChunkBuffer);
4549 if (FDB->OtherFD > 0)
4550 close(FDB->OtherFD);
4551 FDIOBufferFlush(FDB);
4554 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4556 ssize_t sent, pipesize;
4558 if (FDB->TotalSendSize > 0)
4563 if (FDB->PipeSize == 0)
4565 pipesize = splice(FDB->OtherFD,
4566 &FDB->TotalSentAlready,
4569 FDB->ChunkSendRemain,
4574 *Err = strerror(errno);
4577 FDB->PipeSize = pipesize;
4579 sent = splice(FDB->SplicePipe[0],
4584 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4587 *Err = strerror(errno);
4590 FDB->PipeSize -= sent;
4591 FDB->ChunkSendRemain -= sent;
4600 pRead = FDB->ChunkBuffer->buf;
4601 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4603 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4605 FDB->ChunkBuffer->BufUsed += nRead;
4606 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4608 else if (nRead == 0) {}
4612 nRead = write(FDB->IOB->fd,
4613 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4614 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4617 FDB->TotalSentAlready += nRead;
4618 FDB->ChunkSendRemain -= nRead;
4619 return FDB->ChunkSendRemain;
4631 if (FDB->PipeSize == 0)
4633 pipesize = splice(FDB->OtherFD,
4634 &FDB->TotalSentAlready,
4642 *Err = strerror(errno);
4645 FDB->PipeSize = pipesize;
4649 sent = splice(FDB->SplicePipe[0],
4654 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4657 *Err = strerror(errno);
4660 FDB->PipeSize -= sent;
4661 FDB->ChunkSendRemain -= sent;
4670 pRead = FDB->ChunkBuffer->buf;
4671 while ((FDB->ChunkSendRemain == 0) &&
4672 (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
4675 FDB->TotalSentAlready = 0;
4676 nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
4678 FDB->ChunkBuffer->BufUsed += nRead;
4679 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4680 FDB->ChunkSendRemain += nRead;
4682 else if (nRead == 0)
4688 *Err = strerror(errno);
4693 nRead = write(FDB->IOB->fd,
4694 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4695 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4698 FDB->TotalSentAlready += nRead;
4699 FDB->ChunkSendRemain -= nRead;
4700 if (FDB->ChunkSendRemain == 0)
4702 FDB->ChunkBuffer->BufUsed = 0;
4703 FDB->TotalSentAlready = 0;
4705 return FDB->ChunkSendRemain;
4714 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4716 ssize_t sent, pipesize;
4721 if (FDB->PipeSize == 0)
4723 pipesize = splice(FDB->IOB->fd,
4727 FDB->ChunkSendRemain,
4728 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4732 *Err = strerror(errno);
4735 FDB->PipeSize = pipesize;
4738 sent = splice(FDB->SplicePipe[0],
4741 &FDB->TotalSentAlready,
4743 SPLICE_F_MORE | SPLICE_F_MOVE);
4747 *Err = strerror(errno);
4750 FDB->PipeSize -= sent;
4751 FDB->ChunkSendRemain -= sent;
4757 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4762 FDB->ChunkBuffer->BufUsed = sent;
4764 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4765 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4767 *Err = strerror(errno);
4773 FDB->ChunkBuffer->BufUsed = 0;
4774 FDB->TotalSentAlready += sent;
4775 FDB->ChunkSendRemain -= sent;
4776 return FDB->ChunkSendRemain;
4778 else if (sent < 0) {
4779 *Err = strerror(errno);
4786 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4788 ssize_t sent, pipesize;
4793 if (FDB->PipeSize == 0)
4795 pipesize = splice(FDB->IOB->fd,
4796 &FDB->TotalReadAlready,
4799 FDB->ChunkSendRemain,
4800 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4804 *Err = strerror(errno);
4807 FDB->PipeSize = pipesize;
4810 sent = splice(FDB->SplicePipe[0],
4813 &FDB->TotalSentAlready,
4815 SPLICE_F_MORE | SPLICE_F_MOVE);
4819 *Err = strerror(errno);
4822 FDB->PipeSize -= sent;
4823 FDB->ChunkSendRemain -= sent;
4829 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4834 FDB->ChunkBuffer->BufUsed = sent;
4836 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4837 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4839 *Err = strerror(errno);
4845 FDB->ChunkBuffer->BufUsed = 0;
4846 FDB->TotalSentAlready += sent;
4847 FDB->ChunkSendRemain -= sent;
4848 return FDB->ChunkSendRemain;
4850 else if (sent < 0) {
4851 *Err = strerror(errno);
4858 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4864 int nSuccessLess = 0;
4868 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4869 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4871 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4872 (FDB->ChunkSendRemain > 0))
4875 tv.tv_sec = 1; /* selectresolution; */
4879 FD_SET(FDB->OtherFD, &rfds);
4880 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4881 *Error = strerror(errno);
4885 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4890 should_write = FDB->IOB->Buf->BufUsed -
4891 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4892 if (should_write > FDB->ChunkSendRemain)
4893 should_write = FDB->ChunkSendRemain;
4895 rlen = write(FDB->OtherFD,
4896 FDB->IOB->ReadWritePointer,
4899 *Error = strerror(errno);
4903 FDB->TotalSentAlready += rlen;
4904 FDB->IOB->ReadWritePointer += rlen;
4905 FDB->ChunkSendRemain -= rlen;
4907 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4909 FlushStrBuf(FDB->IOB->Buf);
4910 FDB->IOB->ReadWritePointer = NULL;
4913 if (FDB->ChunkSendRemain == 0)
4914 return eReadSuccess;
4916 return eMustReadMore;
4919 /*******************************************************************************
4920 * File I/O; Prefer buffered read since its faster! *
4921 *******************************************************************************/
4924 * @ingroup StrBuf_IO
4925 * @brief Read a line from socket
4926 * flushes and closes the FD on error
4927 * @param buf the buffer to get the input to
4928 * @param fd pointer to the filedescriptor to read
4929 * @param append Append to an existing string or replace?
4930 * @param Error strerror() on error
4931 * @returns numbers of chars read
4933 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4935 int len, rlen, slen;
4937 if ((buf == NULL) || (buf->buf == NULL)) {
4938 *Error = strerror(EINVAL);
4945 slen = len = buf->BufUsed;
4947 rlen = read(*fd, &buf->buf[len], 1);
4949 *Error = strerror(errno);
4956 if (buf->buf[len] == '\n')
4958 if (buf->buf[len] != '\r')
4960 if (len + 2 >= buf->BufSize) {
4962 buf->buf[len+1] = '\0';
4963 IncreaseBuf(buf, 1, -1);
4967 buf->buf[len] = '\0';
4972 * @ingroup StrBuf_BufferedIO
4973 * @brief Read a line from socket
4974 * flushes and closes the FD on error
4975 * @param Line the line to read from the fd / I/O Buffer
4976 * @param buf the buffer to get the input to
4977 * @param fd pointer to the filedescriptor to read
4978 * @param timeout number of successless selects until we bail out
4979 * @param selectresolution how long to wait on each select
4980 * @param Error strerror() on error
4981 * @returns numbers of chars read
4983 int StrBufTCP_read_buffered_line(StrBuf *Line,
4987 int selectresolution,
4991 int nSuccessLess = 0;
4998 if (buf->BufUsed > 0) {
4999 pch = strchr(buf->buf, '\n');
5002 len = pch - buf->buf;
5003 if (len > 0 && (*(pch - 1) == '\r') )
5005 StrBufSub(Line, buf, 0, len - rlen);
5006 StrBufCutLeft(buf, len + 1);
5011 if (buf->BufSize - buf->BufUsed < 10)
5012 IncreaseBuf(buf, 1, -1);
5014 fdflags = fcntl(*fd, F_GETFL);
5015 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5017 while ((nSuccessLess < timeout) && (pch == NULL)) {
5019 tv.tv_sec = selectresolution;
5024 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
5025 *Error = strerror(errno);
5031 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
5036 &buf->buf[buf->BufUsed],
5037 buf->BufSize - buf->BufUsed - 1);
5039 *Error = strerror(errno);
5044 else if (rlen > 0) {
5046 buf->BufUsed += rlen;
5047 buf->buf[buf->BufUsed] = '\0';
5048 pch = strchr(buf->buf, '\n');
5049 if ((pch == NULL) &&
5050 (buf->BufUsed + 10 > buf->BufSize) &&
5051 (IncreaseBuf(buf, 1, -1) == -1))
5059 len = pch - buf->buf;
5060 if (len > 0 && (*(pch - 1) == '\r') )
5062 StrBufSub(Line, buf, 0, len - rlen);
5063 StrBufCutLeft(buf, len + 1);
5070 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
5071 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
5072 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
5074 * @ingroup StrBuf_BufferedIO
5075 * @brief Read a line from socket
5076 * flushes and closes the FD on error
5077 * @param Line where to append our Line read from the fd / I/O Buffer;
5078 * @param IOBuf the buffer to get the input to; lifetime pair to FD
5079 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
5080 * @param fd pointer to the filedescriptor to read
5081 * @param timeout number of successless selects until we bail out
5082 * @param selectresolution how long to wait on each select
5083 * @param Error strerror() on error
5084 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
5086 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
5091 int selectresolution,
5094 const char *pche = NULL;
5095 const char *pos = NULL;
5097 int len, rlen, retlen;
5098 int nSuccessLess = 0;
5100 const char *pch = NULL;
5106 if ((Line == NULL) ||
5113 *Error = ErrRBLF_PreConditionFailed;
5118 if ((IOBuf->BufUsed > 0) &&
5120 (pos < IOBuf->buf + IOBuf->BufUsed))
5124 pche = IOBuf->buf + IOBuf->BufUsed;
5128 while ((pch < pche) && (*pch != '\n'))
5130 if (Line->BufUsed + 10 > Line->BufSize)
5133 apos = pcht - Line->buf;
5135 IncreaseBuf(Line, 1, -1);
5136 pcht = Line->buf + apos;
5144 if (len > 0 && (*(pch - 1) == '\r') )
5153 if ((pch >= pche) || (*pch == '\0'))
5161 if ((pch != NULL) &&
5164 if (pch + 1 >= pche) {
5177 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
5179 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
5180 IncreaseBuf(IOBuf, 1, -1);
5182 fdflags = fcntl(*fd, F_GETFL);
5183 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5186 while ((nSuccessLess < timeout) &&
5196 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
5197 *Error = strerror(errno);
5201 *Error = ErrRBLF_SelectFailed;
5204 if (! FD_ISSET(*fd, &rfds) != 0) {
5210 &IOBuf->buf[IOBuf->BufUsed],
5211 IOBuf->BufSize - IOBuf->BufUsed - 1);
5213 *Error = strerror(errno);
5218 else if (rlen > 0) {
5220 pLF = IOBuf->buf + IOBuf->BufUsed;
5221 IOBuf->BufUsed += rlen;
5222 IOBuf->buf[IOBuf->BufUsed] = '\0';
5224 pche = IOBuf->buf + IOBuf->BufUsed;
5226 while ((pLF < pche) && (*pLF != '\n'))
5228 if ((pLF >= pche) || (*pLF == '\0'))
5231 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
5235 if (pLF != NULL) apos = pLF - IOBuf->buf;
5236 IncreaseBuf(IOBuf, 1, -1);
5237 if (pLF != NULL) pLF = IOBuf->buf + apos;
5251 if (len > 0 && (*(pLF - 1) == '\r') )
5253 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
5254 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
5260 return retlen + len;
5262 *Error = ErrRBLF_NotEnoughSentFromServer;
5267 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
5269 * @ingroup StrBuf_IO
5270 * @brief Input binary data from socket
5271 * flushes and closes the FD on error
5272 * @param Buf the buffer to get the input to
5273 * @param fd pointer to the filedescriptor to read
5274 * @param append Append to an existing string or replace?
5275 * @param nBytes the maximal number of bytes to read
5276 * @param Error strerror() on error
5277 * @returns numbers of chars read
5279 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
5290 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
5292 *Error = ErrRBLF_BLOBPreConditionFailed;
5297 if (Buf->BufUsed + nBytes >= Buf->BufSize)
5298 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
5300 ptr = Buf->buf + Buf->BufUsed;
5302 fdflags = fcntl(*fd, F_GETFL);
5303 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5305 while ((nRead < nBytes) &&
5315 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5316 *Error = strerror(errno);
5320 *Error = ErrRBLF_SelectFailed;
5323 if (! FD_ISSET(*fd, &rfds) != 0) {
5329 if ((rlen = read(*fd,
5331 nBytes - nRead)) == -1) {
5334 *Error = strerror(errno);
5339 Buf->BufUsed += rlen;
5341 Buf->buf[Buf->BufUsed] = '\0';
5345 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
5346 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
5348 * @ingroup StrBuf_BufferedIO
5349 * @brief Input binary data from socket
5350 * flushes and closes the FD on error
5351 * @param Blob put binary thing here
5352 * @param IOBuf the buffer to get the input to
5353 * @param Pos offset inside of IOBuf
5354 * @param fd pointer to the filedescriptor to read
5355 * @param append Append to an existing string or replace?
5356 * @param nBytes the maximal number of bytes to read
5357 * @param check whether we should search for '000\n' terminators in case of timeouts
5358 * @param Error strerror() on error
5359 * @returns numbers of chars read
5361 int StrBufReadBLOBBuffered(StrBuf *Blob,
5374 int nAlreadyRead = 0;
5379 int nSuccessLess = 0;
5382 if ((Blob == NULL) ||
5389 *Error = ErrRBB_BLOBFPreConditionFailed;
5395 if (Blob->BufUsed + nBytes >= Blob->BufSize)
5396 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
5401 rlen = pos - IOBuf->buf;
5402 rlen = IOBuf->BufUsed - rlen;
5405 if ((IOBuf->BufUsed > 0) &&
5407 (pos < IOBuf->buf + IOBuf->BufUsed))
5409 if (rlen < nBytes) {
5410 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5411 Blob->BufUsed += rlen;
5412 Blob->buf[Blob->BufUsed] = '\0';
5413 nAlreadyRead = nRead = rlen;
5416 if (rlen >= nBytes) {
5417 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5418 Blob->BufUsed += nBytes;
5419 Blob->buf[Blob->BufUsed] = '\0';
5420 if (rlen == nBytes) {
5432 if (IOBuf->BufSize < nBytes - nRead)
5433 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5436 fdflags = fcntl(*fd, F_GETFL);
5437 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5445 while ((nSuccessLess < MaxTries) &&
5455 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5456 *Error = strerror(errno);
5460 *Error = ErrRBLF_SelectFailed;
5463 if (! FD_ISSET(*fd, &rfds) != 0) {
5470 IOBuf->BufSize - (ptr - IOBuf->buf));
5474 *Error = strerror(errno);
5477 else if (rlen == 0){
5478 if ((check == NNN_TERM) &&
5480 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
5482 StrBufPlain(Blob, HKEY("\n000\n"));
5483 StrBufCutRight(Blob, 5);
5484 return Blob->BufUsed;
5486 else if (!IsNonBlock)
5488 else if (nSuccessLess > MaxTries) {
5490 *Error = ErrRBB_too_many_selects;
5494 else if (rlen > 0) {
5498 IOBuf->BufUsed += rlen;
5501 if (nSuccessLess >= MaxTries) {
5503 *Error = ErrRBB_too_many_selects;
5507 if (nRead > nBytes) {
5508 *Pos = IOBuf->buf + nBytes;
5510 Blob->buf[Blob->BufUsed] = '\0';
5511 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5515 return nRead + nAlreadyRead;
5519 * @ingroup StrBuf_IO
5520 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5521 * @param LineBuf your line will be copied here.
5522 * @param Buf BLOB with lines of text...
5523 * @param Ptr moved arround to keep the next-line across several iterations
5524 * has to be &NULL on start; will be &NotNULL on end of buffer
5525 * @returns size of remaining buffer
5527 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5529 const char *aptr, *ptr, *eptr;
5532 if ((Buf == NULL) ||
5533 (*Ptr == StrBufNOTNULL) ||
5535 (LineBuf->buf == NULL))
5537 *Ptr = StrBufNOTNULL;
5541 FlushStrBuf(LineBuf);
5543 ptr = aptr = Buf->buf;
5547 optr = LineBuf->buf;
5548 eptr = Buf->buf + Buf->BufUsed;
5549 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5551 while ((ptr <= eptr) &&
5558 LineBuf->BufUsed = optr - LineBuf->buf;
5559 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5560 optr = LineBuf->buf + LineBuf->BufUsed;
5561 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5565 if ((ptr >= eptr) && (optr > LineBuf->buf))
5567 LineBuf->BufUsed = optr - LineBuf->buf;
5569 if ((ptr <= eptr) && (*ptr == '\r'))
5571 if ((ptr <= eptr) && (*ptr == '\n'))
5578 *Ptr = StrBufNOTNULL;
5581 return Buf->BufUsed - (ptr - Buf->buf);
5586 * @ingroup StrBuf_IO
5587 * @brief removes double slashes from pathnames
5588 * @param Dir directory string to filter
5589 * @param RemoveTrailingSlash allows / disallows trailing slashes
5591 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5597 while (!IsEmptyStr(a)) {
5609 if ((RemoveTrailingSlash) &&
5615 Dir->BufUsed = b - Dir->buf;