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"
49 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
50 const Bytef * source, uLong sourceLen, int level);
52 int BaseStrBufSize = 64;
55 const char *StrBufNOTNULL = ((char*) NULL) - 1;
57 const char HexList[256][3] = {
58 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
59 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
60 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
61 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
62 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
63 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
64 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
65 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
66 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
67 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
68 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
69 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
70 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
71 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
72 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
73 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
76 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
77 * StrBuf is a versatile class, aiding the handling of dynamic strings
78 * * reduce de/reallocations
79 * * reduce the need to remeasure it
80 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
81 * * allow asyncroneous IO for line and Blob based operations
82 * * reduce the use of memove in those
83 * * Quick filling in several operations with append functions
87 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
92 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
94 * use these operators to interfere with code demanding char*;
95 * if you need to own the content, smash me. Avoid, since we loose the length information.
99 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
101 * operations to get your Strings into a StrBuf, manipulating them, or appending
104 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
106 * Quick tokenizer; demands of the user to pull its tokens in sequence
110 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
112 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
116 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
118 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
119 * External Cursor to maintain the current read position inside of the buffer
120 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
124 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
130 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
132 * these functions translate the content of a buffer into another representation;
133 * some are combined Fillers and encoders
137 * Private Structure for the Stringbuffer
140 char *buf; /**< the pointer to the dynamic buffer */
141 long BufSize; /**< how many spcae do we optain */
142 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
143 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
145 long nIncreases; /**< for profiling; cound how many times we needed more */
146 char bt [SIZ]; /**< Stacktrace of last increase */
147 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
152 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
153 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
156 #ifdef HAVE_BACKTRACE
157 static void StrBufBacktrace(StrBuf *Buf, int which)
161 void *stack_frames[50];
166 pstart = pch = Buf->bt;
168 pstart = pch = Buf->bt_lastinc;
169 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
170 strings = backtrace_symbols(stack_frames, size);
171 for (i = 0; i < size; i++) {
173 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
175 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
184 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
186 if (hFreeDbglog == -1){
187 pid_t pid = getpid();
189 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
190 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
192 if ((*FreeMe)->nIncreases > 0)
196 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
198 (*FreeMe)->nIncreases,
202 (*FreeMe)->bt_lastinc);
203 n = write(hFreeDbglog, buf, n);
209 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
213 n = write(hFreeDbglog, buf, n);
217 void dbg_IncreaseBuf(StrBuf *IncMe)
220 #ifdef HAVE_BACKTRACE
221 StrBufBacktrace(Buf, 1);
225 void dbg_Init(StrBuf *Buf)
229 Buf->bt_lastinc[0] = '\0';
230 #ifdef HAVE_BACKTRACE
231 StrBufBacktrace(Buf, 0);
237 #define dbg_FreeStrBuf(a, b)
238 #define dbg_IncreaseBuf(a)
245 * @brief swaps the contents of two StrBufs
246 * this is to be used to have cheap switched between a work-buffer and a target buffer
248 * @param B second one
250 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
254 memcpy(&C, A, sizeof(*A));
255 memcpy(A, B, sizeof(*B));
256 memcpy(B, &C, sizeof(C));
261 * @ingroup StrBuf_Cast
262 * @brief Cast operator to Plain String
263 * @note if the buffer is altered by StrBuf operations, this pointer may become
264 * invalid. So don't lean on it after altering the buffer!
265 * Since this operation is considered cheap, rather call it often than risking
266 * your pointer to become invalid!
267 * @param Str the string we want to get the c-string representation for
268 * @returns the Pointer to the Content. Don't mess with it!
270 inline const char *ChrPtr(const StrBuf *Str)
278 * @ingroup StrBuf_Cast
279 * @brief since we know strlen()'s result, provide it here.
280 * @param Str the string to return the length to
281 * @returns contentlength of the buffer
283 inline int StrLength(const StrBuf *Str)
285 return (Str != NULL) ? Str->BufUsed : 0;
289 * @ingroup StrBuf_DeConstructors
290 * @brief local utility function to resize the buffer
291 * @param Buf the buffer whichs storage we should increase
292 * @param KeepOriginal should we copy the original buffer or just start over with a new one
293 * @param DestSize what should fit in after?
295 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
298 size_t NewSize = Buf->BufSize * 2;
304 while ((NewSize <= DestSize) && (NewSize != 0))
310 NewBuf= (char*) malloc(NewSize);
314 if (KeepOriginal && (Buf->BufUsed > 0))
316 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
325 Buf->BufSize = NewSize;
327 dbg_IncreaseBuf(Buf);
333 * @ingroup StrBuf_DeConstructors
334 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
335 * @param Buf Buffer to shrink (has to be empty)
336 * @param ThreshHold if the buffer is bigger then this, its readjusted
337 * @param NewSize if we Shrink it, how big are we going to be afterwards?
339 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
342 (Buf->BufUsed == 0) &&
343 (Buf->BufSize < ThreshHold)) {
345 Buf->buf = (char*) malloc(NewSize);
347 Buf->BufSize = NewSize;
352 * @ingroup StrBuf_DeConstructors
353 * @brief shrink long term buffers to their real size so they don't waste memory
354 * @param Buf buffer to shrink
355 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
356 * @returns physical size of the buffer
358 long StrBufShrinkToFit(StrBuf *Buf, int Force)
363 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
367 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
371 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
372 Buf->BufSize = Buf->BufUsed + 1;
380 * @ingroup StrBuf_DeConstructors
381 * @brief Allocate a new buffer with default buffer size
382 * @returns the new stringbuffer
384 StrBuf* NewStrBuf(void)
388 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
392 NewBuf->buf = (char*) malloc(BaseStrBufSize);
393 if (NewBuf->buf == NULL)
398 NewBuf->buf[0] = '\0';
399 NewBuf->BufSize = BaseStrBufSize;
401 NewBuf->ConstBuf = 0;
409 * @ingroup StrBuf_DeConstructors
410 * @brief Copy Constructor; returns a duplicate of CopyMe
411 * @param CopyMe Buffer to faxmilate
412 * @returns the new stringbuffer
414 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
421 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
425 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
426 if (NewBuf->buf == NULL)
432 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
433 NewBuf->BufUsed = CopyMe->BufUsed;
434 NewBuf->BufSize = CopyMe->BufSize;
435 NewBuf->ConstBuf = 0;
443 * @ingroup StrBuf_DeConstructors
444 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
445 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
446 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
447 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
448 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
449 * @returns the new stringbuffer
451 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
455 if (CreateRelpaceMe == NULL)
460 if (*CreateRelpaceMe != NULL)
461 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
463 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
467 if (CopyFlushMe == NULL)
469 if (*CreateRelpaceMe != NULL)
470 FlushStrBuf(*CreateRelpaceMe);
472 *CreateRelpaceMe = NewStrBuf();
477 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
478 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
479 * be a big IO-Buffer...
481 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
483 if (*CreateRelpaceMe == NULL)
485 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
490 NewBuf = *CreateRelpaceMe;
493 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
497 if (*CreateRelpaceMe == NULL)
499 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
503 NewBuf = *CreateRelpaceMe;
504 SwapBuffers (NewBuf, CopyFlushMe);
507 FlushStrBuf(CopyFlushMe);
512 * @ingroup StrBuf_DeConstructors
513 * @brief create a new Buffer using an existing c-string
514 * this function should also be used if you want to pre-suggest
515 * the buffer size to allocate in conjunction with ptr == NULL
516 * @param ptr the c-string to copy; may be NULL to create a blank instance
517 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
518 * @returns the new stringbuffer
520 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
523 size_t Siz = BaseStrBufSize;
526 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
531 CopySize = strlen((ptr != NULL)?ptr:"");
535 while ((Siz <= CopySize) && (Siz != 0))
544 NewBuf->buf = (char*) malloc(Siz);
545 if (NewBuf->buf == NULL)
550 NewBuf->BufSize = Siz;
552 memcpy(NewBuf->buf, ptr, CopySize);
553 NewBuf->buf[CopySize] = '\0';
554 NewBuf->BufUsed = CopySize;
557 NewBuf->buf[0] = '\0';
560 NewBuf->ConstBuf = 0;
568 * @ingroup StrBuf_DeConstructors
569 * @brief Set an existing buffer from a c-string
570 * @param Buf buffer to load
571 * @param ptr c-string to put into
572 * @param nChars set to -1 if we should work 0-terminated
573 * @returns the new length of the string
575 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
590 CopySize = strlen(ptr);
594 while ((Siz <= CopySize) && (Siz != 0))
602 if (Siz != Buf->BufSize)
603 IncreaseBuf(Buf, 0, Siz);
604 memcpy(Buf->buf, ptr, CopySize);
605 Buf->buf[CopySize] = '\0';
606 Buf->BufUsed = CopySize;
613 * @ingroup StrBuf_DeConstructors
614 * @brief use strbuf as wrapper for a string constant for easy handling
615 * @param StringConstant a string to wrap
616 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
618 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
622 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
625 NewBuf->buf = (char*) StringConstant;
626 NewBuf->BufSize = SizeOfStrConstant;
627 NewBuf->BufUsed = SizeOfStrConstant;
628 NewBuf->ConstBuf = 1;
637 * @ingroup StrBuf_DeConstructors
638 * @brief flush the content of a Buf; keep its struct
639 * @param buf Buffer to flush
641 int FlushStrBuf(StrBuf *buf)
643 if ((buf == NULL) || (buf->buf == NULL))
653 * @ingroup StrBuf_DeConstructors
654 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
655 * @param buf Buffer to wipe
657 int FLUSHStrBuf(StrBuf *buf)
663 if (buf->BufUsed > 0) {
664 memset(buf->buf, 0, buf->BufUsed);
671 int hFreeDbglog = -1;
674 * @ingroup StrBuf_DeConstructors
675 * @brief Release a Buffer
676 * Its a double pointer, so it can NULL your pointer
677 * so fancy SIG11 appear instead of random results
678 * @param FreeMe Pointer Pointer to the buffer to free
680 void FreeStrBuf (StrBuf **FreeMe)
685 dbg_FreeStrBuf(FreeMe, 'F');
687 if (!(*FreeMe)->ConstBuf)
688 free((*FreeMe)->buf);
694 * @ingroup StrBuf_DeConstructors
695 * @brief flatten a Buffer to the Char * we return
696 * Its a double pointer, so it can NULL your pointer
697 * so fancy SIG11 appear instead of random results
698 * The Callee then owns the buffer and is responsible for freeing it.
699 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
700 * @returns the pointer of the buffer; Callee owns the memory thereafter.
702 char *SmashStrBuf (StrBuf **SmashMe)
706 if ((SmashMe == NULL) || (*SmashMe == NULL))
709 dbg_FreeStrBuf(SmashMe, 'S');
711 Ret = (*SmashMe)->buf;
718 * @ingroup StrBuf_DeConstructors
719 * @brief Release the buffer
720 * If you want put your StrBuf into a Hash, use this as Destructor.
721 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
723 void HFreeStrBuf (void *VFreeMe)
725 StrBuf *FreeMe = (StrBuf*)VFreeMe;
729 dbg_FreeStrBuf(SmashMe, 'H');
731 if (!FreeMe->ConstBuf)
737 /*******************************************************************************
738 * Simple string transformations *
739 *******************************************************************************/
743 * @brief Wrapper around atol
745 long StrTol(const StrBuf *Buf)
750 return atol(Buf->buf);
757 * @brief Wrapper around atoi
759 int StrToi(const StrBuf *Buf)
763 if (Buf->BufUsed > 0)
764 return atoi(Buf->buf);
771 * @brief Checks to see if the string is a pure number
772 * @param Buf The buffer to inspect
773 * @returns 1 if its a pure number, 0, if not.
775 int StrBufIsNumber(const StrBuf *Buf) {
777 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
780 strtoll(Buf->buf, &pEnd, 10);
781 if (pEnd == Buf->buf)
783 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
785 if (Buf->buf == pEnd)
791 * @ingroup StrBuf_Filler
792 * @brief modifies a Single char of the Buf
793 * You can point to it via char* or a zero-based integer
794 * @param Buf The buffer to manipulate
795 * @param ptr char* to zero; use NULL if unused
796 * @param nThChar zero based pointer into the string; use -1 if unused
797 * @param PeekValue The Character to place into the position
799 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
804 nThChar = ptr - Buf->buf;
805 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
807 Buf->buf[nThChar] = PeekValue;
812 * @ingroup StrBuf_Filler
813 * @brief modifies a range of chars of the Buf
814 * You can point to it via char* or a zero-based integer
815 * @param Buf The buffer to manipulate
816 * @param ptr char* to zero; use NULL if unused
817 * @param nThChar zero based pointer into the string; use -1 if unused
818 * @param nChars how many chars are to be flushed?
819 * @param PookValue The Character to place into that area
821 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
826 nThChar = ptr - Buf->buf;
827 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
829 if (nThChar + nChars > Buf->BufUsed)
830 nChars = Buf->BufUsed - nThChar;
832 memset(Buf->buf + nThChar, PookValue, nChars);
833 /* just to be shure... */
834 Buf->buf[Buf->BufUsed] = 0;
839 * @ingroup StrBuf_Filler
840 * @brief Append a StringBuffer to the buffer
841 * @param Buf Buffer to modify
842 * @param AppendBuf Buffer to copy at the end of our buffer
843 * @param Offset Should we start copying from an offset?
845 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
847 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
848 (Buf == NULL) || (Buf->buf == NULL))
851 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
854 AppendBuf->BufUsed + Buf->BufUsed);
856 memcpy(Buf->buf + Buf->BufUsed,
857 AppendBuf->buf + Offset,
858 AppendBuf->BufUsed - Offset);
859 Buf->BufUsed += AppendBuf->BufUsed - Offset;
860 Buf->buf[Buf->BufUsed] = '\0';
865 * @ingroup StrBuf_Filler
866 * @brief Append a C-String to the buffer
867 * @param Buf Buffer to modify
868 * @param AppendBuf Buffer to copy at the end of our buffer
869 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
870 * @param Offset Should we start copying from an offset?
872 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
875 long BufSizeRequired;
877 if ((AppendBuf == NULL) || (Buf == NULL))
881 aps = strlen(AppendBuf + Offset);
883 aps = AppendSize - Offset;
885 BufSizeRequired = Buf->BufUsed + aps + 1;
886 if (Buf->BufSize <= BufSizeRequired)
887 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
889 memcpy(Buf->buf + Buf->BufUsed,
893 Buf->buf[Buf->BufUsed] = '\0';
897 * @ingroup StrBuf_Filler
898 * @brief sprintf like function appending the formated string to the buffer
899 * vsnprintf version to wrap into own calls
900 * @param Buf Buffer to extend by format and Params
901 * @param format printf alike format to add
902 * @param ap va_list containing the items for format
904 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
912 if ((Buf == NULL) || (format == NULL))
915 BufSize = Buf->BufSize;
916 nWritten = Buf->BufSize + 1;
917 Offset = Buf->BufUsed;
918 newused = Offset + nWritten;
920 while (newused >= BufSize) {
922 nWritten = vsnprintf(Buf->buf + Offset,
923 Buf->BufSize - Offset,
926 newused = Offset + nWritten;
927 if (newused >= Buf->BufSize) {
928 if (IncreaseBuf(Buf, 1, newused) == -1)
929 return; /* TODO: error handling? */
930 newused = Buf->BufSize + 1;
933 Buf->BufUsed = Offset + nWritten;
934 BufSize = Buf->BufSize;
941 * @ingroup StrBuf_Filler
942 * @brief sprintf like function appending the formated string to the buffer
943 * @param Buf Buffer to extend by format and Params
944 * @param format printf alike format to add
946 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
954 if ((Buf == NULL) || (format == NULL))
957 BufSize = Buf->BufSize;
958 nWritten = Buf->BufSize + 1;
959 Offset = Buf->BufUsed;
960 newused = Offset + nWritten;
962 while (newused >= BufSize) {
963 va_start(arg_ptr, format);
964 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
965 Buf->BufSize - Buf->BufUsed,
968 newused = Buf->BufUsed + nWritten;
969 if (newused >= Buf->BufSize) {
970 if (IncreaseBuf(Buf, 1, newused) == -1)
971 return; /* TODO: error handling? */
972 newused = Buf->BufSize + 1;
975 Buf->BufUsed += nWritten;
976 BufSize = Buf->BufSize;
983 * @ingroup StrBuf_Filler
984 * @brief sprintf like function putting the formated string into the buffer
985 * @param Buf Buffer to extend by format and Parameters
986 * @param format printf alike format to add
988 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
993 if ((Buf == NULL) || (format == NULL))
996 nWritten = Buf->BufSize + 1;
997 while (nWritten >= Buf->BufSize) {
998 va_start(arg_ptr, format);
999 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1001 if (nWritten >= Buf->BufSize) {
1002 if (IncreaseBuf(Buf, 0, 0) == -1)
1003 return; /* TODO: error handling? */
1004 nWritten = Buf->BufSize + 1;
1007 Buf->BufUsed = nWritten ;
1012 * @ingroup StrBuf_Filler
1013 * @brief Callback for cURL to append the webserver reply to a buffer
1014 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1015 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1016 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1017 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1019 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1028 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1029 return size * nmemb;
1035 * @brief extracts a substring from Source into dest
1036 * @param dest buffer to place substring into
1037 * @param Source string to copy substring from
1038 * @param Offset chars to skip from start
1039 * @param nChars number of chars to copy
1040 * @returns the number of chars copied; may be different from nChars due to the size of Source
1042 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1044 size_t NCharsRemain;
1045 if (Offset > Source->BufUsed)
1051 if (Offset + nChars < Source->BufUsed)
1053 if ((nChars >= dest->BufSize) &&
1054 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1056 memcpy(dest->buf, Source->buf + Offset, nChars);
1057 dest->BufUsed = nChars;
1058 dest->buf[dest->BufUsed] = '\0';
1061 NCharsRemain = Source->BufUsed - Offset;
1062 if ((NCharsRemain >= dest->BufSize) &&
1063 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1065 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1066 dest->BufUsed = NCharsRemain;
1067 dest->buf[dest->BufUsed] = '\0';
1068 return NCharsRemain;
1073 * @brief Cut nChars from the start of the string
1074 * @param Buf Buffer to modify
1075 * @param nChars how many chars should be skipped?
1077 void StrBufCutLeft(StrBuf *Buf, int nChars)
1079 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1080 if (nChars >= Buf->BufUsed) {
1084 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1085 Buf->BufUsed -= nChars;
1086 Buf->buf[Buf->BufUsed] = '\0';
1091 * @brief Cut the trailing n Chars from the string
1092 * @param Buf Buffer to modify
1093 * @param nChars how many chars should be trunkated?
1095 void StrBufCutRight(StrBuf *Buf, int nChars)
1097 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1100 if (nChars >= Buf->BufUsed) {
1104 Buf->BufUsed -= nChars;
1105 Buf->buf[Buf->BufUsed] = '\0';
1110 * @brief Cut the string after n Chars
1111 * @param Buf Buffer to modify
1112 * @param AfternChars after how many chars should we trunkate the string?
1113 * @param At if non-null and points inside of our string, cut it there.
1115 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1117 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1119 AfternChars = At - Buf->buf;
1122 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1124 Buf->BufUsed = AfternChars;
1125 Buf->buf[Buf->BufUsed] = '\0';
1131 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1132 * @param Buf the string to modify
1134 void StrBufTrim(StrBuf *Buf)
1137 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1139 while ((Buf->BufUsed > 0) &&
1140 isspace(Buf->buf[Buf->BufUsed - 1]))
1144 Buf->buf[Buf->BufUsed] = '\0';
1146 if (Buf->BufUsed == 0) return;
1148 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1151 if (delta > 0) StrBufCutLeft(Buf, delta);
1155 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1156 * @param Buf the string to modify
1158 void StrBufSpaceToBlank(StrBuf *Buf)
1162 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1165 pche = pch + Buf->BufUsed;
1174 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1179 if ((Buf == NULL) || (Buf->buf == NULL)) {
1183 pRight = strchr(Buf->buf, rightboundary);
1184 if (pRight != NULL) {
1185 StrBufCutAt(Buf, 0, pRight);
1188 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1189 if (pLeft != NULL) {
1190 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1196 * @ingroup StrBuf_Filler
1197 * @brief uppercase the contents of a buffer
1198 * @param Buf the buffer to translate
1200 void StrBufUpCase(StrBuf *Buf)
1204 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1207 pche = pch + Buf->BufUsed;
1208 while (pch < pche) {
1209 *pch = toupper(*pch);
1216 * @ingroup StrBuf_Filler
1217 * @brief lowercase the contents of a buffer
1218 * @param Buf the buffer to translate
1220 void StrBufLowerCase(StrBuf *Buf)
1224 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1227 pche = pch + Buf->BufUsed;
1228 while (pch < pche) {
1229 *pch = tolower(*pch);
1235 /*******************************************************************************
1236 * a tokenizer that kills, maims, and destroys *
1237 *******************************************************************************/
1240 * @ingroup StrBuf_Tokenizer
1241 * @brief Replace a token at a given place with a given length by another token with given length
1242 * @param Buf String where to work on
1243 * @param where where inside of the Buf is the search-token
1244 * @param HowLong How long is the token to be replaced
1245 * @param Repl Token to insert at 'where'
1246 * @param ReplLen Length of repl
1247 * @returns -1 if fail else length of resulting Buf
1249 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1250 const char *Repl, long ReplLen)
1253 if ((Buf == NULL) ||
1254 (where > Buf->BufUsed) ||
1255 (where + HowLong > Buf->BufUsed))
1258 if (where + ReplLen - HowLong > Buf->BufSize)
1259 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1262 memmove(Buf->buf + where + ReplLen,
1263 Buf->buf + where + HowLong,
1264 Buf->BufUsed - where - HowLong);
1266 memcpy(Buf->buf + where,
1269 Buf->BufUsed += ReplLen - HowLong;
1271 return Buf->BufUsed;
1275 * @ingroup StrBuf_Tokenizer
1276 * @brief Counts the numbmer of tokens in a buffer
1277 * @param source String to count tokens in
1278 * @param tok Tokenizer char to count
1279 * @returns numbers of tokenizer chars found
1281 int StrBufNum_tokens(const StrBuf *source, char tok)
1285 if ((source == NULL) || (source->BufUsed == 0))
1287 if ((source->BufUsed == 1) && (*source->buf == tok))
1291 pche = pch + source->BufUsed;
1302 * @ingroup StrBuf_Tokenizer
1303 * @brief a string tokenizer
1304 * @param Source StringBuffer to read into
1305 * @param parmnum n'th Parameter to remove
1306 * @param separator tokenizer character
1307 * @returns -1 if not found, else length of token.
1309 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1312 char *d, *s, *end; /* dest, source */
1315 /* Find desired @parameter */
1316 end = Source->buf + Source->BufUsed;
1318 while ((d <= end) &&
1321 /* End of string, bail! */
1326 if (*d == separator) {
1331 if ((d == NULL) || (d >= end))
1332 return 0; /* @Parameter not found */
1334 /* Find next @parameter */
1336 while ((s <= end) &&
1337 (*s && *s != separator))
1341 if (*s == separator)
1345 /* Hack and slash */
1350 memmove(d, s, Source->BufUsed - (s - Source->buf));
1351 Source->BufUsed += ReducedBy;
1352 Source->buf[Source->BufUsed] = '\0';
1354 else if (d == Source->buf) {
1356 Source->BufUsed = 0;
1360 Source->BufUsed += ReducedBy;
1371 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1373 const StrBuf Temp = {
1386 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1390 * @ingroup StrBuf_Tokenizer
1391 * @brief a string tokenizer
1392 * @param dest Destination StringBuffer
1393 * @param Source StringBuffer to read into
1394 * @param parmnum n'th Parameter to extract
1395 * @param separator tokenizer character
1396 * @returns -1 if not found, else length of token.
1398 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1400 const char *s, *e; //* source * /
1401 int len = 0; //* running total length of extracted string * /
1402 int current_token = 0; //* token currently being processed * /
1405 dest->buf[0] = '\0';
1411 if ((Source == NULL) || (Source->BufUsed ==0)) {
1415 e = s + Source->BufUsed;
1418 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1420 while ((s < e) && !IsEmptyStr(s)) {
1421 if (*s == separator) {
1424 if (len >= dest->BufSize) {
1425 dest->BufUsed = len;
1426 if (IncreaseBuf(dest, 1, -1) < 0) {
1431 if ( (current_token == parmnum) &&
1432 (*s != separator)) {
1433 dest->buf[len] = *s;
1436 else if (current_token > parmnum) {
1442 dest->buf[len] = '\0';
1443 dest->BufUsed = len;
1445 if (current_token < parmnum) {
1446 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1449 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1458 * @ingroup StrBuf_Tokenizer
1459 * @brief a string tokenizer to fetch an integer
1460 * @param Source String containing tokens
1461 * @param parmnum n'th Parameter to extract
1462 * @param separator tokenizer character
1463 * @returns 0 if not found, else integer representation of the token
1465 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1475 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1482 * @ingroup StrBuf_Tokenizer
1483 * @brief a string tokenizer to fetch a long integer
1484 * @param Source String containing tokens
1485 * @param parmnum n'th Parameter to extract
1486 * @param separator tokenizer character
1487 * @returns 0 if not found, else long integer representation of the token
1489 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1499 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1507 * @ingroup StrBuf_Tokenizer
1508 * @brief a string tokenizer to fetch an unsigned long
1509 * @param Source String containing tokens
1510 * @param parmnum n'th Parameter to extract
1511 * @param separator tokenizer character
1512 * @returns 0 if not found, else unsigned long representation of the token
1514 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1525 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1529 return (unsigned long) atol(pnum);
1538 * @ingroup StrBuf_NextTokenizer
1539 * @brief a string tokenizer; Bounds checker
1540 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1541 * @param Source our tokenbuffer
1542 * @param pStart the token iterator pointer to inspect
1543 * @returns whether the revolving pointer is inside of the search range
1545 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1547 if ((Source == NULL) ||
1548 (*pStart == StrBufNOTNULL) ||
1549 (Source->BufUsed == 0))
1553 if (*pStart == NULL)
1557 else if (*pStart > Source->buf + Source->BufUsed)
1561 else if (*pStart <= Source->buf)
1570 * @ingroup StrBuf_NextTokenizer
1571 * @brief a string tokenizer
1572 * @param dest Destination StringBuffer
1573 * @param Source StringBuffer to read into
1574 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1575 * @param separator tokenizer
1576 * @returns -1 if not found, else length of token.
1578 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1580 const char *s; /* source */
1581 const char *EndBuffer; /* end stop of source buffer */
1582 int current_token = 0; /* token currently being processed */
1583 int len = 0; /* running total length of extracted string */
1585 if ((Source == NULL) ||
1586 (Source->BufUsed == 0) )
1588 *pStart = StrBufNOTNULL;
1594 EndBuffer = Source->buf + Source->BufUsed;
1598 dest->buf[0] = '\0';
1603 *pStart = EndBuffer + 1;
1607 if (*pStart == NULL)
1609 *pStart = Source->buf; /* we're starting to examine this buffer. */
1611 else if ((*pStart < Source->buf) ||
1612 (*pStart > EndBuffer ) )
1614 return -1; /* no more tokens to find. */
1618 /* start to find the next token */
1619 while ((s <= EndBuffer) &&
1620 (current_token == 0) )
1622 if (*s == separator)
1624 /* we found the next token */
1628 if (len >= dest->BufSize)
1630 /* our Dest-buffer isn't big enough, increase it. */
1631 dest->BufUsed = len;
1633 if (IncreaseBuf(dest, 1, -1) < 0) {
1634 /* WHUT? no more mem? bail out. */
1641 if ( (current_token == 0 ) && /* are we in our target token? */
1642 (!IsEmptyStr(s) ) &&
1643 (separator != *s) ) /* don't copy the token itself */
1645 dest->buf[len] = *s; /* Copy the payload */
1646 ++len; /* remember the bigger size. */
1652 /* did we reach the end? */
1653 if ((s > EndBuffer)) {
1654 EndBuffer = StrBufNOTNULL;
1655 *pStart = EndBuffer;
1658 *pStart = s; /* remember the position for the next run */
1661 /* sanitize our extracted token */
1662 dest->buf[len] = '\0';
1663 dest->BufUsed = len;
1670 * @ingroup StrBuf_NextTokenizer
1671 * @brief a string tokenizer
1672 * @param Source StringBuffer to read from
1673 * @param pStart pointer to the end of the last token. Feed with NULL.
1674 * @param separator tokenizer character
1675 * @param nTokens number of tokens to fastforward over
1676 * @returns -1 if not found, else length of token.
1678 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1680 const char *s, *EndBuffer; //* source * /
1681 int len = 0; //* running total length of extracted string * /
1682 int current_token = 0; //* token currently being processed * /
1684 if ((Source == NULL) ||
1685 (Source->BufUsed ==0)) {
1689 return Source->BufUsed;
1691 if (*pStart == NULL)
1692 *pStart = Source->buf;
1694 EndBuffer = Source->buf + Source->BufUsed;
1696 if ((*pStart < Source->buf) ||
1697 (*pStart > EndBuffer)) {
1705 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1707 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1708 if (*s == separator) {
1711 if (current_token >= nTokens) {
1723 * @ingroup StrBuf_NextTokenizer
1724 * @brief a string tokenizer to fetch an integer
1725 * @param Source StringBuffer to read from
1726 * @param pStart Cursor on the tokenstring
1727 * @param separator tokenizer character
1728 * @returns 0 if not found, else integer representation of the token
1730 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1740 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1747 * @ingroup StrBuf_NextTokenizer
1748 * @brief a string tokenizer to fetch a long integer
1749 * @param Source StringBuffer to read from
1750 * @param pStart Cursor on the tokenstring
1751 * @param separator tokenizer character
1752 * @returns 0 if not found, else long integer representation of the token
1754 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1764 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1772 * @ingroup StrBuf_NextTokenizer
1773 * @brief a string tokenizer to fetch an unsigned long
1774 * @param Source StringBuffer to read from
1775 * @param pStart Cursor on the tokenstring
1776 * @param separator tokenizer character
1777 * @returns 0 if not found, else unsigned long representation of the token
1779 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1790 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1794 return (unsigned long) atol(pnum);
1804 /*******************************************************************************
1805 * Escape Appending *
1806 *******************************************************************************/
1809 * @ingroup StrBuf_DeEnCoder
1810 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1811 * @param OutBuf the output buffer
1812 * @param In Buffer to encode
1813 * @param PlainIn way in from plain old c strings
1815 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1817 const char *pch, *pche;
1821 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1823 if (PlainIn != NULL) {
1824 len = strlen(PlainIn);
1830 pche = pch + In->BufUsed;
1837 pt = OutBuf->buf + OutBuf->BufUsed;
1838 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1840 while (pch < pche) {
1842 IncreaseBuf(OutBuf, 1, -1);
1843 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1844 pt = OutBuf->buf + OutBuf->BufUsed;
1847 if((*pch >= 'a' && *pch <= 'z') ||
1848 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1849 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1850 (*pch == '!') || (*pch == '_') ||
1851 (*pch == ',') || (*pch == '.'))
1858 *(pt + 1) = HexList[(unsigned char)*pch][0];
1859 *(pt + 2) = HexList[(unsigned char)*pch][1];
1861 OutBuf->BufUsed += 3;
1869 * @ingroup StrBuf_DeEnCoder
1870 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1871 * @param OutBuf the output buffer
1872 * @param In Buffer to encode
1873 * @param PlainIn way in from plain old c strings
1875 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1877 const char *pch, *pche;
1881 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1883 if (PlainIn != NULL) {
1884 len = strlen(PlainIn);
1890 pche = pch + In->BufUsed;
1897 pt = OutBuf->buf + OutBuf->BufUsed;
1898 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1900 while (pch < pche) {
1902 IncreaseBuf(OutBuf, 1, -1);
1903 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1904 pt = OutBuf->buf + OutBuf->BufUsed;
1907 if((*pch >= 'a' && *pch <= 'z') ||
1908 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1909 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1910 (*pch == '!') || (*pch == '_') ||
1911 (*pch == ',') || (*pch == '.'))
1918 *(pt + 1) = HexList[(unsigned char)*pch][0];
1919 *(pt + 2) = HexList[(unsigned char)*pch][1];
1921 OutBuf->BufUsed += 3;
1929 * @ingroup StrBuf_DeEnCoder
1930 * @brief append a string in hex encoding to the buffer
1931 * @param OutBuf the output buffer
1932 * @param In Buffer to encode
1933 * @param PlainIn way in from plain old c strings
1934 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1936 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1938 const unsigned char *pch, *pche;
1942 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1944 if (PlainIn != NULL) {
1946 len = strlen((const char*)PlainIn);
1953 pch = (const unsigned char*)In->buf;
1954 pche = pch + In->BufUsed;
1961 pt = OutBuf->buf + OutBuf->BufUsed;
1962 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1964 while (pch < pche) {
1966 IncreaseBuf(OutBuf, 1, -1);
1967 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1968 pt = OutBuf->buf + OutBuf->BufUsed;
1971 *pt = HexList[*pch][0];
1973 *pt = HexList[*pch][1];
1974 pt ++; pch ++; OutBuf->BufUsed += 2;
1980 * @ingroup StrBuf_DeEnCoder
1981 * @brief append a string in hex encoding to the buffer
1982 * @param OutBuf the output buffer
1983 * @param In Buffer to encode
1984 * @param PlainIn way in from plain old c strings
1986 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1988 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1992 * @ingroup StrBuf_DeEnCoder
1993 * @brief Append a string, escaping characters which have meaning in HTML.
1995 * @param Target target buffer
1996 * @param Source source buffer; set to NULL if you just have a C-String
1997 * @param PlainIn Plain-C string to append; set to NULL if unused
1998 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1999 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2000 * if set to 2, linebreaks are replaced by <br/>
2002 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2004 const char *aptr, *eiptr;
2008 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2011 if (PlainIn != NULL) {
2013 len = strlen(PlainIn);
2018 eiptr = aptr + Source->BufUsed;
2019 len = Source->BufUsed;
2025 bptr = Target->buf + Target->BufUsed;
2026 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2028 while (aptr < eiptr){
2030 IncreaseBuf(Target, 1, -1);
2031 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2032 bptr = Target->buf + Target->BufUsed;
2035 memcpy(bptr, "<", 4);
2037 Target->BufUsed += 4;
2039 else if (*aptr == '>') {
2040 memcpy(bptr, ">", 4);
2042 Target->BufUsed += 4;
2044 else if (*aptr == '&') {
2045 memcpy(bptr, "&", 5);
2047 Target->BufUsed += 5;
2049 else if (*aptr == '"') {
2050 memcpy(bptr, """, 6);
2052 Target->BufUsed += 6;
2054 else if (*aptr == '\'') {
2055 memcpy(bptr, "'", 5);
2057 Target->BufUsed += 5;
2059 else if (*aptr == LB) {
2064 else if (*aptr == RB) {
2069 else if (*aptr == QU) {
2074 else if ((*aptr == 32) && (nbsp == 1)) {
2075 memcpy(bptr, " ", 6);
2077 Target->BufUsed += 6;
2079 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2080 *bptr='\0'; /* nothing */
2082 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2083 memcpy(bptr, "<br/>", 11);
2085 Target->BufUsed += 11;
2089 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2090 *bptr='\0'; /* nothing */
2100 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2102 return Target->BufUsed;
2106 * @ingroup StrBuf_DeEnCoder
2107 * @brief Append a string, escaping characters which have meaning in HTML.
2108 * Converts linebreaks into blanks; escapes single quotes
2109 * @param Target target buffer
2110 * @param Source source buffer; set to NULL if you just have a C-String
2111 * @param PlainIn Plain-C string to append; set to NULL if unused
2113 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2115 const char *aptr, *eiptr;
2119 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2122 if (PlainIn != NULL) {
2124 len = strlen(PlainIn);
2129 eiptr = aptr + Source->BufUsed;
2130 len = Source->BufUsed;
2136 eptr = Target->buf + Target->BufSize - 8;
2137 tptr = Target->buf + Target->BufUsed;
2139 while (aptr < eiptr){
2141 IncreaseBuf(Target, 1, -1);
2142 eptr = Target->buf + Target->BufSize - 8;
2143 tptr = Target->buf + Target->BufUsed;
2146 if (*aptr == '\n') {
2150 else if (*aptr == '\r') {
2154 else if (*aptr == '\'') {
2160 Target->BufUsed += 5;
2173 * @ingroup StrBuf_DeEnCoder
2174 * @brief Append a string, escaping characters which have meaning in ICAL.
2176 * @param Target target buffer
2177 * @param Source source buffer; set to NULL if you just have a C-String
2178 * @param PlainIn Plain-C string to append; set to NULL if unused
2180 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2182 const char *aptr, *eiptr;
2186 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2189 if (PlainIn != NULL) {
2191 len = strlen(PlainIn);
2196 eiptr = aptr + Source->BufUsed;
2197 len = Source->BufUsed;
2203 eptr = Target->buf + Target->BufSize - 8;
2204 tptr = Target->buf + Target->BufUsed;
2206 while (aptr < eiptr){
2207 if(tptr + 3 >= eptr) {
2208 IncreaseBuf(Target, 1, -1);
2209 eptr = Target->buf + Target->BufSize - 8;
2210 tptr = Target->buf + Target->BufUsed;
2213 if (*aptr == '\n') {
2220 else if (*aptr == '\r') {
2227 else if (*aptr == ',') {
2243 * @ingroup StrBuf_DeEnCoder
2244 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2246 * @param Target target buffer
2247 * @param Source source buffer; set to NULL if you just have a C-String
2248 * @param PlainIn Plain-C string to append; set to NULL if unused
2249 * @returns size of result or -1
2251 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2253 const char *aptr, *eiptr;
2258 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2261 if (PlainIn != NULL) {
2263 len = strlen(PlainIn);
2268 eiptr = aptr + Source->BufUsed;
2269 len = Source->BufUsed;
2275 bptr = Target->buf + Target->BufUsed;
2276 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2278 while (aptr < eiptr){
2280 IncreaseBuf(Target, 1, -1);
2281 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2282 bptr = Target->buf + Target->BufUsed;
2286 memcpy(bptr, HKEY("\\n"));
2288 Target->BufUsed += 2;
2291 memcpy(bptr, HKEY("\\r"));
2293 Target->BufUsed += 2;
2300 Target->BufUsed += 2;
2303 if ((*(aptr + 1) == 'u') &&
2304 isxdigit(*(aptr + 2)) &&
2305 isxdigit(*(aptr + 3)) &&
2306 isxdigit(*(aptr + 4)) &&
2307 isxdigit(*(aptr + 5)))
2308 { /* oh, a unicode escaper. let it pass through. */
2309 memcpy(bptr, aptr, 6);
2312 Target->BufUsed += 6;
2320 Target->BufUsed += 2;
2328 Target->BufUsed += 2;
2335 Target->BufUsed += 2;
2342 Target->BufUsed += 2;
2345 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2346 while (IsUtf8Sequence > 0){
2349 if (--IsUtf8Sequence)
2357 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2359 return Target->BufUsed;
2363 * @ingroup StrBuf_DeEnCoder
2364 * @brief Append a string, escaping characters which have meaning in HTML + json.
2366 * @param Target target buffer
2367 * @param Source source buffer; set to NULL if you just have a C-String
2368 * @param PlainIn Plain-C string to append; set to NULL if unused
2369 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2370 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2371 * if set to 2, linebreaks are replaced by <br/>
2373 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2375 const char *aptr, *eiptr;
2378 int IsUtf8Sequence = 0;
2380 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2383 if (PlainIn != NULL) {
2385 len = strlen(PlainIn);
2390 eiptr = aptr + Source->BufUsed;
2391 len = Source->BufUsed;
2397 bptr = Target->buf + Target->BufUsed;
2398 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2400 while (aptr < eiptr){
2402 IncreaseBuf(Target, 1, -1);
2403 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2404 bptr = Target->buf + Target->BufUsed;
2408 memcpy(bptr, HKEY("<"));
2410 Target->BufUsed += 4;
2413 memcpy(bptr, HKEY(">"));
2415 Target->BufUsed += 4;
2418 memcpy(bptr, HKEY("&"));
2420 Target->BufUsed += 5;
2433 switch (nolinebreaks) {
2435 *bptr='\0'; /* nothing */
2438 memcpy(bptr, HKEY("<br/>"));
2440 Target->BufUsed += 11;
2443 memcpy(bptr, HKEY("\\n"));
2445 Target->BufUsed += 2;
2449 switch (nolinebreaks) {
2452 *bptr='\0'; /* nothing */
2455 memcpy(bptr, HKEY("\\r"));
2457 Target->BufUsed += 2;
2467 Target->BufUsed += 2;
2470 if ((*(aptr + 1) == 'u') &&
2471 isxdigit(*(aptr + 2)) &&
2472 isxdigit(*(aptr + 3)) &&
2473 isxdigit(*(aptr + 4)) &&
2474 isxdigit(*(aptr + 5)))
2475 { /* oh, a unicode escaper. let it pass through. */
2476 memcpy(bptr, aptr, 6);
2479 Target->BufUsed += 6;
2487 Target->BufUsed += 2;
2495 Target->BufUsed += 2;
2502 Target->BufUsed += 2;
2509 Target->BufUsed += 2;
2513 memcpy(bptr, HKEY(" "));
2515 Target->BufUsed += 6;
2519 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2520 while (IsUtf8Sequence > 0){
2523 if (--IsUtf8Sequence)
2531 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2533 return Target->BufUsed;
2538 * @ingroup StrBuf_DeEnCoder
2539 * @brief replace all non-Ascii characters by another
2540 * @param Buf buffer to inspect
2541 * @param repl charater to stamp over non ascii chars
2543 void StrBufAsciify(StrBuf *Buf, const char repl)
2547 for (offset = 0; offset < Buf->BufUsed; offset ++)
2548 if (!isascii(Buf->buf[offset]))
2549 Buf->buf[offset] = repl;
2554 * @ingroup StrBuf_DeEnCoder
2555 * @brief unhide special chars hidden to the HTML escaper
2556 * @param target buffer to put the unescaped string in
2557 * @param source buffer to unescape
2559 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2564 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2570 FlushStrBuf(target);
2572 len = source->BufUsed;
2573 for (a = 0; a < len; ++a) {
2574 if (target->BufUsed >= target->BufSize)
2575 IncreaseBuf(target, 1, -1);
2577 if (source->buf[a] == '=') {
2578 hex[0] = source->buf[a + 1];
2579 hex[1] = source->buf[a + 2];
2582 sscanf(hex, "%02x", &b);
2583 target->buf[target->BufUsed] = b;
2584 target->buf[++target->BufUsed] = 0;
2588 target->buf[target->BufUsed] = source->buf[a];
2589 target->buf[++target->BufUsed] = 0;
2596 * @ingroup StrBuf_DeEnCoder
2597 * @brief hide special chars from the HTML escapers and friends
2598 * @param target buffer to put the escaped string in
2599 * @param source buffer to escape
2601 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2606 FlushStrBuf(target);
2608 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2613 len = source->BufUsed;
2614 for (i=0; i<len; ++i) {
2615 if (target->BufUsed + 4 >= target->BufSize)
2616 IncreaseBuf(target, 1, -1);
2617 if ( (isalnum(source->buf[i])) ||
2618 (source->buf[i]=='-') ||
2619 (source->buf[i]=='_') ) {
2620 target->buf[target->BufUsed++] = source->buf[i];
2623 sprintf(&target->buf[target->BufUsed],
2625 (0xFF &source->buf[i]));
2626 target->BufUsed += 3;
2629 target->buf[target->BufUsed + 1] = '\0';
2633 /*******************************************************************************
2634 * Quoted Printable de/encoding *
2635 *******************************************************************************/
2638 * @ingroup StrBuf_DeEnCoder
2639 * @brief decode a buffer from base 64 encoding; destroys original
2640 * @param Buf Buffor to transform
2642 int StrBufDecodeBase64(StrBuf *Buf)
2650 xferbuf = (char*) malloc(Buf->BufSize);
2651 if (xferbuf == NULL)
2655 siz = CtdlDecodeBase64(xferbuf,
2665 * @ingroup StrBuf_DeEnCoder
2666 * @brief decode a buffer from base 64 encoding; destroys original
2667 * @param Buf Buffor to transform
2669 int StrBufDecodeHex(StrBuf *Buf)
2672 char *pch, *pche, *pchi;
2674 if (Buf == NULL) return -1;
2676 pch = pchi = Buf->buf;
2677 pche = pch + Buf->BufUsed;
2679 while (pchi < pche){
2680 ch = decode_hex(pchi);
2687 Buf->BufUsed = pch - Buf->buf;
2688 return Buf->BufUsed;
2692 * @ingroup StrBuf_DeEnCoder
2693 * @brief replace all chars >0x20 && < 0x7F with Mute
2694 * @param Mute char to put over invalid chars
2695 * @param Buf Buffor to transform
2697 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2701 if (Buf == NULL) return -1;
2702 pch = (unsigned char *)Buf->buf;
2703 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2704 if ((*pch < 0x20) || (*pch > 0x7F))
2708 return Buf->BufUsed;
2713 * @ingroup StrBuf_DeEnCoder
2714 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2715 * @param Buf Buffer to translate
2716 * @param StripBlanks Reduce several blanks to one?
2718 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2727 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2728 Buf->buf[Buf->BufUsed - 1] = '\0';
2733 while (a < Buf->BufUsed) {
2734 if (Buf->buf[a] == '+')
2736 else if (Buf->buf[a] == '%') {
2737 /* don't let % chars through, rather truncate the input. */
2738 if (a + 2 > Buf->BufUsed) {
2743 hex[0] = Buf->buf[a + 1];
2744 hex[1] = Buf->buf[a + 2];
2747 sscanf(hex, "%02x", &b);
2748 Buf->buf[a] = (char) b;
2749 len = Buf->BufUsed - a - 2;
2751 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2763 * @ingroup StrBuf_DeEnCoder
2764 * @brief RFC2047-encode a header field if necessary.
2765 * If no non-ASCII characters are found, the string
2766 * will be copied verbatim without encoding.
2768 * @param target Target buffer.
2769 * @param source Source string to be encoded.
2770 * @returns encoded length; -1 if non success.
2772 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2774 const char headerStr[] = "=?UTF-8?Q?";
2775 int need_to_encode = 0;
2779 if ((source == NULL) ||
2783 while ((i < source->BufUsed) &&
2784 (!IsEmptyStr (&source->buf[i])) &&
2785 (need_to_encode == 0)) {
2786 if (((unsigned char) source->buf[i] < 32) ||
2787 ((unsigned char) source->buf[i] > 126)) {
2793 if (!need_to_encode) {
2794 if (*target == NULL) {
2795 *target = NewStrBufPlain(source->buf, source->BufUsed);
2798 FlushStrBuf(*target);
2799 StrBufAppendBuf(*target, source, 0);
2802 return (*target)->BufUsed;
2806 if (*target == NULL)
2807 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2808 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2809 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2810 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2811 (*target)->BufUsed = sizeof(headerStr) - 1;
2812 for (i=0; (i < source->BufUsed); ++i) {
2813 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2814 IncreaseBuf(*target, 1, 0);
2815 ch = (unsigned char) source->buf[i];
2825 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2826 (*target)->BufUsed += 3;
2830 (*target)->buf[(*target)->BufUsed] = '_';
2832 (*target)->buf[(*target)->BufUsed] = ch;
2833 (*target)->BufUsed++;
2837 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2838 IncreaseBuf(*target, 1, 0);
2840 (*target)->buf[(*target)->BufUsed++] = '?';
2841 (*target)->buf[(*target)->BufUsed++] = '=';
2842 (*target)->buf[(*target)->BufUsed] = '\0';
2843 return (*target)->BufUsed;;
2848 static void AddRecipient(StrBuf *Target,
2850 StrBuf *EmailAddress,
2855 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2856 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2858 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2859 StrBufRFC2047encode(&EncBuf, UserName);
2860 StrBufAppendBuf(Target, EncBuf, 0);
2861 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2862 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2864 if (StrLength(EmailAddress) > 0){
2865 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2866 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2867 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2873 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2874 * \param Recp Source list of email recipients
2875 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2876 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2877 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2878 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2880 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2882 StrBuf *EmailAddress,
2886 const char *pch, *pche;
2887 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2889 if ((Recp == NULL) || (StrLength(Recp) == 0))
2893 pche = pch + StrLength(Recp);
2895 if (!CheckEncode(pch, -1, pche))
2896 return NewStrBufDup(Recp);
2898 Target = NewStrBufPlain(NULL, StrLength(Recp));
2900 while ((pch != NULL) && (pch < pche))
2902 while (isspace(*pch)) pch++;
2903 UserEnd = EmailStart = EmailEnd = NULL;
2905 if ((*pch == '"') || (*pch == '\'')) {
2906 UserStart = pch + 1;
2908 UserEnd = strchr(UserStart, *pch);
2909 if (UserEnd == NULL)
2910 break; ///TODO: Userfeedback??
2911 EmailStart = UserEnd + 1;
2912 while (isspace(*EmailStart))
2914 if (UserEnd == UserStart) {
2915 UserStart = UserEnd = NULL;
2918 if (*EmailStart == '<') {
2920 EmailEnd = strchr(EmailStart, '>');
2921 if (EmailEnd == NULL)
2922 EmailEnd = strchr(EmailStart, ',');
2926 EmailEnd = strchr(EmailStart, ',');
2928 if (EmailEnd == NULL)
2935 EmailEnd = strchr(UserStart, ',');
2936 if (EmailEnd == NULL) {
2937 EmailEnd = strchr(pch, '>');
2939 if (EmailEnd != NULL) {
2949 while ((EmailEnd > UserStart) && !gt &&
2950 ((*EmailEnd == ',') ||
2951 (*EmailEnd == '>') ||
2952 (isspace(*EmailEnd))))
2954 if (*EmailEnd == '>')
2959 if (EmailEnd == UserStart)
2963 EmailStart = strchr(UserStart, '<');
2964 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2966 UserEnd = EmailStart;
2968 while ((UserEnd > UserStart) &&
2969 isspace (*(UserEnd - 1)))
2972 if (UserStart >= UserEnd)
2973 UserStart = UserEnd = NULL;
2975 else { /* this is a local recipient... no domain, just a realname */
2976 EmailStart = UserStart;
2977 At = strchr(EmailStart, '@');
2983 EmailStart = UserStart;
2989 if ((UserStart != NULL) && (UserEnd != NULL))
2990 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2991 else if ((UserStart != NULL) && (UserEnd == NULL))
2992 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2994 FlushStrBuf(UserName);
2996 if ((EmailStart != NULL) && (EmailEnd != NULL))
2997 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2998 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2999 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3001 FlushStrBuf(EmailAddress);
3003 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3008 if ((pch != NULL) && (*pch == ','))
3010 if (pch != NULL) while (isspace(*pch))
3019 * @brief replaces all occurances of 'search' by 'replace'
3020 * @param buf Buffer to modify
3021 * @param search character to search
3022 * @param replace character to replace search by
3024 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3029 for (i=0; i<buf->BufUsed; i++)
3030 if (buf->buf[i] == search)
3031 buf->buf[i] = replace;
3037 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3038 * @param buf Buffer to modify
3040 void StrBufToUnixLF(StrBuf *buf)
3042 char *pche, *pchS, *pchT;
3046 pche = buf->buf + buf->BufUsed;
3047 pchS = pchT = buf->buf;
3053 if (*pchS != '\n') {
3062 buf->BufUsed = pchT - buf->buf;
3066 /*******************************************************************************
3067 * Iconv Wrapper; RFC822 de/encoding *
3068 *******************************************************************************/
3071 * @ingroup StrBuf_DeEnCoder
3072 * @brief Wrapper around iconv_open()
3073 * Our version adds aliases for non-standard Microsoft charsets
3074 * such as 'MS950', aliasing them to names like 'CP950'
3076 * @param tocode Target encoding
3077 * @param fromcode Source encoding
3078 * @param pic anonimized pointer to iconv struct
3080 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3083 iconv_t ic = (iconv_t)(-1) ;
3084 ic = iconv_open(tocode, fromcode);
3085 if (ic == (iconv_t)(-1) ) {
3086 char alias_fromcode[64];
3087 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3088 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3089 alias_fromcode[0] = 'C';
3090 alias_fromcode[1] = 'P';
3091 ic = iconv_open(tocode, alias_fromcode);
3094 *(iconv_t *)pic = ic;
3100 * @ingroup StrBuf_DeEnCoder
3101 * @brief find one chunk of a RFC822 encoded string
3102 * @param Buffer where to search
3103 * @param bptr where to start searching
3104 * @returns found position, NULL if none.
3106 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3109 /* Find the next ?Q? */
3110 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3113 end = strchr(bptr + 2, '?');
3118 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3119 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3120 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3121 (*(end + 2) == '?')) {
3122 /* skip on to the end of the cluster, the next ?= */
3123 end = strstr(end + 3, "?=");
3126 /* sort of half valid encoding, try to find an end. */
3127 end = strstr(bptr, "?=");
3134 * @ingroup StrBuf_DeEnCoder
3135 * @brief convert one buffer according to the preselected iconv pointer PIC
3136 * @param ConvertBuf buffer we need to translate
3137 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3138 * @param pic Pointer to the iconv-session Object
3140 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3146 char *ibuf; /**< Buffer of characters to be converted */
3147 char *obuf; /**< Buffer for converted characters */
3148 size_t ibuflen; /**< Length of input buffer */
3149 size_t obuflen; /**< Length of output buffer */
3152 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3155 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3156 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3157 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3159 ic = *(iconv_t*)pic;
3160 ibuf = ConvertBuf->buf;
3161 ibuflen = ConvertBuf->BufUsed;
3163 obuflen = TmpBuf->BufSize;
3165 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3168 if (errno == E2BIG) {
3170 IncreaseBuf(TmpBuf, 0, 0);
3175 else if (errno == EILSEQ){
3176 /* hm, invalid utf8 sequence... what to do now? */
3177 /* An invalid multibyte sequence has been encountered in the input */
3179 else if (errno == EINVAL) {
3180 /* An incomplete multibyte sequence has been encountered in the input. */
3183 FlushStrBuf(TmpBuf);
3186 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3187 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3189 /* little card game: wheres the red lady? */
3190 SwapBuffers(ConvertBuf, TmpBuf);
3191 FlushStrBuf(TmpBuf);
3198 * @ingroup StrBuf_DeEnCoder
3199 * @brief catches one RFC822 encoded segment, and decodes it.
3200 * @param Target buffer to fill with result
3201 * @param DecodeMe buffer with stuff to process
3202 * @param SegmentStart points to our current segment in DecodeMe
3203 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3204 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3205 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3206 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3208 inline static void DecodeSegment(StrBuf *Target,
3209 const StrBuf *DecodeMe,
3210 const char *SegmentStart,
3211 const char *SegmentEnd,
3213 StrBuf *ConvertBuf2,
3214 StrBuf *FoundCharset)
3220 iconv_t ic = (iconv_t)(-1);
3224 /* Now we handle foreign character sets properly encoded
3225 * in RFC2047 format.
3227 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3228 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3229 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3230 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3231 if (FoundCharset != NULL) {
3232 FlushStrBuf(FoundCharset);
3233 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3235 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3236 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3238 *encoding = toupper(*encoding);
3239 if (*encoding == 'B') { /**< base64 */
3240 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3241 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3242 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3244 ConvertBuf->BufUsed);
3246 else if (*encoding == 'Q') { /**< quoted-printable */
3250 while (pos < ConvertBuf->BufUsed)
3252 if (ConvertBuf->buf[pos] == '_')
3253 ConvertBuf->buf[pos] = ' ';
3257 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3258 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3260 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3263 ConvertBuf->BufUsed);
3266 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3269 ctdl_iconv_open("UTF-8", charset, &ic);
3270 if (ic != (iconv_t)(-1) ) {
3272 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3273 StrBufAppendBuf(Target, ConvertBuf2, 0);
3278 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3284 * @ingroup StrBuf_DeEnCoder
3285 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3286 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3287 * @param Target where to put the decoded string to
3288 * @param DecodeMe buffer with encoded string
3289 * @param DefaultCharset if we don't find one, which should we use?
3290 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3291 * put it here for later use where no string might be known.
3293 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3296 StrBuf *ConvertBuf2;
3297 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3298 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3300 StrBuf_RFC822_2_Utf8(Target,
3306 FreeStrBuf(&ConvertBuf);
3307 FreeStrBuf(&ConvertBuf2);
3311 * @ingroup StrBuf_DeEnCoder
3312 * @brief Handle subjects with RFC2047 encoding such as:
3313 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3314 * @param Target where to put the decoded string to
3315 * @param DecodeMe buffer with encoded string
3316 * @param DefaultCharset if we don't find one, which should we use?
3317 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3318 * put it here for later use where no string might be known.
3319 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3320 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3322 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3323 const StrBuf *DecodeMe,
3324 const StrBuf* DefaultCharset,
3325 StrBuf *FoundCharset,
3327 StrBuf *ConvertBuf2)
3329 StrBuf *DecodedInvalidBuf = NULL;
3330 const StrBuf *DecodeMee = DecodeMe;
3331 const char *start, *end, *next, *nextend, *ptr = NULL;
3333 iconv_t ic = (iconv_t)(-1) ;
3338 int illegal_non_rfc2047_encoding = 0;
3341 if (DecodeMe == NULL)
3343 /* Sometimes, badly formed messages contain strings which were simply
3344 * written out directly in some foreign character set instead of
3345 * using RFC2047 encoding. This is illegal but we will attempt to
3346 * handle it anyway by converting from a user-specified default
3347 * charset to UTF-8 if we see any nonprintable characters.
3350 for (i=0; i<DecodeMe->BufUsed; ++i) {
3351 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3352 illegal_non_rfc2047_encoding = 1;
3357 if ((illegal_non_rfc2047_encoding) &&
3358 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3359 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3362 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3363 if (ic != (iconv_t)(-1) ) {
3364 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3365 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3366 DecodeMee = DecodedInvalidBuf;
3372 /* pre evaluate the first pair */
3374 start = strstr(DecodeMee->buf, "=?");
3375 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3377 end = FindNextEnd (DecodeMee, start + 2);
3379 StrBufAppendBuf(Target, DecodeMee, 0);
3380 FreeStrBuf(&DecodedInvalidBuf);
3385 if (start != DecodeMee->buf) {
3388 nFront = start - DecodeMee->buf;
3389 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3392 * Since spammers will go to all sorts of absurd lengths to get their
3393 * messages through, there are LOTS of corrupt headers out there.
3394 * So, prevent a really badly formed RFC2047 header from throwing
3395 * this function into an infinite loop.
3397 while ((start != NULL) &&
3404 DecodeSegment(Target,
3412 next = strstr(end, "=?");
3414 if ((next != NULL) &&
3416 nextend = FindNextEnd(DecodeMee, next);
3417 if (nextend == NULL)
3420 /* did we find two partitions */
3421 if ((next != NULL) &&
3425 while ((ptr < next) &&
3432 * did we find a gab just filled with blanks?
3433 * if not, copy its stuff over.
3437 StrBufAppendBufPlain(Target,
3443 /* our next-pair is our new first pair now. */
3449 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3450 if ((end != NULL) && (end < nextend)) {
3452 while ( (ptr < nextend) &&
3459 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3461 FreeStrBuf(&DecodedInvalidBuf);
3464 /*******************************************************************************
3465 * Manipulating UTF-8 Strings *
3466 *******************************************************************************/
3470 * @brief evaluate the length of an utf8 special character sequence
3471 * @param Char the character to examine
3472 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3474 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3477 unsigned char test = (1<<7);
3479 if ((*CharS & 0xC0) != 0xC0)
3483 ((test & ((unsigned char)*CharS)) != 0))
3488 if ((n > 6) || ((CharE - CharS) < n))
3495 * @brief detect whether this char starts an utf-8 encoded char
3496 * @param Char character to inspect
3497 * @returns yes or no
3499 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3501 /** 11??.???? indicates an UTF8 Sequence. */
3502 return ((Char & 0xC0) == 0xC0);
3507 * @brief measure the number of glyphs in an UTF8 string...
3508 * @param Buf string to measure
3509 * @returns the number of glyphs in Buf
3511 long StrBuf_Utf8StrLen(StrBuf *Buf)
3517 if ((Buf == NULL) || (Buf->BufUsed == 0))
3520 eptr = Buf->buf + Buf->BufUsed;
3521 while ((aptr < eptr) && (*aptr != '\0')) {
3522 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3523 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3524 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3537 * @brief cuts a string after maxlen glyphs
3538 * @param Buf string to cut to maxlen glyphs
3539 * @param maxlen how long may the string become?
3540 * @returns current length of the string
3542 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3548 eptr = Buf->buf + Buf->BufUsed;
3549 while ((aptr < eptr) && (*aptr != '\0')) {
3550 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3551 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3552 while ((*aptr++ != '\0') && (m-- > 0));
3561 Buf->BufUsed = aptr - Buf->buf;
3562 return Buf->BufUsed;
3565 return Buf->BufUsed;
3573 /*******************************************************************************
3575 *******************************************************************************/
3578 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3579 #define OS_CODE 0x03 /*< unix */
3582 * @ingroup StrBuf_DeEnCoder
3583 * @brief uses the same calling syntax as compress2(), but it
3584 * creates a stream compatible with HTTP "Content-encoding: gzip"
3585 * @param dest compressed buffer
3586 * @param destLen length of the compresed data
3587 * @param source source to encode
3588 * @param sourceLen length of source to encode
3589 * @param level compression level
3591 int ZEXPORT compress_gzip(Bytef * dest,
3593 const Bytef * source,
3597 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3599 /* write gzip header */
3600 snprintf((char *) dest, *destLen,
3601 "%c%c%c%c%c%c%c%c%c%c",
3602 gz_magic[0], gz_magic[1], Z_DEFLATED,
3603 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3606 /* normal deflate */
3609 stream.next_in = (Bytef *) source;
3610 stream.avail_in = (uInt) sourceLen;
3611 stream.next_out = dest + 10L; // after header
3612 stream.avail_out = (uInt) * destLen;
3613 if ((uLong) stream.avail_out != *destLen)
3616 stream.zalloc = (alloc_func) 0;
3617 stream.zfree = (free_func) 0;
3618 stream.opaque = (voidpf) 0;
3620 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3621 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3625 err = deflate(&stream, Z_FINISH);
3626 if (err != Z_STREAM_END) {
3627 deflateEnd(&stream);
3628 return err == Z_OK ? Z_BUF_ERROR : err;
3630 *destLen = stream.total_out + 10L;
3632 /* write CRC and Length */
3633 uLong crc = crc32(0L, source, sourceLen);
3635 for (n = 0; n < 4; ++n, ++*destLen) {
3636 dest[*destLen] = (int) (crc & 0xff);
3639 uLong len = stream.total_in;
3640 for (n = 0; n < 4; ++n, ++*destLen) {
3641 dest[*destLen] = (int) (len & 0xff);
3644 err = deflateEnd(&stream);
3651 * @ingroup StrBuf_DeEnCoder
3652 * @brief compress the buffer with gzip
3653 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3654 * @param Buf buffer whose content is to be gzipped
3656 int CompressBuffer(StrBuf *Buf)
3659 char *compressed_data = NULL;
3660 size_t compressed_len, bufsize;
3663 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3664 compressed_data = malloc(compressed_len);
3666 if (compressed_data == NULL)
3668 /* Flush some space after the used payload so valgrind shuts up... */
3669 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3670 Buf->buf[Buf->BufUsed + i++] = '\0';
3671 if (compress_gzip((Bytef *) compressed_data,
3674 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3677 Buf->buf = compressed_data;
3678 Buf->BufUsed = compressed_len;
3679 Buf->BufSize = bufsize;
3680 /* Flush some space after the used payload so valgrind shuts up... */
3682 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3683 Buf->buf[Buf->BufUsed + i++] = '\0';
3686 free(compressed_data);
3688 #endif /* HAVE_ZLIB */
3692 /*******************************************************************************
3693 * File I/O; Callbacks to libevent *
3694 *******************************************************************************/
3696 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3701 if ((FB == NULL) || (FB->Buf == NULL))
3705 * check whether the read pointer is somewhere in a range
3706 * where a cut left is inexpensive
3709 if (FB->ReadWritePointer != NULL)
3713 already_read = FB->ReadWritePointer - FB->Buf->buf;
3714 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3716 if (already_read != 0) {
3719 unread = FB->Buf->BufUsed - already_read;
3721 /* else nothing to compact... */
3723 FB->ReadWritePointer = FB->Buf->buf;
3724 bufremain = FB->Buf->BufSize;
3726 else if ((unread < 64) ||
3727 (bufremain < already_read))
3730 * if its just a tiny bit remaining, or we run out of space...
3733 FB->Buf->BufUsed = unread;
3734 if (unread < already_read)
3735 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3737 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3738 FB->ReadWritePointer = FB->Buf->buf;
3739 bufremain = FB->Buf->BufSize - unread - 1;
3741 else if (bufremain < (FB->Buf->BufSize / 10))
3743 /* get a bigger buffer */
3745 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3747 FB->ReadWritePointer = FB->Buf->buf + unread;
3749 bufremain = FB->Buf->BufSize - unread - 1;
3750 /*TODO: special increase function that won't copy the already read! */
3753 else if (bufremain < 10) {
3754 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3756 FB->ReadWritePointer = FB->Buf->buf;
3758 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3763 FB->ReadWritePointer = FB->Buf->buf;
3764 bufremain = FB->Buf->BufSize - 1;
3767 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3770 FB->Buf->BufUsed += n;
3771 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3776 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3781 if ((FB == NULL) || (FB->Buf == NULL))
3784 if (FB->ReadWritePointer != NULL)
3786 WriteRemain = FB->Buf->BufUsed -
3787 (FB->ReadWritePointer -
3791 FB->ReadWritePointer = FB->Buf->buf;
3792 WriteRemain = FB->Buf->BufUsed;
3795 n = write(fd, FB->ReadWritePointer, WriteRemain);
3797 FB->ReadWritePointer += n;
3799 if (FB->ReadWritePointer ==
3800 FB->Buf->buf + FB->Buf->BufUsed)
3802 FlushStrBuf(FB->Buf);
3803 FB->ReadWritePointer = NULL;
3806 // check whether we've got something to write
3807 // get the maximum chunk plus the pointer we can send
3808 // write whats there
3809 // if not all was sent, remember the send pointer for the next time
3810 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3816 * @ingroup StrBuf_IO
3817 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3818 * @param LineBuf your line will be copied here.
3819 * @param FB BLOB with lines of text...
3820 * @param Ptr moved arround to keep the next-line across several iterations
3821 * has to be &NULL on start; will be &NotNULL on end of buffer
3822 * @returns size of copied buffer
3824 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3826 const char *aptr, *ptr, *eptr;
3829 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3833 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3834 FB->ReadWritePointer = StrBufNOTNULL;
3838 FlushStrBuf(LineBuf);
3839 if (FB->ReadWritePointer == NULL)
3840 ptr = aptr = FB->Buf->buf;
3842 ptr = aptr = FB->ReadWritePointer;
3844 optr = LineBuf->buf;
3845 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3846 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3848 while ((ptr <= eptr) &&
3855 LineBuf->BufUsed = optr - LineBuf->buf;
3856 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3857 optr = LineBuf->buf + LineBuf->BufUsed;
3858 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3863 if (optr > LineBuf->buf)
3865 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3866 LineBuf->BufUsed = optr - LineBuf->buf;
3868 if ((FB->ReadWritePointer != NULL) &&
3869 (FB->ReadWritePointer != FB->Buf->buf))
3871 /* Ok, the client application read all the data
3872 it was interested in so far. Since there is more to read,
3873 we now shrink the buffer, and move the rest over.
3875 StrBufCutLeft(FB->Buf,
3876 FB->ReadWritePointer - FB->Buf->buf);
3877 FB->ReadWritePointer = FB->Buf->buf;
3879 return eMustReadMore;
3882 LineBuf->BufUsed = optr - LineBuf->buf;
3884 if ((ptr <= eptr) && (*ptr == '\r'))
3886 if ((ptr <= eptr) && (*ptr == '\n'))
3890 FB->ReadWritePointer = ptr;
3893 FlushStrBuf(FB->Buf);
3894 FB->ReadWritePointer = NULL;
3897 return eReadSuccess;
3901 * @ingroup StrBuf_CHUNKED_IO
3902 * @brief check whether the chunk-buffer has more data waiting or not.
3903 * @param FB Chunk-Buffer to inspect
3905 eReadState StrBufCheckBuffer(IOBuffer *FB)
3909 if (FB->Buf->BufUsed == 0)
3910 return eReadSuccess;
3911 if (FB->ReadWritePointer == NULL)
3912 return eBufferNotEmpty;
3913 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3914 return eBufferNotEmpty;
3915 return eReadSuccess;
3918 long IOBufferStrLength(IOBuffer *FB)
3920 if ((FB == NULL) || (FB->Buf == NULL))
3922 if (FB->ReadWritePointer == NULL)
3923 return StrLength(FB->Buf);
3925 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3928 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
3930 memset(FDB, 0, sizeof(FDIOBuffer));
3932 FDB->SplicePipe[0] = -1;
3933 FDB->SplicePipe[1] = -1;
3936 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3938 FDIOBufferFlush(FDB);
3940 FDB->TotalSendSize = TotalSendSize;
3941 if (TotalSendSize > 0)
3942 FDB->ChunkSize = TotalSendSize;
3945 TotalSendSize = SIZ * 10;
3946 FDB->ChunkSize = TotalSendSize;
3952 pipe(FDB->SplicePipe);
3955 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
3960 void FDIOBufferDelete(FDIOBuffer *FDB)
3965 if (FDB->SplicePipe[0] > 0)
3966 close(FDB->SplicePipe[0]);
3967 if (FDB->SplicePipe[1] > 0)
3968 close(FDB->SplicePipe[1]);
3972 FreeStrBuf(&FDB->ChunkBuffer);
3974 if (FDB->OtherFD > 0)
3975 close(FDB->OtherFD);
3976 FDIOBufferFlush(FDB);
3979 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3981 ssize_t sent, pipesize;
3983 if (FDB->TotalSendSize > 0)
3988 if (FDB->PipeSize == 0)
3990 pipesize = splice(FDB->OtherFD,
3991 &FDB->TotalSentAlready,
3994 FDB->ChunkSendRemain,
3999 *Err = strerror(errno);
4002 FDB->PipeSize = pipesize;
4004 sent = splice(FDB->SplicePipe[0],
4009 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4012 *Err = strerror(errno);
4015 FDB->PipeSize -= sent;
4016 FDB->ChunkSendRemain -= sent;
4025 pRead = FDB->ChunkBuffer->buf;
4026 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4028 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4030 FDB->ChunkBuffer->BufUsed += nRead;
4031 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4033 else if (nRead == 0) {}
4037 nRead = write(FDB->IOB->fd,
4038 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4039 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4042 FDB->TotalSentAlready += nRead;
4043 FDB->ChunkSendRemain -= nRead;
4044 return FDB->ChunkSendRemain;
4056 if (FDB->PipeSize == 0)
4058 pipesize = splice(FDB->OtherFD,
4059 &FDB->TotalSentAlready,
4067 *Err = strerror(errno);
4070 FDB->PipeSize = pipesize;
4074 sent = splice(FDB->SplicePipe[0],
4079 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4082 *Err = strerror(errno);
4085 FDB->PipeSize -= sent;
4086 FDB->ChunkSendRemain -= sent;
4095 pRead = FDB->ChunkBuffer->buf;
4096 while ((FDB->ChunkSendRemain == 0) &&
4097 (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
4100 FDB->TotalSentAlready = 0;
4101 nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
4103 FDB->ChunkBuffer->BufUsed += nRead;
4104 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4105 FDB->ChunkSendRemain += nRead;
4107 else if (nRead == 0)
4113 *Err = strerror(errno);
4118 nRead = write(FDB->IOB->fd,
4119 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4120 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4123 FDB->TotalSentAlready += nRead;
4124 FDB->ChunkSendRemain -= nRead;
4125 if (FDB->ChunkSendRemain == 0)
4127 FDB->ChunkBuffer->BufUsed = 0;
4128 FDB->TotalSentAlready = 0;
4130 return FDB->ChunkSendRemain;
4139 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4141 ssize_t sent, pipesize;
4146 if (FDB->PipeSize == 0)
4148 pipesize = splice(FDB->IOB->fd,
4152 FDB->ChunkSendRemain,
4153 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4157 *Err = strerror(errno);
4160 FDB->PipeSize = pipesize;
4163 sent = splice(FDB->SplicePipe[0],
4166 &FDB->TotalSentAlready,
4168 SPLICE_F_MORE | SPLICE_F_MOVE);
4172 *Err = strerror(errno);
4175 FDB->PipeSize -= sent;
4176 FDB->ChunkSendRemain -= sent;
4182 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4187 FDB->ChunkBuffer->BufUsed = sent;
4189 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4190 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4192 *Err = strerror(errno);
4198 FDB->ChunkBuffer->BufUsed = 0;
4199 FDB->TotalSentAlready += sent;
4200 FDB->ChunkSendRemain -= sent;
4201 return FDB->ChunkSendRemain;
4203 else if (sent < 0) {
4204 *Err = strerror(errno);
4211 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4213 ssize_t sent, pipesize;
4218 if (FDB->PipeSize == 0)
4220 pipesize = splice(FDB->IOB->fd,
4221 &FDB->TotalReadAlready,
4224 FDB->ChunkSendRemain,
4225 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4229 *Err = strerror(errno);
4232 FDB->PipeSize = pipesize;
4235 sent = splice(FDB->SplicePipe[0],
4238 &FDB->TotalSentAlready,
4240 SPLICE_F_MORE | SPLICE_F_MOVE);
4244 *Err = strerror(errno);
4247 FDB->PipeSize -= sent;
4248 FDB->ChunkSendRemain -= sent;
4254 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4259 FDB->ChunkBuffer->BufUsed = sent;
4261 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4262 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4264 *Err = strerror(errno);
4270 FDB->ChunkBuffer->BufUsed = 0;
4271 FDB->TotalSentAlready += sent;
4272 FDB->ChunkSendRemain -= sent;
4273 return FDB->ChunkSendRemain;
4275 else if (sent < 0) {
4276 *Err = strerror(errno);
4283 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4289 int nSuccessLess = 0;
4293 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4294 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4296 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4297 (FDB->ChunkSendRemain > 0))
4300 tv.tv_sec = 1; /* selectresolution; */
4304 FD_SET(FDB->OtherFD, &rfds);
4305 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4306 *Error = strerror(errno);
4310 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4315 should_write = FDB->IOB->Buf->BufUsed -
4316 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4317 if (should_write > FDB->ChunkSendRemain)
4318 should_write = FDB->ChunkSendRemain;
4320 rlen = write(FDB->OtherFD,
4321 FDB->IOB->ReadWritePointer,
4324 *Error = strerror(errno);
4328 FDB->TotalSentAlready += rlen;
4329 FDB->IOB->ReadWritePointer += rlen;
4330 FDB->ChunkSendRemain -= rlen;
4332 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4334 FlushStrBuf(FDB->IOB->Buf);
4335 FDB->IOB->ReadWritePointer = NULL;
4338 if (FDB->ChunkSendRemain == 0)
4339 return eReadSuccess;
4341 return eMustReadMore;
4344 /*******************************************************************************
4345 * File I/O; Prefer buffered read since its faster! *
4346 *******************************************************************************/
4349 * @ingroup StrBuf_IO
4350 * @brief Read a line from socket
4351 * flushes and closes the FD on error
4352 * @param buf the buffer to get the input to
4353 * @param fd pointer to the filedescriptor to read
4354 * @param append Append to an existing string or replace?
4355 * @param Error strerror() on error
4356 * @returns numbers of chars read
4358 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4360 int len, rlen, slen;
4362 if ((buf == NULL) || (buf->buf == NULL)) {
4363 *Error = strerror(EINVAL);
4370 slen = len = buf->BufUsed;
4372 rlen = read(*fd, &buf->buf[len], 1);
4374 *Error = strerror(errno);
4381 if (buf->buf[len] == '\n')
4383 if (buf->buf[len] != '\r')
4385 if (len + 2 >= buf->BufSize) {
4387 buf->buf[len+1] = '\0';
4388 IncreaseBuf(buf, 1, -1);
4392 buf->buf[len] = '\0';
4397 * @ingroup StrBuf_BufferedIO
4398 * @brief Read a line from socket
4399 * flushes and closes the FD on error
4400 * @param Line the line to read from the fd / I/O Buffer
4401 * @param buf the buffer to get the input to
4402 * @param fd pointer to the filedescriptor to read
4403 * @param timeout number of successless selects until we bail out
4404 * @param selectresolution how long to wait on each select
4405 * @param Error strerror() on error
4406 * @returns numbers of chars read
4408 int StrBufTCP_read_buffered_line(StrBuf *Line,
4412 int selectresolution,
4416 int nSuccessLess = 0;
4423 if (buf->BufUsed > 0) {
4424 pch = strchr(buf->buf, '\n');
4427 len = pch - buf->buf;
4428 if (len > 0 && (*(pch - 1) == '\r') )
4430 StrBufSub(Line, buf, 0, len - rlen);
4431 StrBufCutLeft(buf, len + 1);
4436 if (buf->BufSize - buf->BufUsed < 10)
4437 IncreaseBuf(buf, 1, -1);
4439 fdflags = fcntl(*fd, F_GETFL);
4440 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4442 while ((nSuccessLess < timeout) && (pch == NULL)) {
4444 tv.tv_sec = selectresolution;
4449 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4450 *Error = strerror(errno);
4456 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4461 &buf->buf[buf->BufUsed],
4462 buf->BufSize - buf->BufUsed - 1);
4464 *Error = strerror(errno);
4469 else if (rlen > 0) {
4471 buf->BufUsed += rlen;
4472 buf->buf[buf->BufUsed] = '\0';
4473 pch = strchr(buf->buf, '\n');
4474 if ((pch == NULL) &&
4475 (buf->BufUsed + 10 > buf->BufSize) &&
4476 (IncreaseBuf(buf, 1, -1) == -1))
4484 len = pch - buf->buf;
4485 if (len > 0 && (*(pch - 1) == '\r') )
4487 StrBufSub(Line, buf, 0, len - rlen);
4488 StrBufCutLeft(buf, len + 1);
4495 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4496 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4497 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4499 * @ingroup StrBuf_BufferedIO
4500 * @brief Read a line from socket
4501 * flushes and closes the FD on error
4502 * @param Line where to append our Line read from the fd / I/O Buffer;
4503 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4504 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4505 * @param fd pointer to the filedescriptor to read
4506 * @param timeout number of successless selects until we bail out
4507 * @param selectresolution how long to wait on each select
4508 * @param Error strerror() on error
4509 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4511 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4516 int selectresolution,
4519 const char *pche = NULL;
4520 const char *pos = NULL;
4522 int len, rlen, retlen;
4523 int nSuccessLess = 0;
4525 const char *pch = NULL;
4531 if ((Line == NULL) ||
4538 *Error = ErrRBLF_PreConditionFailed;
4543 if ((IOBuf->BufUsed > 0) &&
4545 (pos < IOBuf->buf + IOBuf->BufUsed))
4549 pche = IOBuf->buf + IOBuf->BufUsed;
4553 while ((pch < pche) && (*pch != '\n'))
4555 if (Line->BufUsed + 10 > Line->BufSize)
4558 apos = pcht - Line->buf;
4560 IncreaseBuf(Line, 1, -1);
4561 pcht = Line->buf + apos;
4569 if (len > 0 && (*(pch - 1) == '\r') )
4578 if ((pch >= pche) || (*pch == '\0'))
4586 if ((pch != NULL) &&
4589 if (pch + 1 >= pche) {
4602 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4604 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4605 IncreaseBuf(IOBuf, 1, -1);
4607 fdflags = fcntl(*fd, F_GETFL);
4608 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4611 while ((nSuccessLess < timeout) &&
4621 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4622 *Error = strerror(errno);
4626 *Error = ErrRBLF_SelectFailed;
4629 if (! FD_ISSET(*fd, &rfds) != 0) {
4635 &IOBuf->buf[IOBuf->BufUsed],
4636 IOBuf->BufSize - IOBuf->BufUsed - 1);
4638 *Error = strerror(errno);
4643 else if (rlen > 0) {
4645 pLF = IOBuf->buf + IOBuf->BufUsed;
4646 IOBuf->BufUsed += rlen;
4647 IOBuf->buf[IOBuf->BufUsed] = '\0';
4649 pche = IOBuf->buf + IOBuf->BufUsed;
4651 while ((pLF < pche) && (*pLF != '\n'))
4653 if ((pLF >= pche) || (*pLF == '\0'))
4656 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4660 if (pLF != NULL) apos = pLF - IOBuf->buf;
4661 IncreaseBuf(IOBuf, 1, -1);
4662 if (pLF != NULL) pLF = IOBuf->buf + apos;
4676 if (len > 0 && (*(pLF - 1) == '\r') )
4678 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4679 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4685 return retlen + len;
4687 *Error = ErrRBLF_NotEnoughSentFromServer;
4692 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4694 * @ingroup StrBuf_IO
4695 * @brief Input binary data from socket
4696 * flushes and closes the FD on error
4697 * @param Buf the buffer to get the input to
4698 * @param fd pointer to the filedescriptor to read
4699 * @param append Append to an existing string or replace?
4700 * @param nBytes the maximal number of bytes to read
4701 * @param Error strerror() on error
4702 * @returns numbers of chars read
4704 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4715 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4717 *Error = ErrRBLF_BLOBPreConditionFailed;
4722 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4723 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4725 ptr = Buf->buf + Buf->BufUsed;
4727 fdflags = fcntl(*fd, F_GETFL);
4728 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4730 while ((nRead < nBytes) &&
4740 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4741 *Error = strerror(errno);
4745 *Error = ErrRBLF_SelectFailed;
4748 if (! FD_ISSET(*fd, &rfds) != 0) {
4754 if ((rlen = read(*fd,
4756 nBytes - nRead)) == -1) {
4759 *Error = strerror(errno);
4764 Buf->BufUsed += rlen;
4766 Buf->buf[Buf->BufUsed] = '\0';
4770 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4771 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4773 * @ingroup StrBuf_BufferedIO
4774 * @brief Input binary data from socket
4775 * flushes and closes the FD on error
4776 * @param Blob put binary thing here
4777 * @param IOBuf the buffer to get the input to
4778 * @param Pos offset inside of IOBuf
4779 * @param fd pointer to the filedescriptor to read
4780 * @param append Append to an existing string or replace?
4781 * @param nBytes the maximal number of bytes to read
4782 * @param check whether we should search for '000\n' terminators in case of timeouts
4783 * @param Error strerror() on error
4784 * @returns numbers of chars read
4786 int StrBufReadBLOBBuffered(StrBuf *Blob,
4799 int nAlreadyRead = 0;
4804 int nSuccessLess = 0;
4807 if ((Blob == NULL) ||
4814 *Error = ErrRBB_BLOBFPreConditionFailed;
4820 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4821 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4826 rlen = pos - IOBuf->buf;
4827 rlen = IOBuf->BufUsed - rlen;
4830 if ((IOBuf->BufUsed > 0) &&
4832 (pos < IOBuf->buf + IOBuf->BufUsed))
4834 if (rlen < nBytes) {
4835 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4836 Blob->BufUsed += rlen;
4837 Blob->buf[Blob->BufUsed] = '\0';
4838 nAlreadyRead = nRead = rlen;
4841 if (rlen >= nBytes) {
4842 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4843 Blob->BufUsed += nBytes;
4844 Blob->buf[Blob->BufUsed] = '\0';
4845 if (rlen == nBytes) {
4857 if (IOBuf->BufSize < nBytes - nRead)
4858 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4861 fdflags = fcntl(*fd, F_GETFL);
4862 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4870 while ((nSuccessLess < MaxTries) &&
4880 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4881 *Error = strerror(errno);
4885 *Error = ErrRBLF_SelectFailed;
4888 if (! FD_ISSET(*fd, &rfds) != 0) {
4895 IOBuf->BufSize - (ptr - IOBuf->buf));
4899 *Error = strerror(errno);
4902 else if (rlen == 0){
4903 if ((check == NNN_TERM) &&
4905 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4907 StrBufPlain(Blob, HKEY("\n000\n"));
4908 StrBufCutRight(Blob, 5);
4909 return Blob->BufUsed;
4911 else if (!IsNonBlock)
4913 else if (nSuccessLess > MaxTries) {
4915 *Error = ErrRBB_too_many_selects;
4919 else if (rlen > 0) {
4923 IOBuf->BufUsed += rlen;
4926 if (nSuccessLess >= MaxTries) {
4928 *Error = ErrRBB_too_many_selects;
4932 if (nRead > nBytes) {
4933 *Pos = IOBuf->buf + nBytes;
4935 Blob->buf[Blob->BufUsed] = '\0';
4936 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4940 return nRead + nAlreadyRead;
4944 * @ingroup StrBuf_IO
4945 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4946 * @param LineBuf your line will be copied here.
4947 * @param Buf BLOB with lines of text...
4948 * @param Ptr moved arround to keep the next-line across several iterations
4949 * has to be &NULL on start; will be &NotNULL on end of buffer
4950 * @returns size of remaining buffer
4952 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4954 const char *aptr, *ptr, *eptr;
4957 if ((Buf == NULL) ||
4958 (*Ptr == StrBufNOTNULL) ||
4960 (LineBuf->buf == NULL))
4962 *Ptr = StrBufNOTNULL;
4966 FlushStrBuf(LineBuf);
4968 ptr = aptr = Buf->buf;
4972 optr = LineBuf->buf;
4973 eptr = Buf->buf + Buf->BufUsed;
4974 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4976 while ((ptr <= eptr) &&
4983 LineBuf->BufUsed = optr - LineBuf->buf;
4984 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4985 optr = LineBuf->buf + LineBuf->BufUsed;
4986 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4990 if ((ptr >= eptr) && (optr > LineBuf->buf))
4992 LineBuf->BufUsed = optr - LineBuf->buf;
4994 if ((ptr <= eptr) && (*ptr == '\r'))
4996 if ((ptr <= eptr) && (*ptr == '\n'))
5003 *Ptr = StrBufNOTNULL;
5006 return Buf->BufUsed - (ptr - Buf->buf);
5011 * @ingroup StrBuf_IO
5012 * @brief removes double slashes from pathnames
5013 * @param Dir directory string to filter
5014 * @param RemoveTrailingSlash allows / disallows trailing slashes
5016 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5022 while (!IsEmptyStr(a)) {
5034 if ((RemoveTrailingSlash) &&
5040 Dir->BufUsed = b - Dir->buf;