2 * Copyright (c) 1987-2011 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))
365 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
366 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
367 Buf->BufSize = Buf->BufUsed + 1;
375 * @ingroup StrBuf_DeConstructors
376 * @brief Allocate a new buffer with default buffer size
377 * @returns the new stringbuffer
379 StrBuf* NewStrBuf(void)
383 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
384 NewBuf->buf = (char*) malloc(BaseStrBufSize);
385 NewBuf->buf[0] = '\0';
386 NewBuf->BufSize = BaseStrBufSize;
388 NewBuf->ConstBuf = 0;
396 * @ingroup StrBuf_DeConstructors
397 * @brief Copy Constructor; returns a duplicate of CopyMe
398 * @param CopyMe Buffer to faxmilate
399 * @returns the new stringbuffer
401 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
408 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
409 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
410 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
411 NewBuf->BufUsed = CopyMe->BufUsed;
412 NewBuf->BufSize = CopyMe->BufSize;
413 NewBuf->ConstBuf = 0;
421 * @ingroup StrBuf_DeConstructors
422 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
423 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
424 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
425 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
426 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
427 * @returns the new stringbuffer
429 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
433 if (CreateRelpaceMe == NULL)
438 if (*CreateRelpaceMe != NULL)
439 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
441 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
445 if (CopyFlushMe == NULL)
447 if (*CreateRelpaceMe != NULL)
448 FlushStrBuf(*CreateRelpaceMe);
450 *CreateRelpaceMe = NewStrBuf();
455 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
456 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
457 * be a big IO-Buffer...
459 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
461 if (*CreateRelpaceMe == NULL)
463 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
468 NewBuf = *CreateRelpaceMe;
471 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
475 if (*CreateRelpaceMe == NULL)
477 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
481 NewBuf = *CreateRelpaceMe;
482 SwapBuffers (NewBuf, CopyFlushMe);
485 FlushStrBuf(CopyFlushMe);
490 * @ingroup StrBuf_DeConstructors
491 * @brief create a new Buffer using an existing c-string
492 * this function should also be used if you want to pre-suggest
493 * the buffer size to allocate in conjunction with ptr == NULL
494 * @param ptr the c-string to copy; may be NULL to create a blank instance
495 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
496 * @returns the new stringbuffer
498 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
501 size_t Siz = BaseStrBufSize;
504 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
506 CopySize = strlen((ptr != NULL)?ptr:"");
510 while ((Siz <= CopySize) && (Siz != 0))
519 NewBuf->buf = (char*) malloc(Siz);
520 if (NewBuf->buf == NULL)
525 NewBuf->BufSize = Siz;
527 memcpy(NewBuf->buf, ptr, CopySize);
528 NewBuf->buf[CopySize] = '\0';
529 NewBuf->BufUsed = CopySize;
532 NewBuf->buf[0] = '\0';
535 NewBuf->ConstBuf = 0;
543 * @ingroup StrBuf_DeConstructors
544 * @brief Set an existing buffer from a c-string
545 * @param Buf buffer to load
546 * @param ptr c-string to put into
547 * @param nChars set to -1 if we should work 0-terminated
548 * @returns the new length of the string
550 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
565 CopySize = strlen(ptr);
569 while ((Siz <= CopySize) && (Siz != 0))
577 if (Siz != Buf->BufSize)
578 IncreaseBuf(Buf, 0, Siz);
579 memcpy(Buf->buf, ptr, CopySize);
580 Buf->buf[CopySize] = '\0';
581 Buf->BufUsed = CopySize;
588 * @ingroup StrBuf_DeConstructors
589 * @brief use strbuf as wrapper for a string constant for easy handling
590 * @param StringConstant a string to wrap
591 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
593 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
597 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
598 NewBuf->buf = (char*) StringConstant;
599 NewBuf->BufSize = SizeOfStrConstant;
600 NewBuf->BufUsed = SizeOfStrConstant;
601 NewBuf->ConstBuf = 1;
610 * @ingroup StrBuf_DeConstructors
611 * @brief flush the content of a Buf; keep its struct
612 * @param buf Buffer to flush
614 int FlushStrBuf(StrBuf *buf)
616 if ((buf == NULL) || (buf->buf == NULL))
626 * @ingroup StrBuf_DeConstructors
627 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
628 * @param buf Buffer to wipe
630 int FLUSHStrBuf(StrBuf *buf)
636 if (buf->BufUsed > 0) {
637 memset(buf->buf, 0, buf->BufUsed);
644 int hFreeDbglog = -1;
647 * @ingroup StrBuf_DeConstructors
648 * @brief Release a Buffer
649 * Its a double pointer, so it can NULL your pointer
650 * so fancy SIG11 appear instead of random results
651 * @param FreeMe Pointer Pointer to the buffer to free
653 void FreeStrBuf (StrBuf **FreeMe)
658 dbg_FreeStrBuf(FreeMe, 'F');
660 if (!(*FreeMe)->ConstBuf)
661 free((*FreeMe)->buf);
667 * @ingroup StrBuf_DeConstructors
668 * @brief flatten a Buffer to the Char * we return
669 * Its a double pointer, so it can NULL your pointer
670 * so fancy SIG11 appear instead of random results
671 * The Callee then owns the buffer and is responsible for freeing it.
672 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
673 * @returns the pointer of the buffer; Callee owns the memory thereafter.
675 char *SmashStrBuf (StrBuf **SmashMe)
679 if ((SmashMe == NULL) || (*SmashMe == NULL))
682 dbg_FreeStrBuf(SmashMe, 'S');
684 Ret = (*SmashMe)->buf;
691 * @ingroup StrBuf_DeConstructors
692 * @brief Release the buffer
693 * If you want put your StrBuf into a Hash, use this as Destructor.
694 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
696 void HFreeStrBuf (void *VFreeMe)
698 StrBuf *FreeMe = (StrBuf*)VFreeMe;
702 dbg_FreeStrBuf(SmashMe, 'H');
704 if (!FreeMe->ConstBuf)
710 /*******************************************************************************
711 * Simple string transformations *
712 *******************************************************************************/
716 * @brief Wrapper around atol
718 long StrTol(const StrBuf *Buf)
723 return atol(Buf->buf);
730 * @brief Wrapper around atoi
732 int StrToi(const StrBuf *Buf)
736 if (Buf->BufUsed > 0)
737 return atoi(Buf->buf);
744 * @brief Checks to see if the string is a pure number
745 * @param Buf The buffer to inspect
746 * @returns 1 if its a pure number, 0, if not.
748 int StrBufIsNumber(const StrBuf *Buf) {
750 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
753 strtoll(Buf->buf, &pEnd, 10);
754 if (pEnd == Buf->buf)
756 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
758 if (Buf->buf == pEnd)
764 * @ingroup StrBuf_Filler
765 * @brief modifies a Single char of the Buf
766 * You can point to it via char* or a zero-based integer
767 * @param Buf The buffer to manipulate
768 * @param ptr char* to zero; use NULL if unused
769 * @param nThChar zero based pointer into the string; use -1 if unused
770 * @param PeekValue The Character to place into the position
772 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
777 nThChar = ptr - Buf->buf;
778 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
780 Buf->buf[nThChar] = PeekValue;
785 * @ingroup StrBuf_Filler
786 * @brief modifies a range of chars of the Buf
787 * You can point to it via char* or a zero-based integer
788 * @param Buf The buffer to manipulate
789 * @param ptr char* to zero; use NULL if unused
790 * @param nThChar zero based pointer into the string; use -1 if unused
791 * @param nChars how many chars are to be flushed?
792 * @param PookValue The Character to place into that area
794 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
799 nThChar = ptr - Buf->buf;
800 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
802 if (nThChar + nChars > Buf->BufUsed)
803 nChars = Buf->BufUsed - nThChar;
805 memset(Buf->buf + nThChar, PookValue, nChars);
806 /* just to be shure... */
807 Buf->buf[Buf->BufUsed] = 0;
812 * @ingroup StrBuf_Filler
813 * @brief Append a StringBuffer to the buffer
814 * @param Buf Buffer to modify
815 * @param AppendBuf Buffer to copy at the end of our buffer
816 * @param Offset Should we start copying from an offset?
818 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
820 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
821 (Buf == NULL) || (Buf->buf == NULL))
824 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
827 AppendBuf->BufUsed + Buf->BufUsed);
829 memcpy(Buf->buf + Buf->BufUsed,
830 AppendBuf->buf + Offset,
831 AppendBuf->BufUsed - Offset);
832 Buf->BufUsed += AppendBuf->BufUsed - Offset;
833 Buf->buf[Buf->BufUsed] = '\0';
838 * @ingroup StrBuf_Filler
839 * @brief Append a C-String to the buffer
840 * @param Buf Buffer to modify
841 * @param AppendBuf Buffer to copy at the end of our buffer
842 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
843 * @param Offset Should we start copying from an offset?
845 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
848 long BufSizeRequired;
850 if ((AppendBuf == NULL) || (Buf == NULL))
854 aps = strlen(AppendBuf + Offset);
856 aps = AppendSize - Offset;
858 BufSizeRequired = Buf->BufUsed + aps + 1;
859 if (Buf->BufSize <= BufSizeRequired)
860 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
862 memcpy(Buf->buf + Buf->BufUsed,
866 Buf->buf[Buf->BufUsed] = '\0';
870 * @ingroup StrBuf_Filler
871 * @brief sprintf like function appending the formated string to the buffer
872 * vsnprintf version to wrap into own calls
873 * @param Buf Buffer to extend by format and Params
874 * @param format printf alike format to add
875 * @param ap va_list containing the items for format
877 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
885 if ((Buf == NULL) || (format == NULL))
888 BufSize = Buf->BufSize;
889 nWritten = Buf->BufSize + 1;
890 Offset = Buf->BufUsed;
891 newused = Offset + nWritten;
893 while (newused >= BufSize) {
895 nWritten = vsnprintf(Buf->buf + Offset,
896 Buf->BufSize - Offset,
899 newused = Offset + nWritten;
900 if (newused >= Buf->BufSize) {
901 if (IncreaseBuf(Buf, 1, newused) == -1)
902 return; /* TODO: error handling? */
903 newused = Buf->BufSize + 1;
906 Buf->BufUsed = Offset + nWritten;
907 BufSize = Buf->BufSize;
914 * @ingroup StrBuf_Filler
915 * @brief sprintf like function appending the formated string to the buffer
916 * @param Buf Buffer to extend by format and Params
917 * @param format printf alike format to add
919 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
927 if ((Buf == NULL) || (format == NULL))
930 BufSize = Buf->BufSize;
931 nWritten = Buf->BufSize + 1;
932 Offset = Buf->BufUsed;
933 newused = Offset + nWritten;
935 while (newused >= BufSize) {
936 va_start(arg_ptr, format);
937 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
938 Buf->BufSize - Buf->BufUsed,
941 newused = Buf->BufUsed + nWritten;
942 if (newused >= Buf->BufSize) {
943 if (IncreaseBuf(Buf, 1, newused) == -1)
944 return; /* TODO: error handling? */
945 newused = Buf->BufSize + 1;
948 Buf->BufUsed += nWritten;
949 BufSize = Buf->BufSize;
956 * @ingroup StrBuf_Filler
957 * @brief sprintf like function putting the formated string into the buffer
958 * @param Buf Buffer to extend by format and Parameters
959 * @param format printf alike format to add
961 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
966 if ((Buf == NULL) || (format == NULL))
969 nWritten = Buf->BufSize + 1;
970 while (nWritten >= Buf->BufSize) {
971 va_start(arg_ptr, format);
972 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
974 if (nWritten >= Buf->BufSize) {
975 if (IncreaseBuf(Buf, 0, 0) == -1)
976 return; /* TODO: error handling? */
977 nWritten = Buf->BufSize + 1;
980 Buf->BufUsed = nWritten ;
985 * @ingroup StrBuf_Filler
986 * @brief Callback for cURL to append the webserver reply to a buffer
987 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
988 * @param size pre-defined by the cURL API; see man 3 curl for mre info
989 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
990 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
992 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1001 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1002 return size * nmemb;
1008 * @brief extracts a substring from Source into dest
1009 * @param dest buffer to place substring into
1010 * @param Source string to copy substring from
1011 * @param Offset chars to skip from start
1012 * @param nChars number of chars to copy
1013 * @returns the number of chars copied; may be different from nChars due to the size of Source
1015 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1017 size_t NCharsRemain;
1018 if (Offset > Source->BufUsed)
1024 if (Offset + nChars < Source->BufUsed)
1026 if ((nChars >= dest->BufSize) &&
1027 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1029 memcpy(dest->buf, Source->buf + Offset, nChars);
1030 dest->BufUsed = nChars;
1031 dest->buf[dest->BufUsed] = '\0';
1034 NCharsRemain = Source->BufUsed - Offset;
1035 if ((NCharsRemain >= dest->BufSize) &&
1036 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1038 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1039 dest->BufUsed = NCharsRemain;
1040 dest->buf[dest->BufUsed] = '\0';
1041 return NCharsRemain;
1046 * @brief Cut nChars from the start of the string
1047 * @param Buf Buffer to modify
1048 * @param nChars how many chars should be skipped?
1050 void StrBufCutLeft(StrBuf *Buf, int nChars)
1052 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1053 if (nChars >= Buf->BufUsed) {
1057 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1058 Buf->BufUsed -= nChars;
1059 Buf->buf[Buf->BufUsed] = '\0';
1064 * @brief Cut the trailing n Chars from the string
1065 * @param Buf Buffer to modify
1066 * @param nChars how many chars should be trunkated?
1068 void StrBufCutRight(StrBuf *Buf, int nChars)
1070 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1073 if (nChars >= Buf->BufUsed) {
1077 Buf->BufUsed -= nChars;
1078 Buf->buf[Buf->BufUsed] = '\0';
1083 * @brief Cut the string after n Chars
1084 * @param Buf Buffer to modify
1085 * @param AfternChars after how many chars should we trunkate the string?
1086 * @param At if non-null and points inside of our string, cut it there.
1088 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1090 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1092 AfternChars = At - Buf->buf;
1095 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1097 Buf->BufUsed = AfternChars;
1098 Buf->buf[Buf->BufUsed] = '\0';
1104 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1105 * @param Buf the string to modify
1107 void StrBufTrim(StrBuf *Buf)
1110 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1112 while ((Buf->BufUsed > 0) &&
1113 isspace(Buf->buf[Buf->BufUsed - 1]))
1117 Buf->buf[Buf->BufUsed] = '\0';
1119 if (Buf->BufUsed == 0) return;
1121 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1124 if (delta > 0) StrBufCutLeft(Buf, delta);
1128 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1129 * @param Buf the string to modify
1131 void StrBufSpaceToBlank(StrBuf *Buf)
1135 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1138 pche = pch + Buf->BufUsed;
1147 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1153 if ((Buf == NULL) || (Buf->buf == NULL))
1155 pLeft = pBuff = Buf->buf;
1156 while (pBuff != NULL) {
1158 pBuff = strchr(pBuff, leftboundary);
1167 pRight = strchr(pBuff, rightboundary);
1169 StrBufCutAt(Buf, 0, pRight);
1171 StrBufCutLeft(Buf, pLeft - Buf->buf);
1176 * @ingroup StrBuf_Filler
1177 * @brief uppercase the contents of a buffer
1178 * @param Buf the buffer to translate
1180 void StrBufUpCase(StrBuf *Buf)
1184 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1187 pche = pch + Buf->BufUsed;
1188 while (pch < pche) {
1189 *pch = toupper(*pch);
1196 * @ingroup StrBuf_Filler
1197 * @brief lowercase the contents of a buffer
1198 * @param Buf the buffer to translate
1200 void StrBufLowerCase(StrBuf *Buf)
1204 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1207 pche = pch + Buf->BufUsed;
1208 while (pch < pche) {
1209 *pch = tolower(*pch);
1215 /*******************************************************************************
1216 * a tokenizer that kills, maims, and destroys *
1217 *******************************************************************************/
1220 * @ingroup StrBuf_Tokenizer
1221 * @brief Replace a token at a given place with a given length by another token with given length
1222 * @param Buf String where to work on
1223 * @param where where inside of the Buf is the search-token
1224 * @param HowLong How long is the token to be replaced
1225 * @param Repl Token to insert at 'where'
1226 * @param ReplLen Length of repl
1227 * @returns -1 if fail else length of resulting Buf
1229 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1230 const char *Repl, long ReplLen)
1233 if ((Buf == NULL) ||
1234 (where > Buf->BufUsed) ||
1235 (where + HowLong > Buf->BufUsed))
1238 if (where + ReplLen - HowLong > Buf->BufSize)
1239 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1242 memmove(Buf->buf + where + ReplLen,
1243 Buf->buf + where + HowLong,
1244 Buf->BufUsed - where - HowLong);
1246 memcpy(Buf->buf + where,
1249 Buf->BufUsed += ReplLen - HowLong;
1251 return Buf->BufUsed;
1255 * @ingroup StrBuf_Tokenizer
1256 * @brief Counts the numbmer of tokens in a buffer
1257 * @param source String to count tokens in
1258 * @param tok Tokenizer char to count
1259 * @returns numbers of tokenizer chars found
1261 int StrBufNum_tokens(const StrBuf *source, char tok)
1265 if ((source == NULL) || (source->BufUsed == 0))
1267 if ((source->BufUsed == 1) && (*source->buf == tok))
1271 pche = pch + source->BufUsed;
1282 * @ingroup StrBuf_Tokenizer
1283 * @brief a string tokenizer
1284 * @param Source StringBuffer to read into
1285 * @param parmnum n'th Parameter to remove
1286 * @param separator tokenizer character
1287 * @returns -1 if not found, else length of token.
1289 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1292 char *d, *s, *end; /* dest, source */
1295 /* Find desired @parameter */
1296 end = Source->buf + Source->BufUsed;
1298 while ((d <= end) &&
1301 /* End of string, bail! */
1306 if (*d == separator) {
1311 if ((d == NULL) || (d >= end))
1312 return 0; /* @Parameter not found */
1314 /* Find next @parameter */
1316 while ((s <= end) &&
1317 (*s && *s != separator))
1321 if (*s == separator)
1325 /* Hack and slash */
1330 memmove(d, s, Source->BufUsed - (s - Source->buf));
1331 Source->BufUsed += ReducedBy;
1332 Source->buf[Source->BufUsed] = '\0';
1334 else if (d == Source->buf) {
1336 Source->BufUsed = 0;
1340 Source->BufUsed += ReducedBy;
1351 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1353 const StrBuf Temp = {
1366 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1370 * @ingroup StrBuf_Tokenizer
1371 * @brief a string tokenizer
1372 * @param dest Destination StringBuffer
1373 * @param Source StringBuffer to read into
1374 * @param parmnum n'th Parameter to extract
1375 * @param separator tokenizer character
1376 * @returns -1 if not found, else length of token.
1378 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1380 const char *s, *e; //* source * /
1381 int len = 0; //* running total length of extracted string * /
1382 int current_token = 0; //* token currently being processed * /
1385 dest->buf[0] = '\0';
1391 if ((Source == NULL) || (Source->BufUsed ==0)) {
1395 e = s + Source->BufUsed;
1398 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1400 while ((s < e) && !IsEmptyStr(s)) {
1401 if (*s == separator) {
1404 if (len >= dest->BufSize) {
1405 dest->BufUsed = len;
1406 if (IncreaseBuf(dest, 1, -1) < 0) {
1411 if ( (current_token == parmnum) &&
1412 (*s != separator)) {
1413 dest->buf[len] = *s;
1416 else if (current_token > parmnum) {
1422 dest->buf[len] = '\0';
1423 dest->BufUsed = len;
1425 if (current_token < parmnum) {
1426 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1429 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1438 * @ingroup StrBuf_Tokenizer
1439 * @brief a string tokenizer to fetch an integer
1440 * @param Source String containing tokens
1441 * @param parmnum n'th Parameter to extract
1442 * @param separator tokenizer character
1443 * @returns 0 if not found, else integer representation of the token
1445 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1455 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1462 * @ingroup StrBuf_Tokenizer
1463 * @brief a string tokenizer to fetch a long integer
1464 * @param Source String containing tokens
1465 * @param parmnum n'th Parameter to extract
1466 * @param separator tokenizer character
1467 * @returns 0 if not found, else long integer representation of the token
1469 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1479 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1487 * @ingroup StrBuf_Tokenizer
1488 * @brief a string tokenizer to fetch an unsigned long
1489 * @param Source String containing tokens
1490 * @param parmnum n'th Parameter to extract
1491 * @param separator tokenizer character
1492 * @returns 0 if not found, else unsigned long representation of the token
1494 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1505 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1509 return (unsigned long) atol(pnum);
1518 * @ingroup StrBuf_NextTokenizer
1519 * @brief a string tokenizer; Bounds checker
1520 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1521 * @param Source our tokenbuffer
1522 * @param pStart the token iterator pointer to inspect
1523 * @returns whether the revolving pointer is inside of the search range
1525 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1527 if ((Source == NULL) ||
1528 (*pStart == StrBufNOTNULL) ||
1529 (Source->BufUsed == 0))
1533 if (*pStart == NULL)
1537 else if (*pStart > Source->buf + Source->BufUsed)
1541 else if (*pStart <= Source->buf)
1550 * @ingroup StrBuf_NextTokenizer
1551 * @brief a string tokenizer
1552 * @param dest Destination StringBuffer
1553 * @param Source StringBuffer to read into
1554 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1555 * @param separator tokenizer
1556 * @returns -1 if not found, else length of token.
1558 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1560 const char *s; /* source */
1561 const char *EndBuffer; /* end stop of source buffer */
1562 int current_token = 0; /* token currently being processed */
1563 int len = 0; /* running total length of extracted string */
1565 if ((Source == NULL) ||
1566 (Source->BufUsed == 0) )
1568 *pStart = StrBufNOTNULL;
1574 EndBuffer = Source->buf + Source->BufUsed;
1578 dest->buf[0] = '\0';
1583 *pStart = EndBuffer + 1;
1587 if (*pStart == NULL)
1589 *pStart = Source->buf; /* we're starting to examine this buffer. */
1591 else if ((*pStart < Source->buf) ||
1592 (*pStart > EndBuffer ) )
1594 return -1; /* no more tokens to find. */
1598 /* start to find the next token */
1599 while ((s <= EndBuffer) &&
1600 (current_token == 0) )
1602 if (*s == separator)
1604 /* we found the next token */
1608 if (len >= dest->BufSize)
1610 /* our Dest-buffer isn't big enough, increase it. */
1611 dest->BufUsed = len;
1613 if (IncreaseBuf(dest, 1, -1) < 0) {
1614 /* WHUT? no more mem? bail out. */
1621 if ( (current_token == 0 ) && /* are we in our target token? */
1622 (!IsEmptyStr(s) ) &&
1623 (separator != *s) ) /* don't copy the token itself */
1625 dest->buf[len] = *s; /* Copy the payload */
1626 ++len; /* remember the bigger size. */
1632 /* did we reach the end? */
1633 if ((s > EndBuffer)) {
1634 EndBuffer = StrBufNOTNULL;
1635 *pStart = EndBuffer;
1638 *pStart = s; /* remember the position for the next run */
1641 /* sanitize our extracted token */
1642 dest->buf[len] = '\0';
1643 dest->BufUsed = len;
1650 * @ingroup StrBuf_NextTokenizer
1651 * @brief a string tokenizer
1652 * @param Source StringBuffer to read from
1653 * @param pStart pointer to the end of the last token. Feed with NULL.
1654 * @param separator tokenizer character
1655 * @param nTokens number of tokens to fastforward over
1656 * @returns -1 if not found, else length of token.
1658 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1660 const char *s, *EndBuffer; //* source * /
1661 int len = 0; //* running total length of extracted string * /
1662 int current_token = 0; //* token currently being processed * /
1664 if ((Source == NULL) ||
1665 (Source->BufUsed ==0)) {
1669 return Source->BufUsed;
1671 if (*pStart == NULL)
1672 *pStart = Source->buf;
1674 EndBuffer = Source->buf + Source->BufUsed;
1676 if ((*pStart < Source->buf) ||
1677 (*pStart > EndBuffer)) {
1685 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1687 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1688 if (*s == separator) {
1691 if (current_token >= nTokens) {
1703 * @ingroup StrBuf_NextTokenizer
1704 * @brief a string tokenizer to fetch an integer
1705 * @param Source StringBuffer to read from
1706 * @param pStart Cursor on the tokenstring
1707 * @param separator tokenizer character
1708 * @returns 0 if not found, else integer representation of the token
1710 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1720 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1727 * @ingroup StrBuf_NextTokenizer
1728 * @brief a string tokenizer to fetch a long integer
1729 * @param Source StringBuffer to read from
1730 * @param pStart Cursor on the tokenstring
1731 * @param separator tokenizer character
1732 * @returns 0 if not found, else long integer representation of the token
1734 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1744 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1752 * @ingroup StrBuf_NextTokenizer
1753 * @brief a string tokenizer to fetch an unsigned long
1754 * @param Source StringBuffer to read from
1755 * @param pStart Cursor on the tokenstring
1756 * @param separator tokenizer character
1757 * @returns 0 if not found, else unsigned long representation of the token
1759 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1770 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1774 return (unsigned long) atol(pnum);
1784 /*******************************************************************************
1785 * Escape Appending *
1786 *******************************************************************************/
1789 * @ingroup StrBuf_DeEnCoder
1790 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1791 * @param OutBuf the output buffer
1792 * @param In Buffer to encode
1793 * @param PlainIn way in from plain old c strings
1795 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1797 const char *pch, *pche;
1801 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1803 if (PlainIn != NULL) {
1804 len = strlen(PlainIn);
1810 pche = pch + In->BufUsed;
1817 pt = OutBuf->buf + OutBuf->BufUsed;
1818 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1820 while (pch < pche) {
1822 IncreaseBuf(OutBuf, 1, -1);
1823 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1824 pt = OutBuf->buf + OutBuf->BufUsed;
1827 if((*pch >= 'a' && *pch <= 'z') ||
1828 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1829 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1830 (*pch == '!') || (*pch == '_') ||
1831 (*pch == ',') || (*pch == '.'))
1838 *(pt + 1) = HexList[(unsigned char)*pch][0];
1839 *(pt + 2) = HexList[(unsigned char)*pch][1];
1841 OutBuf->BufUsed += 3;
1849 * @ingroup StrBuf_DeEnCoder
1850 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1851 * @param OutBuf the output buffer
1852 * @param In Buffer to encode
1853 * @param PlainIn way in from plain old c strings
1855 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1857 const char *pch, *pche;
1861 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1863 if (PlainIn != NULL) {
1864 len = strlen(PlainIn);
1870 pche = pch + In->BufUsed;
1877 pt = OutBuf->buf + OutBuf->BufUsed;
1878 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1880 while (pch < pche) {
1882 IncreaseBuf(OutBuf, 1, -1);
1883 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1884 pt = OutBuf->buf + OutBuf->BufUsed;
1887 if((*pch >= 'a' && *pch <= 'z') ||
1888 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1889 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1890 (*pch == '!') || (*pch == '_') ||
1891 (*pch == ',') || (*pch == '.'))
1898 *(pt + 1) = HexList[(unsigned char)*pch][0];
1899 *(pt + 2) = HexList[(unsigned char)*pch][1];
1901 OutBuf->BufUsed += 3;
1909 * @ingroup StrBuf_DeEnCoder
1910 * @brief append a string in hex encoding to the buffer
1911 * @param OutBuf the output buffer
1912 * @param In Buffer to encode
1913 * @param PlainIn way in from plain old c strings
1914 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1916 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1918 const unsigned char *pch, *pche;
1922 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1924 if (PlainIn != NULL) {
1926 len = strlen((const char*)PlainIn);
1933 pch = (const unsigned char*)In->buf;
1934 pche = pch + In->BufUsed;
1941 pt = OutBuf->buf + OutBuf->BufUsed;
1942 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1944 while (pch < pche) {
1946 IncreaseBuf(OutBuf, 1, -1);
1947 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1948 pt = OutBuf->buf + OutBuf->BufUsed;
1951 *pt = HexList[*pch][0];
1953 *pt = HexList[*pch][1];
1954 pt ++; pch ++; OutBuf->BufUsed += 2;
1960 * @ingroup StrBuf_DeEnCoder
1961 * @brief append a string in hex encoding to the buffer
1962 * @param OutBuf the output buffer
1963 * @param In Buffer to encode
1964 * @param PlainIn way in from plain old c strings
1966 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1968 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1972 * @ingroup StrBuf_DeEnCoder
1973 * @brief Append a string, escaping characters which have meaning in HTML.
1975 * @param Target target buffer
1976 * @param Source source buffer; set to NULL if you just have a C-String
1977 * @param PlainIn Plain-C string to append; set to NULL if unused
1978 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1979 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1980 * if set to 2, linebreaks are replaced by <br/>
1982 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1984 const char *aptr, *eiptr;
1988 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1991 if (PlainIn != NULL) {
1993 len = strlen(PlainIn);
1998 eiptr = aptr + Source->BufUsed;
1999 len = Source->BufUsed;
2005 bptr = Target->buf + Target->BufUsed;
2006 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2008 while (aptr < eiptr){
2010 IncreaseBuf(Target, 1, -1);
2011 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2012 bptr = Target->buf + Target->BufUsed;
2015 memcpy(bptr, "<", 4);
2017 Target->BufUsed += 4;
2019 else if (*aptr == '>') {
2020 memcpy(bptr, ">", 4);
2022 Target->BufUsed += 4;
2024 else if (*aptr == '&') {
2025 memcpy(bptr, "&", 5);
2027 Target->BufUsed += 5;
2029 else if (*aptr == '"') {
2030 memcpy(bptr, """, 6);
2032 Target->BufUsed += 6;
2034 else if (*aptr == '\'') {
2035 memcpy(bptr, "'", 5);
2037 Target->BufUsed += 5;
2039 else if (*aptr == LB) {
2044 else if (*aptr == RB) {
2049 else if (*aptr == QU) {
2054 else if ((*aptr == 32) && (nbsp == 1)) {
2055 memcpy(bptr, " ", 6);
2057 Target->BufUsed += 6;
2059 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2060 *bptr='\0'; /* nothing */
2062 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2063 memcpy(bptr, "<br/>", 11);
2065 Target->BufUsed += 11;
2069 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2070 *bptr='\0'; /* nothing */
2080 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2082 return Target->BufUsed;
2086 * @ingroup StrBuf_DeEnCoder
2087 * @brief Append a string, escaping characters which have meaning in HTML.
2088 * Converts linebreaks into blanks; escapes single quotes
2089 * @param Target target buffer
2090 * @param Source source buffer; set to NULL if you just have a C-String
2091 * @param PlainIn Plain-C string to append; set to NULL if unused
2093 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2095 const char *aptr, *eiptr;
2099 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2102 if (PlainIn != NULL) {
2104 len = strlen(PlainIn);
2109 eiptr = aptr + Source->BufUsed;
2110 len = Source->BufUsed;
2116 eptr = Target->buf + Target->BufSize - 8;
2117 tptr = Target->buf + Target->BufUsed;
2119 while (aptr < eiptr){
2121 IncreaseBuf(Target, 1, -1);
2122 eptr = Target->buf + Target->BufSize - 8;
2123 tptr = Target->buf + Target->BufUsed;
2126 if (*aptr == '\n') {
2130 else if (*aptr == '\r') {
2134 else if (*aptr == '\'') {
2140 Target->BufUsed += 5;
2153 * @ingroup StrBuf_DeEnCoder
2154 * @brief Append a string, escaping characters which have meaning in ICAL.
2156 * @param Target target buffer
2157 * @param Source source buffer; set to NULL if you just have a C-String
2158 * @param PlainIn Plain-C string to append; set to NULL if unused
2160 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2162 const char *aptr, *eiptr;
2166 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2169 if (PlainIn != NULL) {
2171 len = strlen(PlainIn);
2176 eiptr = aptr + Source->BufUsed;
2177 len = Source->BufUsed;
2183 eptr = Target->buf + Target->BufSize - 8;
2184 tptr = Target->buf + Target->BufUsed;
2186 while (aptr < eiptr){
2187 if(tptr + 3 >= eptr) {
2188 IncreaseBuf(Target, 1, -1);
2189 eptr = Target->buf + Target->BufSize - 8;
2190 tptr = Target->buf + Target->BufUsed;
2193 if (*aptr == '\n') {
2200 else if (*aptr == '\r') {
2207 else if (*aptr == ',') {
2223 * @ingroup StrBuf_DeEnCoder
2224 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2226 * @param Target target buffer
2227 * @param Source source buffer; set to NULL if you just have a C-String
2228 * @param PlainIn Plain-C string to append; set to NULL if unused
2229 * @returns size of result or -1
2231 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2233 const char *aptr, *eiptr;
2238 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2241 if (PlainIn != NULL) {
2243 len = strlen(PlainIn);
2248 eiptr = aptr + Source->BufUsed;
2249 len = Source->BufUsed;
2255 bptr = Target->buf + Target->BufUsed;
2256 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2258 while (aptr < eiptr){
2260 IncreaseBuf(Target, 1, -1);
2261 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2262 bptr = Target->buf + Target->BufUsed;
2266 memcpy(bptr, HKEY("\\n"));
2268 Target->BufUsed += 2;
2271 memcpy(bptr, HKEY("\\r"));
2273 Target->BufUsed += 2;
2280 Target->BufUsed += 2;
2283 if ((*(aptr + 1) == 'u') &&
2284 isxdigit(*(aptr + 2)) &&
2285 isxdigit(*(aptr + 3)) &&
2286 isxdigit(*(aptr + 4)) &&
2287 isxdigit(*(aptr + 5)))
2288 { /* oh, a unicode escaper. let it pass through. */
2289 memcpy(bptr, aptr, 6);
2292 Target->BufUsed += 6;
2300 Target->BufUsed += 2;
2308 Target->BufUsed += 2;
2315 Target->BufUsed += 2;
2322 Target->BufUsed += 2;
2325 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2326 while (IsUtf8Sequence > 0){
2329 if (--IsUtf8Sequence)
2337 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2339 return Target->BufUsed;
2343 * @ingroup StrBuf_DeEnCoder
2344 * @brief Append a string, escaping characters which have meaning in HTML + json.
2346 * @param Target target buffer
2347 * @param Source source buffer; set to NULL if you just have a C-String
2348 * @param PlainIn Plain-C string to append; set to NULL if unused
2349 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2350 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2351 * if set to 2, linebreaks are replaced by <br/>
2353 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2355 const char *aptr, *eiptr;
2358 int IsUtf8Sequence = 0;
2360 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2363 if (PlainIn != NULL) {
2365 len = strlen(PlainIn);
2370 eiptr = aptr + Source->BufUsed;
2371 len = Source->BufUsed;
2377 bptr = Target->buf + Target->BufUsed;
2378 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2380 while (aptr < eiptr){
2382 IncreaseBuf(Target, 1, -1);
2383 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2384 bptr = Target->buf + Target->BufUsed;
2388 memcpy(bptr, HKEY("<"));
2390 Target->BufUsed += 4;
2393 memcpy(bptr, HKEY(">"));
2395 Target->BufUsed += 4;
2398 memcpy(bptr, HKEY("&"));
2400 Target->BufUsed += 5;
2413 switch (nolinebreaks) {
2415 *bptr='\0'; /* nothing */
2418 memcpy(bptr, HKEY("<br/>"));
2420 Target->BufUsed += 11;
2423 memcpy(bptr, HKEY("\\n"));
2425 Target->BufUsed += 2;
2429 switch (nolinebreaks) {
2432 *bptr='\0'; /* nothing */
2435 memcpy(bptr, HKEY("\\r"));
2437 Target->BufUsed += 2;
2447 Target->BufUsed += 2;
2450 if ((*(aptr + 1) == 'u') &&
2451 isxdigit(*(aptr + 2)) &&
2452 isxdigit(*(aptr + 3)) &&
2453 isxdigit(*(aptr + 4)) &&
2454 isxdigit(*(aptr + 5)))
2455 { /* oh, a unicode escaper. let it pass through. */
2456 memcpy(bptr, aptr, 6);
2459 Target->BufUsed += 6;
2467 Target->BufUsed += 2;
2475 Target->BufUsed += 2;
2482 Target->BufUsed += 2;
2489 Target->BufUsed += 2;
2493 memcpy(bptr, HKEY(" "));
2495 Target->BufUsed += 6;
2499 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2500 while (IsUtf8Sequence > 0){
2503 if (--IsUtf8Sequence)
2511 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2513 return Target->BufUsed;
2517 * @ingroup StrBuf_DeEnCoder
2518 * @brief unhide special chars hidden to the HTML escaper
2519 * @param target buffer to put the unescaped string in
2520 * @param source buffer to unescape
2522 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2527 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2533 FlushStrBuf(target);
2535 len = source->BufUsed;
2536 for (a = 0; a < len; ++a) {
2537 if (target->BufUsed >= target->BufSize)
2538 IncreaseBuf(target, 1, -1);
2540 if (source->buf[a] == '=') {
2541 hex[0] = source->buf[a + 1];
2542 hex[1] = source->buf[a + 2];
2545 sscanf(hex, "%02x", &b);
2546 target->buf[target->BufUsed] = b;
2547 target->buf[++target->BufUsed] = 0;
2551 target->buf[target->BufUsed] = source->buf[a];
2552 target->buf[++target->BufUsed] = 0;
2559 * @ingroup StrBuf_DeEnCoder
2560 * @brief hide special chars from the HTML escapers and friends
2561 * @param target buffer to put the escaped string in
2562 * @param source buffer to escape
2564 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2569 FlushStrBuf(target);
2571 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2576 len = source->BufUsed;
2577 for (i=0; i<len; ++i) {
2578 if (target->BufUsed + 4 >= target->BufSize)
2579 IncreaseBuf(target, 1, -1);
2580 if ( (isalnum(source->buf[i])) ||
2581 (source->buf[i]=='-') ||
2582 (source->buf[i]=='_') ) {
2583 target->buf[target->BufUsed++] = source->buf[i];
2586 sprintf(&target->buf[target->BufUsed],
2588 (0xFF &source->buf[i]));
2589 target->BufUsed += 3;
2592 target->buf[target->BufUsed + 1] = '\0';
2596 /*******************************************************************************
2597 * Quoted Printable de/encoding *
2598 *******************************************************************************/
2601 * @ingroup StrBuf_DeEnCoder
2602 * @brief decode a buffer from base 64 encoding; destroys original
2603 * @param Buf Buffor to transform
2605 int StrBufDecodeBase64(StrBuf *Buf)
2609 if (Buf == NULL) return -1;
2611 xferbuf = (char*) malloc(Buf->BufSize);
2613 siz = CtdlDecodeBase64(xferbuf,
2623 * @ingroup StrBuf_DeEnCoder
2624 * @brief decode a buffer from base 64 encoding; destroys original
2625 * @param Buf Buffor to transform
2627 int StrBufDecodeHex(StrBuf *Buf)
2630 char *pch, *pche, *pchi;
2632 if (Buf == NULL) return -1;
2634 pch = pchi = Buf->buf;
2635 pche = pch + Buf->BufUsed;
2637 while (pchi < pche){
2638 ch = decode_hex(pchi);
2645 Buf->BufUsed = pch - Buf->buf;
2646 return Buf->BufUsed;
2650 * @ingroup StrBuf_DeEnCoder
2651 * @brief replace all chars >0x20 && < 0x7F with Mute
2652 * @param Mute char to put over invalid chars
2653 * @param Buf Buffor to transform
2655 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2659 if (Buf == NULL) return -1;
2660 pch = (unsigned char *)Buf->buf;
2661 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2662 if ((*pch < 0x20) || (*pch > 0x7F))
2666 return Buf->BufUsed;
2671 * @ingroup StrBuf_DeEnCoder
2672 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2673 * @param Buf Buffer to translate
2674 * @param StripBlanks Reduce several blanks to one?
2676 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2685 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2686 Buf->buf[Buf->BufUsed - 1] = '\0';
2691 while (a < Buf->BufUsed) {
2692 if (Buf->buf[a] == '+')
2694 else if (Buf->buf[a] == '%') {
2695 /* don't let % chars through, rather truncate the input. */
2696 if (a + 2 > Buf->BufUsed) {
2701 hex[0] = Buf->buf[a + 1];
2702 hex[1] = Buf->buf[a + 2];
2705 sscanf(hex, "%02x", &b);
2706 Buf->buf[a] = (char) b;
2707 len = Buf->BufUsed - a - 2;
2709 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2721 * @ingroup StrBuf_DeEnCoder
2722 * @brief RFC2047-encode a header field if necessary.
2723 * If no non-ASCII characters are found, the string
2724 * will be copied verbatim without encoding.
2726 * @param target Target buffer.
2727 * @param source Source string to be encoded.
2728 * @returns encoded length; -1 if non success.
2730 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2732 const char headerStr[] = "=?UTF-8?Q?";
2733 int need_to_encode = 0;
2737 if ((source == NULL) ||
2741 while ((i < source->BufUsed) &&
2742 (!IsEmptyStr (&source->buf[i])) &&
2743 (need_to_encode == 0)) {
2744 if (((unsigned char) source->buf[i] < 32) ||
2745 ((unsigned char) source->buf[i] > 126)) {
2751 if (!need_to_encode) {
2752 if (*target == NULL) {
2753 *target = NewStrBufPlain(source->buf, source->BufUsed);
2756 FlushStrBuf(*target);
2757 StrBufAppendBuf(*target, source, 0);
2760 return (*target)->BufUsed;
2764 if (*target == NULL)
2765 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2766 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2767 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2768 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2769 (*target)->BufUsed = sizeof(headerStr) - 1;
2770 for (i=0; (i < source->BufUsed); ++i) {
2771 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2772 IncreaseBuf(*target, 1, 0);
2773 ch = (unsigned char) source->buf[i];
2783 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2784 (*target)->BufUsed += 3;
2788 (*target)->buf[(*target)->BufUsed] = '_';
2790 (*target)->buf[(*target)->BufUsed] = ch;
2791 (*target)->BufUsed++;
2795 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2796 IncreaseBuf(*target, 1, 0);
2798 (*target)->buf[(*target)->BufUsed++] = '?';
2799 (*target)->buf[(*target)->BufUsed++] = '=';
2800 (*target)->buf[(*target)->BufUsed] = '\0';
2801 return (*target)->BufUsed;;
2806 static void AddRecipient(StrBuf *Target,
2808 StrBuf *EmailAddress,
2813 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2814 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2816 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2817 StrBufRFC2047encode(&EncBuf, UserName);
2818 StrBufAppendBuf(Target, EncBuf, 0);
2819 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2820 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2822 if (StrLength(EmailAddress) > 0){
2823 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2824 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2825 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2831 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2832 * \param Recp Source list of email recipients
2833 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2834 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2835 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2836 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2838 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2840 StrBuf *EmailAddress,
2844 const char *pch, *pche;
2845 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2847 if ((Recp == NULL) || (StrLength(Recp) == 0))
2851 pche = pch + StrLength(Recp);
2853 if (!CheckEncode(pch, -1, pche))
2854 return NewStrBufDup(Recp);
2856 Target = NewStrBufPlain(NULL, StrLength(Recp));
2858 while ((pch != NULL) && (pch < pche))
2860 while (isspace(*pch)) pch++;
2861 UserEnd = EmailStart = EmailEnd = NULL;
2863 if ((*pch == '"') || (*pch == '\'')) {
2864 UserStart = pch + 1;
2866 UserEnd = strchr(UserStart, *pch);
2867 if (UserEnd == NULL)
2868 break; ///TODO: Userfeedback??
2869 EmailStart = UserEnd + 1;
2870 while (isspace(*EmailStart))
2872 if (UserEnd == UserStart) {
2873 UserStart = UserEnd = NULL;
2876 if (*EmailStart == '<') {
2878 EmailEnd = strchr(EmailStart, '>');
2879 if (EmailEnd == NULL)
2880 EmailEnd = strchr(EmailStart, ',');
2884 EmailEnd = strchr(EmailStart, ',');
2886 if (EmailEnd == NULL)
2893 EmailEnd = strchr(UserStart, ',');
2894 if (EmailEnd == NULL) {
2895 EmailEnd = strchr(pch, '>');
2897 if (EmailEnd != NULL) {
2907 while ((EmailEnd > UserStart) && !gt &&
2908 ((*EmailEnd == ',') ||
2909 (*EmailEnd == '>') ||
2910 (isspace(*EmailEnd))))
2912 if (*EmailEnd == '>')
2917 if (EmailEnd == UserStart)
2921 EmailStart = strchr(UserStart, '<');
2922 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2924 UserEnd = EmailStart;
2926 while ((UserEnd > UserStart) &&
2927 isspace (*(UserEnd - 1)))
2930 if (UserStart >= UserEnd)
2931 UserStart = UserEnd = NULL;
2933 else { /* this is a local recipient... no domain, just a realname */
2934 EmailStart = UserStart;
2935 At = strchr(EmailStart, '@');
2941 EmailStart = UserStart;
2947 if ((UserStart != NULL) && (UserEnd != NULL))
2948 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2949 else if ((UserStart != NULL) && (UserEnd == NULL))
2950 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2952 FlushStrBuf(UserName);
2954 if ((EmailStart != NULL) && (EmailEnd != NULL))
2955 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2956 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2957 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2959 FlushStrBuf(EmailAddress);
2961 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2966 if ((pch != NULL) && (*pch == ','))
2968 if (pch != NULL) while (isspace(*pch))
2977 * @brief replaces all occurances of 'search' by 'replace'
2978 * @param buf Buffer to modify
2979 * @param search character to search
2980 * @param replace character to replace search by
2982 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2987 for (i=0; i<buf->BufUsed; i++)
2988 if (buf->buf[i] == search)
2989 buf->buf[i] = replace;
2995 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2996 * @param buf Buffer to modify
2998 void StrBufToUnixLF(StrBuf *buf)
3000 char *pche, *pchS, *pchT;
3004 pche = buf->buf + buf->BufUsed;
3005 pchS = pchT = buf->buf;
3011 if (*pchS != '\n') {
3020 buf->BufUsed = pchT - buf->buf;
3024 /*******************************************************************************
3025 * Iconv Wrapper; RFC822 de/encoding *
3026 *******************************************************************************/
3029 * @ingroup StrBuf_DeEnCoder
3030 * @brief Wrapper around iconv_open()
3031 * Our version adds aliases for non-standard Microsoft charsets
3032 * such as 'MS950', aliasing them to names like 'CP950'
3034 * @param tocode Target encoding
3035 * @param fromcode Source encoding
3036 * @param pic anonimized pointer to iconv struct
3038 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3041 iconv_t ic = (iconv_t)(-1) ;
3042 ic = iconv_open(tocode, fromcode);
3043 if (ic == (iconv_t)(-1) ) {
3044 char alias_fromcode[64];
3045 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3046 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3047 alias_fromcode[0] = 'C';
3048 alias_fromcode[1] = 'P';
3049 ic = iconv_open(tocode, alias_fromcode);
3052 *(iconv_t *)pic = ic;
3058 * @ingroup StrBuf_DeEnCoder
3059 * @brief find one chunk of a RFC822 encoded string
3060 * @param Buffer where to search
3061 * @param bptr where to start searching
3062 * @returns found position, NULL if none.
3064 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3067 /* Find the next ?Q? */
3068 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3071 end = strchr(bptr + 2, '?');
3076 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3077 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3078 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3079 (*(end + 2) == '?')) {
3080 /* skip on to the end of the cluster, the next ?= */
3081 end = strstr(end + 3, "?=");
3084 /* sort of half valid encoding, try to find an end. */
3085 end = strstr(bptr, "?=");
3092 * @ingroup StrBuf_DeEnCoder
3093 * @brief convert one buffer according to the preselected iconv pointer PIC
3094 * @param ConvertBuf buffer we need to translate
3095 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3096 * @param pic Pointer to the iconv-session Object
3098 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3104 char *ibuf; /**< Buffer of characters to be converted */
3105 char *obuf; /**< Buffer for converted characters */
3106 size_t ibuflen; /**< Length of input buffer */
3107 size_t obuflen; /**< Length of output buffer */
3110 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3113 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3114 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3115 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3117 ic = *(iconv_t*)pic;
3118 ibuf = ConvertBuf->buf;
3119 ibuflen = ConvertBuf->BufUsed;
3121 obuflen = TmpBuf->BufSize;
3123 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3126 if (errno == E2BIG) {
3128 IncreaseBuf(TmpBuf, 0, 0);
3133 else if (errno == EILSEQ){
3134 /* hm, invalid utf8 sequence... what to do now? */
3135 /* An invalid multibyte sequence has been encountered in the input */
3137 else if (errno == EINVAL) {
3138 /* An incomplete multibyte sequence has been encountered in the input. */
3141 FlushStrBuf(TmpBuf);
3144 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3145 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3147 /* little card game: wheres the red lady? */
3148 SwapBuffers(ConvertBuf, TmpBuf);
3149 FlushStrBuf(TmpBuf);
3156 * @ingroup StrBuf_DeEnCoder
3157 * @brief catches one RFC822 encoded segment, and decodes it.
3158 * @param Target buffer to fill with result
3159 * @param DecodeMe buffer with stuff to process
3160 * @param SegmentStart points to our current segment in DecodeMe
3161 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3162 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3163 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3164 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3166 inline static void DecodeSegment(StrBuf *Target,
3167 const StrBuf *DecodeMe,
3168 const char *SegmentStart,
3169 const char *SegmentEnd,
3171 StrBuf *ConvertBuf2,
3172 StrBuf *FoundCharset)
3178 iconv_t ic = (iconv_t)(-1);
3182 /* Now we handle foreign character sets properly encoded
3183 * in RFC2047 format.
3185 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3186 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3187 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3188 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3189 if (FoundCharset != NULL) {
3190 FlushStrBuf(FoundCharset);
3191 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3193 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3194 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3196 *encoding = toupper(*encoding);
3197 if (*encoding == 'B') { /**< base64 */
3198 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3199 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3200 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3202 ConvertBuf->BufUsed);
3204 else if (*encoding == 'Q') { /**< quoted-printable */
3208 while (pos < ConvertBuf->BufUsed)
3210 if (ConvertBuf->buf[pos] == '_')
3211 ConvertBuf->buf[pos] = ' ';
3215 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3216 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3218 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3221 ConvertBuf->BufUsed);
3224 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3227 ctdl_iconv_open("UTF-8", charset, &ic);
3228 if (ic != (iconv_t)(-1) ) {
3230 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3231 StrBufAppendBuf(Target, ConvertBuf2, 0);
3236 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3242 * @ingroup StrBuf_DeEnCoder
3243 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3244 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3245 * @param Target where to put the decoded string to
3246 * @param DecodeMe buffer with encoded string
3247 * @param DefaultCharset if we don't find one, which should we use?
3248 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3249 * put it here for later use where no string might be known.
3251 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3254 StrBuf *ConvertBuf2;
3255 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3256 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3258 StrBuf_RFC822_2_Utf8(Target,
3264 FreeStrBuf(&ConvertBuf);
3265 FreeStrBuf(&ConvertBuf2);
3269 * @ingroup StrBuf_DeEnCoder
3270 * @brief Handle subjects with RFC2047 encoding such as:
3271 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3272 * @param Target where to put the decoded string to
3273 * @param DecodeMe buffer with encoded string
3274 * @param DefaultCharset if we don't find one, which should we use?
3275 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3276 * put it here for later use where no string might be known.
3277 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3278 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3280 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3281 const StrBuf *DecodeMe,
3282 const StrBuf* DefaultCharset,
3283 StrBuf *FoundCharset,
3285 StrBuf *ConvertBuf2)
3287 StrBuf *DecodedInvalidBuf = NULL;
3288 const StrBuf *DecodeMee = DecodeMe;
3289 const char *start, *end, *next, *nextend, *ptr = NULL;
3291 iconv_t ic = (iconv_t)(-1) ;
3296 int illegal_non_rfc2047_encoding = 0;
3299 if (DecodeMe == NULL)
3301 /* Sometimes, badly formed messages contain strings which were simply
3302 * written out directly in some foreign character set instead of
3303 * using RFC2047 encoding. This is illegal but we will attempt to
3304 * handle it anyway by converting from a user-specified default
3305 * charset to UTF-8 if we see any nonprintable characters.
3308 for (i=0; i<DecodeMe->BufUsed; ++i) {
3309 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3310 illegal_non_rfc2047_encoding = 1;
3315 if ((illegal_non_rfc2047_encoding) &&
3316 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3317 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3320 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3321 if (ic != (iconv_t)(-1) ) {
3322 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3323 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3324 DecodeMee = DecodedInvalidBuf;
3330 /* pre evaluate the first pair */
3332 start = strstr(DecodeMee->buf, "=?");
3333 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3335 end = FindNextEnd (DecodeMee, start + 2);
3337 StrBufAppendBuf(Target, DecodeMee, 0);
3338 FreeStrBuf(&DecodedInvalidBuf);
3343 if (start != DecodeMee->buf) {
3346 nFront = start - DecodeMee->buf;
3347 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3350 * Since spammers will go to all sorts of absurd lengths to get their
3351 * messages through, there are LOTS of corrupt headers out there.
3352 * So, prevent a really badly formed RFC2047 header from throwing
3353 * this function into an infinite loop.
3355 while ((start != NULL) &&
3362 DecodeSegment(Target,
3370 next = strstr(end, "=?");
3372 if ((next != NULL) &&
3374 nextend = FindNextEnd(DecodeMee, next);
3375 if (nextend == NULL)
3378 /* did we find two partitions */
3379 if ((next != NULL) &&
3383 while ((ptr < next) &&
3390 * did we find a gab just filled with blanks?
3391 * if not, copy its stuff over.
3395 StrBufAppendBufPlain(Target,
3401 /* our next-pair is our new first pair now. */
3407 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3408 if ((end != NULL) && (end < nextend)) {
3410 while ( (ptr < nextend) &&
3417 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3419 FreeStrBuf(&DecodedInvalidBuf);
3422 /*******************************************************************************
3423 * Manipulating UTF-8 Strings *
3424 *******************************************************************************/
3428 * @brief evaluate the length of an utf8 special character sequence
3429 * @param Char the character to examine
3430 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3432 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3435 unsigned char test = (1<<7);
3437 if ((*CharS & 0xC0) != 0xC0)
3441 ((test & ((unsigned char)*CharS)) != 0))
3446 if ((n > 6) || ((CharE - CharS) < n))
3453 * @brief detect whether this char starts an utf-8 encoded char
3454 * @param Char character to inspect
3455 * @returns yes or no
3457 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3459 /** 11??.???? indicates an UTF8 Sequence. */
3460 return ((Char & 0xC0) == 0xC0);
3465 * @brief measure the number of glyphs in an UTF8 string...
3466 * @param Buf string to measure
3467 * @returns the number of glyphs in Buf
3469 long StrBuf_Utf8StrLen(StrBuf *Buf)
3475 if ((Buf == NULL) || (Buf->BufUsed == 0))
3478 eptr = Buf->buf + Buf->BufUsed;
3479 while ((aptr < eptr) && (*aptr != '\0')) {
3480 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3481 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3482 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3495 * @brief cuts a string after maxlen glyphs
3496 * @param Buf string to cut to maxlen glyphs
3497 * @param maxlen how long may the string become?
3498 * @returns current length of the string
3500 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3506 eptr = Buf->buf + Buf->BufUsed;
3507 while ((aptr < eptr) && (*aptr != '\0')) {
3508 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3509 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3510 while ((*aptr++ != '\0') && (m-- > 0));
3519 Buf->BufUsed = aptr - Buf->buf;
3520 return Buf->BufUsed;
3523 return Buf->BufUsed;
3531 /*******************************************************************************
3533 *******************************************************************************/
3536 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3537 #define OS_CODE 0x03 /*< unix */
3540 * @ingroup StrBuf_DeEnCoder
3541 * @brief uses the same calling syntax as compress2(), but it
3542 * creates a stream compatible with HTTP "Content-encoding: gzip"
3543 * @param dest compressed buffer
3544 * @param destLen length of the compresed data
3545 * @param source source to encode
3546 * @param sourceLen length of source to encode
3547 * @param level compression level
3549 int ZEXPORT compress_gzip(Bytef * dest,
3551 const Bytef * source,
3555 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3557 /* write gzip header */
3558 snprintf((char *) dest, *destLen,
3559 "%c%c%c%c%c%c%c%c%c%c",
3560 gz_magic[0], gz_magic[1], Z_DEFLATED,
3561 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3564 /* normal deflate */
3567 stream.next_in = (Bytef *) source;
3568 stream.avail_in = (uInt) sourceLen;
3569 stream.next_out = dest + 10L; // after header
3570 stream.avail_out = (uInt) * destLen;
3571 if ((uLong) stream.avail_out != *destLen)
3574 stream.zalloc = (alloc_func) 0;
3575 stream.zfree = (free_func) 0;
3576 stream.opaque = (voidpf) 0;
3578 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3579 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3583 err = deflate(&stream, Z_FINISH);
3584 if (err != Z_STREAM_END) {
3585 deflateEnd(&stream);
3586 return err == Z_OK ? Z_BUF_ERROR : err;
3588 *destLen = stream.total_out + 10L;
3590 /* write CRC and Length */
3591 uLong crc = crc32(0L, source, sourceLen);
3593 for (n = 0; n < 4; ++n, ++*destLen) {
3594 dest[*destLen] = (int) (crc & 0xff);
3597 uLong len = stream.total_in;
3598 for (n = 0; n < 4; ++n, ++*destLen) {
3599 dest[*destLen] = (int) (len & 0xff);
3602 err = deflateEnd(&stream);
3609 * @ingroup StrBuf_DeEnCoder
3610 * @brief compress the buffer with gzip
3611 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3612 * @param Buf buffer whose content is to be gzipped
3614 int CompressBuffer(StrBuf *Buf)
3617 char *compressed_data = NULL;
3618 size_t compressed_len, bufsize;
3621 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3622 compressed_data = malloc(compressed_len);
3624 if (compressed_data == NULL)
3626 /* Flush some space after the used payload so valgrind shuts up... */
3627 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3628 Buf->buf[Buf->BufUsed + i++] = '\0';
3629 if (compress_gzip((Bytef *) compressed_data,
3632 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3635 Buf->buf = compressed_data;
3636 Buf->BufUsed = compressed_len;
3637 Buf->BufSize = bufsize;
3638 /* Flush some space after the used payload so valgrind shuts up... */
3640 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3641 Buf->buf[Buf->BufUsed + i++] = '\0';
3644 free(compressed_data);
3646 #endif /* HAVE_ZLIB */
3650 /*******************************************************************************
3651 * File I/O; Callbacks to libevent *
3652 *******************************************************************************/
3654 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3659 if ((FB == NULL) || (FB->Buf == NULL))
3663 * check whether the read pointer is somewhere in a range
3664 * where a cut left is inexpensive
3667 if (FB->ReadWritePointer != NULL)
3671 already_read = FB->ReadWritePointer - FB->Buf->buf;
3672 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3674 if (already_read != 0) {
3677 unread = FB->Buf->BufUsed - already_read;
3679 /* else nothing to compact... */
3681 FB->ReadWritePointer = FB->Buf->buf;
3682 bufremain = FB->Buf->BufSize;
3684 else if ((unread < 64) ||
3685 (bufremain < already_read))
3688 * if its just a tiny bit remaining, or we run out of space...
3691 FB->Buf->BufUsed = unread;
3692 if (unread < already_read)
3693 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3695 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3696 FB->ReadWritePointer = FB->Buf->buf;
3697 bufremain = FB->Buf->BufSize - unread - 1;
3699 else if (bufremain < (FB->Buf->BufSize / 10))
3701 /* get a bigger buffer */
3703 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3705 FB->ReadWritePointer = FB->Buf->buf + unread;
3707 bufremain = FB->Buf->BufSize - unread - 1;
3708 /*TODO: special increase function that won't copy the already read! */
3711 else if (bufremain < 10) {
3712 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3714 FB->ReadWritePointer = FB->Buf->buf;
3716 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3721 FB->ReadWritePointer = FB->Buf->buf;
3722 bufremain = FB->Buf->BufSize - 1;
3725 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3728 FB->Buf->BufUsed += n;
3729 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3734 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3739 if ((FB == NULL) || (FB->Buf == NULL))
3742 if (FB->ReadWritePointer != NULL)
3744 WriteRemain = FB->Buf->BufUsed -
3745 (FB->ReadWritePointer -
3749 FB->ReadWritePointer = FB->Buf->buf;
3750 WriteRemain = FB->Buf->BufUsed;
3753 n = write(fd, FB->ReadWritePointer, WriteRemain);
3755 FB->ReadWritePointer += n;
3757 if (FB->ReadWritePointer ==
3758 FB->Buf->buf + FB->Buf->BufUsed)
3760 FlushStrBuf(FB->Buf);
3761 FB->ReadWritePointer = NULL;
3764 // check whether we've got something to write
3765 // get the maximum chunk plus the pointer we can send
3766 // write whats there
3767 // if not all was sent, remember the send pointer for the next time
3768 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3774 * @ingroup StrBuf_IO
3775 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3776 * @param LineBuf your line will be copied here.
3777 * @param FB BLOB with lines of text...
3778 * @param Ptr moved arround to keep the next-line across several iterations
3779 * has to be &NULL on start; will be &NotNULL on end of buffer
3780 * @returns size of copied buffer
3782 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3784 const char *aptr, *ptr, *eptr;
3787 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3791 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3792 FB->ReadWritePointer = StrBufNOTNULL;
3796 FlushStrBuf(LineBuf);
3797 if (FB->ReadWritePointer == NULL)
3798 ptr = aptr = FB->Buf->buf;
3800 ptr = aptr = FB->ReadWritePointer;
3802 optr = LineBuf->buf;
3803 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3804 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3806 while ((ptr <= eptr) &&
3813 LineBuf->BufUsed = optr - LineBuf->buf;
3814 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3815 optr = LineBuf->buf + LineBuf->BufUsed;
3816 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3821 if (optr > LineBuf->buf)
3823 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3824 LineBuf->BufUsed = optr - LineBuf->buf;
3826 if ((FB->ReadWritePointer != NULL) &&
3827 (FB->ReadWritePointer != FB->Buf->buf))
3829 /* Ok, the client application read all the data
3830 it was interested in so far. Since there is more to read,
3831 we now shrink the buffer, and move the rest over.
3833 StrBufCutLeft(FB->Buf,
3834 FB->ReadWritePointer - FB->Buf->buf);
3835 FB->ReadWritePointer = FB->Buf->buf;
3837 return eMustReadMore;
3840 LineBuf->BufUsed = optr - LineBuf->buf;
3842 if ((ptr <= eptr) && (*ptr == '\r'))
3844 if ((ptr <= eptr) && (*ptr == '\n'))
3848 FB->ReadWritePointer = ptr;
3851 FlushStrBuf(FB->Buf);
3852 FB->ReadWritePointer = NULL;
3855 return eReadSuccess;
3859 * @ingroup StrBuf_CHUNKED_IO
3860 * @brief check whether the chunk-buffer has more data waiting or not.
3861 * @param FB Chunk-Buffer to inspect
3863 eReadState StrBufCheckBuffer(IOBuffer *FB)
3867 if (FB->Buf->BufUsed == 0)
3868 return eReadSuccess;
3869 if (FB->ReadWritePointer == NULL)
3870 return eBufferNotEmpty;
3871 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3872 return eBufferNotEmpty;
3873 return eReadSuccess;
3876 long IOBufferStrLength(IOBuffer *FB)
3878 if ((FB == NULL) || (FB->Buf == NULL))
3880 if (FB->ReadWritePointer == NULL)
3881 return StrLength(FB->Buf);
3883 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3886 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3888 memset(FDB, 0, sizeof(FDIOBuffer));
3890 FDB->TotalSendSize = TotalSendSize;
3894 pipe(FDB->SplicePipe);
3897 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3902 void FDIOBufferDelete(FDIOBuffer *FDB)
3907 close(FDB->SplicePipe[0]);
3908 close(FDB->SplicePipe[1]);
3912 FreeStrBuf(&FDB->ChunkBuffer);
3914 close(FDB->OtherFD);
3915 memset(FDB, 0, sizeof(FDIOBuffer));
3918 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3920 ssize_t sent, pipesize;
3924 if (FDB->PipeSize == 0)
3926 pipesize = splice(FDB->OtherFD,
3927 &FDB->TotalSentAlready,
3930 FDB->ChunkSendRemain,
3935 *Err = strerror(errno);
3938 FDB->PipeSize = pipesize;
3940 sent = splice(FDB->SplicePipe[0],
3945 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
3948 *Err = strerror(errno);
3951 FDB->PipeSize -= sent;
3952 FDB->ChunkSendRemain -= sent;
3961 pRead = FDB->ChunkBuffer->buf;
3962 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3964 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3966 FDB->ChunkBuffer->BufUsed += nRead;
3967 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3969 else if (nRead == 0) {}
3974 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3977 FDB->TotalSentAlready += nRead;
3978 FDB->ChunkSendRemain -= nRead;
3979 return FDB->ChunkSendRemain;
3987 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3989 ssize_t sent, pipesize;
3994 if (FDB->PipeSize == 0)
3996 pipesize = splice(FDB->IOB->fd,
4000 FDB->ChunkSendRemain,
4001 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4005 *Err = strerror(errno);
4008 FDB->PipeSize = pipesize;
4011 sent = splice(FDB->SplicePipe[0],
4014 &FDB->TotalSentAlready,
4016 SPLICE_F_MORE | SPLICE_F_MOVE);
4020 *Err = strerror(errno);
4023 FDB->PipeSize -= sent;
4024 FDB->ChunkSendRemain -= sent;
4030 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4035 FDB->ChunkBuffer->BufUsed = sent;
4037 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4038 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4040 *Err = strerror(errno);
4046 FDB->ChunkBuffer->BufUsed = 0;
4047 FDB->TotalSentAlready += sent;
4048 FDB->ChunkSendRemain -= sent;
4049 return FDB->ChunkSendRemain;
4051 else if (sent < 0) {
4052 *Err = strerror(errno);
4059 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4061 ssize_t sent, pipesize;
4066 if (FDB->PipeSize == 0)
4068 pipesize = splice(FDB->IOB->fd,
4069 &FDB->TotalReadAlready,
4072 FDB->ChunkSendRemain,
4073 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4077 *Err = strerror(errno);
4080 FDB->PipeSize = pipesize;
4083 sent = splice(FDB->SplicePipe[0],
4086 &FDB->TotalSentAlready,
4088 SPLICE_F_MORE | SPLICE_F_MOVE);
4092 *Err = strerror(errno);
4095 FDB->PipeSize -= sent;
4096 FDB->ChunkSendRemain -= sent;
4102 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4107 FDB->ChunkBuffer->BufUsed = sent;
4109 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4110 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4112 *Err = strerror(errno);
4118 FDB->ChunkBuffer->BufUsed = 0;
4119 FDB->TotalSentAlready += sent;
4120 FDB->ChunkSendRemain -= sent;
4121 return FDB->ChunkSendRemain;
4123 else if (sent < 0) {
4124 *Err = strerror(errno);
4131 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4137 int nSuccessLess = 0;
4141 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4142 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4144 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4145 (FDB->ChunkSendRemain > 0))
4148 tv.tv_sec = 1; /* selectresolution; */
4152 FD_SET(FDB->OtherFD, &rfds);
4153 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4154 *Error = strerror(errno);
4158 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4163 should_write = FDB->IOB->Buf->BufUsed -
4164 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4165 if (should_write > FDB->ChunkSendRemain)
4166 should_write = FDB->ChunkSendRemain;
4168 rlen = write(FDB->OtherFD,
4169 FDB->IOB->ReadWritePointer,
4172 *Error = strerror(errno);
4176 FDB->TotalSentAlready += rlen;
4177 FDB->IOB->ReadWritePointer += rlen;
4178 FDB->ChunkSendRemain -= rlen;
4180 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4182 FlushStrBuf(FDB->IOB->Buf);
4183 FDB->IOB->ReadWritePointer = NULL;
4186 if (FDB->ChunkSendRemain == 0)
4187 return eReadSuccess;
4189 return eMustReadMore;
4192 /*******************************************************************************
4193 * File I/O; Prefer buffered read since its faster! *
4194 *******************************************************************************/
4197 * @ingroup StrBuf_IO
4198 * @brief Read a line from socket
4199 * flushes and closes the FD on error
4200 * @param buf the buffer to get the input to
4201 * @param fd pointer to the filedescriptor to read
4202 * @param append Append to an existing string or replace?
4203 * @param Error strerror() on error
4204 * @returns numbers of chars read
4206 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4208 int len, rlen, slen;
4210 if ((buf == NULL) || (buf->buf == NULL)) {
4211 *Error = strerror(EINVAL);
4218 slen = len = buf->BufUsed;
4220 rlen = read(*fd, &buf->buf[len], 1);
4222 *Error = strerror(errno);
4229 if (buf->buf[len] == '\n')
4231 if (buf->buf[len] != '\r')
4233 if (len + 2 >= buf->BufSize) {
4235 buf->buf[len+1] = '\0';
4236 IncreaseBuf(buf, 1, -1);
4240 buf->buf[len] = '\0';
4245 * @ingroup StrBuf_BufferedIO
4246 * @brief Read a line from socket
4247 * flushes and closes the FD on error
4248 * @param Line the line to read from the fd / I/O Buffer
4249 * @param buf the buffer to get the input to
4250 * @param fd pointer to the filedescriptor to read
4251 * @param timeout number of successless selects until we bail out
4252 * @param selectresolution how long to wait on each select
4253 * @param Error strerror() on error
4254 * @returns numbers of chars read
4256 int StrBufTCP_read_buffered_line(StrBuf *Line,
4260 int selectresolution,
4264 int nSuccessLess = 0;
4271 if (buf->BufUsed > 0) {
4272 pch = strchr(buf->buf, '\n');
4275 len = pch - buf->buf;
4276 if (len > 0 && (*(pch - 1) == '\r') )
4278 StrBufSub(Line, buf, 0, len - rlen);
4279 StrBufCutLeft(buf, len + 1);
4284 if (buf->BufSize - buf->BufUsed < 10)
4285 IncreaseBuf(buf, 1, -1);
4287 fdflags = fcntl(*fd, F_GETFL);
4288 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4290 while ((nSuccessLess < timeout) && (pch == NULL)) {
4292 tv.tv_sec = selectresolution;
4297 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4298 *Error = strerror(errno);
4304 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4309 &buf->buf[buf->BufUsed],
4310 buf->BufSize - buf->BufUsed - 1);
4312 *Error = strerror(errno);
4317 else if (rlen > 0) {
4319 buf->BufUsed += rlen;
4320 buf->buf[buf->BufUsed] = '\0';
4321 pch = strchr(buf->buf, '\n');
4322 if ((pch == NULL) &&
4323 (buf->BufUsed + 10 > buf->BufSize) &&
4324 (IncreaseBuf(buf, 1, -1) == -1))
4332 len = pch - buf->buf;
4333 if (len > 0 && (*(pch - 1) == '\r') )
4335 StrBufSub(Line, buf, 0, len - rlen);
4336 StrBufCutLeft(buf, len + 1);
4343 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4344 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4345 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4347 * @ingroup StrBuf_BufferedIO
4348 * @brief Read a line from socket
4349 * flushes and closes the FD on error
4350 * @param Line where to append our Line read from the fd / I/O Buffer;
4351 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4352 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4353 * @param fd pointer to the filedescriptor to read
4354 * @param timeout number of successless selects until we bail out
4355 * @param selectresolution how long to wait on each select
4356 * @param Error strerror() on error
4357 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4359 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4364 int selectresolution,
4367 const char *pche = NULL;
4368 const char *pos = NULL;
4370 int len, rlen, retlen;
4371 int nSuccessLess = 0;
4373 const char *pch = NULL;
4379 if ((Line == NULL) ||
4386 *Error = ErrRBLF_PreConditionFailed;
4391 if ((IOBuf->BufUsed > 0) &&
4393 (pos < IOBuf->buf + IOBuf->BufUsed))
4397 pche = IOBuf->buf + IOBuf->BufUsed;
4401 while ((pch < pche) && (*pch != '\n'))
4403 if (Line->BufUsed + 10 > Line->BufSize)
4406 apos = pcht - Line->buf;
4408 IncreaseBuf(Line, 1, -1);
4409 pcht = Line->buf + apos;
4417 if (len > 0 && (*(pch - 1) == '\r') )
4426 if ((pch >= pche) || (*pch == '\0'))
4434 if ((pch != NULL) &&
4437 if (pch + 1 >= pche) {
4450 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4452 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4453 IncreaseBuf(IOBuf, 1, -1);
4455 fdflags = fcntl(*fd, F_GETFL);
4456 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4459 while ((nSuccessLess < timeout) &&
4469 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4470 *Error = strerror(errno);
4474 *Error = ErrRBLF_SelectFailed;
4477 if (! FD_ISSET(*fd, &rfds) != 0) {
4483 &IOBuf->buf[IOBuf->BufUsed],
4484 IOBuf->BufSize - IOBuf->BufUsed - 1);
4486 *Error = strerror(errno);
4491 else if (rlen > 0) {
4493 pLF = IOBuf->buf + IOBuf->BufUsed;
4494 IOBuf->BufUsed += rlen;
4495 IOBuf->buf[IOBuf->BufUsed] = '\0';
4497 pche = IOBuf->buf + IOBuf->BufUsed;
4499 while ((pLF < pche) && (*pLF != '\n'))
4501 if ((pLF >= pche) || (*pLF == '\0'))
4504 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4508 if (pLF != NULL) apos = pLF - IOBuf->buf;
4509 IncreaseBuf(IOBuf, 1, -1);
4510 if (pLF != NULL) pLF = IOBuf->buf + apos;
4520 if (len > 0 && (*(pLF - 1) == '\r') )
4522 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4523 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4529 return retlen + len;
4531 *Error = ErrRBLF_NotEnoughSentFromServer;
4536 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4538 * @ingroup StrBuf_IO
4539 * @brief Input binary data from socket
4540 * flushes and closes the FD on error
4541 * @param Buf the buffer to get the input to
4542 * @param fd pointer to the filedescriptor to read
4543 * @param append Append to an existing string or replace?
4544 * @param nBytes the maximal number of bytes to read
4545 * @param Error strerror() on error
4546 * @returns numbers of chars read
4548 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4559 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4561 *Error = ErrRBLF_BLOBPreConditionFailed;
4566 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4567 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4569 ptr = Buf->buf + Buf->BufUsed;
4571 fdflags = fcntl(*fd, F_GETFL);
4572 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4574 while ((nRead < nBytes) &&
4584 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4585 *Error = strerror(errno);
4589 *Error = ErrRBLF_SelectFailed;
4592 if (! FD_ISSET(*fd, &rfds) != 0) {
4598 if ((rlen = read(*fd,
4600 nBytes - nRead)) == -1) {
4603 *Error = strerror(errno);
4608 Buf->BufUsed += rlen;
4610 Buf->buf[Buf->BufUsed] = '\0';
4614 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4615 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4617 * @ingroup StrBuf_BufferedIO
4618 * @brief Input binary data from socket
4619 * flushes and closes the FD on error
4620 * @param Blob put binary thing here
4621 * @param IOBuf the buffer to get the input to
4622 * @param Pos offset inside of IOBuf
4623 * @param fd pointer to the filedescriptor to read
4624 * @param append Append to an existing string or replace?
4625 * @param nBytes the maximal number of bytes to read
4626 * @param check whether we should search for '000\n' terminators in case of timeouts
4627 * @param Error strerror() on error
4628 * @returns numbers of chars read
4630 int StrBufReadBLOBBuffered(StrBuf *Blob,
4643 int nAlreadyRead = 0;
4648 int nSuccessLess = 0;
4651 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4655 *Error = ErrRBB_BLOBFPreConditionFailed;
4661 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4662 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4667 rlen = pos - IOBuf->buf;
4668 rlen = IOBuf->BufUsed - rlen;
4671 if ((IOBuf->BufUsed > 0) &&
4673 (pos < IOBuf->buf + IOBuf->BufUsed))
4675 if (rlen < nBytes) {
4676 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4677 Blob->BufUsed += rlen;
4678 Blob->buf[Blob->BufUsed] = '\0';
4679 nAlreadyRead = nRead = rlen;
4682 if (rlen >= nBytes) {
4683 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4684 Blob->BufUsed += nBytes;
4685 Blob->buf[Blob->BufUsed] = '\0';
4686 if (rlen == nBytes) {
4698 if (IOBuf->BufSize < nBytes - nRead)
4699 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4702 fdflags = fcntl(*fd, F_GETFL);
4703 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4711 while ((nSuccessLess < MaxTries) &&
4721 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4722 *Error = strerror(errno);
4726 *Error = ErrRBLF_SelectFailed;
4729 if (! FD_ISSET(*fd, &rfds) != 0) {
4736 IOBuf->BufSize - (ptr - IOBuf->buf));
4740 *Error = strerror(errno);
4743 else if (rlen == 0){
4744 if ((check == NNN_TERM) &&
4746 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4748 StrBufPlain(Blob, HKEY("\n000\n"));
4749 StrBufCutRight(Blob, 5);
4750 return Blob->BufUsed;
4752 else if (!IsNonBlock)
4754 else if (nSuccessLess > MaxTries) {
4756 *Error = ErrRBB_too_many_selects;
4760 else if (rlen > 0) {
4764 IOBuf->BufUsed += rlen;
4767 if (nSuccessLess >= MaxTries) {
4769 *Error = ErrRBB_too_many_selects;
4773 if (nRead > nBytes) {
4774 *Pos = IOBuf->buf + nBytes;
4776 Blob->buf[Blob->BufUsed] = '\0';
4777 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4781 return nRead + nAlreadyRead;
4785 * @ingroup StrBuf_IO
4786 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4787 * @param LineBuf your line will be copied here.
4788 * @param Buf BLOB with lines of text...
4789 * @param Ptr moved arround to keep the next-line across several iterations
4790 * has to be &NULL on start; will be &NotNULL on end of buffer
4791 * @returns size of remaining buffer
4793 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4795 const char *aptr, *ptr, *eptr;
4798 if ((Buf == NULL) ||
4799 (*Ptr == StrBufNOTNULL) ||
4801 (LineBuf->buf == NULL))
4803 *Ptr = StrBufNOTNULL;
4807 FlushStrBuf(LineBuf);
4809 ptr = aptr = Buf->buf;
4813 optr = LineBuf->buf;
4814 eptr = Buf->buf + Buf->BufUsed;
4815 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4817 while ((ptr <= eptr) &&
4824 LineBuf->BufUsed = optr - LineBuf->buf;
4825 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4826 optr = LineBuf->buf + LineBuf->BufUsed;
4827 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4831 if ((ptr >= eptr) && (optr > LineBuf->buf))
4833 LineBuf->BufUsed = optr - LineBuf->buf;
4835 if ((ptr <= eptr) && (*ptr == '\r'))
4837 if ((ptr <= eptr) && (*ptr == '\n'))
4844 *Ptr = StrBufNOTNULL;
4847 return Buf->BufUsed - (ptr - Buf->buf);
4852 * @ingroup StrBuf_IO
4853 * @brief removes double slashes from pathnames
4854 * @param Dir directory string to filter
4855 * @param RemoveTrailingSlash allows / disallows trailing slashes
4857 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4863 while (!IsEmptyStr(a)) {
4875 if ((RemoveTrailingSlash) &&
4881 Dir->BufUsed = b - Dir->buf;