2 * Copyright (c) 1987-2013 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #include "libcitadel.h"
49 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
50 const Bytef * source, uLong sourceLen, int level);
52 int BaseStrBufSize = 64;
55 const char *StrBufNOTNULL = ((char*) NULL) - 1;
57 const char HexList[256][3] = {
58 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
59 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
60 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
61 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
62 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
63 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
64 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
65 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
66 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
67 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
68 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
69 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
70 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
71 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
72 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
73 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
76 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
77 * StrBuf is a versatile class, aiding the handling of dynamic strings
78 * * reduce de/reallocations
79 * * reduce the need to remeasure it
80 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
81 * * allow asyncroneous IO for line and Blob based operations
82 * * reduce the use of memove in those
83 * * Quick filling in several operations with append functions
87 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
92 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
94 * use these operators to interfere with code demanding char*;
95 * if you need to own the content, smash me. Avoid, since we loose the length information.
99 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
101 * operations to get your Strings into a StrBuf, manipulating them, or appending
104 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
106 * Quick tokenizer; demands of the user to pull its tokens in sequence
110 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
112 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
116 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
118 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
119 * External Cursor to maintain the current read position inside of the buffer
120 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
124 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
130 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
132 * these functions translate the content of a buffer into another representation;
133 * some are combined Fillers and encoders
137 * Private Structure for the Stringbuffer
140 char *buf; /**< the pointer to the dynamic buffer */
141 long BufSize; /**< how many spcae do we optain */
142 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
143 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
145 long nIncreases; /**< for profiling; cound how many times we needed more */
146 char bt [SIZ]; /**< Stacktrace of last increase */
147 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
152 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
153 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
156 #ifdef HAVE_BACKTRACE
157 static void StrBufBacktrace(StrBuf *Buf, int which)
161 void *stack_frames[50];
166 pstart = pch = Buf->bt;
168 pstart = pch = Buf->bt_lastinc;
169 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
170 strings = backtrace_symbols(stack_frames, size);
171 for (i = 0; i < size; i++) {
173 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
175 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
184 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
186 if (hFreeDbglog == -1){
187 pid_t pid = getpid();
189 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
190 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
192 if ((*FreeMe)->nIncreases > 0)
196 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
198 (*FreeMe)->nIncreases,
202 (*FreeMe)->bt_lastinc);
203 n = write(hFreeDbglog, buf, n);
209 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
213 n = write(hFreeDbglog, buf, n);
217 void dbg_IncreaseBuf(StrBuf *IncMe)
220 #ifdef HAVE_BACKTRACE
221 StrBufBacktrace(Buf, 1);
225 void dbg_Init(StrBuf *Buf)
229 Buf->bt_lastinc[0] = '\0';
230 #ifdef HAVE_BACKTRACE
231 StrBufBacktrace(Buf, 0);
237 #define dbg_FreeStrBuf(a, b)
238 #define dbg_IncreaseBuf(a)
245 * @brief swaps the contents of two StrBufs
246 * this is to be used to have cheap switched between a work-buffer and a target buffer
248 * @param B second one
250 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
254 memcpy(&C, A, sizeof(*A));
255 memcpy(A, B, sizeof(*B));
256 memcpy(B, &C, sizeof(C));
261 * @ingroup StrBuf_Cast
262 * @brief Cast operator to Plain String
263 * @note if the buffer is altered by StrBuf operations, this pointer may become
264 * invalid. So don't lean on it after altering the buffer!
265 * Since this operation is considered cheap, rather call it often than risking
266 * your pointer to become invalid!
267 * @param Str the string we want to get the c-string representation for
268 * @returns the Pointer to the Content. Don't mess with it!
270 inline const char *ChrPtr(const StrBuf *Str)
278 * @ingroup StrBuf_Cast
279 * @brief since we know strlen()'s result, provide it here.
280 * @param Str the string to return the length to
281 * @returns contentlength of the buffer
283 inline int StrLength(const StrBuf *Str)
285 return (Str != NULL) ? Str->BufUsed : 0;
289 * @ingroup StrBuf_DeConstructors
290 * @brief local utility function to resize the buffer
291 * @param Buf the buffer whichs storage we should increase
292 * @param KeepOriginal should we copy the original buffer or just start over with a new one
293 * @param DestSize what should fit in after?
295 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
298 size_t NewSize = Buf->BufSize * 2;
304 while ((NewSize <= DestSize) && (NewSize != 0))
310 NewBuf= (char*) malloc(NewSize);
314 if (KeepOriginal && (Buf->BufUsed > 0))
316 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
325 Buf->BufSize = NewSize;
327 dbg_IncreaseBuf(Buf);
333 * @ingroup StrBuf_DeConstructors
334 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
335 * @param Buf Buffer to shrink (has to be empty)
336 * @param ThreshHold if the buffer is bigger then this, its readjusted
337 * @param NewSize if we Shrink it, how big are we going to be afterwards?
339 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
342 (Buf->BufUsed == 0) &&
343 (Buf->BufSize < ThreshHold)) {
345 Buf->buf = (char*) malloc(NewSize);
347 Buf->BufSize = NewSize;
352 * @ingroup StrBuf_DeConstructors
353 * @brief shrink long term buffers to their real size so they don't waste memory
354 * @param Buf buffer to shrink
355 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
356 * @returns physical size of the buffer
358 long StrBufShrinkToFit(StrBuf *Buf, int Force)
363 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
367 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
371 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
372 Buf->BufSize = Buf->BufUsed + 1;
380 * @ingroup StrBuf_DeConstructors
381 * @brief Allocate a new buffer with default buffer size
382 * @returns the new stringbuffer
384 StrBuf* NewStrBuf(void)
388 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
392 NewBuf->buf = (char*) malloc(BaseStrBufSize);
393 if (NewBuf->buf == NULL)
398 NewBuf->buf[0] = '\0';
399 NewBuf->BufSize = BaseStrBufSize;
401 NewBuf->ConstBuf = 0;
409 * @ingroup StrBuf_DeConstructors
410 * @brief Copy Constructor; returns a duplicate of CopyMe
411 * @param CopyMe Buffer to faxmilate
412 * @returns the new stringbuffer
414 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
421 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
425 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
426 if (NewBuf->buf == NULL)
432 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
433 NewBuf->BufUsed = CopyMe->BufUsed;
434 NewBuf->BufSize = CopyMe->BufSize;
435 NewBuf->ConstBuf = 0;
443 * @ingroup StrBuf_DeConstructors
444 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
445 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
446 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
447 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
448 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
449 * @returns the new stringbuffer
451 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
455 if (CreateRelpaceMe == NULL)
460 if (*CreateRelpaceMe != NULL)
461 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
463 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
467 if (CopyFlushMe == NULL)
469 if (*CreateRelpaceMe != NULL)
470 FlushStrBuf(*CreateRelpaceMe);
472 *CreateRelpaceMe = NewStrBuf();
477 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
478 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
479 * be a big IO-Buffer...
481 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
483 if (*CreateRelpaceMe == NULL)
485 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
490 NewBuf = *CreateRelpaceMe;
493 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
497 if (*CreateRelpaceMe == NULL)
499 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
503 NewBuf = *CreateRelpaceMe;
504 SwapBuffers (NewBuf, CopyFlushMe);
507 FlushStrBuf(CopyFlushMe);
512 * @ingroup StrBuf_DeConstructors
513 * @brief create a new Buffer using an existing c-string
514 * this function should also be used if you want to pre-suggest
515 * the buffer size to allocate in conjunction with ptr == NULL
516 * @param ptr the c-string to copy; may be NULL to create a blank instance
517 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
518 * @returns the new stringbuffer
520 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
523 size_t Siz = BaseStrBufSize;
526 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
531 CopySize = strlen((ptr != NULL)?ptr:"");
535 while ((Siz <= CopySize) && (Siz != 0))
544 NewBuf->buf = (char*) malloc(Siz);
545 if (NewBuf->buf == NULL)
550 NewBuf->BufSize = Siz;
552 memcpy(NewBuf->buf, ptr, CopySize);
553 NewBuf->buf[CopySize] = '\0';
554 NewBuf->BufUsed = CopySize;
557 NewBuf->buf[0] = '\0';
560 NewBuf->ConstBuf = 0;
568 * @ingroup StrBuf_DeConstructors
569 * @brief Set an existing buffer from a c-string
570 * @param Buf buffer to load
571 * @param ptr c-string to put into
572 * @param nChars set to -1 if we should work 0-terminated
573 * @returns the new length of the string
575 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
590 CopySize = strlen(ptr);
594 while ((Siz <= CopySize) && (Siz != 0))
602 if (Siz != Buf->BufSize)
603 IncreaseBuf(Buf, 0, Siz);
604 memcpy(Buf->buf, ptr, CopySize);
605 Buf->buf[CopySize] = '\0';
606 Buf->BufUsed = CopySize;
613 * @ingroup StrBuf_DeConstructors
614 * @brief use strbuf as wrapper for a string constant for easy handling
615 * @param StringConstant a string to wrap
616 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
618 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
622 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
625 NewBuf->buf = (char*) StringConstant;
626 NewBuf->BufSize = SizeOfStrConstant;
627 NewBuf->BufUsed = SizeOfStrConstant;
628 NewBuf->ConstBuf = 1;
637 * @ingroup StrBuf_DeConstructors
638 * @brief flush the content of a Buf; keep its struct
639 * @param buf Buffer to flush
641 int FlushStrBuf(StrBuf *buf)
643 if ((buf == NULL) || (buf->buf == NULL))
653 * @ingroup StrBuf_DeConstructors
654 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
655 * @param buf Buffer to wipe
657 int FLUSHStrBuf(StrBuf *buf)
663 if (buf->BufUsed > 0) {
664 memset(buf->buf, 0, buf->BufUsed);
671 int hFreeDbglog = -1;
674 * @ingroup StrBuf_DeConstructors
675 * @brief Release a Buffer
676 * Its a double pointer, so it can NULL your pointer
677 * so fancy SIG11 appear instead of random results
678 * @param FreeMe Pointer Pointer to the buffer to free
680 void FreeStrBuf (StrBuf **FreeMe)
685 dbg_FreeStrBuf(FreeMe, 'F');
687 if (!(*FreeMe)->ConstBuf)
688 free((*FreeMe)->buf);
694 * @ingroup StrBuf_DeConstructors
695 * @brief flatten a Buffer to the Char * we return
696 * Its a double pointer, so it can NULL your pointer
697 * so fancy SIG11 appear instead of random results
698 * The Callee then owns the buffer and is responsible for freeing it.
699 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
700 * @returns the pointer of the buffer; Callee owns the memory thereafter.
702 char *SmashStrBuf (StrBuf **SmashMe)
706 if ((SmashMe == NULL) || (*SmashMe == NULL))
709 dbg_FreeStrBuf(SmashMe, 'S');
711 Ret = (*SmashMe)->buf;
718 * @ingroup StrBuf_DeConstructors
719 * @brief Release the buffer
720 * If you want put your StrBuf into a Hash, use this as Destructor.
721 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
723 void HFreeStrBuf (void *VFreeMe)
725 StrBuf *FreeMe = (StrBuf*)VFreeMe;
729 dbg_FreeStrBuf(SmashMe, 'H');
731 if (!FreeMe->ConstBuf)
737 /*******************************************************************************
738 * Simple string transformations *
739 *******************************************************************************/
743 * @brief Wrapper around atol
745 long StrTol(const StrBuf *Buf)
750 return atol(Buf->buf);
757 * @brief Wrapper around atoi
759 int StrToi(const StrBuf *Buf)
763 if (Buf->BufUsed > 0)
764 return atoi(Buf->buf);
771 * @brief Checks to see if the string is a pure number
772 * @param Buf The buffer to inspect
773 * @returns 1 if its a pure number, 0, if not.
775 int StrBufIsNumber(const StrBuf *Buf) {
777 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
780 strtoll(Buf->buf, &pEnd, 10);
781 if (pEnd == Buf->buf)
783 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
785 if (Buf->buf == pEnd)
791 * @ingroup StrBuf_Filler
792 * @brief modifies a Single char of the Buf
793 * You can point to it via char* or a zero-based integer
794 * @param Buf The buffer to manipulate
795 * @param ptr char* to zero; use NULL if unused
796 * @param nThChar zero based pointer into the string; use -1 if unused
797 * @param PeekValue The Character to place into the position
799 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
804 nThChar = ptr - Buf->buf;
805 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
807 Buf->buf[nThChar] = PeekValue;
812 * @ingroup StrBuf_Filler
813 * @brief modifies a range of chars of the Buf
814 * You can point to it via char* or a zero-based integer
815 * @param Buf The buffer to manipulate
816 * @param ptr char* to zero; use NULL if unused
817 * @param nThChar zero based pointer into the string; use -1 if unused
818 * @param nChars how many chars are to be flushed?
819 * @param PookValue The Character to place into that area
821 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
826 nThChar = ptr - Buf->buf;
827 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
829 if (nThChar + nChars > Buf->BufUsed)
830 nChars = Buf->BufUsed - nThChar;
832 memset(Buf->buf + nThChar, PookValue, nChars);
833 /* just to be shure... */
834 Buf->buf[Buf->BufUsed] = 0;
839 * @ingroup StrBuf_Filler
840 * @brief Append a StringBuffer to the buffer
841 * @param Buf Buffer to modify
842 * @param AppendBuf Buffer to copy at the end of our buffer
843 * @param Offset Should we start copying from an offset?
845 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
847 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
848 (Buf == NULL) || (Buf->buf == NULL))
851 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
854 AppendBuf->BufUsed + Buf->BufUsed);
856 memcpy(Buf->buf + Buf->BufUsed,
857 AppendBuf->buf + Offset,
858 AppendBuf->BufUsed - Offset);
859 Buf->BufUsed += AppendBuf->BufUsed - Offset;
860 Buf->buf[Buf->BufUsed] = '\0';
865 * @ingroup StrBuf_Filler
866 * @brief Append a C-String to the buffer
867 * @param Buf Buffer to modify
868 * @param AppendBuf Buffer to copy at the end of our buffer
869 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
870 * @param Offset Should we start copying from an offset?
872 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
875 long BufSizeRequired;
877 if ((AppendBuf == NULL) || (Buf == NULL))
881 aps = strlen(AppendBuf + Offset);
883 aps = AppendSize - Offset;
885 BufSizeRequired = Buf->BufUsed + aps + 1;
886 if (Buf->BufSize <= BufSizeRequired)
887 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
889 memcpy(Buf->buf + Buf->BufUsed,
893 Buf->buf[Buf->BufUsed] = '\0';
897 * @ingroup StrBuf_Filler
898 * @brief sprintf like function appending the formated string to the buffer
899 * vsnprintf version to wrap into own calls
900 * @param Buf Buffer to extend by format and Params
901 * @param format printf alike format to add
902 * @param ap va_list containing the items for format
904 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
912 if ((Buf == NULL) || (format == NULL))
915 BufSize = Buf->BufSize;
916 nWritten = Buf->BufSize + 1;
917 Offset = Buf->BufUsed;
918 newused = Offset + nWritten;
920 while (newused >= BufSize) {
922 nWritten = vsnprintf(Buf->buf + Offset,
923 Buf->BufSize - Offset,
926 newused = Offset + nWritten;
927 if (newused >= Buf->BufSize) {
928 if (IncreaseBuf(Buf, 1, newused) == -1)
929 return; /* TODO: error handling? */
930 newused = Buf->BufSize + 1;
933 Buf->BufUsed = Offset + nWritten;
934 BufSize = Buf->BufSize;
941 * @ingroup StrBuf_Filler
942 * @brief sprintf like function appending the formated string to the buffer
943 * @param Buf Buffer to extend by format and Params
944 * @param format printf alike format to add
946 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
954 if ((Buf == NULL) || (format == NULL))
957 BufSize = Buf->BufSize;
958 nWritten = Buf->BufSize + 1;
959 Offset = Buf->BufUsed;
960 newused = Offset + nWritten;
962 while (newused >= BufSize) {
963 va_start(arg_ptr, format);
964 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
965 Buf->BufSize - Buf->BufUsed,
968 newused = Buf->BufUsed + nWritten;
969 if (newused >= Buf->BufSize) {
970 if (IncreaseBuf(Buf, 1, newused) == -1)
971 return; /* TODO: error handling? */
972 newused = Buf->BufSize + 1;
975 Buf->BufUsed += nWritten;
976 BufSize = Buf->BufSize;
983 * @ingroup StrBuf_Filler
984 * @brief sprintf like function putting the formated string into the buffer
985 * @param Buf Buffer to extend by format and Parameters
986 * @param format printf alike format to add
988 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
993 if ((Buf == NULL) || (format == NULL))
996 nWritten = Buf->BufSize + 1;
997 while (nWritten >= Buf->BufSize) {
998 va_start(arg_ptr, format);
999 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1001 if (nWritten >= Buf->BufSize) {
1002 if (IncreaseBuf(Buf, 0, 0) == -1)
1003 return; /* TODO: error handling? */
1004 nWritten = Buf->BufSize + 1;
1007 Buf->BufUsed = nWritten ;
1012 * @ingroup StrBuf_Filler
1013 * @brief Callback for cURL to append the webserver reply to a buffer
1014 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1015 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1016 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1017 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1019 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1028 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1029 return size * nmemb;
1035 * @brief extracts a substring from Source into dest
1036 * @param dest buffer to place substring into
1037 * @param Source string to copy substring from
1038 * @param Offset chars to skip from start
1039 * @param nChars number of chars to copy
1040 * @returns the number of chars copied; may be different from nChars due to the size of Source
1042 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1044 size_t NCharsRemain;
1045 if (Offset > Source->BufUsed)
1051 if (Offset + nChars < Source->BufUsed)
1053 if ((nChars >= dest->BufSize) &&
1054 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1056 memcpy(dest->buf, Source->buf + Offset, nChars);
1057 dest->BufUsed = nChars;
1058 dest->buf[dest->BufUsed] = '\0';
1061 NCharsRemain = Source->BufUsed - Offset;
1062 if ((NCharsRemain >= dest->BufSize) &&
1063 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1065 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1066 dest->BufUsed = NCharsRemain;
1067 dest->buf[dest->BufUsed] = '\0';
1068 return NCharsRemain;
1073 * @brief Cut nChars from the start of the string
1074 * @param Buf Buffer to modify
1075 * @param nChars how many chars should be skipped?
1077 void StrBufCutLeft(StrBuf *Buf, int nChars)
1079 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1080 if (nChars >= Buf->BufUsed) {
1084 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1085 Buf->BufUsed -= nChars;
1086 Buf->buf[Buf->BufUsed] = '\0';
1091 * @brief Cut the trailing n Chars from the string
1092 * @param Buf Buffer to modify
1093 * @param nChars how many chars should be trunkated?
1095 void StrBufCutRight(StrBuf *Buf, int nChars)
1097 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1100 if (nChars >= Buf->BufUsed) {
1104 Buf->BufUsed -= nChars;
1105 Buf->buf[Buf->BufUsed] = '\0';
1110 * @brief Cut the string after n Chars
1111 * @param Buf Buffer to modify
1112 * @param AfternChars after how many chars should we trunkate the string?
1113 * @param At if non-null and points inside of our string, cut it there.
1115 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1117 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1119 AfternChars = At - Buf->buf;
1122 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1124 Buf->BufUsed = AfternChars;
1125 Buf->buf[Buf->BufUsed] = '\0';
1131 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1132 * @param Buf the string to modify
1134 void StrBufTrim(StrBuf *Buf)
1137 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1139 while ((Buf->BufUsed > 0) &&
1140 isspace(Buf->buf[Buf->BufUsed - 1]))
1144 Buf->buf[Buf->BufUsed] = '\0';
1146 if (Buf->BufUsed == 0) return;
1148 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1151 if (delta > 0) StrBufCutLeft(Buf, delta);
1155 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1156 * @param Buf the string to modify
1158 void StrBufSpaceToBlank(StrBuf *Buf)
1162 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1165 pche = pch + Buf->BufUsed;
1174 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1179 if ((Buf == NULL) || (Buf->buf == NULL)) {
1183 pRight = strchr(Buf->buf, rightboundary);
1184 if (pRight != NULL) {
1185 StrBufCutAt(Buf, 0, pRight);
1188 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1189 if (pLeft != NULL) {
1190 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1196 * @ingroup StrBuf_Filler
1197 * @brief uppercase the contents of a buffer
1198 * @param Buf the buffer to translate
1200 void StrBufUpCase(StrBuf *Buf)
1204 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1207 pche = pch + Buf->BufUsed;
1208 while (pch < pche) {
1209 *pch = toupper(*pch);
1216 * @ingroup StrBuf_Filler
1217 * @brief lowercase the contents of a buffer
1218 * @param Buf the buffer to translate
1220 void StrBufLowerCase(StrBuf *Buf)
1224 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1227 pche = pch + Buf->BufUsed;
1228 while (pch < pche) {
1229 *pch = tolower(*pch);
1235 /*******************************************************************************
1236 * a tokenizer that kills, maims, and destroys *
1237 *******************************************************************************/
1240 * @ingroup StrBuf_Tokenizer
1241 * @brief Replace a token at a given place with a given length by another token with given length
1242 * @param Buf String where to work on
1243 * @param where where inside of the Buf is the search-token
1244 * @param HowLong How long is the token to be replaced
1245 * @param Repl Token to insert at 'where'
1246 * @param ReplLen Length of repl
1247 * @returns -1 if fail else length of resulting Buf
1249 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1250 const char *Repl, long ReplLen)
1253 if ((Buf == NULL) ||
1254 (where > Buf->BufUsed) ||
1255 (where + HowLong > Buf->BufUsed))
1258 if (where + ReplLen - HowLong > Buf->BufSize)
1259 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1262 memmove(Buf->buf + where + ReplLen,
1263 Buf->buf + where + HowLong,
1264 Buf->BufUsed - where - HowLong);
1266 memcpy(Buf->buf + where,
1269 Buf->BufUsed += ReplLen - HowLong;
1271 return Buf->BufUsed;
1275 * @ingroup StrBuf_Tokenizer
1276 * @brief Counts the numbmer of tokens in a buffer
1277 * @param source String to count tokens in
1278 * @param tok Tokenizer char to count
1279 * @returns numbers of tokenizer chars found
1281 int StrBufNum_tokens(const StrBuf *source, char tok)
1285 if ((source == NULL) || (source->BufUsed == 0))
1287 if ((source->BufUsed == 1) && (*source->buf == tok))
1291 pche = pch + source->BufUsed;
1302 * @ingroup StrBuf_Tokenizer
1303 * @brief a string tokenizer
1304 * @param Source StringBuffer to read into
1305 * @param parmnum n'th Parameter to remove
1306 * @param separator tokenizer character
1307 * @returns -1 if not found, else length of token.
1309 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1312 char *d, *s, *end; /* dest, source */
1315 /* Find desired @parameter */
1316 end = Source->buf + Source->BufUsed;
1318 while ((d <= end) &&
1321 /* End of string, bail! */
1326 if (*d == separator) {
1331 if ((d == NULL) || (d >= end))
1332 return 0; /* @Parameter not found */
1334 /* Find next @parameter */
1336 while ((s <= end) &&
1337 (*s && *s != separator))
1341 if (*s == separator)
1345 /* Hack and slash */
1350 memmove(d, s, Source->BufUsed - (s - Source->buf));
1351 Source->BufUsed += ReducedBy;
1352 Source->buf[Source->BufUsed] = '\0';
1354 else if (d == Source->buf) {
1356 Source->BufUsed = 0;
1360 Source->BufUsed += ReducedBy;
1371 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1373 const StrBuf Temp = {
1386 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1390 * @ingroup StrBuf_Tokenizer
1391 * @brief a string tokenizer
1392 * @param dest Destination StringBuffer
1393 * @param Source StringBuffer to read into
1394 * @param parmnum n'th Parameter to extract
1395 * @param separator tokenizer character
1396 * @returns -1 if not found, else length of token.
1398 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1400 const char *s, *e; //* source * /
1401 int len = 0; //* running total length of extracted string * /
1402 int current_token = 0; //* token currently being processed * /
1405 dest->buf[0] = '\0';
1411 if ((Source == NULL) || (Source->BufUsed ==0)) {
1415 e = s + Source->BufUsed;
1418 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1420 while ((s < e) && !IsEmptyStr(s)) {
1421 if (*s == separator) {
1424 if (len >= dest->BufSize) {
1425 dest->BufUsed = len;
1426 if (IncreaseBuf(dest, 1, -1) < 0) {
1431 if ( (current_token == parmnum) &&
1432 (*s != separator)) {
1433 dest->buf[len] = *s;
1436 else if (current_token > parmnum) {
1442 dest->buf[len] = '\0';
1443 dest->BufUsed = len;
1445 if (current_token < parmnum) {
1446 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1449 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1458 * @ingroup StrBuf_Tokenizer
1459 * @brief a string tokenizer to fetch an integer
1460 * @param Source String containing tokens
1461 * @param parmnum n'th Parameter to extract
1462 * @param separator tokenizer character
1463 * @returns 0 if not found, else integer representation of the token
1465 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1475 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1482 * @ingroup StrBuf_Tokenizer
1483 * @brief a string tokenizer to fetch a long integer
1484 * @param Source String containing tokens
1485 * @param parmnum n'th Parameter to extract
1486 * @param separator tokenizer character
1487 * @returns 0 if not found, else long integer representation of the token
1489 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1499 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1507 * @ingroup StrBuf_Tokenizer
1508 * @brief a string tokenizer to fetch an unsigned long
1509 * @param Source String containing tokens
1510 * @param parmnum n'th Parameter to extract
1511 * @param separator tokenizer character
1512 * @returns 0 if not found, else unsigned long representation of the token
1514 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1525 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1529 return (unsigned long) atol(pnum);
1538 * @ingroup StrBuf_NextTokenizer
1539 * @brief a string tokenizer; Bounds checker
1540 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1541 * @param Source our tokenbuffer
1542 * @param pStart the token iterator pointer to inspect
1543 * @returns whether the revolving pointer is inside of the search range
1545 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1547 if ((Source == NULL) ||
1548 (*pStart == StrBufNOTNULL) ||
1549 (Source->BufUsed == 0))
1553 if (*pStart == NULL)
1557 else if (*pStart > Source->buf + Source->BufUsed)
1561 else if (*pStart <= Source->buf)
1570 * @ingroup StrBuf_NextTokenizer
1571 * @brief a string tokenizer
1572 * @param dest Destination StringBuffer
1573 * @param Source StringBuffer to read into
1574 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1575 * @param separator tokenizer
1576 * @returns -1 if not found, else length of token.
1578 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1580 const char *s; /* source */
1581 const char *EndBuffer; /* end stop of source buffer */
1582 int current_token = 0; /* token currently being processed */
1583 int len = 0; /* running total length of extracted string */
1585 if ((Source == NULL) ||
1586 (Source->BufUsed == 0) )
1588 *pStart = StrBufNOTNULL;
1594 EndBuffer = Source->buf + Source->BufUsed;
1598 dest->buf[0] = '\0';
1603 *pStart = EndBuffer + 1;
1607 if (*pStart == NULL)
1609 *pStart = Source->buf; /* we're starting to examine this buffer. */
1611 else if ((*pStart < Source->buf) ||
1612 (*pStart > EndBuffer ) )
1614 return -1; /* no more tokens to find. */
1618 /* start to find the next token */
1619 while ((s <= EndBuffer) &&
1620 (current_token == 0) )
1622 if (*s == separator)
1624 /* we found the next token */
1628 if (len >= dest->BufSize)
1630 /* our Dest-buffer isn't big enough, increase it. */
1631 dest->BufUsed = len;
1633 if (IncreaseBuf(dest, 1, -1) < 0) {
1634 /* WHUT? no more mem? bail out. */
1641 if ( (current_token == 0 ) && /* are we in our target token? */
1642 (!IsEmptyStr(s) ) &&
1643 (separator != *s) ) /* don't copy the token itself */
1645 dest->buf[len] = *s; /* Copy the payload */
1646 ++len; /* remember the bigger size. */
1652 /* did we reach the end? */
1653 if ((s > EndBuffer)) {
1654 EndBuffer = StrBufNOTNULL;
1655 *pStart = EndBuffer;
1658 *pStart = s; /* remember the position for the next run */
1661 /* sanitize our extracted token */
1662 dest->buf[len] = '\0';
1663 dest->BufUsed = len;
1670 * @ingroup StrBuf_NextTokenizer
1671 * @brief a string tokenizer
1672 * @param Source StringBuffer to read from
1673 * @param pStart pointer to the end of the last token. Feed with NULL.
1674 * @param separator tokenizer character
1675 * @param nTokens number of tokens to fastforward over
1676 * @returns -1 if not found, else length of token.
1678 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1680 const char *s, *EndBuffer; //* source * /
1681 int len = 0; //* running total length of extracted string * /
1682 int current_token = 0; //* token currently being processed * /
1684 if ((Source == NULL) ||
1685 (Source->BufUsed ==0)) {
1689 return Source->BufUsed;
1691 if (*pStart == NULL)
1692 *pStart = Source->buf;
1694 EndBuffer = Source->buf + Source->BufUsed;
1696 if ((*pStart < Source->buf) ||
1697 (*pStart > EndBuffer)) {
1705 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1707 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1708 if (*s == separator) {
1711 if (current_token >= nTokens) {
1723 * @ingroup StrBuf_NextTokenizer
1724 * @brief a string tokenizer to fetch an integer
1725 * @param Source StringBuffer to read from
1726 * @param pStart Cursor on the tokenstring
1727 * @param separator tokenizer character
1728 * @returns 0 if not found, else integer representation of the token
1730 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1740 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1747 * @ingroup StrBuf_NextTokenizer
1748 * @brief a string tokenizer to fetch a long integer
1749 * @param Source StringBuffer to read from
1750 * @param pStart Cursor on the tokenstring
1751 * @param separator tokenizer character
1752 * @returns 0 if not found, else long integer representation of the token
1754 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1764 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1772 * @ingroup StrBuf_NextTokenizer
1773 * @brief a string tokenizer to fetch an unsigned long
1774 * @param Source StringBuffer to read from
1775 * @param pStart Cursor on the tokenstring
1776 * @param separator tokenizer character
1777 * @returns 0 if not found, else unsigned long representation of the token
1779 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1790 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1794 return (unsigned long) atol(pnum);
1804 /*******************************************************************************
1805 * Escape Appending *
1806 *******************************************************************************/
1809 * @ingroup StrBuf_DeEnCoder
1810 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1811 * @param OutBuf the output buffer
1812 * @param In Buffer to encode
1813 * @param PlainIn way in from plain old c strings
1815 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1817 const char *pch, *pche;
1821 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1823 if (PlainIn != NULL) {
1824 len = strlen(PlainIn);
1830 pche = pch + In->BufUsed;
1837 pt = OutBuf->buf + OutBuf->BufUsed;
1838 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1840 while (pch < pche) {
1842 IncreaseBuf(OutBuf, 1, -1);
1843 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1844 pt = OutBuf->buf + OutBuf->BufUsed;
1847 if((*pch >= 'a' && *pch <= 'z') ||
1848 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1849 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1850 (*pch == '!') || (*pch == '_') ||
1851 (*pch == ',') || (*pch == '.'))
1858 *(pt + 1) = HexList[(unsigned char)*pch][0];
1859 *(pt + 2) = HexList[(unsigned char)*pch][1];
1861 OutBuf->BufUsed += 3;
1869 * @ingroup StrBuf_DeEnCoder
1870 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1871 * @param OutBuf the output buffer
1872 * @param In Buffer to encode
1873 * @param PlainIn way in from plain old c strings
1875 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1877 const char *pch, *pche;
1881 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1883 if (PlainIn != NULL) {
1884 len = strlen(PlainIn);
1890 pche = pch + In->BufUsed;
1897 pt = OutBuf->buf + OutBuf->BufUsed;
1898 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1900 while (pch < pche) {
1902 IncreaseBuf(OutBuf, 1, -1);
1903 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1904 pt = OutBuf->buf + OutBuf->BufUsed;
1907 if((*pch >= 'a' && *pch <= 'z') ||
1908 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1909 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1910 (*pch == '!') || (*pch == '_') ||
1911 (*pch == ',') || (*pch == '.'))
1918 *(pt + 1) = HexList[(unsigned char)*pch][0];
1919 *(pt + 2) = HexList[(unsigned char)*pch][1];
1921 OutBuf->BufUsed += 3;
1929 * @ingroup StrBuf_DeEnCoder
1930 * @brief append a string in hex encoding to the buffer
1931 * @param OutBuf the output buffer
1932 * @param In Buffer to encode
1933 * @param PlainIn way in from plain old c strings
1934 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1936 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1938 const unsigned char *pch, *pche;
1942 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1944 if (PlainIn != NULL) {
1946 len = strlen((const char*)PlainIn);
1953 pch = (const unsigned char*)In->buf;
1954 pche = pch + In->BufUsed;
1961 pt = OutBuf->buf + OutBuf->BufUsed;
1962 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1964 while (pch < pche) {
1966 IncreaseBuf(OutBuf, 1, -1);
1967 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1968 pt = OutBuf->buf + OutBuf->BufUsed;
1971 *pt = HexList[*pch][0];
1973 *pt = HexList[*pch][1];
1974 pt ++; pch ++; OutBuf->BufUsed += 2;
1979 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
1986 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1988 if (PlainIn != NULL) {
1990 len = strlen(PlainIn);
2003 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2005 if (ExpectLen > OutBuf->BufSize)
2006 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2009 pt = OutBuf->buf + OutBuf->BufUsed;
2011 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2014 OutBuf->BufUsed += len;
2019 * @ingroup StrBuf_DeEnCoder
2020 * @brief append a string in hex encoding to the buffer
2021 * @param OutBuf the output buffer
2022 * @param In Buffer to encode
2023 * @param PlainIn way in from plain old c strings
2025 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2027 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2031 * @ingroup StrBuf_DeEnCoder
2032 * @brief Append a string, escaping characters which have meaning in HTML.
2034 * @param Target target buffer
2035 * @param Source source buffer; set to NULL if you just have a C-String
2036 * @param PlainIn Plain-C string to append; set to NULL if unused
2037 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2038 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2039 * if set to 2, linebreaks are replaced by <br/>
2041 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2043 const char *aptr, *eiptr;
2047 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2050 if (PlainIn != NULL) {
2052 len = strlen(PlainIn);
2057 eiptr = aptr + Source->BufUsed;
2058 len = Source->BufUsed;
2064 bptr = Target->buf + Target->BufUsed;
2065 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2067 while (aptr < eiptr){
2069 IncreaseBuf(Target, 1, -1);
2070 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2071 bptr = Target->buf + Target->BufUsed;
2074 memcpy(bptr, "<", 4);
2076 Target->BufUsed += 4;
2078 else if (*aptr == '>') {
2079 memcpy(bptr, ">", 4);
2081 Target->BufUsed += 4;
2083 else if (*aptr == '&') {
2084 memcpy(bptr, "&", 5);
2086 Target->BufUsed += 5;
2088 else if (*aptr == '"') {
2089 memcpy(bptr, """, 6);
2091 Target->BufUsed += 6;
2093 else if (*aptr == '\'') {
2094 memcpy(bptr, "'", 5);
2096 Target->BufUsed += 5;
2098 else if (*aptr == LB) {
2103 else if (*aptr == RB) {
2108 else if (*aptr == QU) {
2113 else if ((*aptr == 32) && (nbsp == 1)) {
2114 memcpy(bptr, " ", 6);
2116 Target->BufUsed += 6;
2118 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2119 *bptr='\0'; /* nothing */
2121 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2122 memcpy(bptr, "<br/>", 11);
2124 Target->BufUsed += 11;
2128 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2129 *bptr='\0'; /* nothing */
2139 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2141 return Target->BufUsed;
2145 * @ingroup StrBuf_DeEnCoder
2146 * @brief Append a string, escaping characters which have meaning in HTML.
2147 * Converts linebreaks into blanks; escapes single quotes
2148 * @param Target target buffer
2149 * @param Source source buffer; set to NULL if you just have a C-String
2150 * @param PlainIn Plain-C string to append; set to NULL if unused
2152 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2154 const char *aptr, *eiptr;
2158 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2161 if (PlainIn != NULL) {
2163 len = strlen(PlainIn);
2168 eiptr = aptr + Source->BufUsed;
2169 len = Source->BufUsed;
2175 eptr = Target->buf + Target->BufSize - 8;
2176 tptr = Target->buf + Target->BufUsed;
2178 while (aptr < eiptr){
2180 IncreaseBuf(Target, 1, -1);
2181 eptr = Target->buf + Target->BufSize - 8;
2182 tptr = Target->buf + Target->BufUsed;
2185 if (*aptr == '\n') {
2189 else if (*aptr == '\r') {
2193 else if (*aptr == '\'') {
2199 Target->BufUsed += 5;
2212 * @ingroup StrBuf_DeEnCoder
2213 * @brief Append a string, escaping characters which have meaning in ICAL.
2215 * @param Target target buffer
2216 * @param Source source buffer; set to NULL if you just have a C-String
2217 * @param PlainIn Plain-C string to append; set to NULL if unused
2219 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2221 const char *aptr, *eiptr;
2225 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2228 if (PlainIn != NULL) {
2230 len = strlen(PlainIn);
2235 eiptr = aptr + Source->BufUsed;
2236 len = Source->BufUsed;
2242 eptr = Target->buf + Target->BufSize - 8;
2243 tptr = Target->buf + Target->BufUsed;
2245 while (aptr < eiptr){
2246 if(tptr + 3 >= eptr) {
2247 IncreaseBuf(Target, 1, -1);
2248 eptr = Target->buf + Target->BufSize - 8;
2249 tptr = Target->buf + Target->BufUsed;
2252 if (*aptr == '\n') {
2259 else if (*aptr == '\r') {
2266 else if (*aptr == ',') {
2282 * @ingroup StrBuf_DeEnCoder
2283 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2285 * @param Target target buffer
2286 * @param Source source buffer; set to NULL if you just have a C-String
2287 * @param PlainIn Plain-C string to append; set to NULL if unused
2288 * @returns size of result or -1
2290 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2292 const char *aptr, *eiptr;
2297 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2300 if (PlainIn != NULL) {
2302 len = strlen(PlainIn);
2307 eiptr = aptr + Source->BufUsed;
2308 len = Source->BufUsed;
2314 bptr = Target->buf + Target->BufUsed;
2315 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2317 while (aptr < eiptr){
2319 IncreaseBuf(Target, 1, -1);
2320 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2321 bptr = Target->buf + Target->BufUsed;
2325 memcpy(bptr, HKEY("\\n"));
2327 Target->BufUsed += 2;
2330 memcpy(bptr, HKEY("\\r"));
2332 Target->BufUsed += 2;
2339 Target->BufUsed += 2;
2342 if ((*(aptr + 1) == 'u') &&
2343 isxdigit(*(aptr + 2)) &&
2344 isxdigit(*(aptr + 3)) &&
2345 isxdigit(*(aptr + 4)) &&
2346 isxdigit(*(aptr + 5)))
2347 { /* oh, a unicode escaper. let it pass through. */
2348 memcpy(bptr, aptr, 6);
2351 Target->BufUsed += 6;
2359 Target->BufUsed += 2;
2367 Target->BufUsed += 2;
2374 Target->BufUsed += 2;
2381 Target->BufUsed += 2;
2384 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2385 while (IsUtf8Sequence > 0){
2388 if (--IsUtf8Sequence)
2396 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2398 return Target->BufUsed;
2402 * @ingroup StrBuf_DeEnCoder
2403 * @brief Append a string, escaping characters which have meaning in HTML + json.
2405 * @param Target target buffer
2406 * @param Source source buffer; set to NULL if you just have a C-String
2407 * @param PlainIn Plain-C string to append; set to NULL if unused
2408 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2409 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2410 * if set to 2, linebreaks are replaced by <br/>
2412 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2414 const char *aptr, *eiptr;
2417 int IsUtf8Sequence = 0;
2419 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2422 if (PlainIn != NULL) {
2424 len = strlen(PlainIn);
2429 eiptr = aptr + Source->BufUsed;
2430 len = Source->BufUsed;
2436 bptr = Target->buf + Target->BufUsed;
2437 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2439 while (aptr < eiptr){
2441 IncreaseBuf(Target, 1, -1);
2442 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2443 bptr = Target->buf + Target->BufUsed;
2447 memcpy(bptr, HKEY("<"));
2449 Target->BufUsed += 4;
2452 memcpy(bptr, HKEY(">"));
2454 Target->BufUsed += 4;
2457 memcpy(bptr, HKEY("&"));
2459 Target->BufUsed += 5;
2472 switch (nolinebreaks) {
2474 *bptr='\0'; /* nothing */
2477 memcpy(bptr, HKEY("<br/>"));
2479 Target->BufUsed += 11;
2482 memcpy(bptr, HKEY("\\n"));
2484 Target->BufUsed += 2;
2488 switch (nolinebreaks) {
2491 *bptr='\0'; /* nothing */
2494 memcpy(bptr, HKEY("\\r"));
2496 Target->BufUsed += 2;
2506 Target->BufUsed += 2;
2509 if ((*(aptr + 1) == 'u') &&
2510 isxdigit(*(aptr + 2)) &&
2511 isxdigit(*(aptr + 3)) &&
2512 isxdigit(*(aptr + 4)) &&
2513 isxdigit(*(aptr + 5)))
2514 { /* oh, a unicode escaper. let it pass through. */
2515 memcpy(bptr, aptr, 6);
2518 Target->BufUsed += 6;
2526 Target->BufUsed += 2;
2534 Target->BufUsed += 2;
2541 Target->BufUsed += 2;
2548 Target->BufUsed += 2;
2552 memcpy(bptr, HKEY(" "));
2554 Target->BufUsed += 6;
2558 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2559 while (IsUtf8Sequence > 0){
2562 if (--IsUtf8Sequence)
2570 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2572 return Target->BufUsed;
2577 * @ingroup StrBuf_DeEnCoder
2578 * @brief replace all non-Ascii characters by another
2579 * @param Buf buffer to inspect
2580 * @param repl charater to stamp over non ascii chars
2582 void StrBufAsciify(StrBuf *Buf, const char repl)
2586 for (offset = 0; offset < Buf->BufUsed; offset ++)
2587 if (!isascii(Buf->buf[offset]))
2588 Buf->buf[offset] = repl;
2593 * @ingroup StrBuf_DeEnCoder
2594 * @brief unhide special chars hidden to the HTML escaper
2595 * @param target buffer to put the unescaped string in
2596 * @param source buffer to unescape
2598 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2603 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2609 FlushStrBuf(target);
2611 len = source->BufUsed;
2612 for (a = 0; a < len; ++a) {
2613 if (target->BufUsed >= target->BufSize)
2614 IncreaseBuf(target, 1, -1);
2616 if (source->buf[a] == '=') {
2617 hex[0] = source->buf[a + 1];
2618 hex[1] = source->buf[a + 2];
2621 sscanf(hex, "%02x", &b);
2622 target->buf[target->BufUsed] = b;
2623 target->buf[++target->BufUsed] = 0;
2627 target->buf[target->BufUsed] = source->buf[a];
2628 target->buf[++target->BufUsed] = 0;
2635 * @ingroup StrBuf_DeEnCoder
2636 * @brief hide special chars from the HTML escapers and friends
2637 * @param target buffer to put the escaped string in
2638 * @param source buffer to escape
2640 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2645 FlushStrBuf(target);
2647 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2652 len = source->BufUsed;
2653 for (i=0; i<len; ++i) {
2654 if (target->BufUsed + 4 >= target->BufSize)
2655 IncreaseBuf(target, 1, -1);
2656 if ( (isalnum(source->buf[i])) ||
2657 (source->buf[i]=='-') ||
2658 (source->buf[i]=='_') ) {
2659 target->buf[target->BufUsed++] = source->buf[i];
2662 sprintf(&target->buf[target->BufUsed],
2664 (0xFF &source->buf[i]));
2665 target->BufUsed += 3;
2668 target->buf[target->BufUsed + 1] = '\0';
2672 /*******************************************************************************
2673 * Quoted Printable de/encoding *
2674 *******************************************************************************/
2677 * @ingroup StrBuf_DeEnCoder
2678 * @brief decode a buffer from base 64 encoding; destroys original
2679 * @param Buf Buffor to transform
2681 int StrBufDecodeBase64(StrBuf *Buf)
2689 xferbuf = (char*) malloc(Buf->BufSize);
2690 if (xferbuf == NULL)
2694 siz = CtdlDecodeBase64(xferbuf,
2704 * @ingroup StrBuf_DeEnCoder
2705 * @brief decode a buffer from base 64 encoding; destroys original
2706 * @param Buf Buffor to transform
2708 int StrBufDecodeHex(StrBuf *Buf)
2711 char *pch, *pche, *pchi;
2713 if (Buf == NULL) return -1;
2715 pch = pchi = Buf->buf;
2716 pche = pch + Buf->BufUsed;
2718 while (pchi < pche){
2719 ch = decode_hex(pchi);
2726 Buf->BufUsed = pch - Buf->buf;
2727 return Buf->BufUsed;
2731 * @ingroup StrBuf_DeEnCoder
2732 * @brief replace all chars >0x20 && < 0x7F with Mute
2733 * @param Mute char to put over invalid chars
2734 * @param Buf Buffor to transform
2736 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2740 if (Buf == NULL) return -1;
2741 pch = (unsigned char *)Buf->buf;
2742 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2743 if ((*pch < 0x20) || (*pch > 0x7F))
2747 return Buf->BufUsed;
2752 * @ingroup StrBuf_DeEnCoder
2753 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2754 * @param Buf Buffer to translate
2755 * @param StripBlanks Reduce several blanks to one?
2757 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2766 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2767 Buf->buf[Buf->BufUsed - 1] = '\0';
2772 while (a < Buf->BufUsed) {
2773 if (Buf->buf[a] == '+')
2775 else if (Buf->buf[a] == '%') {
2776 /* don't let % chars through, rather truncate the input. */
2777 if (a + 2 > Buf->BufUsed) {
2782 hex[0] = Buf->buf[a + 1];
2783 hex[1] = Buf->buf[a + 2];
2786 sscanf(hex, "%02x", &b);
2787 Buf->buf[a] = (char) b;
2788 len = Buf->BufUsed - a - 2;
2790 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2802 * @ingroup StrBuf_DeEnCoder
2803 * @brief RFC2047-encode a header field if necessary.
2804 * If no non-ASCII characters are found, the string
2805 * will be copied verbatim without encoding.
2807 * @param target Target buffer.
2808 * @param source Source string to be encoded.
2809 * @returns encoded length; -1 if non success.
2811 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2813 const char headerStr[] = "=?UTF-8?Q?";
2814 int need_to_encode = 0;
2818 if ((source == NULL) ||
2822 while ((i < source->BufUsed) &&
2823 (!IsEmptyStr (&source->buf[i])) &&
2824 (need_to_encode == 0)) {
2825 if (((unsigned char) source->buf[i] < 32) ||
2826 ((unsigned char) source->buf[i] > 126)) {
2832 if (!need_to_encode) {
2833 if (*target == NULL) {
2834 *target = NewStrBufPlain(source->buf, source->BufUsed);
2837 FlushStrBuf(*target);
2838 StrBufAppendBuf(*target, source, 0);
2841 return (*target)->BufUsed;
2845 if (*target == NULL)
2846 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2847 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2848 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2849 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2850 (*target)->BufUsed = sizeof(headerStr) - 1;
2851 for (i=0; (i < source->BufUsed); ++i) {
2852 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2853 IncreaseBuf(*target, 1, 0);
2854 ch = (unsigned char) source->buf[i];
2864 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2865 (*target)->BufUsed += 3;
2869 (*target)->buf[(*target)->BufUsed] = '_';
2871 (*target)->buf[(*target)->BufUsed] = ch;
2872 (*target)->BufUsed++;
2876 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2877 IncreaseBuf(*target, 1, 0);
2879 (*target)->buf[(*target)->BufUsed++] = '?';
2880 (*target)->buf[(*target)->BufUsed++] = '=';
2881 (*target)->buf[(*target)->BufUsed] = '\0';
2882 return (*target)->BufUsed;;
2886 * @ingroup StrBuf_DeEnCoder
2887 * @brief Quoted-Printable encode a message; make it < 80 columns width.
2888 * @param source Source string to be encoded.
2889 * @returns buffer with encoded message.
2891 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
2895 const char *ptr, *eptr;
2899 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
2901 OEptr = OutBuf->buf + OutBuf->BufSize;
2902 ptr = EncodeMe->buf;
2903 eptr = EncodeMe->buf + EncodeMe->BufUsed;
2908 if (Optr + 4 >= OEptr)
2911 Offset = Optr - OutBuf->buf;
2912 IncreaseBuf(OutBuf, 1, 0);
2913 Optr = OutBuf->buf + Offset;
2914 OEptr = OutBuf->buf + OutBuf->BufSize;
2916 if ((*ptr == '\r') || (*ptr == '\n'))
2918 /* ignore carriage returns */
2921 else if (*ptr == 10) {
2922 /* hard line break */
2923 if ((LinePos > 0) && (isspace(*(Optr-1))))
2925 memcpy(Optr, HKEY("=0A"));
2931 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
2932 ( (*ptr >= 62) && (*ptr <= 126) ))
2943 *Optr = HexList[ch][0];
2945 *Optr = HexList[ch][1];
2952 /* soft line break */
2953 if (isspace(*(Optr - 1))) {
2958 *Optr = HexList[ch][0];
2960 *Optr = HexList[ch][1];
2972 OutBuf->BufUsed = Optr - OutBuf->buf;
2978 static void AddRecipient(StrBuf *Target,
2980 StrBuf *EmailAddress,
2985 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2986 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2988 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2989 StrBufRFC2047encode(&EncBuf, UserName);
2990 StrBufAppendBuf(Target, EncBuf, 0);
2991 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2992 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2994 if (StrLength(EmailAddress) > 0){
2995 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2996 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2997 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3003 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3004 * \param Recp Source list of email recipients
3005 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3006 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3007 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3008 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3010 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3012 StrBuf *EmailAddress,
3016 const char *pch, *pche;
3017 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3019 if ((Recp == NULL) || (StrLength(Recp) == 0))
3023 pche = pch + StrLength(Recp);
3025 if (!CheckEncode(pch, -1, pche))
3026 return NewStrBufDup(Recp);
3028 Target = NewStrBufPlain(NULL, StrLength(Recp));
3030 while ((pch != NULL) && (pch < pche))
3032 while (isspace(*pch)) pch++;
3033 UserEnd = EmailStart = EmailEnd = NULL;
3035 if ((*pch == '"') || (*pch == '\'')) {
3036 UserStart = pch + 1;
3038 UserEnd = strchr(UserStart, *pch);
3039 if (UserEnd == NULL)
3040 break; ///TODO: Userfeedback??
3041 EmailStart = UserEnd + 1;
3042 while (isspace(*EmailStart))
3044 if (UserEnd == UserStart) {
3045 UserStart = UserEnd = NULL;
3048 if (*EmailStart == '<') {
3050 EmailEnd = strchr(EmailStart, '>');
3051 if (EmailEnd == NULL)
3052 EmailEnd = strchr(EmailStart, ',');
3056 EmailEnd = strchr(EmailStart, ',');
3058 if (EmailEnd == NULL)
3065 EmailEnd = strchr(UserStart, ',');
3066 if (EmailEnd == NULL) {
3067 EmailEnd = strchr(pch, '>');
3069 if (EmailEnd != NULL) {
3079 while ((EmailEnd > UserStart) && !gt &&
3080 ((*EmailEnd == ',') ||
3081 (*EmailEnd == '>') ||
3082 (isspace(*EmailEnd))))
3084 if (*EmailEnd == '>')
3089 if (EmailEnd == UserStart)
3093 EmailStart = strchr(UserStart, '<');
3094 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3096 UserEnd = EmailStart;
3098 while ((UserEnd > UserStart) &&
3099 isspace (*(UserEnd - 1)))
3102 if (UserStart >= UserEnd)
3103 UserStart = UserEnd = NULL;
3105 else { /* this is a local recipient... no domain, just a realname */
3106 EmailStart = UserStart;
3107 At = strchr(EmailStart, '@');
3113 EmailStart = UserStart;
3119 if ((UserStart != NULL) && (UserEnd != NULL))
3120 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3121 else if ((UserStart != NULL) && (UserEnd == NULL))
3122 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3124 FlushStrBuf(UserName);
3126 if ((EmailStart != NULL) && (EmailEnd != NULL))
3127 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3128 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3129 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3131 FlushStrBuf(EmailAddress);
3133 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3138 if ((pch != NULL) && (*pch == ','))
3140 if (pch != NULL) while (isspace(*pch))
3149 * @brief replaces all occurances of 'search' by 'replace'
3150 * @param buf Buffer to modify
3151 * @param search character to search
3152 * @param replace character to replace search by
3154 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3159 for (i=0; i<buf->BufUsed; i++)
3160 if (buf->buf[i] == search)
3161 buf->buf[i] = replace;
3167 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3168 * @param buf Buffer to modify
3170 void StrBufToUnixLF(StrBuf *buf)
3172 char *pche, *pchS, *pchT;
3176 pche = buf->buf + buf->BufUsed;
3177 pchS = pchT = buf->buf;
3183 if (*pchS != '\n') {
3192 buf->BufUsed = pchT - buf->buf;
3196 /*******************************************************************************
3197 * Iconv Wrapper; RFC822 de/encoding *
3198 *******************************************************************************/
3201 * @ingroup StrBuf_DeEnCoder
3202 * @brief Wrapper around iconv_open()
3203 * Our version adds aliases for non-standard Microsoft charsets
3204 * such as 'MS950', aliasing them to names like 'CP950'
3206 * @param tocode Target encoding
3207 * @param fromcode Source encoding
3208 * @param pic anonimized pointer to iconv struct
3210 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3213 iconv_t ic = (iconv_t)(-1) ;
3214 ic = iconv_open(tocode, fromcode);
3215 if (ic == (iconv_t)(-1) ) {
3216 char alias_fromcode[64];
3217 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3218 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3219 alias_fromcode[0] = 'C';
3220 alias_fromcode[1] = 'P';
3221 ic = iconv_open(tocode, alias_fromcode);
3224 *(iconv_t *)pic = ic;
3230 * @ingroup StrBuf_DeEnCoder
3231 * @brief find one chunk of a RFC822 encoded string
3232 * @param Buffer where to search
3233 * @param bptr where to start searching
3234 * @returns found position, NULL if none.
3236 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3239 /* Find the next ?Q? */
3240 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3243 end = strchr(bptr + 2, '?');
3248 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3249 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3250 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3251 (*(end + 2) == '?')) {
3252 /* skip on to the end of the cluster, the next ?= */
3253 end = strstr(end + 3, "?=");
3256 /* sort of half valid encoding, try to find an end. */
3257 end = strstr(bptr, "?=");
3264 * @ingroup StrBuf_DeEnCoder
3265 * @brief convert one buffer according to the preselected iconv pointer PIC
3266 * @param ConvertBuf buffer we need to translate
3267 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3268 * @param pic Pointer to the iconv-session Object
3270 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3276 char *ibuf; /**< Buffer of characters to be converted */
3277 char *obuf; /**< Buffer for converted characters */
3278 size_t ibuflen; /**< Length of input buffer */
3279 size_t obuflen; /**< Length of output buffer */
3282 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3285 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3286 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3287 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3289 ic = *(iconv_t*)pic;
3290 ibuf = ConvertBuf->buf;
3291 ibuflen = ConvertBuf->BufUsed;
3293 obuflen = TmpBuf->BufSize;
3295 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3298 if (errno == E2BIG) {
3300 IncreaseBuf(TmpBuf, 0, 0);
3305 else if (errno == EILSEQ){
3306 /* hm, invalid utf8 sequence... what to do now? */
3307 /* An invalid multibyte sequence has been encountered in the input */
3309 else if (errno == EINVAL) {
3310 /* An incomplete multibyte sequence has been encountered in the input. */
3313 FlushStrBuf(TmpBuf);
3316 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3317 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3319 /* little card game: wheres the red lady? */
3320 SwapBuffers(ConvertBuf, TmpBuf);
3321 FlushStrBuf(TmpBuf);
3328 * @ingroup StrBuf_DeEnCoder
3329 * @brief catches one RFC822 encoded segment, and decodes it.
3330 * @param Target buffer to fill with result
3331 * @param DecodeMe buffer with stuff to process
3332 * @param SegmentStart points to our current segment in DecodeMe
3333 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3334 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3335 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3336 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3338 inline static void DecodeSegment(StrBuf *Target,
3339 const StrBuf *DecodeMe,
3340 const char *SegmentStart,
3341 const char *SegmentEnd,
3343 StrBuf *ConvertBuf2,
3344 StrBuf *FoundCharset)
3350 iconv_t ic = (iconv_t)(-1);
3354 /* Now we handle foreign character sets properly encoded
3355 * in RFC2047 format.
3357 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3358 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3359 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3360 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3361 if (FoundCharset != NULL) {
3362 FlushStrBuf(FoundCharset);
3363 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3365 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3366 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3368 *encoding = toupper(*encoding);
3369 if (*encoding == 'B') { /**< base64 */
3370 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3371 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3372 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3374 ConvertBuf->BufUsed);
3376 else if (*encoding == 'Q') { /**< quoted-printable */
3380 while (pos < ConvertBuf->BufUsed)
3382 if (ConvertBuf->buf[pos] == '_')
3383 ConvertBuf->buf[pos] = ' ';
3387 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3388 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3390 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3393 ConvertBuf->BufUsed);
3396 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3399 ctdl_iconv_open("UTF-8", charset, &ic);
3400 if (ic != (iconv_t)(-1) ) {
3402 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3403 StrBufAppendBuf(Target, ConvertBuf2, 0);
3408 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3414 * @ingroup StrBuf_DeEnCoder
3415 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3416 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3417 * @param Target where to put the decoded string to
3418 * @param DecodeMe buffer with encoded string
3419 * @param DefaultCharset if we don't find one, which should we use?
3420 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3421 * put it here for later use where no string might be known.
3423 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3426 StrBuf *ConvertBuf2;
3427 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3428 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3430 StrBuf_RFC822_2_Utf8(Target,
3436 FreeStrBuf(&ConvertBuf);
3437 FreeStrBuf(&ConvertBuf2);
3441 * @ingroup StrBuf_DeEnCoder
3442 * @brief Handle subjects with RFC2047 encoding such as:
3443 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3444 * @param Target where to put the decoded string to
3445 * @param DecodeMe buffer with encoded string
3446 * @param DefaultCharset if we don't find one, which should we use?
3447 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3448 * put it here for later use where no string might be known.
3449 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3450 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3452 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3453 const StrBuf *DecodeMe,
3454 const StrBuf* DefaultCharset,
3455 StrBuf *FoundCharset,
3457 StrBuf *ConvertBuf2)
3459 StrBuf *DecodedInvalidBuf = NULL;
3460 const StrBuf *DecodeMee = DecodeMe;
3461 const char *start, *end, *next, *nextend, *ptr = NULL;
3463 iconv_t ic = (iconv_t)(-1) ;
3468 int illegal_non_rfc2047_encoding = 0;
3471 if (DecodeMe == NULL)
3473 /* Sometimes, badly formed messages contain strings which were simply
3474 * written out directly in some foreign character set instead of
3475 * using RFC2047 encoding. This is illegal but we will attempt to
3476 * handle it anyway by converting from a user-specified default
3477 * charset to UTF-8 if we see any nonprintable characters.
3480 for (i=0; i<DecodeMe->BufUsed; ++i) {
3481 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3482 illegal_non_rfc2047_encoding = 1;
3487 if ((illegal_non_rfc2047_encoding) &&
3488 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3489 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3492 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3493 if (ic != (iconv_t)(-1) ) {
3494 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3495 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3496 DecodeMee = DecodedInvalidBuf;
3502 /* pre evaluate the first pair */
3504 start = strstr(DecodeMee->buf, "=?");
3505 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3507 end = FindNextEnd (DecodeMee, start + 2);
3509 StrBufAppendBuf(Target, DecodeMee, 0);
3510 FreeStrBuf(&DecodedInvalidBuf);
3515 if (start != DecodeMee->buf) {
3518 nFront = start - DecodeMee->buf;
3519 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3522 * Since spammers will go to all sorts of absurd lengths to get their
3523 * messages through, there are LOTS of corrupt headers out there.
3524 * So, prevent a really badly formed RFC2047 header from throwing
3525 * this function into an infinite loop.
3527 while ((start != NULL) &&
3534 DecodeSegment(Target,
3542 next = strstr(end, "=?");
3544 if ((next != NULL) &&
3546 nextend = FindNextEnd(DecodeMee, next);
3547 if (nextend == NULL)
3550 /* did we find two partitions */
3551 if ((next != NULL) &&
3555 while ((ptr < next) &&
3562 * did we find a gab just filled with blanks?
3563 * if not, copy its stuff over.
3567 StrBufAppendBufPlain(Target,
3573 /* our next-pair is our new first pair now. */
3579 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3580 if ((end != NULL) && (end < nextend)) {
3582 while ( (ptr < nextend) &&
3589 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3591 FreeStrBuf(&DecodedInvalidBuf);
3594 /*******************************************************************************
3595 * Manipulating UTF-8 Strings *
3596 *******************************************************************************/
3600 * @brief evaluate the length of an utf8 special character sequence
3601 * @param Char the character to examine
3602 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3604 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3607 unsigned char test = (1<<7);
3609 if ((*CharS & 0xC0) != 0xC0)
3613 ((test & ((unsigned char)*CharS)) != 0))
3618 if ((n > 6) || ((CharE - CharS) < n))
3625 * @brief detect whether this char starts an utf-8 encoded char
3626 * @param Char character to inspect
3627 * @returns yes or no
3629 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3631 /** 11??.???? indicates an UTF8 Sequence. */
3632 return ((Char & 0xC0) == 0xC0);
3637 * @brief measure the number of glyphs in an UTF8 string...
3638 * @param Buf string to measure
3639 * @returns the number of glyphs in Buf
3641 long StrBuf_Utf8StrLen(StrBuf *Buf)
3647 if ((Buf == NULL) || (Buf->BufUsed == 0))
3650 eptr = Buf->buf + Buf->BufUsed;
3651 while ((aptr < eptr) && (*aptr != '\0')) {
3652 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3653 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3654 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3667 * @brief cuts a string after maxlen glyphs
3668 * @param Buf string to cut to maxlen glyphs
3669 * @param maxlen how long may the string become?
3670 * @returns current length of the string
3672 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3678 eptr = Buf->buf + Buf->BufUsed;
3679 while ((aptr < eptr) && (*aptr != '\0')) {
3680 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3681 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3682 while ((*aptr++ != '\0') && (m-- > 0));
3691 Buf->BufUsed = aptr - Buf->buf;
3692 return Buf->BufUsed;
3695 return Buf->BufUsed;
3703 /*******************************************************************************
3705 *******************************************************************************/
3708 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3709 #define OS_CODE 0x03 /*< unix */
3712 * @ingroup StrBuf_DeEnCoder
3713 * @brief uses the same calling syntax as compress2(), but it
3714 * creates a stream compatible with HTTP "Content-encoding: gzip"
3715 * @param dest compressed buffer
3716 * @param destLen length of the compresed data
3717 * @param source source to encode
3718 * @param sourceLen length of source to encode
3719 * @param level compression level
3721 int ZEXPORT compress_gzip(Bytef * dest,
3723 const Bytef * source,
3727 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3729 /* write gzip header */
3730 snprintf((char *) dest, *destLen,
3731 "%c%c%c%c%c%c%c%c%c%c",
3732 gz_magic[0], gz_magic[1], Z_DEFLATED,
3733 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3736 /* normal deflate */
3739 stream.next_in = (Bytef *) source;
3740 stream.avail_in = (uInt) sourceLen;
3741 stream.next_out = dest + 10L; // after header
3742 stream.avail_out = (uInt) * destLen;
3743 if ((uLong) stream.avail_out != *destLen)
3746 stream.zalloc = (alloc_func) 0;
3747 stream.zfree = (free_func) 0;
3748 stream.opaque = (voidpf) 0;
3750 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3751 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3755 err = deflate(&stream, Z_FINISH);
3756 if (err != Z_STREAM_END) {
3757 deflateEnd(&stream);
3758 return err == Z_OK ? Z_BUF_ERROR : err;
3760 *destLen = stream.total_out + 10L;
3762 /* write CRC and Length */
3763 uLong crc = crc32(0L, source, sourceLen);
3765 for (n = 0; n < 4; ++n, ++*destLen) {
3766 dest[*destLen] = (int) (crc & 0xff);
3769 uLong len = stream.total_in;
3770 for (n = 0; n < 4; ++n, ++*destLen) {
3771 dest[*destLen] = (int) (len & 0xff);
3774 err = deflateEnd(&stream);
3781 * @ingroup StrBuf_DeEnCoder
3782 * @brief compress the buffer with gzip
3783 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3784 * @param Buf buffer whose content is to be gzipped
3786 int CompressBuffer(StrBuf *Buf)
3789 char *compressed_data = NULL;
3790 size_t compressed_len, bufsize;
3793 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3794 compressed_data = malloc(compressed_len);
3796 if (compressed_data == NULL)
3798 /* Flush some space after the used payload so valgrind shuts up... */
3799 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3800 Buf->buf[Buf->BufUsed + i++] = '\0';
3801 if (compress_gzip((Bytef *) compressed_data,
3804 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3807 Buf->buf = compressed_data;
3808 Buf->BufUsed = compressed_len;
3809 Buf->BufSize = bufsize;
3810 /* Flush some space after the used payload so valgrind shuts up... */
3812 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3813 Buf->buf[Buf->BufUsed + i++] = '\0';
3816 free(compressed_data);
3818 #endif /* HAVE_ZLIB */
3822 /*******************************************************************************
3823 * File I/O; Callbacks to libevent *
3824 *******************************************************************************/
3826 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3831 if ((FB == NULL) || (FB->Buf == NULL))
3835 * check whether the read pointer is somewhere in a range
3836 * where a cut left is inexpensive
3839 if (FB->ReadWritePointer != NULL)
3843 already_read = FB->ReadWritePointer - FB->Buf->buf;
3844 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3846 if (already_read != 0) {
3849 unread = FB->Buf->BufUsed - already_read;
3851 /* else nothing to compact... */
3853 FB->ReadWritePointer = FB->Buf->buf;
3854 bufremain = FB->Buf->BufSize;
3856 else if ((unread < 64) ||
3857 (bufremain < already_read))
3860 * if its just a tiny bit remaining, or we run out of space...
3863 FB->Buf->BufUsed = unread;
3864 if (unread < already_read)
3865 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3867 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3868 FB->ReadWritePointer = FB->Buf->buf;
3869 bufremain = FB->Buf->BufSize - unread - 1;
3871 else if (bufremain < (FB->Buf->BufSize / 10))
3873 /* get a bigger buffer */
3875 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3877 FB->ReadWritePointer = FB->Buf->buf + unread;
3879 bufremain = FB->Buf->BufSize - unread - 1;
3880 /*TODO: special increase function that won't copy the already read! */
3883 else if (bufremain < 10) {
3884 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3886 FB->ReadWritePointer = FB->Buf->buf;
3888 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3893 FB->ReadWritePointer = FB->Buf->buf;
3894 bufremain = FB->Buf->BufSize - 1;
3897 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3900 FB->Buf->BufUsed += n;
3901 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3906 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3911 if ((FB == NULL) || (FB->Buf == NULL))
3914 if (FB->ReadWritePointer != NULL)
3916 WriteRemain = FB->Buf->BufUsed -
3917 (FB->ReadWritePointer -
3921 FB->ReadWritePointer = FB->Buf->buf;
3922 WriteRemain = FB->Buf->BufUsed;
3925 n = write(fd, FB->ReadWritePointer, WriteRemain);
3927 FB->ReadWritePointer += n;
3929 if (FB->ReadWritePointer ==
3930 FB->Buf->buf + FB->Buf->BufUsed)
3932 FlushStrBuf(FB->Buf);
3933 FB->ReadWritePointer = NULL;
3936 // check whether we've got something to write
3937 // get the maximum chunk plus the pointer we can send
3938 // write whats there
3939 // if not all was sent, remember the send pointer for the next time
3940 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3946 * @ingroup StrBuf_IO
3947 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3948 * @param LineBuf your line will be copied here.
3949 * @param FB BLOB with lines of text...
3950 * @param Ptr moved arround to keep the next-line across several iterations
3951 * has to be &NULL on start; will be &NotNULL on end of buffer
3952 * @returns size of copied buffer
3954 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3956 const char *aptr, *ptr, *eptr;
3959 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3963 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3964 FB->ReadWritePointer = StrBufNOTNULL;
3968 FlushStrBuf(LineBuf);
3969 if (FB->ReadWritePointer == NULL)
3970 ptr = aptr = FB->Buf->buf;
3972 ptr = aptr = FB->ReadWritePointer;
3974 optr = LineBuf->buf;
3975 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3976 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3978 while ((ptr <= eptr) &&
3985 LineBuf->BufUsed = optr - LineBuf->buf;
3986 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3987 optr = LineBuf->buf + LineBuf->BufUsed;
3988 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3993 if (optr > LineBuf->buf)
3995 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3996 LineBuf->BufUsed = optr - LineBuf->buf;
3998 if ((FB->ReadWritePointer != NULL) &&
3999 (FB->ReadWritePointer != FB->Buf->buf))
4001 /* Ok, the client application read all the data
4002 it was interested in so far. Since there is more to read,
4003 we now shrink the buffer, and move the rest over.
4005 StrBufCutLeft(FB->Buf,
4006 FB->ReadWritePointer - FB->Buf->buf);
4007 FB->ReadWritePointer = FB->Buf->buf;
4009 return eMustReadMore;
4012 LineBuf->BufUsed = optr - LineBuf->buf;
4014 if ((ptr <= eptr) && (*ptr == '\r'))
4016 if ((ptr <= eptr) && (*ptr == '\n'))
4020 FB->ReadWritePointer = ptr;
4023 FlushStrBuf(FB->Buf);
4024 FB->ReadWritePointer = NULL;
4027 return eReadSuccess;
4031 * @ingroup StrBuf_CHUNKED_IO
4032 * @brief check whether the chunk-buffer has more data waiting or not.
4033 * @param FB Chunk-Buffer to inspect
4035 eReadState StrBufCheckBuffer(IOBuffer *FB)
4039 if (FB->Buf->BufUsed == 0)
4040 return eReadSuccess;
4041 if (FB->ReadWritePointer == NULL)
4042 return eBufferNotEmpty;
4043 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4044 return eBufferNotEmpty;
4045 return eReadSuccess;
4048 long IOBufferStrLength(IOBuffer *FB)
4050 if ((FB == NULL) || (FB->Buf == NULL))
4052 if (FB->ReadWritePointer == NULL)
4053 return StrLength(FB->Buf);
4055 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4058 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
4060 memset(FDB, 0, sizeof(FDIOBuffer));
4062 FDB->SplicePipe[0] = -1;
4063 FDB->SplicePipe[1] = -1;
4066 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
4068 FDIOBufferFlush(FDB);
4070 FDB->TotalSendSize = TotalSendSize;
4074 pipe(FDB->SplicePipe);
4077 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
4082 void FDIOBufferDelete(FDIOBuffer *FDB)
4087 if (FDB->SplicePipe[0] > 0)
4088 close(FDB->SplicePipe[0]);
4089 if (FDB->SplicePipe[1] > 0)
4090 close(FDB->SplicePipe[1]);
4094 FreeStrBuf(&FDB->ChunkBuffer);
4096 if (FDB->OtherFD > 0)
4097 close(FDB->OtherFD);
4098 FDIOBufferFlush(FDB);
4101 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4103 ssize_t sent, pipesize;
4107 if (FDB->PipeSize == 0)
4109 pipesize = splice(FDB->OtherFD,
4110 &FDB->TotalSentAlready,
4113 FDB->ChunkSendRemain,
4118 *Err = strerror(errno);
4121 FDB->PipeSize = pipesize;
4123 sent = splice(FDB->SplicePipe[0],
4128 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4131 *Err = strerror(errno);
4134 FDB->PipeSize -= sent;
4135 FDB->ChunkSendRemain -= sent;
4144 pRead = FDB->ChunkBuffer->buf;
4145 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4147 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4149 FDB->ChunkBuffer->BufUsed += nRead;
4150 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4152 else if (nRead == 0) {}
4157 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
4160 FDB->TotalSentAlready += nRead;
4161 FDB->ChunkSendRemain -= nRead;
4162 return FDB->ChunkSendRemain;
4170 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4172 ssize_t sent, pipesize;
4177 if (FDB->PipeSize == 0)
4179 pipesize = splice(FDB->IOB->fd,
4183 FDB->ChunkSendRemain,
4184 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4188 *Err = strerror(errno);
4191 FDB->PipeSize = pipesize;
4194 sent = splice(FDB->SplicePipe[0],
4197 &FDB->TotalSentAlready,
4199 SPLICE_F_MORE | SPLICE_F_MOVE);
4203 *Err = strerror(errno);
4206 FDB->PipeSize -= sent;
4207 FDB->ChunkSendRemain -= sent;
4213 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4218 FDB->ChunkBuffer->BufUsed = sent;
4220 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4221 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4223 *Err = strerror(errno);
4229 FDB->ChunkBuffer->BufUsed = 0;
4230 FDB->TotalSentAlready += sent;
4231 FDB->ChunkSendRemain -= sent;
4232 return FDB->ChunkSendRemain;
4234 else if (sent < 0) {
4235 *Err = strerror(errno);
4242 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4244 ssize_t sent, pipesize;
4249 if (FDB->PipeSize == 0)
4251 pipesize = splice(FDB->IOB->fd,
4252 &FDB->TotalReadAlready,
4255 FDB->ChunkSendRemain,
4256 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4260 *Err = strerror(errno);
4263 FDB->PipeSize = pipesize;
4266 sent = splice(FDB->SplicePipe[0],
4269 &FDB->TotalSentAlready,
4271 SPLICE_F_MORE | SPLICE_F_MOVE);
4275 *Err = strerror(errno);
4278 FDB->PipeSize -= sent;
4279 FDB->ChunkSendRemain -= sent;
4285 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4290 FDB->ChunkBuffer->BufUsed = sent;
4292 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4293 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4295 *Err = strerror(errno);
4301 FDB->ChunkBuffer->BufUsed = 0;
4302 FDB->TotalSentAlready += sent;
4303 FDB->ChunkSendRemain -= sent;
4304 return FDB->ChunkSendRemain;
4306 else if (sent < 0) {
4307 *Err = strerror(errno);
4314 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4320 int nSuccessLess = 0;
4324 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4325 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4327 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4328 (FDB->ChunkSendRemain > 0))
4331 tv.tv_sec = 1; /* selectresolution; */
4335 FD_SET(FDB->OtherFD, &rfds);
4336 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4337 *Error = strerror(errno);
4341 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4346 should_write = FDB->IOB->Buf->BufUsed -
4347 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4348 if (should_write > FDB->ChunkSendRemain)
4349 should_write = FDB->ChunkSendRemain;
4351 rlen = write(FDB->OtherFD,
4352 FDB->IOB->ReadWritePointer,
4355 *Error = strerror(errno);
4359 FDB->TotalSentAlready += rlen;
4360 FDB->IOB->ReadWritePointer += rlen;
4361 FDB->ChunkSendRemain -= rlen;
4363 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4365 FlushStrBuf(FDB->IOB->Buf);
4366 FDB->IOB->ReadWritePointer = NULL;
4369 if (FDB->ChunkSendRemain == 0)
4370 return eReadSuccess;
4372 return eMustReadMore;
4375 /*******************************************************************************
4376 * File I/O; Prefer buffered read since its faster! *
4377 *******************************************************************************/
4380 * @ingroup StrBuf_IO
4381 * @brief Read a line from socket
4382 * flushes and closes the FD on error
4383 * @param buf the buffer to get the input to
4384 * @param fd pointer to the filedescriptor to read
4385 * @param append Append to an existing string or replace?
4386 * @param Error strerror() on error
4387 * @returns numbers of chars read
4389 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4391 int len, rlen, slen;
4393 if ((buf == NULL) || (buf->buf == NULL)) {
4394 *Error = strerror(EINVAL);
4401 slen = len = buf->BufUsed;
4403 rlen = read(*fd, &buf->buf[len], 1);
4405 *Error = strerror(errno);
4412 if (buf->buf[len] == '\n')
4414 if (buf->buf[len] != '\r')
4416 if (len + 2 >= buf->BufSize) {
4418 buf->buf[len+1] = '\0';
4419 IncreaseBuf(buf, 1, -1);
4423 buf->buf[len] = '\0';
4428 * @ingroup StrBuf_BufferedIO
4429 * @brief Read a line from socket
4430 * flushes and closes the FD on error
4431 * @param Line the line to read from the fd / I/O Buffer
4432 * @param buf the buffer to get the input to
4433 * @param fd pointer to the filedescriptor to read
4434 * @param timeout number of successless selects until we bail out
4435 * @param selectresolution how long to wait on each select
4436 * @param Error strerror() on error
4437 * @returns numbers of chars read
4439 int StrBufTCP_read_buffered_line(StrBuf *Line,
4443 int selectresolution,
4447 int nSuccessLess = 0;
4454 if (buf->BufUsed > 0) {
4455 pch = strchr(buf->buf, '\n');
4458 len = pch - buf->buf;
4459 if (len > 0 && (*(pch - 1) == '\r') )
4461 StrBufSub(Line, buf, 0, len - rlen);
4462 StrBufCutLeft(buf, len + 1);
4467 if (buf->BufSize - buf->BufUsed < 10)
4468 IncreaseBuf(buf, 1, -1);
4470 fdflags = fcntl(*fd, F_GETFL);
4471 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4473 while ((nSuccessLess < timeout) && (pch == NULL)) {
4475 tv.tv_sec = selectresolution;
4480 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4481 *Error = strerror(errno);
4487 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4492 &buf->buf[buf->BufUsed],
4493 buf->BufSize - buf->BufUsed - 1);
4495 *Error = strerror(errno);
4500 else if (rlen > 0) {
4502 buf->BufUsed += rlen;
4503 buf->buf[buf->BufUsed] = '\0';
4504 pch = strchr(buf->buf, '\n');
4505 if ((pch == NULL) &&
4506 (buf->BufUsed + 10 > buf->BufSize) &&
4507 (IncreaseBuf(buf, 1, -1) == -1))
4515 len = pch - buf->buf;
4516 if (len > 0 && (*(pch - 1) == '\r') )
4518 StrBufSub(Line, buf, 0, len - rlen);
4519 StrBufCutLeft(buf, len + 1);
4526 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4527 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4528 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4530 * @ingroup StrBuf_BufferedIO
4531 * @brief Read a line from socket
4532 * flushes and closes the FD on error
4533 * @param Line where to append our Line read from the fd / I/O Buffer;
4534 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4535 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4536 * @param fd pointer to the filedescriptor to read
4537 * @param timeout number of successless selects until we bail out
4538 * @param selectresolution how long to wait on each select
4539 * @param Error strerror() on error
4540 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4542 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4547 int selectresolution,
4550 const char *pche = NULL;
4551 const char *pos = NULL;
4553 int len, rlen, retlen;
4554 int nSuccessLess = 0;
4556 const char *pch = NULL;
4562 if ((Line == NULL) ||
4569 *Error = ErrRBLF_PreConditionFailed;
4574 if ((IOBuf->BufUsed > 0) &&
4576 (pos < IOBuf->buf + IOBuf->BufUsed))
4580 pche = IOBuf->buf + IOBuf->BufUsed;
4584 while ((pch < pche) && (*pch != '\n'))
4586 if (Line->BufUsed + 10 > Line->BufSize)
4589 apos = pcht - Line->buf;
4591 IncreaseBuf(Line, 1, -1);
4592 pcht = Line->buf + apos;
4600 if (len > 0 && (*(pch - 1) == '\r') )
4609 if ((pch >= pche) || (*pch == '\0'))
4617 if ((pch != NULL) &&
4620 if (pch + 1 >= pche) {
4633 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4635 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4636 IncreaseBuf(IOBuf, 1, -1);
4638 fdflags = fcntl(*fd, F_GETFL);
4639 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4642 while ((nSuccessLess < timeout) &&
4652 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4653 *Error = strerror(errno);
4657 *Error = ErrRBLF_SelectFailed;
4660 if (! FD_ISSET(*fd, &rfds) != 0) {
4666 &IOBuf->buf[IOBuf->BufUsed],
4667 IOBuf->BufSize - IOBuf->BufUsed - 1);
4669 *Error = strerror(errno);
4674 else if (rlen > 0) {
4676 pLF = IOBuf->buf + IOBuf->BufUsed;
4677 IOBuf->BufUsed += rlen;
4678 IOBuf->buf[IOBuf->BufUsed] = '\0';
4680 pche = IOBuf->buf + IOBuf->BufUsed;
4682 while ((pLF < pche) && (*pLF != '\n'))
4684 if ((pLF >= pche) || (*pLF == '\0'))
4687 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4691 if (pLF != NULL) apos = pLF - IOBuf->buf;
4692 IncreaseBuf(IOBuf, 1, -1);
4693 if (pLF != NULL) pLF = IOBuf->buf + apos;
4707 if (len > 0 && (*(pLF - 1) == '\r') )
4709 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4710 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4716 return retlen + len;
4718 *Error = ErrRBLF_NotEnoughSentFromServer;
4723 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4725 * @ingroup StrBuf_IO
4726 * @brief Input binary data from socket
4727 * flushes and closes the FD on error
4728 * @param Buf the buffer to get the input to
4729 * @param fd pointer to the filedescriptor to read
4730 * @param append Append to an existing string or replace?
4731 * @param nBytes the maximal number of bytes to read
4732 * @param Error strerror() on error
4733 * @returns numbers of chars read
4735 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4746 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4748 *Error = ErrRBLF_BLOBPreConditionFailed;
4753 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4754 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4756 ptr = Buf->buf + Buf->BufUsed;
4758 fdflags = fcntl(*fd, F_GETFL);
4759 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4761 while ((nRead < nBytes) &&
4771 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4772 *Error = strerror(errno);
4776 *Error = ErrRBLF_SelectFailed;
4779 if (! FD_ISSET(*fd, &rfds) != 0) {
4785 if ((rlen = read(*fd,
4787 nBytes - nRead)) == -1) {
4790 *Error = strerror(errno);
4795 Buf->BufUsed += rlen;
4797 Buf->buf[Buf->BufUsed] = '\0';
4801 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4802 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4804 * @ingroup StrBuf_BufferedIO
4805 * @brief Input binary data from socket
4806 * flushes and closes the FD on error
4807 * @param Blob put binary thing here
4808 * @param IOBuf the buffer to get the input to
4809 * @param Pos offset inside of IOBuf
4810 * @param fd pointer to the filedescriptor to read
4811 * @param append Append to an existing string or replace?
4812 * @param nBytes the maximal number of bytes to read
4813 * @param check whether we should search for '000\n' terminators in case of timeouts
4814 * @param Error strerror() on error
4815 * @returns numbers of chars read
4817 int StrBufReadBLOBBuffered(StrBuf *Blob,
4830 int nAlreadyRead = 0;
4835 int nSuccessLess = 0;
4838 if ((Blob == NULL) ||
4845 *Error = ErrRBB_BLOBFPreConditionFailed;
4851 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4852 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4857 rlen = pos - IOBuf->buf;
4858 rlen = IOBuf->BufUsed - rlen;
4861 if ((IOBuf->BufUsed > 0) &&
4863 (pos < IOBuf->buf + IOBuf->BufUsed))
4865 if (rlen < nBytes) {
4866 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4867 Blob->BufUsed += rlen;
4868 Blob->buf[Blob->BufUsed] = '\0';
4869 nAlreadyRead = nRead = rlen;
4872 if (rlen >= nBytes) {
4873 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4874 Blob->BufUsed += nBytes;
4875 Blob->buf[Blob->BufUsed] = '\0';
4876 if (rlen == nBytes) {
4888 if (IOBuf->BufSize < nBytes - nRead)
4889 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4892 fdflags = fcntl(*fd, F_GETFL);
4893 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4901 while ((nSuccessLess < MaxTries) &&
4911 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4912 *Error = strerror(errno);
4916 *Error = ErrRBLF_SelectFailed;
4919 if (! FD_ISSET(*fd, &rfds) != 0) {
4926 IOBuf->BufSize - (ptr - IOBuf->buf));
4930 *Error = strerror(errno);
4933 else if (rlen == 0){
4934 if ((check == NNN_TERM) &&
4936 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4938 StrBufPlain(Blob, HKEY("\n000\n"));
4939 StrBufCutRight(Blob, 5);
4940 return Blob->BufUsed;
4942 else if (!IsNonBlock)
4944 else if (nSuccessLess > MaxTries) {
4946 *Error = ErrRBB_too_many_selects;
4950 else if (rlen > 0) {
4954 IOBuf->BufUsed += rlen;
4957 if (nSuccessLess >= MaxTries) {
4959 *Error = ErrRBB_too_many_selects;
4963 if (nRead > nBytes) {
4964 *Pos = IOBuf->buf + nBytes;
4966 Blob->buf[Blob->BufUsed] = '\0';
4967 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4971 return nRead + nAlreadyRead;
4975 * @ingroup StrBuf_IO
4976 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4977 * @param LineBuf your line will be copied here.
4978 * @param Buf BLOB with lines of text...
4979 * @param Ptr moved arround to keep the next-line across several iterations
4980 * has to be &NULL on start; will be &NotNULL on end of buffer
4981 * @returns size of remaining buffer
4983 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4985 const char *aptr, *ptr, *eptr;
4988 if ((Buf == NULL) ||
4989 (*Ptr == StrBufNOTNULL) ||
4991 (LineBuf->buf == NULL))
4993 *Ptr = StrBufNOTNULL;
4997 FlushStrBuf(LineBuf);
4999 ptr = aptr = Buf->buf;
5003 optr = LineBuf->buf;
5004 eptr = Buf->buf + Buf->BufUsed;
5005 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5007 while ((ptr <= eptr) &&
5014 LineBuf->BufUsed = optr - LineBuf->buf;
5015 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5016 optr = LineBuf->buf + LineBuf->BufUsed;
5017 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5021 if ((ptr >= eptr) && (optr > LineBuf->buf))
5023 LineBuf->BufUsed = optr - LineBuf->buf;
5025 if ((ptr <= eptr) && (*ptr == '\r'))
5027 if ((ptr <= eptr) && (*ptr == '\n'))
5034 *Ptr = StrBufNOTNULL;
5037 return Buf->BufUsed - (ptr - Buf->buf);
5042 * @ingroup StrBuf_IO
5043 * @brief removes double slashes from pathnames
5044 * @param Dir directory string to filter
5045 * @param RemoveTrailingSlash allows / disallows trailing slashes
5047 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5053 while (!IsEmptyStr(a)) {
5065 if ((RemoveTrailingSlash) &&
5071 Dir->BufUsed = b - Dir->buf;