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)) {
1180 StrBufCutAt(Buf, 0, Buf->buf);
1184 pRight = strchr(Buf->buf, rightboundary);
1185 if (pRight != NULL) {
1186 StrBufCutAt(Buf, 0, pRight);
1189 StrBufCutAt(Buf, 0, Buf->buf);
1193 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1194 if (pLeft != NULL) {
1195 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1198 StrBufCutAt(Buf, 0, Buf->buf);
1205 * @ingroup StrBuf_Filler
1206 * @brief uppercase the contents of a buffer
1207 * @param Buf the buffer to translate
1209 void StrBufUpCase(StrBuf *Buf)
1213 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1216 pche = pch + Buf->BufUsed;
1217 while (pch < pche) {
1218 *pch = toupper(*pch);
1225 * @ingroup StrBuf_Filler
1226 * @brief lowercase the contents of a buffer
1227 * @param Buf the buffer to translate
1229 void StrBufLowerCase(StrBuf *Buf)
1233 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1236 pche = pch + Buf->BufUsed;
1237 while (pch < pche) {
1238 *pch = tolower(*pch);
1244 /*******************************************************************************
1245 * a tokenizer that kills, maims, and destroys *
1246 *******************************************************************************/
1249 * @ingroup StrBuf_Tokenizer
1250 * @brief Replace a token at a given place with a given length by another token with given length
1251 * @param Buf String where to work on
1252 * @param where where inside of the Buf is the search-token
1253 * @param HowLong How long is the token to be replaced
1254 * @param Repl Token to insert at 'where'
1255 * @param ReplLen Length of repl
1256 * @returns -1 if fail else length of resulting Buf
1258 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1259 const char *Repl, long ReplLen)
1262 if ((Buf == NULL) ||
1263 (where > Buf->BufUsed) ||
1264 (where + HowLong > Buf->BufUsed))
1267 if (where + ReplLen - HowLong > Buf->BufSize)
1268 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1271 memmove(Buf->buf + where + ReplLen,
1272 Buf->buf + where + HowLong,
1273 Buf->BufUsed - where - HowLong);
1275 memcpy(Buf->buf + where,
1278 Buf->BufUsed += ReplLen - HowLong;
1280 return Buf->BufUsed;
1284 * @ingroup StrBuf_Tokenizer
1285 * @brief Counts the numbmer of tokens in a buffer
1286 * @param source String to count tokens in
1287 * @param tok Tokenizer char to count
1288 * @returns numbers of tokenizer chars found
1290 int StrBufNum_tokens(const StrBuf *source, char tok)
1294 if ((source == NULL) || (source->BufUsed == 0))
1296 if ((source->BufUsed == 1) && (*source->buf == tok))
1300 pche = pch + source->BufUsed;
1311 * @ingroup StrBuf_Tokenizer
1312 * @brief a string tokenizer
1313 * @param Source StringBuffer to read into
1314 * @param parmnum n'th Parameter to remove
1315 * @param separator tokenizer character
1316 * @returns -1 if not found, else length of token.
1318 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1321 char *d, *s, *end; /* dest, source */
1324 /* Find desired @parameter */
1325 end = Source->buf + Source->BufUsed;
1327 while ((d <= end) &&
1330 /* End of string, bail! */
1335 if (*d == separator) {
1340 if ((d == NULL) || (d >= end))
1341 return 0; /* @Parameter not found */
1343 /* Find next @parameter */
1345 while ((s <= end) &&
1346 (*s && *s != separator))
1350 if (*s == separator)
1354 /* Hack and slash */
1359 memmove(d, s, Source->BufUsed - (s - Source->buf));
1360 Source->BufUsed += ReducedBy;
1361 Source->buf[Source->BufUsed] = '\0';
1363 else if (d == Source->buf) {
1365 Source->BufUsed = 0;
1369 Source->BufUsed += ReducedBy;
1380 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1382 const StrBuf Temp = {
1395 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1399 * @ingroup StrBuf_Tokenizer
1400 * @brief a string tokenizer
1401 * @param dest Destination StringBuffer
1402 * @param Source StringBuffer to read into
1403 * @param parmnum n'th Parameter to extract
1404 * @param separator tokenizer character
1405 * @returns -1 if not found, else length of token.
1407 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1409 const char *s, *e; //* source * /
1410 int len = 0; //* running total length of extracted string * /
1411 int current_token = 0; //* token currently being processed * /
1414 dest->buf[0] = '\0';
1420 if ((Source == NULL) || (Source->BufUsed ==0)) {
1424 e = s + Source->BufUsed;
1427 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1429 while ((s < e) && !IsEmptyStr(s)) {
1430 if (*s == separator) {
1433 if (len >= dest->BufSize) {
1434 dest->BufUsed = len;
1435 if (IncreaseBuf(dest, 1, -1) < 0) {
1440 if ( (current_token == parmnum) &&
1441 (*s != separator)) {
1442 dest->buf[len] = *s;
1445 else if (current_token > parmnum) {
1451 dest->buf[len] = '\0';
1452 dest->BufUsed = len;
1454 if (current_token < parmnum) {
1455 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1458 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1467 * @ingroup StrBuf_Tokenizer
1468 * @brief a string tokenizer to fetch an integer
1469 * @param Source String containing tokens
1470 * @param parmnum n'th Parameter to extract
1471 * @param separator tokenizer character
1472 * @returns 0 if not found, else integer representation of the token
1474 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1484 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1491 * @ingroup StrBuf_Tokenizer
1492 * @brief a string tokenizer to fetch a long integer
1493 * @param Source String containing tokens
1494 * @param parmnum n'th Parameter to extract
1495 * @param separator tokenizer character
1496 * @returns 0 if not found, else long integer representation of the token
1498 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1508 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1516 * @ingroup StrBuf_Tokenizer
1517 * @brief a string tokenizer to fetch an unsigned long
1518 * @param Source String containing tokens
1519 * @param parmnum n'th Parameter to extract
1520 * @param separator tokenizer character
1521 * @returns 0 if not found, else unsigned long representation of the token
1523 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1534 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1538 return (unsigned long) atol(pnum);
1547 * @ingroup StrBuf_NextTokenizer
1548 * @brief a string tokenizer; Bounds checker
1549 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1550 * @param Source our tokenbuffer
1551 * @param pStart the token iterator pointer to inspect
1552 * @returns whether the revolving pointer is inside of the search range
1554 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1556 if ((Source == NULL) ||
1557 (*pStart == StrBufNOTNULL) ||
1558 (Source->BufUsed == 0))
1562 if (*pStart == NULL)
1566 else if (*pStart > Source->buf + Source->BufUsed)
1570 else if (*pStart <= Source->buf)
1579 * @ingroup StrBuf_NextTokenizer
1580 * @brief a string tokenizer
1581 * @param dest Destination StringBuffer
1582 * @param Source StringBuffer to read into
1583 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1584 * @param separator tokenizer
1585 * @returns -1 if not found, else length of token.
1587 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1589 const char *s; /* source */
1590 const char *EndBuffer; /* end stop of source buffer */
1591 int current_token = 0; /* token currently being processed */
1592 int len = 0; /* running total length of extracted string */
1594 if ((Source == NULL) ||
1595 (Source->BufUsed == 0) )
1597 *pStart = StrBufNOTNULL;
1603 EndBuffer = Source->buf + Source->BufUsed;
1607 dest->buf[0] = '\0';
1612 *pStart = EndBuffer + 1;
1616 if (*pStart == NULL)
1618 *pStart = Source->buf; /* we're starting to examine this buffer. */
1620 else if ((*pStart < Source->buf) ||
1621 (*pStart > EndBuffer ) )
1623 return -1; /* no more tokens to find. */
1627 /* start to find the next token */
1628 while ((s <= EndBuffer) &&
1629 (current_token == 0) )
1631 if (*s == separator)
1633 /* we found the next token */
1637 if (len >= dest->BufSize)
1639 /* our Dest-buffer isn't big enough, increase it. */
1640 dest->BufUsed = len;
1642 if (IncreaseBuf(dest, 1, -1) < 0) {
1643 /* WHUT? no more mem? bail out. */
1650 if ( (current_token == 0 ) && /* are we in our target token? */
1651 (!IsEmptyStr(s) ) &&
1652 (separator != *s) ) /* don't copy the token itself */
1654 dest->buf[len] = *s; /* Copy the payload */
1655 ++len; /* remember the bigger size. */
1661 /* did we reach the end? */
1662 if ((s > EndBuffer)) {
1663 EndBuffer = StrBufNOTNULL;
1664 *pStart = EndBuffer;
1667 *pStart = s; /* remember the position for the next run */
1670 /* sanitize our extracted token */
1671 dest->buf[len] = '\0';
1672 dest->BufUsed = len;
1679 * @ingroup StrBuf_NextTokenizer
1680 * @brief a string tokenizer
1681 * @param Source StringBuffer to read from
1682 * @param pStart pointer to the end of the last token. Feed with NULL.
1683 * @param separator tokenizer character
1684 * @param nTokens number of tokens to fastforward over
1685 * @returns -1 if not found, else length of token.
1687 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1689 const char *s, *EndBuffer; //* source * /
1690 int len = 0; //* running total length of extracted string * /
1691 int current_token = 0; //* token currently being processed * /
1693 if ((Source == NULL) ||
1694 (Source->BufUsed ==0)) {
1698 return Source->BufUsed;
1700 if (*pStart == NULL)
1701 *pStart = Source->buf;
1703 EndBuffer = Source->buf + Source->BufUsed;
1705 if ((*pStart < Source->buf) ||
1706 (*pStart > EndBuffer)) {
1714 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1716 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1717 if (*s == separator) {
1720 if (current_token >= nTokens) {
1732 * @ingroup StrBuf_NextTokenizer
1733 * @brief a string tokenizer to fetch an integer
1734 * @param Source StringBuffer to read from
1735 * @param pStart Cursor on the tokenstring
1736 * @param separator tokenizer character
1737 * @returns 0 if not found, else integer representation of the token
1739 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1749 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1756 * @ingroup StrBuf_NextTokenizer
1757 * @brief a string tokenizer to fetch a long integer
1758 * @param Source StringBuffer to read from
1759 * @param pStart Cursor on the tokenstring
1760 * @param separator tokenizer character
1761 * @returns 0 if not found, else long integer representation of the token
1763 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1773 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1781 * @ingroup StrBuf_NextTokenizer
1782 * @brief a string tokenizer to fetch an unsigned long
1783 * @param Source StringBuffer to read from
1784 * @param pStart Cursor on the tokenstring
1785 * @param separator tokenizer character
1786 * @returns 0 if not found, else unsigned long representation of the token
1788 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1799 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1803 return (unsigned long) atol(pnum);
1813 /*******************************************************************************
1814 * Escape Appending *
1815 *******************************************************************************/
1818 * @ingroup StrBuf_DeEnCoder
1819 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1820 * @param OutBuf the output buffer
1821 * @param In Buffer to encode
1822 * @param PlainIn way in from plain old c strings
1824 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1826 const char *pch, *pche;
1830 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1832 if (PlainIn != NULL) {
1833 len = strlen(PlainIn);
1839 pche = pch + In->BufUsed;
1846 pt = OutBuf->buf + OutBuf->BufUsed;
1847 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1849 while (pch < pche) {
1851 IncreaseBuf(OutBuf, 1, -1);
1852 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1853 pt = OutBuf->buf + OutBuf->BufUsed;
1856 if((*pch >= 'a' && *pch <= 'z') ||
1857 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1858 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1859 (*pch == '!') || (*pch == '_') ||
1860 (*pch == ',') || (*pch == '.'))
1867 *(pt + 1) = HexList[(unsigned char)*pch][0];
1868 *(pt + 2) = HexList[(unsigned char)*pch][1];
1870 OutBuf->BufUsed += 3;
1878 * @ingroup StrBuf_DeEnCoder
1879 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1880 * @param OutBuf the output buffer
1881 * @param In Buffer to encode
1882 * @param PlainIn way in from plain old c strings
1884 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1886 const char *pch, *pche;
1890 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1892 if (PlainIn != NULL) {
1893 len = strlen(PlainIn);
1899 pche = pch + In->BufUsed;
1906 pt = OutBuf->buf + OutBuf->BufUsed;
1907 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1909 while (pch < pche) {
1911 IncreaseBuf(OutBuf, 1, -1);
1912 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1913 pt = OutBuf->buf + OutBuf->BufUsed;
1916 if((*pch >= 'a' && *pch <= 'z') ||
1917 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1918 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1919 (*pch == '!') || (*pch == '_') ||
1920 (*pch == ',') || (*pch == '.'))
1927 *(pt + 1) = HexList[(unsigned char)*pch][0];
1928 *(pt + 2) = HexList[(unsigned char)*pch][1];
1930 OutBuf->BufUsed += 3;
1938 * @ingroup StrBuf_DeEnCoder
1939 * @brief append a string in hex encoding to the buffer
1940 * @param OutBuf the output buffer
1941 * @param In Buffer to encode
1942 * @param PlainIn way in from plain old c strings
1943 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1945 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1947 const unsigned char *pch, *pche;
1951 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1953 if (PlainIn != NULL) {
1955 len = strlen((const char*)PlainIn);
1962 pch = (const unsigned char*)In->buf;
1963 pche = pch + In->BufUsed;
1970 pt = OutBuf->buf + OutBuf->BufUsed;
1971 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1973 while (pch < pche) {
1975 IncreaseBuf(OutBuf, 1, -1);
1976 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1977 pt = OutBuf->buf + OutBuf->BufUsed;
1980 *pt = HexList[*pch][0];
1982 *pt = HexList[*pch][1];
1983 pt ++; pch ++; OutBuf->BufUsed += 2;
1989 * @ingroup StrBuf_DeEnCoder
1990 * @brief append a string in hex encoding to the buffer
1991 * @param OutBuf the output buffer
1992 * @param In Buffer to encode
1993 * @param PlainIn way in from plain old c strings
1995 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1997 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2001 * @ingroup StrBuf_DeEnCoder
2002 * @brief Append a string, escaping characters which have meaning in HTML.
2004 * @param Target target buffer
2005 * @param Source source buffer; set to NULL if you just have a C-String
2006 * @param PlainIn Plain-C string to append; set to NULL if unused
2007 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2008 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2009 * if set to 2, linebreaks are replaced by <br/>
2011 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2013 const char *aptr, *eiptr;
2017 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2020 if (PlainIn != NULL) {
2022 len = strlen(PlainIn);
2027 eiptr = aptr + Source->BufUsed;
2028 len = Source->BufUsed;
2034 bptr = Target->buf + Target->BufUsed;
2035 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2037 while (aptr < eiptr){
2039 IncreaseBuf(Target, 1, -1);
2040 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2041 bptr = Target->buf + Target->BufUsed;
2044 memcpy(bptr, "<", 4);
2046 Target->BufUsed += 4;
2048 else if (*aptr == '>') {
2049 memcpy(bptr, ">", 4);
2051 Target->BufUsed += 4;
2053 else if (*aptr == '&') {
2054 memcpy(bptr, "&", 5);
2056 Target->BufUsed += 5;
2058 else if (*aptr == '"') {
2059 memcpy(bptr, """, 6);
2061 Target->BufUsed += 6;
2063 else if (*aptr == '\'') {
2064 memcpy(bptr, "'", 5);
2066 Target->BufUsed += 5;
2068 else if (*aptr == LB) {
2073 else if (*aptr == RB) {
2078 else if (*aptr == QU) {
2083 else if ((*aptr == 32) && (nbsp == 1)) {
2084 memcpy(bptr, " ", 6);
2086 Target->BufUsed += 6;
2088 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2089 *bptr='\0'; /* nothing */
2091 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2092 memcpy(bptr, "<br/>", 11);
2094 Target->BufUsed += 11;
2098 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2099 *bptr='\0'; /* nothing */
2109 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2111 return Target->BufUsed;
2115 * @ingroup StrBuf_DeEnCoder
2116 * @brief Append a string, escaping characters which have meaning in HTML.
2117 * Converts linebreaks into blanks; escapes single quotes
2118 * @param Target target buffer
2119 * @param Source source buffer; set to NULL if you just have a C-String
2120 * @param PlainIn Plain-C string to append; set to NULL if unused
2122 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2124 const char *aptr, *eiptr;
2128 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2131 if (PlainIn != NULL) {
2133 len = strlen(PlainIn);
2138 eiptr = aptr + Source->BufUsed;
2139 len = Source->BufUsed;
2145 eptr = Target->buf + Target->BufSize - 8;
2146 tptr = Target->buf + Target->BufUsed;
2148 while (aptr < eiptr){
2150 IncreaseBuf(Target, 1, -1);
2151 eptr = Target->buf + Target->BufSize - 8;
2152 tptr = Target->buf + Target->BufUsed;
2155 if (*aptr == '\n') {
2159 else if (*aptr == '\r') {
2163 else if (*aptr == '\'') {
2169 Target->BufUsed += 5;
2182 * @ingroup StrBuf_DeEnCoder
2183 * @brief Append a string, escaping characters which have meaning in ICAL.
2185 * @param Target target buffer
2186 * @param Source source buffer; set to NULL if you just have a C-String
2187 * @param PlainIn Plain-C string to append; set to NULL if unused
2189 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2191 const char *aptr, *eiptr;
2195 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2198 if (PlainIn != NULL) {
2200 len = strlen(PlainIn);
2205 eiptr = aptr + Source->BufUsed;
2206 len = Source->BufUsed;
2212 eptr = Target->buf + Target->BufSize - 8;
2213 tptr = Target->buf + Target->BufUsed;
2215 while (aptr < eiptr){
2216 if(tptr + 3 >= eptr) {
2217 IncreaseBuf(Target, 1, -1);
2218 eptr = Target->buf + Target->BufSize - 8;
2219 tptr = Target->buf + Target->BufUsed;
2222 if (*aptr == '\n') {
2229 else if (*aptr == '\r') {
2236 else if (*aptr == ',') {
2252 * @ingroup StrBuf_DeEnCoder
2253 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2255 * @param Target target buffer
2256 * @param Source source buffer; set to NULL if you just have a C-String
2257 * @param PlainIn Plain-C string to append; set to NULL if unused
2258 * @returns size of result or -1
2260 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2262 const char *aptr, *eiptr;
2267 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2270 if (PlainIn != NULL) {
2272 len = strlen(PlainIn);
2277 eiptr = aptr + Source->BufUsed;
2278 len = Source->BufUsed;
2284 bptr = Target->buf + Target->BufUsed;
2285 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2287 while (aptr < eiptr){
2289 IncreaseBuf(Target, 1, -1);
2290 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2291 bptr = Target->buf + Target->BufUsed;
2295 memcpy(bptr, HKEY("\\n"));
2297 Target->BufUsed += 2;
2300 memcpy(bptr, HKEY("\\r"));
2302 Target->BufUsed += 2;
2309 Target->BufUsed += 2;
2312 if ((*(aptr + 1) == 'u') &&
2313 isxdigit(*(aptr + 2)) &&
2314 isxdigit(*(aptr + 3)) &&
2315 isxdigit(*(aptr + 4)) &&
2316 isxdigit(*(aptr + 5)))
2317 { /* oh, a unicode escaper. let it pass through. */
2318 memcpy(bptr, aptr, 6);
2321 Target->BufUsed += 6;
2329 Target->BufUsed += 2;
2337 Target->BufUsed += 2;
2344 Target->BufUsed += 2;
2351 Target->BufUsed += 2;
2354 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2355 while (IsUtf8Sequence > 0){
2358 if (--IsUtf8Sequence)
2366 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2368 return Target->BufUsed;
2372 * @ingroup StrBuf_DeEnCoder
2373 * @brief Append a string, escaping characters which have meaning in HTML + json.
2375 * @param Target target buffer
2376 * @param Source source buffer; set to NULL if you just have a C-String
2377 * @param PlainIn Plain-C string to append; set to NULL if unused
2378 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2379 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2380 * if set to 2, linebreaks are replaced by <br/>
2382 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2384 const char *aptr, *eiptr;
2387 int IsUtf8Sequence = 0;
2389 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2392 if (PlainIn != NULL) {
2394 len = strlen(PlainIn);
2399 eiptr = aptr + Source->BufUsed;
2400 len = Source->BufUsed;
2406 bptr = Target->buf + Target->BufUsed;
2407 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2409 while (aptr < eiptr){
2411 IncreaseBuf(Target, 1, -1);
2412 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2413 bptr = Target->buf + Target->BufUsed;
2417 memcpy(bptr, HKEY("<"));
2419 Target->BufUsed += 4;
2422 memcpy(bptr, HKEY(">"));
2424 Target->BufUsed += 4;
2427 memcpy(bptr, HKEY("&"));
2429 Target->BufUsed += 5;
2442 switch (nolinebreaks) {
2444 *bptr='\0'; /* nothing */
2447 memcpy(bptr, HKEY("<br/>"));
2449 Target->BufUsed += 11;
2452 memcpy(bptr, HKEY("\\n"));
2454 Target->BufUsed += 2;
2458 switch (nolinebreaks) {
2461 *bptr='\0'; /* nothing */
2464 memcpy(bptr, HKEY("\\r"));
2466 Target->BufUsed += 2;
2476 Target->BufUsed += 2;
2479 if ((*(aptr + 1) == 'u') &&
2480 isxdigit(*(aptr + 2)) &&
2481 isxdigit(*(aptr + 3)) &&
2482 isxdigit(*(aptr + 4)) &&
2483 isxdigit(*(aptr + 5)))
2484 { /* oh, a unicode escaper. let it pass through. */
2485 memcpy(bptr, aptr, 6);
2488 Target->BufUsed += 6;
2496 Target->BufUsed += 2;
2504 Target->BufUsed += 2;
2511 Target->BufUsed += 2;
2518 Target->BufUsed += 2;
2522 memcpy(bptr, HKEY(" "));
2524 Target->BufUsed += 6;
2528 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2529 while (IsUtf8Sequence > 0){
2532 if (--IsUtf8Sequence)
2540 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2542 return Target->BufUsed;
2547 * @ingroup StrBuf_DeEnCoder
2548 * @brief replace all non-Ascii characters by another
2549 * @param Buf buffer to inspect
2550 * @param repl charater to stamp over non ascii chars
2552 void StrBufAsciify(StrBuf *Buf, const char repl)
2556 for (offset = 0; offset < Buf->BufUsed; offset ++)
2557 if (!isascii(Buf->buf[offset]))
2558 Buf->buf[offset] = repl;
2563 * @ingroup StrBuf_DeEnCoder
2564 * @brief unhide special chars hidden to the HTML escaper
2565 * @param target buffer to put the unescaped string in
2566 * @param source buffer to unescape
2568 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2573 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2579 FlushStrBuf(target);
2581 len = source->BufUsed;
2582 for (a = 0; a < len; ++a) {
2583 if (target->BufUsed >= target->BufSize)
2584 IncreaseBuf(target, 1, -1);
2586 if (source->buf[a] == '=') {
2587 hex[0] = source->buf[a + 1];
2588 hex[1] = source->buf[a + 2];
2591 sscanf(hex, "%02x", &b);
2592 target->buf[target->BufUsed] = b;
2593 target->buf[++target->BufUsed] = 0;
2597 target->buf[target->BufUsed] = source->buf[a];
2598 target->buf[++target->BufUsed] = 0;
2605 * @ingroup StrBuf_DeEnCoder
2606 * @brief hide special chars from the HTML escapers and friends
2607 * @param target buffer to put the escaped string in
2608 * @param source buffer to escape
2610 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2615 FlushStrBuf(target);
2617 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2622 len = source->BufUsed;
2623 for (i=0; i<len; ++i) {
2624 if (target->BufUsed + 4 >= target->BufSize)
2625 IncreaseBuf(target, 1, -1);
2626 if ( (isalnum(source->buf[i])) ||
2627 (source->buf[i]=='-') ||
2628 (source->buf[i]=='_') ) {
2629 target->buf[target->BufUsed++] = source->buf[i];
2632 sprintf(&target->buf[target->BufUsed],
2634 (0xFF &source->buf[i]));
2635 target->BufUsed += 3;
2638 target->buf[target->BufUsed + 1] = '\0';
2642 /*******************************************************************************
2643 * Quoted Printable de/encoding *
2644 *******************************************************************************/
2647 * @ingroup StrBuf_DeEnCoder
2648 * @brief decode a buffer from base 64 encoding; destroys original
2649 * @param Buf Buffor to transform
2651 int StrBufDecodeBase64(StrBuf *Buf)
2659 xferbuf = (char*) malloc(Buf->BufSize);
2660 if (xferbuf == NULL)
2664 siz = CtdlDecodeBase64(xferbuf,
2674 * @ingroup StrBuf_DeEnCoder
2675 * @brief decode a buffer from base 64 encoding; destroys original
2676 * @param Buf Buffor to transform
2678 int StrBufDecodeHex(StrBuf *Buf)
2681 char *pch, *pche, *pchi;
2683 if (Buf == NULL) return -1;
2685 pch = pchi = Buf->buf;
2686 pche = pch + Buf->BufUsed;
2688 while (pchi < pche){
2689 ch = decode_hex(pchi);
2696 Buf->BufUsed = pch - Buf->buf;
2697 return Buf->BufUsed;
2701 * @ingroup StrBuf_DeEnCoder
2702 * @brief replace all chars >0x20 && < 0x7F with Mute
2703 * @param Mute char to put over invalid chars
2704 * @param Buf Buffor to transform
2706 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2710 if (Buf == NULL) return -1;
2711 pch = (unsigned char *)Buf->buf;
2712 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2713 if ((*pch < 0x20) || (*pch > 0x7F))
2717 return Buf->BufUsed;
2722 * @ingroup StrBuf_DeEnCoder
2723 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2724 * @param Buf Buffer to translate
2725 * @param StripBlanks Reduce several blanks to one?
2727 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2736 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2737 Buf->buf[Buf->BufUsed - 1] = '\0';
2742 while (a < Buf->BufUsed) {
2743 if (Buf->buf[a] == '+')
2745 else if (Buf->buf[a] == '%') {
2746 /* don't let % chars through, rather truncate the input. */
2747 if (a + 2 > Buf->BufUsed) {
2752 hex[0] = Buf->buf[a + 1];
2753 hex[1] = Buf->buf[a + 2];
2756 sscanf(hex, "%02x", &b);
2757 Buf->buf[a] = (char) b;
2758 len = Buf->BufUsed - a - 2;
2760 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2772 * @ingroup StrBuf_DeEnCoder
2773 * @brief RFC2047-encode a header field if necessary.
2774 * If no non-ASCII characters are found, the string
2775 * will be copied verbatim without encoding.
2777 * @param target Target buffer.
2778 * @param source Source string to be encoded.
2779 * @returns encoded length; -1 if non success.
2781 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2783 const char headerStr[] = "=?UTF-8?Q?";
2784 int need_to_encode = 0;
2788 if ((source == NULL) ||
2792 while ((i < source->BufUsed) &&
2793 (!IsEmptyStr (&source->buf[i])) &&
2794 (need_to_encode == 0)) {
2795 if (((unsigned char) source->buf[i] < 32) ||
2796 ((unsigned char) source->buf[i] > 126)) {
2802 if (!need_to_encode) {
2803 if (*target == NULL) {
2804 *target = NewStrBufPlain(source->buf, source->BufUsed);
2807 FlushStrBuf(*target);
2808 StrBufAppendBuf(*target, source, 0);
2811 return (*target)->BufUsed;
2815 if (*target == NULL)
2816 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2817 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2818 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2819 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2820 (*target)->BufUsed = sizeof(headerStr) - 1;
2821 for (i=0; (i < source->BufUsed); ++i) {
2822 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2823 IncreaseBuf(*target, 1, 0);
2824 ch = (unsigned char) source->buf[i];
2834 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2835 (*target)->BufUsed += 3;
2839 (*target)->buf[(*target)->BufUsed] = '_';
2841 (*target)->buf[(*target)->BufUsed] = ch;
2842 (*target)->BufUsed++;
2846 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2847 IncreaseBuf(*target, 1, 0);
2849 (*target)->buf[(*target)->BufUsed++] = '?';
2850 (*target)->buf[(*target)->BufUsed++] = '=';
2851 (*target)->buf[(*target)->BufUsed] = '\0';
2852 return (*target)->BufUsed;;
2857 static void AddRecipient(StrBuf *Target,
2859 StrBuf *EmailAddress,
2864 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2865 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2867 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2868 StrBufRFC2047encode(&EncBuf, UserName);
2869 StrBufAppendBuf(Target, EncBuf, 0);
2870 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2871 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2873 if (StrLength(EmailAddress) > 0){
2874 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2875 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2876 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2882 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2883 * \param Recp Source list of email recipients
2884 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2885 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2886 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2887 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2889 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2891 StrBuf *EmailAddress,
2895 const char *pch, *pche;
2896 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2898 if ((Recp == NULL) || (StrLength(Recp) == 0))
2902 pche = pch + StrLength(Recp);
2904 if (!CheckEncode(pch, -1, pche))
2905 return NewStrBufDup(Recp);
2907 Target = NewStrBufPlain(NULL, StrLength(Recp));
2909 while ((pch != NULL) && (pch < pche))
2911 while (isspace(*pch)) pch++;
2912 UserEnd = EmailStart = EmailEnd = NULL;
2914 if ((*pch == '"') || (*pch == '\'')) {
2915 UserStart = pch + 1;
2917 UserEnd = strchr(UserStart, *pch);
2918 if (UserEnd == NULL)
2919 break; ///TODO: Userfeedback??
2920 EmailStart = UserEnd + 1;
2921 while (isspace(*EmailStart))
2923 if (UserEnd == UserStart) {
2924 UserStart = UserEnd = NULL;
2927 if (*EmailStart == '<') {
2929 EmailEnd = strchr(EmailStart, '>');
2930 if (EmailEnd == NULL)
2931 EmailEnd = strchr(EmailStart, ',');
2935 EmailEnd = strchr(EmailStart, ',');
2937 if (EmailEnd == NULL)
2944 EmailEnd = strchr(UserStart, ',');
2945 if (EmailEnd == NULL) {
2946 EmailEnd = strchr(pch, '>');
2948 if (EmailEnd != NULL) {
2958 while ((EmailEnd > UserStart) && !gt &&
2959 ((*EmailEnd == ',') ||
2960 (*EmailEnd == '>') ||
2961 (isspace(*EmailEnd))))
2963 if (*EmailEnd == '>')
2968 if (EmailEnd == UserStart)
2972 EmailStart = strchr(UserStart, '<');
2973 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2975 UserEnd = EmailStart;
2977 while ((UserEnd > UserStart) &&
2978 isspace (*(UserEnd - 1)))
2981 if (UserStart >= UserEnd)
2982 UserStart = UserEnd = NULL;
2984 else { /* this is a local recipient... no domain, just a realname */
2985 EmailStart = UserStart;
2986 At = strchr(EmailStart, '@');
2992 EmailStart = UserStart;
2998 if ((UserStart != NULL) && (UserEnd != NULL))
2999 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3000 else if ((UserStart != NULL) && (UserEnd == NULL))
3001 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3003 FlushStrBuf(UserName);
3005 if ((EmailStart != NULL) && (EmailEnd != NULL))
3006 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3007 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3008 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3010 FlushStrBuf(EmailAddress);
3012 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3017 if ((pch != NULL) && (*pch == ','))
3019 if (pch != NULL) while (isspace(*pch))
3028 * @brief replaces all occurances of 'search' by 'replace'
3029 * @param buf Buffer to modify
3030 * @param search character to search
3031 * @param replace character to replace search by
3033 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3038 for (i=0; i<buf->BufUsed; i++)
3039 if (buf->buf[i] == search)
3040 buf->buf[i] = replace;
3046 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3047 * @param buf Buffer to modify
3049 void StrBufToUnixLF(StrBuf *buf)
3051 char *pche, *pchS, *pchT;
3055 pche = buf->buf + buf->BufUsed;
3056 pchS = pchT = buf->buf;
3062 if (*pchS != '\n') {
3071 buf->BufUsed = pchT - buf->buf;
3075 /*******************************************************************************
3076 * Iconv Wrapper; RFC822 de/encoding *
3077 *******************************************************************************/
3080 * @ingroup StrBuf_DeEnCoder
3081 * @brief Wrapper around iconv_open()
3082 * Our version adds aliases for non-standard Microsoft charsets
3083 * such as 'MS950', aliasing them to names like 'CP950'
3085 * @param tocode Target encoding
3086 * @param fromcode Source encoding
3087 * @param pic anonimized pointer to iconv struct
3089 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3092 iconv_t ic = (iconv_t)(-1) ;
3093 ic = iconv_open(tocode, fromcode);
3094 if (ic == (iconv_t)(-1) ) {
3095 char alias_fromcode[64];
3096 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3097 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3098 alias_fromcode[0] = 'C';
3099 alias_fromcode[1] = 'P';
3100 ic = iconv_open(tocode, alias_fromcode);
3103 *(iconv_t *)pic = ic;
3109 * @ingroup StrBuf_DeEnCoder
3110 * @brief find one chunk of a RFC822 encoded string
3111 * @param Buffer where to search
3112 * @param bptr where to start searching
3113 * @returns found position, NULL if none.
3115 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3118 /* Find the next ?Q? */
3119 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3122 end = strchr(bptr + 2, '?');
3127 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3128 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3129 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3130 (*(end + 2) == '?')) {
3131 /* skip on to the end of the cluster, the next ?= */
3132 end = strstr(end + 3, "?=");
3135 /* sort of half valid encoding, try to find an end. */
3136 end = strstr(bptr, "?=");
3143 * @ingroup StrBuf_DeEnCoder
3144 * @brief convert one buffer according to the preselected iconv pointer PIC
3145 * @param ConvertBuf buffer we need to translate
3146 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3147 * @param pic Pointer to the iconv-session Object
3149 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3155 char *ibuf; /**< Buffer of characters to be converted */
3156 char *obuf; /**< Buffer for converted characters */
3157 size_t ibuflen; /**< Length of input buffer */
3158 size_t obuflen; /**< Length of output buffer */
3161 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3164 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3165 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3166 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3168 ic = *(iconv_t*)pic;
3169 ibuf = ConvertBuf->buf;
3170 ibuflen = ConvertBuf->BufUsed;
3172 obuflen = TmpBuf->BufSize;
3174 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3177 if (errno == E2BIG) {
3179 IncreaseBuf(TmpBuf, 0, 0);
3184 else if (errno == EILSEQ){
3185 /* hm, invalid utf8 sequence... what to do now? */
3186 /* An invalid multibyte sequence has been encountered in the input */
3188 else if (errno == EINVAL) {
3189 /* An incomplete multibyte sequence has been encountered in the input. */
3192 FlushStrBuf(TmpBuf);
3195 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3196 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3198 /* little card game: wheres the red lady? */
3199 SwapBuffers(ConvertBuf, TmpBuf);
3200 FlushStrBuf(TmpBuf);
3207 * @ingroup StrBuf_DeEnCoder
3208 * @brief catches one RFC822 encoded segment, and decodes it.
3209 * @param Target buffer to fill with result
3210 * @param DecodeMe buffer with stuff to process
3211 * @param SegmentStart points to our current segment in DecodeMe
3212 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3213 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3214 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3215 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3217 inline static void DecodeSegment(StrBuf *Target,
3218 const StrBuf *DecodeMe,
3219 const char *SegmentStart,
3220 const char *SegmentEnd,
3222 StrBuf *ConvertBuf2,
3223 StrBuf *FoundCharset)
3229 iconv_t ic = (iconv_t)(-1);
3233 /* Now we handle foreign character sets properly encoded
3234 * in RFC2047 format.
3236 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3237 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3238 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3239 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3240 if (FoundCharset != NULL) {
3241 FlushStrBuf(FoundCharset);
3242 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3244 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3245 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3247 *encoding = toupper(*encoding);
3248 if (*encoding == 'B') { /**< base64 */
3249 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3250 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3251 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3253 ConvertBuf->BufUsed);
3255 else if (*encoding == 'Q') { /**< quoted-printable */
3259 while (pos < ConvertBuf->BufUsed)
3261 if (ConvertBuf->buf[pos] == '_')
3262 ConvertBuf->buf[pos] = ' ';
3266 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3267 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3269 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3272 ConvertBuf->BufUsed);
3275 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3278 ctdl_iconv_open("UTF-8", charset, &ic);
3279 if (ic != (iconv_t)(-1) ) {
3281 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3282 StrBufAppendBuf(Target, ConvertBuf2, 0);
3287 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3293 * @ingroup StrBuf_DeEnCoder
3294 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3295 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3296 * @param Target where to put the decoded string to
3297 * @param DecodeMe buffer with encoded string
3298 * @param DefaultCharset if we don't find one, which should we use?
3299 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3300 * put it here for later use where no string might be known.
3302 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3305 StrBuf *ConvertBuf2;
3306 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3307 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3309 StrBuf_RFC822_2_Utf8(Target,
3315 FreeStrBuf(&ConvertBuf);
3316 FreeStrBuf(&ConvertBuf2);
3320 * @ingroup StrBuf_DeEnCoder
3321 * @brief Handle subjects with RFC2047 encoding such as:
3322 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3323 * @param Target where to put the decoded string to
3324 * @param DecodeMe buffer with encoded string
3325 * @param DefaultCharset if we don't find one, which should we use?
3326 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3327 * put it here for later use where no string might be known.
3328 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3329 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3331 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3332 const StrBuf *DecodeMe,
3333 const StrBuf* DefaultCharset,
3334 StrBuf *FoundCharset,
3336 StrBuf *ConvertBuf2)
3338 StrBuf *DecodedInvalidBuf = NULL;
3339 const StrBuf *DecodeMee = DecodeMe;
3340 const char *start, *end, *next, *nextend, *ptr = NULL;
3342 iconv_t ic = (iconv_t)(-1) ;
3347 int illegal_non_rfc2047_encoding = 0;
3350 if (DecodeMe == NULL)
3352 /* Sometimes, badly formed messages contain strings which were simply
3353 * written out directly in some foreign character set instead of
3354 * using RFC2047 encoding. This is illegal but we will attempt to
3355 * handle it anyway by converting from a user-specified default
3356 * charset to UTF-8 if we see any nonprintable characters.
3359 for (i=0; i<DecodeMe->BufUsed; ++i) {
3360 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3361 illegal_non_rfc2047_encoding = 1;
3366 if ((illegal_non_rfc2047_encoding) &&
3367 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3368 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3371 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3372 if (ic != (iconv_t)(-1) ) {
3373 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3374 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3375 DecodeMee = DecodedInvalidBuf;
3381 /* pre evaluate the first pair */
3383 start = strstr(DecodeMee->buf, "=?");
3384 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3386 end = FindNextEnd (DecodeMee, start + 2);
3388 StrBufAppendBuf(Target, DecodeMee, 0);
3389 FreeStrBuf(&DecodedInvalidBuf);
3394 if (start != DecodeMee->buf) {
3397 nFront = start - DecodeMee->buf;
3398 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3401 * Since spammers will go to all sorts of absurd lengths to get their
3402 * messages through, there are LOTS of corrupt headers out there.
3403 * So, prevent a really badly formed RFC2047 header from throwing
3404 * this function into an infinite loop.
3406 while ((start != NULL) &&
3413 DecodeSegment(Target,
3421 next = strstr(end, "=?");
3423 if ((next != NULL) &&
3425 nextend = FindNextEnd(DecodeMee, next);
3426 if (nextend == NULL)
3429 /* did we find two partitions */
3430 if ((next != NULL) &&
3434 while ((ptr < next) &&
3441 * did we find a gab just filled with blanks?
3442 * if not, copy its stuff over.
3446 StrBufAppendBufPlain(Target,
3452 /* our next-pair is our new first pair now. */
3458 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3459 if ((end != NULL) && (end < nextend)) {
3461 while ( (ptr < nextend) &&
3468 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3470 FreeStrBuf(&DecodedInvalidBuf);
3473 /*******************************************************************************
3474 * Manipulating UTF-8 Strings *
3475 *******************************************************************************/
3479 * @brief evaluate the length of an utf8 special character sequence
3480 * @param Char the character to examine
3481 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3483 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3486 unsigned char test = (1<<7);
3488 if ((*CharS & 0xC0) != 0xC0)
3492 ((test & ((unsigned char)*CharS)) != 0))
3497 if ((n > 6) || ((CharE - CharS) < n))
3504 * @brief detect whether this char starts an utf-8 encoded char
3505 * @param Char character to inspect
3506 * @returns yes or no
3508 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3510 /** 11??.???? indicates an UTF8 Sequence. */
3511 return ((Char & 0xC0) == 0xC0);
3516 * @brief measure the number of glyphs in an UTF8 string...
3517 * @param Buf string to measure
3518 * @returns the number of glyphs in Buf
3520 long StrBuf_Utf8StrLen(StrBuf *Buf)
3526 if ((Buf == NULL) || (Buf->BufUsed == 0))
3529 eptr = Buf->buf + Buf->BufUsed;
3530 while ((aptr < eptr) && (*aptr != '\0')) {
3531 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3532 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3533 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3546 * @brief cuts a string after maxlen glyphs
3547 * @param Buf string to cut to maxlen glyphs
3548 * @param maxlen how long may the string become?
3549 * @returns current length of the string
3551 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3557 eptr = Buf->buf + Buf->BufUsed;
3558 while ((aptr < eptr) && (*aptr != '\0')) {
3559 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3560 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3561 while ((*aptr++ != '\0') && (m-- > 0));
3570 Buf->BufUsed = aptr - Buf->buf;
3571 return Buf->BufUsed;
3574 return Buf->BufUsed;
3582 /*******************************************************************************
3584 *******************************************************************************/
3587 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3588 #define OS_CODE 0x03 /*< unix */
3591 * @ingroup StrBuf_DeEnCoder
3592 * @brief uses the same calling syntax as compress2(), but it
3593 * creates a stream compatible with HTTP "Content-encoding: gzip"
3594 * @param dest compressed buffer
3595 * @param destLen length of the compresed data
3596 * @param source source to encode
3597 * @param sourceLen length of source to encode
3598 * @param level compression level
3600 int ZEXPORT compress_gzip(Bytef * dest,
3602 const Bytef * source,
3606 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3608 /* write gzip header */
3609 snprintf((char *) dest, *destLen,
3610 "%c%c%c%c%c%c%c%c%c%c",
3611 gz_magic[0], gz_magic[1], Z_DEFLATED,
3612 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3615 /* normal deflate */
3618 stream.next_in = (Bytef *) source;
3619 stream.avail_in = (uInt) sourceLen;
3620 stream.next_out = dest + 10L; // after header
3621 stream.avail_out = (uInt) * destLen;
3622 if ((uLong) stream.avail_out != *destLen)
3625 stream.zalloc = (alloc_func) 0;
3626 stream.zfree = (free_func) 0;
3627 stream.opaque = (voidpf) 0;
3629 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3630 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3634 err = deflate(&stream, Z_FINISH);
3635 if (err != Z_STREAM_END) {
3636 deflateEnd(&stream);
3637 return err == Z_OK ? Z_BUF_ERROR : err;
3639 *destLen = stream.total_out + 10L;
3641 /* write CRC and Length */
3642 uLong crc = crc32(0L, source, sourceLen);
3644 for (n = 0; n < 4; ++n, ++*destLen) {
3645 dest[*destLen] = (int) (crc & 0xff);
3648 uLong len = stream.total_in;
3649 for (n = 0; n < 4; ++n, ++*destLen) {
3650 dest[*destLen] = (int) (len & 0xff);
3653 err = deflateEnd(&stream);
3660 * @ingroup StrBuf_DeEnCoder
3661 * @brief compress the buffer with gzip
3662 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3663 * @param Buf buffer whose content is to be gzipped
3665 int CompressBuffer(StrBuf *Buf)
3668 char *compressed_data = NULL;
3669 size_t compressed_len, bufsize;
3672 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3673 compressed_data = malloc(compressed_len);
3675 if (compressed_data == NULL)
3677 /* Flush some space after the used payload so valgrind shuts up... */
3678 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3679 Buf->buf[Buf->BufUsed + i++] = '\0';
3680 if (compress_gzip((Bytef *) compressed_data,
3683 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3686 Buf->buf = compressed_data;
3687 Buf->BufUsed = compressed_len;
3688 Buf->BufSize = bufsize;
3689 /* Flush some space after the used payload so valgrind shuts up... */
3691 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3692 Buf->buf[Buf->BufUsed + i++] = '\0';
3695 free(compressed_data);
3697 #endif /* HAVE_ZLIB */
3701 /*******************************************************************************
3702 * File I/O; Callbacks to libevent *
3703 *******************************************************************************/
3705 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3710 if ((FB == NULL) || (FB->Buf == NULL))
3714 * check whether the read pointer is somewhere in a range
3715 * where a cut left is inexpensive
3718 if (FB->ReadWritePointer != NULL)
3722 already_read = FB->ReadWritePointer - FB->Buf->buf;
3723 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3725 if (already_read != 0) {
3728 unread = FB->Buf->BufUsed - already_read;
3730 /* else nothing to compact... */
3732 FB->ReadWritePointer = FB->Buf->buf;
3733 bufremain = FB->Buf->BufSize;
3735 else if ((unread < 64) ||
3736 (bufremain < already_read))
3739 * if its just a tiny bit remaining, or we run out of space...
3742 FB->Buf->BufUsed = unread;
3743 if (unread < already_read)
3744 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3746 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3747 FB->ReadWritePointer = FB->Buf->buf;
3748 bufremain = FB->Buf->BufSize - unread - 1;
3750 else if (bufremain < (FB->Buf->BufSize / 10))
3752 /* get a bigger buffer */
3754 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3756 FB->ReadWritePointer = FB->Buf->buf + unread;
3758 bufremain = FB->Buf->BufSize - unread - 1;
3759 /*TODO: special increase function that won't copy the already read! */
3762 else if (bufremain < 10) {
3763 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3765 FB->ReadWritePointer = FB->Buf->buf;
3767 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3772 FB->ReadWritePointer = FB->Buf->buf;
3773 bufremain = FB->Buf->BufSize - 1;
3776 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3779 FB->Buf->BufUsed += n;
3780 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3785 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3790 if ((FB == NULL) || (FB->Buf == NULL))
3793 if (FB->ReadWritePointer != NULL)
3795 WriteRemain = FB->Buf->BufUsed -
3796 (FB->ReadWritePointer -
3800 FB->ReadWritePointer = FB->Buf->buf;
3801 WriteRemain = FB->Buf->BufUsed;
3804 n = write(fd, FB->ReadWritePointer, WriteRemain);
3806 FB->ReadWritePointer += n;
3808 if (FB->ReadWritePointer ==
3809 FB->Buf->buf + FB->Buf->BufUsed)
3811 FlushStrBuf(FB->Buf);
3812 FB->ReadWritePointer = NULL;
3815 // check whether we've got something to write
3816 // get the maximum chunk plus the pointer we can send
3817 // write whats there
3818 // if not all was sent, remember the send pointer for the next time
3819 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3825 * @ingroup StrBuf_IO
3826 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3827 * @param LineBuf your line will be copied here.
3828 * @param FB BLOB with lines of text...
3829 * @param Ptr moved arround to keep the next-line across several iterations
3830 * has to be &NULL on start; will be &NotNULL on end of buffer
3831 * @returns size of copied buffer
3833 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3835 const char *aptr, *ptr, *eptr;
3838 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3842 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3843 FB->ReadWritePointer = StrBufNOTNULL;
3847 FlushStrBuf(LineBuf);
3848 if (FB->ReadWritePointer == NULL)
3849 ptr = aptr = FB->Buf->buf;
3851 ptr = aptr = FB->ReadWritePointer;
3853 optr = LineBuf->buf;
3854 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3855 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3857 while ((ptr <= eptr) &&
3864 LineBuf->BufUsed = optr - LineBuf->buf;
3865 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3866 optr = LineBuf->buf + LineBuf->BufUsed;
3867 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3872 if (optr > LineBuf->buf)
3874 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3875 LineBuf->BufUsed = optr - LineBuf->buf;
3877 if ((FB->ReadWritePointer != NULL) &&
3878 (FB->ReadWritePointer != FB->Buf->buf))
3880 /* Ok, the client application read all the data
3881 it was interested in so far. Since there is more to read,
3882 we now shrink the buffer, and move the rest over.
3884 StrBufCutLeft(FB->Buf,
3885 FB->ReadWritePointer - FB->Buf->buf);
3886 FB->ReadWritePointer = FB->Buf->buf;
3888 return eMustReadMore;
3891 LineBuf->BufUsed = optr - LineBuf->buf;
3893 if ((ptr <= eptr) && (*ptr == '\r'))
3895 if ((ptr <= eptr) && (*ptr == '\n'))
3899 FB->ReadWritePointer = ptr;
3902 FlushStrBuf(FB->Buf);
3903 FB->ReadWritePointer = NULL;
3906 return eReadSuccess;
3910 * @ingroup StrBuf_CHUNKED_IO
3911 * @brief check whether the chunk-buffer has more data waiting or not.
3912 * @param FB Chunk-Buffer to inspect
3914 eReadState StrBufCheckBuffer(IOBuffer *FB)
3918 if (FB->Buf->BufUsed == 0)
3919 return eReadSuccess;
3920 if (FB->ReadWritePointer == NULL)
3921 return eBufferNotEmpty;
3922 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3923 return eBufferNotEmpty;
3924 return eReadSuccess;
3927 long IOBufferStrLength(IOBuffer *FB)
3929 if ((FB == NULL) || (FB->Buf == NULL))
3931 if (FB->ReadWritePointer == NULL)
3932 return StrLength(FB->Buf);
3934 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3937 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3939 memset(FDB, 0, sizeof(FDIOBuffer));
3941 FDB->TotalSendSize = TotalSendSize;
3945 pipe(FDB->SplicePipe);
3948 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3953 void FDIOBufferDelete(FDIOBuffer *FDB)
3958 if (FDB->SplicePipe[0] > 0)
3959 close(FDB->SplicePipe[0]);
3960 if (FDB->SplicePipe[1] > 0)
3961 close(FDB->SplicePipe[1]);
3965 FreeStrBuf(&FDB->ChunkBuffer);
3967 if (FDB->OtherFD > 0)
3968 close(FDB->OtherFD);
3969 memset(FDB, 0, sizeof(FDIOBuffer));
3972 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3974 ssize_t sent, pipesize;
3978 if (FDB->PipeSize == 0)
3980 pipesize = splice(FDB->OtherFD,
3981 &FDB->TotalSentAlready,
3984 FDB->ChunkSendRemain,
3989 *Err = strerror(errno);
3992 FDB->PipeSize = pipesize;
3994 sent = splice(FDB->SplicePipe[0],
3999 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4002 *Err = strerror(errno);
4005 FDB->PipeSize -= sent;
4006 FDB->ChunkSendRemain -= sent;
4015 pRead = FDB->ChunkBuffer->buf;
4016 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4018 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4020 FDB->ChunkBuffer->BufUsed += nRead;
4021 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4023 else if (nRead == 0) {}
4028 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
4031 FDB->TotalSentAlready += nRead;
4032 FDB->ChunkSendRemain -= nRead;
4033 return FDB->ChunkSendRemain;
4041 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4043 ssize_t sent, pipesize;
4048 if (FDB->PipeSize == 0)
4050 pipesize = splice(FDB->IOB->fd,
4054 FDB->ChunkSendRemain,
4055 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4059 *Err = strerror(errno);
4062 FDB->PipeSize = pipesize;
4065 sent = splice(FDB->SplicePipe[0],
4068 &FDB->TotalSentAlready,
4070 SPLICE_F_MORE | SPLICE_F_MOVE);
4074 *Err = strerror(errno);
4077 FDB->PipeSize -= sent;
4078 FDB->ChunkSendRemain -= sent;
4084 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4089 FDB->ChunkBuffer->BufUsed = sent;
4091 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4092 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4094 *Err = strerror(errno);
4100 FDB->ChunkBuffer->BufUsed = 0;
4101 FDB->TotalSentAlready += sent;
4102 FDB->ChunkSendRemain -= sent;
4103 return FDB->ChunkSendRemain;
4105 else if (sent < 0) {
4106 *Err = strerror(errno);
4113 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4115 ssize_t sent, pipesize;
4120 if (FDB->PipeSize == 0)
4122 pipesize = splice(FDB->IOB->fd,
4123 &FDB->TotalReadAlready,
4126 FDB->ChunkSendRemain,
4127 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4131 *Err = strerror(errno);
4134 FDB->PipeSize = pipesize;
4137 sent = splice(FDB->SplicePipe[0],
4140 &FDB->TotalSentAlready,
4142 SPLICE_F_MORE | SPLICE_F_MOVE);
4146 *Err = strerror(errno);
4149 FDB->PipeSize -= sent;
4150 FDB->ChunkSendRemain -= sent;
4156 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4161 FDB->ChunkBuffer->BufUsed = sent;
4163 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4164 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4166 *Err = strerror(errno);
4172 FDB->ChunkBuffer->BufUsed = 0;
4173 FDB->TotalSentAlready += sent;
4174 FDB->ChunkSendRemain -= sent;
4175 return FDB->ChunkSendRemain;
4177 else if (sent < 0) {
4178 *Err = strerror(errno);
4185 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4191 int nSuccessLess = 0;
4195 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4196 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4198 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4199 (FDB->ChunkSendRemain > 0))
4202 tv.tv_sec = 1; /* selectresolution; */
4206 FD_SET(FDB->OtherFD, &rfds);
4207 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4208 *Error = strerror(errno);
4212 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4217 should_write = FDB->IOB->Buf->BufUsed -
4218 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4219 if (should_write > FDB->ChunkSendRemain)
4220 should_write = FDB->ChunkSendRemain;
4222 rlen = write(FDB->OtherFD,
4223 FDB->IOB->ReadWritePointer,
4226 *Error = strerror(errno);
4230 FDB->TotalSentAlready += rlen;
4231 FDB->IOB->ReadWritePointer += rlen;
4232 FDB->ChunkSendRemain -= rlen;
4234 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4236 FlushStrBuf(FDB->IOB->Buf);
4237 FDB->IOB->ReadWritePointer = NULL;
4240 if (FDB->ChunkSendRemain == 0)
4241 return eReadSuccess;
4243 return eMustReadMore;
4246 /*******************************************************************************
4247 * File I/O; Prefer buffered read since its faster! *
4248 *******************************************************************************/
4251 * @ingroup StrBuf_IO
4252 * @brief Read a line from socket
4253 * flushes and closes the FD on error
4254 * @param buf the buffer to get the input to
4255 * @param fd pointer to the filedescriptor to read
4256 * @param append Append to an existing string or replace?
4257 * @param Error strerror() on error
4258 * @returns numbers of chars read
4260 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4262 int len, rlen, slen;
4264 if ((buf == NULL) || (buf->buf == NULL)) {
4265 *Error = strerror(EINVAL);
4272 slen = len = buf->BufUsed;
4274 rlen = read(*fd, &buf->buf[len], 1);
4276 *Error = strerror(errno);
4283 if (buf->buf[len] == '\n')
4285 if (buf->buf[len] != '\r')
4287 if (len + 2 >= buf->BufSize) {
4289 buf->buf[len+1] = '\0';
4290 IncreaseBuf(buf, 1, -1);
4294 buf->buf[len] = '\0';
4299 * @ingroup StrBuf_BufferedIO
4300 * @brief Read a line from socket
4301 * flushes and closes the FD on error
4302 * @param Line the line to read from the fd / I/O Buffer
4303 * @param buf the buffer to get the input to
4304 * @param fd pointer to the filedescriptor to read
4305 * @param timeout number of successless selects until we bail out
4306 * @param selectresolution how long to wait on each select
4307 * @param Error strerror() on error
4308 * @returns numbers of chars read
4310 int StrBufTCP_read_buffered_line(StrBuf *Line,
4314 int selectresolution,
4318 int nSuccessLess = 0;
4325 if (buf->BufUsed > 0) {
4326 pch = strchr(buf->buf, '\n');
4329 len = pch - buf->buf;
4330 if (len > 0 && (*(pch - 1) == '\r') )
4332 StrBufSub(Line, buf, 0, len - rlen);
4333 StrBufCutLeft(buf, len + 1);
4338 if (buf->BufSize - buf->BufUsed < 10)
4339 IncreaseBuf(buf, 1, -1);
4341 fdflags = fcntl(*fd, F_GETFL);
4342 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4344 while ((nSuccessLess < timeout) && (pch == NULL)) {
4346 tv.tv_sec = selectresolution;
4351 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4352 *Error = strerror(errno);
4358 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4363 &buf->buf[buf->BufUsed],
4364 buf->BufSize - buf->BufUsed - 1);
4366 *Error = strerror(errno);
4371 else if (rlen > 0) {
4373 buf->BufUsed += rlen;
4374 buf->buf[buf->BufUsed] = '\0';
4375 pch = strchr(buf->buf, '\n');
4376 if ((pch == NULL) &&
4377 (buf->BufUsed + 10 > buf->BufSize) &&
4378 (IncreaseBuf(buf, 1, -1) == -1))
4386 len = pch - buf->buf;
4387 if (len > 0 && (*(pch - 1) == '\r') )
4389 StrBufSub(Line, buf, 0, len - rlen);
4390 StrBufCutLeft(buf, len + 1);
4397 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4398 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4399 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4401 * @ingroup StrBuf_BufferedIO
4402 * @brief Read a line from socket
4403 * flushes and closes the FD on error
4404 * @param Line where to append our Line read from the fd / I/O Buffer;
4405 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4406 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4407 * @param fd pointer to the filedescriptor to read
4408 * @param timeout number of successless selects until we bail out
4409 * @param selectresolution how long to wait on each select
4410 * @param Error strerror() on error
4411 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4413 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4418 int selectresolution,
4421 const char *pche = NULL;
4422 const char *pos = NULL;
4424 int len, rlen, retlen;
4425 int nSuccessLess = 0;
4427 const char *pch = NULL;
4433 if ((Line == NULL) ||
4440 *Error = ErrRBLF_PreConditionFailed;
4445 if ((IOBuf->BufUsed > 0) &&
4447 (pos < IOBuf->buf + IOBuf->BufUsed))
4451 pche = IOBuf->buf + IOBuf->BufUsed;
4455 while ((pch < pche) && (*pch != '\n'))
4457 if (Line->BufUsed + 10 > Line->BufSize)
4460 apos = pcht - Line->buf;
4462 IncreaseBuf(Line, 1, -1);
4463 pcht = Line->buf + apos;
4471 if (len > 0 && (*(pch - 1) == '\r') )
4480 if ((pch >= pche) || (*pch == '\0'))
4488 if ((pch != NULL) &&
4491 if (pch + 1 >= pche) {
4504 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4506 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4507 IncreaseBuf(IOBuf, 1, -1);
4509 fdflags = fcntl(*fd, F_GETFL);
4510 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4513 while ((nSuccessLess < timeout) &&
4523 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4524 *Error = strerror(errno);
4528 *Error = ErrRBLF_SelectFailed;
4531 if (! FD_ISSET(*fd, &rfds) != 0) {
4537 &IOBuf->buf[IOBuf->BufUsed],
4538 IOBuf->BufSize - IOBuf->BufUsed - 1);
4540 *Error = strerror(errno);
4545 else if (rlen > 0) {
4547 pLF = IOBuf->buf + IOBuf->BufUsed;
4548 IOBuf->BufUsed += rlen;
4549 IOBuf->buf[IOBuf->BufUsed] = '\0';
4551 pche = IOBuf->buf + IOBuf->BufUsed;
4553 while ((pLF < pche) && (*pLF != '\n'))
4555 if ((pLF >= pche) || (*pLF == '\0'))
4558 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4562 if (pLF != NULL) apos = pLF - IOBuf->buf;
4563 IncreaseBuf(IOBuf, 1, -1);
4564 if (pLF != NULL) pLF = IOBuf->buf + apos;
4578 if (len > 0 && (*(pLF - 1) == '\r') )
4580 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4581 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4587 return retlen + len;
4589 *Error = ErrRBLF_NotEnoughSentFromServer;
4594 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4596 * @ingroup StrBuf_IO
4597 * @brief Input binary data from socket
4598 * flushes and closes the FD on error
4599 * @param Buf the buffer to get the input to
4600 * @param fd pointer to the filedescriptor to read
4601 * @param append Append to an existing string or replace?
4602 * @param nBytes the maximal number of bytes to read
4603 * @param Error strerror() on error
4604 * @returns numbers of chars read
4606 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4617 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4619 *Error = ErrRBLF_BLOBPreConditionFailed;
4624 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4625 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4627 ptr = Buf->buf + Buf->BufUsed;
4629 fdflags = fcntl(*fd, F_GETFL);
4630 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4632 while ((nRead < nBytes) &&
4642 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4643 *Error = strerror(errno);
4647 *Error = ErrRBLF_SelectFailed;
4650 if (! FD_ISSET(*fd, &rfds) != 0) {
4656 if ((rlen = read(*fd,
4658 nBytes - nRead)) == -1) {
4661 *Error = strerror(errno);
4666 Buf->BufUsed += rlen;
4668 Buf->buf[Buf->BufUsed] = '\0';
4672 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4673 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4675 * @ingroup StrBuf_BufferedIO
4676 * @brief Input binary data from socket
4677 * flushes and closes the FD on error
4678 * @param Blob put binary thing here
4679 * @param IOBuf the buffer to get the input to
4680 * @param Pos offset inside of IOBuf
4681 * @param fd pointer to the filedescriptor to read
4682 * @param append Append to an existing string or replace?
4683 * @param nBytes the maximal number of bytes to read
4684 * @param check whether we should search for '000\n' terminators in case of timeouts
4685 * @param Error strerror() on error
4686 * @returns numbers of chars read
4688 int StrBufReadBLOBBuffered(StrBuf *Blob,
4701 int nAlreadyRead = 0;
4706 int nSuccessLess = 0;
4709 if ((Blob == NULL) ||
4716 *Error = ErrRBB_BLOBFPreConditionFailed;
4722 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4723 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4728 rlen = pos - IOBuf->buf;
4729 rlen = IOBuf->BufUsed - rlen;
4732 if ((IOBuf->BufUsed > 0) &&
4734 (pos < IOBuf->buf + IOBuf->BufUsed))
4736 if (rlen < nBytes) {
4737 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4738 Blob->BufUsed += rlen;
4739 Blob->buf[Blob->BufUsed] = '\0';
4740 nAlreadyRead = nRead = rlen;
4743 if (rlen >= nBytes) {
4744 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4745 Blob->BufUsed += nBytes;
4746 Blob->buf[Blob->BufUsed] = '\0';
4747 if (rlen == nBytes) {
4759 if (IOBuf->BufSize < nBytes - nRead)
4760 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4763 fdflags = fcntl(*fd, F_GETFL);
4764 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4772 while ((nSuccessLess < MaxTries) &&
4782 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4783 *Error = strerror(errno);
4787 *Error = ErrRBLF_SelectFailed;
4790 if (! FD_ISSET(*fd, &rfds) != 0) {
4797 IOBuf->BufSize - (ptr - IOBuf->buf));
4801 *Error = strerror(errno);
4804 else if (rlen == 0){
4805 if ((check == NNN_TERM) &&
4807 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4809 StrBufPlain(Blob, HKEY("\n000\n"));
4810 StrBufCutRight(Blob, 5);
4811 return Blob->BufUsed;
4813 else if (!IsNonBlock)
4815 else if (nSuccessLess > MaxTries) {
4817 *Error = ErrRBB_too_many_selects;
4821 else if (rlen > 0) {
4825 IOBuf->BufUsed += rlen;
4828 if (nSuccessLess >= MaxTries) {
4830 *Error = ErrRBB_too_many_selects;
4834 if (nRead > nBytes) {
4835 *Pos = IOBuf->buf + nBytes;
4837 Blob->buf[Blob->BufUsed] = '\0';
4838 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4842 return nRead + nAlreadyRead;
4846 * @ingroup StrBuf_IO
4847 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4848 * @param LineBuf your line will be copied here.
4849 * @param Buf BLOB with lines of text...
4850 * @param Ptr moved arround to keep the next-line across several iterations
4851 * has to be &NULL on start; will be &NotNULL on end of buffer
4852 * @returns size of remaining buffer
4854 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4856 const char *aptr, *ptr, *eptr;
4859 if ((Buf == NULL) ||
4860 (*Ptr == StrBufNOTNULL) ||
4862 (LineBuf->buf == NULL))
4864 *Ptr = StrBufNOTNULL;
4868 FlushStrBuf(LineBuf);
4870 ptr = aptr = Buf->buf;
4874 optr = LineBuf->buf;
4875 eptr = Buf->buf + Buf->BufUsed;
4876 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4878 while ((ptr <= eptr) &&
4885 LineBuf->BufUsed = optr - LineBuf->buf;
4886 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4887 optr = LineBuf->buf + LineBuf->BufUsed;
4888 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4892 if ((ptr >= eptr) && (optr > LineBuf->buf))
4894 LineBuf->BufUsed = optr - LineBuf->buf;
4896 if ((ptr <= eptr) && (*ptr == '\r'))
4898 if ((ptr <= eptr) && (*ptr == '\n'))
4905 *Ptr = StrBufNOTNULL;
4908 return Buf->BufUsed - (ptr - Buf->buf);
4913 * @ingroup StrBuf_IO
4914 * @brief removes double slashes from pathnames
4915 * @param Dir directory string to filter
4916 * @param RemoveTrailingSlash allows / disallows trailing slashes
4918 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4924 while (!IsEmptyStr(a)) {
4936 if ((RemoveTrailingSlash) &&
4942 Dir->BufUsed = b - Dir->buf;