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)) {
2009 else if (*pch < 0x20) {
2010 /* we probably shouldn't be doing this */
2011 if (OverrideLowChars)
2021 *pt = HexList[*(unsigned char*)pch][0];
2023 *pt = HexList[*(unsigned char*)pch][1];
2031 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
2034 while (IsUtf8Sequence > 0){
2045 *pt = HexList[*(unsigned char*)pch][0];
2047 *pt = HexList[*(unsigned char*)pch][1];
2056 OutBuf->BufUsed = pt - OutBuf->buf;
2061 * @ingroup StrBuf_DeEnCoder
2062 * @brief append a string in hex encoding to the buffer
2063 * @param OutBuf the output buffer
2064 * @param In Buffer to encode
2065 * @param PlainIn way in from plain old c strings
2066 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2068 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2070 const unsigned char *pch, *pche;
2074 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2076 if (PlainIn != NULL) {
2078 len = strlen((const char*)PlainIn);
2085 pch = (const unsigned char*)In->buf;
2086 pche = pch + In->BufUsed;
2093 pt = OutBuf->buf + OutBuf->BufUsed;
2094 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2096 while (pch < pche) {
2098 IncreaseBuf(OutBuf, 1, -1);
2099 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2100 pt = OutBuf->buf + OutBuf->BufUsed;
2103 *pt = HexList[*pch][0];
2105 *pt = HexList[*pch][1];
2106 pt ++; pch ++; OutBuf->BufUsed += 2;
2111 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2118 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2120 if (PlainIn != NULL) {
2122 len = strlen(PlainIn);
2135 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2137 if (ExpectLen > OutBuf->BufSize)
2138 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2141 pt = OutBuf->buf + OutBuf->BufUsed;
2143 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2146 OutBuf->BufUsed += len;
2151 * @ingroup StrBuf_DeEnCoder
2152 * @brief append a string in hex encoding to the buffer
2153 * @param OutBuf the output buffer
2154 * @param In Buffer to encode
2155 * @param PlainIn way in from plain old c strings
2157 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2159 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2163 * @ingroup StrBuf_DeEnCoder
2164 * @brief Append a string, escaping characters which have meaning in HTML.
2166 * @param Target target buffer
2167 * @param Source source buffer; set to NULL if you just have a C-String
2168 * @param PlainIn Plain-C string to append; set to NULL if unused
2169 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2170 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2171 * if set to 2, linebreaks are replaced by <br/>
2173 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2175 const char *aptr, *eiptr;
2179 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2182 if (PlainIn != NULL) {
2184 len = strlen(PlainIn);
2189 eiptr = aptr + Source->BufUsed;
2190 len = Source->BufUsed;
2196 bptr = Target->buf + Target->BufUsed;
2197 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2199 while (aptr < eiptr){
2201 IncreaseBuf(Target, 1, -1);
2202 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2203 bptr = Target->buf + Target->BufUsed;
2206 memcpy(bptr, "<", 4);
2208 Target->BufUsed += 4;
2210 else if (*aptr == '>') {
2211 memcpy(bptr, ">", 4);
2213 Target->BufUsed += 4;
2215 else if (*aptr == '&') {
2216 memcpy(bptr, "&", 5);
2218 Target->BufUsed += 5;
2220 else if (*aptr == '"') {
2221 memcpy(bptr, """, 6);
2223 Target->BufUsed += 6;
2225 else if (*aptr == '\'') {
2226 memcpy(bptr, "'", 5);
2228 Target->BufUsed += 5;
2230 else if (*aptr == LB) {
2235 else if (*aptr == RB) {
2240 else if (*aptr == QU) {
2245 else if ((*aptr == 32) && (nbsp == 1)) {
2246 memcpy(bptr, " ", 6);
2248 Target->BufUsed += 6;
2250 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2251 *bptr='\0'; /* nothing */
2253 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2254 memcpy(bptr, "<br/>", 11);
2256 Target->BufUsed += 11;
2260 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2261 *bptr='\0'; /* nothing */
2271 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2273 return Target->BufUsed;
2277 * @ingroup StrBuf_DeEnCoder
2278 * @brief Append a string, escaping characters which have meaning in HTML.
2279 * Converts linebreaks into blanks; escapes single quotes
2280 * @param Target target buffer
2281 * @param Source source buffer; set to NULL if you just have a C-String
2282 * @param PlainIn Plain-C string to append; set to NULL if unused
2284 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2286 const char *aptr, *eiptr;
2290 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2293 if (PlainIn != NULL) {
2295 len = strlen(PlainIn);
2300 eiptr = aptr + Source->BufUsed;
2301 len = Source->BufUsed;
2307 eptr = Target->buf + Target->BufSize - 8;
2308 tptr = Target->buf + Target->BufUsed;
2310 while (aptr < eiptr){
2312 IncreaseBuf(Target, 1, -1);
2313 eptr = Target->buf + Target->BufSize - 8;
2314 tptr = Target->buf + Target->BufUsed;
2317 if (*aptr == '\n') {
2321 else if (*aptr == '\r') {
2325 else if (*aptr == '\'') {
2331 Target->BufUsed += 5;
2344 * @ingroup StrBuf_DeEnCoder
2345 * @brief Append a string, escaping characters which have meaning in ICAL.
2347 * @param Target target buffer
2348 * @param Source source buffer; set to NULL if you just have a C-String
2349 * @param PlainIn Plain-C string to append; set to NULL if unused
2351 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2353 const char *aptr, *eiptr;
2357 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2360 if (PlainIn != NULL) {
2362 len = strlen(PlainIn);
2367 eiptr = aptr + Source->BufUsed;
2368 len = Source->BufUsed;
2374 eptr = Target->buf + Target->BufSize - 8;
2375 tptr = Target->buf + Target->BufUsed;
2377 while (aptr < eiptr){
2378 if(tptr + 3 >= eptr) {
2379 IncreaseBuf(Target, 1, -1);
2380 eptr = Target->buf + Target->BufSize - 8;
2381 tptr = Target->buf + Target->BufUsed;
2384 if (*aptr == '\n') {
2391 else if (*aptr == '\r') {
2398 else if (*aptr == ',') {
2414 * @ingroup StrBuf_DeEnCoder
2415 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2417 * @param Target target buffer
2418 * @param Source source buffer; set to NULL if you just have a C-String
2419 * @param PlainIn Plain-C string to append; set to NULL if unused
2420 * @returns size of result or -1
2422 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2424 const char *aptr, *eiptr;
2429 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2432 if (PlainIn != NULL) {
2434 len = strlen(PlainIn);
2439 eiptr = aptr + Source->BufUsed;
2440 len = Source->BufUsed;
2446 bptr = Target->buf + Target->BufUsed;
2447 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2449 while (aptr < eiptr){
2451 IncreaseBuf(Target, 1, -1);
2452 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2453 bptr = Target->buf + Target->BufUsed;
2457 memcpy(bptr, HKEY("\\n"));
2459 Target->BufUsed += 2;
2462 memcpy(bptr, HKEY("\\r"));
2464 Target->BufUsed += 2;
2471 Target->BufUsed += 2;
2474 if ((*(aptr + 1) == 'u') &&
2475 isxdigit(*(aptr + 2)) &&
2476 isxdigit(*(aptr + 3)) &&
2477 isxdigit(*(aptr + 4)) &&
2478 isxdigit(*(aptr + 5)))
2479 { /* oh, a unicode escaper. let it pass through. */
2480 memcpy(bptr, aptr, 6);
2483 Target->BufUsed += 6;
2491 Target->BufUsed += 2;
2499 Target->BufUsed += 2;
2506 Target->BufUsed += 2;
2513 Target->BufUsed += 2;
2516 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2517 while (IsUtf8Sequence > 0){
2520 if (--IsUtf8Sequence)
2528 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2530 return Target->BufUsed;
2534 * @ingroup StrBuf_DeEnCoder
2535 * @brief Append a string, escaping characters which have meaning in HTML + json.
2537 * @param Target target buffer
2538 * @param Source source buffer; set to NULL if you just have a C-String
2539 * @param PlainIn Plain-C string to append; set to NULL if unused
2540 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2541 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2542 * if set to 2, linebreaks are replaced by <br/>
2544 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2546 const char *aptr, *eiptr;
2549 int IsUtf8Sequence = 0;
2551 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2554 if (PlainIn != NULL) {
2556 len = strlen(PlainIn);
2561 eiptr = aptr + Source->BufUsed;
2562 len = Source->BufUsed;
2568 bptr = Target->buf + Target->BufUsed;
2569 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2571 while (aptr < eiptr){
2573 IncreaseBuf(Target, 1, -1);
2574 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2575 bptr = Target->buf + Target->BufUsed;
2579 memcpy(bptr, HKEY("<"));
2581 Target->BufUsed += 4;
2584 memcpy(bptr, HKEY(">"));
2586 Target->BufUsed += 4;
2589 memcpy(bptr, HKEY("&"));
2591 Target->BufUsed += 5;
2604 switch (nolinebreaks) {
2606 *bptr='\0'; /* nothing */
2609 memcpy(bptr, HKEY("<br/>"));
2611 Target->BufUsed += 11;
2614 memcpy(bptr, HKEY("\\n"));
2616 Target->BufUsed += 2;
2620 switch (nolinebreaks) {
2623 *bptr='\0'; /* nothing */
2626 memcpy(bptr, HKEY("\\r"));
2628 Target->BufUsed += 2;
2638 Target->BufUsed += 2;
2641 if ((*(aptr + 1) == 'u') &&
2642 isxdigit(*(aptr + 2)) &&
2643 isxdigit(*(aptr + 3)) &&
2644 isxdigit(*(aptr + 4)) &&
2645 isxdigit(*(aptr + 5)))
2646 { /* oh, a unicode escaper. let it pass through. */
2647 memcpy(bptr, aptr, 6);
2650 Target->BufUsed += 6;
2658 Target->BufUsed += 2;
2666 Target->BufUsed += 2;
2673 Target->BufUsed += 2;
2680 Target->BufUsed += 2;
2684 memcpy(bptr, HKEY(" "));
2686 Target->BufUsed += 6;
2690 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2691 while (IsUtf8Sequence > 0){
2694 if (--IsUtf8Sequence)
2702 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2704 return Target->BufUsed;
2709 * @ingroup StrBuf_DeEnCoder
2710 * @brief replace all non-Ascii characters by another
2711 * @param Buf buffer to inspect
2712 * @param repl charater to stamp over non ascii chars
2714 void StrBufAsciify(StrBuf *Buf, const char repl)
2718 for (offset = 0; offset < Buf->BufUsed; offset ++)
2719 if (!isascii(Buf->buf[offset]))
2720 Buf->buf[offset] = repl;
2725 * @ingroup StrBuf_DeEnCoder
2726 * @brief unhide special chars hidden to the HTML escaper
2727 * @param target buffer to put the unescaped string in
2728 * @param source buffer to unescape
2730 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2735 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2741 FlushStrBuf(target);
2743 len = source->BufUsed;
2744 for (a = 0; a < len; ++a) {
2745 if (target->BufUsed >= target->BufSize)
2746 IncreaseBuf(target, 1, -1);
2748 if (source->buf[a] == '=') {
2749 hex[0] = source->buf[a + 1];
2750 hex[1] = source->buf[a + 2];
2753 sscanf(hex, "%02x", &b);
2754 target->buf[target->BufUsed] = b;
2755 target->buf[++target->BufUsed] = 0;
2759 target->buf[target->BufUsed] = source->buf[a];
2760 target->buf[++target->BufUsed] = 0;
2767 * @ingroup StrBuf_DeEnCoder
2768 * @brief hide special chars from the HTML escapers and friends
2769 * @param target buffer to put the escaped string in
2770 * @param source buffer to escape
2772 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2777 FlushStrBuf(target);
2779 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2784 len = source->BufUsed;
2785 for (i=0; i<len; ++i) {
2786 if (target->BufUsed + 4 >= target->BufSize)
2787 IncreaseBuf(target, 1, -1);
2788 if ( (isalnum(source->buf[i])) ||
2789 (source->buf[i]=='-') ||
2790 (source->buf[i]=='_') ) {
2791 target->buf[target->BufUsed++] = source->buf[i];
2794 sprintf(&target->buf[target->BufUsed],
2796 (0xFF &source->buf[i]));
2797 target->BufUsed += 3;
2800 target->buf[target->BufUsed + 1] = '\0';
2804 /*******************************************************************************
2805 * Quoted Printable de/encoding *
2806 *******************************************************************************/
2809 * @ingroup StrBuf_DeEnCoder
2810 * @brief decode a buffer from base 64 encoding; destroys original
2811 * @param Buf Buffor to transform
2813 int StrBufDecodeBase64(StrBuf *Buf)
2821 xferbuf = (char*) malloc(Buf->BufSize);
2822 if (xferbuf == NULL)
2826 siz = CtdlDecodeBase64(xferbuf,
2833 Buf->buf[Buf->BufUsed] = '\0';
2838 * @ingroup StrBuf_DeEnCoder
2839 * @brief decode a buffer from base 64 encoding; expects targetbuffer
2840 * @param BufIn Buffor to transform
2841 * @param BufOut Buffer to put result into
2843 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2845 if ((BufIn == NULL) || (BufOut == NULL))
2848 if (BufOut->BufSize < BufIn->BufUsed)
2849 IncreaseBuf(BufOut, BufIn->BufUsed, 0);
2851 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2854 return BufOut->BufUsed;
2857 typedef struct __z_enc_stream {
2862 void *StrBufNewStreamContext(eStreamType type, const char **Err)
2864 base64_decodestate *state;;
2871 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2872 base64_init_decodestate(state);
2878 z_enc_stream *stream;
2881 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2882 memset(stream, 0, sizeof(z_enc_stream));
2883 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2884 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2886 err = inflateInit(&stream->zstream);
2889 StrBufDestroyStreamContext(type, (void**)&stream, Err);
2898 z_enc_stream *stream;
2901 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2902 memset(stream, 0, sizeof(z_enc_stream));
2903 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2904 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2905 /* write gzip header */
2906 stream->OutBuf.BufUsed = snprintf
2907 (stream->OutBuf.buf,
2908 stream->OutBuf.BufSize,
2909 "%c%c%c%c%c%c%c%c%c%c",
2910 gz_magic[0], gz_magic[1], Z_DEFLATED,
2911 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2914 err = deflateInit2(&stream->zstream,
2915 ZLibCompressionRatio,
2919 Z_DEFAULT_STRATEGY);
2921 StrBufDestroyStreamContext(type, (void**) &stream, Err);
2935 int StrBufDestroyStreamContext(eStreamType type, void **vStream, const char **Err)
2941 if ((vStream == NULL) || (*vStream==NULL)) {
2942 *Err = strerror(EINVAL);
2954 z_enc_stream *stream = (z_enc_stream *)*vStream;
2955 (void)inflateEnd(&stream->zstream);
2956 free(stream->OutBuf.buf);
2961 z_enc_stream *stream = (z_enc_stream *)*vStream;
2962 err = deflateEnd(&stream->zstream);
2967 free(stream->OutBuf.buf);
2978 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, void *vStream, int LastChunk, const char **Err)
2987 // base64_decodestate *state = (base64_decodestate*)vStream;
2993 pInLen = In->BufUsed;
2995 if ((In == NULL) || (vStream == NULL))
2998 ExpectLen = (pInLen / 4) * 3;
3000 if (Target->BufSize - Target->BufUsed < ExpectLen)
3002 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3005 //// ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3006 Target->BufUsed += ExpectLen;
3007 Target->buf[Target->BufUsed] = '\0';
3014 base64_decodestate *state = (base64_decodestate *)vStream;
3020 pInLen = In->BufUsed;
3022 if ((pIn == NULL) || (vStream == NULL))
3025 ExpectLen = (pInLen / 4) * 3;
3027 if (Target->BufSize - Target->BufUsed < ExpectLen)
3029 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3032 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3033 Target->BufUsed += ExpectLen;
3034 Target->buf[Target->BufUsed] = '\0';
3040 z_enc_stream *stream = (z_enc_stream *)vStream;
3041 int org_outbuf_len = stream->OutBuf.BufUsed;
3043 unsigned int chunkavail;
3045 if (In->ReadWritePointer != NULL)
3047 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3048 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3049 (In->ReadWritePointer - In->Buf->buf);
3053 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3054 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3057 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3058 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3060 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3062 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3066 (stream->OutBuf.BufUsed != org_outbuf_len)
3069 SwapBuffers(Target->Buf, &stream->OutBuf);
3072 if (stream->zstream.avail_in == 0)
3074 FlushStrBuf(In->Buf);
3075 In->ReadWritePointer = NULL;
3079 if (stream->zstream.avail_in < 64)
3081 memmove(In->Buf->buf,
3082 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3083 stream->zstream.avail_in);
3085 In->Buf->BufUsed = stream->zstream.avail_in;
3086 In->Buf->buf[In->Buf->BufUsed] = '\0';
3091 In->ReadWritePointer = In->Buf->buf +
3092 (In->Buf->BufUsed - stream->zstream.avail_in);
3095 rc = (LastChunk && (err != Z_FINISH));
3096 if (!rc && (err != Z_OK)) {
3103 z_enc_stream *stream = (z_enc_stream *)vStream;
3104 int org_outbuf_len = stream->zstream.total_out;
3107 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3108 if (In->ReadWritePointer != NULL)
3110 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3111 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
3112 (In->ReadWritePointer - In->Buf->buf);
3116 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3117 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3121 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3122 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3124 err = inflate(&stream->zstream, Z_NO_FLUSH);
3126 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
3129 err = Z_DATA_ERROR; /* and fall through */
3134 (void)inflateEnd(&stream->zstream);
3138 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3140 if (Target) SwapBuffers(Target->Buf, &stream->OutBuf);
3142 if (stream->zstream.avail_in == 0)
3144 FlushStrBuf(In->Buf);
3145 In->ReadWritePointer = NULL;
3149 if (stream->zstream.avail_in < 64)
3151 memmove(In->Buf->buf,
3152 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3153 stream->zstream.avail_in);
3155 In->Buf->BufUsed = stream->zstream.avail_in;
3156 In->Buf->buf[In->Buf->BufUsed] = '\0';
3161 In->ReadWritePointer = In->Buf->buf +
3162 (In->Buf->BufUsed - stream->zstream.avail_in);
3176 * @ingroup StrBuf_DeEnCoder
3177 * @brief decode a buffer from base 64 encoding; destroys original
3178 * @param Buf Buffor to transform
3180 int StrBufDecodeHex(StrBuf *Buf)
3183 char *pch, *pche, *pchi;
3185 if (Buf == NULL) return -1;
3187 pch = pchi = Buf->buf;
3188 pche = pch + Buf->BufUsed;
3190 while (pchi < pche){
3191 ch = decode_hex(pchi);
3198 Buf->BufUsed = pch - Buf->buf;
3199 return Buf->BufUsed;
3203 * @ingroup StrBuf_DeEnCoder
3204 * @brief replace all chars >0x20 && < 0x7F with Mute
3205 * @param Mute char to put over invalid chars
3206 * @param Buf Buffor to transform
3208 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3212 if (Buf == NULL) return -1;
3213 pch = (unsigned char *)Buf->buf;
3214 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3215 if ((*pch < 0x20) || (*pch > 0x7F))
3219 return Buf->BufUsed;
3224 * @ingroup StrBuf_DeEnCoder
3225 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3226 * @param Buf Buffer to translate
3227 * @param StripBlanks Reduce several blanks to one?
3229 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3238 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3239 Buf->buf[Buf->BufUsed - 1] = '\0';
3244 while (a < Buf->BufUsed) {
3245 if (Buf->buf[a] == '+')
3247 else if (Buf->buf[a] == '%') {
3248 /* don't let % chars through, rather truncate the input. */
3249 if (a + 2 > Buf->BufUsed) {
3254 hex[0] = Buf->buf[a + 1];
3255 hex[1] = Buf->buf[a + 2];
3258 sscanf(hex, "%02x", &b);
3259 Buf->buf[a] = (char) b;
3260 len = Buf->BufUsed - a - 2;
3262 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3274 * @ingroup StrBuf_DeEnCoder
3275 * @brief RFC2047-encode a header field if necessary.
3276 * If no non-ASCII characters are found, the string
3277 * will be copied verbatim without encoding.
3279 * @param target Target buffer.
3280 * @param source Source string to be encoded.
3281 * @returns encoded length; -1 if non success.
3283 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3285 const char headerStr[] = "=?UTF-8?Q?";
3286 int need_to_encode = 0;
3290 if ((source == NULL) ||
3294 while ((i < source->BufUsed) &&
3295 (!IsEmptyStr (&source->buf[i])) &&
3296 (need_to_encode == 0)) {
3297 if (((unsigned char) source->buf[i] < 32) ||
3298 ((unsigned char) source->buf[i] > 126)) {
3304 if (!need_to_encode) {
3305 if (*target == NULL) {
3306 *target = NewStrBufPlain(source->buf, source->BufUsed);
3309 FlushStrBuf(*target);
3310 StrBufAppendBuf(*target, source, 0);
3313 return (*target)->BufUsed;
3317 if (*target == NULL)
3318 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3319 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3320 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3321 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3322 (*target)->BufUsed = sizeof(headerStr) - 1;
3323 for (i=0; (i < source->BufUsed); ++i) {
3324 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3325 IncreaseBuf(*target, 1, 0);
3326 ch = (unsigned char) source->buf[i];
3335 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3336 (*target)->BufUsed += 3;
3340 (*target)->buf[(*target)->BufUsed] = '_';
3342 (*target)->buf[(*target)->BufUsed] = ch;
3343 (*target)->BufUsed++;
3347 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3348 IncreaseBuf(*target, 1, 0);
3350 (*target)->buf[(*target)->BufUsed++] = '?';
3351 (*target)->buf[(*target)->BufUsed++] = '=';
3352 (*target)->buf[(*target)->BufUsed] = '\0';
3353 return (*target)->BufUsed;;
3357 * @ingroup StrBuf_DeEnCoder
3358 * @brief Quoted-Printable encode a message; make it < 80 columns width.
3359 * @param source Source string to be encoded.
3360 * @returns buffer with encoded message.
3362 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3366 const char *ptr, *eptr;
3370 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3372 OEptr = OutBuf->buf + OutBuf->BufSize;
3373 ptr = EncodeMe->buf;
3374 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3379 if (Optr + 4 >= OEptr)
3382 Offset = Optr - OutBuf->buf;
3383 OutBuf->BufUsed = Optr - OutBuf->buf;
3384 IncreaseBuf(OutBuf, 1, 0);
3385 Optr = OutBuf->buf + Offset;
3386 OEptr = OutBuf->buf + OutBuf->BufSize;
3390 /* ignore carriage returns */
3393 else if (*ptr == '\n') {
3394 /* hard line break */
3395 memcpy(Optr, HKEY("=0A"));
3400 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3401 ( (*ptr >= 62) && (*ptr <= 126) ))
3412 *Optr = HexList[ch][0];
3414 *Optr = HexList[ch][1];
3421 /* soft line break */
3422 if (isspace(*(Optr - 1))) {
3427 *Optr = HexList[ch][0];
3429 *Optr = HexList[ch][1];
3441 OutBuf->BufUsed = Optr - OutBuf->buf;
3447 static void AddRecipient(StrBuf *Target,
3449 StrBuf *EmailAddress,
3454 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3455 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3457 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3458 StrBufRFC2047encode(&EncBuf, UserName);
3459 StrBufAppendBuf(Target, EncBuf, 0);
3460 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3461 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3463 if (StrLength(EmailAddress) > 0){
3464 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3465 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3466 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3472 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3473 * \param Recp Source list of email recipients
3474 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3475 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3476 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3477 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3479 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3481 StrBuf *EmailAddress,
3485 const char *pch, *pche;
3486 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3488 if ((Recp == NULL) || (StrLength(Recp) == 0))
3492 pche = pch + StrLength(Recp);
3494 if (!CheckEncode(pch, -1, pche))
3495 return NewStrBufDup(Recp);
3497 Target = NewStrBufPlain(NULL, StrLength(Recp));
3499 while ((pch != NULL) && (pch < pche))
3501 while (isspace(*pch)) pch++;
3502 UserEnd = EmailStart = EmailEnd = NULL;
3504 if ((*pch == '"') || (*pch == '\'')) {
3505 UserStart = pch + 1;
3507 UserEnd = strchr(UserStart, *pch);
3508 if (UserEnd == NULL)
3509 break; ///TODO: Userfeedback??
3510 EmailStart = UserEnd + 1;
3511 while (isspace(*EmailStart))
3513 if (UserEnd == UserStart) {
3514 UserStart = UserEnd = NULL;
3517 if (*EmailStart == '<') {
3519 EmailEnd = strchr(EmailStart, '>');
3520 if (EmailEnd == NULL)
3521 EmailEnd = strchr(EmailStart, ',');
3525 EmailEnd = strchr(EmailStart, ',');
3527 if (EmailEnd == NULL)
3534 EmailEnd = strchr(UserStart, ',');
3535 if (EmailEnd == NULL) {
3536 EmailEnd = strchr(pch, '>');
3538 if (EmailEnd != NULL) {
3548 while ((EmailEnd > UserStart) && !gt &&
3549 ((*EmailEnd == ',') ||
3550 (*EmailEnd == '>') ||
3551 (isspace(*EmailEnd))))
3553 if (*EmailEnd == '>')
3558 if (EmailEnd == UserStart)
3562 EmailStart = strchr(UserStart, '<');
3563 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3565 UserEnd = EmailStart;
3567 while ((UserEnd > UserStart) &&
3568 isspace (*(UserEnd - 1)))
3571 if (UserStart >= UserEnd)
3572 UserStart = UserEnd = NULL;
3574 else { /* this is a local recipient... no domain, just a realname */
3575 EmailStart = UserStart;
3576 At = strchr(EmailStart, '@');
3582 EmailStart = UserStart;
3588 if ((UserStart != NULL) && (UserEnd != NULL))
3589 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3590 else if ((UserStart != NULL) && (UserEnd == NULL))
3591 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3593 FlushStrBuf(UserName);
3595 if ((EmailStart != NULL) && (EmailEnd != NULL))
3596 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3597 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3598 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3600 FlushStrBuf(EmailAddress);
3602 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3607 if ((pch != NULL) && (*pch == ','))
3609 if (pch != NULL) while (isspace(*pch))
3618 * @brief replaces all occurances of 'search' by 'replace'
3619 * @param buf Buffer to modify
3620 * @param search character to search
3621 * @param replace character to replace search by
3623 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3628 for (i=0; i<buf->BufUsed; i++)
3629 if (buf->buf[i] == search)
3630 buf->buf[i] = replace;
3636 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3637 * @param buf Buffer to modify
3639 void StrBufToUnixLF(StrBuf *buf)
3641 char *pche, *pchS, *pchT;
3645 pche = buf->buf + buf->BufUsed;
3646 pchS = pchT = buf->buf;
3652 if (*pchS != '\n') {
3661 buf->BufUsed = pchT - buf->buf;
3665 /*******************************************************************************
3666 * Iconv Wrapper; RFC822 de/encoding *
3667 *******************************************************************************/
3670 * @ingroup StrBuf_DeEnCoder
3671 * @brief Wrapper around iconv_open()
3672 * Our version adds aliases for non-standard Microsoft charsets
3673 * such as 'MS950', aliasing them to names like 'CP950'
3675 * @param tocode Target encoding
3676 * @param fromcode Source encoding
3677 * @param pic anonimized pointer to iconv struct
3679 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3682 iconv_t ic = (iconv_t)(-1) ;
3683 ic = iconv_open(tocode, fromcode);
3684 if (ic == (iconv_t)(-1) ) {
3685 char alias_fromcode[64];
3686 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3687 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3688 alias_fromcode[0] = 'C';
3689 alias_fromcode[1] = 'P';
3690 ic = iconv_open(tocode, alias_fromcode);
3693 *(iconv_t *)pic = ic;
3699 * @ingroup StrBuf_DeEnCoder
3700 * @brief find one chunk of a RFC822 encoded string
3701 * @param Buffer where to search
3702 * @param bptr where to start searching
3703 * @returns found position, NULL if none.
3705 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3708 /* Find the next ?Q? */
3709 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3712 end = strchr(bptr + 2, '?');
3717 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3718 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3719 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3720 (*(end + 2) == '?')) {
3721 /* skip on to the end of the cluster, the next ?= */
3722 end = strstr(end + 3, "?=");
3725 /* sort of half valid encoding, try to find an end. */
3726 end = strstr(bptr, "?=");
3733 * @ingroup StrBuf_DeEnCoder
3734 * @brief convert one buffer according to the preselected iconv pointer PIC
3735 * @param ConvertBuf buffer we need to translate
3736 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3737 * @param pic Pointer to the iconv-session Object
3739 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3745 char *ibuf; /**< Buffer of characters to be converted */
3746 char *obuf; /**< Buffer for converted characters */
3747 size_t ibuflen; /**< Length of input buffer */
3748 size_t obuflen; /**< Length of output buffer */
3751 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3754 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3755 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3756 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3758 ic = *(iconv_t*)pic;
3759 ibuf = ConvertBuf->buf;
3760 ibuflen = ConvertBuf->BufUsed;
3762 obuflen = TmpBuf->BufSize;
3764 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3767 if (errno == E2BIG) {
3769 IncreaseBuf(TmpBuf, 0, 0);
3774 else if (errno == EILSEQ){
3775 /* hm, invalid utf8 sequence... what to do now? */
3776 /* An invalid multibyte sequence has been encountered in the input */
3778 else if (errno == EINVAL) {
3779 /* An incomplete multibyte sequence has been encountered in the input. */
3782 FlushStrBuf(TmpBuf);
3785 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3786 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3788 /* little card game: wheres the red lady? */
3789 SwapBuffers(ConvertBuf, TmpBuf);
3790 FlushStrBuf(TmpBuf);
3797 * @ingroup StrBuf_DeEnCoder
3798 * @brief catches one RFC822 encoded segment, and decodes it.
3799 * @param Target buffer to fill with result
3800 * @param DecodeMe buffer with stuff to process
3801 * @param SegmentStart points to our current segment in DecodeMe
3802 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3803 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3804 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3805 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3807 inline static void DecodeSegment(StrBuf *Target,
3808 const StrBuf *DecodeMe,
3809 const char *SegmentStart,
3810 const char *SegmentEnd,
3812 StrBuf *ConvertBuf2,
3813 StrBuf *FoundCharset)
3819 iconv_t ic = (iconv_t)(-1);
3823 /* Now we handle foreign character sets properly encoded
3824 * in RFC2047 format.
3826 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3827 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3828 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3829 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3830 if (FoundCharset != NULL) {
3831 FlushStrBuf(FoundCharset);
3832 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3834 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3835 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3837 *encoding = toupper(*encoding);
3838 if (*encoding == 'B') { /**< base64 */
3839 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3840 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3841 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3843 ConvertBuf->BufUsed);
3845 else if (*encoding == 'Q') { /**< quoted-printable */
3849 while (pos < ConvertBuf->BufUsed)
3851 if (ConvertBuf->buf[pos] == '_')
3852 ConvertBuf->buf[pos] = ' ';
3856 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3857 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3859 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3862 ConvertBuf->BufUsed);
3865 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3868 ctdl_iconv_open("UTF-8", charset, &ic);
3869 if (ic != (iconv_t)(-1) ) {
3871 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3872 StrBufAppendBuf(Target, ConvertBuf2, 0);
3877 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3883 * @ingroup StrBuf_DeEnCoder
3884 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3885 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3886 * @param Target where to put the decoded string to
3887 * @param DecodeMe buffer with encoded string
3888 * @param DefaultCharset if we don't find one, which should we use?
3889 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3890 * put it here for later use where no string might be known.
3892 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3895 StrBuf *ConvertBuf2;
3896 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3897 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3899 StrBuf_RFC822_2_Utf8(Target,
3905 FreeStrBuf(&ConvertBuf);
3906 FreeStrBuf(&ConvertBuf2);
3910 * @ingroup StrBuf_DeEnCoder
3911 * @brief Handle subjects with RFC2047 encoding such as:
3912 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3913 * @param Target where to put the decoded string to
3914 * @param DecodeMe buffer with encoded string
3915 * @param DefaultCharset if we don't find one, which should we use?
3916 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3917 * put it here for later use where no string might be known.
3918 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3919 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3921 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3922 const StrBuf *DecodeMe,
3923 const StrBuf* DefaultCharset,
3924 StrBuf *FoundCharset,
3926 StrBuf *ConvertBuf2)
3928 StrBuf *DecodedInvalidBuf = NULL;
3929 const StrBuf *DecodeMee = DecodeMe;
3930 const char *start, *end, *next, *nextend, *ptr = NULL;
3932 iconv_t ic = (iconv_t)(-1) ;
3937 int illegal_non_rfc2047_encoding = 0;
3940 if (DecodeMe == NULL)
3942 /* Sometimes, badly formed messages contain strings which were simply
3943 * written out directly in some foreign character set instead of
3944 * using RFC2047 encoding. This is illegal but we will attempt to
3945 * handle it anyway by converting from a user-specified default
3946 * charset to UTF-8 if we see any nonprintable characters.
3949 for (i=0; i<DecodeMe->BufUsed; ++i) {
3950 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3951 illegal_non_rfc2047_encoding = 1;
3956 if ((illegal_non_rfc2047_encoding) &&
3957 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3958 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3961 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3962 if (ic != (iconv_t)(-1) ) {
3963 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3964 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3965 DecodeMee = DecodedInvalidBuf;
3971 /* pre evaluate the first pair */
3973 start = strstr(DecodeMee->buf, "=?");
3974 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3976 end = FindNextEnd (DecodeMee, start + 2);
3978 StrBufAppendBuf(Target, DecodeMee, 0);
3979 FreeStrBuf(&DecodedInvalidBuf);
3984 if (start != DecodeMee->buf) {
3987 nFront = start - DecodeMee->buf;
3988 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3991 * Since spammers will go to all sorts of absurd lengths to get their
3992 * messages through, there are LOTS of corrupt headers out there.
3993 * So, prevent a really badly formed RFC2047 header from throwing
3994 * this function into an infinite loop.
3996 while ((start != NULL) &&
4003 DecodeSegment(Target,
4011 next = strstr(end, "=?");
4013 if ((next != NULL) &&
4015 nextend = FindNextEnd(DecodeMee, next);
4016 if (nextend == NULL)
4019 /* did we find two partitions */
4020 if ((next != NULL) &&
4024 while ((ptr < next) &&
4031 * did we find a gab just filled with blanks?
4032 * if not, copy its stuff over.
4036 StrBufAppendBufPlain(Target,
4042 /* our next-pair is our new first pair now. */
4048 nextend = DecodeMee->buf + DecodeMee->BufUsed;
4049 if ((end != NULL) && (end < nextend)) {
4051 while ( (ptr < nextend) &&
4058 StrBufAppendBufPlain(Target, end, nextend - end, 0);
4060 FreeStrBuf(&DecodedInvalidBuf);
4063 /*******************************************************************************
4064 * Manipulating UTF-8 Strings *
4065 *******************************************************************************/
4069 * @brief evaluate the length of an utf8 special character sequence
4070 * @param Char the character to examine
4071 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4073 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4076 unsigned char test = (1<<7);
4078 if ((*CharS & 0xC0) != 0xC0)
4082 ((test & ((unsigned char)*CharS)) != 0))
4087 if ((n > 6) || ((CharE - CharS) < n))
4094 * @brief detect whether this char starts an utf-8 encoded char
4095 * @param Char character to inspect
4096 * @returns yes or no
4098 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4100 /** 11??.???? indicates an UTF8 Sequence. */
4101 return ((Char & 0xC0) == 0xC0);
4106 * @brief measure the number of glyphs in an UTF8 string...
4107 * @param Buf string to measure
4108 * @returns the number of glyphs in Buf
4110 long StrBuf_Utf8StrLen(StrBuf *Buf)
4116 if ((Buf == NULL) || (Buf->BufUsed == 0))
4119 eptr = Buf->buf + Buf->BufUsed;
4120 while ((aptr < eptr) && (*aptr != '\0')) {
4121 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4122 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4123 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4136 * @brief cuts a string after maxlen glyphs
4137 * @param Buf string to cut to maxlen glyphs
4138 * @param maxlen how long may the string become?
4139 * @returns current length of the string
4141 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4147 eptr = Buf->buf + Buf->BufUsed;
4148 while ((aptr < eptr) && (*aptr != '\0')) {
4149 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4150 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4151 while ((*aptr++ != '\0') && (m-- > 0));
4160 Buf->BufUsed = aptr - Buf->buf;
4161 return Buf->BufUsed;
4164 return Buf->BufUsed;
4172 /*******************************************************************************
4174 *******************************************************************************/
4177 * @ingroup StrBuf_DeEnCoder
4178 * @brief uses the same calling syntax as compress2(), but it
4179 * creates a stream compatible with HTTP "Content-encoding: gzip"
4180 * @param dest compressed buffer
4181 * @param destLen length of the compresed data
4182 * @param source source to encode
4183 * @param sourceLen length of source to encode
4184 * @param level compression level
4187 int ZEXPORT compress_gzip(Bytef * dest,
4189 const Bytef * source,
4193 /* write gzip header */
4194 snprintf((char *) dest, *destLen,
4195 "%c%c%c%c%c%c%c%c%c%c",
4196 gz_magic[0], gz_magic[1], Z_DEFLATED,
4197 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4200 /* normal deflate */
4203 stream.next_in = (Bytef *) source;
4204 stream.avail_in = (uInt) sourceLen;
4205 stream.next_out = dest + 10L; // after header
4206 stream.avail_out = (uInt) * destLen;
4207 if ((uLong) stream.avail_out != *destLen)
4210 stream.zalloc = (alloc_func) 0;
4211 stream.zfree = (free_func) 0;
4212 stream.opaque = (voidpf) 0;
4214 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4215 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4219 err = deflate(&stream, Z_FINISH);
4220 if (err != Z_STREAM_END) {
4221 deflateEnd(&stream);
4222 return err == Z_OK ? Z_BUF_ERROR : err;
4224 *destLen = stream.total_out + 10L;
4226 /* write CRC and Length */
4227 uLong crc = crc32(0L, source, sourceLen);
4229 for (n = 0; n < 4; ++n, ++*destLen) {
4230 dest[*destLen] = (int) (crc & 0xff);
4233 uLong len = stream.total_in;
4234 for (n = 0; n < 4; ++n, ++*destLen) {
4235 dest[*destLen] = (int) (len & 0xff);
4238 err = deflateEnd(&stream);
4245 * @ingroup StrBuf_DeEnCoder
4246 * @brief compress the buffer with gzip
4247 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4248 * @param Buf buffer whose content is to be gzipped
4250 int CompressBuffer(StrBuf *Buf)
4253 char *compressed_data = NULL;
4254 size_t compressed_len, bufsize;
4257 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
4258 compressed_data = malloc(compressed_len);
4260 if (compressed_data == NULL)
4262 /* Flush some space after the used payload so valgrind shuts up... */
4263 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4264 Buf->buf[Buf->BufUsed + i++] = '\0';
4265 if (compress_gzip((Bytef *) compressed_data,
4268 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4271 Buf->buf = compressed_data;
4272 Buf->BufUsed = compressed_len;
4273 Buf->BufSize = bufsize;
4274 /* Flush some space after the used payload so valgrind shuts up... */
4276 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4277 Buf->buf[Buf->BufUsed + i++] = '\0';
4280 free(compressed_data);
4282 #endif /* HAVE_ZLIB */
4286 /*******************************************************************************
4287 * File I/O; Callbacks to libevent *
4288 *******************************************************************************/
4290 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4295 if ((FB == NULL) || (FB->Buf == NULL))
4299 * check whether the read pointer is somewhere in a range
4300 * where a cut left is inexpensive
4303 if (FB->ReadWritePointer != NULL)
4307 already_read = FB->ReadWritePointer - FB->Buf->buf;
4308 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4310 if (already_read != 0) {
4313 unread = FB->Buf->BufUsed - already_read;
4315 /* else nothing to compact... */
4317 FB->ReadWritePointer = FB->Buf->buf;
4318 bufremain = FB->Buf->BufSize;
4320 else if ((unread < 64) ||
4321 (bufremain < already_read))
4324 * if its just a tiny bit remaining, or we run out of space...
4327 FB->Buf->BufUsed = unread;
4328 if (unread < already_read)
4329 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4331 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4332 FB->ReadWritePointer = FB->Buf->buf;
4333 bufremain = FB->Buf->BufSize - unread - 1;
4335 else if (bufremain < (FB->Buf->BufSize / 10))
4337 /* get a bigger buffer */
4339 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4341 FB->ReadWritePointer = FB->Buf->buf + unread;
4343 bufremain = FB->Buf->BufSize - unread - 1;
4344 /*TODO: special increase function that won't copy the already read! */
4347 else if (bufremain < 10) {
4348 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4350 FB->ReadWritePointer = FB->Buf->buf;
4352 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4357 FB->ReadWritePointer = FB->Buf->buf;
4358 bufremain = FB->Buf->BufSize - 1;
4361 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4364 FB->Buf->BufUsed += n;
4365 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4370 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4375 if ((FB == NULL) || (FB->Buf == NULL))
4378 if (FB->ReadWritePointer != NULL)
4380 WriteRemain = FB->Buf->BufUsed -
4381 (FB->ReadWritePointer -
4385 FB->ReadWritePointer = FB->Buf->buf;
4386 WriteRemain = FB->Buf->BufUsed;
4389 n = write(fd, FB->ReadWritePointer, WriteRemain);
4391 FB->ReadWritePointer += n;
4393 if (FB->ReadWritePointer ==
4394 FB->Buf->buf + FB->Buf->BufUsed)
4396 FlushStrBuf(FB->Buf);
4397 FB->ReadWritePointer = NULL;
4400 // check whether we've got something to write
4401 // get the maximum chunk plus the pointer we can send
4402 // write whats there
4403 // if not all was sent, remember the send pointer for the next time
4404 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4410 * @ingroup StrBuf_IO
4411 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4412 * @param LineBuf your line will be copied here.
4413 * @param FB BLOB with lines of text...
4414 * @param Ptr moved arround to keep the next-line across several iterations
4415 * has to be &NULL on start; will be &NotNULL on end of buffer
4416 * @returns size of copied buffer
4418 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4420 const char *aptr, *ptr, *eptr;
4423 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4427 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4428 FB->ReadWritePointer = StrBufNOTNULL;
4432 FlushStrBuf(LineBuf);
4433 if (FB->ReadWritePointer == NULL)
4434 ptr = aptr = FB->Buf->buf;
4436 ptr = aptr = FB->ReadWritePointer;
4438 optr = LineBuf->buf;
4439 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4440 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4442 while ((ptr <= eptr) &&
4449 LineBuf->BufUsed = optr - LineBuf->buf;
4450 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4451 optr = LineBuf->buf + LineBuf->BufUsed;
4452 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4457 if (optr > LineBuf->buf)
4459 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4460 LineBuf->BufUsed = optr - LineBuf->buf;
4462 if ((FB->ReadWritePointer != NULL) &&
4463 (FB->ReadWritePointer != FB->Buf->buf))
4465 /* Ok, the client application read all the data
4466 it was interested in so far. Since there is more to read,
4467 we now shrink the buffer, and move the rest over.
4469 StrBufCutLeft(FB->Buf,
4470 FB->ReadWritePointer - FB->Buf->buf);
4471 FB->ReadWritePointer = FB->Buf->buf;
4473 return eMustReadMore;
4476 LineBuf->BufUsed = optr - LineBuf->buf;
4478 if ((ptr <= eptr) && (*ptr == '\r'))
4480 if ((ptr <= eptr) && (*ptr == '\n'))
4484 FB->ReadWritePointer = ptr;
4487 FlushStrBuf(FB->Buf);
4488 FB->ReadWritePointer = NULL;
4491 return eReadSuccess;
4495 * @ingroup StrBuf_CHUNKED_IO
4496 * @brief check whether the chunk-buffer has more data waiting or not.
4497 * @param FB Chunk-Buffer to inspect
4499 eReadState StrBufCheckBuffer(IOBuffer *FB)
4503 if (FB->Buf->BufUsed == 0)
4504 return eReadSuccess;
4505 if (FB->ReadWritePointer == NULL)
4506 return eBufferNotEmpty;
4507 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4508 return eBufferNotEmpty;
4509 return eReadSuccess;
4512 long IOBufferStrLength(IOBuffer *FB)
4514 if ((FB == NULL) || (FB->Buf == NULL))
4516 if (FB->ReadWritePointer == NULL)
4517 return StrLength(FB->Buf);
4519 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4522 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
4524 memset(FDB, 0, sizeof(FDIOBuffer));
4526 FDB->SplicePipe[0] = -1;
4527 FDB->SplicePipe[1] = -1;
4530 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
4532 FDIOBufferFlush(FDB);
4534 FDB->TotalSendSize = TotalSendSize;
4535 if (TotalSendSize > 0)
4536 FDB->ChunkSize = TotalSendSize;
4539 TotalSendSize = SIZ * 10;
4540 FDB->ChunkSize = TotalSendSize;
4546 pipe(FDB->SplicePipe);
4549 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
4554 void FDIOBufferDelete(FDIOBuffer *FDB)
4559 if (FDB->SplicePipe[0] > 0)
4560 close(FDB->SplicePipe[0]);
4561 if (FDB->SplicePipe[1] > 0)
4562 close(FDB->SplicePipe[1]);
4566 FreeStrBuf(&FDB->ChunkBuffer);
4568 if (FDB->OtherFD > 0)
4569 close(FDB->OtherFD);
4570 FDIOBufferFlush(FDB);
4573 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4575 ssize_t sent, pipesize;
4577 if (FDB->TotalSendSize > 0)
4582 if (FDB->PipeSize == 0)
4584 pipesize = splice(FDB->OtherFD,
4585 &FDB->TotalSentAlready,
4588 FDB->ChunkSendRemain,
4593 *Err = strerror(errno);
4596 FDB->PipeSize = pipesize;
4598 sent = splice(FDB->SplicePipe[0],
4603 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4606 *Err = strerror(errno);
4609 FDB->PipeSize -= sent;
4610 FDB->ChunkSendRemain -= sent;
4619 pRead = FDB->ChunkBuffer->buf;
4620 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4622 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4624 FDB->ChunkBuffer->BufUsed += nRead;
4625 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4627 else if (nRead == 0) {}
4631 nRead = write(FDB->IOB->fd,
4632 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4633 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4636 FDB->TotalSentAlready += nRead;
4637 FDB->ChunkSendRemain -= nRead;
4638 return FDB->ChunkSendRemain;
4650 if (FDB->PipeSize == 0)
4652 pipesize = splice(FDB->OtherFD,
4653 &FDB->TotalSentAlready,
4661 *Err = strerror(errno);
4664 FDB->PipeSize = pipesize;
4668 sent = splice(FDB->SplicePipe[0],
4673 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4676 *Err = strerror(errno);
4679 FDB->PipeSize -= sent;
4680 FDB->ChunkSendRemain -= sent;
4689 pRead = FDB->ChunkBuffer->buf;
4690 while ((FDB->ChunkSendRemain == 0) &&
4691 (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
4694 FDB->TotalSentAlready = 0;
4695 nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
4697 FDB->ChunkBuffer->BufUsed += nRead;
4698 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4699 FDB->ChunkSendRemain += nRead;
4701 else if (nRead == 0)
4707 *Err = strerror(errno);
4712 nRead = write(FDB->IOB->fd,
4713 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4714 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4717 FDB->TotalSentAlready += nRead;
4718 FDB->ChunkSendRemain -= nRead;
4719 if (FDB->ChunkSendRemain == 0)
4721 FDB->ChunkBuffer->BufUsed = 0;
4722 FDB->TotalSentAlready = 0;
4724 return FDB->ChunkSendRemain;
4733 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4735 ssize_t sent, pipesize;
4740 if (FDB->PipeSize == 0)
4742 pipesize = splice(FDB->IOB->fd,
4746 FDB->ChunkSendRemain,
4747 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4751 *Err = strerror(errno);
4754 FDB->PipeSize = pipesize;
4757 sent = splice(FDB->SplicePipe[0],
4760 &FDB->TotalSentAlready,
4762 SPLICE_F_MORE | SPLICE_F_MOVE);
4766 *Err = strerror(errno);
4769 FDB->PipeSize -= sent;
4770 FDB->ChunkSendRemain -= sent;
4776 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4781 FDB->ChunkBuffer->BufUsed = sent;
4783 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4784 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4786 *Err = strerror(errno);
4792 FDB->ChunkBuffer->BufUsed = 0;
4793 FDB->TotalSentAlready += sent;
4794 FDB->ChunkSendRemain -= sent;
4795 return FDB->ChunkSendRemain;
4797 else if (sent < 0) {
4798 *Err = strerror(errno);
4805 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4807 ssize_t sent, pipesize;
4812 if (FDB->PipeSize == 0)
4814 pipesize = splice(FDB->IOB->fd,
4815 &FDB->TotalReadAlready,
4818 FDB->ChunkSendRemain,
4819 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4823 *Err = strerror(errno);
4826 FDB->PipeSize = pipesize;
4829 sent = splice(FDB->SplicePipe[0],
4832 &FDB->TotalSentAlready,
4834 SPLICE_F_MORE | SPLICE_F_MOVE);
4838 *Err = strerror(errno);
4841 FDB->PipeSize -= sent;
4842 FDB->ChunkSendRemain -= sent;
4848 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4853 FDB->ChunkBuffer->BufUsed = sent;
4855 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4856 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4858 *Err = strerror(errno);
4864 FDB->ChunkBuffer->BufUsed = 0;
4865 FDB->TotalSentAlready += sent;
4866 FDB->ChunkSendRemain -= sent;
4867 return FDB->ChunkSendRemain;
4869 else if (sent < 0) {
4870 *Err = strerror(errno);
4877 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4883 int nSuccessLess = 0;
4887 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4888 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4890 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4891 (FDB->ChunkSendRemain > 0))
4894 tv.tv_sec = 1; /* selectresolution; */
4898 FD_SET(FDB->OtherFD, &rfds);
4899 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4900 *Error = strerror(errno);
4904 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4909 should_write = FDB->IOB->Buf->BufUsed -
4910 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4911 if (should_write > FDB->ChunkSendRemain)
4912 should_write = FDB->ChunkSendRemain;
4914 rlen = write(FDB->OtherFD,
4915 FDB->IOB->ReadWritePointer,
4918 *Error = strerror(errno);
4922 FDB->TotalSentAlready += rlen;
4923 FDB->IOB->ReadWritePointer += rlen;
4924 FDB->ChunkSendRemain -= rlen;
4926 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4928 FlushStrBuf(FDB->IOB->Buf);
4929 FDB->IOB->ReadWritePointer = NULL;
4932 if (FDB->ChunkSendRemain == 0)
4933 return eReadSuccess;
4935 return eMustReadMore;
4938 /*******************************************************************************
4939 * File I/O; Prefer buffered read since its faster! *
4940 *******************************************************************************/
4943 * @ingroup StrBuf_IO
4944 * @brief Read a line from socket
4945 * flushes and closes the FD on error
4946 * @param buf the buffer to get the input to
4947 * @param fd pointer to the filedescriptor to read
4948 * @param append Append to an existing string or replace?
4949 * @param Error strerror() on error
4950 * @returns numbers of chars read
4952 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4954 int len, rlen, slen;
4956 if ((buf == NULL) || (buf->buf == NULL)) {
4957 *Error = strerror(EINVAL);
4964 slen = len = buf->BufUsed;
4966 rlen = read(*fd, &buf->buf[len], 1);
4968 *Error = strerror(errno);
4975 if (buf->buf[len] == '\n')
4977 if (buf->buf[len] != '\r')
4979 if (len + 2 >= buf->BufSize) {
4981 buf->buf[len+1] = '\0';
4982 IncreaseBuf(buf, 1, -1);
4986 buf->buf[len] = '\0';
4991 * @ingroup StrBuf_BufferedIO
4992 * @brief Read a line from socket
4993 * flushes and closes the FD on error
4994 * @param Line the line to read from the fd / I/O Buffer
4995 * @param buf the buffer to get the input to
4996 * @param fd pointer to the filedescriptor to read
4997 * @param timeout number of successless selects until we bail out
4998 * @param selectresolution how long to wait on each select
4999 * @param Error strerror() on error
5000 * @returns numbers of chars read
5002 int StrBufTCP_read_buffered_line(StrBuf *Line,
5006 int selectresolution,
5010 int nSuccessLess = 0;
5017 if (buf->BufUsed > 0) {
5018 pch = strchr(buf->buf, '\n');
5021 len = pch - buf->buf;
5022 if (len > 0 && (*(pch - 1) == '\r') )
5024 StrBufSub(Line, buf, 0, len - rlen);
5025 StrBufCutLeft(buf, len + 1);
5030 if (buf->BufSize - buf->BufUsed < 10)
5031 IncreaseBuf(buf, 1, -1);
5033 fdflags = fcntl(*fd, F_GETFL);
5034 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5036 while ((nSuccessLess < timeout) && (pch == NULL)) {
5038 tv.tv_sec = selectresolution;
5043 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
5044 *Error = strerror(errno);
5050 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
5055 &buf->buf[buf->BufUsed],
5056 buf->BufSize - buf->BufUsed - 1);
5058 *Error = strerror(errno);
5063 else if (rlen > 0) {
5065 buf->BufUsed += rlen;
5066 buf->buf[buf->BufUsed] = '\0';
5067 pch = strchr(buf->buf, '\n');
5068 if ((pch == NULL) &&
5069 (buf->BufUsed + 10 > buf->BufSize) &&
5070 (IncreaseBuf(buf, 1, -1) == -1))
5078 len = pch - buf->buf;
5079 if (len > 0 && (*(pch - 1) == '\r') )
5081 StrBufSub(Line, buf, 0, len - rlen);
5082 StrBufCutLeft(buf, len + 1);
5089 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
5090 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
5091 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
5093 * @ingroup StrBuf_BufferedIO
5094 * @brief Read a line from socket
5095 * flushes and closes the FD on error
5096 * @param Line where to append our Line read from the fd / I/O Buffer;
5097 * @param IOBuf the buffer to get the input to; lifetime pair to FD
5098 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
5099 * @param fd pointer to the filedescriptor to read
5100 * @param timeout number of successless selects until we bail out
5101 * @param selectresolution how long to wait on each select
5102 * @param Error strerror() on error
5103 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
5105 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
5110 int selectresolution,
5113 const char *pche = NULL;
5114 const char *pos = NULL;
5116 int len, rlen, retlen;
5117 int nSuccessLess = 0;
5119 const char *pch = NULL;
5125 if ((Line == NULL) ||
5132 *Error = ErrRBLF_PreConditionFailed;
5137 if ((IOBuf->BufUsed > 0) &&
5139 (pos < IOBuf->buf + IOBuf->BufUsed))
5143 pche = IOBuf->buf + IOBuf->BufUsed;
5147 while ((pch < pche) && (*pch != '\n'))
5149 if (Line->BufUsed + 10 > Line->BufSize)
5152 apos = pcht - Line->buf;
5154 IncreaseBuf(Line, 1, -1);
5155 pcht = Line->buf + apos;
5163 if (len > 0 && (*(pch - 1) == '\r') )
5172 if ((pch >= pche) || (*pch == '\0'))
5180 if ((pch != NULL) &&
5183 if (pch + 1 >= pche) {
5196 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
5198 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
5199 IncreaseBuf(IOBuf, 1, -1);
5201 fdflags = fcntl(*fd, F_GETFL);
5202 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5205 while ((nSuccessLess < timeout) &&
5215 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
5216 *Error = strerror(errno);
5220 *Error = ErrRBLF_SelectFailed;
5223 if (! FD_ISSET(*fd, &rfds) != 0) {
5229 &IOBuf->buf[IOBuf->BufUsed],
5230 IOBuf->BufSize - IOBuf->BufUsed - 1);
5232 *Error = strerror(errno);
5237 else if (rlen > 0) {
5239 pLF = IOBuf->buf + IOBuf->BufUsed;
5240 IOBuf->BufUsed += rlen;
5241 IOBuf->buf[IOBuf->BufUsed] = '\0';
5243 pche = IOBuf->buf + IOBuf->BufUsed;
5245 while ((pLF < pche) && (*pLF != '\n'))
5247 if ((pLF >= pche) || (*pLF == '\0'))
5250 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
5254 if (pLF != NULL) apos = pLF - IOBuf->buf;
5255 IncreaseBuf(IOBuf, 1, -1);
5256 if (pLF != NULL) pLF = IOBuf->buf + apos;
5270 if (len > 0 && (*(pLF - 1) == '\r') )
5272 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
5273 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
5279 return retlen + len;
5281 *Error = ErrRBLF_NotEnoughSentFromServer;
5286 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
5288 * @ingroup StrBuf_IO
5289 * @brief Input binary data from socket
5290 * flushes and closes the FD on error
5291 * @param Buf the buffer to get the input to
5292 * @param fd pointer to the filedescriptor to read
5293 * @param append Append to an existing string or replace?
5294 * @param nBytes the maximal number of bytes to read
5295 * @param Error strerror() on error
5296 * @returns numbers of chars read
5298 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
5309 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
5311 *Error = ErrRBLF_BLOBPreConditionFailed;
5316 if (Buf->BufUsed + nBytes >= Buf->BufSize)
5317 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
5319 ptr = Buf->buf + Buf->BufUsed;
5321 fdflags = fcntl(*fd, F_GETFL);
5322 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5324 while ((nRead < nBytes) &&
5334 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5335 *Error = strerror(errno);
5339 *Error = ErrRBLF_SelectFailed;
5342 if (! FD_ISSET(*fd, &rfds) != 0) {
5348 if ((rlen = read(*fd,
5350 nBytes - nRead)) == -1) {
5353 *Error = strerror(errno);
5358 Buf->BufUsed += rlen;
5360 Buf->buf[Buf->BufUsed] = '\0';
5364 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
5365 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
5367 * @ingroup StrBuf_BufferedIO
5368 * @brief Input binary data from socket
5369 * flushes and closes the FD on error
5370 * @param Blob put binary thing here
5371 * @param IOBuf the buffer to get the input to
5372 * @param Pos offset inside of IOBuf
5373 * @param fd pointer to the filedescriptor to read
5374 * @param append Append to an existing string or replace?
5375 * @param nBytes the maximal number of bytes to read
5376 * @param check whether we should search for '000\n' terminators in case of timeouts
5377 * @param Error strerror() on error
5378 * @returns numbers of chars read
5380 int StrBufReadBLOBBuffered(StrBuf *Blob,
5393 int nAlreadyRead = 0;
5398 int nSuccessLess = 0;
5401 if ((Blob == NULL) ||
5408 *Error = ErrRBB_BLOBFPreConditionFailed;
5414 if (Blob->BufUsed + nBytes >= Blob->BufSize)
5415 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
5420 rlen = pos - IOBuf->buf;
5421 rlen = IOBuf->BufUsed - rlen;
5424 if ((IOBuf->BufUsed > 0) &&
5426 (pos < IOBuf->buf + IOBuf->BufUsed))
5428 if (rlen < nBytes) {
5429 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5430 Blob->BufUsed += rlen;
5431 Blob->buf[Blob->BufUsed] = '\0';
5432 nAlreadyRead = nRead = rlen;
5435 if (rlen >= nBytes) {
5436 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5437 Blob->BufUsed += nBytes;
5438 Blob->buf[Blob->BufUsed] = '\0';
5439 if (rlen == nBytes) {
5451 if (IOBuf->BufSize < nBytes - nRead)
5452 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5455 fdflags = fcntl(*fd, F_GETFL);
5456 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5464 while ((nSuccessLess < MaxTries) &&
5474 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5475 *Error = strerror(errno);
5479 *Error = ErrRBLF_SelectFailed;
5482 if (! FD_ISSET(*fd, &rfds) != 0) {
5489 IOBuf->BufSize - (ptr - IOBuf->buf));
5493 *Error = strerror(errno);
5496 else if (rlen == 0){
5497 if ((check == NNN_TERM) &&
5499 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
5501 StrBufPlain(Blob, HKEY("\n000\n"));
5502 StrBufCutRight(Blob, 5);
5503 return Blob->BufUsed;
5505 else if (!IsNonBlock)
5507 else if (nSuccessLess > MaxTries) {
5509 *Error = ErrRBB_too_many_selects;
5513 else if (rlen > 0) {
5517 IOBuf->BufUsed += rlen;
5520 if (nSuccessLess >= MaxTries) {
5522 *Error = ErrRBB_too_many_selects;
5526 if (nRead > nBytes) {
5527 *Pos = IOBuf->buf + nBytes;
5529 Blob->buf[Blob->BufUsed] = '\0';
5530 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5534 return nRead + nAlreadyRead;
5538 * @ingroup StrBuf_IO
5539 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5540 * @param LineBuf your line will be copied here.
5541 * @param Buf BLOB with lines of text...
5542 * @param Ptr moved arround to keep the next-line across several iterations
5543 * has to be &NULL on start; will be &NotNULL on end of buffer
5544 * @returns size of remaining buffer
5546 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5548 const char *aptr, *ptr, *eptr;
5551 if ((Buf == NULL) ||
5552 (*Ptr == StrBufNOTNULL) ||
5554 (LineBuf->buf == NULL))
5556 *Ptr = StrBufNOTNULL;
5560 FlushStrBuf(LineBuf);
5562 ptr = aptr = Buf->buf;
5566 optr = LineBuf->buf;
5567 eptr = Buf->buf + Buf->BufUsed;
5568 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5570 while ((ptr <= eptr) &&
5577 LineBuf->BufUsed = optr - LineBuf->buf;
5578 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5579 optr = LineBuf->buf + LineBuf->BufUsed;
5580 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5584 if ((ptr >= eptr) && (optr > LineBuf->buf))
5586 LineBuf->BufUsed = optr - LineBuf->buf;
5588 if ((ptr <= eptr) && (*ptr == '\r'))
5590 if ((ptr <= eptr) && (*ptr == '\n'))
5597 *Ptr = StrBufNOTNULL;
5600 return Buf->BufUsed - (ptr - Buf->buf);
5605 * @ingroup StrBuf_IO
5606 * @brief removes double slashes from pathnames
5607 * @param Dir directory string to filter
5608 * @param RemoveTrailingSlash allows / disallows trailing slashes
5610 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5616 while (!IsEmptyStr(a)) {
5628 if ((RemoveTrailingSlash) &&
5634 Dir->BufUsed = b - Dir->buf;