8 #include <sys/select.h>
10 #include <sys/types.h>
11 #define SHOW_ME_VAPPEND_PRINTF
13 #include "libcitadel.h"
25 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
26 const Bytef * source, uLong sourceLen, int level);
28 int BaseStrBufSize = 64;
30 const char *StrBufNOTNULL = ((char*) NULL) - 1;
32 const char HexList[256][3] = {
33 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
34 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
35 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
36 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
37 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
38 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
39 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
40 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
41 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
42 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
43 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
44 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
45 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
46 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
47 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
48 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
51 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
52 * StrBuf is a versatile class, aiding the handling of dynamic strings
53 * * reduce de/reallocations
54 * * reduce the need to remeasure it
55 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
56 * * allow asyncroneous IO for line and Blob based operations
57 * * reduce the use of memove in those
58 * * Quick filling in several operations with append functions
62 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
67 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
69 * use these operators to interfere with code demanding char*;
70 * if you need to own the content, smash me. Avoid, since we loose the length information.
74 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
76 * operations to get your Strings into a StrBuf, manipulating them, or appending
79 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
81 * Quick tokenizer; demands of the user to pull its tokens in sequence
85 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
87 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
91 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
93 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
94 * External Cursor to maintain the current read position inside of the buffer
95 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
99 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
105 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
107 * these functions translate the content of a buffer into another representation;
108 * some are combined Fillers and encoders
112 * Private Structure for the Stringbuffer
115 char *buf; /**< the pointer to the dynamic buffer */
116 long BufSize; /**< how many spcae do we optain */
117 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
118 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
120 long nIncreases; /**< for profiling; cound how many times we needed more */
121 char bt [SIZ]; /**< Stacktrace of last increase */
122 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
127 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
128 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
131 #ifdef HAVE_BACKTRACE
132 static void StrBufBacktrace(StrBuf *Buf, int which)
136 void *stack_frames[50];
141 pstart = pch = Buf->bt;
143 pstart = pch = Buf->bt_lastinc;
144 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
145 strings = backtrace_symbols(stack_frames, size);
146 for (i = 0; i < size; i++) {
148 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
150 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
159 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
161 if (hFreeDbglog == -1){
162 pid_t pid = getpid();
164 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
165 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
167 if ((*FreeMe)->nIncreases > 0)
171 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
173 (*FreeMe)->nIncreases,
177 (*FreeMe)->bt_lastinc);
178 n = write(hFreeDbglog, buf, n);
184 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
188 n = write(hFreeDbglog, buf, n);
192 void dbg_IncreaseBuf(StrBuf *IncMe)
195 #ifdef HAVE_BACKTRACE
196 StrBufBacktrace(Buf, 1);
200 void dbg_Init(StrBuf *Buf)
204 Buf->bt_lastinc[0] = '\0';
205 #ifdef HAVE_BACKTRACE
206 StrBufBacktrace(Buf, 0);
212 #define dbg_FreeStrBuf(a, b)
213 #define dbg_IncreaseBuf(a)
220 * @brief swaps the contents of two StrBufs
221 * this is to be used to have cheap switched between a work-buffer and a target buffer
223 * @param B second one
225 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
229 memcpy(&C, A, sizeof(*A));
230 memcpy(A, B, sizeof(*B));
231 memcpy(B, &C, sizeof(C));
236 * @ingroup StrBuf_Cast
237 * @brief Cast operator to Plain String
238 * @note if the buffer is altered by StrBuf operations, this pointer may become
239 * invalid. So don't lean on it after altering the buffer!
240 * Since this operation is considered cheap, rather call it often than risking
241 * your pointer to become invalid!
242 * @param Str the string we want to get the c-string representation for
243 * @returns the Pointer to the Content. Don't mess with it!
245 inline const char *ChrPtr(const StrBuf *Str)
253 * @ingroup StrBuf_Cast
254 * @brief since we know strlen()'s result, provide it here.
255 * @param Str the string to return the length to
256 * @returns contentlength of the buffer
258 inline int StrLength(const StrBuf *Str)
260 return (Str != NULL) ? Str->BufUsed : 0;
264 * @ingroup StrBuf_DeConstructors
265 * @brief local utility function to resize the buffer
266 * @param Buf the buffer whichs storage we should increase
267 * @param KeepOriginal should we copy the original buffer or just start over with a new one
268 * @param DestSize what should fit in after?
270 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
273 size_t NewSize = Buf->BufSize * 2;
279 while ((NewSize <= DestSize) && (NewSize != 0))
285 NewBuf= (char*) malloc(NewSize);
289 if (KeepOriginal && (Buf->BufUsed > 0))
291 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
300 Buf->BufSize = NewSize;
302 dbg_IncreaseBuf(Buf);
308 * @ingroup StrBuf_DeConstructors
309 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
310 * @param Buf Buffer to shrink (has to be empty)
311 * @param ThreshHold if the buffer is bigger then this, its readjusted
312 * @param NewSize if we Shrink it, how big are we going to be afterwards?
314 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
317 (Buf->BufUsed == 0) &&
318 (Buf->BufSize < ThreshHold)) {
320 Buf->buf = (char*) malloc(NewSize);
322 Buf->BufSize = NewSize;
327 * @ingroup StrBuf_DeConstructors
328 * @brief shrink long term buffers to their real size so they don't waste memory
329 * @param Buf buffer to shrink
330 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
331 * @returns physical size of the buffer
333 long StrBufShrinkToFit(StrBuf *Buf, int Force)
338 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
340 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
341 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
342 Buf->BufSize = Buf->BufUsed + 1;
350 * @ingroup StrBuf_DeConstructors
351 * @brief Allocate a new buffer with default buffer size
352 * @returns the new stringbuffer
354 StrBuf* NewStrBuf(void)
358 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
359 NewBuf->buf = (char*) malloc(BaseStrBufSize);
360 NewBuf->buf[0] = '\0';
361 NewBuf->BufSize = BaseStrBufSize;
363 NewBuf->ConstBuf = 0;
371 * @ingroup StrBuf_DeConstructors
372 * @brief Copy Constructor; returns a duplicate of CopyMe
373 * @param CopyMe Buffer to faxmilate
374 * @returns the new stringbuffer
376 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
383 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
384 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
385 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
386 NewBuf->BufUsed = CopyMe->BufUsed;
387 NewBuf->BufSize = CopyMe->BufSize;
388 NewBuf->ConstBuf = 0;
396 * @ingroup StrBuf_DeConstructors
397 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
398 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
399 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
400 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
401 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
402 * @returns the new stringbuffer
404 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
408 if (CreateRelpaceMe == NULL)
413 if (*CreateRelpaceMe != NULL)
414 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
416 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
420 if (CopyFlushMe == NULL)
422 if (*CreateRelpaceMe != NULL)
423 FlushStrBuf(*CreateRelpaceMe);
425 *CreateRelpaceMe = NewStrBuf();
430 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
431 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
432 * be a big IO-Buffer...
434 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
436 if (*CreateRelpaceMe == NULL)
438 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
443 NewBuf = *CreateRelpaceMe;
446 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
450 if (*CreateRelpaceMe == NULL)
452 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
456 NewBuf = *CreateRelpaceMe;
457 SwapBuffers (NewBuf, CopyFlushMe);
460 FlushStrBuf(CopyFlushMe);
465 * @ingroup StrBuf_DeConstructors
466 * @brief create a new Buffer using an existing c-string
467 * this function should also be used if you want to pre-suggest
468 * the buffer size to allocate in conjunction with ptr == NULL
469 * @param ptr the c-string to copy; may be NULL to create a blank instance
470 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
471 * @returns the new stringbuffer
473 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
476 size_t Siz = BaseStrBufSize;
479 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
481 CopySize = strlen((ptr != NULL)?ptr:"");
485 while ((Siz <= CopySize) && (Siz != 0))
493 NewBuf->buf = (char*) malloc(Siz);
494 if (NewBuf->buf == NULL)
499 NewBuf->BufSize = Siz;
501 memcpy(NewBuf->buf, ptr, CopySize);
502 NewBuf->buf[CopySize] = '\0';
503 NewBuf->BufUsed = CopySize;
506 NewBuf->buf[0] = '\0';
509 NewBuf->ConstBuf = 0;
517 * @ingroup StrBuf_DeConstructors
518 * @brief Set an existing buffer from a c-string
519 * @param Buf buffer to load
520 * @param ptr c-string to put into
521 * @param nChars set to -1 if we should work 0-terminated
522 * @returns the new length of the string
524 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
539 CopySize = strlen(ptr);
543 while ((Siz <= CopySize) && (Siz != 0))
551 if (Siz != Buf->BufSize)
552 IncreaseBuf(Buf, 0, Siz);
553 memcpy(Buf->buf, ptr, CopySize);
554 Buf->buf[CopySize] = '\0';
555 Buf->BufUsed = CopySize;
562 * @ingroup StrBuf_DeConstructors
563 * @brief use strbuf as wrapper for a string constant for easy handling
564 * @param StringConstant a string to wrap
565 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
567 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
571 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
572 NewBuf->buf = (char*) StringConstant;
573 NewBuf->BufSize = SizeOfStrConstant;
574 NewBuf->BufUsed = SizeOfStrConstant;
575 NewBuf->ConstBuf = 1;
584 * @ingroup StrBuf_DeConstructors
585 * @brief flush the content of a Buf; keep its struct
586 * @param buf Buffer to flush
588 int FlushStrBuf(StrBuf *buf)
600 * @ingroup StrBuf_DeConstructors
601 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
602 * @param buf Buffer to wipe
604 int FLUSHStrBuf(StrBuf *buf)
610 if (buf->BufUsed > 0) {
611 memset(buf->buf, 0, buf->BufUsed);
618 int hFreeDbglog = -1;
621 * @ingroup StrBuf_DeConstructors
622 * @brief Release a Buffer
623 * Its a double pointer, so it can NULL your pointer
624 * so fancy SIG11 appear instead of random results
625 * @param FreeMe Pointer Pointer to the buffer to free
627 void FreeStrBuf (StrBuf **FreeMe)
632 dbg_FreeStrBuf(FreeMe, 'F');
634 if (!(*FreeMe)->ConstBuf)
635 free((*FreeMe)->buf);
641 * @ingroup StrBuf_DeConstructors
642 * @brief flatten a Buffer to the Char * we return
643 * Its a double pointer, so it can NULL your pointer
644 * so fancy SIG11 appear instead of random results
645 * The Callee then owns the buffer and is responsible for freeing it.
646 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
647 * @returns the pointer of the buffer; Callee owns the memory thereafter.
649 char *SmashStrBuf (StrBuf **SmashMe)
653 if ((SmashMe == NULL) || (*SmashMe == NULL))
656 dbg_FreeStrBuf(SmashMe, 'S');
658 Ret = (*SmashMe)->buf;
665 * @ingroup StrBuf_DeConstructors
666 * @brief Release the buffer
667 * If you want put your StrBuf into a Hash, use this as Destructor.
668 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
670 void HFreeStrBuf (void *VFreeMe)
672 StrBuf *FreeMe = (StrBuf*)VFreeMe;
676 dbg_FreeStrBuf(SmashMe, 'H');
678 if (!FreeMe->ConstBuf)
684 /*******************************************************************************
685 * Simple string transformations *
686 *******************************************************************************/
690 * @brief Wrapper around atol
692 long StrTol(const StrBuf *Buf)
697 return atol(Buf->buf);
704 * @brief Wrapper around atoi
706 int StrToi(const StrBuf *Buf)
710 if (Buf->BufUsed > 0)
711 return atoi(Buf->buf);
718 * @brief Checks to see if the string is a pure number
719 * @param Buf The buffer to inspect
720 * @returns 1 if its a pure number, 0, if not.
722 int StrBufIsNumber(const StrBuf *Buf) {
724 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
727 strtoll(Buf->buf, &pEnd, 10);
728 if (pEnd == Buf->buf)
730 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
732 if (Buf->buf == pEnd)
738 * @ingroup StrBuf_Filler
739 * @brief modifies a Single char of the Buf
740 * You can point to it via char* or a zero-based integer
741 * @param Buf The buffer to manipulate
742 * @param ptr char* to zero; use NULL if unused
743 * @param nThChar zero based pointer into the string; use -1 if unused
744 * @param PeekValue The Character to place into the position
746 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
751 nThChar = ptr - Buf->buf;
752 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
754 Buf->buf[nThChar] = PeekValue;
759 * @ingroup StrBuf_Filler
760 * @brief modifies a range of chars of the Buf
761 * You can point to it via char* or a zero-based integer
762 * @param Buf The buffer to manipulate
763 * @param ptr char* to zero; use NULL if unused
764 * @param nThChar zero based pointer into the string; use -1 if unused
765 * @param nChars how many chars are to be flushed?
766 * @param PookValue The Character to place into that area
768 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
773 nThChar = ptr - Buf->buf;
774 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
776 if (nThChar + nChars > Buf->BufUsed)
777 nChars = Buf->BufUsed - nThChar;
779 memset(Buf->buf + nThChar, PookValue, nChars);
780 /* just to be shure... */
781 Buf->buf[Buf->BufUsed] = 0;
786 * @ingroup StrBuf_Filler
787 * @brief Append a StringBuffer to the buffer
788 * @param Buf Buffer to modify
789 * @param AppendBuf Buffer to copy at the end of our buffer
790 * @param Offset Should we start copying from an offset?
792 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
794 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
797 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
800 AppendBuf->BufUsed + Buf->BufUsed);
802 memcpy(Buf->buf + Buf->BufUsed,
803 AppendBuf->buf + Offset,
804 AppendBuf->BufUsed - Offset);
805 Buf->BufUsed += AppendBuf->BufUsed - Offset;
806 Buf->buf[Buf->BufUsed] = '\0';
811 * @ingroup StrBuf_Filler
812 * @brief Append a C-String to the buffer
813 * @param Buf Buffer to modify
814 * @param AppendBuf Buffer to copy at the end of our buffer
815 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
816 * @param Offset Should we start copying from an offset?
818 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
821 long BufSizeRequired;
823 if ((AppendBuf == NULL) || (Buf == NULL))
827 aps = strlen(AppendBuf + Offset);
829 aps = AppendSize - Offset;
831 BufSizeRequired = Buf->BufUsed + aps + 1;
832 if (Buf->BufSize <= BufSizeRequired)
833 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
835 memcpy(Buf->buf + Buf->BufUsed,
839 Buf->buf[Buf->BufUsed] = '\0';
843 * @ingroup StrBuf_Filler
844 * @brief sprintf like function appending the formated string to the buffer
845 * vsnprintf version to wrap into own calls
846 * @param Buf Buffer to extend by format and Params
847 * @param format printf alike format to add
848 * @param ap va_list containing the items for format
850 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
858 if ((Buf == NULL) || (format == NULL))
861 BufSize = Buf->BufSize;
862 nWritten = Buf->BufSize + 1;
863 Offset = Buf->BufUsed;
864 newused = Offset + nWritten;
866 while (newused >= BufSize) {
868 nWritten = vsnprintf(Buf->buf + Offset,
869 Buf->BufSize - Offset,
872 newused = Offset + nWritten;
873 if (newused >= Buf->BufSize) {
874 if (IncreaseBuf(Buf, 1, newused) == -1)
875 return; /* TODO: error handling? */
876 newused = Buf->BufSize + 1;
879 Buf->BufUsed = Offset + nWritten;
880 BufSize = Buf->BufSize;
887 * @ingroup StrBuf_Filler
888 * @brief sprintf like function appending the formated string to the buffer
889 * @param Buf Buffer to extend by format and Params
890 * @param format printf alike format to add
892 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
900 if ((Buf == NULL) || (format == NULL))
903 BufSize = Buf->BufSize;
904 nWritten = Buf->BufSize + 1;
905 Offset = Buf->BufUsed;
906 newused = Offset + nWritten;
908 while (newused >= BufSize) {
909 va_start(arg_ptr, format);
910 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
911 Buf->BufSize - Buf->BufUsed,
914 newused = Buf->BufUsed + nWritten;
915 if (newused >= Buf->BufSize) {
916 if (IncreaseBuf(Buf, 1, newused) == -1)
917 return; /* TODO: error handling? */
918 newused = Buf->BufSize + 1;
921 Buf->BufUsed += nWritten;
922 BufSize = Buf->BufSize;
929 * @ingroup StrBuf_Filler
930 * @brief sprintf like function putting the formated string into the buffer
931 * @param Buf Buffer to extend by format and Parameters
932 * @param format printf alike format to add
934 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
939 if ((Buf == NULL) || (format == NULL))
942 nWritten = Buf->BufSize + 1;
943 while (nWritten >= Buf->BufSize) {
944 va_start(arg_ptr, format);
945 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
947 if (nWritten >= Buf->BufSize) {
948 if (IncreaseBuf(Buf, 0, 0) == -1)
949 return; /* TODO: error handling? */
950 nWritten = Buf->BufSize + 1;
953 Buf->BufUsed = nWritten ;
958 * @ingroup StrBuf_Filler
959 * @brief Callback for cURL to append the webserver reply to a buffer
960 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
961 * @param size pre-defined by the cURL API; see man 3 curl for mre info
962 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
963 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
965 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
974 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
981 * @brief extracts a substring from Source into dest
982 * @param dest buffer to place substring into
983 * @param Source string to copy substring from
984 * @param Offset chars to skip from start
985 * @param nChars number of chars to copy
986 * @returns the number of chars copied; may be different from nChars due to the size of Source
988 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
991 if (Offset > Source->BufUsed)
997 if (Offset + nChars < Source->BufUsed)
999 if (nChars >= dest->BufSize)
1000 IncreaseBuf(dest, 0, nChars + 1);
1001 memcpy(dest->buf, Source->buf + Offset, nChars);
1002 dest->BufUsed = nChars;
1003 dest->buf[dest->BufUsed] = '\0';
1006 NCharsRemain = Source->BufUsed - Offset;
1007 if (NCharsRemain >= dest->BufSize)
1008 IncreaseBuf(dest, 0, NCharsRemain + 1);
1009 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1010 dest->BufUsed = NCharsRemain;
1011 dest->buf[dest->BufUsed] = '\0';
1012 return NCharsRemain;
1017 * @brief Cut nChars from the start of the string
1018 * @param Buf Buffer to modify
1019 * @param nChars how many chars should be skipped?
1021 void StrBufCutLeft(StrBuf *Buf, int nChars)
1023 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1024 if (nChars >= Buf->BufUsed) {
1028 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1029 Buf->BufUsed -= nChars;
1030 Buf->buf[Buf->BufUsed] = '\0';
1035 * @brief Cut the trailing n Chars from the string
1036 * @param Buf Buffer to modify
1037 * @param nChars how many chars should be trunkated?
1039 void StrBufCutRight(StrBuf *Buf, int nChars)
1041 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1042 if (nChars >= Buf->BufUsed) {
1046 Buf->BufUsed -= nChars;
1047 Buf->buf[Buf->BufUsed] = '\0';
1052 * @brief Cut the string after n Chars
1053 * @param Buf Buffer to modify
1054 * @param AfternChars after how many chars should we trunkate the string?
1055 * @param At if non-null and points inside of our string, cut it there.
1057 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1059 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1061 AfternChars = At - Buf->buf;
1064 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1066 Buf->BufUsed = AfternChars;
1067 Buf->buf[Buf->BufUsed] = '\0';
1073 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1074 * @param Buf the string to modify
1076 void StrBufTrim(StrBuf *Buf)
1079 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1081 while ((Buf->BufUsed > 0) &&
1082 isspace(Buf->buf[Buf->BufUsed - 1]))
1086 Buf->buf[Buf->BufUsed] = '\0';
1088 if (Buf->BufUsed == 0) return;
1090 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1093 if (delta > 0) StrBufCutLeft(Buf, delta);
1097 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1098 * @param Buf the string to modify
1100 void StrBufSpaceToBlank(StrBuf *Buf)
1104 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1107 pche = pch + Buf->BufUsed;
1116 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1124 pLeft = pBuff = Buf->buf;
1125 while (pBuff != NULL) {
1127 pBuff = strchr(pBuff, leftboundary);
1136 pRight = strchr(pBuff, rightboundary);
1138 StrBufCutAt(Buf, 0, pRight);
1140 StrBufCutLeft(Buf, pLeft - Buf->buf);
1145 * @ingroup StrBuf_Filler
1146 * @brief uppercase the contents of a buffer
1147 * @param Buf the buffer to translate
1149 void StrBufUpCase(StrBuf *Buf)
1153 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1156 pche = pch + Buf->BufUsed;
1157 while (pch < pche) {
1158 *pch = toupper(*pch);
1165 * @ingroup StrBuf_Filler
1166 * @brief lowercase the contents of a buffer
1167 * @param Buf the buffer to translate
1169 void StrBufLowerCase(StrBuf *Buf)
1173 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1176 pche = pch + Buf->BufUsed;
1177 while (pch < pche) {
1178 *pch = tolower(*pch);
1184 /*******************************************************************************
1185 * a tokenizer that kills, maims, and destroys *
1186 *******************************************************************************/
1189 * @ingroup StrBuf_Tokenizer
1190 * @brief Replace a token at a given place with a given length by another token with given length
1191 * @param Buf String where to work on
1192 * @param where where inside of the Buf is the search-token
1193 * @param HowLong How long is the token to be replaced
1194 * @param Repl Token to insert at 'where'
1195 * @param ReplLen Length of repl
1196 * @returns -1 if fail else length of resulting Buf
1198 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1199 const char *Repl, long ReplLen)
1202 if ((Buf == NULL) ||
1203 (where > Buf->BufUsed) ||
1204 (where + HowLong > Buf->BufUsed))
1207 if (where + ReplLen - HowLong > Buf->BufSize)
1208 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1211 memmove(Buf->buf + where + ReplLen,
1212 Buf->buf + where + HowLong,
1213 Buf->BufUsed - where - HowLong);
1215 memcpy(Buf->buf + where,
1218 Buf->BufUsed += ReplLen - HowLong;
1220 return Buf->BufUsed;
1224 * @ingroup StrBuf_Tokenizer
1225 * @brief Counts the numbmer of tokens in a buffer
1226 * @param source String to count tokens in
1227 * @param tok Tokenizer char to count
1228 * @returns numbers of tokenizer chars found
1230 int StrBufNum_tokens(const StrBuf *source, char tok)
1234 if ((source == NULL) || (source->BufUsed == 0))
1236 if ((source->BufUsed == 1) && (*source->buf == tok))
1240 pche = pch + source->BufUsed;
1251 * @ingroup StrBuf_Tokenizer
1252 * @brief a string tokenizer
1253 * @param Source StringBuffer to read into
1254 * @param parmnum n'th Parameter to remove
1255 * @param separator tokenizer character
1256 * @returns -1 if not found, else length of token.
1258 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1261 char *d, *s, *end; /* dest, source */
1264 /* Find desired @parameter */
1265 end = Source->buf + Source->BufUsed;
1267 while ((d <= end) &&
1270 /* End of string, bail! */
1275 if (*d == separator) {
1280 if ((d == NULL) || (d >= end))
1281 return 0; /* @Parameter not found */
1283 /* Find next @parameter */
1285 while ((s <= end) &&
1286 (*s && *s != separator))
1290 if (*s == separator)
1294 /* Hack and slash */
1299 memmove(d, s, Source->BufUsed - (s - Source->buf));
1300 Source->BufUsed += ReducedBy;
1301 Source->buf[Source->BufUsed] = '\0';
1303 else if (d == Source->buf) {
1305 Source->BufUsed = 0;
1309 Source->BufUsed += ReducedBy;
1322 * @ingroup StrBuf_Tokenizer
1323 * @brief a string tokenizer
1324 * @param dest Destination StringBuffer
1325 * @param Source StringBuffer to read into
1326 * @param parmnum n'th Parameter to extract
1327 * @param separator tokenizer character
1328 * @returns -1 if not found, else length of token.
1330 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1332 const char *s, *e; //* source * /
1333 int len = 0; //* running total length of extracted string * /
1334 int current_token = 0; //* token currently being processed * /
1337 dest->buf[0] = '\0';
1343 if ((Source == NULL) || (Source->BufUsed ==0)) {
1347 e = s + Source->BufUsed;
1350 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1352 while ((s < e) && !IsEmptyStr(s)) {
1353 if (*s == separator) {
1356 if (len >= dest->BufSize) {
1357 dest->BufUsed = len;
1358 if (IncreaseBuf(dest, 1, -1) < 0) {
1363 if ( (current_token == parmnum) &&
1364 (*s != separator)) {
1365 dest->buf[len] = *s;
1368 else if (current_token > parmnum) {
1374 dest->buf[len] = '\0';
1375 dest->BufUsed = len;
1377 if (current_token < parmnum) {
1378 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1381 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1390 * @ingroup StrBuf_Tokenizer
1391 * @brief a string tokenizer to fetch an integer
1392 * @param Source String containing tokens
1393 * @param parmnum n'th Parameter to extract
1394 * @param separator tokenizer character
1395 * @returns 0 if not found, else integer representation of the token
1397 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1407 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1414 * @ingroup StrBuf_Tokenizer
1415 * @brief a string tokenizer to fetch a long integer
1416 * @param Source String containing tokens
1417 * @param parmnum n'th Parameter to extract
1418 * @param separator tokenizer character
1419 * @returns 0 if not found, else long integer representation of the token
1421 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1431 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1439 * @ingroup StrBuf_Tokenizer
1440 * @brief a string tokenizer to fetch an unsigned long
1441 * @param Source String containing tokens
1442 * @param parmnum n'th Parameter to extract
1443 * @param separator tokenizer character
1444 * @returns 0 if not found, else unsigned long representation of the token
1446 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1457 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1461 return (unsigned long) atol(pnum);
1470 * @ingroup StrBuf_NextTokenizer
1471 * @brief a string tokenizer; Bounds checker
1472 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1473 * @param Source our tokenbuffer
1474 * @param pStart the token iterator pointer to inspect
1475 * @returns whether the revolving pointer is inside of the search range
1477 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1479 if ((Source == NULL) ||
1480 (*pStart == StrBufNOTNULL) ||
1481 (Source->BufUsed == 0))
1485 if (*pStart == NULL)
1489 else if (*pStart > Source->buf + Source->BufUsed)
1493 else if (*pStart <= Source->buf)
1502 * @ingroup StrBuf_NextTokenizer
1503 * @brief a string tokenizer
1504 * @param dest Destination StringBuffer
1505 * @param Source StringBuffer to read into
1506 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1507 * @param separator tokenizer
1508 * @returns -1 if not found, else length of token.
1510 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1512 const char *s; /* source */
1513 const char *EndBuffer; /* end stop of source buffer */
1514 int current_token = 0; /* token currently being processed */
1515 int len = 0; /* running total length of extracted string */
1517 if ((Source == NULL) ||
1518 (Source->BufUsed == 0) )
1520 *pStart = StrBufNOTNULL;
1526 EndBuffer = Source->buf + Source->BufUsed;
1530 dest->buf[0] = '\0';
1535 *pStart = EndBuffer + 1;
1539 if (*pStart == NULL)
1541 *pStart = Source->buf; /* we're starting to examine this buffer. */
1543 else if ((*pStart < Source->buf) ||
1544 (*pStart > EndBuffer ) )
1546 return -1; /* no more tokens to find. */
1550 /* start to find the next token */
1551 while ((s <= EndBuffer) &&
1552 (current_token == 0) )
1554 if (*s == separator)
1556 /* we found the next token */
1560 if (len >= dest->BufSize)
1562 /* our Dest-buffer isn't big enough, increase it. */
1563 dest->BufUsed = len;
1565 if (IncreaseBuf(dest, 1, -1) < 0) {
1566 /* WHUT? no more mem? bail out. */
1573 if ( (current_token == 0 ) && /* are we in our target token? */
1574 (!IsEmptyStr(s) ) &&
1575 (separator != *s) ) /* don't copy the token itself */
1577 dest->buf[len] = *s; /* Copy the payload */
1578 ++len; /* remember the bigger size. */
1584 /* did we reach the end? */
1585 if ((s > EndBuffer)) {
1586 EndBuffer = StrBufNOTNULL;
1587 *pStart = EndBuffer;
1590 *pStart = s; /* remember the position for the next run */
1593 /* sanitize our extracted token */
1594 dest->buf[len] = '\0';
1595 dest->BufUsed = len;
1602 * @ingroup StrBuf_NextTokenizer
1603 * @brief a string tokenizer
1604 * @param Source StringBuffer to read from
1605 * @param pStart pointer to the end of the last token. Feed with NULL.
1606 * @param separator tokenizer character
1607 * @param nTokens number of tokens to fastforward over
1608 * @returns -1 if not found, else length of token.
1610 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1612 const char *s, *EndBuffer; //* source * /
1613 int len = 0; //* running total length of extracted string * /
1614 int current_token = 0; //* token currently being processed * /
1616 if ((Source == NULL) ||
1617 (Source->BufUsed ==0)) {
1621 return Source->BufUsed;
1623 if (*pStart == NULL)
1624 *pStart = Source->buf;
1626 EndBuffer = Source->buf + Source->BufUsed;
1628 if ((*pStart < Source->buf) ||
1629 (*pStart > EndBuffer)) {
1637 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1639 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1640 if (*s == separator) {
1643 if (current_token >= nTokens) {
1655 * @ingroup StrBuf_NextTokenizer
1656 * @brief a string tokenizer to fetch an integer
1657 * @param Source StringBuffer to read from
1658 * @param pStart Cursor on the tokenstring
1659 * @param separator tokenizer character
1660 * @returns 0 if not found, else integer representation of the token
1662 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1672 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1679 * @ingroup StrBuf_NextTokenizer
1680 * @brief a string tokenizer to fetch a long integer
1681 * @param Source StringBuffer to read from
1682 * @param pStart Cursor on the tokenstring
1683 * @param separator tokenizer character
1684 * @returns 0 if not found, else long integer representation of the token
1686 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1696 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1704 * @ingroup StrBuf_NextTokenizer
1705 * @brief a string tokenizer to fetch an unsigned long
1706 * @param Source StringBuffer to read from
1707 * @param pStart Cursor on the tokenstring
1708 * @param separator tokenizer character
1709 * @returns 0 if not found, else unsigned long representation of the token
1711 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1722 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1726 return (unsigned long) atol(pnum);
1736 /*******************************************************************************
1737 * Escape Appending *
1738 *******************************************************************************/
1741 * @ingroup StrBuf_DeEnCoder
1742 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1743 * @param OutBuf the output buffer
1744 * @param In Buffer to encode
1745 * @param PlainIn way in from plain old c strings
1747 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1749 const char *pch, *pche;
1753 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1755 if (PlainIn != NULL) {
1756 len = strlen(PlainIn);
1762 pche = pch + In->BufUsed;
1769 pt = OutBuf->buf + OutBuf->BufUsed;
1770 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1772 while (pch < pche) {
1774 IncreaseBuf(OutBuf, 1, -1);
1775 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1776 pt = OutBuf->buf + OutBuf->BufUsed;
1779 if((*pch >= 'a' && *pch <= 'z') ||
1780 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1781 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1782 (*pch == '!') || (*pch == '_') ||
1783 (*pch == ',') || (*pch == '.'))
1790 *(pt + 1) = HexList[(unsigned char)*pch][0];
1791 *(pt + 2) = HexList[(unsigned char)*pch][1];
1793 OutBuf->BufUsed += 3;
1801 * @ingroup StrBuf_DeEnCoder
1802 * @brief append a string in hex encoding to the buffer
1803 * @param OutBuf the output buffer
1804 * @param In Buffer to encode
1805 * @param PlainIn way in from plain old c strings
1806 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1808 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1810 const unsigned char *pch, *pche;
1814 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1816 if (PlainIn != NULL) {
1818 len = strlen((const char*)PlainIn);
1825 pch = (const unsigned char*)In->buf;
1826 pche = pch + In->BufUsed;
1833 pt = OutBuf->buf + OutBuf->BufUsed;
1834 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1836 while (pch < pche) {
1838 IncreaseBuf(OutBuf, 1, -1);
1839 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1840 pt = OutBuf->buf + OutBuf->BufUsed;
1843 *pt = HexList[*pch][0];
1845 *pt = HexList[*pch][1];
1846 pt ++; pch ++; OutBuf->BufUsed += 2;
1852 * @ingroup StrBuf_DeEnCoder
1853 * @brief append a string in hex encoding to the buffer
1854 * @param OutBuf the output buffer
1855 * @param In Buffer to encode
1856 * @param PlainIn way in from plain old c strings
1858 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1860 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1864 * @ingroup StrBuf_DeEnCoder
1865 * @brief Append a string, escaping characters which have meaning in HTML.
1867 * @param Target target buffer
1868 * @param Source source buffer; set to NULL if you just have a C-String
1869 * @param PlainIn Plain-C string to append; set to NULL if unused
1870 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1871 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1872 * if set to 2, linebreaks are replaced by <br/>
1874 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1876 const char *aptr, *eiptr;
1880 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1883 if (PlainIn != NULL) {
1885 len = strlen(PlainIn);
1890 eiptr = aptr + Source->BufUsed;
1891 len = Source->BufUsed;
1897 bptr = Target->buf + Target->BufUsed;
1898 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1900 while (aptr < eiptr){
1902 IncreaseBuf(Target, 1, -1);
1903 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1904 bptr = Target->buf + Target->BufUsed;
1907 memcpy(bptr, "<", 4);
1909 Target->BufUsed += 4;
1911 else if (*aptr == '>') {
1912 memcpy(bptr, ">", 4);
1914 Target->BufUsed += 4;
1916 else if (*aptr == '&') {
1917 memcpy(bptr, "&", 5);
1919 Target->BufUsed += 5;
1921 else if (*aptr == '"') {
1922 memcpy(bptr, """, 6);
1924 Target->BufUsed += 6;
1926 else if (*aptr == '\'') {
1927 memcpy(bptr, "'", 5);
1929 Target->BufUsed += 5;
1931 else if (*aptr == LB) {
1936 else if (*aptr == RB) {
1941 else if (*aptr == QU) {
1946 else if ((*aptr == 32) && (nbsp == 1)) {
1947 memcpy(bptr, " ", 6);
1949 Target->BufUsed += 6;
1951 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1952 *bptr='\0'; /* nothing */
1954 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1955 memcpy(bptr, "<br/>", 11);
1957 Target->BufUsed += 11;
1961 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1962 *bptr='\0'; /* nothing */
1972 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1974 return Target->BufUsed;
1978 * @ingroup StrBuf_DeEnCoder
1979 * @brief Append a string, escaping characters which have meaning in HTML.
1980 * Converts linebreaks into blanks; escapes single quotes
1981 * @param Target target buffer
1982 * @param Source source buffer; set to NULL if you just have a C-String
1983 * @param PlainIn Plain-C string to append; set to NULL if unused
1985 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
1987 const char *aptr, *eiptr;
1991 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1994 if (PlainIn != NULL) {
1996 len = strlen(PlainIn);
2001 eiptr = aptr + Source->BufUsed;
2002 len = Source->BufUsed;
2008 eptr = Target->buf + Target->BufSize - 8;
2009 tptr = Target->buf + Target->BufUsed;
2011 while (aptr < eiptr){
2013 IncreaseBuf(Target, 1, -1);
2014 eptr = Target->buf + Target->BufSize - 8;
2015 tptr = Target->buf + Target->BufUsed;
2018 if (*aptr == '\n') {
2022 else if (*aptr == '\r') {
2026 else if (*aptr == '\'') {
2032 Target->BufUsed += 5;
2045 * @ingroup StrBuf_DeEnCoder
2046 * @brief Append a string, escaping characters which have meaning in ICAL.
2048 * @param Target target buffer
2049 * @param Source source buffer; set to NULL if you just have a C-String
2050 * @param PlainIn Plain-C string to append; set to NULL if unused
2052 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2054 const char *aptr, *eiptr;
2058 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2061 if (PlainIn != NULL) {
2063 len = strlen(PlainIn);
2068 eiptr = aptr + Source->BufUsed;
2069 len = Source->BufUsed;
2075 eptr = Target->buf + Target->BufSize - 8;
2076 tptr = Target->buf + Target->BufUsed;
2078 while (aptr < eiptr){
2079 if(tptr + 3 >= eptr) {
2080 IncreaseBuf(Target, 1, -1);
2081 eptr = Target->buf + Target->BufSize - 8;
2082 tptr = Target->buf + Target->BufUsed;
2085 if (*aptr == '\n') {
2092 else if (*aptr == '\r') {
2099 else if (*aptr == ',') {
2115 * @ingroup StrBuf_DeEnCoder
2116 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2118 * @param Target target buffer
2119 * @param Source source buffer; set to NULL if you just have a C-String
2120 * @param PlainIn Plain-C string to append; set to NULL if unused
2121 * @returns size of result or -1
2123 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2125 const char *aptr, *eiptr;
2130 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2133 if (PlainIn != NULL) {
2135 len = strlen(PlainIn);
2140 eiptr = aptr + Source->BufUsed;
2141 len = Source->BufUsed;
2147 bptr = Target->buf + Target->BufUsed;
2148 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2150 while (aptr < eiptr){
2152 IncreaseBuf(Target, 1, -1);
2153 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2154 bptr = Target->buf + Target->BufUsed;
2158 memcpy(bptr, HKEY("\\n"));
2160 Target->BufUsed += 2;
2163 memcpy(bptr, HKEY("\\r"));
2165 Target->BufUsed += 2;
2172 Target->BufUsed += 2;
2175 if ((*(aptr + 1) == 'u') &&
2176 isxdigit(*(aptr + 2)) &&
2177 isxdigit(*(aptr + 3)) &&
2178 isxdigit(*(aptr + 4)) &&
2179 isxdigit(*(aptr + 5)))
2180 { /* oh, a unicode escaper. let it pass through. */
2181 memcpy(bptr, aptr, 6);
2184 Target->BufUsed += 6;
2192 Target->BufUsed += 2;
2200 Target->BufUsed += 2;
2207 Target->BufUsed += 2;
2214 Target->BufUsed += 2;
2217 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2218 while (IsUtf8Sequence > 0){
2221 if (--IsUtf8Sequence)
2229 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2231 return Target->BufUsed;
2235 * @ingroup StrBuf_DeEnCoder
2236 * @brief Append a string, escaping characters which have meaning in HTML + json.
2238 * @param Target target buffer
2239 * @param Source source buffer; set to NULL if you just have a C-String
2240 * @param PlainIn Plain-C string to append; set to NULL if unused
2241 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2242 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2243 * if set to 2, linebreaks are replaced by <br/>
2245 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2247 const char *aptr, *eiptr;
2250 int IsUtf8Sequence = 0;
2252 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2255 if (PlainIn != NULL) {
2257 len = strlen(PlainIn);
2262 eiptr = aptr + Source->BufUsed;
2263 len = Source->BufUsed;
2269 bptr = Target->buf + Target->BufUsed;
2270 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2272 while (aptr < eiptr){
2274 IncreaseBuf(Target, 1, -1);
2275 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2276 bptr = Target->buf + Target->BufUsed;
2280 memcpy(bptr, HKEY("<"));
2282 Target->BufUsed += 4;
2285 memcpy(bptr, HKEY(">"));
2287 Target->BufUsed += 4;
2290 memcpy(bptr, HKEY("&"));
2292 Target->BufUsed += 5;
2305 switch (nolinebreaks) {
2307 *bptr='\0'; /* nothing */
2310 memcpy(bptr, HKEY("<br/>"));
2312 Target->BufUsed += 11;
2315 memcpy(bptr, HKEY("\\n"));
2317 Target->BufUsed += 2;
2321 switch (nolinebreaks) {
2324 *bptr='\0'; /* nothing */
2327 memcpy(bptr, HKEY("\\r"));
2329 Target->BufUsed += 2;
2339 Target->BufUsed += 2;
2342 if ((*(aptr + 1) == 'u') &&
2343 isxdigit(*(aptr + 2)) &&
2344 isxdigit(*(aptr + 3)) &&
2345 isxdigit(*(aptr + 4)) &&
2346 isxdigit(*(aptr + 5)))
2347 { /* oh, a unicode escaper. let it pass through. */
2348 memcpy(bptr, aptr, 6);
2351 Target->BufUsed += 6;
2359 Target->BufUsed += 2;
2367 Target->BufUsed += 2;
2374 Target->BufUsed += 2;
2381 Target->BufUsed += 2;
2385 memcpy(bptr, HKEY(" "));
2387 Target->BufUsed += 6;
2391 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2392 while (IsUtf8Sequence > 0){
2395 if (--IsUtf8Sequence)
2403 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2405 return Target->BufUsed;
2409 * @ingroup StrBuf_DeEnCoder
2410 * @brief unhide special chars hidden to the HTML escaper
2411 * @param target buffer to put the unescaped string in
2412 * @param source buffer to unescape
2414 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2420 FlushStrBuf(target);
2422 if (source == NULL ||target == NULL)
2427 len = source->BufUsed;
2428 for (a = 0; a < len; ++a) {
2429 if (target->BufUsed >= target->BufSize)
2430 IncreaseBuf(target, 1, -1);
2432 if (source->buf[a] == '=') {
2433 hex[0] = source->buf[a + 1];
2434 hex[1] = source->buf[a + 2];
2437 sscanf(hex, "%02x", &b);
2438 target->buf[target->BufUsed] = b;
2439 target->buf[++target->BufUsed] = 0;
2443 target->buf[target->BufUsed] = source->buf[a];
2444 target->buf[++target->BufUsed] = 0;
2451 * @ingroup StrBuf_DeEnCoder
2452 * @brief hide special chars from the HTML escapers and friends
2453 * @param target buffer to put the escaped string in
2454 * @param source buffer to escape
2456 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2461 FlushStrBuf(target);
2463 if (source == NULL ||target == NULL)
2468 len = source->BufUsed;
2469 for (i=0; i<len; ++i) {
2470 if (target->BufUsed + 4 >= target->BufSize)
2471 IncreaseBuf(target, 1, -1);
2472 if ( (isalnum(source->buf[i])) ||
2473 (source->buf[i]=='-') ||
2474 (source->buf[i]=='_') ) {
2475 target->buf[target->BufUsed++] = source->buf[i];
2478 sprintf(&target->buf[target->BufUsed],
2480 (0xFF &source->buf[i]));
2481 target->BufUsed += 3;
2484 target->buf[target->BufUsed + 1] = '\0';
2488 /*******************************************************************************
2489 * Quoted Printable de/encoding *
2490 *******************************************************************************/
2493 * @ingroup StrBuf_DeEnCoder
2494 * @brief decode a buffer from base 64 encoding; destroys original
2495 * @param Buf Buffor to transform
2497 int StrBufDecodeBase64(StrBuf *Buf)
2501 if (Buf == NULL) return -1;
2503 xferbuf = (char*) malloc(Buf->BufSize);
2505 siz = CtdlDecodeBase64(xferbuf,
2515 * @ingroup StrBuf_DeEnCoder
2516 * @brief decode a buffer from base 64 encoding; destroys original
2517 * @param Buf Buffor to transform
2519 int StrBufDecodeHex(StrBuf *Buf)
2522 char *pch, *pche, *pchi;
2524 if (Buf == NULL) return -1;
2526 pch = pchi = Buf->buf;
2527 pche = pch + Buf->BufUsed;
2529 while (pchi < pche){
2530 ch = decode_hex(pchi);
2537 Buf->BufUsed = pch - Buf->buf;
2538 return Buf->BufUsed;
2542 * @ingroup StrBuf_DeEnCoder
2543 * @brief replace all chars >0x20 && < 0x7F with Mute
2544 * @param Mute char to put over invalid chars
2545 * @param Buf Buffor to transform
2547 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2551 if (Buf == NULL) return -1;
2552 pch = (unsigned char *)Buf->buf;
2553 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2554 if ((*pch < 0x20) || (*pch > 0x7F))
2558 return Buf->BufUsed;
2563 * @ingroup StrBuf_DeEnCoder
2564 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2565 * @param Buf Buffer to translate
2566 * @param StripBlanks Reduce several blanks to one?
2568 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2577 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2578 Buf->buf[Buf->BufUsed - 1] = '\0';
2583 while (a < Buf->BufUsed) {
2584 if (Buf->buf[a] == '+')
2586 else if (Buf->buf[a] == '%') {
2587 /* don't let % chars through, rather truncate the input. */
2588 if (a + 2 > Buf->BufUsed) {
2593 hex[0] = Buf->buf[a + 1];
2594 hex[1] = Buf->buf[a + 2];
2597 sscanf(hex, "%02x", &b);
2598 Buf->buf[a] = (char) b;
2599 len = Buf->BufUsed - a - 2;
2601 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2613 * @ingroup StrBuf_DeEnCoder
2614 * @brief RFC2047-encode a header field if necessary.
2615 * If no non-ASCII characters are found, the string
2616 * will be copied verbatim without encoding.
2618 * @param target Target buffer.
2619 * @param source Source string to be encoded.
2620 * @returns encoded length; -1 if non success.
2622 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2624 const char headerStr[] = "=?UTF-8?Q?";
2625 int need_to_encode = 0;
2629 if ((source == NULL) ||
2633 while ((i < source->BufUsed) &&
2634 (!IsEmptyStr (&source->buf[i])) &&
2635 (need_to_encode == 0)) {
2636 if (((unsigned char) source->buf[i] < 32) ||
2637 ((unsigned char) source->buf[i] > 126)) {
2643 if (!need_to_encode) {
2644 if (*target == NULL) {
2645 *target = NewStrBufPlain(source->buf, source->BufUsed);
2648 FlushStrBuf(*target);
2649 StrBufAppendBuf(*target, source, 0);
2651 return (*target)->BufUsed;
2653 if (*target == NULL)
2654 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2655 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2656 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2657 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2658 (*target)->BufUsed = sizeof(headerStr) - 1;
2659 for (i=0; (i < source->BufUsed); ++i) {
2660 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2661 IncreaseBuf(*target, 1, 0);
2662 ch = (unsigned char) source->buf[i];
2672 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2673 (*target)->BufUsed += 3;
2677 (*target)->buf[(*target)->BufUsed] = '_';
2679 (*target)->buf[(*target)->BufUsed] = ch;
2680 (*target)->BufUsed++;
2684 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2685 IncreaseBuf(*target, 1, 0);
2687 (*target)->buf[(*target)->BufUsed++] = '?';
2688 (*target)->buf[(*target)->BufUsed++] = '=';
2689 (*target)->buf[(*target)->BufUsed] = '\0';
2690 return (*target)->BufUsed;;
2695 static void AddRecipient(StrBuf *Target,
2697 StrBuf *EmailAddress,
2702 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2703 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2705 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2706 StrBufRFC2047encode(&EncBuf, UserName);
2707 StrBufAppendBuf(Target, EncBuf, 0);
2708 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2709 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2711 if (StrLength(EmailAddress) > 0){
2712 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2713 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2714 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2720 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2721 * \param Recp Source list of email recipients
2722 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2723 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2724 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2725 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2727 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2729 StrBuf *EmailAddress,
2733 const char *pch, *pche;
2734 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2736 if ((Recp == NULL) || (StrLength(Recp) == 0))
2740 pche = pch + StrLength(Recp);
2742 if (!CheckEncode(pch, -1, pche))
2743 return NewStrBufDup(Recp);
2745 Target = NewStrBufPlain(NULL, StrLength(Recp));
2747 while ((pch != NULL) && (pch < pche))
2749 while (isspace(*pch)) pch++;
2750 UserStart = UserEnd = EmailStart = EmailEnd = NULL;
2752 if ((*pch == '"') || (*pch == '\'')) {
2753 UserStart = pch + 1;
2755 UserEnd = strchr(UserStart, *pch);
2756 if (UserEnd == NULL)
2757 break; ///TODO: Userfeedback??
2758 EmailStart = UserEnd + 1;
2759 while (isspace(*EmailStart))
2761 if (UserEnd == UserStart) {
2762 UserStart = UserEnd = NULL;
2765 if (*EmailStart == '<') {
2767 EmailEnd = strchr(EmailStart, '>');
2768 if (EmailEnd == NULL)
2769 EmailEnd = strchr(EmailStart, ',');
2773 EmailEnd = strchr(EmailStart, ',');
2775 if (EmailEnd == NULL)
2782 EmailEnd = strchr(UserStart, ',');
2783 if (EmailEnd == NULL) {
2784 EmailEnd = strchr(pch, '>');
2786 if (EmailEnd != NULL) {
2796 while ((EmailEnd > UserStart) && !gt &&
2797 ((*EmailEnd == ',') ||
2798 (*EmailEnd == '>') ||
2799 (isspace(*EmailEnd))))
2801 if (*EmailEnd == '>')
2806 if (EmailEnd == UserStart)
2810 EmailStart = strchr(UserStart, '<');
2811 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2813 UserEnd = EmailStart;
2815 while ((UserEnd > UserStart) &&
2816 isspace (*(UserEnd - 1)))
2819 if (UserStart >= UserEnd)
2820 UserStart = UserEnd = NULL;
2821 At = strchr(EmailStart, '@');
2823 else { /* this is a local recipient... no domain, just a realname */
2824 EmailStart = UserStart;
2825 At = strchr(EmailStart, '@');
2831 EmailStart = UserStart;
2837 if ((UserStart != NULL) && (UserEnd != NULL))
2838 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2839 else if ((UserStart != NULL) && (UserEnd == NULL))
2840 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2842 FlushStrBuf(UserName);
2844 if ((EmailStart != NULL) && (EmailEnd != NULL))
2845 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2846 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2847 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2849 FlushStrBuf(EmailAddress);
2851 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2856 if ((pch != NULL) && (*pch == ','))
2858 if (pch != NULL) while (isspace(*pch))
2867 * @brief replaces all occurances of 'search' by 'replace'
2868 * @param buf Buffer to modify
2869 * @param search character to search
2870 * @param replace character to replace search by
2872 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2877 for (i=0; i<buf->BufUsed; i++)
2878 if (buf->buf[i] == search)
2879 buf->buf[i] = replace;
2885 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2886 * @param buf Buffer to modify
2888 void StrBufToUnixLF(StrBuf *buf)
2890 char *pche, *pchS, *pchT;
2894 pche = buf->buf + buf->BufUsed;
2895 pchS = pchT = buf->buf;
2901 if (*pchS != '\n') {
2910 buf->BufUsed = pchT - buf->buf;
2914 /*******************************************************************************
2915 * Iconv Wrapper; RFC822 de/encoding *
2916 *******************************************************************************/
2919 * @ingroup StrBuf_DeEnCoder
2920 * @brief Wrapper around iconv_open()
2921 * Our version adds aliases for non-standard Microsoft charsets
2922 * such as 'MS950', aliasing them to names like 'CP950'
2924 * @param tocode Target encoding
2925 * @param fromcode Source encoding
2926 * @param pic anonimized pointer to iconv struct
2928 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2931 iconv_t ic = (iconv_t)(-1) ;
2932 ic = iconv_open(tocode, fromcode);
2933 if (ic == (iconv_t)(-1) ) {
2934 char alias_fromcode[64];
2935 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2936 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2937 alias_fromcode[0] = 'C';
2938 alias_fromcode[1] = 'P';
2939 ic = iconv_open(tocode, alias_fromcode);
2942 *(iconv_t *)pic = ic;
2948 * @ingroup StrBuf_DeEnCoder
2949 * @brief find one chunk of a RFC822 encoded string
2950 * @param Buffer where to search
2951 * @param bptr where to start searching
2952 * @returns found position, NULL if none.
2954 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
2957 /* Find the next ?Q? */
2958 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
2961 end = strchr(bptr + 2, '?');
2966 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2967 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
2968 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
2969 (*(end + 2) == '?')) {
2970 /* skip on to the end of the cluster, the next ?= */
2971 end = strstr(end + 3, "?=");
2974 /* sort of half valid encoding, try to find an end. */
2975 end = strstr(bptr, "?=");
2982 * @ingroup StrBuf_DeEnCoder
2983 * @brief convert one buffer according to the preselected iconv pointer PIC
2984 * @param ConvertBuf buffer we need to translate
2985 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
2986 * @param pic Pointer to the iconv-session Object
2988 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
2994 char *ibuf; /**< Buffer of characters to be converted */
2995 char *obuf; /**< Buffer for converted characters */
2996 size_t ibuflen; /**< Length of input buffer */
2997 size_t obuflen; /**< Length of output buffer */
3000 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3001 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3002 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3004 ic = *(iconv_t*)pic;
3005 ibuf = ConvertBuf->buf;
3006 ibuflen = ConvertBuf->BufUsed;
3008 obuflen = TmpBuf->BufSize;
3010 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3013 if (errno == E2BIG) {
3015 IncreaseBuf(TmpBuf, 0, 0);
3020 else if (errno == EILSEQ){
3021 /* hm, invalid utf8 sequence... what to do now? */
3022 /* An invalid multibyte sequence has been encountered in the input */
3024 else if (errno == EINVAL) {
3025 /* An incomplete multibyte sequence has been encountered in the input. */
3028 FlushStrBuf(TmpBuf);
3031 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3032 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3034 /* little card game: wheres the red lady? */
3035 SwapBuffers(ConvertBuf, TmpBuf);
3036 FlushStrBuf(TmpBuf);
3043 * @ingroup StrBuf_DeEnCoder
3044 * @brief catches one RFC822 encoded segment, and decodes it.
3045 * @param Target buffer to fill with result
3046 * @param DecodeMe buffer with stuff to process
3047 * @param SegmentStart points to our current segment in DecodeMe
3048 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3049 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3050 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3051 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3053 inline static void DecodeSegment(StrBuf *Target,
3054 const StrBuf *DecodeMe,
3055 const char *SegmentStart,
3056 const char *SegmentEnd,
3058 StrBuf *ConvertBuf2,
3059 StrBuf *FoundCharset)
3065 iconv_t ic = (iconv_t)(-1);
3069 /* Now we handle foreign character sets properly encoded
3070 * in RFC2047 format.
3072 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3073 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3074 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3075 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3076 if (FoundCharset != NULL) {
3077 FlushStrBuf(FoundCharset);
3078 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3080 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3081 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3083 *encoding = toupper(*encoding);
3084 if (*encoding == 'B') { /**< base64 */
3085 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3086 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3087 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3089 ConvertBuf->BufUsed);
3091 else if (*encoding == 'Q') { /**< quoted-printable */
3095 while (pos < ConvertBuf->BufUsed)
3097 if (ConvertBuf->buf[pos] == '_')
3098 ConvertBuf->buf[pos] = ' ';
3102 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3103 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3105 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3108 ConvertBuf->BufUsed);
3111 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3114 ctdl_iconv_open("UTF-8", charset, &ic);
3115 if (ic != (iconv_t)(-1) ) {
3117 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3118 StrBufAppendBuf(Target, ConvertBuf2, 0);
3123 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3129 * @ingroup StrBuf_DeEnCoder
3130 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3131 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3132 * @param Target where to put the decoded string to
3133 * @param DecodeMe buffer with encoded string
3134 * @param DefaultCharset if we don't find one, which should we use?
3135 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3136 * put it here for later use where no string might be known.
3138 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3141 StrBuf *ConvertBuf2;
3142 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3143 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3145 StrBuf_RFC822_2_Utf8(Target,
3151 FreeStrBuf(&ConvertBuf);
3152 FreeStrBuf(&ConvertBuf2);
3156 * @ingroup StrBuf_DeEnCoder
3157 * @brief Handle subjects with RFC2047 encoding such as:
3158 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3159 * @param Target where to put the decoded string to
3160 * @param DecodeMe buffer with encoded string
3161 * @param DefaultCharset if we don't find one, which should we use?
3162 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3163 * put it here for later use where no string might be known.
3164 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3165 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3167 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3168 const StrBuf *DecodeMe,
3169 const StrBuf* DefaultCharset,
3170 StrBuf *FoundCharset,
3172 StrBuf *ConvertBuf2)
3174 StrBuf *DecodedInvalidBuf = NULL;
3175 const StrBuf *DecodeMee = DecodeMe;
3176 const char *start, *end, *next, *nextend, *ptr = NULL;
3178 iconv_t ic = (iconv_t)(-1) ;
3183 int illegal_non_rfc2047_encoding = 0;
3185 /* Sometimes, badly formed messages contain strings which were simply
3186 * written out directly in some foreign character set instead of
3187 * using RFC2047 encoding. This is illegal but we will attempt to
3188 * handle it anyway by converting from a user-specified default
3189 * charset to UTF-8 if we see any nonprintable characters.
3192 len = StrLength(DecodeMe);
3193 for (i=0; i<DecodeMe->BufUsed; ++i) {
3194 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3195 illegal_non_rfc2047_encoding = 1;
3200 if ((illegal_non_rfc2047_encoding) &&
3201 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3202 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3205 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3206 if (ic != (iconv_t)(-1) ) {
3207 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3208 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3209 DecodeMee = DecodedInvalidBuf;
3215 /* pre evaluate the first pair */
3216 nextend = end = NULL;
3217 len = StrLength(DecodeMee);
3218 start = strstr(DecodeMee->buf, "=?");
3219 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3221 end = FindNextEnd (DecodeMee, start + 2);
3223 StrBufAppendBuf(Target, DecodeMee, 0);
3224 FreeStrBuf(&DecodedInvalidBuf);
3229 if (start != DecodeMee->buf) {
3232 nFront = start - DecodeMee->buf;
3233 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3237 * Since spammers will go to all sorts of absurd lengths to get their
3238 * messages through, there are LOTS of corrupt headers out there.
3239 * So, prevent a really badly formed RFC2047 header from throwing
3240 * this function into an infinite loop.
3242 while ((start != NULL) &&
3249 DecodeSegment(Target,
3257 next = strstr(end, "=?");
3259 if ((next != NULL) &&
3261 nextend = FindNextEnd(DecodeMee, next);
3262 if (nextend == NULL)
3265 /* did we find two partitions */
3266 if ((next != NULL) &&
3270 while ((ptr < next) &&
3277 * did we find a gab just filled with blanks?
3278 * if not, copy its stuff over.
3282 StrBufAppendBufPlain(Target,
3288 /* our next-pair is our new first pair now. */
3294 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3295 if ((end != NULL) && (end < nextend)) {
3297 while ( (ptr < nextend) &&
3304 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3306 FreeStrBuf(&DecodedInvalidBuf);
3309 /*******************************************************************************
3310 * Manipulating UTF-8 Strings *
3311 *******************************************************************************/
3315 * @brief evaluate the length of an utf8 special character sequence
3316 * @param Char the character to examine
3317 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3319 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3322 unsigned char test = (1<<7);
3324 if ((*CharS & 0xC0) != 0xC0)
3328 ((test & ((unsigned char)*CharS)) != 0))
3333 if ((n > 6) || ((CharE - CharS) < n))
3340 * @brief detect whether this char starts an utf-8 encoded char
3341 * @param Char character to inspect
3342 * @returns yes or no
3344 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3346 /** 11??.???? indicates an UTF8 Sequence. */
3347 return ((Char & 0xC0) == 0xC0);
3352 * @brief measure the number of glyphs in an UTF8 string...
3353 * @param Buf string to measure
3354 * @returns the number of glyphs in Buf
3356 long StrBuf_Utf8StrLen(StrBuf *Buf)
3362 if ((Buf == NULL) || (Buf->BufUsed == 0))
3365 eptr = Buf->buf + Buf->BufUsed;
3366 while ((aptr < eptr) && (*aptr != '\0')) {
3367 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3368 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3369 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3382 * @brief cuts a string after maxlen glyphs
3383 * @param Buf string to cut to maxlen glyphs
3384 * @param maxlen how long may the string become?
3385 * @returns current length of the string
3387 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3393 eptr = Buf->buf + Buf->BufUsed;
3394 while ((aptr < eptr) && (*aptr != '\0')) {
3395 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3396 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3397 while ((*aptr++ != '\0') && (m-- > 0));
3406 Buf->BufUsed = aptr - Buf->buf;
3407 return Buf->BufUsed;
3410 return Buf->BufUsed;
3418 /*******************************************************************************
3420 *******************************************************************************/
3423 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3424 #define OS_CODE 0x03 /*< unix */
3427 * @ingroup StrBuf_DeEnCoder
3428 * @brief uses the same calling syntax as compress2(), but it
3429 * creates a stream compatible with HTTP "Content-encoding: gzip"
3430 * @param dest compressed buffer
3431 * @param destLen length of the compresed data
3432 * @param source source to encode
3433 * @param sourceLen length of source to encode
3434 * @param level compression level
3436 int ZEXPORT compress_gzip(Bytef * dest,
3438 const Bytef * source,
3442 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3444 /* write gzip header */
3445 snprintf((char *) dest, *destLen,
3446 "%c%c%c%c%c%c%c%c%c%c",
3447 gz_magic[0], gz_magic[1], Z_DEFLATED,
3448 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3451 /* normal deflate */
3454 stream.next_in = (Bytef *) source;
3455 stream.avail_in = (uInt) sourceLen;
3456 stream.next_out = dest + 10L; // after header
3457 stream.avail_out = (uInt) * destLen;
3458 if ((uLong) stream.avail_out != *destLen)
3461 stream.zalloc = (alloc_func) 0;
3462 stream.zfree = (free_func) 0;
3463 stream.opaque = (voidpf) 0;
3465 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3466 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3470 err = deflate(&stream, Z_FINISH);
3471 if (err != Z_STREAM_END) {
3472 deflateEnd(&stream);
3473 return err == Z_OK ? Z_BUF_ERROR : err;
3475 *destLen = stream.total_out + 10L;
3477 /* write CRC and Length */
3478 uLong crc = crc32(0L, source, sourceLen);
3480 for (n = 0; n < 4; ++n, ++*destLen) {
3481 dest[*destLen] = (int) (crc & 0xff);
3484 uLong len = stream.total_in;
3485 for (n = 0; n < 4; ++n, ++*destLen) {
3486 dest[*destLen] = (int) (len & 0xff);
3489 err = deflateEnd(&stream);
3496 * @ingroup StrBuf_DeEnCoder
3497 * @brief compress the buffer with gzip
3498 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3499 * @param Buf buffer whose content is to be gzipped
3501 int CompressBuffer(StrBuf *Buf)
3504 char *compressed_data = NULL;
3505 size_t compressed_len, bufsize;
3508 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3509 compressed_data = malloc(compressed_len);
3511 if (compressed_data == NULL)
3513 /* Flush some space after the used payload so valgrind shuts up... */
3514 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3515 Buf->buf[Buf->BufUsed + i++] = '\0';
3516 if (compress_gzip((Bytef *) compressed_data,
3519 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3522 Buf->buf = compressed_data;
3523 Buf->BufUsed = compressed_len;
3524 Buf->BufSize = bufsize;
3525 /* Flush some space after the used payload so valgrind shuts up... */
3527 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3528 Buf->buf[Buf->BufUsed + i++] = '\0';
3531 free(compressed_data);
3533 #endif /* HAVE_ZLIB */
3537 /*******************************************************************************
3538 * File I/O; Callbacks to libevent *
3539 *******************************************************************************/
3541 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3546 if ((FB == NULL) || (FB->Buf == NULL))
3550 * check whether the read pointer is somewhere in a range
3551 * where a cut left is inexpensive
3554 if (FB->ReadWritePointer != NULL)
3558 already_read = FB->ReadWritePointer - FB->Buf->buf;
3559 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3561 if (already_read != 0) {
3564 unread = FB->Buf->BufUsed - already_read;
3566 /* else nothing to compact... */
3568 FB->ReadWritePointer = FB->Buf->buf;
3569 bufremain = FB->Buf->BufSize;
3571 else if ((unread < 64) ||
3572 (bufremain < already_read))
3575 * if its just a tiny bit remaining, or we run out of space...
3578 FB->Buf->BufUsed = unread;
3579 if (unread < already_read)
3580 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3582 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3583 FB->ReadWritePointer = FB->Buf->buf;
3584 bufremain = FB->Buf->BufSize - unread - 1;
3586 else if (bufremain < (FB->Buf->BufSize / 10))
3588 /* get a bigger buffer */
3590 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3592 FB->ReadWritePointer = FB->Buf->buf + unread;
3594 bufremain = FB->Buf->BufSize - unread - 1;
3595 /*TODO: special increase function that won't copy the already read! */
3598 else if (bufremain < 10) {
3599 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3601 FB->ReadWritePointer = FB->Buf->buf;
3603 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3608 FB->ReadWritePointer = FB->Buf->buf;
3609 bufremain = FB->Buf->BufSize - 1;
3612 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3615 FB->Buf->BufUsed += n;
3616 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3621 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3626 if ((FB == NULL) || (FB->Buf == NULL))
3629 if (FB->ReadWritePointer != NULL)
3631 WriteRemain = FB->Buf->BufUsed -
3632 (FB->ReadWritePointer -
3636 FB->ReadWritePointer = FB->Buf->buf;
3637 WriteRemain = FB->Buf->BufUsed;
3640 n = write(fd, FB->ReadWritePointer, WriteRemain);
3642 FB->ReadWritePointer += n;
3644 if (FB->ReadWritePointer ==
3645 FB->Buf->buf + FB->Buf->BufUsed)
3647 FlushStrBuf(FB->Buf);
3648 FB->ReadWritePointer = NULL;
3651 // check whether we've got something to write
3652 // get the maximum chunk plus the pointer we can send
3653 // write whats there
3654 // if not all was sent, remember the send pointer for the next time
3655 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3661 * @ingroup StrBuf_IO
3662 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3663 * @param LineBuf your line will be copied here.
3664 * @param FB BLOB with lines of text...
3665 * @param Ptr moved arround to keep the next-line across several iterations
3666 * has to be &NULL on start; will be &NotNULL on end of buffer
3667 * @returns size of copied buffer
3669 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3671 const char *aptr, *ptr, *eptr;
3674 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3675 FB->ReadWritePointer = StrBufNOTNULL;
3679 FlushStrBuf(LineBuf);
3680 if (FB->ReadWritePointer == NULL)
3681 ptr = aptr = FB->Buf->buf;
3683 ptr = aptr = FB->ReadWritePointer;
3685 optr = LineBuf->buf;
3686 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3687 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3689 while ((ptr <= eptr) &&
3696 LineBuf->BufUsed = optr - LineBuf->buf;
3697 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3698 optr = LineBuf->buf + LineBuf->BufUsed;
3699 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3704 if (optr > LineBuf->buf)
3706 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3707 LineBuf->BufUsed = optr - LineBuf->buf;
3709 if ((FB->ReadWritePointer != NULL) &&
3710 (FB->ReadWritePointer != FB->Buf->buf))
3712 /* Ok, the client application read all the data
3713 it was interested in so far. Since there is more to read,
3714 we now shrink the buffer, and move the rest over.
3716 StrBufCutLeft(FB->Buf,
3717 FB->ReadWritePointer - FB->Buf->buf);
3718 FB->ReadWritePointer = FB->Buf->buf;
3720 return eMustReadMore;
3723 LineBuf->BufUsed = optr - LineBuf->buf;
3725 if ((ptr <= eptr) && (*ptr == '\r'))
3727 if ((ptr <= eptr) && (*ptr == '\n'))
3731 FB->ReadWritePointer = ptr;
3734 FlushStrBuf(FB->Buf);
3735 FB->ReadWritePointer = NULL;
3738 return eReadSuccess;
3742 * @ingroup StrBuf_CHUNKED_IO
3743 * @brief check whether the chunk-buffer has more data waiting or not.
3744 * @param FB Chunk-Buffer to inspect
3746 eReadState StrBufCheckBuffer(IOBuffer *FB)
3750 if (FB->Buf->BufUsed == 0)
3751 return eReadSuccess;
3752 if (FB->ReadWritePointer == NULL)
3753 return eBufferNotEmpty;
3754 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3755 return eBufferNotEmpty;
3756 return eReadSuccess;
3759 /*******************************************************************************
3760 * File I/O; Prefer buffered read since its faster! *
3761 *******************************************************************************/
3764 * @ingroup StrBuf_IO
3765 * @brief Read a line from socket
3766 * flushes and closes the FD on error
3767 * @param buf the buffer to get the input to
3768 * @param fd pointer to the filedescriptor to read
3769 * @param append Append to an existing string or replace?
3770 * @param Error strerror() on error
3771 * @returns numbers of chars read
3773 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
3775 int len, rlen, slen;
3780 slen = len = buf->BufUsed;
3782 rlen = read(*fd, &buf->buf[len], 1);
3784 *Error = strerror(errno);
3791 if (buf->buf[len] == '\n')
3793 if (buf->buf[len] != '\r')
3795 if (len + 2 >= buf->BufSize) {
3797 buf->buf[len+1] = '\0';
3798 IncreaseBuf(buf, 1, -1);
3802 buf->buf[len] = '\0';
3807 * @ingroup StrBuf_BufferedIO
3808 * @brief Read a line from socket
3809 * flushes and closes the FD on error
3810 * @param Line the line to read from the fd / I/O Buffer
3811 * @param buf the buffer to get the input to
3812 * @param fd pointer to the filedescriptor to read
3813 * @param timeout number of successless selects until we bail out
3814 * @param selectresolution how long to wait on each select
3815 * @param Error strerror() on error
3816 * @returns numbers of chars read
3818 int StrBufTCP_read_buffered_line(StrBuf *Line,
3822 int selectresolution,
3826 int nSuccessLess = 0;
3833 if (buf->BufUsed > 0) {
3834 pch = strchr(buf->buf, '\n');
3837 len = pch - buf->buf;
3838 if (len > 0 && (*(pch - 1) == '\r') )
3840 StrBufSub(Line, buf, 0, len - rlen);
3841 StrBufCutLeft(buf, len + 1);
3846 if (buf->BufSize - buf->BufUsed < 10)
3847 IncreaseBuf(buf, 1, -1);
3849 fdflags = fcntl(*fd, F_GETFL);
3850 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3852 while ((nSuccessLess < timeout) && (pch == NULL)) {
3854 tv.tv_sec = selectresolution;
3859 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
3860 *Error = strerror(errno);
3866 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
3871 &buf->buf[buf->BufUsed],
3872 buf->BufSize - buf->BufUsed - 1);
3874 *Error = strerror(errno);
3879 else if (rlen > 0) {
3881 buf->BufUsed += rlen;
3882 buf->buf[buf->BufUsed] = '\0';
3883 if (buf->BufUsed + 10 > buf->BufSize) {
3884 IncreaseBuf(buf, 1, -1);
3886 pch = strchr(buf->buf, '\n');
3893 len = pch - buf->buf;
3894 if (len > 0 && (*(pch - 1) == '\r') )
3896 StrBufSub(Line, buf, 0, len - rlen);
3897 StrBufCutLeft(buf, len + 1);
3904 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
3905 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
3906 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
3908 * @ingroup StrBuf_BufferedIO
3909 * @brief Read a line from socket
3910 * flushes and closes the FD on error
3911 * @param Line where to append our Line read from the fd / I/O Buffer;
3912 * @param IOBuf the buffer to get the input to; lifetime pair to FD
3913 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
3914 * @param fd pointer to the filedescriptor to read
3915 * @param timeout number of successless selects until we bail out
3916 * @param selectresolution how long to wait on each select
3917 * @param Error strerror() on error
3918 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
3920 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
3925 int selectresolution,
3928 const char *pche = NULL;
3929 const char *pos = NULL;
3931 int len, rlen, retlen;
3932 int nSuccessLess = 0;
3934 const char *pch = NULL;
3940 if ((Line == NULL) ||
3947 *Error = ErrRBLF_PreConditionFailed;
3952 if ((IOBuf->BufUsed > 0) &&
3954 (pos < IOBuf->buf + IOBuf->BufUsed))
3958 pche = IOBuf->buf + IOBuf->BufUsed;
3962 while ((pch < pche) && (*pch != '\n'))
3964 if (Line->BufUsed + 10 > Line->BufSize)
3967 apos = pcht - Line->buf;
3969 IncreaseBuf(Line, 1, -1);
3970 pcht = Line->buf + apos;
3978 if (len > 0 && (*(pch - 1) == '\r') )
3987 if ((pch >= pche) || (*pch == '\0'))
3995 if ((pch != NULL) &&
3998 if (pch + 1 >= pche) {
4011 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4013 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4014 IncreaseBuf(IOBuf, 1, -1);
4016 fdflags = fcntl(*fd, F_GETFL);
4017 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4020 while ((nSuccessLess < timeout) &&
4030 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4031 *Error = strerror(errno);
4035 *Error = ErrRBLF_SelectFailed;
4038 if (! FD_ISSET(*fd, &rfds) != 0) {
4044 &IOBuf->buf[IOBuf->BufUsed],
4045 IOBuf->BufSize - IOBuf->BufUsed - 1);
4047 *Error = strerror(errno);
4052 else if (rlen > 0) {
4054 pLF = IOBuf->buf + IOBuf->BufUsed;
4055 IOBuf->BufUsed += rlen;
4056 IOBuf->buf[IOBuf->BufUsed] = '\0';
4058 pche = IOBuf->buf + IOBuf->BufUsed;
4060 while ((pLF < pche) && (*pLF != '\n'))
4062 if ((pLF >= pche) || (*pLF == '\0'))
4065 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4069 if (pLF != NULL) apos = pLF - IOBuf->buf;
4070 IncreaseBuf(IOBuf, 1, -1);
4071 if (pLF != NULL) pLF = IOBuf->buf + apos;
4081 if (len > 0 && (*(pLF - 1) == '\r') )
4083 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4084 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4090 return retlen + len;
4092 *Error = ErrRBLF_NotEnoughSentFromServer;
4097 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4099 * @ingroup StrBuf_IO
4100 * @brief Input binary data from socket
4101 * flushes and closes the FD on error
4102 * @param Buf the buffer to get the input to
4103 * @param fd pointer to the filedescriptor to read
4104 * @param append Append to an existing string or replace?
4105 * @param nBytes the maximal number of bytes to read
4106 * @param Error strerror() on error
4107 * @returns numbers of chars read
4109 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4120 if ((Buf == NULL) || (*fd == -1))
4122 *Error = ErrRBLF_BLOBPreConditionFailed;
4127 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4128 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4130 ptr = Buf->buf + Buf->BufUsed;
4132 fdflags = fcntl(*fd, F_GETFL);
4133 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4135 while ((nRead < nBytes) &&
4145 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4146 *Error = strerror(errno);
4150 *Error = ErrRBLF_SelectFailed;
4153 if (! FD_ISSET(*fd, &rfds) != 0) {
4159 if ((rlen = read(*fd,
4161 nBytes - nRead)) == -1) {
4164 *Error = strerror(errno);
4169 Buf->BufUsed += rlen;
4171 Buf->buf[Buf->BufUsed] = '\0';
4175 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4176 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4178 * @ingroup StrBuf_BufferedIO
4179 * @brief Input binary data from socket
4180 * flushes and closes the FD on error
4181 * @param Blob put binary thing here
4182 * @param IOBuf the buffer to get the input to
4183 * @param Pos offset inside of IOBuf
4184 * @param fd pointer to the filedescriptor to read
4185 * @param append Append to an existing string or replace?
4186 * @param nBytes the maximal number of bytes to read
4187 * @param check whether we should search for '000\n' terminators in case of timeouts
4188 * @param Error strerror() on error
4189 * @returns numbers of chars read
4191 int StrBufReadBLOBBuffered(StrBuf *Blob,
4205 int nAlreadyRead = 0;
4210 int nSuccessLess = 0;
4213 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4217 *Error = ErrRBB_BLOBFPreConditionFailed;
4223 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4224 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4229 len = pos - IOBuf->buf;
4230 rlen = IOBuf->BufUsed - len;
4233 if ((IOBuf->BufUsed > 0) &&
4235 (pos < IOBuf->buf + IOBuf->BufUsed))
4237 if (rlen < nBytes) {
4238 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4239 Blob->BufUsed += rlen;
4240 Blob->buf[Blob->BufUsed] = '\0';
4241 nAlreadyRead = nRead = rlen;
4244 if (rlen >= nBytes) {
4245 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4246 Blob->BufUsed += nBytes;
4247 Blob->buf[Blob->BufUsed] = '\0';
4248 if (rlen == nBytes) {
4260 if (IOBuf->BufSize < nBytes - nRead)
4261 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4264 len = Blob->BufUsed;
4266 fdflags = fcntl(*fd, F_GETFL);
4267 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4275 while ((nSuccessLess < MaxTries) &&
4285 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4286 *Error = strerror(errno);
4290 *Error = ErrRBLF_SelectFailed;
4293 if (! FD_ISSET(*fd, &rfds) != 0) {
4300 IOBuf->BufSize - (ptr - IOBuf->buf));
4304 *Error = strerror(errno);
4307 else if (rlen == 0){
4308 if ((check == NNN_TERM) &&
4310 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4312 StrBufPlain(Blob, HKEY("\n000\n"));
4313 StrBufCutRight(Blob, 5);
4314 return Blob->BufUsed;
4316 else if (!IsNonBlock)
4318 else if (nSuccessLess > MaxTries) {
4320 *Error = ErrRBB_too_many_selects;
4324 else if (rlen > 0) {
4328 IOBuf->BufUsed += rlen;
4331 if (nSuccessLess >= MaxTries) {
4333 *Error = ErrRBB_too_many_selects;
4337 if (nRead > nBytes) {
4338 *Pos = IOBuf->buf + nBytes;
4340 Blob->buf[Blob->BufUsed] = '\0';
4341 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4345 return nRead + nAlreadyRead;
4349 * @ingroup StrBuf_IO
4350 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4351 * @param LineBuf your line will be copied here.
4352 * @param Buf BLOB with lines of text...
4353 * @param Ptr moved arround to keep the next-line across several iterations
4354 * has to be &NULL on start; will be &NotNULL on end of buffer
4355 * @returns size of remaining buffer
4357 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4359 const char *aptr, *ptr, *eptr;
4362 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4363 *Ptr = StrBufNOTNULL;
4367 FlushStrBuf(LineBuf);
4369 ptr = aptr = Buf->buf;
4373 optr = LineBuf->buf;
4374 eptr = Buf->buf + Buf->BufUsed;
4375 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4377 while ((ptr <= eptr) &&
4384 LineBuf->BufUsed = optr - LineBuf->buf;
4385 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4386 optr = LineBuf->buf + LineBuf->BufUsed;
4387 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4391 if ((ptr >= eptr) && (optr > LineBuf->buf))
4393 LineBuf->BufUsed = optr - LineBuf->buf;
4395 if ((ptr <= eptr) && (*ptr == '\r'))
4397 if ((ptr <= eptr) && (*ptr == '\n'))
4404 *Ptr = StrBufNOTNULL;
4407 return Buf->BufUsed - (ptr - Buf->buf);
4412 * @ingroup StrBuf_IO
4413 * @brief removes double slashes from pathnames
4414 * @param Dir directory string to filter
4415 * @param RemoveTrailingSlash allows / disallows trailing slashes
4417 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4423 while (!IsEmptyStr(a)) {
4435 if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
4440 Dir->BufUsed = b - Dir->buf;