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"
44 #include <sys/sendfile.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);
1028 memcpy(dest->buf, Source->buf + Offset, nChars);
1029 dest->BufUsed = nChars;
1030 dest->buf[dest->BufUsed] = '\0';
1033 NCharsRemain = Source->BufUsed - Offset;
1034 if (NCharsRemain >= dest->BufSize)
1035 IncreaseBuf(dest, 0, NCharsRemain + 1);
1036 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1037 dest->BufUsed = NCharsRemain;
1038 dest->buf[dest->BufUsed] = '\0';
1039 return NCharsRemain;
1044 * @brief Cut nChars from the start of the string
1045 * @param Buf Buffer to modify
1046 * @param nChars how many chars should be skipped?
1048 void StrBufCutLeft(StrBuf *Buf, int nChars)
1050 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1051 if (nChars >= Buf->BufUsed) {
1055 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1056 Buf->BufUsed -= nChars;
1057 Buf->buf[Buf->BufUsed] = '\0';
1062 * @brief Cut the trailing n Chars from the string
1063 * @param Buf Buffer to modify
1064 * @param nChars how many chars should be trunkated?
1066 void StrBufCutRight(StrBuf *Buf, int nChars)
1068 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1071 if (nChars >= Buf->BufUsed) {
1075 Buf->BufUsed -= nChars;
1076 Buf->buf[Buf->BufUsed] = '\0';
1081 * @brief Cut the string after n Chars
1082 * @param Buf Buffer to modify
1083 * @param AfternChars after how many chars should we trunkate the string?
1084 * @param At if non-null and points inside of our string, cut it there.
1086 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1088 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1090 AfternChars = At - Buf->buf;
1093 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1095 Buf->BufUsed = AfternChars;
1096 Buf->buf[Buf->BufUsed] = '\0';
1102 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1103 * @param Buf the string to modify
1105 void StrBufTrim(StrBuf *Buf)
1108 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1110 while ((Buf->BufUsed > 0) &&
1111 isspace(Buf->buf[Buf->BufUsed - 1]))
1115 Buf->buf[Buf->BufUsed] = '\0';
1117 if (Buf->BufUsed == 0) return;
1119 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1122 if (delta > 0) StrBufCutLeft(Buf, delta);
1126 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1127 * @param Buf the string to modify
1129 void StrBufSpaceToBlank(StrBuf *Buf)
1133 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1136 pche = pch + Buf->BufUsed;
1145 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1151 if ((Buf == NULL) || (Buf->buf == NULL))
1153 pLeft = pBuff = Buf->buf;
1154 while (pBuff != NULL) {
1156 pBuff = strchr(pBuff, leftboundary);
1165 pRight = strchr(pBuff, rightboundary);
1167 StrBufCutAt(Buf, 0, pRight);
1169 StrBufCutLeft(Buf, pLeft - Buf->buf);
1174 * @ingroup StrBuf_Filler
1175 * @brief uppercase the contents of a buffer
1176 * @param Buf the buffer to translate
1178 void StrBufUpCase(StrBuf *Buf)
1182 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1185 pche = pch + Buf->BufUsed;
1186 while (pch < pche) {
1187 *pch = toupper(*pch);
1194 * @ingroup StrBuf_Filler
1195 * @brief lowercase the contents of a buffer
1196 * @param Buf the buffer to translate
1198 void StrBufLowerCase(StrBuf *Buf)
1202 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1205 pche = pch + Buf->BufUsed;
1206 while (pch < pche) {
1207 *pch = tolower(*pch);
1213 /*******************************************************************************
1214 * a tokenizer that kills, maims, and destroys *
1215 *******************************************************************************/
1218 * @ingroup StrBuf_Tokenizer
1219 * @brief Replace a token at a given place with a given length by another token with given length
1220 * @param Buf String where to work on
1221 * @param where where inside of the Buf is the search-token
1222 * @param HowLong How long is the token to be replaced
1223 * @param Repl Token to insert at 'where'
1224 * @param ReplLen Length of repl
1225 * @returns -1 if fail else length of resulting Buf
1227 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1228 const char *Repl, long ReplLen)
1231 if ((Buf == NULL) ||
1232 (where > Buf->BufUsed) ||
1233 (where + HowLong > Buf->BufUsed))
1236 if (where + ReplLen - HowLong > Buf->BufSize)
1237 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1240 memmove(Buf->buf + where + ReplLen,
1241 Buf->buf + where + HowLong,
1242 Buf->BufUsed - where - HowLong);
1244 memcpy(Buf->buf + where,
1247 Buf->BufUsed += ReplLen - HowLong;
1249 return Buf->BufUsed;
1253 * @ingroup StrBuf_Tokenizer
1254 * @brief Counts the numbmer of tokens in a buffer
1255 * @param source String to count tokens in
1256 * @param tok Tokenizer char to count
1257 * @returns numbers of tokenizer chars found
1259 int StrBufNum_tokens(const StrBuf *source, char tok)
1263 if ((source == NULL) || (source->BufUsed == 0))
1265 if ((source->BufUsed == 1) && (*source->buf == tok))
1269 pche = pch + source->BufUsed;
1280 * @ingroup StrBuf_Tokenizer
1281 * @brief a string tokenizer
1282 * @param Source StringBuffer to read into
1283 * @param parmnum n'th Parameter to remove
1284 * @param separator tokenizer character
1285 * @returns -1 if not found, else length of token.
1287 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1290 char *d, *s, *end; /* dest, source */
1293 /* Find desired @parameter */
1294 end = Source->buf + Source->BufUsed;
1296 while ((d <= end) &&
1299 /* End of string, bail! */
1304 if (*d == separator) {
1309 if ((d == NULL) || (d >= end))
1310 return 0; /* @Parameter not found */
1312 /* Find next @parameter */
1314 while ((s <= end) &&
1315 (*s && *s != separator))
1319 if (*s == separator)
1323 /* Hack and slash */
1328 memmove(d, s, Source->BufUsed - (s - Source->buf));
1329 Source->BufUsed += ReducedBy;
1330 Source->buf[Source->BufUsed] = '\0';
1332 else if (d == Source->buf) {
1334 Source->BufUsed = 0;
1338 Source->BufUsed += ReducedBy;
1349 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1351 const StrBuf Temp = {
1364 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1368 * @ingroup StrBuf_Tokenizer
1369 * @brief a string tokenizer
1370 * @param dest Destination StringBuffer
1371 * @param Source StringBuffer to read into
1372 * @param parmnum n'th Parameter to extract
1373 * @param separator tokenizer character
1374 * @returns -1 if not found, else length of token.
1376 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1378 const char *s, *e; //* source * /
1379 int len = 0; //* running total length of extracted string * /
1380 int current_token = 0; //* token currently being processed * /
1383 dest->buf[0] = '\0';
1389 if ((Source == NULL) || (Source->BufUsed ==0)) {
1393 e = s + Source->BufUsed;
1396 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1398 while ((s < e) && !IsEmptyStr(s)) {
1399 if (*s == separator) {
1402 if (len >= dest->BufSize) {
1403 dest->BufUsed = len;
1404 if (IncreaseBuf(dest, 1, -1) < 0) {
1409 if ( (current_token == parmnum) &&
1410 (*s != separator)) {
1411 dest->buf[len] = *s;
1414 else if (current_token > parmnum) {
1420 dest->buf[len] = '\0';
1421 dest->BufUsed = len;
1423 if (current_token < parmnum) {
1424 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1427 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1436 * @ingroup StrBuf_Tokenizer
1437 * @brief a string tokenizer to fetch an integer
1438 * @param Source String containing tokens
1439 * @param parmnum n'th Parameter to extract
1440 * @param separator tokenizer character
1441 * @returns 0 if not found, else integer representation of the token
1443 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1453 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1460 * @ingroup StrBuf_Tokenizer
1461 * @brief a string tokenizer to fetch a long integer
1462 * @param Source String containing tokens
1463 * @param parmnum n'th Parameter to extract
1464 * @param separator tokenizer character
1465 * @returns 0 if not found, else long integer representation of the token
1467 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1477 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1485 * @ingroup StrBuf_Tokenizer
1486 * @brief a string tokenizer to fetch an unsigned long
1487 * @param Source String containing tokens
1488 * @param parmnum n'th Parameter to extract
1489 * @param separator tokenizer character
1490 * @returns 0 if not found, else unsigned long representation of the token
1492 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1503 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1507 return (unsigned long) atol(pnum);
1516 * @ingroup StrBuf_NextTokenizer
1517 * @brief a string tokenizer; Bounds checker
1518 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1519 * @param Source our tokenbuffer
1520 * @param pStart the token iterator pointer to inspect
1521 * @returns whether the revolving pointer is inside of the search range
1523 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1525 if ((Source == NULL) ||
1526 (*pStart == StrBufNOTNULL) ||
1527 (Source->BufUsed == 0))
1531 if (*pStart == NULL)
1535 else if (*pStart > Source->buf + Source->BufUsed)
1539 else if (*pStart <= Source->buf)
1548 * @ingroup StrBuf_NextTokenizer
1549 * @brief a string tokenizer
1550 * @param dest Destination StringBuffer
1551 * @param Source StringBuffer to read into
1552 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1553 * @param separator tokenizer
1554 * @returns -1 if not found, else length of token.
1556 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1558 const char *s; /* source */
1559 const char *EndBuffer; /* end stop of source buffer */
1560 int current_token = 0; /* token currently being processed */
1561 int len = 0; /* running total length of extracted string */
1563 if ((Source == NULL) ||
1564 (Source->BufUsed == 0) )
1566 *pStart = StrBufNOTNULL;
1572 EndBuffer = Source->buf + Source->BufUsed;
1576 dest->buf[0] = '\0';
1581 *pStart = EndBuffer + 1;
1585 if (*pStart == NULL)
1587 *pStart = Source->buf; /* we're starting to examine this buffer. */
1589 else if ((*pStart < Source->buf) ||
1590 (*pStart > EndBuffer ) )
1592 return -1; /* no more tokens to find. */
1596 /* start to find the next token */
1597 while ((s <= EndBuffer) &&
1598 (current_token == 0) )
1600 if (*s == separator)
1602 /* we found the next token */
1606 if (len >= dest->BufSize)
1608 /* our Dest-buffer isn't big enough, increase it. */
1609 dest->BufUsed = len;
1611 if (IncreaseBuf(dest, 1, -1) < 0) {
1612 /* WHUT? no more mem? bail out. */
1619 if ( (current_token == 0 ) && /* are we in our target token? */
1620 (!IsEmptyStr(s) ) &&
1621 (separator != *s) ) /* don't copy the token itself */
1623 dest->buf[len] = *s; /* Copy the payload */
1624 ++len; /* remember the bigger size. */
1630 /* did we reach the end? */
1631 if ((s > EndBuffer)) {
1632 EndBuffer = StrBufNOTNULL;
1633 *pStart = EndBuffer;
1636 *pStart = s; /* remember the position for the next run */
1639 /* sanitize our extracted token */
1640 dest->buf[len] = '\0';
1641 dest->BufUsed = len;
1648 * @ingroup StrBuf_NextTokenizer
1649 * @brief a string tokenizer
1650 * @param Source StringBuffer to read from
1651 * @param pStart pointer to the end of the last token. Feed with NULL.
1652 * @param separator tokenizer character
1653 * @param nTokens number of tokens to fastforward over
1654 * @returns -1 if not found, else length of token.
1656 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1658 const char *s, *EndBuffer; //* source * /
1659 int len = 0; //* running total length of extracted string * /
1660 int current_token = 0; //* token currently being processed * /
1662 if ((Source == NULL) ||
1663 (Source->BufUsed ==0)) {
1667 return Source->BufUsed;
1669 if (*pStart == NULL)
1670 *pStart = Source->buf;
1672 EndBuffer = Source->buf + Source->BufUsed;
1674 if ((*pStart < Source->buf) ||
1675 (*pStart > EndBuffer)) {
1683 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1685 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1686 if (*s == separator) {
1689 if (current_token >= nTokens) {
1701 * @ingroup StrBuf_NextTokenizer
1702 * @brief a string tokenizer to fetch an integer
1703 * @param Source StringBuffer to read from
1704 * @param pStart Cursor on the tokenstring
1705 * @param separator tokenizer character
1706 * @returns 0 if not found, else integer representation of the token
1708 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1718 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1725 * @ingroup StrBuf_NextTokenizer
1726 * @brief a string tokenizer to fetch a long integer
1727 * @param Source StringBuffer to read from
1728 * @param pStart Cursor on the tokenstring
1729 * @param separator tokenizer character
1730 * @returns 0 if not found, else long integer representation of the token
1732 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1742 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1750 * @ingroup StrBuf_NextTokenizer
1751 * @brief a string tokenizer to fetch an unsigned long
1752 * @param Source StringBuffer to read from
1753 * @param pStart Cursor on the tokenstring
1754 * @param separator tokenizer character
1755 * @returns 0 if not found, else unsigned long representation of the token
1757 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1768 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1772 return (unsigned long) atol(pnum);
1782 /*******************************************************************************
1783 * Escape Appending *
1784 *******************************************************************************/
1787 * @ingroup StrBuf_DeEnCoder
1788 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1789 * @param OutBuf the output buffer
1790 * @param In Buffer to encode
1791 * @param PlainIn way in from plain old c strings
1793 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1795 const char *pch, *pche;
1799 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1801 if (PlainIn != NULL) {
1802 len = strlen(PlainIn);
1808 pche = pch + In->BufUsed;
1815 pt = OutBuf->buf + OutBuf->BufUsed;
1816 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1818 while (pch < pche) {
1820 IncreaseBuf(OutBuf, 1, -1);
1821 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1822 pt = OutBuf->buf + OutBuf->BufUsed;
1825 if((*pch >= 'a' && *pch <= 'z') ||
1826 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1827 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1828 (*pch == '!') || (*pch == '_') ||
1829 (*pch == ',') || (*pch == '.'))
1836 *(pt + 1) = HexList[(unsigned char)*pch][0];
1837 *(pt + 2) = HexList[(unsigned char)*pch][1];
1839 OutBuf->BufUsed += 3;
1847 * @ingroup StrBuf_DeEnCoder
1848 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1849 * @param OutBuf the output buffer
1850 * @param In Buffer to encode
1851 * @param PlainIn way in from plain old c strings
1853 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1855 const char *pch, *pche;
1859 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1861 if (PlainIn != NULL) {
1862 len = strlen(PlainIn);
1868 pche = pch + In->BufUsed;
1875 pt = OutBuf->buf + OutBuf->BufUsed;
1876 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1878 while (pch < pche) {
1880 IncreaseBuf(OutBuf, 1, -1);
1881 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1882 pt = OutBuf->buf + OutBuf->BufUsed;
1885 if((*pch >= 'a' && *pch <= 'z') ||
1886 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1887 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1888 (*pch == '!') || (*pch == '_') ||
1889 (*pch == ',') || (*pch == '.'))
1896 *(pt + 1) = HexList[(unsigned char)*pch][0];
1897 *(pt + 2) = HexList[(unsigned char)*pch][1];
1899 OutBuf->BufUsed += 3;
1907 * @ingroup StrBuf_DeEnCoder
1908 * @brief append a string in hex encoding to the buffer
1909 * @param OutBuf the output buffer
1910 * @param In Buffer to encode
1911 * @param PlainIn way in from plain old c strings
1912 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1914 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1916 const unsigned char *pch, *pche;
1920 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1922 if (PlainIn != NULL) {
1924 len = strlen((const char*)PlainIn);
1931 pch = (const unsigned char*)In->buf;
1932 pche = pch + In->BufUsed;
1939 pt = OutBuf->buf + OutBuf->BufUsed;
1940 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1942 while (pch < pche) {
1944 IncreaseBuf(OutBuf, 1, -1);
1945 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1946 pt = OutBuf->buf + OutBuf->BufUsed;
1949 *pt = HexList[*pch][0];
1951 *pt = HexList[*pch][1];
1952 pt ++; pch ++; OutBuf->BufUsed += 2;
1958 * @ingroup StrBuf_DeEnCoder
1959 * @brief append a string in hex encoding to the buffer
1960 * @param OutBuf the output buffer
1961 * @param In Buffer to encode
1962 * @param PlainIn way in from plain old c strings
1964 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1966 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1970 * @ingroup StrBuf_DeEnCoder
1971 * @brief Append a string, escaping characters which have meaning in HTML.
1973 * @param Target target buffer
1974 * @param Source source buffer; set to NULL if you just have a C-String
1975 * @param PlainIn Plain-C string to append; set to NULL if unused
1976 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1977 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1978 * if set to 2, linebreaks are replaced by <br/>
1980 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1982 const char *aptr, *eiptr;
1986 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1989 if (PlainIn != NULL) {
1991 len = strlen(PlainIn);
1996 eiptr = aptr + Source->BufUsed;
1997 len = Source->BufUsed;
2003 bptr = Target->buf + Target->BufUsed;
2004 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2006 while (aptr < eiptr){
2008 IncreaseBuf(Target, 1, -1);
2009 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2010 bptr = Target->buf + Target->BufUsed;
2013 memcpy(bptr, "<", 4);
2015 Target->BufUsed += 4;
2017 else if (*aptr == '>') {
2018 memcpy(bptr, ">", 4);
2020 Target->BufUsed += 4;
2022 else if (*aptr == '&') {
2023 memcpy(bptr, "&", 5);
2025 Target->BufUsed += 5;
2027 else if (*aptr == '"') {
2028 memcpy(bptr, """, 6);
2030 Target->BufUsed += 6;
2032 else if (*aptr == '\'') {
2033 memcpy(bptr, "'", 5);
2035 Target->BufUsed += 5;
2037 else if (*aptr == LB) {
2042 else if (*aptr == RB) {
2047 else if (*aptr == QU) {
2052 else if ((*aptr == 32) && (nbsp == 1)) {
2053 memcpy(bptr, " ", 6);
2055 Target->BufUsed += 6;
2057 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2058 *bptr='\0'; /* nothing */
2060 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2061 memcpy(bptr, "<br/>", 11);
2063 Target->BufUsed += 11;
2067 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2068 *bptr='\0'; /* nothing */
2078 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2080 return Target->BufUsed;
2084 * @ingroup StrBuf_DeEnCoder
2085 * @brief Append a string, escaping characters which have meaning in HTML.
2086 * Converts linebreaks into blanks; escapes single quotes
2087 * @param Target target buffer
2088 * @param Source source buffer; set to NULL if you just have a C-String
2089 * @param PlainIn Plain-C string to append; set to NULL if unused
2091 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2093 const char *aptr, *eiptr;
2097 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2100 if (PlainIn != NULL) {
2102 len = strlen(PlainIn);
2107 eiptr = aptr + Source->BufUsed;
2108 len = Source->BufUsed;
2114 eptr = Target->buf + Target->BufSize - 8;
2115 tptr = Target->buf + Target->BufUsed;
2117 while (aptr < eiptr){
2119 IncreaseBuf(Target, 1, -1);
2120 eptr = Target->buf + Target->BufSize - 8;
2121 tptr = Target->buf + Target->BufUsed;
2124 if (*aptr == '\n') {
2128 else if (*aptr == '\r') {
2132 else if (*aptr == '\'') {
2138 Target->BufUsed += 5;
2151 * @ingroup StrBuf_DeEnCoder
2152 * @brief Append a string, escaping characters which have meaning in ICAL.
2154 * @param Target target buffer
2155 * @param Source source buffer; set to NULL if you just have a C-String
2156 * @param PlainIn Plain-C string to append; set to NULL if unused
2158 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2160 const char *aptr, *eiptr;
2164 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2167 if (PlainIn != NULL) {
2169 len = strlen(PlainIn);
2174 eiptr = aptr + Source->BufUsed;
2175 len = Source->BufUsed;
2181 eptr = Target->buf + Target->BufSize - 8;
2182 tptr = Target->buf + Target->BufUsed;
2184 while (aptr < eiptr){
2185 if(tptr + 3 >= eptr) {
2186 IncreaseBuf(Target, 1, -1);
2187 eptr = Target->buf + Target->BufSize - 8;
2188 tptr = Target->buf + Target->BufUsed;
2191 if (*aptr == '\n') {
2198 else if (*aptr == '\r') {
2205 else if (*aptr == ',') {
2221 * @ingroup StrBuf_DeEnCoder
2222 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2224 * @param Target target buffer
2225 * @param Source source buffer; set to NULL if you just have a C-String
2226 * @param PlainIn Plain-C string to append; set to NULL if unused
2227 * @returns size of result or -1
2229 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2231 const char *aptr, *eiptr;
2236 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2239 if (PlainIn != NULL) {
2241 len = strlen(PlainIn);
2246 eiptr = aptr + Source->BufUsed;
2247 len = Source->BufUsed;
2253 bptr = Target->buf + Target->BufUsed;
2254 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2256 while (aptr < eiptr){
2258 IncreaseBuf(Target, 1, -1);
2259 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2260 bptr = Target->buf + Target->BufUsed;
2264 memcpy(bptr, HKEY("\\n"));
2266 Target->BufUsed += 2;
2269 memcpy(bptr, HKEY("\\r"));
2271 Target->BufUsed += 2;
2278 Target->BufUsed += 2;
2281 if ((*(aptr + 1) == 'u') &&
2282 isxdigit(*(aptr + 2)) &&
2283 isxdigit(*(aptr + 3)) &&
2284 isxdigit(*(aptr + 4)) &&
2285 isxdigit(*(aptr + 5)))
2286 { /* oh, a unicode escaper. let it pass through. */
2287 memcpy(bptr, aptr, 6);
2290 Target->BufUsed += 6;
2298 Target->BufUsed += 2;
2306 Target->BufUsed += 2;
2313 Target->BufUsed += 2;
2320 Target->BufUsed += 2;
2323 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2324 while (IsUtf8Sequence > 0){
2327 if (--IsUtf8Sequence)
2335 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2337 return Target->BufUsed;
2341 * @ingroup StrBuf_DeEnCoder
2342 * @brief Append a string, escaping characters which have meaning in HTML + json.
2344 * @param Target target buffer
2345 * @param Source source buffer; set to NULL if you just have a C-String
2346 * @param PlainIn Plain-C string to append; set to NULL if unused
2347 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2348 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2349 * if set to 2, linebreaks are replaced by <br/>
2351 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2353 const char *aptr, *eiptr;
2356 int IsUtf8Sequence = 0;
2358 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2361 if (PlainIn != NULL) {
2363 len = strlen(PlainIn);
2368 eiptr = aptr + Source->BufUsed;
2369 len = Source->BufUsed;
2375 bptr = Target->buf + Target->BufUsed;
2376 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2378 while (aptr < eiptr){
2380 IncreaseBuf(Target, 1, -1);
2381 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2382 bptr = Target->buf + Target->BufUsed;
2386 memcpy(bptr, HKEY("<"));
2388 Target->BufUsed += 4;
2391 memcpy(bptr, HKEY(">"));
2393 Target->BufUsed += 4;
2396 memcpy(bptr, HKEY("&"));
2398 Target->BufUsed += 5;
2411 switch (nolinebreaks) {
2413 *bptr='\0'; /* nothing */
2416 memcpy(bptr, HKEY("<br/>"));
2418 Target->BufUsed += 11;
2421 memcpy(bptr, HKEY("\\n"));
2423 Target->BufUsed += 2;
2427 switch (nolinebreaks) {
2430 *bptr='\0'; /* nothing */
2433 memcpy(bptr, HKEY("\\r"));
2435 Target->BufUsed += 2;
2445 Target->BufUsed += 2;
2448 if ((*(aptr + 1) == 'u') &&
2449 isxdigit(*(aptr + 2)) &&
2450 isxdigit(*(aptr + 3)) &&
2451 isxdigit(*(aptr + 4)) &&
2452 isxdigit(*(aptr + 5)))
2453 { /* oh, a unicode escaper. let it pass through. */
2454 memcpy(bptr, aptr, 6);
2457 Target->BufUsed += 6;
2465 Target->BufUsed += 2;
2473 Target->BufUsed += 2;
2480 Target->BufUsed += 2;
2487 Target->BufUsed += 2;
2491 memcpy(bptr, HKEY(" "));
2493 Target->BufUsed += 6;
2497 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2498 while (IsUtf8Sequence > 0){
2501 if (--IsUtf8Sequence)
2509 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2511 return Target->BufUsed;
2515 * @ingroup StrBuf_DeEnCoder
2516 * @brief unhide special chars hidden to the HTML escaper
2517 * @param target buffer to put the unescaped string in
2518 * @param source buffer to unescape
2520 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2525 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2531 FlushStrBuf(target);
2533 len = source->BufUsed;
2534 for (a = 0; a < len; ++a) {
2535 if (target->BufUsed >= target->BufSize)
2536 IncreaseBuf(target, 1, -1);
2538 if (source->buf[a] == '=') {
2539 hex[0] = source->buf[a + 1];
2540 hex[1] = source->buf[a + 2];
2543 sscanf(hex, "%02x", &b);
2544 target->buf[target->BufUsed] = b;
2545 target->buf[++target->BufUsed] = 0;
2549 target->buf[target->BufUsed] = source->buf[a];
2550 target->buf[++target->BufUsed] = 0;
2557 * @ingroup StrBuf_DeEnCoder
2558 * @brief hide special chars from the HTML escapers and friends
2559 * @param target buffer to put the escaped string in
2560 * @param source buffer to escape
2562 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2567 FlushStrBuf(target);
2569 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2574 len = source->BufUsed;
2575 for (i=0; i<len; ++i) {
2576 if (target->BufUsed + 4 >= target->BufSize)
2577 IncreaseBuf(target, 1, -1);
2578 if ( (isalnum(source->buf[i])) ||
2579 (source->buf[i]=='-') ||
2580 (source->buf[i]=='_') ) {
2581 target->buf[target->BufUsed++] = source->buf[i];
2584 sprintf(&target->buf[target->BufUsed],
2586 (0xFF &source->buf[i]));
2587 target->BufUsed += 3;
2590 target->buf[target->BufUsed + 1] = '\0';
2594 /*******************************************************************************
2595 * Quoted Printable de/encoding *
2596 *******************************************************************************/
2599 * @ingroup StrBuf_DeEnCoder
2600 * @brief decode a buffer from base 64 encoding; destroys original
2601 * @param Buf Buffor to transform
2603 int StrBufDecodeBase64(StrBuf *Buf)
2607 if (Buf == NULL) return -1;
2609 xferbuf = (char*) malloc(Buf->BufSize);
2611 siz = CtdlDecodeBase64(xferbuf,
2621 * @ingroup StrBuf_DeEnCoder
2622 * @brief decode a buffer from base 64 encoding; destroys original
2623 * @param Buf Buffor to transform
2625 int StrBufDecodeHex(StrBuf *Buf)
2628 char *pch, *pche, *pchi;
2630 if (Buf == NULL) return -1;
2632 pch = pchi = Buf->buf;
2633 pche = pch + Buf->BufUsed;
2635 while (pchi < pche){
2636 ch = decode_hex(pchi);
2643 Buf->BufUsed = pch - Buf->buf;
2644 return Buf->BufUsed;
2648 * @ingroup StrBuf_DeEnCoder
2649 * @brief replace all chars >0x20 && < 0x7F with Mute
2650 * @param Mute char to put over invalid chars
2651 * @param Buf Buffor to transform
2653 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2657 if (Buf == NULL) return -1;
2658 pch = (unsigned char *)Buf->buf;
2659 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2660 if ((*pch < 0x20) || (*pch > 0x7F))
2664 return Buf->BufUsed;
2669 * @ingroup StrBuf_DeEnCoder
2670 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2671 * @param Buf Buffer to translate
2672 * @param StripBlanks Reduce several blanks to one?
2674 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2683 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2684 Buf->buf[Buf->BufUsed - 1] = '\0';
2689 while (a < Buf->BufUsed) {
2690 if (Buf->buf[a] == '+')
2692 else if (Buf->buf[a] == '%') {
2693 /* don't let % chars through, rather truncate the input. */
2694 if (a + 2 > Buf->BufUsed) {
2699 hex[0] = Buf->buf[a + 1];
2700 hex[1] = Buf->buf[a + 2];
2703 sscanf(hex, "%02x", &b);
2704 Buf->buf[a] = (char) b;
2705 len = Buf->BufUsed - a - 2;
2707 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2719 * @ingroup StrBuf_DeEnCoder
2720 * @brief RFC2047-encode a header field if necessary.
2721 * If no non-ASCII characters are found, the string
2722 * will be copied verbatim without encoding.
2724 * @param target Target buffer.
2725 * @param source Source string to be encoded.
2726 * @returns encoded length; -1 if non success.
2728 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2730 const char headerStr[] = "=?UTF-8?Q?";
2731 int need_to_encode = 0;
2735 if ((source == NULL) ||
2739 while ((i < source->BufUsed) &&
2740 (!IsEmptyStr (&source->buf[i])) &&
2741 (need_to_encode == 0)) {
2742 if (((unsigned char) source->buf[i] < 32) ||
2743 ((unsigned char) source->buf[i] > 126)) {
2749 if (!need_to_encode) {
2750 if (*target == NULL) {
2751 *target = NewStrBufPlain(source->buf, source->BufUsed);
2754 FlushStrBuf(*target);
2755 StrBufAppendBuf(*target, source, 0);
2758 return (*target)->BufUsed;
2762 if (*target == NULL)
2763 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2764 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2765 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2766 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2767 (*target)->BufUsed = sizeof(headerStr) - 1;
2768 for (i=0; (i < source->BufUsed); ++i) {
2769 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2770 IncreaseBuf(*target, 1, 0);
2771 ch = (unsigned char) source->buf[i];
2781 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2782 (*target)->BufUsed += 3;
2786 (*target)->buf[(*target)->BufUsed] = '_';
2788 (*target)->buf[(*target)->BufUsed] = ch;
2789 (*target)->BufUsed++;
2793 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2794 IncreaseBuf(*target, 1, 0);
2796 (*target)->buf[(*target)->BufUsed++] = '?';
2797 (*target)->buf[(*target)->BufUsed++] = '=';
2798 (*target)->buf[(*target)->BufUsed] = '\0';
2799 return (*target)->BufUsed;;
2804 static void AddRecipient(StrBuf *Target,
2806 StrBuf *EmailAddress,
2811 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2812 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2814 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2815 StrBufRFC2047encode(&EncBuf, UserName);
2816 StrBufAppendBuf(Target, EncBuf, 0);
2817 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2818 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2820 if (StrLength(EmailAddress) > 0){
2821 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2822 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2823 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2829 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2830 * \param Recp Source list of email recipients
2831 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2832 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2833 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2834 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2836 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2838 StrBuf *EmailAddress,
2842 const char *pch, *pche;
2843 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2845 if ((Recp == NULL) || (StrLength(Recp) == 0))
2849 pche = pch + StrLength(Recp);
2851 if (!CheckEncode(pch, -1, pche))
2852 return NewStrBufDup(Recp);
2854 Target = NewStrBufPlain(NULL, StrLength(Recp));
2856 while ((pch != NULL) && (pch < pche))
2858 while (isspace(*pch)) pch++;
2859 UserEnd = EmailStart = EmailEnd = NULL;
2861 if ((*pch == '"') || (*pch == '\'')) {
2862 UserStart = pch + 1;
2864 UserEnd = strchr(UserStart, *pch);
2865 if (UserEnd == NULL)
2866 break; ///TODO: Userfeedback??
2867 EmailStart = UserEnd + 1;
2868 while (isspace(*EmailStart))
2870 if (UserEnd == UserStart) {
2871 UserStart = UserEnd = NULL;
2874 if (*EmailStart == '<') {
2876 EmailEnd = strchr(EmailStart, '>');
2877 if (EmailEnd == NULL)
2878 EmailEnd = strchr(EmailStart, ',');
2882 EmailEnd = strchr(EmailStart, ',');
2884 if (EmailEnd == NULL)
2891 EmailEnd = strchr(UserStart, ',');
2892 if (EmailEnd == NULL) {
2893 EmailEnd = strchr(pch, '>');
2895 if (EmailEnd != NULL) {
2905 while ((EmailEnd > UserStart) && !gt &&
2906 ((*EmailEnd == ',') ||
2907 (*EmailEnd == '>') ||
2908 (isspace(*EmailEnd))))
2910 if (*EmailEnd == '>')
2915 if (EmailEnd == UserStart)
2919 EmailStart = strchr(UserStart, '<');
2920 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2922 UserEnd = EmailStart;
2924 while ((UserEnd > UserStart) &&
2925 isspace (*(UserEnd - 1)))
2928 if (UserStart >= UserEnd)
2929 UserStart = UserEnd = NULL;
2931 else { /* this is a local recipient... no domain, just a realname */
2932 EmailStart = UserStart;
2933 At = strchr(EmailStart, '@');
2939 EmailStart = UserStart;
2945 if ((UserStart != NULL) && (UserEnd != NULL))
2946 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2947 else if ((UserStart != NULL) && (UserEnd == NULL))
2948 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2950 FlushStrBuf(UserName);
2952 if ((EmailStart != NULL) && (EmailEnd != NULL))
2953 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2954 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2955 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2957 FlushStrBuf(EmailAddress);
2959 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2964 if ((pch != NULL) && (*pch == ','))
2966 if (pch != NULL) while (isspace(*pch))
2975 * @brief replaces all occurances of 'search' by 'replace'
2976 * @param buf Buffer to modify
2977 * @param search character to search
2978 * @param replace character to replace search by
2980 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2985 for (i=0; i<buf->BufUsed; i++)
2986 if (buf->buf[i] == search)
2987 buf->buf[i] = replace;
2993 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2994 * @param buf Buffer to modify
2996 void StrBufToUnixLF(StrBuf *buf)
2998 char *pche, *pchS, *pchT;
3002 pche = buf->buf + buf->BufUsed;
3003 pchS = pchT = buf->buf;
3009 if (*pchS != '\n') {
3018 buf->BufUsed = pchT - buf->buf;
3022 /*******************************************************************************
3023 * Iconv Wrapper; RFC822 de/encoding *
3024 *******************************************************************************/
3027 * @ingroup StrBuf_DeEnCoder
3028 * @brief Wrapper around iconv_open()
3029 * Our version adds aliases for non-standard Microsoft charsets
3030 * such as 'MS950', aliasing them to names like 'CP950'
3032 * @param tocode Target encoding
3033 * @param fromcode Source encoding
3034 * @param pic anonimized pointer to iconv struct
3036 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3039 iconv_t ic = (iconv_t)(-1) ;
3040 ic = iconv_open(tocode, fromcode);
3041 if (ic == (iconv_t)(-1) ) {
3042 char alias_fromcode[64];
3043 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3044 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3045 alias_fromcode[0] = 'C';
3046 alias_fromcode[1] = 'P';
3047 ic = iconv_open(tocode, alias_fromcode);
3050 *(iconv_t *)pic = ic;
3056 * @ingroup StrBuf_DeEnCoder
3057 * @brief find one chunk of a RFC822 encoded string
3058 * @param Buffer where to search
3059 * @param bptr where to start searching
3060 * @returns found position, NULL if none.
3062 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3065 /* Find the next ?Q? */
3066 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3069 end = strchr(bptr + 2, '?');
3074 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3075 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3076 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3077 (*(end + 2) == '?')) {
3078 /* skip on to the end of the cluster, the next ?= */
3079 end = strstr(end + 3, "?=");
3082 /* sort of half valid encoding, try to find an end. */
3083 end = strstr(bptr, "?=");
3090 * @ingroup StrBuf_DeEnCoder
3091 * @brief convert one buffer according to the preselected iconv pointer PIC
3092 * @param ConvertBuf buffer we need to translate
3093 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3094 * @param pic Pointer to the iconv-session Object
3096 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3102 char *ibuf; /**< Buffer of characters to be converted */
3103 char *obuf; /**< Buffer for converted characters */
3104 size_t ibuflen; /**< Length of input buffer */
3105 size_t obuflen; /**< Length of output buffer */
3108 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3111 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3112 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3113 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3115 ic = *(iconv_t*)pic;
3116 ibuf = ConvertBuf->buf;
3117 ibuflen = ConvertBuf->BufUsed;
3119 obuflen = TmpBuf->BufSize;
3121 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3124 if (errno == E2BIG) {
3126 IncreaseBuf(TmpBuf, 0, 0);
3131 else if (errno == EILSEQ){
3132 /* hm, invalid utf8 sequence... what to do now? */
3133 /* An invalid multibyte sequence has been encountered in the input */
3135 else if (errno == EINVAL) {
3136 /* An incomplete multibyte sequence has been encountered in the input. */
3139 FlushStrBuf(TmpBuf);
3142 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3143 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3145 /* little card game: wheres the red lady? */
3146 SwapBuffers(ConvertBuf, TmpBuf);
3147 FlushStrBuf(TmpBuf);
3154 * @ingroup StrBuf_DeEnCoder
3155 * @brief catches one RFC822 encoded segment, and decodes it.
3156 * @param Target buffer to fill with result
3157 * @param DecodeMe buffer with stuff to process
3158 * @param SegmentStart points to our current segment in DecodeMe
3159 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3160 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3161 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3162 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3164 inline static void DecodeSegment(StrBuf *Target,
3165 const StrBuf *DecodeMe,
3166 const char *SegmentStart,
3167 const char *SegmentEnd,
3169 StrBuf *ConvertBuf2,
3170 StrBuf *FoundCharset)
3176 iconv_t ic = (iconv_t)(-1);
3180 /* Now we handle foreign character sets properly encoded
3181 * in RFC2047 format.
3183 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3184 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3185 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3186 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3187 if (FoundCharset != NULL) {
3188 FlushStrBuf(FoundCharset);
3189 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3191 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3192 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3194 *encoding = toupper(*encoding);
3195 if (*encoding == 'B') { /**< base64 */
3196 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3197 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3198 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3200 ConvertBuf->BufUsed);
3202 else if (*encoding == 'Q') { /**< quoted-printable */
3206 while (pos < ConvertBuf->BufUsed)
3208 if (ConvertBuf->buf[pos] == '_')
3209 ConvertBuf->buf[pos] = ' ';
3213 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3214 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3216 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3219 ConvertBuf->BufUsed);
3222 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3225 ctdl_iconv_open("UTF-8", charset, &ic);
3226 if (ic != (iconv_t)(-1) ) {
3228 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3229 StrBufAppendBuf(Target, ConvertBuf2, 0);
3234 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3240 * @ingroup StrBuf_DeEnCoder
3241 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3242 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3243 * @param Target where to put the decoded string to
3244 * @param DecodeMe buffer with encoded string
3245 * @param DefaultCharset if we don't find one, which should we use?
3246 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3247 * put it here for later use where no string might be known.
3249 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3252 StrBuf *ConvertBuf2;
3253 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3254 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3256 StrBuf_RFC822_2_Utf8(Target,
3262 FreeStrBuf(&ConvertBuf);
3263 FreeStrBuf(&ConvertBuf2);
3267 * @ingroup StrBuf_DeEnCoder
3268 * @brief Handle subjects with RFC2047 encoding such as:
3269 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3270 * @param Target where to put the decoded string to
3271 * @param DecodeMe buffer with encoded string
3272 * @param DefaultCharset if we don't find one, which should we use?
3273 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3274 * put it here for later use where no string might be known.
3275 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3276 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3278 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3279 const StrBuf *DecodeMe,
3280 const StrBuf* DefaultCharset,
3281 StrBuf *FoundCharset,
3283 StrBuf *ConvertBuf2)
3285 StrBuf *DecodedInvalidBuf = NULL;
3286 const StrBuf *DecodeMee = DecodeMe;
3287 const char *start, *end, *next, *nextend, *ptr = NULL;
3289 iconv_t ic = (iconv_t)(-1) ;
3294 int illegal_non_rfc2047_encoding = 0;
3297 if (DecodeMe == NULL)
3299 /* Sometimes, badly formed messages contain strings which were simply
3300 * written out directly in some foreign character set instead of
3301 * using RFC2047 encoding. This is illegal but we will attempt to
3302 * handle it anyway by converting from a user-specified default
3303 * charset to UTF-8 if we see any nonprintable characters.
3306 for (i=0; i<DecodeMe->BufUsed; ++i) {
3307 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3308 illegal_non_rfc2047_encoding = 1;
3313 if ((illegal_non_rfc2047_encoding) &&
3314 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3315 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3318 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3319 if (ic != (iconv_t)(-1) ) {
3320 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3321 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3322 DecodeMee = DecodedInvalidBuf;
3328 /* pre evaluate the first pair */
3330 start = strstr(DecodeMee->buf, "=?");
3331 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3333 end = FindNextEnd (DecodeMee, start + 2);
3335 StrBufAppendBuf(Target, DecodeMee, 0);
3336 FreeStrBuf(&DecodedInvalidBuf);
3341 if (start != DecodeMee->buf) {
3344 nFront = start - DecodeMee->buf;
3345 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3348 * Since spammers will go to all sorts of absurd lengths to get their
3349 * messages through, there are LOTS of corrupt headers out there.
3350 * So, prevent a really badly formed RFC2047 header from throwing
3351 * this function into an infinite loop.
3353 while ((start != NULL) &&
3360 DecodeSegment(Target,
3368 next = strstr(end, "=?");
3370 if ((next != NULL) &&
3372 nextend = FindNextEnd(DecodeMee, next);
3373 if (nextend == NULL)
3376 /* did we find two partitions */
3377 if ((next != NULL) &&
3381 while ((ptr < next) &&
3388 * did we find a gab just filled with blanks?
3389 * if not, copy its stuff over.
3393 StrBufAppendBufPlain(Target,
3399 /* our next-pair is our new first pair now. */
3405 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3406 if ((end != NULL) && (end < nextend)) {
3408 while ( (ptr < nextend) &&
3415 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3417 FreeStrBuf(&DecodedInvalidBuf);
3420 /*******************************************************************************
3421 * Manipulating UTF-8 Strings *
3422 *******************************************************************************/
3426 * @brief evaluate the length of an utf8 special character sequence
3427 * @param Char the character to examine
3428 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3430 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3433 unsigned char test = (1<<7);
3435 if ((*CharS & 0xC0) != 0xC0)
3439 ((test & ((unsigned char)*CharS)) != 0))
3444 if ((n > 6) || ((CharE - CharS) < n))
3451 * @brief detect whether this char starts an utf-8 encoded char
3452 * @param Char character to inspect
3453 * @returns yes or no
3455 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3457 /** 11??.???? indicates an UTF8 Sequence. */
3458 return ((Char & 0xC0) == 0xC0);
3463 * @brief measure the number of glyphs in an UTF8 string...
3464 * @param Buf string to measure
3465 * @returns the number of glyphs in Buf
3467 long StrBuf_Utf8StrLen(StrBuf *Buf)
3473 if ((Buf == NULL) || (Buf->BufUsed == 0))
3476 eptr = Buf->buf + Buf->BufUsed;
3477 while ((aptr < eptr) && (*aptr != '\0')) {
3478 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3479 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3480 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3493 * @brief cuts a string after maxlen glyphs
3494 * @param Buf string to cut to maxlen glyphs
3495 * @param maxlen how long may the string become?
3496 * @returns current length of the string
3498 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3504 eptr = Buf->buf + Buf->BufUsed;
3505 while ((aptr < eptr) && (*aptr != '\0')) {
3506 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3507 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3508 while ((*aptr++ != '\0') && (m-- > 0));
3517 Buf->BufUsed = aptr - Buf->buf;
3518 return Buf->BufUsed;
3521 return Buf->BufUsed;
3529 /*******************************************************************************
3531 *******************************************************************************/
3534 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3535 #define OS_CODE 0x03 /*< unix */
3538 * @ingroup StrBuf_DeEnCoder
3539 * @brief uses the same calling syntax as compress2(), but it
3540 * creates a stream compatible with HTTP "Content-encoding: gzip"
3541 * @param dest compressed buffer
3542 * @param destLen length of the compresed data
3543 * @param source source to encode
3544 * @param sourceLen length of source to encode
3545 * @param level compression level
3547 int ZEXPORT compress_gzip(Bytef * dest,
3549 const Bytef * source,
3553 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3555 /* write gzip header */
3556 snprintf((char *) dest, *destLen,
3557 "%c%c%c%c%c%c%c%c%c%c",
3558 gz_magic[0], gz_magic[1], Z_DEFLATED,
3559 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3562 /* normal deflate */
3565 stream.next_in = (Bytef *) source;
3566 stream.avail_in = (uInt) sourceLen;
3567 stream.next_out = dest + 10L; // after header
3568 stream.avail_out = (uInt) * destLen;
3569 if ((uLong) stream.avail_out != *destLen)
3572 stream.zalloc = (alloc_func) 0;
3573 stream.zfree = (free_func) 0;
3574 stream.opaque = (voidpf) 0;
3576 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3577 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3581 err = deflate(&stream, Z_FINISH);
3582 if (err != Z_STREAM_END) {
3583 deflateEnd(&stream);
3584 return err == Z_OK ? Z_BUF_ERROR : err;
3586 *destLen = stream.total_out + 10L;
3588 /* write CRC and Length */
3589 uLong crc = crc32(0L, source, sourceLen);
3591 for (n = 0; n < 4; ++n, ++*destLen) {
3592 dest[*destLen] = (int) (crc & 0xff);
3595 uLong len = stream.total_in;
3596 for (n = 0; n < 4; ++n, ++*destLen) {
3597 dest[*destLen] = (int) (len & 0xff);
3600 err = deflateEnd(&stream);
3607 * @ingroup StrBuf_DeEnCoder
3608 * @brief compress the buffer with gzip
3609 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3610 * @param Buf buffer whose content is to be gzipped
3612 int CompressBuffer(StrBuf *Buf)
3615 char *compressed_data = NULL;
3616 size_t compressed_len, bufsize;
3619 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3620 compressed_data = malloc(compressed_len);
3622 if (compressed_data == NULL)
3624 /* Flush some space after the used payload so valgrind shuts up... */
3625 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3626 Buf->buf[Buf->BufUsed + i++] = '\0';
3627 if (compress_gzip((Bytef *) compressed_data,
3630 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3633 Buf->buf = compressed_data;
3634 Buf->BufUsed = compressed_len;
3635 Buf->BufSize = bufsize;
3636 /* Flush some space after the used payload so valgrind shuts up... */
3638 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3639 Buf->buf[Buf->BufUsed + i++] = '\0';
3642 free(compressed_data);
3644 #endif /* HAVE_ZLIB */
3648 /*******************************************************************************
3649 * File I/O; Callbacks to libevent *
3650 *******************************************************************************/
3652 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3657 if ((FB == NULL) || (FB->Buf == NULL))
3661 * check whether the read pointer is somewhere in a range
3662 * where a cut left is inexpensive
3665 if (FB->ReadWritePointer != NULL)
3669 already_read = FB->ReadWritePointer - FB->Buf->buf;
3670 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3672 if (already_read != 0) {
3675 unread = FB->Buf->BufUsed - already_read;
3677 /* else nothing to compact... */
3679 FB->ReadWritePointer = FB->Buf->buf;
3680 bufremain = FB->Buf->BufSize;
3682 else if ((unread < 64) ||
3683 (bufremain < already_read))
3686 * if its just a tiny bit remaining, or we run out of space...
3689 FB->Buf->BufUsed = unread;
3690 if (unread < already_read)
3691 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3693 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3694 FB->ReadWritePointer = FB->Buf->buf;
3695 bufremain = FB->Buf->BufSize - unread - 1;
3697 else if (bufremain < (FB->Buf->BufSize / 10))
3699 /* get a bigger buffer */
3701 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3703 FB->ReadWritePointer = FB->Buf->buf + unread;
3705 bufremain = FB->Buf->BufSize - unread - 1;
3706 /*TODO: special increase function that won't copy the already read! */
3709 else if (bufremain < 10) {
3710 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3712 FB->ReadWritePointer = FB->Buf->buf;
3714 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3719 FB->ReadWritePointer = FB->Buf->buf;
3720 bufremain = FB->Buf->BufSize - 1;
3723 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3726 FB->Buf->BufUsed += n;
3727 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3732 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3737 if ((FB == NULL) || (FB->Buf == NULL))
3740 if (FB->ReadWritePointer != NULL)
3742 WriteRemain = FB->Buf->BufUsed -
3743 (FB->ReadWritePointer -
3747 FB->ReadWritePointer = FB->Buf->buf;
3748 WriteRemain = FB->Buf->BufUsed;
3751 n = write(fd, FB->ReadWritePointer, WriteRemain);
3753 FB->ReadWritePointer += n;
3755 if (FB->ReadWritePointer ==
3756 FB->Buf->buf + FB->Buf->BufUsed)
3758 FlushStrBuf(FB->Buf);
3759 FB->ReadWritePointer = NULL;
3762 // check whether we've got something to write
3763 // get the maximum chunk plus the pointer we can send
3764 // write whats there
3765 // if not all was sent, remember the send pointer for the next time
3766 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3772 * @ingroup StrBuf_IO
3773 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3774 * @param LineBuf your line will be copied here.
3775 * @param FB BLOB with lines of text...
3776 * @param Ptr moved arround to keep the next-line across several iterations
3777 * has to be &NULL on start; will be &NotNULL on end of buffer
3778 * @returns size of copied buffer
3780 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3782 const char *aptr, *ptr, *eptr;
3785 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3789 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3790 FB->ReadWritePointer = StrBufNOTNULL;
3794 FlushStrBuf(LineBuf);
3795 if (FB->ReadWritePointer == NULL)
3796 ptr = aptr = FB->Buf->buf;
3798 ptr = aptr = FB->ReadWritePointer;
3800 optr = LineBuf->buf;
3801 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3802 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3804 while ((ptr <= eptr) &&
3811 LineBuf->BufUsed = optr - LineBuf->buf;
3812 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3813 optr = LineBuf->buf + LineBuf->BufUsed;
3814 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3819 if (optr > LineBuf->buf)
3821 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3822 LineBuf->BufUsed = optr - LineBuf->buf;
3824 if ((FB->ReadWritePointer != NULL) &&
3825 (FB->ReadWritePointer != FB->Buf->buf))
3827 /* Ok, the client application read all the data
3828 it was interested in so far. Since there is more to read,
3829 we now shrink the buffer, and move the rest over.
3831 StrBufCutLeft(FB->Buf,
3832 FB->ReadWritePointer - FB->Buf->buf);
3833 FB->ReadWritePointer = FB->Buf->buf;
3835 return eMustReadMore;
3838 LineBuf->BufUsed = optr - LineBuf->buf;
3840 if ((ptr <= eptr) && (*ptr == '\r'))
3842 if ((ptr <= eptr) && (*ptr == '\n'))
3846 FB->ReadWritePointer = ptr;
3849 FlushStrBuf(FB->Buf);
3850 FB->ReadWritePointer = NULL;
3853 return eReadSuccess;
3857 * @ingroup StrBuf_CHUNKED_IO
3858 * @brief check whether the chunk-buffer has more data waiting or not.
3859 * @param FB Chunk-Buffer to inspect
3861 eReadState StrBufCheckBuffer(IOBuffer *FB)
3865 if (FB->Buf->BufUsed == 0)
3866 return eReadSuccess;
3867 if (FB->ReadWritePointer == NULL)
3868 return eBufferNotEmpty;
3869 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3870 return eBufferNotEmpty;
3871 return eReadSuccess;
3874 long IOBufferStrLength(IOBuffer *FB)
3876 if ((FB == NULL) || (FB->Buf == NULL))
3878 if (FB->ReadWritePointer == NULL)
3879 return StrLength(FB->Buf);
3881 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3884 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3886 memset(FDB, 0, sizeof(FDIOBuffer));
3888 FDB->TotalSendSize = TotalSendSize;
3892 pipe(FDB->SplicePipe);
3895 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3900 void FDIOBufferDelete(FDIOBuffer *FDB)
3905 close(FDB->SplicePipe[0]);
3906 close(FDB->SplicePipe[1]);
3910 FreeStrBuf(&FDB->ChunkBuffer);
3912 close(FDB->OtherFD);
3913 memset(FDB, 0, sizeof(FDIOBuffer));
3916 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3918 ssize_t sent, pipesize;
3922 if (FDB->PipeSize == 0)
3924 pipesize = splice(FDB->OtherFD,
3925 &FDB->TotalSentAlready,
3928 FDB->ChunkSendRemain,
3933 *Err = strerror(errno);
3936 FDB->PipeSize = pipesize;
3938 sent = splice(FDB->SplicePipe[0],
3943 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
3946 *Err = strerror(errno);
3949 FDB->PipeSize -= sent;
3950 FDB->ChunkSendRemain -= sent;
3959 pRead = FDB->ChunkBuffer->buf;
3960 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3962 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3964 FDB->ChunkBuffer->BufUsed += nRead;
3965 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3967 else if (nRead == 0) {}
3972 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3975 FDB->TotalSentAlready += nRead;
3976 FDB->ChunkSendRemain -= nRead;
3977 return FDB->ChunkSendRemain;
3985 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3987 ssize_t sent, pipesize;
3992 if (FDB->PipeSize == 0)
3994 pipesize = splice(FDB->IOB->fd,
3998 FDB->ChunkSendRemain,
3999 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4003 *Err = strerror(errno);
4006 FDB->PipeSize = pipesize;
4009 sent = splice(FDB->SplicePipe[0],
4012 &FDB->TotalSentAlready,
4014 SPLICE_F_MORE | SPLICE_F_MOVE);
4018 *Err = strerror(errno);
4021 FDB->PipeSize -= sent;
4022 FDB->ChunkSendRemain -= sent;
4028 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4033 FDB->ChunkBuffer->BufUsed = sent;
4035 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4036 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4038 *Err = strerror(errno);
4044 FDB->ChunkBuffer->BufUsed = 0;
4045 FDB->TotalSentAlready += sent;
4046 FDB->ChunkSendRemain -= sent;
4047 return FDB->ChunkSendRemain;
4049 else if (sent < 0) {
4050 *Err = strerror(errno);
4057 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4059 ssize_t sent, pipesize;
4064 if (FDB->PipeSize == 0)
4066 pipesize = splice(FDB->IOB->fd,
4067 &FDB->TotalReadAlready,
4070 FDB->ChunkSendRemain,
4071 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4075 *Err = strerror(errno);
4078 FDB->PipeSize = pipesize;
4081 sent = splice(FDB->SplicePipe[0],
4084 &FDB->TotalSentAlready,
4086 SPLICE_F_MORE | SPLICE_F_MOVE);
4090 *Err = strerror(errno);
4093 FDB->PipeSize -= sent;
4094 FDB->ChunkSendRemain -= sent;
4100 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4105 FDB->ChunkBuffer->BufUsed = sent;
4107 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4108 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4110 *Err = strerror(errno);
4116 FDB->ChunkBuffer->BufUsed = 0;
4117 FDB->TotalSentAlready += sent;
4118 FDB->ChunkSendRemain -= sent;
4119 return FDB->ChunkSendRemain;
4121 else if (sent < 0) {
4122 *Err = strerror(errno);
4129 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4135 int nSuccessLess = 0;
4139 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4140 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4142 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4143 (FDB->ChunkSendRemain > 0))
4146 tv.tv_sec = 1; /* selectresolution; */
4150 FD_SET(FDB->OtherFD, &rfds);
4151 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4152 *Error = strerror(errno);
4156 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4161 should_write = FDB->IOB->Buf->BufUsed -
4162 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4163 if (should_write > FDB->ChunkSendRemain)
4164 should_write = FDB->ChunkSendRemain;
4166 rlen = write(FDB->OtherFD,
4167 FDB->IOB->ReadWritePointer,
4170 *Error = strerror(errno);
4174 FDB->TotalSentAlready += rlen;
4175 FDB->IOB->ReadWritePointer += rlen;
4176 FDB->ChunkSendRemain -= rlen;
4178 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4180 FlushStrBuf(FDB->IOB->Buf);
4181 FDB->IOB->ReadWritePointer = NULL;
4184 if (FDB->ChunkSendRemain == 0)
4185 return eReadSuccess;
4187 return eMustReadMore;
4190 /*******************************************************************************
4191 * File I/O; Prefer buffered read since its faster! *
4192 *******************************************************************************/
4195 * @ingroup StrBuf_IO
4196 * @brief Read a line from socket
4197 * flushes and closes the FD on error
4198 * @param buf the buffer to get the input to
4199 * @param fd pointer to the filedescriptor to read
4200 * @param append Append to an existing string or replace?
4201 * @param Error strerror() on error
4202 * @returns numbers of chars read
4204 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4206 int len, rlen, slen;
4208 if ((buf == NULL) || (buf->buf == NULL)) {
4209 *Error = strerror(EINVAL);
4216 slen = len = buf->BufUsed;
4218 rlen = read(*fd, &buf->buf[len], 1);
4220 *Error = strerror(errno);
4227 if (buf->buf[len] == '\n')
4229 if (buf->buf[len] != '\r')
4231 if (len + 2 >= buf->BufSize) {
4233 buf->buf[len+1] = '\0';
4234 IncreaseBuf(buf, 1, -1);
4238 buf->buf[len] = '\0';
4243 * @ingroup StrBuf_BufferedIO
4244 * @brief Read a line from socket
4245 * flushes and closes the FD on error
4246 * @param Line the line to read from the fd / I/O Buffer
4247 * @param buf the buffer to get the input to
4248 * @param fd pointer to the filedescriptor to read
4249 * @param timeout number of successless selects until we bail out
4250 * @param selectresolution how long to wait on each select
4251 * @param Error strerror() on error
4252 * @returns numbers of chars read
4254 int StrBufTCP_read_buffered_line(StrBuf *Line,
4258 int selectresolution,
4262 int nSuccessLess = 0;
4269 if (buf->BufUsed > 0) {
4270 pch = strchr(buf->buf, '\n');
4273 len = pch - buf->buf;
4274 if (len > 0 && (*(pch - 1) == '\r') )
4276 StrBufSub(Line, buf, 0, len - rlen);
4277 StrBufCutLeft(buf, len + 1);
4282 if (buf->BufSize - buf->BufUsed < 10)
4283 IncreaseBuf(buf, 1, -1);
4285 fdflags = fcntl(*fd, F_GETFL);
4286 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4288 while ((nSuccessLess < timeout) && (pch == NULL)) {
4290 tv.tv_sec = selectresolution;
4295 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4296 *Error = strerror(errno);
4302 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4307 &buf->buf[buf->BufUsed],
4308 buf->BufSize - buf->BufUsed - 1);
4310 *Error = strerror(errno);
4315 else if (rlen > 0) {
4317 buf->BufUsed += rlen;
4318 buf->buf[buf->BufUsed] = '\0';
4319 if (buf->BufUsed + 10 > buf->BufSize) {
4320 IncreaseBuf(buf, 1, -1);
4322 pch = strchr(buf->buf, '\n');
4329 len = pch - buf->buf;
4330 if (len > 0 && (*(pch - 1) == '\r') )
4332 StrBufSub(Line, buf, 0, len - rlen);
4333 StrBufCutLeft(buf, len + 1);
4340 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4341 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4342 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4344 * @ingroup StrBuf_BufferedIO
4345 * @brief Read a line from socket
4346 * flushes and closes the FD on error
4347 * @param Line where to append our Line read from the fd / I/O Buffer;
4348 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4349 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4350 * @param fd pointer to the filedescriptor to read
4351 * @param timeout number of successless selects until we bail out
4352 * @param selectresolution how long to wait on each select
4353 * @param Error strerror() on error
4354 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4356 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4361 int selectresolution,
4364 const char *pche = NULL;
4365 const char *pos = NULL;
4367 int len, rlen, retlen;
4368 int nSuccessLess = 0;
4370 const char *pch = NULL;
4376 if ((Line == NULL) ||
4383 *Error = ErrRBLF_PreConditionFailed;
4388 if ((IOBuf->BufUsed > 0) &&
4390 (pos < IOBuf->buf + IOBuf->BufUsed))
4394 pche = IOBuf->buf + IOBuf->BufUsed;
4398 while ((pch < pche) && (*pch != '\n'))
4400 if (Line->BufUsed + 10 > Line->BufSize)
4403 apos = pcht - Line->buf;
4405 IncreaseBuf(Line, 1, -1);
4406 pcht = Line->buf + apos;
4414 if (len > 0 && (*(pch - 1) == '\r') )
4423 if ((pch >= pche) || (*pch == '\0'))
4431 if ((pch != NULL) &&
4434 if (pch + 1 >= pche) {
4447 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4449 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4450 IncreaseBuf(IOBuf, 1, -1);
4452 fdflags = fcntl(*fd, F_GETFL);
4453 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4456 while ((nSuccessLess < timeout) &&
4466 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4467 *Error = strerror(errno);
4471 *Error = ErrRBLF_SelectFailed;
4474 if (! FD_ISSET(*fd, &rfds) != 0) {
4480 &IOBuf->buf[IOBuf->BufUsed],
4481 IOBuf->BufSize - IOBuf->BufUsed - 1);
4483 *Error = strerror(errno);
4488 else if (rlen > 0) {
4490 pLF = IOBuf->buf + IOBuf->BufUsed;
4491 IOBuf->BufUsed += rlen;
4492 IOBuf->buf[IOBuf->BufUsed] = '\0';
4494 pche = IOBuf->buf + IOBuf->BufUsed;
4496 while ((pLF < pche) && (*pLF != '\n'))
4498 if ((pLF >= pche) || (*pLF == '\0'))
4501 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4505 if (pLF != NULL) apos = pLF - IOBuf->buf;
4506 IncreaseBuf(IOBuf, 1, -1);
4507 if (pLF != NULL) pLF = IOBuf->buf + apos;
4517 if (len > 0 && (*(pLF - 1) == '\r') )
4519 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4520 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4526 return retlen + len;
4528 *Error = ErrRBLF_NotEnoughSentFromServer;
4533 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4535 * @ingroup StrBuf_IO
4536 * @brief Input binary data from socket
4537 * flushes and closes the FD on error
4538 * @param Buf the buffer to get the input to
4539 * @param fd pointer to the filedescriptor to read
4540 * @param append Append to an existing string or replace?
4541 * @param nBytes the maximal number of bytes to read
4542 * @param Error strerror() on error
4543 * @returns numbers of chars read
4545 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4556 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4558 *Error = ErrRBLF_BLOBPreConditionFailed;
4563 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4564 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4566 ptr = Buf->buf + Buf->BufUsed;
4568 fdflags = fcntl(*fd, F_GETFL);
4569 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4571 while ((nRead < nBytes) &&
4581 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4582 *Error = strerror(errno);
4586 *Error = ErrRBLF_SelectFailed;
4589 if (! FD_ISSET(*fd, &rfds) != 0) {
4595 if ((rlen = read(*fd,
4597 nBytes - nRead)) == -1) {
4600 *Error = strerror(errno);
4605 Buf->BufUsed += rlen;
4607 Buf->buf[Buf->BufUsed] = '\0';
4611 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4612 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4614 * @ingroup StrBuf_BufferedIO
4615 * @brief Input binary data from socket
4616 * flushes and closes the FD on error
4617 * @param Blob put binary thing here
4618 * @param IOBuf the buffer to get the input to
4619 * @param Pos offset inside of IOBuf
4620 * @param fd pointer to the filedescriptor to read
4621 * @param append Append to an existing string or replace?
4622 * @param nBytes the maximal number of bytes to read
4623 * @param check whether we should search for '000\n' terminators in case of timeouts
4624 * @param Error strerror() on error
4625 * @returns numbers of chars read
4627 int StrBufReadBLOBBuffered(StrBuf *Blob,
4640 int nAlreadyRead = 0;
4645 int nSuccessLess = 0;
4648 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4652 *Error = ErrRBB_BLOBFPreConditionFailed;
4658 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4659 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4664 rlen = pos - IOBuf->buf;
4665 rlen = IOBuf->BufUsed - rlen;
4668 if ((IOBuf->BufUsed > 0) &&
4670 (pos < IOBuf->buf + IOBuf->BufUsed))
4672 if (rlen < nBytes) {
4673 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4674 Blob->BufUsed += rlen;
4675 Blob->buf[Blob->BufUsed] = '\0';
4676 nAlreadyRead = nRead = rlen;
4679 if (rlen >= nBytes) {
4680 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4681 Blob->BufUsed += nBytes;
4682 Blob->buf[Blob->BufUsed] = '\0';
4683 if (rlen == nBytes) {
4695 if (IOBuf->BufSize < nBytes - nRead)
4696 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4699 fdflags = fcntl(*fd, F_GETFL);
4700 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4708 while ((nSuccessLess < MaxTries) &&
4718 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4719 *Error = strerror(errno);
4723 *Error = ErrRBLF_SelectFailed;
4726 if (! FD_ISSET(*fd, &rfds) != 0) {
4733 IOBuf->BufSize - (ptr - IOBuf->buf));
4737 *Error = strerror(errno);
4740 else if (rlen == 0){
4741 if ((check == NNN_TERM) &&
4743 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4745 StrBufPlain(Blob, HKEY("\n000\n"));
4746 StrBufCutRight(Blob, 5);
4747 return Blob->BufUsed;
4749 else if (!IsNonBlock)
4751 else if (nSuccessLess > MaxTries) {
4753 *Error = ErrRBB_too_many_selects;
4757 else if (rlen > 0) {
4761 IOBuf->BufUsed += rlen;
4764 if (nSuccessLess >= MaxTries) {
4766 *Error = ErrRBB_too_many_selects;
4770 if (nRead > nBytes) {
4771 *Pos = IOBuf->buf + nBytes;
4773 Blob->buf[Blob->BufUsed] = '\0';
4774 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4778 return nRead + nAlreadyRead;
4782 * @ingroup StrBuf_IO
4783 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4784 * @param LineBuf your line will be copied here.
4785 * @param Buf BLOB with lines of text...
4786 * @param Ptr moved arround to keep the next-line across several iterations
4787 * has to be &NULL on start; will be &NotNULL on end of buffer
4788 * @returns size of remaining buffer
4790 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4792 const char *aptr, *ptr, *eptr;
4795 if ((Buf == NULL) ||
4796 (*Ptr == StrBufNOTNULL) ||
4798 (LineBuf->buf == NULL))
4800 *Ptr = StrBufNOTNULL;
4804 FlushStrBuf(LineBuf);
4806 ptr = aptr = Buf->buf;
4810 optr = LineBuf->buf;
4811 eptr = Buf->buf + Buf->BufUsed;
4812 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4814 while ((ptr <= eptr) &&
4821 LineBuf->BufUsed = optr - LineBuf->buf;
4822 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4823 optr = LineBuf->buf + LineBuf->BufUsed;
4824 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4828 if ((ptr >= eptr) && (optr > LineBuf->buf))
4830 LineBuf->BufUsed = optr - LineBuf->buf;
4832 if ((ptr <= eptr) && (*ptr == '\r'))
4834 if ((ptr <= eptr) && (*ptr == '\n'))
4841 *Ptr = StrBufNOTNULL;
4844 return Buf->BufUsed - (ptr - Buf->buf);
4849 * @ingroup StrBuf_IO
4850 * @brief removes double slashes from pathnames
4851 * @param Dir directory string to filter
4852 * @param RemoveTrailingSlash allows / disallows trailing slashes
4854 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4860 while (!IsEmptyStr(a)) {
4872 if ((RemoveTrailingSlash) &&
4878 Dir->BufUsed = b - Dir->buf;